• Skip to content
  • Skip to link menu
KDE 4.1 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( KIO::ERR_DISK_FULL, _dest );
00953                 remove( _dest.data() );
00954             }
00955             else {
00956                 error( KIO::ERR_SLAVE_DEFINED,
00957                         i18n("Cannot copy file from %1 to %2. (Errno: %3)",
00958                           src.toLocalFile(), dest.toLocalFile(), errno ) );
00959             }
00960           } else
00961 #endif
00962           error( KIO::ERR_COULD_NOT_READ, _src );
00963           ::close(src_fd);
00964           ::close(dest_fd);
00965 #ifdef HAVE_POSIX_ACL
00966           if (acl) acl_free(acl);
00967 #endif
00968           return;
00969        }
00970        if (n == 0)
00971           break; // Finished
00972 #ifdef USE_SENDFILE
00973        if ( !use_sendfile ) {
00974 #endif
00975          if (write_all( dest_fd, buffer, n))
00976          {
00977            ::close(src_fd);
00978            ::close(dest_fd);
00979 
00980            if ( errno == ENOSPC ) // disk full
00981            {
00982               error( KIO::ERR_DISK_FULL, _dest );
00983               remove( _dest.data() );
00984            }
00985            else
00986            {
00987               kWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno);
00988               error( KIO::ERR_COULD_NOT_WRITE, _dest );
00989            }
00990 #ifdef HAVE_POSIX_ACL
00991            if (acl) acl_free(acl);
00992 #endif
00993            return;
00994          }
00995          processed_size += n;
00996 #ifdef USE_SENDFILE
00997        }
00998 #endif
00999        processedSize( processed_size );
01000     }
01001 
01002     ::close( src_fd );
01003 
01004     if (::close( dest_fd))
01005     {
01006         kWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno);
01007         error( KIO::ERR_COULD_NOT_WRITE, _dest );
01008 #ifdef HAVE_POSIX_ACL
01009         if (acl) acl_free(acl);
01010 #endif
01011         return;
01012     }
01013 
01014     // set final permissions
01015     if ( _mode != -1 )
01016     {
01017         if ( (::chmod(_dest.data(), _mode) != 0)
01018 #ifdef HAVE_POSIX_ACL
01019           || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
01020 #endif
01021         )
01022        {
01023            KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(_dest);
01024            // Eat the error if the filesystem apparently doesn't support chmod.
01025            if ( mp && mp->testFileSystemFlag( KMountPoint::SupportsChmod ) )
01026                warning( i18n( "Could not change permissions for\n%1" ,  dest.toLocalFile() ) );
01027        }
01028     }
01029 #ifdef HAVE_POSIX_ACL
01030     if (acl) acl_free(acl);
01031 #endif
01032 
01033     // copy access and modification time
01034     struct utimbuf ut;
01035     ut.actime = buff_src.st_atime;
01036     ut.modtime = buff_src.st_mtime;
01037     if ( ::utime( _dest.data(), &ut ) != 0 )
01038     {
01039         kWarning() << QString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg( _dest.data() );
01040     }
01041 
01042     processedSize( buff_src.st_size );
01043     finished();
01044 }
01045 
01046 void FileProtocol::rename( const KUrl &src, const KUrl &dest,
01047                            KIO::JobFlags _flags )
01048 {
01049     char off_t_should_be_64_bits[sizeof(off_t) >= 8 ? 1 : -1]; (void) off_t_should_be_64_bits;
01050     QByteArray _src(QFile::encodeName(src.toLocalFile()));
01051     QByteArray _dest(QFile::encodeName(dest.toLocalFile()));
01052     KDE_struct_stat buff_src;
01053     if ( KDE_lstat( _src.data(), &buff_src ) == -1 ) {
01054         if ( errno == EACCES )
01055            error( KIO::ERR_ACCESS_DENIED, _src );
01056         else
01057            error( KIO::ERR_DOES_NOT_EXIST, _src );
01058         return;
01059     }
01060 
01061     KDE_struct_stat buff_dest;
01062     // stat symlinks here (lstat, not stat), to avoid ERR_IDENTICAL_FILES when replacing symlink
01063     // with its target (#169547)
01064     bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
01065     if ( dest_exists )
01066     {
01067         if (S_ISDIR(buff_dest.st_mode))
01068         {
01069            error( KIO::ERR_DIR_ALREADY_EXIST, _dest );
01070            return;
01071         }
01072 
01073     if ( same_inode( buff_dest, buff_src) )
01074     {
01075         error( KIO::ERR_IDENTICAL_FILES, _dest );
01076         return;
01077     }
01078 
01079         if (!(_flags & KIO::Overwrite))
01080         {
01081            error( KIO::ERR_FILE_ALREADY_EXIST, _dest );
01082            return;
01083         }
01084     }
01085 
01086     if ( KDE_rename( _src.data(), _dest.data()))
01087     {
01088         if (( errno == EACCES ) || (errno == EPERM)) {
01089             error( KIO::ERR_ACCESS_DENIED, _dest );
01090         }
01091         else if (errno == EXDEV) {
01092            error( KIO::ERR_UNSUPPORTED_ACTION, QLatin1String("rename"));
01093         }
01094         else if (errno == EROFS) { // The file is on a read-only filesystem
01095            error( KIO::ERR_CANNOT_DELETE, _src );
01096         }
01097         else {
01098            error( KIO::ERR_CANNOT_RENAME, _src );
01099         }
01100         return;
01101     }
01102 
01103     finished();
01104 }
01105 
01106 void FileProtocol::symlink( const QString &target, const KUrl &dest, KIO::JobFlags flags )
01107 {
01108     // Assume dest is local too (wouldn't be here otherwise)
01109     if ( ::symlink( QFile::encodeName( target ), QFile::encodeName( dest.path() ) ) == -1 )
01110     {
01111         // Does the destination already exist ?
01112         if ( errno == EEXIST )
01113         {
01114             if ( (flags & KIO::Overwrite) )
01115             {
01116                 // Try to delete the destination
01117                 if ( unlink( QFile::encodeName( dest.path() ) ) != 0 )
01118                 {
01119                     error( KIO::ERR_CANNOT_DELETE, dest.path() );
01120                     return;
01121                 }
01122                 // Try again - this won't loop forever since unlink succeeded
01123                 symlink( target, dest, flags );
01124             }
01125             else
01126             {
01127                 KDE_struct_stat buff_dest;
01128                 KDE_lstat( QFile::encodeName( dest.path() ), &buff_dest );
01129                 if (S_ISDIR(buff_dest.st_mode))
01130                     error( KIO::ERR_DIR_ALREADY_EXIST, dest.path() );
01131                 else
01132                     error( KIO::ERR_FILE_ALREADY_EXIST, dest.path() );
01133                 return;
01134             }
01135         }
01136         else
01137         {
01138             // Some error occurred while we tried to symlink
01139             error( KIO::ERR_CANNOT_SYMLINK, dest.path() );
01140             return;
01141         }
01142     }
01143     finished();
01144 }
01145 
01146 void FileProtocol::del( const KUrl& url, bool isfile)
01147 {
01148     QByteArray _path( QFile::encodeName(url.toLocalFile()));
01149     /*****
01150      * Delete files
01151      *****/
01152 
01153     if (isfile) {
01154     kDebug( 7101 ) <<  "Deleting file "<< url.url();
01155 
01156     // TODO deletingFile( source );
01157 
01158     if ( unlink( _path.data() ) == -1 ) {
01159             if ((errno == EACCES) || (errno == EPERM))
01160                error( KIO::ERR_ACCESS_DENIED, _path );
01161             else if (errno == EISDIR)
01162                error( KIO::ERR_IS_DIRECTORY, _path );
01163             else
01164                error( KIO::ERR_CANNOT_DELETE, _path );
01165         return;
01166     }
01167     } else {
01168 
01169       /*****
01170        * Delete empty directory
01171        *****/
01172 
01173       kDebug( 7101 ) << "Deleting directory " << url.url();
01174 
01175       if ( ::rmdir( _path.data() ) == -1 ) {
01176     if ((errno == EACCES) || (errno == EPERM))
01177       error( KIO::ERR_ACCESS_DENIED, _path );
01178     else {
01179       kDebug( 7101 ) << "could not rmdir " << perror;
01180       error( KIO::ERR_COULD_NOT_RMDIR, _path );
01181       return;
01182     }
01183       }
01184     }
01185 
01186     finished();
01187 }
01188 
01189 #endif  // Q_OS_WIN
01190 
01191 QString FileProtocol::getUserName( uid_t uid ) const
01192 {
01193     if ( !mUsercache.contains( uid ) ) {
01194         struct passwd *user = getpwuid( uid );
01195         if ( user ) {
01196             mUsercache.insert( uid, QString::fromLatin1(user->pw_name) );
01197         }
01198         else
01199             return QString::number( uid );
01200     }
01201     return mUsercache[uid];
01202 }
01203 
01204 QString FileProtocol::getGroupName( gid_t gid ) const
01205 {
01206     if ( !mGroupcache.contains( gid ) ) {
01207         struct group *grp = getgrgid( gid );
01208         if ( grp ) {
01209             mGroupcache.insert( gid, QString::fromLatin1(grp->gr_name) );
01210         }
01211         else
01212             return QString::number( gid );
01213     }
01214     return mGroupcache[gid];
01215 }
01216 
01217 bool FileProtocol::createUDSEntry( const QString & filename, const QByteArray & path, UDSEntry & entry,
01218                                    short int details, bool withACL )
01219 {
01220 #ifndef HAVE_POSIX_ACL
01221     Q_UNUSED(withACL);
01222 #endif
01223     assert(entry.count() == 0); // by contract :-)
01224     // entry.reserve( 8 ); // speed up QHash insertion
01225 
01226     // Note: details = 0 (only "file or directory or symlink or doesn't exist") isn't implemented
01227     // because there's no real performance penalty in kio_file for returning the complete
01228     // details. Please consider doing it in your kioslave if you're using this one as a model :)
01229 
01230     entry.insert( KIO::UDSEntry::UDS_NAME, filename );
01231 
01232     mode_t type;
01233     mode_t access;
01234     KDE_struct_stat buff;
01235 
01236     if ( KDE_lstat( path.data(), &buff ) == 0 )  {
01237 
01238         if (S_ISLNK(buff.st_mode)) {
01239 
01240             char buffer2[ 1000 ];
01241             int n = readlink( path.data(), buffer2, 1000 );
01242             if ( n != -1 ) {
01243                 buffer2[ n ] = 0;
01244             }
01245 
01246             entry.insert( KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName( buffer2 ) );
01247 
01248             // A symlink -> follow it only if details>1
01249             if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) {
01250                 // It is a link pointing to nowhere
01251                 type = S_IFMT - 1;
01252                 access = S_IRWXU | S_IRWXG | S_IRWXO;
01253 
01254                 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
01255                 entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
01256                 entry.insert( KIO::UDSEntry::UDS_SIZE, 0LL );
01257                 goto notype;
01258 
01259             }
01260         }
01261     } else {
01262         // kWarning() << "lstat didn't work on " << path.data();
01263         return false;
01264     }
01265 
01266     type = buff.st_mode & S_IFMT; // extract file type
01267     access = buff.st_mode & 07777; // extract permissions
01268 
01269     entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
01270     entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
01271 
01272     entry.insert( KIO::UDSEntry::UDS_SIZE, buff.st_size );
01273 
01274 #ifdef HAVE_POSIX_ACL
01275     /* Append an atom indicating whether the file has extended acl information
01276      * and if withACL is specified also one with the acl itself. If it's a directory
01277      * and it has a default ACL, also append that. */
01278     appendACLAtoms( path, entry, type, withACL );
01279 #endif
01280 
01281  notype:
01282     entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime );
01283     entry.insert( KIO::UDSEntry::UDS_USER, getUserName( buff.st_uid ) );
01284     entry.insert( KIO::UDSEntry::UDS_GROUP, getGroupName( buff.st_gid ) );
01285     entry.insert( KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime );
01286 
01287     // Note: buff.st_ctime isn't the creation time !
01288     // We made that mistake for KDE 2.0, but it's in fact the
01289     // "file status" change time, which we don't care about.
01290 
01291     return true;
01292 }
01293 
01294 void FileProtocol::stat( const KUrl & url )
01295 {
01296     if (!url.isLocalFile()) {
01297         KUrl redir(url);
01298     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
01299     redirection(redir);
01300     kDebug(7101) << "redirecting to " << redir.url();
01301     finished();
01302     return;
01303     }
01304 
01305     /* directories may not have a slash at the end if
01306      * we want to stat() them; it requires that we
01307      * change into it .. which may not be allowed
01308      * stat("/is/unaccessible")  -> rwx------
01309      * stat("/is/unaccessible/") -> EPERM            H.Z.
01310      * This is the reason for the -1
01311      */
01312 #ifdef Q_WS_WIN
01313     QByteArray _path( QFile::encodeName(url.toLocalFile()) );
01314 #else
01315     QByteArray _path( QFile::encodeName(url.path(KUrl::RemoveTrailingSlash)));
01316 #endif
01317     QString sDetails = metaData(QLatin1String("details"));
01318     int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
01319     kDebug(7101) << "FileProtocol::stat details=" << details;
01320 
01321     UDSEntry entry;
01322     if ( !createUDSEntry( url.fileName(), _path, entry, details, true /*with acls*/ ) )
01323     {
01324         error( KIO::ERR_DOES_NOT_EXIST, _path );
01325         return;
01326     }
01327 #if 0
01329     MetaData::iterator it1 = mOutgoingMetaData.begin();
01330     for ( ; it1 != mOutgoingMetaData.end(); it1++ ) {
01331         kDebug(7101) << it1.key() << " = " << it1.data();
01332     }
01334 #endif
01335     statEntry( entry );
01336 
01337     finished();
01338 }
01339 
01340 #ifndef Q_OS_WIN
01341 void FileProtocol::listDir( const KUrl& url)
01342 {
01343     kDebug(7101) << "========= LIST " << url.url() << " =========";
01344     if (!url.isLocalFile()) {
01345         KUrl redir(url);
01346     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
01347     redirection(redir);
01348     kDebug(7101) << "redirecting to " << redir.url();
01349     finished();
01350     return;
01351     }
01352     QByteArray _path( QFile::encodeName(url.toLocalFile()) );
01353     KDE_struct_stat buff;
01354     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
01355     error( KIO::ERR_DOES_NOT_EXIST, _path );
01356     return;
01357     }
01358 
01359     if ( !S_ISDIR( buff.st_mode ) ) {
01360     error( KIO::ERR_IS_FILE, _path );
01361     return;
01362     }
01363 
01364     DIR *dp = 0L;
01365     KDE_struct_dirent *ep;
01366 
01367     dp = opendir( _path.data() );
01368     if ( dp == 0 ) {
01369         switch (errno)
01370         {
01371 #ifdef ENOMEDIUM
01372     case ENOMEDIUM:
01373             error( ERR_SLAVE_DEFINED,
01374                    i18n( "No media in device for %1", url.toLocalFile() ) );
01375             break;
01376 #else
01377         case ENOENT: // just to avoid the warning
01378 #endif
01379         default:
01380             error( KIO::ERR_CANNOT_ENTER_DIRECTORY, _path );
01381             break;
01382         }
01383     return;
01384     }
01385 
01386     // Don't make this a QStringList. The locale file name we get here
01387     // should be passed intact to createUDSEntry to avoid problems with
01388     // files where QFile::encodeName(QFile::decodeName(a)) != a.
01389     QList<QByteArray> entryNames;
01390     while ( ( ep = KDE_readdir( dp ) ) != 0L )
01391     entryNames.append( ep->d_name );
01392 
01393     closedir( dp );
01394     totalSize( entryNames.count() );
01395 
01396     /* set the current dir to the path to speed up
01397        in not having to pass an absolute path.
01398        We restore the path later to get out of the
01399        path - the kernel wouldn't unmount or delete
01400        directories we keep as active directory. And
01401        as the slave runs in the background, it's hard
01402        to see for the user what the problem would be */
01403     char path_buffer[PATH_MAX];
01404     getcwd(path_buffer, PATH_MAX - 1);
01405     if ( chdir( _path.data() ) )  {
01406         if (errno == EACCES)
01407             error(ERR_ACCESS_DENIED, _path);
01408         else
01409             error(ERR_CANNOT_ENTER_DIRECTORY, _path);
01410         finished();
01411     }
01412 
01413     UDSEntry entry;
01414     QList<QByteArray>::ConstIterator it = entryNames.constBegin();
01415     QList<QByteArray>::ConstIterator end = entryNames.constEnd();
01416     for (; it != end; ++it) {
01417         entry.clear();
01418         if ( createUDSEntry( QFile::decodeName(*it),
01419                              *it /* we can use the filename as relative path*/,
01420                              entry, 2, true ) )
01421           listEntry( entry, false);
01422     }
01423 
01424     listEntry( entry, true ); // ready
01425 
01426     kDebug(7101) << "============= COMPLETED LIST ============";
01427 
01428     chdir(path_buffer);
01429     finished();
01430 }
01431 #endif  // !Q_OS_WIN
01432 
01433 /*
01434 void FileProtocol::testDir( const QString& path )
01435 {
01436     QByteArray _path( QFile::encodeName(path));
01437     KDE_struct_stat buff;
01438     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
01439     error( KIO::ERR_DOES_NOT_EXIST, path );
01440     return;
01441     }
01442 
01443     if ( S_ISDIR( buff.st_mode ) )
01444     isDirectory();
01445     else
01446     isFile();
01447 
01448     finished();
01449 }
01450 */
01451 
01452 void FileProtocol::special( const QByteArray &data)
01453 {
01454     int tmp;
01455     QDataStream stream(data);
01456 
01457     stream >> tmp;
01458     switch (tmp) {
01459     case 1:
01460       {
01461     QString fstype, dev, point;
01462     qint8 iRo;
01463 
01464     stream >> iRo >> fstype >> dev >> point;
01465 
01466     bool ro = ( iRo != 0 );
01467 
01468     kDebug(7101) << "MOUNTING fstype=" << fstype << " dev=" << dev << " point=" << point << " ro=" << ro;
01469     bool ok = pmount( dev );
01470     if (ok)
01471         finished();
01472     else
01473         mount( ro, fstype.toAscii(), dev, point );
01474 
01475       }
01476       break;
01477     case 2:
01478       {
01479     QString point;
01480     stream >> point;
01481     bool ok = pumount( point );
01482     if (ok)
01483         finished();
01484     else
01485         unmount( point );
01486       }
01487       break;
01488 
01489     default:
01490       break;
01491     }
01492 }
01493 
01494 // Connected to KShred
01495 void FileProtocol::slotProcessedSize( KIO::filesize_t bytes )
01496 {
01497   kDebug(7101) << "FileProtocol::slotProcessedSize (" << (unsigned int) bytes << ")";
01498   processedSize( bytes );
01499 }
01500 
01501 // Connected to KShred
01502 void FileProtocol::slotInfoMessage( const QString & msg )
01503 {
01504   kDebug(7101) << "FileProtocol::slotInfoMessage (" << msg << ")";
01505   infoMessage( msg );
01506 }
01507 
01508 void FileProtocol::mount( bool _ro, const char *_fstype, const QString& _dev, const QString& _point )
01509 {
01510     kDebug(7101) << "FileProtocol::mount _fstype=" << _fstype;
01511 
01512 #ifdef HAVE_VOLMGT
01513     /*
01514      *  support for Solaris volume management
01515      */
01516     QString err;
01517     QByteArray devname = QFile::encodeName( _dev );
01518 
01519     if( volmgt_running() ) {
01520 //      kDebug(7101) << "VOLMGT: vold ok.";
01521         if( volmgt_check( devname.data() ) == 0 ) {
01522             kDebug(7101) << "VOLMGT: no media in "
01523                     << devname.data();
01524             err = i18n("No Media inserted or Media not recognized.");
01525             error( KIO::ERR_COULD_NOT_MOUNT, err );
01526             return;
01527         } else {
01528             kDebug(7101) << "VOLMGT: " << devname.data()
01529                 << ": media ok";
01530             finished();
01531             return;
01532         }
01533     } else {
01534         err = i18n("\"vold\" is not running.");
01535         kDebug(7101) << "VOLMGT: " << err;
01536         error( KIO::ERR_COULD_NOT_MOUNT, err );
01537         return;
01538     }
01539 #else
01540 
01541 
01542     KTemporaryFile tmpFile;
01543     tmpFile.setAutoRemove(false);
01544     tmpFile.open();
01545     QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
01546     QByteArray dev;
01547     if ( _dev.startsWith( "LABEL=" ) ) { // turn LABEL=foo into -L foo (#71430)
01548         QString labelName = _dev.mid( 6 );
01549         dev = "-L ";
01550         dev += QFile::encodeName( KShell::quoteArg( labelName ) ); // is it correct to assume same encoding as filesystem?
01551     } else if ( _dev.startsWith( "UUID=" ) ) { // and UUID=bar into -U bar
01552         QString uuidName = _dev.mid( 5 );
01553         dev = "-U ";
01554         dev += QFile::encodeName( KShell::quoteArg( uuidName ) );
01555     }
01556     else
01557         dev = QFile::encodeName( KShell::quoteArg(_dev) ); // get those ready to be given to a shell
01558 
01559     QByteArray point = QFile::encodeName( KShell::quoteArg(_point) );
01560     bool fstype_empty = !_fstype || !*_fstype;
01561     QByteArray fstype = KShell::quoteArg(_fstype).toLatin1(); // good guess
01562     QByteArray readonly = _ro ? "-r" : "";
01563     QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
01564     QString path = QLatin1String("/sbin:/bin");
01565     if(!epath.isEmpty())
01566         path += QLatin1String(":") + epath;
01567     QByteArray mountProg = KGlobal::dirs()->findExe("mount", path).toLocal8Bit();
01568     if (mountProg.isEmpty()){
01569       error( KIO::ERR_COULD_NOT_MOUNT, i18n("Could not find program \"mount\""));
01570       return;
01571     }
01572     QByteArray buffer = mountProg + ' ';
01573 
01574     // Two steps, in case mount doesn't like it when we pass all options
01575     for ( int step = 0 ; step <= 1 ; step++ )
01576     {
01577         // Mount using device only if no fstype nor mountpoint (KDE-1.x like)
01578         if ( !_dev.isEmpty() && _point.isEmpty() && fstype_empty )
01579             buffer += dev;
01580         else
01581           // Mount using the mountpoint, if no fstype nor device (impossible in first step)
01582           if ( !_point.isEmpty() && _dev.isEmpty() && fstype_empty )
01583               buffer += point;
01584           else
01585             // mount giving device + mountpoint but no fstype
01586             if ( !_point.isEmpty() && !_dev.isEmpty() && fstype_empty )
01587                 buffer += readonly + ' ' + dev + ' ' + point;
01588             else
01589               // mount giving device + mountpoint + fstype
01590 #if defined(__svr4__) && defined(Q_OS_SOLARIS) // MARCO for Solaris 8 and I
01591                 // believe this is true for SVR4 in general
01592                 buffer += "-F " + fstype + ' ' + (_ro ? "-oro" : "") + ' ' + dev + ' ' + point;
01593 #else
01594                 buffer += readonly + " -t " + fstype + ' ' + dev + ' ' + point;
01595 #endif
01596         buffer += " 2>" + tmpFileName;
01597         kDebug(7101) << buffer;
01598 
01599         int mount_ret = system( buffer.constData() );
01600 
01601         QString err = testLogFile( tmpFileName );
01602         if ( err.isEmpty() && mount_ret == 0)
01603         {
01604             finished();
01605             return;
01606         }
01607         else
01608         {
01609             // Didn't work - or maybe we just got a warning
01610             KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( _dev );
01611             // Is the device mounted ?
01612             if ( mp && mount_ret == 0)
01613             {
01614                 kDebug(7101) << "mount got a warning: " << err;
01615                 warning( err );
01616                 finished();
01617                 return;
01618             }
01619             else
01620             {
01621                 if ( (step == 0) && !_point.isEmpty())
01622                 {
01623                     kDebug(7101) << err;
01624                     kDebug(7101) << "Mounting with those options didn't work, trying with only mountpoint";
01625                     fstype = "";
01626                     fstype_empty = true;
01627                     dev = "";
01628                     // The reason for trying with only mountpoint (instead of
01629                     // only device) is that some people (hi Malte!) have the
01630                     // same device associated with two mountpoints
01631                     // for different fstypes, like /dev/fd0 /mnt/e2floppy and
01632                     // /dev/fd0 /mnt/dosfloppy.
01633                     // If the user has the same mountpoint associated with two
01634                     // different devices, well they shouldn't specify the
01635                     // mountpoint but just the device.
01636                 }
01637                 else
01638                 {
01639                     error( KIO::ERR_COULD_NOT_MOUNT, err );
01640                     return;
01641                 }
01642             }
01643         }
01644     }
01645 #endif /* ! HAVE_VOLMGT */
01646 }
01647 
01648 
01649 void FileProtocol::unmount( const QString& _point )
01650 {
01651     QByteArray buffer;
01652 
01653     KTemporaryFile tmpFile;
01654     tmpFile.setAutoRemove(false);
01655     tmpFile.open();
01656     QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
01657     QString err;
01658 
01659 #ifdef HAVE_VOLMGT
01660     /*
01661      *  support for Solaris volume management
01662      */
01663     char *devname;
01664     char *ptr;
01665     FILE *mnttab;
01666     struct mnttab mnt;
01667 
01668     if( volmgt_running() ) {
01669         kDebug(7101) << "VOLMGT: looking for "
01670             << _point.toLocal8Bit();
01671 
01672         if( (mnttab = KDE_fopen( MNTTAB, "r" )) == NULL ) {
01673             err = "could not open mnttab";
01674             kDebug(7101) << "VOLMGT: " << err;
01675             error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01676             return;
01677         }
01678 
01679         /*
01680          *  since there's no way to derive the device name from
01681          *  the mount point through the volmgt library (and
01682          *  media_findname() won't work in this case), we have to
01683          *  look ourselves...
01684          */
01685         devname = NULL;
01686         rewind( mnttab );
01687         while( getmntent( mnttab, &mnt ) == 0 ) {
01688             if( strcmp( _point.toLocal8Bit(), mnt.mnt_mountp ) == 0 ){
01689                 devname = mnt.mnt_special;
01690                 break;
01691             }
01692         }
01693         fclose( mnttab );
01694 
01695         if( devname == NULL ) {
01696             err = "not in mnttab";
01697             kDebug(7101) << "VOLMGT: "
01698                 << QFile::encodeName(_point).data()
01699                 << ": " << err;
01700             error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01701             return;
01702         }
01703 
01704         /*
01705          *  strip off the directory name (volume name)
01706          *  the eject(1) command will handle unmounting and
01707          *  physically eject the media (if possible)
01708          */
01709         ptr = strrchr( devname, '/' );
01710         *ptr = '\0';
01711                 QByteArray qdevname(QFile::encodeName(KShell::quoteArg(QFile::decodeName(QByteArray(devname)))).data());
01712         buffer = "/usr/bin/eject " + qdevname + " 2>" + tmpFileName;
01713         kDebug(7101) << "VOLMGT: eject " << qdevname;
01714 
01715         /*
01716          *  from eject(1): exit status == 0 => need to manually eject
01717          *                 exit status == 4 => media was ejected
01718          */
01719         if( WEXITSTATUS( system( buffer.constData() )) == 4 ) {
01720             /*
01721              *  this is not an error, so skip "testLogFile()"
01722              *  to avoid wrong/confusing error popup
01723              */
01724             QFile::remove( tmpFileName );
01725             finished();
01726             return;
01727         }
01728     } else {
01729         /*
01730          *  eject(1) should do its job without vold(1M) running,
01731          *  so we probably could call eject anyway, but since the
01732          *  media is mounted now, vold must've died for some reason
01733          *  during the user's session, so it should be restarted...
01734          */
01735         err = i18n("\"vold\" is not running.");
01736         kDebug(7101) << "VOLMGT: " << err;
01737         error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01738         return;
01739     }
01740 #else
01741     QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
01742     QString path = QLatin1String("/sbin:/bin");
01743     if (!epath.isEmpty())
01744        path += ':' + epath;
01745     QByteArray umountProg = KGlobal::dirs()->findExe("umount", path).toLocal8Bit();
01746 
01747     if (umountProg.isEmpty()) {
01748         error( KIO::ERR_COULD_NOT_UNMOUNT, i18n("Could not find program \"umount\""));
01749         return;
01750     }
01751     buffer = umountProg + QFile::encodeName(KShell::quoteArg(_point)) + " 2>" + tmpFileName;
01752     system( buffer.constData() );
01753 #endif /* HAVE_VOLMGT */
01754 
01755     err = testLogFile( tmpFileName );
01756     if ( err.isEmpty() )
01757         finished();
01758     else
01759         error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01760 }
01761 
01762 /*************************************
01763  *
01764  * pmount handling
01765  *
01766  *************************************/
01767 
01768 bool FileProtocol::pmount(const QString &dev)
01769 {
01770     QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
01771     QString path = QLatin1String("/sbin:/bin");
01772     if (!epath.isEmpty())
01773         path += ':' + epath;
01774     QString pmountProg = KGlobal::dirs()->findExe("pmount", path);
01775 
01776     if (pmountProg.isEmpty())
01777         return false;
01778 
01779     QByteArray buffer = QFile::encodeName(pmountProg) + ' ' +
01780                         QFile::encodeName(KShell::quoteArg(dev));
01781 
01782     int res = system( buffer.constData() );
01783 
01784     return res==0;
01785 }
01786 
01787 bool FileProtocol::pumount(const QString &point)
01788 {
01789     KMountPoint::Ptr mp = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName).findByPath(point);
01790     if (!mp)
01791         return false;
01792     QString dev = mp->realDeviceName();
01793     if (dev.isEmpty()) return false;
01794 
01795     QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
01796     QString path = QLatin1String("/sbin:/bin");
01797     if (!epath.isEmpty())
01798         path += ':' + epath;
01799     QString pumountProg = KGlobal::dirs()->findExe("pumount", path);
01800 
01801     if (pumountProg.isEmpty())
01802         return false;
01803 
01804     QByteArray buffer = QFile::encodeName(pumountProg);
01805     buffer += ' ';
01806     buffer += QFile::encodeName(KShell::quoteArg(dev));
01807 
01808     int res = system( buffer.data() );
01809 
01810     return res==0;
01811 }
01812 
01813 /*************************************
01814  *
01815  * Utilities
01816  *
01817  *************************************/
01818 
01819 static QString testLogFile( const QByteArray& _filename )
01820 {
01821     char buffer[ 1024 ];
01822     KDE_struct_stat buff;
01823 
01824     QString result;
01825 
01826     KDE_stat( _filename, &buff );
01827     int size = buff.st_size;
01828     if ( size == 0 ) {
01829     unlink( _filename );
01830     return result;
01831     }
01832 
01833     FILE * f = KDE_fopen( _filename, "rb" );
01834     if ( f == 0L ) {
01835     unlink( _filename );
01836     result = i18n("Could not read %1", QFile::decodeName(_filename));
01837     return result;
01838     }
01839 
01840     result = "";
01841     const char *p = "";
01842     while ( p != 0L ) {
01843     p = fgets( buffer, sizeof(buffer)-1, f );
01844     if ( p != 0L )
01845         result += QString::fromLocal8Bit(buffer);
01846     }
01847 
01848     fclose( f );
01849 
01850     unlink( _filename );
01851 
01852     return result;
01853 }
01854 
01855 /*************************************
01856  *
01857  * ACL handling helpers
01858  *
01859  *************************************/
01860 #ifdef HAVE_POSIX_ACL
01861 
01862 static bool isExtendedACL( acl_t acl )
01863 {
01864     return ( acl_equiv_mode( acl, 0 ) != 0 );
01865 }
01866 
01867 static void appendACLAtoms( const QByteArray & path, UDSEntry& entry, mode_t type, bool withACL )
01868 {
01869     // first check for a noop
01870     if ( acl_extended_file( path.data() ) == 0 ) return;
01871 
01872     acl_t acl = 0;
01873     acl_t defaultAcl = 0;
01874     bool isDir = S_ISDIR( type );
01875     // do we have an acl for the file, and/or a default acl for the dir, if it is one?
01876     acl = acl_get_file( path.data(), ACL_TYPE_ACCESS );
01877     /* Sadly libacl does not provided a means of checking for extended ACL and default
01878      * ACL separately. Since a directory can have both, we need to check again. */
01879     if ( isDir ) {
01880         if ( acl ) {
01881             if ( !isExtendedACL( acl ) ) {
01882                 acl_free( acl );
01883                 acl = 0;
01884             }
01885         }
01886         defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
01887     }
01888     if ( acl || defaultAcl ) {
01889       kDebug(7101) << path.data() << " has extended ACL entries ";
01890       entry.insert( KIO::UDSEntry::UDS_EXTENDED_ACL, 1 );
01891     }
01892     if ( withACL ) {
01893         if ( acl ) {
01894             ssize_t size = acl_size( acl );
01895             const QString str = QString::fromLatin1( acl_to_text( acl, &size ) );
01896             entry.insert( KIO::UDSEntry::UDS_ACL_STRING, str );
01897             kDebug(7101) << path.data() << "ACL: " << str;
01898         }
01899         if ( defaultAcl ) {
01900             ssize_t size = acl_size( defaultAcl );
01901             const QString str = QString::fromLatin1( acl_to_text( defaultAcl, &size ) );
01902             entry.insert( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, str );
01903             kDebug(7101) << path.data() << "DEFAULT ACL: " << str;
01904         }
01905     }
01906     if ( acl ) acl_free( acl );
01907     if ( defaultAcl ) acl_free( defaultAcl );
01908 }
01909 #endif
01910 
01911 #include "file.moc"

KIOSlave

Skip menu "KIOSlave"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal