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