KIOSlave
file.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org> 00003 Copyright (C) 2000-2002 David Faure <faure@kde.org> 00004 Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org> 00005 Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org> 00006 Copyright (C) 2007 Thiago Macieira <thiago@kde.org> 00007 00008 This library is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU Library General Public 00010 License (LGPL) as published by the Free Software Foundation; 00011 either version 2 of the License, or (at your option) any later 00012 version. 00013 00014 This library is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 Library General Public License for more details. 00018 00019 You should have received a copy of the GNU Library General Public License 00020 along with this library; see the file COPYING.LIB. If not, write to 00021 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00022 Boston, MA 02110-1301, USA. 00023 */ 00024 00025 #define QT_NO_CAST_FROM_ASCII 00026 00027 #include "file.h" 00028 #include <QDirIterator> 00029 00030 #include <config.h> 00031 #include <config-acl.h> 00032 #include <config-kioslave-file.h> 00033 00034 00035 #include <sys/types.h> 00036 #include <sys/wait.h> 00037 #include <sys/stat.h> 00038 #include <sys/socket.h> 00039 #ifdef HAVE_SYS_TIME_H 00040 #include <sys/time.h> 00041 #endif 00042 00043 #ifdef HAVE_POSIX_ACL 00044 #include <sys/acl.h> 00045 #include <acl/libacl.h> 00046 #endif 00047 00048 #include <assert.h> 00049 #include <dirent.h> 00050 #include <errno.h> 00051 #include <fcntl.h> 00052 #include <grp.h> 00053 #include <pwd.h> 00054 #include <stdio.h> 00055 #include <stdlib.h> 00056 #include <signal.h> 00057 #include <time.h> 00058 #include <utime.h> 00059 #include <unistd.h> 00060 #ifdef HAVE_STRING_H 00061 #include <string.h> 00062 #endif 00063 00064 #include <QtCore/QByteRef> 00065 #include <QtCore/QDate> 00066 #include <QtCore/QVarLengthArray> 00067 #include <QtCore/QCoreApplication> 00068 #include <QtCore/QRegExp> 00069 #include <QtCore/QFile> 00070 #ifdef Q_WS_WIN 00071 #include <QtCore/QDir> 00072 #include <QtCore/QFileInfo> 00073 #endif 00074 00075 #include <kdebug.h> 00076 #include <kurl.h> 00077 #include <kcomponentdata.h> 00078 #include <kconfig.h> 00079 #include <kconfiggroup.h> 00080 #include <ktemporaryfile.h> 00081 #include <klocale.h> 00082 #include <limits.h> 00083 #include <kshell.h> 00084 #include <kmountpoint.h> 00085 #include <kstandarddirs.h> 00086 00087 #ifdef HAVE_VOLMGT 00088 #include <volmgt.h> 00089 #include <sys/mnttab.h> 00090 #endif 00091 00092 #include <kdirnotify.h> 00093 #include <kio/ioslave_defaults.h> 00094 #include <kde_file.h> 00095 #include <kglobal.h> 00096 #include <kmimetype.h> 00097 00098 using namespace KIO; 00099 00100 #define MAX_IPC_SIZE (1024*32) 00101 00102 static QString testLogFile( const QByteArray&_filename ); 00103 #ifdef HAVE_POSIX_ACL 00104 static bool isExtendedACL( acl_t p_acl ); 00105 static void appendACLAtoms( const QByteArray & path, UDSEntry& entry, 00106 mode_t type, bool withACL ); 00107 #endif 00108 00109 extern "C" int KDE_EXPORT kdemain( int argc, char **argv ) 00110 { 00111 QCoreApplication app( argc, argv ); // needed for QSocketNotifier 00112 KComponentData componentData( "kio_file", "kdelibs4" ); 00113 ( void ) KGlobal::locale(); 00114 00115 kDebug(7101) << "Starting" << getpid(); 00116 00117 if (argc != 4) 00118 { 00119 fprintf(stderr, "Usage: kio_file protocol domain-socket1 domain-socket2\n"); 00120 exit(-1); 00121 } 00122 00123 FileProtocol slave(argv[2], argv[3]); 00124 slave.dispatchLoop(); 00125 00126 kDebug(7101) << "Done"; 00127 return 0; 00128 } 00129 00130 FileProtocol::FileProtocol( const QByteArray &pool, const QByteArray &app ) 00131 : SlaveBase( "file", pool, app ), openFd(-1) 00132 { 00133 } 00134 00135 FileProtocol::~FileProtocol() 00136 { 00137 } 00138 00139 #ifdef HAVE_POSIX_ACL 00140 static QString aclToText(acl_t acl) { 00141 ssize_t size = 0; 00142 char* txt = acl_to_text(acl, &size); 00143 const QString ret = QString::fromLatin1(txt, size); 00144 acl_free(txt); 00145 return ret; 00146 } 00147 #endif 00148 00149 int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault ) 00150 { 00151 int ret = 0; 00152 #ifdef HAVE_POSIX_ACL 00153 00154 const QString ACLString = metaData(QLatin1String("ACL_STRING")); 00155 const QString defaultACLString = metaData(QLatin1String("DEFAULT_ACL_STRING")); 00156 // Empty strings mean leave as is 00157 if ( !ACLString.isEmpty() ) { 00158 acl_t acl = 0; 00159 if (ACLString == QLatin1String("ACL_DELETE")) { 00160 // user told us to delete the extended ACL, so let's write only 00161 // the minimal (UNIX permission bits) part 00162 acl = acl_from_mode( perm ); 00163 } 00164 acl = acl_from_text( ACLString.toLatin1() ); 00165 if ( acl_valid( acl ) == 0 ) { // let's be safe 00166 ret = acl_set_file( path, ACL_TYPE_ACCESS, acl ); 00167 kDebug(7101) << "Set ACL on:" << path << "to:" << aclToText(acl); 00168 } 00169 acl_free( acl ); 00170 if ( ret != 0 ) return ret; // better stop trying right away 00171 } 00172 00173 if ( directoryDefault && !defaultACLString.isEmpty() ) { 00174 if ( defaultACLString == QLatin1String("ACL_DELETE") ) { 00175 // user told us to delete the default ACL, do so 00176 ret += acl_delete_def_file( path ); 00177 } else { 00178 acl_t acl = acl_from_text( defaultACLString.toLatin1() ); 00179 if ( acl_valid( acl ) == 0 ) { // let's be safe 00180 ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl ); 00181 kDebug(7101) << "Set Default ACL on:" << path << "to:" << aclToText(acl); 00182 } 00183 acl_free( acl ); 00184 } 00185 } 00186 #else 00187 Q_UNUSED(path); 00188 Q_UNUSED(perm); 00189 Q_UNUSED(directoryDefault); 00190 #endif 00191 return ret; 00192 } 00193 00194 void FileProtocol::chmod( const KUrl& url, int permissions ) 00195 { 00196 const QString path(url.toLocalFile()); 00197 const QByteArray _path( QFile::encodeName(path) ); 00198 /* FIXME: Should be atomic */ 00199 if ( KDE::chmod( path, permissions ) == -1 || 00200 ( setACL( _path.data(), permissions, false ) == -1 ) || 00201 /* if not a directory, cannot set default ACLs */ 00202 ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) { 00203 00204 switch (errno) { 00205 case EPERM: 00206 case EACCES: 00207 error(KIO::ERR_ACCESS_DENIED, path); 00208 break; 00209 #if defined(ENOTSUP) 00210 case ENOTSUP: // from setACL since chmod can't return ENOTSUP 00211 error(KIO::ERR_UNSUPPORTED_ACTION, i18n("Setting ACL for %1", path)); 00212 break; 00213 #endif 00214 case ENOSPC: 00215 error(KIO::ERR_DISK_FULL, path); 00216 break; 00217 default: 00218 error(KIO::ERR_CANNOT_CHMOD, path); 00219 } 00220 } else 00221 finished(); 00222 } 00223 00224 void FileProtocol::setModificationTime( const KUrl& url, const QDateTime& mtime ) 00225 { 00226 const QString path(url.toLocalFile()); 00227 KDE_struct_stat statbuf; 00228 if (KDE::lstat(path, &statbuf) == 0) { 00229 struct utimbuf utbuf; 00230 utbuf.actime = statbuf.st_atime; // access time, unchanged 00231 utbuf.modtime = mtime.toTime_t(); // modification time 00232 if (KDE::utime(path, &utbuf) != 0) { 00233 // TODO: errno could be EACCES, EPERM, EROFS 00234 error(KIO::ERR_CANNOT_SETTIME, path); 00235 } else { 00236 finished(); 00237 } 00238 } else { 00239 error(KIO::ERR_DOES_NOT_EXIST, path); 00240 } 00241 } 00242 00243 void FileProtocol::mkdir( const KUrl& url, int permissions ) 00244 { 00245 const QString path(url.toLocalFile()); 00246 00247 kDebug(7101) << path << "permission=" << permissions; 00248 00249 // Remove existing file or symlink, if requested (#151851) 00250 if (metaData(QLatin1String("overwrite")) == QLatin1String("true")) 00251 QFile::remove(path); 00252 00253 KDE_struct_stat buff; 00254 if ( KDE::lstat( path, &buff ) == -1 ) { 00255 if ( KDE::mkdir( path, 0777 /*umask will be applied*/ ) != 0 ) { 00256 if ( errno == EACCES ) { 00257 error(KIO::ERR_ACCESS_DENIED, path); 00258 return; 00259 } else if ( errno == ENOSPC ) { 00260 error(KIO::ERR_DISK_FULL, path); 00261 return; 00262 } else { 00263 error(KIO::ERR_COULD_NOT_MKDIR, path); 00264 return; 00265 } 00266 } else { 00267 if ( permissions != -1 ) 00268 chmod( url, permissions ); 00269 else 00270 finished(); 00271 return; 00272 } 00273 } 00274 00275 if ( S_ISDIR( buff.st_mode ) ) { 00276 kDebug(7101) << "ERR_DIR_ALREADY_EXIST"; 00277 error(KIO::ERR_DIR_ALREADY_EXIST, path); 00278 return; 00279 } 00280 error(KIO::ERR_FILE_ALREADY_EXIST, path); 00281 return; 00282 } 00283 00284 void FileProtocol::get( const KUrl& url ) 00285 { 00286 if (!url.isLocalFile()) { 00287 KUrl redir(url); 00288 redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb")); 00289 redirection(redir); 00290 finished(); 00291 return; 00292 } 00293 00294 const QString path(url.toLocalFile()); 00295 KDE_struct_stat buff; 00296 if ( KDE::stat( path, &buff ) == -1 ) { 00297 if ( errno == EACCES ) 00298 error(KIO::ERR_ACCESS_DENIED, path); 00299 else 00300 error(KIO::ERR_DOES_NOT_EXIST, path); 00301 return; 00302 } 00303 00304 if ( S_ISDIR( buff.st_mode ) ) { 00305 error(KIO::ERR_IS_DIRECTORY, path); 00306 return; 00307 } 00308 if ( !S_ISREG( buff.st_mode ) ) { 00309 error(KIO::ERR_CANNOT_OPEN_FOR_READING, path); 00310 return; 00311 } 00312 00313 int fd = KDE::open( path, O_RDONLY); 00314 if ( fd < 0 ) { 00315 error(KIO::ERR_CANNOT_OPEN_FOR_READING, path); 00316 return; 00317 } 00318 00319 #if HAVE_FADVISE 00320 posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL); 00321 #endif 00322 00323 // Determine the mimetype of the file to be retrieved, and emit it. 00324 // This is mandatory in all slaves (for KRun/BrowserRun to work) 00325 // In real "remote" slaves, this is usually done using findByNameAndContent 00326 // after receiving some data. But we don't know how much data the mimemagic rules 00327 // need, so for local files, better use findByUrl with localUrl=true. 00328 KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ ); 00329 emit mimeType( mt->name() ); 00330 // Emit total size AFTER mimetype 00331 totalSize( buff.st_size ); 00332 00333 KIO::filesize_t processed_size = 0; 00334 00335 const QString resumeOffset = metaData(QLatin1String("resume")); 00336 if ( !resumeOffset.isEmpty() ) 00337 { 00338 bool ok; 00339 KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok); 00340 if (ok && (offset > 0) && (offset < buff.st_size)) 00341 { 00342 if (KDE_lseek(fd, offset, SEEK_SET) == offset) 00343 { 00344 canResume (); 00345 processed_size = offset; 00346 kDebug(7101) << "Resume offset:" << KIO::number(offset); 00347 } 00348 } 00349 } 00350 00351 char buffer[ MAX_IPC_SIZE ]; 00352 QByteArray array; 00353 00354 while( 1 ) 00355 { 00356 int n = ::read( fd, buffer, MAX_IPC_SIZE ); 00357 if (n == -1) 00358 { 00359 if (errno == EINTR) 00360 continue; 00361 error(KIO::ERR_COULD_NOT_READ, path); 00362 ::close(fd); 00363 return; 00364 } 00365 if (n == 0) 00366 break; // Finished 00367 00368 array = QByteArray::fromRawData(buffer, n); 00369 data( array ); 00370 array.clear(); 00371 00372 processed_size += n; 00373 processedSize( processed_size ); 00374 00375 //kDebug(7101) << "Processed: " << KIO::number (processed_size); 00376 } 00377 00378 data( QByteArray() ); 00379 00380 ::close( fd ); 00381 00382 processedSize( buff.st_size ); 00383 finished(); 00384 } 00385 00386 int write_all(int fd, const char *buf, size_t len) 00387 { 00388 while (len > 0) 00389 { 00390 ssize_t written = write(fd, buf, len); 00391 if (written < 0) 00392 { 00393 if (errno == EINTR) 00394 continue; 00395 return -1; 00396 } 00397 buf += written; 00398 len -= written; 00399 } 00400 return 0; 00401 } 00402 00403 void FileProtocol::open(const KUrl &url, QIODevice::OpenMode mode) 00404 { 00405 kDebug(7101) << url; 00406 00407 openPath = url.toLocalFile(); 00408 KDE_struct_stat buff; 00409 if (KDE::stat(openPath, &buff) == -1) { 00410 if ( errno == EACCES ) 00411 error(KIO::ERR_ACCESS_DENIED, openPath); 00412 else 00413 error(KIO::ERR_DOES_NOT_EXIST, openPath); 00414 return; 00415 } 00416 00417 if ( S_ISDIR( buff.st_mode ) ) { 00418 error(KIO::ERR_IS_DIRECTORY, openPath); 00419 return; 00420 } 00421 if ( !S_ISREG( buff.st_mode ) ) { 00422 error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath); 00423 return; 00424 } 00425 00426 int flags = 0; 00427 if (mode & QIODevice::ReadOnly) { 00428 if (mode & QIODevice::WriteOnly) { 00429 flags = O_RDWR | O_CREAT; 00430 } else { 00431 flags = O_RDONLY; 00432 } 00433 } else if (mode & QIODevice::WriteOnly) { 00434 flags = O_WRONLY | O_CREAT; 00435 } 00436 00437 if (mode & QIODevice::Append) { 00438 flags |= O_APPEND; 00439 } else if (mode & QIODevice::Truncate) { 00440 flags |= O_TRUNC; 00441 } 00442 00443 int fd = -1; 00444 if ( flags & O_CREAT) 00445 fd = KDE::open( openPath, flags, 0666); 00446 else 00447 fd = KDE::open( openPath, flags); 00448 if ( fd < 0 ) { 00449 error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath); 00450 return; 00451 } 00452 // Determine the mimetype of the file to be retrieved, and emit it. 00453 // This is mandatory in all slaves (for KRun/BrowserRun to work). 00454 // If we're not opening the file ReadOnly or ReadWrite, don't attempt to 00455 // read the file and send the mimetype. 00456 if (mode & QIODevice::ReadOnly){ 00457 KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ ); 00458 emit mimeType( mt->name() ); 00459 } 00460 00461 totalSize( buff.st_size ); 00462 position( 0 ); 00463 00464 emit opened(); 00465 openFd = fd; 00466 } 00467 00468 void FileProtocol::read(KIO::filesize_t bytes) 00469 { 00470 kDebug(7101) << "File::open -- read"; 00471 Q_ASSERT(openFd != -1); 00472 00473 QVarLengthArray<char> buffer(bytes); 00474 while (true) { 00475 int res; 00476 do { 00477 res = ::read(openFd, buffer.data(), bytes); 00478 } while (res == -1 && errno == EINTR); 00479 00480 if (res > 0) { 00481 QByteArray array = QByteArray::fromRawData(buffer.data(), res); 00482 data( array ); 00483 bytes -= res; 00484 } else { 00485 // empty array designates eof 00486 data(QByteArray()); 00487 if (res != 0) { 00488 error(KIO::ERR_COULD_NOT_READ, openPath); 00489 close(); 00490 } 00491 break; 00492 } 00493 if (bytes <= 0) break; 00494 } 00495 } 00496 00497 void FileProtocol::write(const QByteArray &data) 00498 { 00499 kDebug(7101) << "File::open -- write"; 00500 Q_ASSERT(openFd != -1); 00501 00502 if (write_all(openFd, data.constData(), data.size())) { 00503 if (errno == ENOSPC) { // disk full 00504 error(KIO::ERR_DISK_FULL, openPath); 00505 close(); 00506 } else { 00507 kWarning(7101) << "Couldn't write. Error:" << strerror(errno); 00508 error(KIO::ERR_COULD_NOT_WRITE, openPath); 00509 close(); 00510 } 00511 } else { 00512 written(data.size()); 00513 } 00514 } 00515 00516 void FileProtocol::seek(KIO::filesize_t offset) 00517 { 00518 kDebug(7101) << "File::open -- seek"; 00519 Q_ASSERT(openFd != -1); 00520 00521 int res = KDE_lseek(openFd, offset, SEEK_SET); 00522 if (res != -1) { 00523 position( offset ); 00524 } else { 00525 error(KIO::ERR_COULD_NOT_SEEK, openPath); 00526 close(); 00527 } 00528 } 00529 00530 void FileProtocol::close() 00531 { 00532 kDebug(7101) << "File::open -- close "; 00533 Q_ASSERT(openFd != -1); 00534 00535 ::close( openFd ); 00536 openFd = -1; 00537 openPath.clear(); 00538 00539 finished(); 00540 } 00541 00542 void FileProtocol::put( const KUrl& url, int _mode, KIO::JobFlags _flags ) 00543 { 00544 const QString dest_orig = url.toLocalFile(); 00545 00546 kDebug(7101) << dest_orig << "mode=" << _mode; 00547 00548 QString dest_part(dest_orig + QLatin1String(".part")); 00549 00550 KDE_struct_stat buff_orig; 00551 const bool bOrigExists = (KDE::lstat(dest_orig, &buff_orig) != -1); 00552 bool bPartExists = false; 00553 const bool bMarkPartial = config()->readEntry("MarkPartial", true); 00554 00555 if (bMarkPartial) 00556 { 00557 KDE_struct_stat buff_part; 00558 bPartExists = (KDE::stat( dest_part, &buff_part ) != -1); 00559 00560 if (bPartExists && !(_flags & KIO::Resume) && !(_flags & KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode)) 00561 { 00562 kDebug(7101) << "calling canResume with" << KIO::number(buff_part.st_size); 00563 00564 // Maybe we can use this partial file for resuming 00565 // Tell about the size we have, and the app will tell us 00566 // if it's ok to resume or not. 00567 _flags |= canResume( buff_part.st_size ) ? KIO::Resume : KIO::DefaultFlags; 00568 00569 kDebug(7101) << "got answer" << (_flags & KIO::Resume); 00570 } 00571 } 00572 00573 if ( bOrigExists && !(_flags & KIO::Overwrite) && !(_flags & KIO::Resume)) 00574 { 00575 if (S_ISDIR(buff_orig.st_mode)) 00576 error( KIO::ERR_DIR_ALREADY_EXIST, dest_orig ); 00577 else 00578 error( KIO::ERR_FILE_ALREADY_EXIST, dest_orig ); 00579 return; 00580 } 00581 00582 int result; 00583 QString dest; 00584 QByteArray _dest; 00585 00586 int fd = -1; 00587 00588 // Loop until we got 0 (end of data) 00589 do 00590 { 00591 QByteArray buffer; 00592 dataReq(); // Request for data 00593 result = readData( buffer ); 00594 00595 if (result >= 0) 00596 { 00597 if (dest.isEmpty()) 00598 { 00599 if (bMarkPartial) 00600 { 00601 kDebug(7101) << "Appending .part extension to" << dest_orig; 00602 dest = dest_part; 00603 if ( bPartExists && !(_flags & KIO::Resume) ) 00604 { 00605 kDebug(7101) << "Deleting partial file" << dest_part; 00606 QFile::remove( dest_part ); 00607 // Catch errors when we try to open the file. 00608 } 00609 } 00610 else 00611 { 00612 dest = dest_orig; 00613 if ( bOrigExists && !(_flags & KIO::Resume) ) 00614 { 00615 kDebug(7101) << "Deleting destination file" << dest_orig; 00616 QFile::remove( dest_orig ); 00617 // Catch errors when we try to open the file. 00618 } 00619 } 00620 00621 if ( (_flags & KIO::Resume) ) 00622 { 00623 fd = KDE::open( dest, O_RDWR ); // append if resuming 00624 KDE_lseek(fd, 0, SEEK_END); // Seek to end 00625 } 00626 else 00627 { 00628 // WABA: Make sure that we keep writing permissions ourselves, 00629 // otherwise we can be in for a surprise on NFS. 00630 mode_t initialMode; 00631 if (_mode != -1) 00632 initialMode = _mode | S_IWUSR | S_IRUSR; 00633 else 00634 initialMode = 0666; 00635 00636 fd = KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode); 00637 } 00638 00639 if ( fd < 0 ) 00640 { 00641 kDebug(7101) << "####################### COULD NOT WRITE" << dest << "_mode=" << _mode; 00642 kDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")"; 00643 if ( errno == EACCES ) 00644 error(KIO::ERR_WRITE_ACCESS_DENIED, dest); 00645 else 00646 error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest); 00647 return; 00648 } 00649 } 00650 00651 if (write_all( fd, buffer.data(), buffer.size())) 00652 { 00653 if ( errno == ENOSPC ) // disk full 00654 { 00655 error(KIO::ERR_DISK_FULL, dest_orig); 00656 result = -2; // means: remove dest file 00657 } 00658 else 00659 { 00660 kWarning(7101) << "Couldn't write. Error:" << strerror(errno); 00661 error(KIO::ERR_COULD_NOT_WRITE, dest_orig); 00662 result = -1; 00663 } 00664 } 00665 } 00666 } 00667 while ( result > 0 ); 00668 00669 // An error occurred deal with it. 00670 if (result < 0) 00671 { 00672 kDebug(7101) << "Error during 'put'. Aborting."; 00673 00674 if (fd != -1) 00675 { 00676 ::close(fd); 00677 00678 KDE_struct_stat buff; 00679 if (bMarkPartial && KDE::stat( dest, &buff ) == 0) 00680 { 00681 int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE); 00682 if (buff.st_size < size) 00683 remove(_dest.data()); 00684 } 00685 } 00686 00687 ::exit(255); 00688 } 00689 00690 if ( fd == -1 ) // we got nothing to write out, so we never opened the file 00691 { 00692 finished(); 00693 return; 00694 } 00695 00696 if ( ::close(fd) ) 00697 { 00698 kWarning(7101) << "Error when closing file descriptor:" << strerror(errno); 00699 error(KIO::ERR_COULD_NOT_WRITE, dest_orig); 00700 return; 00701 } 00702 00703 // after full download rename the file back to original name 00704 if ( bMarkPartial ) 00705 { 00706 // If the original URL is a symlink and we were asked to overwrite it, 00707 // remove the symlink first. This ensures that we do not overwrite the 00708 // current source if the symlink points to it. 00709 if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) ) 00710 QFile::remove( dest_orig ); 00711 if ( KDE::rename( dest, dest_orig ) ) 00712 { 00713 kWarning(7101) << " Couldn't rename " << _dest << " to " << dest_orig; 00714 error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig); 00715 return; 00716 } 00717 org::kde::KDirNotify::emitFileRenamed(dest, dest_orig); 00718 } 00719 00720 // set final permissions 00721 if ( _mode != -1 && !(_flags & KIO::Resume) ) 00722 { 00723 if (KDE::chmod(dest_orig, _mode) != 0) 00724 { 00725 // couldn't chmod. Eat the error if the filesystem apparently doesn't support it. 00726 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest_orig); 00727 if (mp && mp->testFileSystemFlag(KMountPoint::SupportsChmod)) 00728 warning( i18n( "Could not change permissions for\n%1" , dest_orig ) ); 00729 } 00730 } 00731 00732 // set modification time 00733 const QString mtimeStr = metaData(QLatin1String("modified")); 00734 if ( !mtimeStr.isEmpty() ) { 00735 QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate ); 00736 if ( dt.isValid() ) { 00737 KDE_struct_stat dest_statbuf; 00738 if (KDE::stat( dest_orig, &dest_statbuf ) == 0) { 00739 struct timeval utbuf[2]; 00740 // access time 00741 utbuf[0].tv_sec = dest_statbuf.st_atime; // access time, unchanged ## TODO preserve msec 00742 utbuf[0].tv_usec = 0; 00743 // modification time 00744 utbuf[1].tv_sec = dt.toTime_t(); 00745 utbuf[1].tv_usec = dt.time().msec() * 1000; 00746 utimes( QFile::encodeName(dest_orig), utbuf ); 00747 } 00748 } 00749 00750 } 00751 00752 // We have done our job => finish 00753 finished(); 00754 } 00755 00756 QString FileProtocol::getUserName( uid_t uid ) const 00757 { 00758 if ( !mUsercache.contains( uid ) ) { 00759 struct passwd *user = getpwuid( uid ); 00760 if ( user ) { 00761 mUsercache.insert( uid, QString::fromLatin1(user->pw_name) ); 00762 } 00763 else 00764 return QString::number( uid ); 00765 } 00766 return mUsercache[uid]; 00767 } 00768 00769 QString FileProtocol::getGroupName( gid_t gid ) const 00770 { 00771 if ( !mGroupcache.contains( gid ) ) { 00772 struct group *grp = getgrgid( gid ); 00773 if ( grp ) { 00774 mGroupcache.insert( gid, QString::fromLatin1(grp->gr_name) ); 00775 } 00776 else 00777 return QString::number( gid ); 00778 } 00779 return mGroupcache[gid]; 00780 } 00781 00782 bool FileProtocol::createUDSEntry( const QString & filename, const QByteArray & path, UDSEntry & entry, 00783 short int details, bool withACL ) 00784 { 00785 #ifndef HAVE_POSIX_ACL 00786 Q_UNUSED(withACL); 00787 #endif 00788 assert(entry.count() == 0); // by contract :-) 00789 // entry.reserve( 8 ); // speed up QHash insertion 00790 00791 entry.insert( KIO::UDSEntry::UDS_NAME, filename ); 00792 00793 mode_t type; 00794 mode_t access; 00795 KDE_struct_stat buff; 00796 00797 if ( KDE_lstat( path.data(), &buff ) == 0 ) { 00798 00799 if (S_ISLNK(buff.st_mode)) { 00800 00801 char buffer2[ 1000 ]; 00802 int n = readlink( path.data(), buffer2, 999 ); 00803 if ( n != -1 ) { 00804 buffer2[ n ] = 0; 00805 } 00806 00807 entry.insert( KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName( buffer2 ) ); 00808 00809 // A symlink -> follow it only if details>1 00810 if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) { 00811 // It is a link pointing to nowhere 00812 type = S_IFMT - 1; 00813 access = S_IRWXU | S_IRWXG | S_IRWXO; 00814 00815 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type ); 00816 entry.insert( KIO::UDSEntry::UDS_ACCESS, access ); 00817 entry.insert( KIO::UDSEntry::UDS_SIZE, 0LL ); 00818 goto notype; 00819 00820 } 00821 } 00822 } else { 00823 // kWarning() << "lstat didn't work on " << path.data(); 00824 return false; 00825 } 00826 00827 type = buff.st_mode & S_IFMT; // extract file type 00828 access = buff.st_mode & 07777; // extract permissions 00829 00830 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type ); 00831 entry.insert( KIO::UDSEntry::UDS_ACCESS, access ); 00832 00833 entry.insert( KIO::UDSEntry::UDS_SIZE, buff.st_size ); 00834 00835 #ifdef HAVE_POSIX_ACL 00836 if (details > 0) { 00837 /* Append an atom indicating whether the file has extended acl information 00838 * and if withACL is specified also one with the acl itself. If it's a directory 00839 * and it has a default ACL, also append that. */ 00840 appendACLAtoms( path, entry, type, withACL ); 00841 } 00842 #endif 00843 00844 notype: 00845 if (details > 0) { 00846 entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime ); 00847 entry.insert( KIO::UDSEntry::UDS_USER, getUserName( buff.st_uid ) ); 00848 entry.insert( KIO::UDSEntry::UDS_GROUP, getGroupName( buff.st_gid ) ); 00849 entry.insert( KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime ); 00850 } 00851 00852 // Note: buff.st_ctime isn't the creation time ! 00853 // We made that mistake for KDE 2.0, but it's in fact the 00854 // "file status" change time, which we don't care about. 00855 00856 return true; 00857 } 00858 00859 void FileProtocol::special( const QByteArray &data) 00860 { 00861 int tmp; 00862 QDataStream stream(data); 00863 00864 stream >> tmp; 00865 switch (tmp) { 00866 case 1: 00867 { 00868 QString fstype, dev, point; 00869 qint8 iRo; 00870 00871 stream >> iRo >> fstype >> dev >> point; 00872 00873 bool ro = ( iRo != 0 ); 00874 00875 kDebug(7101) << "MOUNTING fstype=" << fstype << " dev=" << dev << " point=" << point << " ro=" << ro; 00876 bool ok = pmount( dev ); 00877 if (ok) 00878 finished(); 00879 else 00880 mount( ro, fstype.toAscii(), dev, point ); 00881 00882 } 00883 break; 00884 case 2: 00885 { 00886 QString point; 00887 stream >> point; 00888 bool ok = pumount( point ); 00889 if (ok) 00890 finished(); 00891 else 00892 unmount( point ); 00893 } 00894 break; 00895 00896 default: 00897 break; 00898 } 00899 } 00900 00901 void FileProtocol::mount( bool _ro, const char *_fstype, const QString& _dev, const QString& _point ) 00902 { 00903 kDebug(7101) << "fstype=" << _fstype; 00904 00905 #ifndef _WIN32_WCE 00906 #ifdef HAVE_VOLMGT 00907 /* 00908 * support for Solaris volume management 00909 */ 00910 QString err; 00911 QByteArray devname = QFile::encodeName( _dev ); 00912 00913 if( volmgt_running() ) { 00914 // kDebug(7101) << "VOLMGT: vold ok."; 00915 if( volmgt_check( devname.data() ) == 0 ) { 00916 kDebug(7101) << "VOLMGT: no media in " 00917 << devname.data(); 00918 err = i18n("No Media inserted or Media not recognized."); 00919 error( KIO::ERR_COULD_NOT_MOUNT, err ); 00920 return; 00921 } else { 00922 kDebug(7101) << "VOLMGT: " << devname.data() 00923 << ": media ok"; 00924 finished(); 00925 return; 00926 } 00927 } else { 00928 err = i18n("\"vold\" is not running."); 00929 kDebug(7101) << "VOLMGT: " << err; 00930 error( KIO::ERR_COULD_NOT_MOUNT, err ); 00931 return; 00932 } 00933 #else 00934 00935 00936 KTemporaryFile tmpFile; 00937 tmpFile.setAutoRemove(false); 00938 tmpFile.open(); 00939 QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName()); 00940 QByteArray dev; 00941 if (_dev.startsWith(QLatin1String("LABEL="))) { // turn LABEL=foo into -L foo (#71430) 00942 QString labelName = _dev.mid( 6 ); 00943 dev = "-L "; 00944 dev += QFile::encodeName( KShell::quoteArg( labelName ) ); // is it correct to assume same encoding as filesystem? 00945 } else if (_dev.startsWith(QLatin1String("UUID="))) { // and UUID=bar into -U bar 00946 QString uuidName = _dev.mid( 5 ); 00947 dev = "-U "; 00948 dev += QFile::encodeName( KShell::quoteArg( uuidName ) ); 00949 } 00950 else 00951 dev = QFile::encodeName( KShell::quoteArg(_dev) ); // get those ready to be given to a shell 00952 00953 QByteArray point = QFile::encodeName( KShell::quoteArg(_point) ); 00954 bool fstype_empty = !_fstype || !*_fstype; 00955 QByteArray fstype = KShell::quoteArg(QString::fromLatin1(_fstype)).toLatin1(); // good guess 00956 QByteArray readonly = _ro ? "-r" : ""; 00957 QString epath = QString::fromLocal8Bit(qgetenv("PATH")); 00958 QString path = QLatin1String("/sbin:/bin"); 00959 if(!epath.isEmpty()) 00960 path += QLatin1String(":") + epath; 00961 QByteArray mountProg = KGlobal::dirs()->findExe(QLatin1String("mount"), path).toLocal8Bit(); 00962 if (mountProg.isEmpty()){ 00963 error( KIO::ERR_COULD_NOT_MOUNT, i18n("Could not find program \"mount\"")); 00964 return; 00965 } 00966 00967 // Two steps, in case mount doesn't like it when we pass all options 00968 for ( int step = 0 ; step <= 1 ; step++ ) 00969 { 00970 QByteArray buffer = mountProg + ' '; 00971 // Mount using device only if no fstype nor mountpoint (KDE-1.x like) 00972 if ( !dev.isEmpty() && _point.isEmpty() && fstype_empty ) 00973 buffer += dev; 00974 else 00975 // Mount using the mountpoint, if no fstype nor device (impossible in first step) 00976 if ( !_point.isEmpty() && dev.isEmpty() && fstype_empty ) 00977 buffer += point; 00978 else 00979 // mount giving device + mountpoint but no fstype 00980 if ( !_point.isEmpty() && !dev.isEmpty() && fstype_empty ) 00981 buffer += readonly + ' ' + dev + ' ' + point; 00982 else 00983 // mount giving device + mountpoint + fstype 00984 #if defined(__svr4__) && defined(Q_OS_SOLARIS) // MARCO for Solaris 8 and I 00985 // believe this is true for SVR4 in general 00986 buffer += "-F " + fstype + ' ' + (_ro ? "-oro" : "") + ' ' + dev + ' ' + point; 00987 #else 00988 buffer += readonly + " -t " + fstype + ' ' + dev + ' ' + point; 00989 #endif 00990 buffer += " 2>" + tmpFileName; 00991 kDebug(7101) << buffer; 00992 00993 int mount_ret = system( buffer.constData() ); 00994 00995 QString err = testLogFile( tmpFileName ); 00996 if ( err.isEmpty() && mount_ret == 0) 00997 { 00998 finished(); 00999 return; 01000 } 01001 else 01002 { 01003 // Didn't work - or maybe we just got a warning 01004 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( _dev ); 01005 // Is the device mounted ? 01006 if ( mp && mount_ret == 0) 01007 { 01008 kDebug(7101) << "mount got a warning:" << err; 01009 warning( err ); 01010 finished(); 01011 return; 01012 } 01013 else 01014 { 01015 if ( (step == 0) && !_point.isEmpty()) 01016 { 01017 kDebug(7101) << err; 01018 kDebug(7101) << "Mounting with those options didn't work, trying with only mountpoint"; 01019 fstype = ""; 01020 fstype_empty = true; 01021 dev = ""; 01022 // The reason for trying with only mountpoint (instead of 01023 // only device) is that some people (hi Malte!) have the 01024 // same device associated with two mountpoints 01025 // for different fstypes, like /dev/fd0 /mnt/e2floppy and 01026 // /dev/fd0 /mnt/dosfloppy. 01027 // If the user has the same mountpoint associated with two 01028 // different devices, well they shouldn't specify the 01029 // mountpoint but just the device. 01030 } 01031 else 01032 { 01033 error( KIO::ERR_COULD_NOT_MOUNT, err ); 01034 return; 01035 } 01036 } 01037 } 01038 } 01039 #endif /* ! HAVE_VOLMGT */ 01040 #else 01041 QString err; 01042 err = i18n("mounting is not supported by wince."); 01043 error( KIO::ERR_COULD_NOT_MOUNT, err ); 01044 #endif 01045 01046 } 01047 01048 01049 void FileProtocol::unmount( const QString& _point ) 01050 { 01051 #ifndef _WIN32_WCE 01052 QByteArray buffer; 01053 01054 KTemporaryFile tmpFile; 01055 tmpFile.setAutoRemove(false); 01056 tmpFile.open(); 01057 QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName()); 01058 QString err; 01059 01060 #ifdef HAVE_VOLMGT 01061 /* 01062 * support for Solaris volume management 01063 */ 01064 char *devname; 01065 char *ptr; 01066 FILE *mnttab; 01067 struct mnttab mnt; 01068 01069 if( volmgt_running() ) { 01070 kDebug(7101) << "VOLMGT: looking for " 01071 << _point.toLocal8Bit(); 01072 01073 if( (mnttab = KDE_fopen( MNTTAB, "r" )) == NULL ) { 01074 err = QLatin1String("could not open mnttab"); 01075 kDebug(7101) << "VOLMGT: " << err; 01076 error( KIO::ERR_COULD_NOT_UNMOUNT, err ); 01077 return; 01078 } 01079 01080 /* 01081 * since there's no way to derive the device name from 01082 * the mount point through the volmgt library (and 01083 * media_findname() won't work in this case), we have to 01084 * look ourselves... 01085 */ 01086 devname = NULL; 01087 rewind( mnttab ); 01088 while( getmntent( mnttab, &mnt ) == 0 ) { 01089 if( strcmp( _point.toLocal8Bit(), mnt.mnt_mountp ) == 0 ){ 01090 devname = mnt.mnt_special; 01091 break; 01092 } 01093 } 01094 fclose( mnttab ); 01095 01096 if( devname == NULL ) { 01097 err = QLatin1String("not in mnttab"); 01098 kDebug(7101) << "VOLMGT: " 01099 << QFile::encodeName(_point).data() 01100 << ": " << err; 01101 error( KIO::ERR_COULD_NOT_UNMOUNT, err ); 01102 return; 01103 } 01104 01105 /* 01106 * strip off the directory name (volume name) 01107 * the eject(1) command will handle unmounting and 01108 * physically eject the media (if possible) 01109 */ 01110 ptr = strrchr( devname, '/' ); 01111 *ptr = '\0'; 01112 QByteArray qdevname(QFile::encodeName(KShell::quoteArg(QFile::decodeName(QByteArray(devname)))).data()); 01113 buffer = "/usr/bin/eject " + qdevname + " 2>" + tmpFileName; 01114 kDebug(7101) << "VOLMGT: eject " << qdevname; 01115 01116 /* 01117 * from eject(1): exit status == 0 => need to manually eject 01118 * exit status == 4 => media was ejected 01119 */ 01120 if( WEXITSTATUS( system( buffer.constData() )) == 4 ) { 01121 /* 01122 * this is not an error, so skip "testLogFile()" 01123 * to avoid wrong/confusing error popup. The 01124 * temporary file is removed by KTemporaryFile's 01125 * destructor, so don't do that manually. 01126 */ 01127 finished(); 01128 return; 01129 } 01130 } else { 01131 /* 01132 * eject(1) should do its job without vold(1M) running, 01133 * so we probably could call eject anyway, but since the 01134 * media is mounted now, vold must've died for some reason 01135 * during the user's session, so it should be restarted... 01136 */ 01137 err = i18n("\"vold\" is not running."); 01138 kDebug(7101) << "VOLMGT: " << err; 01139 error( KIO::ERR_COULD_NOT_UNMOUNT, err ); 01140 return; 01141 } 01142 #else 01143 QString epath = QString::fromLocal8Bit(qgetenv("PATH")); 01144 QString path = QLatin1String("/sbin:/bin"); 01145 if (!epath.isEmpty()) 01146 path += QLatin1Char(':') + epath; 01147 QByteArray umountProg = KGlobal::dirs()->findExe(QLatin1String("umount"), path).toLocal8Bit(); 01148 01149 if (umountProg.isEmpty()) { 01150 error( KIO::ERR_COULD_NOT_UNMOUNT, i18n("Could not find program \"umount\"")); 01151 return; 01152 } 01153 buffer = umountProg + ' ' + QFile::encodeName(KShell::quoteArg(_point)) + " 2>" + tmpFileName; 01154 system( buffer.constData() ); 01155 #endif /* HAVE_VOLMGT */ 01156 01157 err = testLogFile( tmpFileName ); 01158 if ( err.isEmpty() ) 01159 finished(); 01160 else 01161 error( KIO::ERR_COULD_NOT_UNMOUNT, err ); 01162 #else 01163 QString err; 01164 err = i18n("unmounting is not supported by wince."); 01165 error( KIO::ERR_COULD_NOT_MOUNT, err ); 01166 #endif 01167 } 01168 01169 /************************************* 01170 * 01171 * pmount handling 01172 * 01173 *************************************/ 01174 01175 bool FileProtocol::pmount(const QString &dev) 01176 { 01177 #ifndef _WIN32_WCE 01178 QString epath = QString::fromLocal8Bit(qgetenv("PATH")); 01179 QString path = QLatin1String("/sbin:/bin"); 01180 if (!epath.isEmpty()) 01181 path += QLatin1Char(':') + epath; 01182 QString pmountProg = KGlobal::dirs()->findExe(QLatin1String("pmount"), path); 01183 01184 if (pmountProg.isEmpty()) 01185 return false; 01186 01187 QByteArray buffer = QFile::encodeName(pmountProg) + ' ' + 01188 QFile::encodeName(KShell::quoteArg(dev)); 01189 01190 int res = system( buffer.constData() ); 01191 01192 return res==0; 01193 #else 01194 return false; 01195 #endif 01196 } 01197 01198 bool FileProtocol::pumount(const QString &point) 01199 { 01200 #ifndef _WIN32_WCE 01201 KMountPoint::Ptr mp = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName).findByPath(point); 01202 if (!mp) 01203 return false; 01204 QString dev = mp->realDeviceName(); 01205 if (dev.isEmpty()) return false; 01206 01207 QString epath = QString::fromLocal8Bit(qgetenv("PATH")); 01208 QString path = QLatin1String("/sbin:/bin"); 01209 if (!epath.isEmpty()) 01210 path += QLatin1Char(':') + epath; 01211 QString pumountProg = KGlobal::dirs()->findExe(QLatin1String("pumount"), path); 01212 01213 if (pumountProg.isEmpty()) 01214 return false; 01215 01216 QByteArray buffer = QFile::encodeName(pumountProg); 01217 buffer += ' '; 01218 buffer += QFile::encodeName(KShell::quoteArg(dev)); 01219 01220 int res = system( buffer.data() ); 01221 01222 return res==0; 01223 #else 01224 return false; 01225 #endif 01226 } 01227 01228 /************************************* 01229 * 01230 * Utilities 01231 * 01232 *************************************/ 01233 01234 static QString testLogFile( const QByteArray& _filename ) 01235 { 01236 char buffer[ 1024 ]; 01237 KDE_struct_stat buff; 01238 01239 QString result; 01240 01241 KDE_stat( _filename, &buff ); 01242 int size = buff.st_size; 01243 if ( size == 0 ) { 01244 unlink( _filename ); 01245 return result; 01246 } 01247 01248 FILE * f = KDE_fopen( _filename, "rb" ); 01249 if ( f == 0L ) { 01250 unlink( _filename ); 01251 result = i18n("Could not read %1", QFile::decodeName(_filename)); 01252 return result; 01253 } 01254 01255 result.clear(); 01256 const char *p = ""; 01257 while ( p != 0L ) { 01258 p = fgets( buffer, sizeof(buffer)-1, f ); 01259 if ( p != 0L ) 01260 result += QString::fromLocal8Bit(buffer); 01261 } 01262 01263 fclose( f ); 01264 01265 unlink( _filename ); 01266 01267 return result; 01268 } 01269 01270 /************************************* 01271 * 01272 * ACL handling helpers 01273 * 01274 *************************************/ 01275 #ifdef HAVE_POSIX_ACL 01276 01277 static bool isExtendedACL( acl_t acl ) 01278 { 01279 return ( acl_equiv_mode( acl, 0 ) != 0 ); 01280 } 01281 01282 static void appendACLAtoms( const QByteArray & path, UDSEntry& entry, mode_t type, bool withACL ) 01283 { 01284 // first check for a noop 01285 if ( acl_extended_file( path.data() ) == 0 ) return; 01286 01287 acl_t acl = 0; 01288 acl_t defaultAcl = 0; 01289 bool isDir = S_ISDIR( type ); 01290 // do we have an acl for the file, and/or a default acl for the dir, if it is one? 01291 acl = acl_get_file( path.data(), ACL_TYPE_ACCESS ); 01292 /* Sadly libacl does not provided a means of checking for extended ACL and default 01293 * ACL separately. Since a directory can have both, we need to check again. */ 01294 if ( isDir ) { 01295 if ( acl ) { 01296 if ( !isExtendedACL( acl ) ) { 01297 acl_free( acl ); 01298 acl = 0; 01299 } 01300 } 01301 defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT ); 01302 } 01303 if ( acl || defaultAcl ) { 01304 kDebug(7101) << path.constData() << "has extended ACL entries"; 01305 entry.insert( KIO::UDSEntry::UDS_EXTENDED_ACL, 1 ); 01306 } 01307 if ( withACL ) { 01308 if ( acl ) { 01309 const QString str = aclToText(acl); 01310 entry.insert( KIO::UDSEntry::UDS_ACL_STRING, str ); 01311 kDebug(7101) << path.constData() << "ACL:" << str; 01312 } 01313 if ( defaultAcl ) { 01314 const QString str = aclToText(defaultAcl); 01315 entry.insert( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, str ); 01316 kDebug(7101) << path.constData() << "DEFAULT ACL:" << str; 01317 } 01318 } 01319 if ( acl ) acl_free( acl ); 01320 if ( defaultAcl ) acl_free( defaultAcl ); 01321 } 01322 #endif 01323 01324 // We could port this to KTempDir::removeDir but then we wouldn't be able to tell the user 01325 // where exactly the deletion failed, in case of errors. 01326 bool FileProtocol::deleteRecursive(const QString& path) 01327 { 01328 //kDebug() << path; 01329 QDirIterator it(path, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden, 01330 QDirIterator::Subdirectories); 01331 QStringList dirsToDelete; 01332 while ( it.hasNext() ) { 01333 const QString itemPath = it.next(); 01334 //kDebug() << "itemPath=" << itemPath; 01335 const QFileInfo info = it.fileInfo(); 01336 if (info.isDir() && !info.isSymLink()) 01337 dirsToDelete.prepend(itemPath); 01338 else { 01339 //kDebug() << "QFile::remove" << itemPath; 01340 if (!QFile::remove(itemPath)) { 01341 error(KIO::ERR_CANNOT_DELETE, itemPath); 01342 return false; 01343 } 01344 } 01345 } 01346 QDir dir; 01347 Q_FOREACH(const QString& itemPath, dirsToDelete) { 01348 //kDebug() << "QDir::rmdir" << itemPath; 01349 if (!dir.rmdir(itemPath)) { 01350 error(KIO::ERR_CANNOT_DELETE, itemPath); 01351 return false; 01352 } 01353 } 01354 return true; 01355 } 01356 01357 #include "file.moc"
KDE 4.7 API Reference