00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #define QT_NO_CAST_FROM_ASCII
00027
00028 #include "file.h"
00029
00030 #include <config.h>
00031
00032 #include <QtCore/QFile>
00033
00034 #include <kde_file.h>
00035 #include <kdebug.h>
00036 #include <kconfiggroup.h>
00037 #include <kmountpoint.h>
00038
00039 #include <dirent.h>
00040 #include <errno.h>
00041 #include <fcntl.h>
00042 #include <grp.h>
00043 #include <utime.h>
00044 #include <pwd.h>
00045
00046 #if defined(HAVE_LIMITS_H)
00047 #include <limits.h>
00048 #endif
00049
00050 using namespace KIO;
00051
00052 #define MAX_IPC_SIZE (1024*32)
00053
00054 static bool
00055 same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
00056 {
00057 if (src.st_ino == dest.st_ino &&
00058 src.st_dev == dest.st_dev)
00059 return true;
00060
00061 return false;
00062 }
00063
00064 extern int write_all(int fd, const char *buf, size_t len);
00065
00066 void FileProtocol::copy( const KUrl &srcUrl, const KUrl &destUrl,
00067 int _mode, JobFlags _flags )
00068 {
00069 kDebug(7101) << "copy(): " << srcUrl << " -> " << destUrl << ", mode=" << _mode;
00070
00071 const QString src = srcUrl.toLocalFile();
00072 const QString dest = destUrl.toLocalFile();
00073 QByteArray _src( QFile::encodeName(src));
00074 QByteArray _dest( QFile::encodeName(dest));
00075 KDE_struct_stat buff_src;
00076 #ifdef HAVE_POSIX_ACL
00077 acl_t acl;
00078 #endif
00079
00080 if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
00081 if ( errno == EACCES )
00082 error(KIO::ERR_ACCESS_DENIED, src);
00083 else
00084 error(KIO::ERR_DOES_NOT_EXIST, src);
00085 return;
00086 }
00087
00088 if ( S_ISDIR( buff_src.st_mode ) ) {
00089 error(KIO::ERR_IS_DIRECTORY, src);
00090 return;
00091 }
00092 if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) {
00093 error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
00094 return;
00095 }
00096
00097 KDE_struct_stat buff_dest;
00098 bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00099 if ( dest_exists )
00100 {
00101 if (S_ISDIR(buff_dest.st_mode))
00102 {
00103 error(KIO::ERR_DIR_ALREADY_EXIST, dest);
00104 return;
00105 }
00106
00107 if ( same_inode( buff_dest, buff_src) )
00108 {
00109 error(KIO::ERR_IDENTICAL_FILES, dest);
00110 return;
00111 }
00112
00113 if (!(_flags & KIO::Overwrite))
00114 {
00115 error(KIO::ERR_FILE_ALREADY_EXIST, dest);
00116 return;
00117 }
00118
00119
00120
00121
00122 if ((_flags & KIO::Overwrite) && S_ISLNK(buff_dest.st_mode))
00123 {
00124
00125 remove( _dest.data() );
00126 }
00127 }
00128
00129 int src_fd = KDE_open( _src.data(), O_RDONLY);
00130 if ( src_fd < 0 ) {
00131 error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
00132 return;
00133 }
00134
00135 #ifdef HAVE_FADVISE
00136 posix_fadvise(src_fd,0,0,POSIX_FADV_SEQUENTIAL);
00137 #endif
00138
00139
00140 mode_t initialMode;
00141 if (_mode != -1)
00142 initialMode = _mode | S_IWUSR;
00143 else
00144 initialMode = 0666;
00145
00146 int dest_fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
00147 if ( dest_fd < 0 ) {
00148 kDebug(7101) << "###### COULD NOT WRITE " << dest;
00149 if ( errno == EACCES ) {
00150 error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
00151 } else {
00152 error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
00153 }
00154 ::close(src_fd);
00155 return;
00156 }
00157
00158 #ifdef HAVE_FADVISE
00159 posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
00160 #endif
00161
00162 #ifdef HAVE_POSIX_ACL
00163 acl = acl_get_fd(src_fd);
00164 if ( acl && !isExtendedACL( acl ) ) {
00165 kDebug(7101) << _dest.data() << " doesn't have extended ACL";
00166 acl_free( acl );
00167 acl = NULL;
00168 }
00169 #endif
00170 totalSize( buff_src.st_size );
00171
00172 KIO::filesize_t processed_size = 0;
00173 char buffer[ MAX_IPC_SIZE ];
00174 int n;
00175 #ifdef USE_SENDFILE
00176 bool use_sendfile=buff_src.st_size < 0x7FFFFFFF;
00177 #endif
00178 while( 1 )
00179 {
00180 #ifdef USE_SENDFILE
00181 if (use_sendfile) {
00182 off_t sf = processed_size;
00183 n = KDE_sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE );
00184 processed_size = sf;
00185 if ( n == -1 && ( errno == EINVAL || errno == ENOSYS ) ) {
00186 kDebug(7101) << "sendfile() not supported, falling back ";
00187 use_sendfile = false;
00188 }
00189 }
00190 if (!use_sendfile)
00191 #endif
00192 n = ::read( src_fd, buffer, MAX_IPC_SIZE );
00193
00194 if (n == -1)
00195 {
00196 if (errno == EINTR)
00197 continue;
00198 #ifdef USE_SENDFILE
00199 if ( use_sendfile ) {
00200 kDebug(7101) << "sendfile() error:" << strerror(errno);
00201 if ( errno == ENOSPC )
00202 {
00203 error(KIO::ERR_DISK_FULL, dest);
00204 remove( _dest.data() );
00205 }
00206 else {
00207 error(KIO::ERR_SLAVE_DEFINED,
00208 i18n("Cannot copy file from %1 to %2. (Errno: %3)",
00209 src, dest, errno));
00210 }
00211 } else
00212 #endif
00213 error(KIO::ERR_COULD_NOT_READ, src);
00214 ::close(src_fd);
00215 ::close(dest_fd);
00216 #ifdef HAVE_POSIX_ACL
00217 if (acl) acl_free(acl);
00218 #endif
00219 return;
00220 }
00221 if (n == 0)
00222 break;
00223 #ifdef USE_SENDFILE
00224 if ( !use_sendfile ) {
00225 #endif
00226 if (write_all( dest_fd, buffer, n))
00227 {
00228 ::close(src_fd);
00229 ::close(dest_fd);
00230
00231 if ( errno == ENOSPC )
00232 {
00233 error(KIO::ERR_DISK_FULL, dest);
00234 remove( _dest.data() );
00235 }
00236 else
00237 {
00238 kWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno);
00239 error(KIO::ERR_COULD_NOT_WRITE, dest);
00240 }
00241 #ifdef HAVE_POSIX_ACL
00242 if (acl) acl_free(acl);
00243 #endif
00244 return;
00245 }
00246 processed_size += n;
00247 #ifdef USE_SENDFILE
00248 }
00249 #endif
00250 processedSize( processed_size );
00251 }
00252
00253 ::close( src_fd );
00254
00255 if (::close( dest_fd))
00256 {
00257 kWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno);
00258 error(KIO::ERR_COULD_NOT_WRITE, dest);
00259 #ifdef HAVE_POSIX_ACL
00260 if (acl) acl_free(acl);
00261 #endif
00262 return;
00263 }
00264
00265
00266 if ( _mode != -1 )
00267 {
00268 if ( (::chmod(_dest.data(), _mode) != 0)
00269 #ifdef HAVE_POSIX_ACL
00270 || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
00271 #endif
00272 )
00273 {
00274 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest);
00275
00276 if ( mp && mp->testFileSystemFlag( KMountPoint::SupportsChmod ) )
00277 warning(i18n("Could not change permissions for\n%1", dest));
00278 }
00279 }
00280 #ifdef HAVE_POSIX_ACL
00281 if (acl) acl_free(acl);
00282 #endif
00283
00284
00285 struct utimbuf ut;
00286 ut.actime = buff_src.st_atime;
00287 ut.modtime = buff_src.st_mtime;
00288 if ( ::utime( _dest.data(), &ut ) != 0 )
00289 {
00290 kWarning() << QString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg(dest);
00291 }
00292
00293 processedSize( buff_src.st_size );
00294 finished();
00295 }
00296
00297 void FileProtocol::listDir( const KUrl& url)
00298 {
00299 if (!url.isLocalFile()) {
00300 KUrl redir(url);
00301 redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
00302 redirection(redir);
00303 kDebug(7101) << "redirecting to " << redir.url();
00304 finished();
00305 return;
00306 }
00307 const QString path(url.toLocalFile());
00308 const QByteArray _path(QFile::encodeName(path));
00309 DIR* dp = opendir(_path.data());
00310 if ( dp == 0 ) {
00311 switch (errno) {
00312 case ENOENT:
00313 error(KIO::ERR_DOES_NOT_EXIST, path);
00314 return;
00315 case ENOTDIR:
00316 error(KIO::ERR_IS_FILE, path);
00317 break;
00318 #ifdef ENOMEDIUM
00319 case ENOMEDIUM:
00320 error(ERR_SLAVE_DEFINED,
00321 i18n("No media in device for %1", path));
00322 break;
00323 #endif
00324 default:
00325 error(KIO::ERR_CANNOT_ENTER_DIRECTORY, path);
00326 break;
00327 }
00328 return;
00329 }
00330
00331 const QString sDetails = metaData(QLatin1String("details"));
00332 const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
00333
00334 UDSEntry entry;
00335
00336
00337
00338
00339 QList<QByteArray> entryNames;
00340 KDE_struct_dirent *ep;
00341 if (details == 0) {
00342
00343
00344 while ( ( ep = KDE_readdir( dp ) ) != 0 ) {
00345 entry.clear();
00346 entry.insert(KIO::UDSEntry::UDS_NAME, QFile::decodeName(ep->d_name));
00347 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE,
00348 (ep->d_type & DT_DIR) ? S_IFDIR : S_IFREG );
00349 if (ep->d_type & DT_LNK) {
00350
00351
00352 entry.insert(KIO::UDSEntry::UDS_LINK_DEST, QLatin1String("Dummy Link Target"));
00353 }
00354 listEntry(entry, false);
00355 }
00356 closedir( dp );
00357 listEntry( entry, true );
00358 } else {
00359 while ( ( ep = KDE_readdir( dp ) ) != 0 ) {
00360 entryNames.append( ep->d_name );
00361 }
00362
00363 closedir( dp );
00364 totalSize( entryNames.count() );
00365
00366
00367
00368
00369
00370
00371
00372
00373 char path_buffer[PATH_MAX];
00374 path_buffer[0] = '\0';
00375 (void) getcwd(path_buffer, PATH_MAX - 1);
00376 if ( chdir( _path.data() ) ) {
00377 if (errno == EACCES)
00378 error(ERR_ACCESS_DENIED, path);
00379 else
00380 error(ERR_CANNOT_ENTER_DIRECTORY, path);
00381 finished();
00382 }
00383
00384 QList<QByteArray>::ConstIterator it = entryNames.constBegin();
00385 QList<QByteArray>::ConstIterator end = entryNames.constEnd();
00386 for (; it != end; ++it) {
00387 entry.clear();
00388 if ( createUDSEntry( QFile::decodeName(*it),
00389 *it ,
00390 entry, details, true ) )
00391 listEntry( entry, false);
00392 }
00393
00394 listEntry( entry, true );
00395
00396
00397
00398 if (*path_buffer)
00399 chdir(path_buffer);
00400 }
00401 finished();
00402 }
00403
00404 void FileProtocol::rename( const KUrl &srcUrl, const KUrl &destUrl,
00405 KIO::JobFlags _flags )
00406 {
00407 char off_t_should_be_64_bits[sizeof(off_t) >= 8 ? 1 : -1]; (void) off_t_should_be_64_bits;
00408 const QString src = srcUrl.toLocalFile();
00409 const QString dest = destUrl.toLocalFile();
00410 const QByteArray _src(QFile::encodeName(src));
00411 const QByteArray _dest(QFile::encodeName(dest));
00412 KDE_struct_stat buff_src;
00413 if ( KDE_lstat( _src.data(), &buff_src ) == -1 ) {
00414 if ( errno == EACCES )
00415 error(KIO::ERR_ACCESS_DENIED, src);
00416 else
00417 error(KIO::ERR_DOES_NOT_EXIST, src);
00418 return;
00419 }
00420
00421 KDE_struct_stat buff_dest;
00422
00423
00424 bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00425 if ( dest_exists )
00426 {
00427 if (S_ISDIR(buff_dest.st_mode))
00428 {
00429 error(KIO::ERR_DIR_ALREADY_EXIST, dest);
00430 return;
00431 }
00432
00433 if ( same_inode( buff_dest, buff_src) )
00434 {
00435 error(KIO::ERR_IDENTICAL_FILES, dest);
00436 return;
00437 }
00438
00439 if (!(_flags & KIO::Overwrite))
00440 {
00441 error(KIO::ERR_FILE_ALREADY_EXIST, dest);
00442 return;
00443 }
00444 }
00445
00446 if ( KDE_rename( _src.data(), _dest.data()))
00447 {
00448 if (( errno == EACCES ) || (errno == EPERM)) {
00449 error(KIO::ERR_ACCESS_DENIED, dest);
00450 }
00451 else if (errno == EXDEV) {
00452 error(KIO::ERR_UNSUPPORTED_ACTION, QLatin1String("rename"));
00453 }
00454 else if (errno == EROFS) {
00455 error(KIO::ERR_CANNOT_DELETE, src);
00456 }
00457 else {
00458 error(KIO::ERR_CANNOT_RENAME, src);
00459 }
00460 return;
00461 }
00462
00463 finished();
00464 }
00465
00466 void FileProtocol::symlink( const QString &target, const KUrl &destUrl, KIO::JobFlags flags )
00467 {
00468 const QString dest = destUrl.toLocalFile();
00469
00470 if ( ::symlink( QFile::encodeName(target), QFile::encodeName(dest) ) == -1 )
00471 {
00472
00473 if ( errno == EEXIST )
00474 {
00475 if ( (flags & KIO::Overwrite) )
00476 {
00477
00478 if ( unlink( QFile::encodeName(dest) ) != 0 )
00479 {
00480 error(KIO::ERR_CANNOT_DELETE, dest);
00481 return;
00482 }
00483
00484 symlink( target, destUrl, flags );
00485 }
00486 else
00487 {
00488 KDE_struct_stat buff_dest;
00489 KDE_lstat( QFile::encodeName(dest), &buff_dest );
00490 if (S_ISDIR(buff_dest.st_mode))
00491 error(KIO::ERR_DIR_ALREADY_EXIST, dest);
00492 else
00493 error(KIO::ERR_FILE_ALREADY_EXIST, dest);
00494 return;
00495 }
00496 }
00497 else
00498 {
00499
00500 error(KIO::ERR_CANNOT_SYMLINK, dest);
00501 return;
00502 }
00503 }
00504 finished();
00505 }
00506
00507 void FileProtocol::del(const KUrl& url, bool isfile)
00508 {
00509 const QString path = url.toLocalFile();
00510 const QByteArray _path( QFile::encodeName(path));
00511
00512
00513
00514
00515 if (isfile) {
00516 kDebug(7101) << "Deleting file "<< url;
00517
00518 if ( unlink( _path.data() ) == -1 ) {
00519 if ((errno == EACCES) || (errno == EPERM))
00520 error(KIO::ERR_ACCESS_DENIED, path);
00521 else if (errno == EISDIR)
00522 error(KIO::ERR_IS_DIRECTORY, path);
00523 else
00524 error(KIO::ERR_CANNOT_DELETE, path);
00525 return;
00526 }
00527 } else {
00528
00529
00530
00531
00532
00533 kDebug( 7101 ) << "Deleting directory " << url.url();
00534 if (metaData(QLatin1String("recurse")) == QLatin1String("true")) {
00535 if (!deleteRecursive(path))
00536 return;
00537 }
00538 if ( ::rmdir( _path.data() ) == -1 ) {
00539 if ((errno == EACCES) || (errno == EPERM))
00540 error(KIO::ERR_ACCESS_DENIED, path);
00541 else {
00542 kDebug( 7101 ) << "could not rmdir " << perror;
00543 error(KIO::ERR_COULD_NOT_RMDIR, path);
00544 return;
00545 }
00546 }
00547 }
00548
00549 finished();
00550 }
00551
00552 void FileProtocol::chown( const KUrl& url, const QString& owner, const QString& group )
00553 {
00554 const QString path = url.toLocalFile();
00555 const QByteArray _path( QFile::encodeName(path) );
00556 uid_t uid;
00557 gid_t gid;
00558
00559
00560 {
00561 struct passwd *p = ::getpwnam(owner.toAscii());
00562
00563 if ( ! p ) {
00564 error( KIO::ERR_SLAVE_DEFINED,
00565 i18n( "Could not get user id for given user name %1", owner ) );
00566 return;
00567 }
00568
00569 uid = p->pw_uid;
00570 }
00571
00572
00573 {
00574 struct group *p = ::getgrnam(group.toAscii());
00575
00576 if ( ! p ) {
00577 error( KIO::ERR_SLAVE_DEFINED,
00578 i18n( "Could not get group id for given group name %1", group ) );
00579 return;
00580 }
00581
00582 gid = p->gr_gid;
00583 }
00584
00585 if ( ::chown(_path, uid, gid) == -1 ) {
00586 switch ( errno ) {
00587 case EPERM:
00588 case EACCES:
00589 error(KIO::ERR_ACCESS_DENIED, path);
00590 break;
00591 case ENOSPC:
00592 error(KIO::ERR_DISK_FULL, path);
00593 break;
00594 default:
00595 error(KIO::ERR_CANNOT_CHOWN, path);
00596 }
00597 } else
00598 finished();
00599 }
00600
00601 void FileProtocol::stat( const KUrl & url )
00602 {
00603 if (!url.isLocalFile()) {
00604 KUrl redir(url);
00605 redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
00606 redirection(redir);
00607 kDebug(7101) << "redirecting to " << redir.url();
00608 finished();
00609 return;
00610 }
00611
00612
00613
00614
00615
00616
00617
00618
00619 const QString path(url.path(KUrl::RemoveTrailingSlash));
00620 const QByteArray _path( QFile::encodeName(path));
00621 const QString sDetails = metaData(QLatin1String("details"));
00622 const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
00623
00624 UDSEntry entry;
00625 if ( !createUDSEntry( url.fileName(), _path, entry, details, true ) )
00626 {
00627 error(KIO::ERR_DOES_NOT_EXIST, path);
00628 return;
00629 }
00630 #if 0
00632 MetaData::iterator it1 = mOutgoingMetaData.begin();
00633 for ( ; it1 != mOutgoingMetaData.end(); it1++ ) {
00634 kDebug(7101) << it1.key() << " = " << it1.data();
00635 }
00637 #endif
00638 statEntry( entry );
00639
00640 finished();
00641 }