• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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 #include "file.h"
00026 
00027 #include <config.h>
00028 #include <config-acl.h>
00029 
00030 #include <QtCore/QBool> //for Q_OS_XXX
00031 #include <sys/types.h>
00032 #include <sys/wait.h>
00033 #include <sys/stat.h>
00034 #include <sys/socket.h>
00035 #ifdef HAVE_SYS_TIME_H
00036 #include <sys/time.h>
00037 #endif
00038 
00039 //sendfile has different semantics in different platforms
00040 #if defined HAVE_SENDFILE && defined Q_OS_LINUX
00041 #define USE_SENDFILE 1
00042 #endif
00043 
00044 #ifdef USE_SENDFILE
00045 #include <sys/sendfile.h>
00046 #endif
00047 
00048 #ifdef HAVE_POSIX_ACL
00049 #include <sys/acl.h>
00050 #include <acl/libacl.h>
00051 #endif
00052 
00053 #include <assert.h>
00054 #include <dirent.h>
00055 #include <errno.h>
00056 #include <fcntl.h>
00057 #include <grp.h>
00058 #include <pwd.h>
00059 #include <stdio.h>
00060 #include <stdlib.h>
00061 #include <signal.h>
00062 #include <time.h>
00063 #include <utime.h>
00064 #include <unistd.h>
00065 #ifdef HAVE_STRING_H
00066 #include <string.h>
00067 #endif
00068 
00069 #include <QtCore/QByteRef>
00070 #include <QtCore/QDate>
00071 #include <QtCore/QVarLengthArray>
00072 #include <QtCore/QCoreApplication>
00073 #include <QtCore/QRegExp>
00074 #include <QtCore/QFile>
00075 #ifdef Q_WS_WIN
00076 #include <QtCore/QDir>
00077 #include <QtCore/QFileInfo>
00078 #endif
00079 
00080 #include <kdebug.h>
00081 #include <kurl.h>
00082 #include <kcomponentdata.h>
00083 #include <kconfig.h>
00084 #include <kconfiggroup.h>
00085 #include <ktemporaryfile.h>
00086 #include <klocale.h>
00087 #include <limits.h>
00088 #include <kshell.h>
00089 #include <kmountpoint.h>
00090 #include <kstandarddirs.h>
00091 
00092 #ifdef HAVE_VOLMGT
00093 #include <volmgt.h>
00094 #include <sys/mnttab.h>
00095 #endif
00096 
00097 #include <kio/ioslave_defaults.h>
00098 #include <kde_file.h>
00099 #include <kglobal.h>
00100 #include <kmimetype.h>
00101 
00102 using namespace KIO;
00103 
00104 #define MAX_IPC_SIZE (1024*32)
00105 
00106 static QString testLogFile( const QByteArray&_filename );
00107 #ifdef HAVE_POSIX_ACL
00108 static bool isExtendedACL(  acl_t p_acl );
00109 static void appendACLAtoms( const QByteArray & path, UDSEntry& entry,
00110                             mode_t type, bool withACL );
00111 #endif
00112 
00113 extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
00114 {
00115   QCoreApplication app( argc, argv ); // needed for QSocketNotifier
00116   KComponentData componentData( "kio_file", "kdelibs4" );
00117   ( void ) KGlobal::locale();
00118 
00119   kDebug(7101) << "Starting " << getpid();
00120 
00121   if (argc != 4)
00122   {
00123      fprintf(stderr, "Usage: kio_file protocol domain-socket1 domain-socket2\n");
00124      exit(-1);
00125   }
00126 
00127   FileProtocol slave(argv[2], argv[3]);
00128   slave.dispatchLoop();
00129 
00130   kDebug(7101) << "Done";
00131   return 0;
00132 }
00133 
00134 FileProtocol::FileProtocol( const QByteArray &pool, const QByteArray &app )
00135     : SlaveBase( "file", pool, app ), openFd(-1)
00136 {
00137 }
00138 
00139 FileProtocol::~FileProtocol()
00140 {
00141 }
00142 
00143 int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault )
00144 {
00145     int ret = 0;
00146 #ifdef HAVE_POSIX_ACL
00147 
00148     const QString ACLString = metaData( "ACL_STRING" );
00149     const QString defaultACLString = metaData( "DEFAULT_ACL_STRING" );
00150     // Empty strings mean leave as is
00151     if ( !ACLString.isEmpty() ) {
00152         acl_t acl = 0;
00153         if ( ACLString == "ACL_DELETE" ) {
00154             // user told us to delete the extended ACL, so let's write only
00155             // the minimal (UNIX permission bits) part
00156             acl = acl_from_mode( perm );
00157         }
00158         acl = acl_from_text( ACLString.toLatin1() );
00159         if ( acl_valid( acl ) == 0 ) { // let's be safe
00160             ret = acl_set_file( path, ACL_TYPE_ACCESS, acl );
00161             ssize_t size = acl_size( acl );
00162             kDebug(7101) << "Set ACL on: " << path << " to: " << acl_to_text( acl, &size );
00163         }
00164         acl_free( acl );
00165         if ( ret != 0 ) return ret; // better stop trying right away
00166     }
00167 
00168     if ( directoryDefault && !defaultACLString.isEmpty() ) {
00169         if ( defaultACLString == "ACL_DELETE" ) {
00170             // user told us to delete the default ACL, do so
00171             ret += acl_delete_def_file( path );
00172         } else {
00173             acl_t acl = acl_from_text( defaultACLString.toLatin1() );
00174             if ( acl_valid( acl ) == 0 ) { // let's be safe
00175                 ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
00176                 ssize_t size = acl_size( acl );
00177                 kDebug(7101) << "Set Default ACL on: " << path << " to: " << acl_to_text( acl, &size );
00178             }
00179             acl_free( acl );
00180         }
00181     }
00182 #else
00183     Q_UNUSED(path);
00184     Q_UNUSED(perm);
00185     Q_UNUSED(directoryDefault);
00186 #endif
00187     return ret;
00188 }
00189 
00190 void FileProtocol::chmod( const KUrl& url, int permissions )
00191 {
00192     QByteArray _path( QFile::encodeName(url.toLocalFile()) );
00193     /* FIXME: Should be atomic */
00194     if ( ::chmod( _path.data(), permissions ) == -1 ||
00195         ( setACL( _path.data(), permissions, false ) == -1 ) ||
00196         /* if not a directory, cannot set default ACLs */
00197         ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
00198 
00199         switch (errno) {
00200             case EPERM:
00201             case EACCES:
00202                 error( KIO::ERR_ACCESS_DENIED, _path );
00203                 break;
00204 #if defined(ENOTSUP)
00205             case ENOTSUP: // from setACL since chmod can't return ENOTSUP
00206                 error( KIO::ERR_UNSUPPORTED_ACTION, i18n( "Setting ACL for %1" ,  url.path() ) );
00207                 break;
00208 #endif
00209             case ENOSPC:
00210                 error( KIO::ERR_DISK_FULL, _path );
00211                 break;
00212             default:
00213         error( KIO::ERR_CANNOT_CHMOD, _path );
00214         }
00215     } else
00216         finished();
00217 }
00218 
00219 void FileProtocol::chown( const KUrl& url, const QString& owner, const QString& group )
00220 {
00221     QByteArray _path( QFile::encodeName(url.toLocalFile()) );
00222 #ifdef Q_WS_WIN
00223     error( KIO::ERR_CANNOT_CHOWN, _path );
00224 #else
00225 
00226     uid_t uid;
00227     gid_t gid;
00228 
00229     // get uid from given owner
00230     {
00231         struct passwd *p = ::getpwnam(owner.toAscii());
00232 
00233         if ( ! p ) {
00234             error( KIO::ERR_SLAVE_DEFINED,
00235                    i18n( "Could not get user id for given user name %1", owner ) );
00236             return;
00237         }
00238 
00239         uid = p->pw_uid;
00240     }
00241 
00242     // get gid from given group
00243     {
00244         struct group *p = ::getgrnam(group.toAscii());
00245 
00246         if ( ! p ) {
00247             error( KIO::ERR_SLAVE_DEFINED,
00248                    i18n( "Could not get group id for given group name %1", group ) );
00249             return;
00250         }
00251 
00252         gid = p->gr_gid;
00253     }
00254 
00255     if ( ::chown(_path, uid, gid) == -1 ) {
00256         switch ( errno ) {
00257             case EPERM:
00258             case EACCES:
00259                 error( KIO::ERR_ACCESS_DENIED, _path );
00260                 break;
00261             case ENOSPC:
00262                 error( KIO::ERR_DISK_FULL, _path );
00263                 break;
00264             default:
00265                 error( KIO::ERR_CANNOT_CHOWN, _path );
00266         }
00267     } else
00268         finished();
00269 #endif
00270 }
00271 
00272 void FileProtocol::setModificationTime( const KUrl& url, const QDateTime& mtime )
00273 {
00274     const QByteArray path = QFile::encodeName(url.toLocalFile());
00275     KDE_struct_stat statbuf;
00276     if (KDE_lstat(path, &statbuf) == 0) {
00277         struct utimbuf utbuf;
00278         utbuf.actime = statbuf.st_atime; // access time, unchanged
00279         utbuf.modtime = mtime.toTime_t(); // modification time
00280         if (utime(path, &utbuf) != 0) {
00281             // TODO: errno could be EACCES, EPERM, EROFS
00282             error(KIO::ERR_CANNOT_SETTIME, path);
00283         } else {
00284             finished();
00285         }
00286     } else {
00287         error( KIO::ERR_DOES_NOT_EXIST, path );
00288     }
00289 }
00290 
00291 void FileProtocol::mkdir( const KUrl& url, int permissions )
00292 {
00293     QByteArray _path( QFile::encodeName(url.toLocalFile()));
00294 
00295     kDebug(7101) << "mkdir(): " << _path << ", permission = " << permissions;
00296 
00297     KDE_struct_stat buff;
00298     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
00299         if ( KDE_mkdir( _path.data(), 0777 /*umask will be applied*/ ) != 0 ) {
00300             if ( errno == EACCES ) {
00301           error( KIO::ERR_ACCESS_DENIED, _path );
00302           return;
00303             } else if ( errno == ENOSPC ) {
00304           error( KIO::ERR_DISK_FULL, _path );
00305           return;
00306             } else {
00307           error( KIO::ERR_COULD_NOT_MKDIR, _path );
00308           return;
00309             }
00310         } else {
00311             if ( permissions != -1 )
00312                 chmod( url, permissions );
00313             else
00314                 finished();
00315             return;
00316         }
00317     }
00318 
00319     if ( S_ISDIR( buff.st_mode ) ) {
00320         kDebug(7101) << "ERR_DIR_ALREADY_EXIST";
00321         error( KIO::ERR_DIR_ALREADY_EXIST, _path );
00322         return;
00323     }
00324     error( KIO::ERR_FILE_ALREADY_EXIST, _path );
00325     return;
00326 }
00327 
00328 void FileProtocol::get( const KUrl& url )
00329 {
00330     if (!url.isLocalFile()) {
00331         KUrl redir(url);
00332     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
00333     redirection(redir);
00334     finished();
00335     return;
00336     }
00337 
00338     QByteArray _path( QFile::encodeName(url.toLocalFile()));
00339     KDE_struct_stat buff;
00340     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
00341         if ( errno == EACCES )
00342            error( KIO::ERR_ACCESS_DENIED, _path );
00343         else
00344            error( KIO::ERR_DOES_NOT_EXIST, _path );
00345         return;
00346     }
00347 
00348     if ( S_ISDIR( buff.st_mode ) ) {
00349         error( KIO::ERR_IS_DIRECTORY, _path );
00350         return;
00351     }
00352     if ( !S_ISREG( buff.st_mode ) ) {
00353         error( KIO::ERR_CANNOT_OPEN_FOR_READING, _path );
00354         return;
00355     }
00356 
00357     int fd = KDE_open( _path.data(), O_RDONLY);
00358     if ( fd < 0 ) {
00359         error( KIO::ERR_CANNOT_OPEN_FOR_READING, _path );
00360         return;
00361     }
00362 
00363 #ifdef HAVE_FADVISE
00364     posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL);
00365 #endif
00366 
00367     // Determine the mimetype of the file to be retrieved, and emit it.
00368     // This is mandatory in all slaves (for KRun/BrowserRun to work)
00369     // In real "remote" slaves, this is usually done using findByNameAndContent
00370     // after receiving some data. But we don't know how much data the mimemagic rules
00371     // need, so for local files, better use findByUrl with localUrl=true.
00372     KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ );
00373     emit mimeType( mt->name() );
00374     // Emit total size AFTER mimetype
00375     totalSize( buff.st_size );
00376 
00377     KIO::filesize_t processed_size = 0;
00378 
00379     QString resumeOffset = metaData("resume");
00380     if ( !resumeOffset.isEmpty() )
00381     {
00382         bool ok;
00383         KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
00384         if (ok && (offset > 0) && (offset < buff.st_size))
00385         {
00386             if (KDE_lseek(fd, offset, SEEK_SET) == offset)
00387             {
00388                 canResume ();
00389                 processed_size = offset;
00390                 kDebug( 7101 ) << "Resume offset: " << KIO::number(offset);
00391             }
00392         }
00393     }
00394 
00395     char buffer[ MAX_IPC_SIZE ];
00396     QByteArray array;
00397 
00398     while( 1 )
00399     {
00400        int n = ::read( fd, buffer, MAX_IPC_SIZE );
00401        if (n == -1)
00402        {
00403           if (errno == EINTR)
00404               continue;
00405           error( KIO::ERR_COULD_NOT_READ, _path );
00406           ::close(fd);
00407           return;
00408        }
00409        if (n == 0)
00410           break; // Finished
00411 
00412        array = QByteArray::fromRawData(buffer, n);
00413        data( array );
00414        array.clear();
00415 
00416        processed_size += n;
00417        processedSize( processed_size );
00418 
00419        //kDebug( 7101 ) << "Processed: " << KIO::number (processed_size);
00420     }
00421 
00422     data( QByteArray() );
00423 
00424     ::close( fd );
00425 
00426     processedSize( buff.st_size );
00427     finished();
00428 }
00429 
00430 static int
00431 write_all(int fd, const char *buf, size_t len)
00432 {
00433    while (len > 0)
00434    {
00435       ssize_t written = write(fd, buf, len);
00436       if (written < 0)
00437       {
00438           if (errno == EINTR)
00439              continue;
00440           return -1;
00441       }
00442       buf += written;
00443       len -= written;
00444    }
00445    return 0;
00446 }
00447 
00448 void FileProtocol::open(const KUrl &url, QIODevice::OpenMode mode)
00449 {
00450     kDebug(7101) << "FileProtocol::open " << url.url();
00451 
00452     openPath = QFile::encodeName(url.toLocalFile());
00453     KDE_struct_stat buff;
00454     if ( KDE_stat( openPath.data(), &buff ) == -1 ) {
00455         if ( errno == EACCES )
00456            error( KIO::ERR_ACCESS_DENIED, openPath );
00457         else
00458            error( KIO::ERR_DOES_NOT_EXIST, openPath );
00459         return;
00460     }
00461 
00462     if ( S_ISDIR( buff.st_mode ) ) {
00463         error( KIO::ERR_IS_DIRECTORY, openPath );
00464         return;
00465     }
00466     if ( !S_ISREG( buff.st_mode ) ) {
00467         error( KIO::ERR_CANNOT_OPEN_FOR_READING, openPath );
00468         return;
00469     }
00470 
00471     int flags = 0;
00472     if (mode & QIODevice::ReadOnly) {
00473         if (mode & QIODevice::WriteOnly) {
00474             flags = O_RDWR | O_CREAT;
00475         } else {
00476             flags = O_RDONLY;
00477         }
00478     } else if (mode & QIODevice::WriteOnly) {
00479         flags = O_WRONLY | O_CREAT;
00480     }
00481 
00482     if (mode & QIODevice::Append) {
00483         flags |= O_APPEND;
00484     } else if (mode & QIODevice::Truncate) {
00485         flags |= O_TRUNC;
00486     }
00487 
00488     int fd = KDE_open( openPath.data(), flags);
00489     if ( fd < 0 ) {
00490         error( KIO::ERR_CANNOT_OPEN_FOR_READING, openPath );
00491         return;
00492     }
00493     // Determine the mimetype of the file to be retrieved, and emit it.
00494     // This is mandatory in all slaves (for KRun/BrowserRun to work).
00495     // If we're not opening the file ReadOnly or ReadWrite, don't attempt to
00496     // read the file and send the mimetype.
00497     if (mode & QIODevice::ReadOnly){
00498         KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ );
00499         emit mimeType( mt->name() );
00500    }
00501 
00502     totalSize( buff.st_size );
00503     position( 0 );
00504 
00505     emit opened();
00506     openFd = fd;
00507 }
00508 
00509 void FileProtocol::read(KIO::filesize_t bytes)
00510 {
00511     kDebug( 7101 ) << "File::open -- read";
00512     Q_ASSERT(openFd != -1);
00513 
00514     QVarLengthArray<char> buffer(bytes);
00515     while (true) {
00516         int res;
00517         do {
00518             res = ::read(openFd, buffer.data(), bytes);
00519         } while (res == -1 && errno == EINTR);
00520 
00521         if (res > 0) {
00522             QByteArray array = array.fromRawData(buffer.data(), res);
00523             data( array );
00524             bytes -= res;
00525         } else {
00526             // empty array designates eof
00527             data(QByteArray());
00528             if (res != 0) {
00529                 error(KIO::ERR_COULD_NOT_READ, openPath);
00530                 close();
00531             }
00532             break;
00533         }
00534         if (bytes <= 0) break;
00535     }
00536 }
00537 
00538 void FileProtocol::write(const QByteArray &data)
00539 {
00540     kDebug( 7101 ) << "File::open -- write";
00541     Q_ASSERT(openFd != -1);
00542 
00543     if (write_all(openFd, data.constData(), data.size())) {
00544         if (errno == ENOSPC) { // disk full
00545             error( KIO::ERR_DISK_FULL, openPath );
00546             close();
00547         } else {
00548             kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
00549             error( KIO::ERR_COULD_NOT_WRITE, openPath );
00550             close();
00551         }
00552     } else {
00553         written(data.size());
00554     }
00555 }
00556 
00557 void FileProtocol::seek(KIO::filesize_t offset)
00558 {
00559     kDebug( 7101 ) << "File::open -- seek";
00560     Q_ASSERT(openFd != -1);
00561 
00562     int res = KDE_lseek(openFd, offset, SEEK_SET);
00563     if (res != -1) {
00564         position( offset );
00565     } else {
00566         error(KIO::ERR_COULD_NOT_SEEK, openPath );
00567         close();
00568     }
00569 }
00570 
00571 void FileProtocol::close()
00572 {
00573     kDebug( 7101 ) << "File::open -- close ";
00574     Q_ASSERT(openFd != -1);
00575 
00576     ::close( openFd );
00577     openFd = -1;
00578     openPath.clear();
00579 
00580     finished();
00581 }
00582 
00583 static bool
00584 same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
00585 {
00586    if (src.st_ino == dest.st_ino &&
00587        src.st_dev == dest.st_dev)
00588      return true;
00589 
00590    return false;
00591 }
00592 
00593 void FileProtocol::put( const KUrl& url, int _mode, KIO::JobFlags _flags )
00594 {
00595     QString dest_orig = url.toLocalFile();
00596     QByteArray _dest_orig( QFile::encodeName(dest_orig));
00597 
00598     kDebug(7101) << "put(): " << dest_orig << ", mode=" << _mode;
00599 
00600     QString dest_part( dest_orig );
00601     dest_part += QLatin1String(".part");
00602     QByteArray _dest_part( QFile::encodeName(dest_part));
00603 
00604     KDE_struct_stat buff_orig;
00605     bool bOrigExists = (KDE_lstat( _dest_orig.data(), &buff_orig ) != -1);
00606     bool bPartExists = false;
00607     bool bMarkPartial = config()->readEntry("MarkPartial", true);
00608 
00609     if (bMarkPartial)
00610     {
00611         KDE_struct_stat buff_part;
00612         bPartExists = (KDE_stat( _dest_part.data(), &buff_part ) != -1);
00613 
00614         if (bPartExists && !(_flags & KIO::Resume) && !(_flags & KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
00615         {
00616             kDebug(7101) << "FileProtocol::put : calling canResume with "
00617                           << KIO::number(buff_part.st_size);
00618 
00619             // Maybe we can use this partial file for resuming
00620             // Tell about the size we have, and the app will tell us
00621             // if it's ok to resume or not.
00622             _flags |= canResume( buff_part.st_size ) ? KIO::Resume : KIO::DefaultFlags;
00623 
00624             kDebug(7101) << "FileProtocol::put got answer " << (_flags & KIO::Resume);
00625         }
00626     }
00627 
00628     if ( bOrigExists && !(_flags & KIO::Overwrite) && !(_flags & KIO::Resume))
00629     {
00630         if (S_ISDIR(buff_orig.st_mode))
00631             error( KIO::ERR_DIR_ALREADY_EXIST, dest_orig );
00632         else
00633             error( KIO::ERR_FILE_ALREADY_EXIST, dest_orig );
00634         return;
00635     }
00636 
00637     int result;
00638     QString dest;
00639     QByteArray _dest;
00640 
00641     int fd = -1;
00642 
00643     // Loop until we got 0 (end of data)
00644     do
00645     {
00646         QByteArray buffer;
00647         dataReq(); // Request for data
00648         result = readData( buffer );
00649 
00650         if (result >= 0)
00651         {
00652             if (dest.isEmpty())
00653             {
00654                 if (bMarkPartial)
00655                 {
00656                     kDebug(7101) << "Appending .part extension to " << dest_orig;
00657                     dest = dest_part;
00658                     if ( bPartExists && !(_flags & KIO::Resume) )
00659                     {
00660                         kDebug(7101) << "Deleting partial file " << dest_part;
00661                         remove( _dest_part.data() );
00662                         // Catch errors when we try to open the file.
00663                     }
00664                 }
00665                 else
00666                 {
00667                     dest = dest_orig;
00668                     if ( bOrigExists && !(_flags & KIO::Resume) )
00669                     {
00670                         kDebug(7101) << "Deleting destination file " << dest_orig;
00671                         remove( _dest_orig.data() );
00672                         // Catch errors when we try to open the file.
00673                     }
00674                 }
00675 
00676                 _dest = QFile::encodeName(dest);
00677 
00678                 if ( (_flags & KIO::Resume) )
00679                 {
00680                     fd = KDE_open( _dest.data(), O_RDWR );  // append if resuming
00681                     KDE_lseek(fd, 0, SEEK_END); // Seek to end
00682                 }
00683                 else
00684                 {
00685                     // WABA: Make sure that we keep writing permissions ourselves,
00686                     // otherwise we can be in for a surprise on NFS.
00687                     mode_t initialMode;
00688                     if (_mode != -1)
00689                         initialMode = _mode | S_IWUSR | S_IRUSR;
00690                     else
00691                         initialMode = 0666;
00692 
00693                     fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
00694                 }
00695 
00696                 if ( fd < 0 )
00697                 {
00698                     kDebug(7101) << "####################### COULD NOT WRITE " << dest << " _mode=" << _mode;
00699                     kDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")";
00700                     if ( errno == EACCES )
00701                         error( KIO::ERR_WRITE_ACCESS_DENIED, dest );
00702                     else
00703                         error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest );
00704                     return;
00705                 }
00706             }
00707 
00708             if (write_all( fd, buffer.data(), buffer.size()))
00709             {
00710                 if ( errno == ENOSPC ) // disk full
00711                 {
00712                   error( KIO::ERR_DISK_FULL, dest_orig);
00713                   result = -2; // means: remove dest file
00714                 }
00715                 else
00716                 {
00717                   kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
00718                   error( KIO::ERR_COULD_NOT_WRITE, dest_orig);
00719                   result = -1;
00720                 }
00721             }
00722         }
00723     }
00724     while ( result > 0 );
00725 
00726     // An error occurred deal with it.
00727     if (result < 0)
00728     {
00729         kDebug(7101) << "Error during 'put'. Aborting.";
00730 
00731         if (fd != -1)
00732         {
00733           ::close(fd);
00734 
00735           KDE_struct_stat buff;
00736           if (bMarkPartial && KDE_stat( _dest.data(), &buff ) == 0)
00737           {
00738             int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
00739             if (buff.st_size <  size)
00740               remove(_dest.data());
00741           }
00742         }
00743 
00744         ::exit(255);
00745     }
00746 
00747     if ( fd == -1 ) // we got nothing to write out, so we never opened the file
00748     {
00749         finished();
00750         return;
00751     }
00752 
00753     if ( ::close(fd) )
00754     {
00755         kWarning(7101) << "Error when closing file descriptor:" << strerror(errno);
00756         error( KIO::ERR_COULD_NOT_WRITE, dest_orig);
00757         return;
00758     }
00759 
00760     // after full download rename the file back to original name
00761     if ( bMarkPartial )
00762     {
00763         // If the original URL is a symlink and we were asked to overwrite it,
00764         // remove the symlink first. This ensures that we do not overwrite the
00765         // current source if the symlink points to it.
00766         if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) )
00767           remove( _dest_orig.data() );
00768 
00769 #ifdef Q_OS_WIN
00770         if ( MoveFileExA( _dest.data(),
00771                           _dest_orig.data(),
00772                           MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED ) == 0 )
00773 #else
00774         if ( KDE_rename( _dest.data(), _dest_orig.data() ) )
00775 #endif
00776         {
00777             kWarning(7101) << " Couldn't rename " << _dest << " to " << _dest_orig;
00778             error( KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig );
00779             return;
00780         }
00781     }
00782 
00783     // set final permissions
00784     if ( _mode != -1 && !(_flags & KIO::Resume) )
00785     {
00786         if (::chmod(_dest_orig.data(), _mode) != 0)
00787         {
00788             // couldn't chmod. Eat the error if the filesystem apparently doesn't support it.
00789             KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(_dest_orig);
00790             if (mp && mp->testFileSystemFlag(KMountPoint::SupportsChmod))
00791                  warning( i18n( "Could not change permissions for\n%1" ,  dest_orig ) );
00792         }
00793     }
00794 
00795     // set modification time
00796     const QString mtimeStr = metaData( "modified" );
00797     if ( !mtimeStr.isEmpty() ) {
00798         QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate );
00799         if ( dt.isValid() ) {
00800             KDE_struct_stat dest_statbuf;
00801             if (KDE_stat( _dest_orig.data(), &dest_statbuf ) == 0) {
00802                 struct utimbuf utbuf;
00803                 utbuf.actime = dest_statbuf.st_atime; // access time, unchanged
00804                 utbuf.modtime = dt.toTime_t(); // modification time
00805                 utime( _dest_orig.data(), &utbuf );
00806             }
00807         }
00808 
00809     }
00810 
00811     // We have done our job => finish
00812     finished();
00813 }
00814 
00815 #ifndef Q_OS_WIN
00816 
00817 void FileProtocol::copy( const KUrl &src, const KUrl &dest,
00818                          int _mode, JobFlags _flags )
00819 {
00820     kDebug(7101) << "copy(): " << src << " -> " << dest << ", mode=" << _mode;
00821 
00822     QByteArray _src( QFile::encodeName(src.toLocalFile()));
00823     QByteArray _dest( QFile::encodeName(dest.toLocalFile()));
00824     KDE_struct_stat buff_src;
00825 #ifdef HAVE_POSIX_ACL
00826     acl_t acl;
00827 #endif
00828 
00829     if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
00830         if ( errno == EACCES )
00831            error( KIO::ERR_ACCESS_DENIED, _src );
00832         else
00833            error( KIO::ERR_DOES_NOT_EXIST, _src );
00834     return;
00835     }
00836 
00837     if ( S_ISDIR( buff_src.st_mode ) ) {
00838     error( KIO::ERR_IS_DIRECTORY, src.path() );
00839     return;
00840     }
00841     if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) {
00842     error( KIO::ERR_CANNOT_OPEN_FOR_READING, src.path() );
00843     return;
00844     }
00845 
00846     KDE_struct_stat buff_dest;
00847     bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00848     if ( dest_exists )
00849     {
00850         if (S_ISDIR(buff_dest.st_mode))
00851         {
00852            error( KIO::ERR_DIR_ALREADY_EXIST, _dest );
00853            return;
00854         }
00855 
00856     if ( same_inode( buff_dest, buff_src) )
00857     {
00858         error( KIO::ERR_IDENTICAL_FILES, _dest );
00859         return;
00860     }
00861 
00862         if (!(_flags & KIO::Overwrite))
00863         {
00864            error( KIO::ERR_FILE_ALREADY_EXIST, _dest );
00865            return;
00866         }
00867 
00868         // If the destination is a symlink and overwrite is TRUE,
00869         // remove the symlink first to prevent the scenario where
00870         // the symlink actually points to current source!
00871         if ((_flags & KIO::Overwrite) && S_ISLNK(buff_dest.st_mode))
00872         {
00873             //kDebug(7101) << "copy(): LINK DESTINATION";
00874             remove( _dest.data() );
00875         }
00876     }
00877 
00878     int src_fd = KDE_open( _src.data(), O_RDONLY);
00879     if ( src_fd < 0 ) {
00880     error( KIO::ERR_CANNOT_OPEN_FOR_READING, _src );
00881     return;
00882     }
00883 
00884 #ifdef HAVE_FADVISE
00885     posix_fadvise(src_fd,0,0,POSIX_FADV_SEQUENTIAL);
00886 #endif
00887     // WABA: Make sure that we keep writing permissions ourselves,
00888     // otherwise we can be in for a surprise on NFS.
00889     mode_t initialMode;
00890     if (_mode != -1)
00891        initialMode = _mode | S_IWUSR;
00892     else
00893        initialMode = 0666;
00894 
00895     int dest_fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
00896     if ( dest_fd < 0 ) {
00897     kDebug(7101) << "###### COULD NOT WRITE " << dest.url();
00898         if ( errno == EACCES ) {
00899             error( KIO::ERR_WRITE_ACCESS_DENIED, _dest );
00900         } else {
00901             error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, _dest );
00902         }
00903         ::close(src_fd);
00904         return;
00905     }
00906 
00907 #ifdef HAVE_FADVISE
00908     posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
00909 #endif
00910 
00911 #ifdef HAVE_POSIX_ACL
00912     acl = acl_get_fd(src_fd);
00913     if ( acl && !isExtendedACL( acl ) ) {
00914         kDebug(7101) << _dest.data() << " doesn't have extended ACL";
00915         acl_free( acl );
00916         acl = NULL;
00917     }
00918 #endif
00919     totalSize( buff_src.st_size );
00920 
00921     KIO::filesize_t processed_size = 0;
00922     char buffer[ MAX_IPC_SIZE ];
00923     int n;
00924 #ifdef USE_SENDFILE
00925     bool use_sendfile=buff_src.st_size < 0x7FFFFFFF;
00926 #endif
00927     while( 1 )
00928     {
00929 #ifdef USE_SENDFILE
00930        if (use_sendfile) {
00931             off_t sf = processed_size;
00932             n = KDE_sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE );
00933             processed_size = sf;
00934             if ( n == -1 && ( errno == EINVAL || errno == ENOSYS ) ) { //not all filesystems support sendfile()
00935                 kDebug(7101) << "sendfile() not supported, falling back ";
00936                 use_sendfile = false;
00937             }
00938        }
00939        if (!use_sendfile)
00940 #endif
00941         n = ::read( src_fd, buffer, MAX_IPC_SIZE );
00942 
00943        if (n == -1)
00944        {
00945           if (errno == EINTR)
00946               continue;
00947 #ifdef USE_SENDFILE
00948           if ( use_sendfile ) {
00949             kDebug(7101) << "sendfile() error:" << strerror(errno);
00950             if ( errno == ENOSPC ) // disk full
00951             {
00952                 error(