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