Example #1
0
void FileProtocol::put( const KUrl& url, int _mode, KIO::JobFlags _flags )
{
    const QString dest_orig = url.toLocalFile();

    kDebug(7101) << dest_orig << "mode=" << _mode;

    QString dest_part(dest_orig + QLatin1String(".part"));

    KDE_struct_stat buff_orig;
    const bool bOrigExists = (KDE::lstat(dest_orig, &buff_orig) != -1);
    bool bPartExists = false;
    const bool bMarkPartial = config()->readEntry("MarkPartial", true);

    if (bMarkPartial)
    {
        KDE_struct_stat buff_part;
        bPartExists = (KDE::stat( dest_part, &buff_part ) != -1);

        if (bPartExists && !(_flags & KIO::Resume) && !(_flags & KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
        {
            kDebug(7101) << "calling canResume with" << KIO::number(buff_part.st_size);

            // Maybe we can use this partial file for resuming
            // Tell about the size we have, and the app will tell us
            // if it's ok to resume or not.
            _flags |= canResume( buff_part.st_size ) ? KIO::Resume : KIO::DefaultFlags;

            kDebug(7101) << "got answer" << (_flags & KIO::Resume);
        }
    }

    if ( bOrigExists && !(_flags & KIO::Overwrite) && !(_flags & KIO::Resume))
    {
        if (S_ISDIR(buff_orig.st_mode))
            error( KIO::ERR_DIR_ALREADY_EXIST, dest_orig );
        else
            error( KIO::ERR_FILE_ALREADY_EXIST, dest_orig );
        return;
    }

    int result;
    QString dest;
    QByteArray _dest;

    int fd = -1;

    // Loop until we got 0 (end of data)
    do
    {
        QByteArray buffer;
        dataReq(); // Request for data
        result = readData( buffer );

        if (result >= 0)
        {
            if (dest.isEmpty())
            {
                if (bMarkPartial)
                {
                    kDebug(7101) << "Appending .part extension to" << dest_orig;
                    dest = dest_part;
                    if ( bPartExists && !(_flags & KIO::Resume) )
                    {
                        kDebug(7101) << "Deleting partial file" << dest_part;
                        QFile::remove( dest_part );
                        // Catch errors when we try to open the file.
                    }
                }
                else
                {
                    dest = dest_orig;
                    if ( bOrigExists && !(_flags & KIO::Resume) )
                    {
                        kDebug(7101) << "Deleting destination file" << dest_orig;
                        QFile::remove( dest_orig );
                        // Catch errors when we try to open the file.
                    }
                }

                if ( (_flags & KIO::Resume) )
                {
                    fd = KDE::open( dest, O_RDWR );  // append if resuming
                    KDE_lseek(fd, 0, SEEK_END); // Seek to end
                }
                else
                {
                    // WABA: Make sure that we keep writing permissions ourselves,
                    // otherwise we can be in for a surprise on NFS.
                    mode_t initialMode;
                    if (_mode != -1)
                        initialMode = _mode | S_IWUSR | S_IRUSR;
                    else
                        initialMode = 0666;

                    fd = KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
                }

                if ( fd < 0 )
                {
                    kDebug(7101) << "####################### COULD NOT WRITE" << dest << "_mode=" << _mode;
                    kDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")";
                    if ( errno == EACCES )
                        error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
                    else
                        error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
                    return;
                }
            }

            if (write_all( fd, buffer.data(), buffer.size()))
            {
                if ( errno == ENOSPC ) // disk full
                {
                  error(KIO::ERR_DISK_FULL, dest_orig);
                  result = -2; // means: remove dest file
                }
                else
                {
                  kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
                  error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
                  result = -1;
                }
            }
        }
    }
    while ( result > 0 );

    // An error occurred deal with it.
    if (result < 0)
    {
        kDebug(7101) << "Error during 'put'. Aborting.";

        if (fd != -1)
        {
          ::close(fd);

          KDE_struct_stat buff;
          if (bMarkPartial && KDE::stat( dest, &buff ) == 0)
          {
            int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
            if (buff.st_size <  size)
              remove(_dest.data());
          }
        }

        ::exit(255);
    }

    if ( fd == -1 ) // we got nothing to write out, so we never opened the file
    {
        finished();
        return;
    }

    if ( ::close(fd) )
    {
        kWarning(7101) << "Error when closing file descriptor:" << strerror(errno);
        error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
        return;
    }

    // after full download rename the file back to original name
    if ( bMarkPartial )
    {
        // If the original URL is a symlink and we were asked to overwrite it,
        // remove the symlink first. This ensures that we do not overwrite the
        // current source if the symlink points to it.
        if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) )
          QFile::remove( dest_orig );
        if ( KDE::rename( dest, dest_orig ) )
        {
            kWarning(7101) << " Couldn't rename " << _dest << " to " << dest_orig;
            error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig);
            return;
        }
        org::kde::KDirNotify::emitFileRenamed(dest, dest_orig);
    }

    // set final permissions
    if ( _mode != -1 && !(_flags & KIO::Resume) )
    {
        if (KDE::chmod(dest_orig, _mode) != 0)
        {
            // couldn't chmod. Eat the error if the filesystem apparently doesn't support it.
            KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest_orig);
            if (mp && mp->testFileSystemFlag(KMountPoint::SupportsChmod))
                 warning( i18n( "Could not change permissions for\n%1" ,  dest_orig ) );
        }
    }

    // set modification time
    const QString mtimeStr = metaData(QLatin1String("modified"));
    if ( !mtimeStr.isEmpty() ) {
        QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate );
        if ( dt.isValid() ) {
            KDE_struct_stat dest_statbuf;
            if (KDE::stat( dest_orig, &dest_statbuf ) == 0) {
                struct timeval utbuf[2];
                // access time
                utbuf[0].tv_sec = dest_statbuf.st_atime; // access time, unchanged  ## TODO preserve msec
                utbuf[0].tv_usec = 0;
                // modification time
                utbuf[1].tv_sec = dt.toTime_t();
                utbuf[1].tv_usec = dt.time().msec() * 1000;
                utimes( QFile::encodeName(dest_orig), utbuf );
            }
        }

    }

    // We have done our job => finish
    finished();
}
Example #2
0
void FileProtocol::copy( const KUrl &srcUrl, const KUrl &destUrl,
                         int _mode, JobFlags _flags )
{
    kDebug(7101) << "copy(): " << srcUrl << " -> " << destUrl << ", mode=" << _mode;

    const QString src = srcUrl.toLocalFile();
    const QString dest = destUrl.toLocalFile();
    QByteArray _src( QFile::encodeName(src));
    QByteArray _dest( QFile::encodeName(dest));
    KDE_struct_stat buff_src;
#ifdef HAVE_POSIX_ACL
    acl_t acl;
#endif

    if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
        if ( errno == EACCES )
           error(KIO::ERR_ACCESS_DENIED, src);
        else
           error(KIO::ERR_DOES_NOT_EXIST, src);
	return;
    }

    if ( S_ISDIR( buff_src.st_mode ) ) {
	error(KIO::ERR_IS_DIRECTORY, src);
	return;
    }
    if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) {
	error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
	return;
    }

    KDE_struct_stat buff_dest;
    bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
    if ( dest_exists )
    {
        if (S_ISDIR(buff_dest.st_mode))
        {
           error(KIO::ERR_DIR_ALREADY_EXIST, dest);
           return;
        }

	if ( same_inode( buff_dest, buff_src) )
	{
	    error(KIO::ERR_IDENTICAL_FILES, dest);
	    return;
	}

        if (!(_flags & KIO::Overwrite))
        {
           error(KIO::ERR_FILE_ALREADY_EXIST, dest);
           return;
        }

        // If the destination is a symlink and overwrite is TRUE,
        // remove the symlink first to prevent the scenario where
        // the symlink actually points to current source!
        if ((_flags & KIO::Overwrite) && S_ISLNK(buff_dest.st_mode))
        {
            //kDebug(7101) << "copy(): LINK DESTINATION";
            remove( _dest.data() );
        }
    }

    int src_fd = KDE_open( _src.data(), O_RDONLY);
    if ( src_fd < 0 ) {
	error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
	return;
    }

#if HAVE_FADVISE
    posix_fadvise(src_fd,0,0,POSIX_FADV_SEQUENTIAL);
#endif
    // WABA: Make sure that we keep writing permissions ourselves,
    // otherwise we can be in for a surprise on NFS.
    mode_t initialMode;
    if (_mode != -1)
       initialMode = _mode | S_IWUSR;
    else
       initialMode = 0666;

    int dest_fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
    if ( dest_fd < 0 ) {
	kDebug(7101) << "###### COULD NOT WRITE " << dest;
        if ( errno == EACCES ) {
            error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
        } else {
            error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
        }
        ::close(src_fd);
        return;
    }

#if HAVE_FADVISE
    posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
#endif

#ifdef HAVE_POSIX_ACL
    acl = acl_get_fd(src_fd);
    if ( acl && !isExtendedACL( acl ) ) {
        kDebug(7101) << _dest.data() << " doesn't have extended ACL";
        acl_free( acl );
        acl = NULL;
    }
#endif
    totalSize( buff_src.st_size );

    KIO::filesize_t processed_size = 0;
    char buffer[ MAX_IPC_SIZE ];
    int n;
#ifdef USE_SENDFILE
    bool use_sendfile=buff_src.st_size < 0x7FFFFFFF;
#endif
    while( 1 )
    {
#ifdef USE_SENDFILE
       if (use_sendfile) {
            off_t sf = processed_size;
            n = KDE_sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE );
            processed_size = sf;
            if ( n == -1 && ( errno == EINVAL || errno == ENOSYS ) ) { //not all filesystems support sendfile()
                kDebug(7101) << "sendfile() not supported, falling back ";
                use_sendfile = false;
            }
       }
       if (!use_sendfile)
#endif
        n = ::read( src_fd, buffer, MAX_IPC_SIZE );

       if (n == -1)
       {
          if (errno == EINTR)
              continue;
#ifdef USE_SENDFILE
          if ( use_sendfile ) {
            kDebug(7101) << "sendfile() error:" << strerror(errno);
            if ( errno == ENOSPC ) // disk full
            {
                error(KIO::ERR_DISK_FULL, dest);
                remove( _dest.data() );
            }
            else {
                error(KIO::ERR_SLAVE_DEFINED,
                      i18n("Cannot copy file from %1 to %2. (Errno: %3)",
                      src, dest, errno));
            }
          } else
#endif
          error(KIO::ERR_COULD_NOT_READ, src);
          ::close(src_fd);
          ::close(dest_fd);
#ifdef HAVE_POSIX_ACL
          if (acl) acl_free(acl);
#endif
          return;
       }
       if (n == 0)
          break; // Finished
#ifdef USE_SENDFILE
       if ( !use_sendfile ) {
#endif
         if (write_all( dest_fd, buffer, n))
         {
           ::close(src_fd);
           ::close(dest_fd);

           if ( errno == ENOSPC ) // disk full
           {
              error(KIO::ERR_DISK_FULL, dest);
              remove( _dest.data() );
           }
           else
           {
              kWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno);
              error(KIO::ERR_COULD_NOT_WRITE, dest);
           }
#ifdef HAVE_POSIX_ACL
           if (acl) acl_free(acl);
#endif
           return;
         }
         processed_size += n;
#ifdef USE_SENDFILE
       }
#endif
       processedSize( processed_size );
    }

    ::close( src_fd );

    if (::close( dest_fd))
    {
        kWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno);
        error(KIO::ERR_COULD_NOT_WRITE, dest);
#ifdef HAVE_POSIX_ACL
        if (acl) acl_free(acl);
#endif
        return;
    }

    // set final permissions
    if ( _mode != -1 )
    {
        if ( (::chmod(_dest.data(), _mode) != 0)
#ifdef HAVE_POSIX_ACL
          || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
#endif
        )
       {
           KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest);
           // Eat the error if the filesystem apparently doesn't support chmod.
           if ( mp && mp->testFileSystemFlag( KMountPoint::SupportsChmod ) )
               warning(i18n("Could not change permissions for\n%1", dest));
       }
    }
#ifdef HAVE_POSIX_ACL
    if (acl) acl_free(acl);
#endif

    // copy access and modification time
    struct utimbuf ut;
    ut.actime = buff_src.st_atime;
    ut.modtime = buff_src.st_mtime;
    if ( ::utime( _dest.data(), &ut ) != 0 )
    {
        kWarning() << QString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg(dest);
    }

    processedSize( buff_src.st_size );
    finished();
}