kdelibs/kio/kio/chmodjob.cpp
Ivailo Monev 2c53b75153 kio: correct error message areas in KIO::chmod()
250 is reserved for kfile (kdelibs)

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2022-10-22 23:23:17 +03:00

293 lines
11 KiB
C++

/* This file is part of the KDE libraries
Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
David Faure <faure@kde.org>
Waldo Bastian <bastian@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "chmodjob.h"
#include "job.h"
#include "jobuidelegate.h"
#include <klocale.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kuser.h>
#include <QtCore/QFile>
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include "job_p.h"
namespace KIO {
struct ChmodInfo
{
KUrl url;
int permissions;
};
enum ChmodJobState {
CHMODJOB_STATE_LISTING,
CHMODJOB_STATE_CHMODING
};
class ChmodJobPrivate: public KIO::JobPrivate
{
public:
ChmodJobPrivate(const KFileItemList& lstItems, int permissions, int mask,
int newOwner, int newGroup, bool recursive)
: state( CHMODJOB_STATE_LISTING )
, m_permissions( permissions )
, m_mask( mask )
, m_newOwner( newOwner )
, m_newGroup( newGroup )
, m_recursive( recursive )
, m_lstItems( lstItems )
{
}
ChmodJobState state;
int m_permissions;
int m_mask;
int m_newOwner;
int m_newGroup;
bool m_recursive;
KFileItemList m_lstItems;
QList<ChmodInfo> m_infos;
void chmodNextFile();
void _k_slotEntries( KIO::Job * , const KIO::UDSEntryList & );
void _k_processList();
Q_DECLARE_PUBLIC(ChmodJob)
static inline ChmodJob *newJob(const KFileItemList& lstItems, int permissions, int mask,
int newOwner, int newGroup, bool recursive, JobFlags flags)
{
ChmodJob *job = new ChmodJob(*new ChmodJobPrivate(lstItems,permissions,mask,
newOwner,newGroup,recursive));
job->setUiDelegate(new JobUiDelegate());
if (!(flags & HideProgressInfo))
KIO::getJobTracker()->registerJob(job);
return job;
}
};
} // namespace KIO
using namespace KIO;
ChmodJob::ChmodJob(ChmodJobPrivate &dd)
: KIO::Job(dd)
{
QMetaObject::invokeMethod( this, "_k_processList", Qt::QueuedConnection );
}
ChmodJob::~ChmodJob()
{
}
void ChmodJobPrivate::_k_processList()
{
Q_Q(ChmodJob);
while ( !m_lstItems.isEmpty() )
{
const KFileItem item = m_lstItems.first();
if ( !item.isLink() ) // don't do anything with symlinks
{
// File or directory -> remember to chmod
ChmodInfo info;
info.url = item.url();
// This is a toplevel file, we apply changes directly (no +X emulation here)
const mode_t permissions = item.permissions() & 0777; // get rid of "set gid" and other special flags
info.permissions = ( m_permissions & m_mask ) | ( permissions & ~m_mask );
/*kDebug(7007) << "toplevel url:" << info.url << "\n current permissions=" << QString::number(permissions,8)
<< "\n wanted permission=" << QString::number(m_permissions,8)
<< "\n with mask=" << QString::number(m_mask,8)
<< "\n with ~mask (mask bits we keep) =" << QString::number((uint)~m_mask,8)
<< "\n bits we keep =" << QString::number(permissions & ~m_mask,8)
<< "\n new permissions = " << QString::number(info.permissions,8);*/
m_infos.prepend( info );
//kDebug(7007) << "processList : Adding info for " << info.url;
// Directory and recursive -> list
if ( item.isDir() && m_recursive )
{
//kDebug(7007) << "ChmodJob::processList dir -> listing";
KIO::ListJob * listJob = KIO::listRecursive( item.url(), KIO::HideProgressInfo );
q->connect( listJob, SIGNAL(entries( KIO::Job *,
const KIO::UDSEntryList& )),
SLOT(_k_slotEntries(KIO::Job*,KIO::UDSEntryList)));
q->addSubjob( listJob );
return; // we'll come back later, when this one's finished
}
}
m_lstItems.removeFirst();
}
kDebug(7007) << "ChmodJob::processList -> going to STATE_CHMODING";
// We have finished, move on
state = CHMODJOB_STATE_CHMODING;
chmodNextFile();
}
void ChmodJobPrivate::_k_slotEntries( KIO::Job*, const KIO::UDSEntryList & list )
{
KIO::UDSEntryList::ConstIterator it = list.begin();
KIO::UDSEntryList::ConstIterator end = list.end();
for (; it != end; ++it) {
const KIO::UDSEntry& entry = *it;
const bool isLink = !entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST ).isEmpty();
const QString relativePath = entry.stringValue( KIO::UDSEntry::UDS_NAME );
if ( !isLink && relativePath != ".." )
{
const mode_t permissions = entry.numberValue( KIO::UDSEntry::UDS_ACCESS )
& 0777; // get rid of "set gid" and other special flags
ChmodInfo info;
info.url = m_lstItems.first().url(); // base directory
info.url.addPath( relativePath );
int mask = m_mask;
// Emulate -X: only give +x to files that had a +x bit already
// So the check is the opposite : if the file had no x bit, don't touch x bits
// For dirs this doesn't apply
if ( !entry.isDir() )
{
int newPerms = m_permissions & mask;
if ( (newPerms & 0111) && !(permissions & 0111) )
{
// don't interfere with mandatory file locking
if ( newPerms & 02000 )
mask = mask & ~0101;
else
mask = mask & ~0111;
}
}
info.permissions = ( m_permissions & mask ) | ( permissions & ~mask );
/*kDebug(7007) << info.url << "\n current permissions=" << QString::number(permissions,8)
<< "\n wanted permission=" << QString::number(m_permissions,8)
<< "\n with mask=" << QString::number(mask,8)
<< "\n with ~mask (mask bits we keep) =" << QString::number((uint)~mask,8)
<< "\n bits we keep =" << QString::number(permissions & ~mask,8)
<< "\n new permissions = " << QString::number(info.permissions,8);*/
// Prepend this info in our todo list.
// This way, the toplevel dirs are done last.
m_infos.prepend( info );
}
}
}
void ChmodJobPrivate::chmodNextFile()
{
Q_Q(ChmodJob);
if ( !m_infos.isEmpty() )
{
ChmodInfo info = m_infos.takeFirst();
// First update group / owner (if local file)
// (permissions have to set after, in case of suid and sgid)
if ( info.url.isLocalFile() && ( m_newOwner != -1 || m_newGroup != -1 ) )
{
QString path = info.url.toLocalFile();
if ( chown( KUrl(QFile::encodeName(path)), QString::number(m_newOwner), QString::number(m_newGroup) ) != 0 )
{
int answer = KMessageBox::warningContinueCancel( 0, i18n( "<qt>Could not modify the ownership of file <b>%1</b>. You have insufficient access to the file to perform the change.</qt>" , path), QString(), KGuiItem(i18n("&Skip File")) );
if (answer == KMessageBox::Cancel)
{
q->setError( ERR_USER_CANCELED );
q->emitResult();
return;
}
}
}
kDebug(7007) << "chmod'ing" << info.url
<< "to" << QString::number(info.permissions,8);
KIO::SimpleJob * job = KIO::chmod( info.url, info.permissions );
// copy the metadata for acl and default acl
const QString aclString = q->queryMetaData( QLatin1String("ACL_STRING") );
const QString defaultAclString = q->queryMetaData( QLatin1String("DEFAULT_ACL_STRING") );
if ( !aclString.isEmpty() )
job->addMetaData( QLatin1String("ACL_STRING"), aclString );
if ( !defaultAclString.isEmpty() )
job->addMetaData( QLatin1String("DEFAULT_ACL_STRING"), defaultAclString );
q->addSubjob(job);
}
else
// We have finished
q->emitResult();
}
void ChmodJob::slotResult( KJob * job )
{
Q_D(ChmodJob);
removeSubjob(job);
if ( job->error() )
{
setError( job->error() );
setErrorText( job->errorText() );
emitResult();
return;
}
//kDebug(7007) << "d->m_lstItems:" << d->m_lstItems.count();
switch ( d->state )
{
case CHMODJOB_STATE_LISTING:
d->m_lstItems.removeFirst();
kDebug(7007) << "-> processList";
d->_k_processList();
return;
case CHMODJOB_STATE_CHMODING:
kDebug(7007) << "-> chmodNextFile";
d->chmodNextFile();
return;
default:
assert(0);
return;
}
}
ChmodJob *KIO::chmod( const KFileItemList& lstItems, int permissions, int mask,
const QString& owner, const QString& group,
bool recursive, JobFlags flags )
{
uid_t newOwnerID = uid_t(-1); // chown(2) : -1 means no change
if ( !owner.isEmpty() )
{
const KUser kuser(owner);
if ( !kuser.isValid() )
kError(7007) << "No user" << owner;
else
newOwnerID = kuser.uid();
}
gid_t newGroupID = gid_t(-1); // chown(2) : -1 means no change
if ( !group.isEmpty() )
{
const KUserGroup kusergroup(group);
if ( kusergroup.isValid() )
kError(7007) << "No group" << group;
else
newGroupID = kusergroup.gid();
}
return ChmodJobPrivate::newJob(lstItems, permissions, mask, newOwnerID,
newGroupID, recursive, flags);
}
#include "moc_chmodjob.cpp"