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

KIOSlave

file_unix.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    Copyright (C) 2007 Christian Ehrlicher <ch.ehrlicher@gmx.de>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License (LGPL) as published by the Free Software Foundation;
00012    either version 2 of the License, or (at your option) any later
00013    version.
00014 
00015    This library is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018    Library General Public License for more details.
00019 
00020    You should have received a copy of the GNU Library General Public License
00021    along with this library; see the file COPYING.LIB.  If not, write to
00022    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00023    Boston, MA 02110-1301, USA.
00024 */
00025 
00026 #include "file.h"
00027 
00028 #include <config.h>
00029 
00030 #include <QtCore/QFile>
00031 
00032 #include <kde_file.h>
00033 #include <kdebug.h>
00034 #include <kconfiggroup.h>
00035 #include <kmountpoint.h>
00036 
00037 #include <dirent.h>
00038 #include <errno.h>
00039 #include <fcntl.h>
00040 #include <grp.h>
00041 #include <utime.h>
00042 #include <pwd.h>
00043 
00044 #if defined(HAVE_LIMITS_H)
00045 #include <limits.h>  // PATH_MAX
00046 #endif
00047 
00048 using namespace KIO;
00049 
00050 #define MAX_IPC_SIZE (1024*32)
00051 
00052 static bool
00053 same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
00054 {
00055    if (src.st_ino == dest.st_ino &&
00056        src.st_dev == dest.st_dev)
00057      return true;
00058 
00059    return false;
00060 }
00061 
00062 extern int write_all(int fd, const char *buf, size_t len);
00063 
00064 void FileProtocol::copy( const KUrl &src, const KUrl &dest,
00065                          int _mode, JobFlags _flags )
00066 {
00067     kDebug(7101) << "copy(): " << src << " -> " << dest << ", mode=" << _mode;
00068 
00069     QByteArray _src( QFile::encodeName(src.toLocalFile()));
00070     QByteArray _dest( QFile::encodeName(dest.toLocalFile()));
00071     KDE_struct_stat buff_src;
00072 #ifdef HAVE_POSIX_ACL
00073     acl_t acl;
00074 #endif
00075 
00076     if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
00077         if ( errno == EACCES )
00078            error( KIO::ERR_ACCESS_DENIED, _src );
00079         else
00080            error( KIO::ERR_DOES_NOT_EXIST, _src );
00081     return;
00082     }
00083 
00084     if ( S_ISDIR( buff_src.st_mode ) ) {
00085     error( KIO::ERR_IS_DIRECTORY, src.path() );
00086     return;
00087     }
00088     if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) {
00089     error( KIO::ERR_CANNOT_OPEN_FOR_READING, src.path() );
00090     return;
00091     }
00092 
00093     KDE_struct_stat buff_dest;
00094     bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00095     if ( dest_exists )
00096     {
00097         if (S_ISDIR(buff_dest.st_mode))
00098         {
00099            error( KIO::ERR_DIR_ALREADY_EXIST, _dest );
00100            return;
00101         }
00102 
00103     if ( same_inode( buff_dest, buff_src) )
00104     {
00105         error( KIO::ERR_IDENTICAL_FILES, _dest );
00106         return;
00107     }
00108 
00109         if (!(_flags & KIO::Overwrite))
00110         {
00111            error( KIO::ERR_FILE_ALREADY_EXIST, _dest );
00112            return;
00113         }
00114 
00115         // If the destination is a symlink and overwrite is TRUE,
00116         // remove the symlink first to prevent the scenario where
00117         // the symlink actually points to current source!
00118         if ((_flags & KIO::Overwrite) && S_ISLNK(buff_dest.st_mode))
00119         {
00120             //kDebug(7101) << "copy(): LINK DESTINATION";
00121             remove( _dest.data() );
00122         }
00123     }
00124 
00125     int src_fd = KDE_open( _src.data(), O_RDONLY);
00126     if ( src_fd < 0 ) {
00127     error( KIO::ERR_CANNOT_OPEN_FOR_READING, _src );
00128     return;
00129     }
00130 
00131 #ifdef HAVE_FADVISE
00132     posix_fadvise(src_fd,0,0,POSIX_FADV_SEQUENTIAL);
00133 #endif
00134     // WABA: Make sure that we keep writing permissions ourselves,
00135     // otherwise we can be in for a surprise on NFS.
00136     mode_t initialMode;
00137     if (_mode != -1)
00138        initialMode = _mode | S_IWUSR;
00139     else
00140        initialMode = 0666;
00141 
00142     int dest_fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
00143     if ( dest_fd < 0 ) {
00144     kDebug(7101) << "###### COULD NOT WRITE " << dest.url();
00145         if ( errno == EACCES ) {
00146             error( KIO::ERR_WRITE_ACCESS_DENIED, _dest );
00147         } else {
00148             error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, _dest );
00149         }
00150         ::close(src_fd);
00151         return;
00152     }
00153 
00154 #ifdef HAVE_FADVISE
00155     posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
00156 #endif
00157 
00158 #ifdef HAVE_POSIX_ACL
00159     acl = acl_get_fd(src_fd);
00160     if ( acl && !isExtendedACL( acl ) ) {
00161         kDebug(7101) << _dest.data() << " doesn't have extended ACL";
00162         acl_free( acl );
00163         acl = NULL;
00164     }
00165 #endif
00166     totalSize( buff_src.st_size );
00167 
00168     KIO::filesize_t processed_size = 0;
00169     char buffer[ MAX_IPC_SIZE ];
00170     int n;
00171 #ifdef USE_SENDFILE
00172     bool use_sendfile=buff_src.st_size < 0x7FFFFFFF;
00173 #endif
00174     while( 1 )
00175     {
00176 #ifdef USE_SENDFILE
00177        if (use_sendfile) {
00178             off_t sf = processed_size;
00179             n = KDE_sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE );
00180             processed_size = sf;
00181             if ( n == -1 && ( errno == EINVAL || errno == ENOSYS ) ) { //not all filesystems support sendfile()
00182                 kDebug(7101) << "sendfile() not supported, falling back ";
00183                 use_sendfile = false;
00184             }
00185        }
00186        if (!use_sendfile)
00187 #endif
00188         n = ::read( src_fd, buffer, MAX_IPC_SIZE );
00189 
00190        if (n == -1)
00191        {
00192           if (errno == EINTR)
00193               continue;
00194 #ifdef USE_SENDFILE
00195           if ( use_sendfile ) {
00196             kDebug(7101) << "sendfile() error:" << strerror(errno);
00197             if ( errno == ENOSPC ) // disk full
00198             {
00199                 error( KIO::ERR_DISK_FULL, _dest );
00200                 remove( _dest.data() );
00201             }
00202             else {
00203                 error( KIO::ERR_SLAVE_DEFINED,
00204                         i18n("Cannot copy file from %1 to %2. (Errno: %3)",
00205                           src.toLocalFile(), dest.toLocalFile(), errno ) );
00206             }
00207           } else
00208 #endif
00209           error( KIO::ERR_COULD_NOT_READ, _src );
00210           ::close(src_fd);
00211           ::close(dest_fd);
00212 #ifdef HAVE_POSIX_ACL
00213           if (acl) acl_free(acl);
00214 #endif
00215           return;
00216        }
00217        if (n == 0)
00218           break; // Finished
00219 #ifdef USE_SENDFILE
00220        if ( !use_sendfile ) {
00221 #endif
00222          if (write_all( dest_fd, buffer, n))
00223          {
00224            ::close(src_fd);
00225            ::close(dest_fd);
00226 
00227            if ( errno == ENOSPC ) // disk full
00228            {
00229               error( KIO::ERR_DISK_FULL, _dest );
00230               remove( _dest.data() );
00231            }
00232            else
00233            {
00234               kWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno);
00235               error( KIO::ERR_COULD_NOT_WRITE, _dest );
00236            }
00237 #ifdef HAVE_POSIX_ACL
00238            if (acl) acl_free(acl);
00239 #endif
00240            return;
00241          }
00242          processed_size += n;
00243 #ifdef USE_SENDFILE
00244        }
00245 #endif
00246        processedSize( processed_size );
00247     }
00248 
00249     ::close( src_fd );
00250 
00251     if (::close( dest_fd))
00252     {
00253         kWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno);
00254         error( KIO::ERR_COULD_NOT_WRITE, _dest );
00255 #ifdef HAVE_POSIX_ACL
00256         if (acl) acl_free(acl);
00257 #endif
00258         return;
00259     }
00260 
00261     // set final permissions
00262     if ( _mode != -1 )
00263     {
00264         if ( (::chmod(_dest.data(), _mode) != 0)
00265 #ifdef HAVE_POSIX_ACL
00266           || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
00267 #endif
00268         )
00269        {
00270            KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(_dest);
00271            // Eat the error if the filesystem apparently doesn't support chmod.
00272            if ( mp && mp->testFileSystemFlag( KMountPoint::SupportsChmod ) )
00273                warning( i18n( "Could not change permissions for\n%1" ,  dest.toLocalFile() ) );
00274        }
00275     }
00276 #ifdef HAVE_POSIX_ACL
00277     if (acl) acl_free(acl);
00278 #endif
00279 
00280     // copy access and modification time
00281     struct utimbuf ut;
00282     ut.actime = buff_src.st_atime;
00283     ut.modtime = buff_src.st_mtime;
00284     if ( ::utime( _dest.data(), &ut ) != 0 )
00285     {
00286         kWarning() << QString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg( _dest.data() );
00287     }
00288 
00289     processedSize( buff_src.st_size );
00290     finished();
00291 }
00292 
00293 void FileProtocol::listDir( const KUrl& url)
00294 {
00295     kDebug(7101) << "========= LIST " << url.url() << " =========";
00296     if (!url.isLocalFile()) {
00297         KUrl redir(url);
00298     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
00299     redirection(redir);
00300     kDebug(7101) << "redirecting to " << redir.url();
00301     finished();
00302     return;
00303     }
00304     QByteArray _path( QFile::encodeName(url.toLocalFile()) );
00305     KDE_struct_stat buff;
00306     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
00307     error( KIO::ERR_DOES_NOT_EXIST, _path );
00308     return;
00309     }
00310 
00311     if ( !S_ISDIR( buff.st_mode ) ) {
00312     error( KIO::ERR_IS_FILE, _path );
00313     return;
00314     }
00315 
00316     DIR *dp = 0L;
00317     KDE_struct_dirent *ep;
00318 
00319     dp = opendir( _path.data() );
00320     if ( dp == 0 ) {
00321         switch (errno)
00322         {
00323 #ifdef ENOMEDIUM
00324     case ENOMEDIUM:
00325             error( ERR_SLAVE_DEFINED,
00326                    i18n( "No media in device for %1", url.toLocalFile() ) );
00327             break;
00328 #else
00329         case ENOENT: // just to avoid the warning
00330 #endif
00331         default:
00332             error( KIO::ERR_CANNOT_ENTER_DIRECTORY, _path );
00333             break;
00334         }
00335     return;
00336     }
00337 
00338     // Don't make this a QStringList. The locale file name we get here
00339     // should be passed intact to createUDSEntry to avoid problems with
00340     // files where QFile::encodeName(QFile::decodeName(a)) != a.
00341     QList<QByteArray> entryNames;
00342     while ( ( ep = KDE_readdir( dp ) ) != 0L )
00343     entryNames.append( ep->d_name );
00344 
00345     closedir( dp );
00346     totalSize( entryNames.count() );
00347 
00348     /* set the current dir to the path to speed up
00349        in not having to pass an absolute path.
00350        We restore the path later to get out of the
00351        path - the kernel wouldn't unmount or delete
00352        directories we keep as active directory. And
00353        as the slave runs in the background, it's hard
00354        to see for the user what the problem would be */
00355     char path_buffer[PATH_MAX];
00356     getcwd(path_buffer, PATH_MAX - 1);
00357     if ( chdir( _path.data() ) )  {
00358         if (errno == EACCES)
00359             error(ERR_ACCESS_DENIED, _path);
00360         else
00361             error(ERR_CANNOT_ENTER_DIRECTORY, _path);
00362         finished();
00363     }
00364 
00365     UDSEntry entry;
00366     QList<QByteArray>::ConstIterator it = entryNames.constBegin();
00367     QList<QByteArray>::ConstIterator end = entryNames.constEnd();
00368     for (; it != end; ++it) {
00369         entry.clear();
00370         if ( createUDSEntry( QFile::decodeName(*it),
00371                              *it /* we can use the filename as relative path*/,
00372                              entry, 2, true ) )
00373           listEntry( entry, false);
00374     }
00375 
00376     listEntry( entry, true ); // ready
00377 
00378     kDebug(7101) << "============= COMPLETED LIST ============";
00379 
00380     chdir(path_buffer);
00381     finished();
00382 }
00383 
00384 void FileProtocol::rename( const KUrl &src, const KUrl &dest,
00385                            KIO::JobFlags _flags )
00386 {
00387     char off_t_should_be_64_bits[sizeof(off_t) >= 8 ? 1 : -1]; (void) off_t_should_be_64_bits;
00388     QByteArray _src(QFile::encodeName(src.toLocalFile()));
00389     QByteArray _dest(QFile::encodeName(dest.toLocalFile()));
00390     KDE_struct_stat buff_src;
00391     if ( KDE_lstat( _src.data(), &buff_src ) == -1 ) {
00392         if ( errno == EACCES )
00393            error( KIO::ERR_ACCESS_DENIED, _src );
00394         else
00395            error( KIO::ERR_DOES_NOT_EXIST, _src );
00396         return;
00397     }
00398 
00399     KDE_struct_stat buff_dest;
00400     // stat symlinks here (lstat, not stat), to avoid ERR_IDENTICAL_FILES when replacing symlink
00401     // with its target (#169547)
00402     bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00403     if ( dest_exists )
00404     {
00405         if (S_ISDIR(buff_dest.st_mode))
00406         {
00407            error( KIO::ERR_DIR_ALREADY_EXIST, _dest );
00408            return;
00409         }
00410 
00411     if ( same_inode( buff_dest, buff_src) )
00412     {
00413         error( KIO::ERR_IDENTICAL_FILES, _dest );
00414         return;
00415     }
00416 
00417         if (!(_flags & KIO::Overwrite))
00418         {
00419            error( KIO::ERR_FILE_ALREADY_EXIST, _dest );
00420            return;
00421         }
00422     }
00423 
00424     if ( KDE_rename( _src.data(), _dest.data()))
00425     {
00426         if (( errno == EACCES ) || (errno == EPERM)) {
00427             error( KIO::ERR_ACCESS_DENIED, _dest );
00428         }
00429         else if (errno == EXDEV) {
00430            error( KIO::ERR_UNSUPPORTED_ACTION, QLatin1String("rename"));
00431         }
00432         else if (errno == EROFS) { // The file is on a read-only filesystem
00433            error( KIO::ERR_CANNOT_DELETE, _src );
00434         }
00435         else {
00436            error( KIO::ERR_CANNOT_RENAME, _src );
00437         }
00438         return;
00439     }
00440 
00441     finished();
00442 }
00443 
00444 void FileProtocol::symlink( const QString &target, const KUrl &dest, KIO::JobFlags flags )
00445 {
00446     // Assume dest is local too (wouldn't be here otherwise)
00447     if ( ::symlink( QFile::encodeName( target ), QFile::encodeName( dest.path() ) ) == -1 )
00448     {
00449         // Does the destination already exist ?
00450         if ( errno == EEXIST )
00451         {
00452             if ( (flags & KIO::Overwrite) )
00453             {
00454                 // Try to delete the destination
00455                 if ( unlink( QFile::encodeName( dest.path() ) ) != 0 )
00456                 {
00457                     error( KIO::ERR_CANNOT_DELETE, dest.path() );
00458                     return;
00459                 }
00460                 // Try again - this won't loop forever since unlink succeeded
00461                 symlink( target, dest, flags );
00462             }
00463             else
00464             {
00465                 KDE_struct_stat buff_dest;
00466                 KDE_lstat( QFile::encodeName( dest.path() ), &buff_dest );
00467                 if (S_ISDIR(buff_dest.st_mode))
00468                     error( KIO::ERR_DIR_ALREADY_EXIST, dest.path() );
00469                 else
00470                     error( KIO::ERR_FILE_ALREADY_EXIST, dest.path() );
00471                 return;
00472             }
00473         }
00474         else
00475         {
00476             // Some error occurred while we tried to symlink
00477             error( KIO::ERR_CANNOT_SYMLINK, dest.path() );
00478             return;
00479         }
00480     }
00481     finished();
00482 }
00483 
00484 void FileProtocol::del( const KUrl& url, bool isfile)
00485 {
00486     QByteArray _path( QFile::encodeName(url.toLocalFile()));
00487     /*****
00488      * Delete files
00489      *****/
00490 
00491     if (isfile) {
00492     kDebug( 7101 ) <<  "Deleting file "<< url.url();
00493 
00494     // TODO deletingFile( source );
00495 
00496     if ( unlink( _path.data() ) == -1 ) {
00497             if ((errno == EACCES) || (errno == EPERM))
00498                error( KIO::ERR_ACCESS_DENIED, _path );
00499             else if (errno == EISDIR)
00500                error( KIO::ERR_IS_DIRECTORY, _path );
00501             else
00502                error( KIO::ERR_CANNOT_DELETE, _path );
00503         return;
00504     }
00505     } else {
00506 
00507       /*****
00508        * Delete empty directory
00509        *****/
00510 
00511       kDebug( 7101 ) << "Deleting directory " << url.url();
00512 
00513       if ( ::rmdir( _path.data() ) == -1 ) {
00514     if ((errno == EACCES) || (errno == EPERM))
00515       error( KIO::ERR_ACCESS_DENIED, _path );
00516     else {
00517       kDebug( 7101 ) << "could not rmdir " << perror;
00518       error( KIO::ERR_COULD_NOT_RMDIR, _path );
00519       return;
00520     }
00521       }
00522     }
00523 
00524     finished();
00525 }
00526 
00527 void FileProtocol::chown( const KUrl& url, const QString& owner, const QString& group )
00528 {
00529     const QByteArray _path( QFile::encodeName(url.toLocalFile()) );
00530     uid_t uid;
00531     gid_t gid;
00532 
00533     // get uid from given owner
00534     {
00535         struct passwd *p = ::getpwnam(owner.toAscii());
00536 
00537         if ( ! p ) {
00538             error( KIO::ERR_SLAVE_DEFINED,
00539                    i18n( "Could not get user id for given user name %1", owner ) );
00540             return;
00541         }
00542 
00543         uid = p->pw_uid;
00544     }
00545 
00546     // get gid from given group
00547     {
00548         struct group *p = ::getgrnam(group.toAscii());
00549 
00550         if ( ! p ) {
00551             error( KIO::ERR_SLAVE_DEFINED,
00552                    i18n( "Could not get group id for given group name %1", group ) );
00553             return;
00554         }
00555 
00556         gid = p->gr_gid;
00557     }
00558 
00559     if ( ::chown(_path, uid, gid) == -1 ) {
00560         switch ( errno ) {
00561             case EPERM:
00562             case EACCES:
00563                 error( KIO::ERR_ACCESS_DENIED, _path );
00564                 break;
00565             case ENOSPC:
00566                 error( KIO::ERR_DISK_FULL, _path );
00567                 break;
00568             default:
00569                 error( KIO::ERR_CANNOT_CHOWN, _path );
00570         }
00571     } else
00572         finished();
00573 }

KIOSlave

Skip menu "KIOSlave"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • 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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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