KIO
chmodjob.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Stephan Kulow <coolo@kde.org> 00003 David Faure <faure@kde.org> 00004 Waldo Bastian <bastian@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "chmodjob.h" 00023 00024 #include "job.h" 00025 #include "jobuidelegate.h" 00026 00027 #include <kglobal.h> 00028 00029 #include <klocale.h> 00030 #include <kdebug.h> 00031 #include <kmessagebox.h> 00032 #include <QtCore/QFile> 00033 00034 #include <config.h> 00035 00036 #include <pwd.h> 00037 #include <grp.h> 00038 #include <sys/types.h> 00039 #include <unistd.h> 00040 #include <assert.h> 00041 00042 #include "job_p.h" 00043 00044 namespace KIO { 00045 00046 struct ChmodInfo 00047 { 00048 KUrl url; 00049 int permissions; 00050 }; 00051 00052 enum ChmodJobState { 00053 CHMODJOB_STATE_LISTING, 00054 CHMODJOB_STATE_CHMODING 00055 }; 00056 00057 class ChmodJobPrivate: public KIO::JobPrivate 00058 { 00059 public: 00060 ChmodJobPrivate(const KFileItemList& lstItems, int permissions, int mask, 00061 int newOwner, int newGroup, bool recursive) 00062 : state( CHMODJOB_STATE_LISTING ) 00063 , m_permissions( permissions ) 00064 , m_mask( mask ) 00065 , m_newOwner( newOwner ) 00066 , m_newGroup( newGroup ) 00067 , m_recursive( recursive ) 00068 , m_lstItems( lstItems ) 00069 { 00070 } 00071 00072 ChmodJobState state; 00073 int m_permissions; 00074 int m_mask; 00075 int m_newOwner; 00076 int m_newGroup; 00077 bool m_recursive; 00078 KFileItemList m_lstItems; 00079 QLinkedList<ChmodInfo> m_infos; // linkedlist since we keep removing the first item 00080 00081 void chmodNextFile(); 00082 void _k_slotEntries( KIO::Job * , const KIO::UDSEntryList & ); 00083 void _k_processList(); 00084 00085 Q_DECLARE_PUBLIC(ChmodJob) 00086 00087 static inline ChmodJob *newJob(const KFileItemList& lstItems, int permissions, int mask, 00088 int newOwner, int newGroup, bool recursive, JobFlags flags) 00089 { 00090 ChmodJob *job = new ChmodJob(*new ChmodJobPrivate(lstItems,permissions,mask, 00091 newOwner,newGroup,recursive)); 00092 job->setUiDelegate(new JobUiDelegate()); 00093 if (!(flags & HideProgressInfo)) 00094 KIO::getJobTracker()->registerJob(job); 00095 return job; 00096 } 00097 }; 00098 00099 } // namespace KIO 00100 00101 using namespace KIO; 00102 00103 ChmodJob::ChmodJob(ChmodJobPrivate &dd) 00104 : KIO::Job(dd) 00105 { 00106 QMetaObject::invokeMethod( this, "_k_processList", Qt::QueuedConnection ); 00107 } 00108 00109 ChmodJob::~ChmodJob() 00110 { 00111 } 00112 00113 void ChmodJobPrivate::_k_processList() 00114 { 00115 Q_Q(ChmodJob); 00116 while ( !m_lstItems.isEmpty() ) 00117 { 00118 const KFileItem item = m_lstItems.first(); 00119 if ( !item.isLink() ) // don't do anything with symlinks 00120 { 00121 // File or directory -> remember to chmod 00122 ChmodInfo info; 00123 info.url = item.url(); 00124 // This is a toplevel file, we apply changes directly (no +X emulation here) 00125 const mode_t permissions = item.permissions() & 0777; // get rid of "set gid" and other special flags 00126 info.permissions = ( m_permissions & m_mask ) | ( permissions & ~m_mask ); 00127 /*kDebug(7007) << "toplevel url:" << info.url << "\n current permissions=" << QString::number(permissions,8) 00128 << "\n wanted permission=" << QString::number(m_permissions,8) 00129 << "\n with mask=" << QString::number(m_mask,8) 00130 << "\n with ~mask (mask bits we keep) =" << QString::number((uint)~m_mask,8) 00131 << "\n bits we keep =" << QString::number(permissions & ~m_mask,8) 00132 << "\n new permissions = " << QString::number(info.permissions,8);*/ 00133 m_infos.prepend( info ); 00134 //kDebug(7007) << "processList : Adding info for " << info.url; 00135 // Directory and recursive -> list 00136 if ( item.isDir() && m_recursive ) 00137 { 00138 //kDebug(7007) << "ChmodJob::processList dir -> listing"; 00139 KIO::ListJob * listJob = KIO::listRecursive( item.url(), KIO::HideProgressInfo ); 00140 q->connect( listJob, SIGNAL(entries( KIO::Job *, 00141 const KIO::UDSEntryList& )), 00142 SLOT(_k_slotEntries( KIO::Job*, const KIO::UDSEntryList& ))); 00143 q->addSubjob( listJob ); 00144 return; // we'll come back later, when this one's finished 00145 } 00146 } 00147 m_lstItems.removeFirst(); 00148 } 00149 kDebug(7007) << "ChmodJob::processList -> going to STATE_CHMODING"; 00150 // We have finished, move on 00151 state = CHMODJOB_STATE_CHMODING; 00152 chmodNextFile(); 00153 } 00154 00155 void ChmodJobPrivate::_k_slotEntries( KIO::Job*, const KIO::UDSEntryList & list ) 00156 { 00157 KIO::UDSEntryList::ConstIterator it = list.begin(); 00158 KIO::UDSEntryList::ConstIterator end = list.end(); 00159 for (; it != end; ++it) { 00160 const KIO::UDSEntry& entry = *it; 00161 const bool isLink = !entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST ).isEmpty(); 00162 const QString relativePath = entry.stringValue( KIO::UDSEntry::UDS_NAME ); 00163 if ( !isLink && relativePath != ".." ) 00164 { 00165 const mode_t permissions = entry.numberValue( KIO::UDSEntry::UDS_ACCESS ) 00166 & 0777; // get rid of "set gid" and other special flags 00167 00168 ChmodInfo info; 00169 info.url = m_lstItems.first().url(); // base directory 00170 info.url.addPath( relativePath ); 00171 int mask = m_mask; 00172 // Emulate -X: only give +x to files that had a +x bit already 00173 // So the check is the opposite : if the file had no x bit, don't touch x bits 00174 // For dirs this doesn't apply 00175 if ( !entry.isDir() ) 00176 { 00177 int newPerms = m_permissions & mask; 00178 if ( (newPerms & 0111) && !(permissions & 0111) ) 00179 { 00180 // don't interfere with mandatory file locking 00181 if ( newPerms & 02000 ) 00182 mask = mask & ~0101; 00183 else 00184 mask = mask & ~0111; 00185 } 00186 } 00187 info.permissions = ( m_permissions & mask ) | ( permissions & ~mask ); 00188 /*kDebug(7007) << info.url << "\n current permissions=" << QString::number(permissions,8) 00189 << "\n wanted permission=" << QString::number(m_permissions,8) 00190 << "\n with mask=" << QString::number(mask,8) 00191 << "\n with ~mask (mask bits we keep) =" << QString::number((uint)~mask,8) 00192 << "\n bits we keep =" << QString::number(permissions & ~mask,8) 00193 << "\n new permissions = " << QString::number(info.permissions,8);*/ 00194 // Prepend this info in our todo list. 00195 // This way, the toplevel dirs are done last. 00196 m_infos.prepend( info ); 00197 } 00198 } 00199 } 00200 00201 void ChmodJobPrivate::chmodNextFile() 00202 { 00203 Q_Q(ChmodJob); 00204 if ( !m_infos.isEmpty() ) 00205 { 00206 ChmodInfo info = m_infos.takeFirst(); 00207 // First update group / owner (if local file) 00208 // (permissions have to set after, in case of suid and sgid) 00209 if ( info.url.isLocalFile() && ( m_newOwner != -1 || m_newGroup != -1 ) ) 00210 { 00211 QString path = info.url.toLocalFile(); 00212 if ( chown( QFile::encodeName(path), m_newOwner, m_newGroup ) != 0 ) 00213 { 00214 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")) ); 00215 if (answer == KMessageBox::Cancel) 00216 { 00217 q->setError( ERR_USER_CANCELED ); 00218 q->emitResult(); 00219 return; 00220 } 00221 } 00222 } 00223 00224 kDebug(7007) << "chmod'ing" << info.url 00225 << "to" << QString::number(info.permissions,8); 00226 KIO::SimpleJob * job = KIO::chmod( info.url, info.permissions ); 00227 // copy the metadata for acl and default acl 00228 const QString aclString = q->queryMetaData( QLatin1String("ACL_STRING") ); 00229 const QString defaultAclString = q->queryMetaData( QLatin1String("DEFAULT_ACL_STRING") ); 00230 if ( !aclString.isEmpty() ) 00231 job->addMetaData( QLatin1String("ACL_STRING"), aclString ); 00232 if ( !defaultAclString.isEmpty() ) 00233 job->addMetaData( QLatin1String("DEFAULT_ACL_STRING"), defaultAclString ); 00234 q->addSubjob(job); 00235 } 00236 else 00237 // We have finished 00238 q->emitResult(); 00239 } 00240 00241 void ChmodJob::slotResult( KJob * job ) 00242 { 00243 Q_D(ChmodJob); 00244 removeSubjob(job); 00245 if ( job->error() ) 00246 { 00247 setError( job->error() ); 00248 setErrorText( job->errorText() ); 00249 emitResult(); 00250 return; 00251 } 00252 //kDebug(7007) << "d->m_lstItems:" << d->m_lstItems.count(); 00253 switch ( d->state ) 00254 { 00255 case CHMODJOB_STATE_LISTING: 00256 d->m_lstItems.removeFirst(); 00257 kDebug(7007) << "-> processList"; 00258 d->_k_processList(); 00259 return; 00260 case CHMODJOB_STATE_CHMODING: 00261 kDebug(7007) << "-> chmodNextFile"; 00262 d->chmodNextFile(); 00263 return; 00264 default: 00265 assert(0); 00266 return; 00267 } 00268 } 00269 00270 ChmodJob *KIO::chmod( const KFileItemList& lstItems, int permissions, int mask, 00271 const QString& owner, const QString& group, 00272 bool recursive, JobFlags flags ) 00273 { 00274 uid_t newOwnerID = uid_t(-1); // chown(2) : -1 means no change 00275 if ( !owner.isEmpty() ) 00276 { 00277 struct passwd* pw = getpwnam(QFile::encodeName(owner)); 00278 if ( pw == 0L ) 00279 kError(250) << " ERROR: No user" << owner; 00280 else 00281 newOwnerID = pw->pw_uid; 00282 } 00283 gid_t newGroupID = gid_t(-1); // chown(2) : -1 means no change 00284 if ( !group.isEmpty() ) 00285 { 00286 struct group* g = getgrnam(QFile::encodeName(group)); 00287 if ( g == 0L ) 00288 kError(250) << " ERROR: No group" << group; 00289 else 00290 newGroupID = g->gr_gid; 00291 } 00292 return ChmodJobPrivate::newJob(lstItems, permissions, mask, newOwnerID, 00293 newGroupID, recursive, flags); 00294 } 00295 00296 #include "chmodjob.moc"
KDE 4.6 API Reference