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

kioslave

file.cc

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 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License (LGPL) as published by the Free Software Foundation;
00009    either version 2 of the License, or (at your option) any later
00010    version.
00011 
00012    This library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Library General Public License for more details.
00016 
00017    You should have received a copy of the GNU Library General Public License
00018    along with this library; see the file COPYING.LIB.  If not, write to
00019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020    Boston, MA 02110-1301, USA.
00021 */
00022 
00023 // $Id: file.cc 516747 2006-03-08 13:41:58Z coolo $
00024 
00025 #include <config.h>
00026 
00027 #include <qglobal.h> //for Q_OS_XXX
00028 #include <sys/types.h>
00029 #include <sys/wait.h>
00030 #include <sys/stat.h>
00031 #ifdef HAVE_SYS_TIME_H
00032 #include <sys/time.h>
00033 #endif
00034 
00035 //sendfile has different semantics in different platforms
00036 #if defined HAVE_SENDFILE && defined Q_OS_LINUX
00037 #define USE_SENDFILE 1
00038 #endif
00039 
00040 #ifdef USE_SENDFILE
00041 #include <sys/sendfile.h>
00042 #endif
00043 
00044 #ifdef USE_POSIX_ACL
00045 #include <sys/acl.h>
00046 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
00047 #include <acl/libacl.h>
00048 #else
00049 #include <posixacladdons.h>
00050 #endif
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 <qvaluelist.h>
00070 #include <qregexp.h>
00071 
00072 #include <kshred.h>
00073 #include <kdebug.h>
00074 #include <kurl.h>
00075 #include <kinstance.h>
00076 #include <ksimpleconfig.h>
00077 #include <ktempfile.h>
00078 #include <klocale.h>
00079 #include <qfile.h>
00080 #include <qstrlist.h>
00081 #include "file.h"
00082 #include <limits.h>
00083 #include <kprocess.h>
00084 #include <kmountpoint.h>
00085 #include <kstandarddirs.h>
00086 
00087 #ifdef HAVE_VOLMGT
00088 #include <volmgt.h>
00089 #include <sys/mnttab.h>
00090 #endif
00091 
00092 #include <kstandarddirs.h>
00093 #include <kio/ioslave_defaults.h>
00094 #include <klargefile.h>
00095 #include <kglobal.h>
00096 #include <kmimetype.h>
00097 
00098 using namespace KIO;
00099 
00100 #define MAX_IPC_SIZE (1024*32)
00101 
00102 static QString testLogFile( const char *_filename );
00103 #ifdef USE_POSIX_ACL
00104 static QString aclAsString(  acl_t p_acl );
00105 static bool isExtendedACL(  acl_t p_acl );
00106 static void appendACLAtoms( const QCString & path, UDSEntry& entry,
00107                             mode_t type, bool withACL );
00108 #endif
00109 
00110 extern "C" { KDE_EXPORT int kdemain(int argc, char **argv); }
00111 
00112 int kdemain( int argc, char **argv )
00113 {
00114   KLocale::setMainCatalogue("kdelibs");
00115   KInstance instance( "kio_file" );
00116   ( void ) KGlobal::locale();
00117 
00118   kdDebug(7101) << "Starting " << getpid() << endl;
00119 
00120   if (argc != 4)
00121   {
00122      fprintf(stderr, "Usage: kio_file protocol domain-socket1 domain-socket2\n");
00123      exit(-1);
00124   }
00125 
00126   FileProtocol slave(argv[2], argv[3]);
00127   slave.dispatchLoop();
00128 
00129   kdDebug(7101) << "Done" << endl;
00130   return 0;
00131 }
00132 
00133 
00134 FileProtocol::FileProtocol( const QCString &pool, const QCString &app ) : SlaveBase( "file", pool, app )
00135 {
00136     usercache.setAutoDelete( true );
00137     groupcache.setAutoDelete( true );
00138 }
00139 
00140 
00141 int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault )
00142 {
00143     int ret = 0;
00144 #ifdef USE_POSIX_ACL
00145 
00146     const QString ACLString = metaData( "ACL_STRING" );
00147     const QString defaultACLString = metaData( "DEFAULT_ACL_STRING" );
00148     // Empty strings mean leave as is
00149     if ( !ACLString.isEmpty() ) {
00150         acl_t acl = 0;
00151         if ( ACLString == "ACL_DELETE" ) {
00152             // user told us to delete the extended ACL, so let's write only
00153             // the minimal (UNIX permission bits) part
00154             acl = acl_from_mode( perm );
00155         }
00156         acl = acl_from_text( ACLString.latin1() );
00157         if ( acl_valid( acl ) == 0 ) { // let's be safe
00158             ret = acl_set_file( path, ACL_TYPE_ACCESS, acl );
00159             kdDebug(7101) << "Set ACL on: " << path << " to: " << aclAsString( acl ) << endl;
00160         }
00161         acl_free( acl );
00162         if ( ret != 0 ) return ret; // better stop trying right away
00163     }
00164 
00165     if ( directoryDefault && !defaultACLString.isEmpty() ) {
00166         if ( defaultACLString == "ACL_DELETE" ) {
00167             // user told us to delete the default ACL, do so
00168             ret += acl_delete_def_file( path );
00169         } else {
00170             acl_t acl = acl_from_text( defaultACLString.latin1() );
00171             if ( acl_valid( acl ) == 0 ) { // let's be safe
00172                 ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
00173                 kdDebug(7101) << "Set Default ACL on: " << path << " to: " << aclAsString( acl ) << endl;
00174             }
00175             acl_free( acl );
00176         }
00177     }
00178 #endif
00179     return ret;
00180 }
00181 
00182 void FileProtocol::chmod( const KURL& url, int permissions )
00183 {
00184     QCString _path( QFile::encodeName(url.path()) );
00185     /* FIXME: Should be atomic */
00186     if ( ::chmod( _path.data(), permissions ) == -1 ||
00187         ( setACL( _path.data(), permissions, false ) == -1 ) ||
00188         /* if not a directory, cannot set default ACLs */
00189         ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
00190 
00191         switch (errno) {
00192             case EPERM:
00193             case EACCES:
00194                 error( KIO::ERR_ACCESS_DENIED, url.path() );
00195                 break;
00196             case ENOTSUP:
00197                 error( KIO::ERR_UNSUPPORTED_ACTION, url.path() );
00198                 break;
00199             case ENOSPC:
00200                 error( KIO::ERR_DISK_FULL, url.path() );
00201                 break;
00202             default:
00203                 error( KIO::ERR_CANNOT_CHMOD, url.path() );
00204         }
00205     } else
00206         finished();
00207 }
00208 
00209 void FileProtocol::mkdir( const KURL& url, int permissions )
00210 {
00211     QCString _path( QFile::encodeName(url.path()));
00212 
00213     kdDebug(7101) << "mkdir(): " << _path << ", permission = " << permissions << endl;
00214 
00215     KDE_struct_stat buff;
00216     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
00217         if ( ::mkdir( _path.data(), 0777 /*umask will be applied*/ ) != 0 ) {
00218             if ( errno == EACCES ) {
00219           error( KIO::ERR_ACCESS_DENIED, url.path() );
00220           return;
00221             } else if ( errno == ENOSPC ) {
00222           error( KIO::ERR_DISK_FULL, url.path() );
00223           return;
00224             } else {
00225           error( KIO::ERR_COULD_NOT_MKDIR, url.path() );
00226           return;
00227             }
00228         } else {
00229             if ( permissions != -1 )
00230                 chmod( url, permissions );
00231             else
00232                 finished();
00233             return;
00234         }
00235     }
00236 
00237     if ( S_ISDIR( buff.st_mode ) ) {
00238         kdDebug(7101) << "ERR_DIR_ALREADY_EXIST" << endl;
00239         error( KIO::ERR_DIR_ALREADY_EXIST, url.path() );
00240         return;
00241     }
00242     error( KIO::ERR_FILE_ALREADY_EXIST, url.path() );
00243     return;
00244 }
00245 
00246 void FileProtocol::get( const KURL& url )
00247 {
00248     if (!url.isLocalFile()) {
00249         KURL redir(url);
00250     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
00251     redirection(redir);
00252     finished();
00253     return;
00254     }
00255 
00256     QCString _path( QFile::encodeName(url.path()));
00257     KDE_struct_stat buff;
00258     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
00259         if ( errno == EACCES )
00260            error( KIO::ERR_ACCESS_DENIED, url.path() );
00261         else
00262            error( KIO::ERR_DOES_NOT_EXIST, url.path() );
00263         return;
00264     }
00265 
00266     if ( S_ISDIR( buff.st_mode ) ) {
00267         error( KIO::ERR_IS_DIRECTORY, url.path() );
00268         return;
00269     }
00270     if ( !S_ISREG( buff.st_mode ) ) {
00271         error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
00272         return;
00273     }
00274 
00275     int fd = KDE_open( _path.data(), O_RDONLY);
00276     if ( fd < 0 ) {
00277         error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
00278         return;
00279     }
00280 
00281 #ifdef HAVE_FADVISE
00282     posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL);
00283 #endif
00284 
00285     // Determine the mimetype of the file to be retrieved, and emit it.
00286     // This is mandatory in all slaves (for KRun/BrowserRun to work).
00287     KMimeType::Ptr mt = KMimeType::findByURL( url, buff.st_mode, true /* local URL */ );
00288     emit mimeType( mt->name() );
00289 
00290     KIO::filesize_t processed_size = 0;
00291 
00292     QString resumeOffset = metaData("resume");
00293     if ( !resumeOffset.isEmpty() )
00294     {
00295         bool ok;
00296         KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
00297         if (ok && (offset > 0) && (offset < buff.st_size))
00298         {
00299             if (KDE_lseek(fd, offset, SEEK_SET) == offset)
00300             {
00301                 canResume ();
00302                 processed_size = offset;
00303                 kdDebug( 7101 ) << "Resume offset: " << KIO::number(offset) << endl;
00304             }
00305         }
00306     }
00307 
00308     totalSize( buff.st_size );
00309 
00310     char buffer[ MAX_IPC_SIZE ];
00311     QByteArray array;
00312 
00313     while( 1 )
00314     {
00315        int n = ::read( fd, buffer, MAX_IPC_SIZE );
00316        if (n == -1)
00317        {
00318           if (errno == EINTR)
00319               continue;
00320           error( KIO::ERR_COULD_NOT_READ, url.path());
00321           close(fd);
00322           return;
00323        }
00324        if (n == 0)
00325           break; // Finished
00326 
00327        array.setRawData(buffer, n);
00328        data( array );
00329        array.resetRawData(buffer, n);
00330 
00331        processed_size += n;
00332        processedSize( processed_size );
00333 
00334        //kdDebug( 7101 ) << "Processed: " << KIO::number (processed_size) << endl;
00335     }
00336 
00337     data( QByteArray() );
00338 
00339     close( fd );
00340 
00341     processedSize( buff.st_size );
00342     finished();
00343 }
00344 
00345 static int
00346 write_all(int fd, const char *buf, size_t len)
00347 {
00348    while (len > 0)
00349    {
00350       ssize_t written = write(fd, buf, len);
00351       if (written < 0)
00352       {
00353           if (errno == EINTR)
00354              continue;
00355           return -1;
00356       }
00357       buf += written;
00358       len -= written;
00359    }
00360    return 0;
00361 }
00362 
00363 static bool 
00364 same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
00365 {
00366   if (src.st_ino == dest.st_ino &&
00367       src.st_dev == dest.st_dev)
00368     return true;
00369 
00370   return false;
00371 }
00372 
00373 void FileProtocol::put( const KURL& url, int _mode, bool _overwrite, bool _resume )
00374 {
00375     QString dest_orig = url.path();
00376     QCString _dest_orig( QFile::encodeName(dest_orig));
00377 
00378     kdDebug(7101) << "put(): " << dest_orig << ", mode=" << _mode << endl;
00379 
00380     QString dest_part( dest_orig );
00381     dest_part += QString::fromLatin1(".part");
00382     QCString _dest_part( QFile::encodeName(dest_part));
00383 
00384     KDE_struct_stat buff_orig;
00385     bool bOrigExists = (KDE_lstat( _dest_orig.data(), &buff_orig ) != -1);
00386     bool bPartExists = false;
00387     bool bMarkPartial = config()->readBoolEntry("MarkPartial", true);
00388 
00389     if (bMarkPartial)
00390     {
00391         KDE_struct_stat buff_part;
00392         bPartExists = (KDE_stat( _dest_part.data(), &buff_part ) != -1);
00393 
00394         if (bPartExists && !_resume && !_overwrite && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
00395         {
00396             kdDebug(7101) << "FileProtocol::put : calling canResume with "
00397                           << KIO::number(buff_part.st_size) << endl;
00398 
00399             // Maybe we can use this partial file for resuming
00400             // Tell about the size we have, and the app will tell us
00401             // if it's ok to resume or not.
00402             _resume = canResume( buff_part.st_size );
00403 
00404             kdDebug(7101) << "FileProtocol::put got answer " << _resume << endl;
00405         }
00406     }
00407 
00408     if ( bOrigExists && !_overwrite && !_resume)
00409     {
00410         if (S_ISDIR(buff_orig.st_mode))
00411             error( KIO::ERR_DIR_ALREADY_EXIST, dest_orig );
00412         else
00413             error( KIO::ERR_FILE_ALREADY_EXIST, dest_orig );
00414         return;
00415     }
00416 
00417     int result;
00418     QString dest;
00419     QCString _dest;
00420 
00421     int fd = -1;
00422 
00423     // Loop until we got 0 (end of data)
00424     do
00425     {
00426         QByteArray buffer;
00427         dataReq(); // Request for data
00428         result = readData( buffer );
00429 
00430         if (result >= 0)
00431         {
00432             if (dest.isEmpty())
00433             {
00434                 if (bMarkPartial)
00435                 {
00436                     kdDebug(7101) << "Appending .part extension to " << dest_orig << endl;
00437                     dest = dest_part;
00438                     if ( bPartExists && !_resume )
00439                     {
00440                         kdDebug(7101) << "Deleting partial file " << dest_part << endl;
00441                         remove( _dest_part.data() );
00442                         // Catch errors when we try to open the file.
00443                     }
00444                 }
00445                 else
00446                 {
00447                     dest = dest_orig;
00448                     if ( bOrigExists && !_resume )
00449                     {
00450                         kdDebug(7101) << "Deleting destination file " << dest_orig << endl;
00451                         remove( _dest_orig.data() );
00452                         // Catch errors when we try to open the file.
00453                     }
00454                 }
00455 
00456                 _dest = QFile::encodeName(dest);
00457 
00458                 if ( _resume )
00459                 {
00460                     fd = KDE_open( _dest.data(), O_RDWR );  // append if resuming
00461                     KDE_lseek(fd, 0, SEEK_END); // Seek to end
00462                 }
00463                 else
00464                 {
00465                     // WABA: Make sure that we keep writing permissions ourselves,
00466                     // otherwise we can be in for a surprise on NFS.
00467                     mode_t initialMode;
00468                     if (_mode != -1)
00469                         initialMode = _mode | S_IWUSR | S_IRUSR;
00470                     else
00471                         initialMode = 0666;
00472 
00473                     fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
00474                 }
00475 
00476                 if ( fd < 0 )
00477                 {
00478                     kdDebug(7101) << "####################### COULD NOT WRITE " << dest << " _mode=" << _mode << endl;
00479                     kdDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")" << endl;
00480                     if ( errno == EACCES )
00481                         error( KIO::ERR_WRITE_ACCESS_DENIED, dest );
00482                     else
00483                         error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest );
00484                     return;
00485                 }
00486             }
00487 
00488             if (write_all( fd, buffer.data(), buffer.size()))
00489             {
00490                 if ( errno == ENOSPC ) // disk full
00491                 {
00492                   error( KIO::ERR_DISK_FULL, dest_orig);
00493                   result = -2; // means: remove dest file
00494                 }
00495                 else
00496                 {
00497                   kdWarning(7101) << "Couldn't write. Error:" << strerror(errno) << endl;
00498                   error( KIO::ERR_COULD_NOT_WRITE, dest_orig);
00499                   result = -1;
00500                 }
00501             }
00502         }
00503     }
00504     while ( result > 0 );
00505 
00506     // An error occurred deal with it.
00507     if (result < 0)
00508     {
00509         kdDebug(7101) << "Error during 'put'. Aborting." << endl;
00510 
00511         if (fd != -1)
00512         {
00513           close(fd);
00514 
00515           KDE_struct_stat buff;
00516           if (bMarkPartial && KDE_stat( _dest.data(), &buff ) == 0)
00517           {
00518             int size = config()->readNumEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
00519             if (buff.st_size <  size)
00520               remove(_dest.data());
00521           }
00522         }
00523 
00524         ::exit(255);
00525     }
00526 
00527     if ( fd == -1 ) // we got nothing to write out, so we never opened the file
00528     {
00529         finished();
00530         return;
00531     }
00532 
00533     if ( close(fd) )
00534     {
00535         kdWarning(7101) << "Error when closing file descriptor:" << strerror(errno) << endl;
00536         error( KIO::ERR_COULD_NOT_WRITE, dest_orig);
00537         return;
00538     }
00539 
00540     // after full download rename the file back to original name
00541     if ( bMarkPartial )
00542     {
00543         // If the original URL is a symlink and we were asked to overwrite it,
00544         // remove the symlink first. This ensures that we do not overwrite the
00545         // current source if the symlink points to it.
00546         if( _overwrite && S_ISLNK( buff_orig.st_mode ) )
00547           remove( _dest_orig.data() );
00548 
00549         if ( ::rename( _dest.data(), _dest_orig.data() ) )
00550         {
00551             kdWarning(7101) << " Couldn't rename " << _dest << " to " << _dest_orig << endl;
00552             error( KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig );
00553             return;
00554         }
00555     }
00556 
00557     // set final permissions
00558     if ( _mode != -1 && !_resume )
00559     {
00560         if (::chmod(_dest_orig.data(), _mode) != 0)
00561         {
00562             // couldn't chmod. Eat the error if the filesystem apparently doesn't support it.
00563             if ( KIO::testFileSystemFlag( _dest_orig, KIO::SupportsChmod ) )
00564                  warning( i18n( "Could not change permissions for\n%1" ).arg( dest_orig ) );
00565         }
00566     }
00567 
00568     // set modification time
00569     const QString mtimeStr = metaData( "modified" );
00570     if ( !mtimeStr.isEmpty() ) {
00571         QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate );
00572         if ( dt.isValid() ) {
00573             KDE_struct_stat dest_statbuf;
00574             if (KDE_stat( _dest_orig.data(), &dest_statbuf ) == 0) {
00575                 struct utimbuf utbuf;
00576                 utbuf.actime = dest_statbuf.st_atime; // access time, unchanged
00577                 utbuf.modtime = dt.toTime_t(); // modification time
00578                 kdDebug() << k_funcinfo << "setting modtime to " << utbuf.modtime << endl;
00579                 utime( _dest_orig.data(), &utbuf );
00580             }
00581         }
00582 
00583     }
00584 
00585     // We have done our job => finish
00586     finished();
00587 }
00588 
00589 
00590 void FileProtocol::copy( const KURL &src, const KURL &dest,
00591                          int _mode, bool _overwrite )
00592 {
00593     kdDebug(7101) << "copy(): " << src << " -> " << dest << ", mode=" << _mode << endl;
00594 
00595     QCString _src( QFile::encodeName(src.path()));
00596     QCString _dest( QFile::encodeName(dest.path()));
00597     KDE_struct_stat buff_src;
00598 #ifdef USE_POSIX_ACL
00599     acl_t acl;
00600 #endif
00601 
00602     if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
00603         if ( errno == EACCES )
00604            error( KIO::ERR_ACCESS_DENIED, src.path() );
00605         else
00606            error( KIO::ERR_DOES_NOT_EXIST, src.path() );
00607     return;
00608     }
00609 
00610     if ( S_ISDIR( buff_src.st_mode ) ) {
00611     error( KIO::ERR_IS_DIRECTORY, src.path() );
00612     return;
00613     }
00614     if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) {
00615     error( KIO::ERR_CANNOT_OPEN_FOR_READING, src.path() );
00616     return;
00617     }
00618 
00619     KDE_struct_stat buff_dest;
00620     bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00621     if ( dest_exists )
00622     {
00623         if (S_ISDIR(buff_dest.st_mode))
00624         {
00625            error( KIO::ERR_DIR_ALREADY_EXIST, dest.path() );
00626            return;
00627         }
00628 
00629     if ( same_inode( buff_dest, buff_src) ) 
00630     {
00631         error( KIO::ERR_IDENTICAL_FILES, dest.path() );
00632         return;
00633     }
00634 
00635         if (!_overwrite)
00636         {
00637            error( KIO::ERR_FILE_ALREADY_EXIST, dest.path() );
00638            return;
00639         }
00640 
00641         // If the destination is a symlink and overwrite is TRUE,
00642         // remove the symlink first to prevent the scenario where
00643         // the symlink actually points to current source!
00644         if (_overwrite && S_ISLNK(buff_dest.st_mode))
00645         {
00646             kdDebug(7101) << "copy(): LINK DESTINATION" << endl;
00647             remove( _dest.data() );
00648         }
00649     }
00650 
00651     int src_fd = KDE_open( _src.data(), O_RDONLY);
00652     if ( src_fd < 0 ) {
00653     error( KIO::ERR_CANNOT_OPEN_FOR_READING, src.path() );
00654     return;
00655     }
00656 
00657 #ifdef HAVE_FADVISE
00658     posix_fadvise(src_fd,0,0,POSIX_FADV_SEQUENTIAL);
00659 #endif
00660     // WABA: Make sure that we keep writing permissions ourselves,
00661     // otherwise we can be in for a surprise on NFS.
00662     mode_t initialMode;
00663     if (_mode != -1)
00664        initialMode = _mode | S_IWUSR;
00665     else
00666        initialMode = 0666;
00667 
00668     int dest_fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
00669     if ( dest_fd < 0 ) {
00670     kdDebug(7101) << "###### COULD NOT WRITE " << dest.url() << endl;
00671         if ( errno == EACCES ) {
00672             error( KIO::ERR_WRITE_ACCESS_DENIED, dest.path() );
00673         } else {
00674             error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest.path() );
00675         }
00676         close(src_fd);
00677         return;
00678     }
00679 
00680 #ifdef HAVE_FADVISE
00681     posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
00682 #endif
00683 
00684 #ifdef USE_POSIX_ACL
00685     acl = acl_get_fd(src_fd);
00686     if ( acl && !isExtendedACL( acl ) ) {
00687         kdDebug(7101) << _dest.data() << " doesn't have extended ACL" << endl;
00688         acl_free( acl );
00689         acl = NULL;
00690     }
00691 #endif
00692     totalSize( buff_src.st_size );
00693 
00694     KIO::filesize_t processed_size = 0;
00695     char buffer[ MAX_IPC_SIZE ];
00696     int n;
00697 #ifdef USE_SENDFILE
00698     bool use_sendfile=buff_src.st_size < 0x7FFFFFFF;
00699 #endif
00700     while( 1 )
00701     {
00702 #ifdef USE_SENDFILE
00703        if (use_sendfile) {
00704             off_t sf = processed_size;
00705             n = ::sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE );
00706             processed_size = sf;
00707             if ( n == -1 && errno == EINVAL ) { //not all filesystems support sendfile()
00708                 kdDebug(7101) << "sendfile() not supported, falling back " << endl;
00709                 use_sendfile = false;
00710             }
00711        }
00712        if (!use_sendfile)
00713 #endif
00714         n = ::read( src_fd, buffer, MAX_IPC_SIZE );
00715 
00716        if (n == -1)
00717        {
00718           if (errno == EINTR)
00719               continue;
00720 #ifdef USE_SENDFILE
00721           if ( use_sendfile ) {
00722             kdDebug(7101) << "sendfile() error:" << strerror(errno) << endl;
00723             if ( errno == ENOSPC ) // disk full
00724             {
00725                 error( KIO::ERR_DISK_FULL, dest.path());
00726                 remove( _dest.data() );
00727             }
00728             else {
00729                 error( KIO::ERR_SLAVE_DEFINED,
00730                         i18n("Cannot copy file from %1 to %2. (Errno: %3)")
00731                         .arg( src.path() ).arg( dest.path() ).arg( errno ) );
00732             }
00733           } else
00734 #endif
00735           error( KIO::ERR_COULD_NOT_READ, src.path());
00736           close(src_fd);
00737           close(dest_fd);
00738 #ifdef USE_POSIX_ACL
00739           if (acl) acl_free(acl);
00740 #endif
00741           return;
00742        }
00743        if (n == 0)
00744           break; // Finished
00745 #ifdef USE_SENDFILE
00746        if ( !use_sendfile ) {
00747 #endif
00748          if (write_all( dest_fd, buffer, n))
00749          {
00750            close(src_fd);
00751            close(dest_fd);
00752 
00753            if ( errno == ENOSPC ) // disk full
00754            {
00755               error( KIO::ERR_DISK_FULL, dest.path());
00756               remove( _dest.data() );
00757            }
00758            else
00759            {
00760               kdWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno) << endl;
00761               error( KIO::ERR_COULD_NOT_WRITE, dest.path());
00762            }
00763 #ifdef USE_POSIX_ACL
00764            if (acl) acl_free(acl);
00765 #endif
00766            return;
00767          }
00768          processed_size += n;
00769 #ifdef USE_SENDFILE
00770        }
00771 #endif
00772        processedSize( processed_size );
00773     }
00774 
00775     close( src_fd );
00776 
00777     if (close( dest_fd))
00778     {
00779         kdWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno) << endl;
00780         error( KIO::ERR_COULD_NOT_WRITE, dest.path());
00781 #ifdef USE_POSIX_ACL
00782         if (acl) acl_free(acl);
00783 #endif
00784         return;
00785     }
00786 
00787     // set final permissions
00788     if ( _mode != -1 )
00789     {
00790         if ( (::chmod(_dest.data(), _mode) != 0)
00791 #ifdef USE_POSIX_ACL
00792           || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
00793 #endif
00794         )
00795        {
00796         // Eat the error if the filesystem apparently doesn't support chmod.
00797         if ( KIO::testFileSystemFlag( _dest, KIO::SupportsChmod ) )
00798             warning( i18n( "Could not change permissions for\n%1" ).arg( dest.path() ) );
00799        }
00800     }
00801 #ifdef USE_POSIX_ACL
00802     if (acl) acl_free(acl);
00803 #endif
00804 
00805     // copy access and modification time
00806     struct utimbuf ut;
00807     ut.actime = buff_src.st_atime;
00808     ut.modtime = buff_src.st_mtime;
00809     if ( ::utime( _dest.data(), &ut ) != 0 )
00810     {
00811         kdWarning() << QString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg( dest.path() ) << endl;
00812     }
00813 
00814     processedSize( buff_src.st_size );
00815     finished();
00816 }
00817 
00818 void FileProtocol::rename( const KURL &src, const KURL &dest,
00819                            bool _overwrite )
00820 {
00821     QCString _src( QFile::encodeName(src.path()));
00822     QCString _dest( QFile::encodeName(dest.path()));
00823     KDE_struct_stat buff_src;
00824     if ( KDE_lstat( _src.data(), &buff_src ) == -1 ) {
00825         if ( errno == EACCES )
00826            error( KIO::ERR_ACCESS_DENIED, src.path() );
00827         else
00828            error( KIO::ERR_DOES_NOT_EXIST, src.path() );
00829         return;
00830     }
00831 
00832     KDE_struct_stat buff_dest;
00833     bool dest_exists = ( KDE_stat( _dest.data(), &buff_dest ) != -1 );
00834     if ( dest_exists )
00835     {
00836         if (S_ISDIR(buff_dest.st_mode))
00837         {
00838            error( KIO::ERR_DIR_ALREADY_EXIST, dest.path() );
00839            return;
00840         }
00841 
00842     if ( same_inode( buff_dest, buff_src) ) 
00843     {
00844         error( KIO::ERR_IDENTICAL_FILES, dest.path() );
00845         return;
00846     }
00847 
00848         if (!_overwrite)
00849         {
00850            error( KIO::ERR_FILE_ALREADY_EXIST, dest.path() );
00851            return;
00852         }
00853     }
00854 
00855     if ( ::rename( _src.data(), _dest.data()))
00856     {
00857         if (( errno == EACCES ) || (errno == EPERM)) {
00858             error( KIO::ERR_ACCESS_DENIED, dest.path() );
00859         }
00860         else if (errno == EXDEV) {
00861            error( KIO::ERR_UNSUPPORTED_ACTION, QString::fromLatin1("rename"));
00862         }
00863         else if (errno == EROFS) { // The file is on a read-only filesystem
00864            error( KIO::ERR_CANNOT_DELETE, src.path() );
00865         }
00866         else {
00867            error( KIO::ERR_CANNOT_RENAME, src.path() );
00868         }
00869         return;
00870     }
00871 
00872     finished();
00873 }
00874 
00875 void FileProtocol::symlink( const QString &target, const KURL &dest, bool overwrite )
00876 {
00877     // Assume dest is local too (wouldn't be here otherwise)
00878     if ( ::symlink( QFile::encodeName( target ), QFile::encodeName( dest.path() ) ) == -1 )
00879     {
00880         // Does the destination already exist ?
00881         if ( errno == EEXIST )
00882         {
00883             if ( overwrite )
00884             {
00885                 // Try to delete the destination
00886                 if ( unlink( QFile::encodeName( dest.path() ) ) != 0 )
00887                 {
00888                     error( KIO::ERR_CANNOT_DELETE, dest.path() );
00889                     return;
00890                 }
00891                 // Try again - this won't loop forever since unlink succeeded
00892                 symlink( target, dest, overwrite );
00893             }
00894             else
00895             {
00896                 KDE_struct_stat buff_dest;
00897                 KDE_lstat( QFile::encodeName( dest.path() ), &buff_dest );
00898                 if (S_ISDIR(buff_dest.st_mode))
00899                     error( KIO::ERR_DIR_ALREADY_EXIST, dest.path() );
00900                 else
00901                     error( KIO::ERR_FILE_ALREADY_EXIST, dest.path() );
00902                 return;
00903             }
00904         }
00905         else
00906         {
00907             // Some error occurred while we tried to symlink
00908             error( KIO::ERR_CANNOT_SYMLINK, dest.path() );
00909             return;
00910         }
00911     }
00912     finished();
00913 }
00914 
00915 void FileProtocol::del( const KURL& url, bool isfile)
00916 {
00917     QCString _path( QFile::encodeName(url.path()));
00918     /*****
00919      * Delete files
00920      *****/
00921 
00922     if (isfile) {
00923     kdDebug( 7101 ) <<  "Deleting file "<< url.url() << endl;
00924 
00925     // TODO deletingFile( source );
00926 
00927     if ( unlink( _path.data() ) == -1 ) {
00928             if ((errno == EACCES) || (errno == EPERM))
00929                error( KIO::ERR_ACCESS_DENIED, url.path());
00930             else if (errno == EISDIR)
00931                error( KIO::ERR_IS_DIRECTORY, url.path());
00932             else
00933                error( KIO::ERR_CANNOT_DELETE, url.path() );
00934         return;
00935     }
00936     } else {
00937 
00938       /*****
00939        * Delete empty directory
00940        *****/
00941 
00942       kdDebug( 7101 ) << "Deleting directory " << url.url() << endl;
00943 
00944       if ( ::rmdir( _path.data() ) == -1 ) {
00945     if ((errno == EACCES) || (errno == EPERM))
00946       error( KIO::ERR_ACCESS_DENIED, url.path());
00947     else {
00948       kdDebug( 7101 ) << "could not rmdir " << perror << endl;
00949       error( KIO::ERR_COULD_NOT_RMDIR, url.path() );
00950       return;
00951     }
00952       }
00953     }
00954 
00955     finished();
00956 }
00957 
00958 
00959 QString FileProtocol::getUserName( uid_t uid )
00960 {
00961     QString *temp;
00962     temp = usercache.find( uid );
00963     if ( !temp ) {
00964         struct passwd *user = getpwuid( uid );
00965         if ( user ) {
00966             usercache.insert( uid, new QString(QString::fromLatin1(user->pw_name)) );
00967             return QString::fromLatin1( user->pw_name );
00968         }
00969         else
00970             return QString::number( uid );
00971     }
00972     else
00973         return *temp;
00974 }
00975 
00976 QString FileProtocol::getGroupName( gid_t gid )
00977 {
00978     QString *temp;
00979     temp = groupcache.find( gid );
00980     if ( !temp ) {
00981         struct group *grp = getgrgid( gid );
00982         if ( grp ) {
00983             groupcache.insert( gid, new QString(QString::fromLatin1(grp->gr_name)) );
00984             return QString::fromLatin1( grp->gr_name );
00985         }
00986         else
00987             return QString::number( gid );
00988     }
00989     else
00990         return *temp;
00991 }
00992 
00993 
00994 
00995 bool FileProtocol::createUDSEntry( const QString & filename, const QCString & path, UDSEntry & entry,
00996                                    short int details, bool withACL )
00997 {
00998     assert(entry.count() == 0); // by contract :-)
00999     // Note: details = 0 (only "file or directory or symlink or doesn't exist") isn't implemented
01000     // because there's no real performance penalty in kio_file for returning the complete
01001     // details. Please consider doing it in your kioslave if you're using this one as a model :)
01002     UDSAtom atom;
01003     atom.m_uds = KIO::UDS_NAME;
01004     atom.m_str = filename;
01005     entry.append( atom );
01006 
01007     mode_t type;
01008     mode_t access;
01009     KDE_struct_stat buff;
01010 
01011     if ( KDE_lstat( path.data(), &buff ) == 0 )  {
01012 
01013         if (S_ISLNK(buff.st_mode)) {
01014 
01015             char buffer2[ 1000 ];
01016             int n = readlink( path.data(), buffer2, 1000 );
01017             if ( n != -1 ) {
01018                 buffer2[ n ] = 0;
01019             }
01020 
01021             atom.m_uds = KIO::UDS_LINK_DEST;
01022             atom.m_str = QFile::decodeName( buffer2 );
01023             entry.append( atom );
01024 
01025             // A symlink -> follow it only if details>1
01026             if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) {
01027                 // It is a link pointing to nowhere
01028                 type = S_IFMT - 1;
01029                 access = S_IRWXU | S_IRWXG | S_IRWXO;
01030 
01031                 atom.m_uds = KIO::UDS_FILE_TYPE;
01032                 atom.m_long = type;
01033                 entry.append( atom );
01034 
01035                 atom.m_uds = KIO::UDS_ACCESS;
01036                 atom.m_long = access;
01037                 entry.append( atom );
01038 
01039                 atom.m_uds = KIO::UDS_SIZE;
01040                 atom.m_long = 0L;
01041                 entry.append( atom );
01042 
01043                 goto notype;
01044 
01045             }
01046         }
01047     } else {
01048         // kdWarning() << "lstat didn't work on " << path.data() << endl;
01049         return false;
01050     }
01051 
01052     type = buff.st_mode & S_IFMT; // extract file type
01053     access = buff.st_mode & 07777; // extract permissions
01054 
01055     atom.m_uds = KIO::UDS_FILE_TYPE;
01056     atom.m_long = type;
01057     entry.append( atom );
01058 
01059     atom.m_uds = KIO::UDS_ACCESS;
01060     atom.m_long = access;
01061     entry.append( atom );
01062 
01063     atom.m_uds = KIO::UDS_SIZE;
01064     atom.m_long = buff.st_size;
01065     entry.append( atom );
01066 
01067 #ifdef USE_POSIX_ACL
01068     /* Append an atom indicating whether the file has extended acl information
01069      * and if withACL is specified also one with the acl itself. If it's a directory
01070      * and it has a default ACL, also append that. */
01071     appendACLAtoms( path, entry, type, withACL );
01072 #endif
01073 
01074  notype:
01075     atom.m_uds = KIO::UDS_MODIFICATION_TIME;
01076     atom.m_long = buff.st_mtime;
01077     entry.append( atom );
01078 
01079     atom.m_uds = KIO::UDS_USER;
01080     atom.m_str = getUserName( buff.st_uid );
01081     entry.append( atom );
01082 
01083     atom.m_uds = KIO::UDS_GROUP;
01084     atom.m_str = getGroupName( buff.st_gid );
01085     entry.append( atom );
01086 
01087     atom.m_uds = KIO::UDS_ACCESS_TIME;
01088     atom.m_long = buff.st_atime;
01089     entry.append( atom );
01090 
01091     // Note: buff.st_ctime isn't the creation time !
01092     // We made that mistake for KDE 2.0, but it's in fact the
01093     // "file status" change time, which we don't care about.
01094 
01095     return true;
01096 }
01097 
01098 void FileProtocol::stat( const KURL & url )
01099 {
01100     if (!url.isLocalFile()) {
01101         KURL redir(url);
01102     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
01103     redirection(redir);
01104     kdDebug(7101) << "redirecting to " << redir.url() << endl;
01105     finished();
01106     return;
01107     }
01108 
01109     /* directories may not have a slash at the end if
01110      * we want to stat() them; it requires that we
01111      * change into it .. which may not be allowed
01112      * stat("/is/unaccessible")  -> rwx------
01113      * stat("/is/unaccessible/") -> EPERM            H.Z.
01114      * This is the reason for the -1
01115      */
01116     QCString _path( QFile::encodeName(url.path(-1)));
01117 
01118     QString sDetails = metaData(QString::fromLatin1("details"));
01119     int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
01120     kdDebug(7101) << "FileProtocol::stat details=" << details << endl;
01121 
01122     UDSEntry entry;
01123     if ( !createUDSEntry( url.fileName(), _path, entry, details, true /*with acls*/ ) )
01124     {
01125         error( KIO::ERR_DOES_NOT_EXIST, url.path(-1) );
01126         return;
01127     }
01128 #if 0
01130     KIO::UDSEntry::ConstIterator it = entry.begin();
01131     for( ; it != entry.end(); it++ ) {
01132         switch ((*it).m_uds) {
01133             case KIO::UDS_FILE_TYPE:
01134                 kdDebug(7101) << "File Type : " << (mode_t)((*it).m_long) << endl;
01135                 break;
01136             case KIO::UDS_ACCESS:
01137                 kdDebug(7101) << "Access permissions : " << (mode_t)((*it).m_long) << endl;
01138                 break;
01139             case KIO::UDS_USER:
01140                 kdDebug(7101) << "User : " << ((*it).m_str.ascii() ) << endl;
01141                 break;
01142             case KIO::UDS_GROUP:
01143                 kdDebug(7101) << "Group : " << ((*it).m_str.ascii() ) << endl;
01144                 break;
01145             case KIO::UDS_NAME:
01146                 kdDebug(7101) << "Name : " << ((*it).m_str.ascii() ) << endl;
01147                 //m_strText = decodeFileName( (*it).m_str );
01148                 break;
01149             case KIO::UDS_URL:
01150                 kdDebug(7101) << "URL : " << ((*it).m_str.ascii() ) << endl;
01151                 break;
01152             case KIO::UDS_MIME_TYPE:
01153                 kdDebug(7101) << "MimeType : " << ((*it).m_str.ascii() ) << endl;
01154                 break;
01155             case KIO::UDS_LINK_DEST:
01156                 kdDebug(7101) << "LinkDest : " << ((*it).m_str.ascii() ) << endl;
01157                 break;
01158             case KIO::UDS_EXTENDED_ACL:
01159                 kdDebug(7101) << "Contains extended ACL " << endl;
01160                 break;
01161         }
01162     }
01163     MetaData::iterator it1 = mOutgoingMetaData.begin();
01164     for ( ; it1 != mOutgoingMetaData.end(); it1++ ) {
01165         kdDebug(7101) << it1.key() << " = " << it1.data() << endl;
01166     }
01168 #endif
01169     statEntry( entry );
01170 
01171     finished();
01172 }
01173 
01174 void FileProtocol::listDir( const KURL& url)
01175 {
01176     kdDebug(7101) << "========= LIST " << url.url() << " =========" << endl;
01177     if (!url.isLocalFile()) {
01178         KURL redir(url);
01179     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
01180     redirection(redir);
01181     kdDebug(7101) << "redirecting to " << redir.url() << endl;
01182     finished();
01183     return;
01184     }
01185 
01186     QCString _path( QFile::encodeName(url.path()));
01187 
01188     KDE_struct_stat buff;
01189     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
01190     error( KIO::ERR_DOES_NOT_EXIST, url.path() );
01191     return;
01192     }
01193 
01194     if ( !S_ISDIR( buff.st_mode ) ) {
01195     error( KIO::ERR_IS_FILE, url.path() );
01196     return;
01197     }
01198 
01199     DIR *dp = 0L;
01200     KDE_struct_dirent *ep;
01201 
01202     dp = opendir( _path.data() );
01203     if ( dp == 0 ) {
01204         switch (errno)
01205         {
01206 #ifdef ENOMEDIUM
01207     case ENOMEDIUM:
01208             error( ERR_SLAVE_DEFINED,
01209                    i18n( "No media in device for %1" ).arg( url.path() ) );
01210             break;
01211 #endif
01212         default:
01213             error( KIO::ERR_CANNOT_ENTER_DIRECTORY, url.path() );
01214             break;
01215         }
01216     return;
01217     }
01218 
01219     // Don't make this a QStringList. The locale file name we get here
01220     // should be passed intact to createUDSEntry to avoid problems with
01221     // files where QFile::encodeName(QFile::decodeName(a)) != a.
01222     QStrList entryNames;
01223 
01224     while ( ( ep = KDE_readdir( dp ) ) != 0L )
01225     entryNames.append( ep->d_name );
01226 
01227     closedir( dp );
01228     totalSize( entryNames.count() );
01229 
01230     /* set the current dir to the path to speed up
01231        in not having to pass an absolute path.
01232        We restore the path later to get out of the
01233        path - the kernel wouldn't unmount or delete
01234        directories we keep as active directory. And
01235        as the slave runs in the background, it's hard
01236        to see for the user what the problem would be */
01237     char path_buffer[PATH_MAX];
01238     (void) getcwd(path_buffer, PATH_MAX - 1);
01239     if ( chdir( _path.data() ) )  {
01240         if (errno == EACCES)
01241             error(ERR_ACCESS_DENIED, _path);
01242         else
01243             error(ERR_CANNOT_ENTER_DIRECTORY, _path);
01244         finished();
01245     }
01246 
01247     UDSEntry entry;
01248     QStrListIterator it(entryNames);
01249     for (; it.current(); ++it) {
01250         entry.clear();
01251         if ( createUDSEntry( QFile::decodeName(*it),
01252                              *it /* we can use the filename as relative path*/,
01253                              entry, 2, true ) )
01254           listEntry( entry, false);
01255         //else
01256         // ;//Well, this should never happen... but with wrong encoding names
01257     }
01258 
01259     listEntry( entry, true ); // ready
01260 
01261     kdDebug(7101) << "============= COMPLETED LIST ============" << endl;
01262 
01263     chdir(path_buffer);
01264 
01265     finished();
01266 }
01267 
01268 /*
01269 void FileProtocol::testDir( const QString& path )
01270 {
01271     QCString _path( QFile::encodeName(path));
01272     KDE_struct_stat buff;
01273     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
01274     error( KIO::ERR_DOES_NOT_EXIST, path );
01275     return;
01276     }
01277 
01278     if ( S_ISDIR( buff.st_mode ) )
01279     isDirectory();
01280     else
01281     isFile();
01282 
01283     finished();
01284 }
01285 */
01286 
01287 void FileProtocol::special( const QByteArray &data)
01288 {
01289     int tmp;
01290     QDataStream stream(data, IO_ReadOnly);
01291 
01292     stream >> tmp;
01293     switch (tmp) {
01294     case 1:
01295       {
01296     QString fstype, dev, point;
01297     Q_INT8 iRo;
01298 
01299     stream >> iRo >> fstype >> dev >> point;
01300 
01301     bool ro = ( iRo != 0 );
01302 
01303     kdDebug(7101) << "MOUNTING fstype=" << fstype << " dev=" << dev << " point=" << point << " ro=" << ro << endl;
01304     bool ok = pmount( dev );
01305     if (ok)
01306         finished();
01307     else
01308         mount( ro, fstype.ascii(), dev, point );
01309 
01310       }
01311       break;
01312     case 2:
01313       {
01314     QString point;
01315     stream >> point;
01316     bool ok = pumount( point );
01317     if (ok)
01318         finished();
01319     else
01320         unmount( point );
01321       }
01322       break;
01323 
01324     case 3:
01325     {
01326       QString filename;
01327       stream >> filename;
01328       KShred shred( filename );
01329       connect( &shred, SIGNAL( processedSize( KIO::filesize_t ) ),
01330                this, SLOT( slotProcessedSize( KIO::filesize_t ) ) );
01331       connect( &shred, SIGNAL( infoMessage( const QString & ) ),
01332                this, SLOT( slotInfoMessage( const QString & ) ) );
01333       if (!shred.shred())
01334           error( KIO::ERR_CANNOT_DELETE, filename );
01335       else
01336           finished();
01337       break;
01338     }
01339     default:
01340       break;
01341     }
01342 }
01343 
01344 // Connected to KShred
01345 void FileProtocol::slotProcessedSize( KIO::filesize_t bytes )
01346 {
01347   kdDebug(7101) << "FileProtocol::slotProcessedSize (" << (unsigned int) bytes << ")" << endl;
01348   processedSize( bytes );
01349 }
01350 
01351 // Connected to KShred
01352 void FileProtocol::slotInfoMessage( const QString & msg )
01353 {
01354   kdDebug(7101) << "FileProtocol::slotInfoMessage (" << msg << ")" << endl;
01355   infoMessage( msg );
01356 }
01357 
01358 void FileProtocol::mount( bool _ro, const char *_fstype, const QString& _dev, const QString& _point )
01359 {
01360     kdDebug(7101) << "FileProtocol::mount _fstype=" << _fstype << endl;
01361     QCString buffer;
01362 
01363 #ifdef HAVE_VOLMGT
01364     /*
01365      *  support for Solaris volume management
01366      */
01367     QString err;
01368     QCString devname = QFile::encodeName( _dev );
01369 
01370     if( volmgt_running() ) {
01371 //      kdDebug(7101) << "VOLMGT: vold ok." << endl;
01372         if( volmgt_check( devname.data() ) == 0 ) {
01373             kdDebug(7101) << "VOLMGT: no media in "
01374                     << devname.data() << endl;
01375             err = i18n("No Media inserted or Media not recognized.");
01376             error( KIO::ERR_COULD_NOT_MOUNT, err );
01377             return;
01378         } else {
01379             kdDebug(7101) << "VOLMGT: " << devname.data()
01380                 << ": media ok" << endl;
01381             finished();
01382             return;
01383         }
01384     } else {
01385         err = i18n("\"vold\" is not running.");
01386         kdDebug(7101) << "VOLMGT: " << err << endl;
01387         error( KIO::ERR_COULD_NOT_MOUNT, err );
01388         return;
01389     }
01390 #else
01391 
01392 
01393     KTempFile tmpFile;
01394     QCString tmpFileC = QFile::encodeName(tmpFile.name());
01395     const char *tmp = tmpFileC.data();
01396     QCString dev;
01397     if ( _dev.startsWith( "LABEL=" ) ) { // turn LABEL=foo into -L foo (#71430)
01398         QString labelName = _dev.mid( 6 );
01399         dev = "-L ";
01400         dev += QFile::encodeName( KProcess::quote( labelName ) ); // is it correct to assume same encoding as filesystem?
01401     } else if ( _dev.startsWith( "UUID=" ) ) { // and UUID=bar into -U bar
01402         QString uuidName = _dev.mid( 5 );
01403         dev = "-U ";
01404         dev += QFile::encodeName( KProcess::quote( uuidName ) );
01405     }
01406     else
01407         dev = QFile::encodeName( KProcess::quote(_dev) ); // get those ready to be given to a shell
01408 
01409     QCString point = QFile::encodeName( KProcess::quote(_point) );
01410     bool fstype_empty = !_fstype || !*_fstype;
01411     QCString fstype = KProcess::quote(_fstype).latin1(); // good guess
01412     QCString readonly = _ro ? "-r" : "";
01413     QString epath = QString::fromLatin1(getenv("PATH"));
01414     QString path = QString::fromLatin1("/sbin:/bin");
01415     if(!epath.isEmpty())
01416         path += QString::fromLatin1(":") + epath;
01417     QString mountProg = KGlobal::dirs()->findExe("mount", path);
01418     if (mountProg.isEmpty()){
01419       error( KIO::ERR_COULD_NOT_MOUNT, i18n("Could not find program \"mount\""));
01420       return;
01421     }
01422 
01423     // Two steps, in case mount doesn't like it when we pass all options
01424     for ( int step = 0 ; step <= 1 ; step++ )
01425     {
01426         // Mount using device only if no fstype nor mountpoint (KDE-1.x like)
01427         if ( !_dev.isEmpty() && _point.isEmpty() && fstype_empty )
01428             buffer.sprintf( "%s %s 2>%s", mountProg.latin1(), dev.data(), tmp );
01429         else
01430           // Mount using the mountpoint, if no fstype nor device (impossible in first step)
01431           if ( !_point.isEmpty() && _dev.isEmpty() && fstype_empty )
01432             buffer.sprintf( "%s %s 2>%s", mountProg.latin1(), point.data(), tmp );
01433           else
01434             // mount giving device + mountpoint but no fstype
01435             if ( !_point.isEmpty() && !_dev.isEmpty() && fstype_empty )
01436               buffer.sprintf( "%s %s %s %s 2>%s", mountProg.latin1(), readonly.data(), dev.data(), point.data(), tmp );
01437             else
01438               // mount giving device + mountpoint + fstype
01439 #if defined(__svr4__) && defined(__sun__) // MARCO for Solaris 8 and I
01440                 // believe this is true for SVR4 in general
01441                 buffer.sprintf( "%s -F %s %s %s %s 2>%s"
01442                 mountProg.latin1()
01443                                 fstype.data()
01444                                 _ro ? "-oro" : ""
01445                                 dev.data()
01446                                 point.data()
01447                                 tmp );
01448 #else
01449               buffer.sprintf( "%s %s -t %s %s %s 2>%s", mountProg.latin1(), readonly.data(),
01450                               fstype.data(), dev.data(), point.data(), tmp );
01451 #endif
01452 
01453         kdDebug(7101) << buffer << endl;
01454 
01455         int mount_ret = system( buffer.data() );
01456 
01457         QString err = testLogFile( tmp );
01458         if ( err.isEmpty() && mount_ret == 0)
01459         {
01460             finished();
01461             return;
01462         }
01463         else
01464         {
01465             // Didn't work - or maybe we just got a warning
01466             QString mp = KIO::findDeviceMountPoint( _dev );
01467             // Is the device mounted ?
01468             if ( !mp.isEmpty() && mount_ret == 0)
01469             {
01470                 kdDebug(7101) << "mount got a warning: " << err << endl;
01471                 warning( err );
01472                 finished();
01473                 return;
01474             }
01475             else
01476             {
01477                 if ( (step == 0) && !_point.isEmpty())
01478                 {
01479                     kdDebug(7101) << err << endl;
01480                     kdDebug(7101) << "Mounting with those options didn't work, trying with only mountpoint" << endl;
01481                     fstype = "";
01482                     fstype_empty = true;
01483                     dev = "";
01484                     // The reason for trying with only mountpoint (instead of
01485                     // only device) is that some people (hi Malte!) have the
01486                     // same device associated with two mountpoints
01487                     // for different fstypes, like /dev/fd0 /mnt/e2floppy and
01488                     // /dev/fd0 /mnt/dosfloppy.
01489                     // If the user has the same mountpoint associated with two
01490                     // different devices, well they shouldn't specify the
01491                     // mountpoint but just the device.
01492                 }
01493                 else
01494                 {
01495                     error( KIO::ERR_COULD_NOT_MOUNT, err );
01496                     return;
01497                 }
01498             }
01499         }
01500     }
01501 #endif /* ! HAVE_VOLMGT */
01502 }
01503 
01504 
01505 void FileProtocol::unmount( const QString& _point )
01506 {
01507     QCString buffer;
01508 
01509     KTempFile tmpFile;
01510     QCString tmpFileC = QFile::encodeName(tmpFile.name());
01511     QString err;
01512     const char *tmp = tmpFileC.data();
01513 
01514 #ifdef HAVE_VOLMGT
01515     /*
01516      *  support for Solaris volume management
01517      */
01518     char *devname;
01519     char *ptr;
01520     FILE *mnttab;
01521     struct mnttab mnt;
01522 
01523     if( volmgt_running() ) {
01524         kdDebug(7101) << "VOLMGT: looking for "
01525             << _point.local8Bit() << endl;
01526 
01527         if( (mnttab = KDE_fopen( MNTTAB, "r" )) == NULL ) {
01528             err = "couldn't open mnttab";
01529             kdDebug(7101) << "VOLMGT: " << err << endl;
01530             error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01531             return;
01532         }
01533 
01534         /*
01535          *  since there's no way to derive the device name from
01536          *  the mount point through the volmgt library (and
01537          *  media_findname() won't work in this case), we have to
01538          *  look ourselves...
01539          */
01540         devname = NULL;
01541         rewind( mnttab );
01542         while( getmntent( mnttab, &mnt ) == 0 ) {
01543             if( strcmp( _point.local8Bit(), mnt.mnt_mountp ) == 0 ){
01544                 devname = mnt.mnt_special;
01545                 break;
01546             }
01547         }
01548         fclose( mnttab );
01549 
01550         if( devname == NULL ) {
01551             err = "not in mnttab";
01552             kdDebug(7101) << "VOLMGT: "
01553                 << QFile::encodeName(_point).data()
01554                 << ": " << err << endl;
01555             error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01556             return;
01557         }
01558 
01559         /*
01560          *  strip off the directory name (volume name)
01561          *  the eject(1) command will handle unmounting and
01562          *  physically eject the media (if possible)
01563          */
01564         ptr = strrchr( devname, '/' );
01565         *ptr = '\0';
01566                 QCString qdevname(QFile::encodeName(KProcess::quote(QFile::decodeName(QCString(devname)))).data());
01567         buffer.sprintf( "/usr/bin/eject %s 2>%s", qdevname.data(), tmp );
01568         kdDebug(7101) << "VOLMGT: eject " << qdevname << endl;
01569 
01570         /*
01571          *  from eject(1): exit status == 0 => need to manually eject
01572          *                 exit status == 4 => media was ejected
01573          */
01574 //      if( WEXITSTATUS( system( buffer.local8Bit() )) == 4 ) {
01575         if( WEXITSTATUS( system( buffer.data() )) == 4 ) {  // Fix for QString -> QCString?
01576             /*
01577              *  this is not an error, so skip "testLogFile()"
01578              *  to avoid wrong/confusing error popup
01579              */
01580             unlink( tmp );
01581             finished();
01582             return;
01583         }
01584     } else {
01585         /*
01586          *  eject(1) should do its job without vold(1M) running,
01587          *  so we probably could call eject anyway, but since the
01588          *  media is mounted now, vold must've died for some reason
01589          *  during the user's session, so it should be restarted...
01590          */
01591         err = i18n("\"vold\" is not running.");
01592         kdDebug(7101) << "VOLMGT: " << err << endl;
01593         error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01594         return;
01595     }
01596 #else
01597     QString epath = getenv("PATH");
01598     QString path = QString::fromLatin1("/sbin:/bin");
01599     if (!epath.isEmpty())
01600        path += ":" + epath;
01601     QString umountProg = KGlobal::dirs()->findExe("umount", path);
01602 
01603     if (umountProg.isEmpty()) {
01604         error( KIO::ERR_COULD_NOT_UNMOUNT, i18n("Could not find program \"umount\""));
01605         return;
01606     }
01607     buffer.sprintf( "%s %s 2>%s", umountProg.latin1(), QFile::encodeName(KProcess::quote(_point)).data(), tmp );
01608     system( buffer.data() );
01609 #endif /* HAVE_VOLMGT */
01610 
01611     err = testLogFile( tmp );
01612     if ( err.isEmpty() )
01613     finished();
01614     else
01615         error( KIO::ERR_COULD_NOT_UNMOUNT, err );
01616 }
01617 
01618 /*************************************
01619  *
01620  * pmount handling
01621  *
01622  *************************************/
01623 
01624 bool FileProtocol::pmount(const QString &dev)
01625 {
01626     QString epath = getenv("PATH");
01627     QString path = QString::fromLatin1("/sbin:/bin");
01628     if (!epath.isEmpty())
01629         path += ":" + epath;
01630     QString pmountProg = KGlobal::dirs()->findExe("pmount", path);
01631 
01632     if (pmountProg.isEmpty())
01633         return false;
01634 
01635     QCString buffer;
01636     buffer.sprintf( "%s %s", QFile::encodeName(pmountProg).data(),
01637                     QFile::encodeName(KProcess::quote(dev)).data() );
01638 
01639     int res = system( buffer.data() );
01640 
01641     return res==0;
01642 }
01643 
01644 bool FileProtocol::pumount(const QString &point)
01645 {
01646     QString real_point = KStandardDirs::realPath(point);
01647 
01648     KMountPoint::List mtab = KMountPoint::currentMountPoints();
01649 
01650     KMountPoint::List::const_iterator it = mtab.begin();
01651     KMountPoint::List::const_iterator end = mtab.end();
01652 
01653     QString dev;
01654 
01655     for (; it!=end; ++it)
01656     {
01657         QString tmp = (*it)->mountedFrom();
01658         QString mp = (*it)->mountPoint();
01659         mp = KStandardDirs::realPath(mp);
01660 
01661         if (mp==real_point)
01662             dev = KStandardDirs::realPath(tmp);
01663     }
01664 
01665     if (dev.isEmpty()) return false;
01666     if (dev.endsWith("/")) dev.truncate(dev.length()-1);
01667 
01668     QString epath = getenv("PATH");
01669     QString path = QString::fromLatin1("/sbin:/bin");
01670     if (!epath.isEmpty())
01671         path += ":" + epath;
01672     QString pumountProg = KGlobal::dirs()->findExe("pumount", path);
01673 
01674     if (pumountProg.isEmpty())
01675         return false;
01676 
01677     QCString buffer;
01678     buffer.sprintf( "%s %s", QFile::encodeName(pumountProg).data(),
01679                     QFile::encodeName(KProcess::quote(dev)).data() );
01680 
01681     int res = system( buffer.data() );
01682 
01683     return res==0;
01684 }
01685 
01686 /*************************************
01687  *
01688  * Utilities
01689  *
01690  *************************************/
01691 
01692 static QString testLogFile( const char *_filename )
01693 {
01694     char buffer[ 1024 ];
01695     KDE_struct_stat buff;
01696 
01697     QString result;
01698 
01699     KDE_stat( _filename, &buff );
01700     int size = buff.st_size;
01701     if ( size == 0 ) {
01702     unlink( _filename );
01703     return result;
01704     }
01705 
01706     FILE * f = KDE_fopen( _filename, "rb" );
01707     if ( f == 0L ) {
01708     unlink( _filename );
01709     result = i18n("Could not read %1").arg(QFile::decodeName(_filename));
01710     return result;
01711     }
01712 
01713     result = "";
01714     const char *p = "";
01715     while ( p != 0L ) {
01716     p = fgets( buffer, sizeof(buffer)-1, f );
01717     if ( p != 0L )
01718         result += QString::fromLocal8Bit(buffer);
01719     }
01720 
01721     fclose( f );
01722 
01723     unlink( _filename );
01724 
01725     return result;
01726 }
01727 
01728 /*************************************
01729  *
01730  * ACL handling helpers
01731  *
01732  *************************************/
01733 #ifdef USE_POSIX_ACL
01734 
01735 static bool isExtendedACL( acl_t acl )
01736 {
01737     return ( acl_equiv_mode( acl, 0 ) != 0 );
01738 }
01739 
01740 static QString aclAsString(  acl_t acl )
01741 {
01742     char *aclString = acl_to_text( acl, 0 );
01743     QString ret = QString::fromLatin1( aclString );
01744     acl_free( (void*)aclString );
01745     return ret;
01746 }
01747 
01748 static void appendACLAtoms( const QCString & path, UDSEntry& entry, mode_t type, bool withACL )
01749 {
01750     // first check for a noop
01751 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
01752     if ( acl_extended_file( path.data() ) == 0 ) return;
01753 #endif
01754 
01755     acl_t acl = 0;
01756     acl_t defaultAcl = 0;
01757     UDSAtom atom;
01758     bool isDir = S_ISDIR( type );
01759     // do we have an acl for the file, and/or a default acl for the dir, if it is one?
01760     if ( ( acl = acl_get_file( path.data(), ACL_TYPE_ACCESS ) ) ) {
01761         if ( !isExtendedACL( acl ) ) {
01762             acl_free( acl );
01763             acl = 0;
01764         }
01765     }
01766 
01767     /* Sadly libacl does not provided a means of checking for extended ACL and default
01768      * ACL separately. Since a directory can have both, we need to check again. */
01769     if ( isDir )
01770         defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
01771 
01772     if ( acl || defaultAcl ) {
01773       kdDebug(7101) << path.data() << " has extended ACL entries " << endl;
01774       atom.m_uds = KIO::UDS_EXTENDED_ACL;
01775       atom.m_long = 1;
01776       entry.append( atom );
01777     }
01778     if ( withACL ) {
01779         if ( acl ) {
01780             atom.m_uds = KIO::UDS_ACL_STRING;
01781             atom.m_str = aclAsString( acl );
01782             entry.append( atom );
01783             kdDebug(7101) << path.data() << "ACL: " << atom.m_str << endl;
01784         }
01785         if ( defaultAcl ) {
01786             atom.m_uds = KIO::UDS_DEFAULT_ACL_STRING;
01787             atom.m_str = aclAsString( defaultAcl );
01788             entry.append( atom );
01789             kdDebug(7101) << path.data() << "DEFAULT ACL: " << atom.m_str << endl;
01790         }
01791     }
01792     if ( acl ) acl_free( acl );
01793     if ( defaultAcl ) acl_free( defaultAcl );
01794 }
01795 #endif
01796 
01797 #include "file.moc"

kioslave

Skip menu "kioslave"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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