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