diff options
Diffstat (limited to 'tdeioslave/file')
-rw-r--r-- | tdeioslave/file/CMakeLists.txt | 55 | ||||
-rw-r--r-- | tdeioslave/file/Makefile.am | 22 | ||||
-rw-r--r-- | tdeioslave/file/file.cc | 1831 | ||||
-rw-r--r-- | tdeioslave/file/file.h | 98 | ||||
-rw-r--r-- | tdeioslave/file/file.protocol | 15 |
5 files changed, 2021 insertions, 0 deletions
diff --git a/tdeioslave/file/CMakeLists.txt b/tdeioslave/file/CMakeLists.txt new file mode 100644 index 000000000..0392de535 --- /dev/null +++ b/tdeioslave/file/CMakeLists.txt @@ -0,0 +1,55 @@ +################################################# +# +# (C) 2010 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_definitions( + -D_LARGEFILE64_SOURCE +) + +include_directories( + ${TQT_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}/tdecore + ${CMAKE_SOURCE_DIR}/dcop + ${CMAKE_SOURCE_DIR}/tdecore + ${CMAKE_SOURCE_DIR}/tdeio + ${CMAKE_SOURCE_DIR}/tdeio/tdeio +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### headers ################################### + +install( FILES file.h DESTINATION ${INCLUDE_INSTALL_DIR}/tdeio ) + + +##### other data ################################ + +install( FILES file.protocol DESTINATION ${SERVICES_INSTALL_DIR} ) + + +##### kio_file ################################## + +set( target kio_file ) + +set( ${target}_SRCS + file.cc +) + +tde_add_kpart( ${target} AUTOMOC + SOURCES ${${target}_SRCS} + LINK tdeio-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + diff --git a/tdeioslave/file/Makefile.am b/tdeioslave/file/Makefile.am new file mode 100644 index 000000000..518b79f46 --- /dev/null +++ b/tdeioslave/file/Makefile.am @@ -0,0 +1,22 @@ +## Makefile.am of tdebase/tdeioslave/file + +AM_CPPFLAGS = -D_LARGEFILE64_SOURCE + +INCLUDES = $(all_includes) + +####### Files + +kde_module_LTLIBRARIES = kio_file.la + +kio_file_la_SOURCES = file.cc +kio_file_la_LIBADD = $(LIB_KIO) $(LIB_QT) $(LIB_TDECORE) $(ACL_LIBS) +kio_file_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) $(top_builddir)/dcop/libDCOP.la +noinst_HEADERS = file.h + +fileinclude_HEADERS = file.h +fileincludedir = $(includedir)/tdeio + +METASOURCES = AUTO + +kdelnkdir = $(kde_servicesdir) +kdelnk_DATA = file.protocol diff --git a/tdeioslave/file/file.cc b/tdeioslave/file/file.cc new file mode 100644 index 000000000..499a69330 --- /dev/null +++ b/tdeioslave/file/file.cc @@ -0,0 +1,1831 @@ +/* + Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org> + Copyright (C) 2000-2002 David Faure <faure@kde.org> + Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License (LGPL) as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later + version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// $Id$ + +#include <config.h> + +#include <tqglobal.h> //for Q_OS_XXX +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +//sendfile has different semantics in different platforms +#if defined HAVE_SENDFILE && defined Q_OS_LINUX +#define USE_SENDFILE 1 +#endif + +#ifdef USE_SENDFILE +#include <sys/sendfile.h> +#endif + +#ifdef USE_POSIX_ACL +#include <sys/acl.h> +#ifdef HAVE_NON_POSIX_ACL_EXTENSIONS +#include <acl/libacl.h> +#else +#include <posixacladdons.h> +#endif +#endif + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <utime.h> +#include <unistd.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <tqvaluelist.h> +#include <tqregexp.h> + +#include <dcopref.h> +#include <kshred.h> +#include <kdebug.h> +#include <kurl.h> +#include <kinstance.h> +#include <ksimpleconfig.h> +#include <ktempfile.h> +#include <klocale.h> +#include <tqfile.h> +#include <tqstrlist.h> +#include "file.h" +#include <limits.h> +#include <kprocess.h> +#include <kmountpoint.h> +#include <kstandarddirs.h> + +#ifdef HAVE_VOLMGT +#include <volmgt.h> +#include <sys/mnttab.h> +#endif + +#include <kstandarddirs.h> +#include <tdeio/ioslave_defaults.h> +#include <klargefile.h> +#include <kglobal.h> +#include <kmimetype.h> + +using namespace TDEIO; + +#define MAX_IPC_SIZE (1024*32) + +static TQString testLogFile( const char *_filename ); +#ifdef USE_POSIX_ACL +static TQString aclAsString( acl_t p_acl ); +static bool isExtendedACL( acl_t p_acl ); +static void appendACLAtoms( const TQCString & path, UDSEntry& entry, + mode_t type, bool withACL ); +#endif + +extern "C" { KDE_EXPORT int kdemain(int argc, char **argv); } + +int kdemain( int argc, char **argv ) +{ + KLocale::setMainCatalogue("tdelibs"); + TDEInstance instance( "kio_file" ); + ( void ) TDEGlobal::locale(); + + kdDebug(7101) << "Starting " << getpid() << endl; + + if (argc != 4) + { + fprintf(stderr, "Usage: kio_file protocol domain-socket1 domain-socket2\n"); + exit(-1); + } + + FileProtocol slave(argv[2], argv[3]); + slave.dispatchLoop(); + + kdDebug(7101) << "Done" << endl; + return 0; +} + + +FileProtocol::FileProtocol( const TQCString &pool, const TQCString &app ) : SlaveBase( "file", pool, app ) +{ + usercache.setAutoDelete( true ); + groupcache.setAutoDelete( true ); +} + + +int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault ) +{ + int ret = 0; +#ifdef USE_POSIX_ACL + + const TQString ACLString = metaData( "ACL_STRING" ); + const TQString defaultACLString = metaData( "DEFAULT_ACL_STRING" ); + // Empty strings mean leave as is + if ( !ACLString.isEmpty() ) { + acl_t acl = 0; + if ( ACLString == "ACL_DELETE" ) { + // user told us to delete the extended ACL, so let's write only + // the minimal (UNIX permission bits) part + acl = acl_from_mode( perm ); + } + acl = acl_from_text( ACLString.latin1() ); + if ( acl_valid( acl ) == 0 ) { // let's be safe + ret = acl_set_file( path, ACL_TYPE_ACCESS, acl ); + kdDebug(7101) << "Set ACL on: " << path << " to: " << aclAsString( acl ) << endl; + } + acl_free( acl ); + if ( ret != 0 ) return ret; // better stop trying right away + } + + if ( directoryDefault && !defaultACLString.isEmpty() ) { + if ( defaultACLString == "ACL_DELETE" ) { + // user told us to delete the default ACL, do so + ret += acl_delete_def_file( path ); + } else { + acl_t acl = acl_from_text( defaultACLString.latin1() ); + if ( acl_valid( acl ) == 0 ) { // let's be safe + ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl ); + kdDebug(7101) << "Set Default ACL on: " << path << " to: " << aclAsString( acl ) << endl; + } + acl_free( acl ); + } + } +#endif + return ret; +} + +void FileProtocol::chmod( const KURL& url, int permissions ) +{ + TQCString _path( TQFile::encodeName(url.path()) ); + /* FIXME: Should be atomic */ + if ( ::chmod( _path.data(), permissions ) == -1 || + ( setACL( _path.data(), permissions, false ) == -1 ) || + /* if not a directory, cannot set default ACLs */ + ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) { + + switch (errno) { + case EPERM: + case EACCES: + error( TDEIO::ERR_ACCESS_DENIED, url.path() ); + break; + case ENOTSUP: + error( TDEIO::ERR_UNSUPPORTED_ACTION, url.path() ); + break; + case ENOSPC: + error( TDEIO::ERR_DISK_FULL, url.path() ); + break; + default: + error( TDEIO::ERR_CANNOT_CHMOD, url.path() ); + } + } else + finished(); +} + +void FileProtocol::mkdir( const KURL& url, int permissions ) +{ + TQCString _path( TQFile::encodeName(url.path())); + + kdDebug(7101) << "mkdir(): " << _path << ", permission = " << permissions << endl; + + KDE_struct_stat buff; + if ( KDE_stat( _path.data(), &buff ) == -1 ) { + if ( ::mkdir( _path.data(), 0777 /*umask will be applied*/ ) != 0 ) { + if ( errno == EACCES ) { + error( TDEIO::ERR_ACCESS_DENIED, url.path() ); + return; + } else if ( errno == ENOSPC ) { + error( TDEIO::ERR_DISK_FULL, url.path() ); + return; + } else { + error( TDEIO::ERR_COULD_NOT_MKDIR, url.path() ); + return; + } + } else { + if ( permissions != -1 ) + chmod( url, permissions ); + else + finished(); + return; + } + } + + if ( S_ISDIR( buff.st_mode ) ) { + kdDebug(7101) << "ERR_DIR_ALREADY_EXIST" << endl; + error( TDEIO::ERR_DIR_ALREADY_EXIST, url.path() ); + return; + } + error( TDEIO::ERR_FILE_ALREADY_EXIST, url.path() ); + return; +} + +void FileProtocol::get( const KURL& url ) +{ + if (!url.isLocalFile()) { + KURL redir(url); + redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb")); + redirection(redir); + finished(); + return; + } + + TQCString _path( TQFile::encodeName(url.path())); + KDE_struct_stat buff; + if ( KDE_stat( _path.data(), &buff ) == -1 ) { + if ( errno == EACCES ) + error( TDEIO::ERR_ACCESS_DENIED, url.path() ); + else + error( TDEIO::ERR_DOES_NOT_EXIST, url.path() ); + return; + } + + if ( S_ISDIR( buff.st_mode ) ) { + error( TDEIO::ERR_IS_DIRECTORY, url.path() ); + return; + } + if ( !S_ISREG( buff.st_mode ) ) { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() ); + return; + } + + int fd = KDE_open( _path.data(), O_RDONLY); + if ( fd < 0 ) { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() ); + return; + } + +#ifdef HAVE_FADVISE + posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL); +#endif + + // Determine the mimetype of the file to be retrieved, and emit it. + // This is mandatory in all slaves (for KRun/BrowserRun to work). + KMimeType::Ptr mt = KMimeType::findByURL( url, buff.st_mode, true /* local URL */ ); + emit mimeType( mt->name() ); + + TDEIO::filesize_t processed_size = 0; + + TQString resumeOffset = metaData("resume"); + if ( !resumeOffset.isEmpty() ) + { + bool ok; + TDEIO::fileoffset_t offset = resumeOffset.toLongLong(&ok); + if (ok && (offset > 0) && (offset < buff.st_size)) + { + if (KDE_lseek(fd, offset, SEEK_SET) == offset) + { + canResume (); + processed_size = offset; + kdDebug( 7101 ) << "Resume offset: " << TDEIO::number(offset) << endl; + } + } + } + + totalSize( buff.st_size ); + + char buffer[ MAX_IPC_SIZE ]; + TQByteArray array; + + while( 1 ) + { + int n = ::read( fd, buffer, MAX_IPC_SIZE ); + if (n == -1) + { + if (errno == EINTR) + continue; + error( TDEIO::ERR_COULD_NOT_READ, url.path()); + close(fd); + return; + } + if (n == 0) + break; // Finished + + array.setRawData(buffer, n); + data( array ); + array.resetRawData(buffer, n); + + processed_size += n; + processedSize( processed_size ); + + //kdDebug( 7101 ) << "Processed: " << TDEIO::number (processed_size) << endl; + } + + data( TQByteArray() ); + + close( fd ); + + processedSize( buff.st_size ); + finished(); +} + +static int +write_all(int fd, const char *buf, size_t len) +{ + while (len > 0) + { + ssize_t written = write(fd, buf, len); + if (written < 0) + { + if (errno == EINTR) + continue; + return -1; + } + buf += written; + len -= written; + } + return 0; +} + +static bool +same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest) +{ + if (src.st_ino == dest.st_ino && + src.st_dev == dest.st_dev) + return true; + + return false; +} + +void FileProtocol::put( const KURL& url, int _mode, bool _overwrite, bool _resume ) +{ + TQString dest_orig = url.path(); + TQCString _dest_orig( TQFile::encodeName(dest_orig)); + + kdDebug(7101) << "put(): " << dest_orig << ", mode=" << _mode << endl; + + TQString dest_part( dest_orig ); + dest_part += TQString::fromLatin1(".part"); + TQCString _dest_part( TQFile::encodeName(dest_part)); + + KDE_struct_stat buff_orig; + bool bOrigExists = (KDE_lstat( _dest_orig.data(), &buff_orig ) != -1); + bool bPartExists = false; + bool bMarkPartial = config()->readBoolEntry("MarkPartial", true); + + if (bMarkPartial) + { + KDE_struct_stat buff_part; + bPartExists = (KDE_stat( _dest_part.data(), &buff_part ) != -1); + + if (bPartExists && !_resume && !_overwrite && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode)) + { + kdDebug(7101) << "FileProtocol::put : calling canResume with " + << TDEIO::number(buff_part.st_size) << endl; + + // 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. + _resume = canResume( buff_part.st_size ); + + kdDebug(7101) << "FileProtocol::put got answer " << _resume << endl; + } + } + + if ( bOrigExists && !_overwrite && !_resume) + { + if (S_ISDIR(buff_orig.st_mode)) + error( TDEIO::ERR_DIR_ALREADY_EXIST, dest_orig ); + else + error( TDEIO::ERR_FILE_ALREADY_EXIST, dest_orig ); + return; + } + + int result; + TQString dest; + TQCString _dest; + + int fd = -1; + + // Loop until we got 0 (end of data) + do + { + TQByteArray buffer; + dataReq(); // Request for data + result = readData( buffer ); + + if (result >= 0) + { + if (dest.isEmpty()) + { + if (bMarkPartial) + { + kdDebug(7101) << "Appending .part extension to " << dest_orig << endl; + dest = dest_part; + if ( bPartExists && !_resume ) + { + kdDebug(7101) << "Deleting partial file " << dest_part << endl; + remove( _dest_part.data() ); + // Catch errors when we try to open the file. + } + } + else + { + dest = dest_orig; + if ( bOrigExists && !_resume ) + { + kdDebug(7101) << "Deleting destination file " << dest_orig << endl; + remove( _dest_orig.data() ); + // Catch errors when we try to open the file. + } + } + + _dest = TQFile::encodeName(dest); + + if ( _resume ) + { + fd = KDE_open( _dest.data(), 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.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode); + } + + if ( fd < 0 ) + { + kdDebug(7101) << "####################### COULD NOT WRITE " << dest << " _mode=" << _mode << endl; + kdDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")" << endl; + if ( errno == EACCES ) + error( TDEIO::ERR_WRITE_ACCESS_DENIED, dest ); + else + error( TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, dest ); + return; + } + } + + if (write_all( fd, buffer.data(), buffer.size())) + { + if ( errno == ENOSPC ) // disk full + { + error( TDEIO::ERR_DISK_FULL, dest_orig); + result = -2; // means: remove dest file + } + else + { + kdWarning(7101) << "Couldn't write. Error:" << strerror(errno) << endl; + error( TDEIO::ERR_COULD_NOT_WRITE, dest_orig); + result = -1; + } + } + } + } + while ( result > 0 ); + + // An error occurred deal with it. + if (result < 0) + { + kdDebug(7101) << "Error during 'put'. Aborting." << endl; + + if (fd != -1) + { + close(fd); + + KDE_struct_stat buff; + if (bMarkPartial && KDE_stat( _dest.data(), &buff ) == 0) + { + int size = config()->readNumEntry("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) ) + { + kdWarning(7101) << "Error when closing file descriptor:" << strerror(errno) << endl; + error( TDEIO::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( _overwrite && S_ISLNK( buff_orig.st_mode ) ) + remove( _dest_orig.data() ); + + if ( ::rename( _dest.data(), _dest_orig.data() ) ) + { + kdWarning(7101) << " Couldn't rename " << _dest << " to " << _dest_orig << endl; + error( TDEIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig ); + return; + } + } + + // set final permissions + if ( _mode != -1 && !_resume ) + { + if (::chmod(_dest_orig.data(), _mode) != 0) + { + // couldn't chmod. Eat the error if the filesystem apparently doesn't support it. + if ( TDEIO::testFileSystemFlag( _dest_orig, TDEIO::SupportsChmod ) ) + warning( i18n( "Could not change permissions for\n%1" ).arg( dest_orig ) ); + } + } + + // set modification time + const TQString mtimeStr = metaData( "modified" ); + if ( !mtimeStr.isEmpty() ) { + TQDateTime dt = TQT_TQDATETIME_OBJECT(TQDateTime::fromString( mtimeStr, Qt::ISODate )); + if ( dt.isValid() ) { + KDE_struct_stat dest_statbuf; + if (KDE_stat( _dest_orig.data(), &dest_statbuf ) == 0) { + struct utimbuf utbuf; + utbuf.actime = dest_statbuf.st_atime; // access time, unchanged + utbuf.modtime = dt.toTime_t(); // modification time + kdDebug() << k_funcinfo << "setting modtime to " << utbuf.modtime << endl; + utime( _dest_orig.data(), &utbuf ); + } + } + + } + + // We have done our job => finish + finished(); +} + + +void FileProtocol::copy( const KURL &src, const KURL &dest, + int _mode, bool _overwrite ) +{ + kdDebug(7101) << "copy(): " << src << " -> " << dest << ", mode=" << _mode << endl; + + TQCString _src( TQFile::encodeName(src.path())); + TQCString _dest( TQFile::encodeName(dest.path())); + KDE_struct_stat buff_src; +#ifdef USE_POSIX_ACL + acl_t acl; +#endif + + if ( KDE_stat( _src.data(), &buff_src ) == -1 ) { + if ( errno == EACCES ) + error( TDEIO::ERR_ACCESS_DENIED, src.path() ); + else + error( TDEIO::ERR_DOES_NOT_EXIST, src.path() ); + return; + } + + if ( S_ISDIR( buff_src.st_mode ) ) { + error( TDEIO::ERR_IS_DIRECTORY, src.path() ); + return; + } + if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, src.path() ); + 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( TDEIO::ERR_DIR_ALREADY_EXIST, dest.path() ); + return; + } + + if ( same_inode( buff_dest, buff_src) ) + { + error( TDEIO::ERR_IDENTICAL_FILES, dest.path() ); + return; + } + + if (!_overwrite) + { + error( TDEIO::ERR_FILE_ALREADY_EXIST, dest.path() ); + 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 (_overwrite && S_ISLNK(buff_dest.st_mode)) + { + kdDebug(7101) << "copy(): LINK DESTINATION" << endl; + remove( _dest.data() ); + } + } + + int src_fd = KDE_open( _src.data(), O_RDONLY); + if ( src_fd < 0 ) { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, src.path() ); + return; + } + +#ifdef 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 ) { + kdDebug(7101) << "###### COULD NOT WRITE " << dest.url() << endl; + if ( errno == EACCES ) { + error( TDEIO::ERR_WRITE_ACCESS_DENIED, dest.path() ); + } else { + error( TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, dest.path() ); + } + close(src_fd); + return; + } + +#ifdef HAVE_FADVISE + posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL); +#endif + +#ifdef USE_POSIX_ACL + acl = acl_get_fd(src_fd); + if ( acl && !isExtendedACL( acl ) ) { + kdDebug(7101) << _dest.data() << " doesn't have extended ACL" << endl; + acl_free( acl ); + acl = NULL; + } +#endif + totalSize( buff_src.st_size ); + + TDEIO::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 = ::sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE ); + processed_size = sf; + if ( n == -1 && errno == EINVAL ) { //not all filesystems support sendfile() + kdDebug(7101) << "sendfile() not supported, falling back " << endl; + 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 ) { + kdDebug(7101) << "sendfile() error:" << strerror(errno) << endl; + if ( errno == ENOSPC ) // disk full + { + error( TDEIO::ERR_DISK_FULL, dest.path()); + remove( _dest.data() ); + } + else { + error( TDEIO::ERR_SLAVE_DEFINED, + i18n("Cannot copy file from %1 to %2. (Errno: %3)") + .arg( src.path() ).arg( dest.path() ).arg( errno ) ); + } + } else +#endif + error( TDEIO::ERR_COULD_NOT_READ, src.path()); + close(src_fd); + close(dest_fd); +#ifdef USE_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( TDEIO::ERR_DISK_FULL, dest.path()); + remove( _dest.data() ); + } + else + { + kdWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno) << endl; + error( TDEIO::ERR_COULD_NOT_WRITE, dest.path()); + } +#ifdef USE_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)) + { + kdWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno) << endl; + error( TDEIO::ERR_COULD_NOT_WRITE, dest.path()); +#ifdef USE_POSIX_ACL + if (acl) acl_free(acl); +#endif + return; + } + + // set final permissions + if ( _mode != -1 ) + { + if ( (::chmod(_dest.data(), _mode) != 0) +#ifdef USE_POSIX_ACL + || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0) +#endif + ) + { + // Eat the error if the filesystem apparently doesn't support chmod. + if ( TDEIO::testFileSystemFlag( _dest, TDEIO::SupportsChmod ) ) + warning( i18n( "Could not change permissions for\n%1" ).arg( dest.path() ) ); + } + } +#ifdef USE_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 ) + { + kdWarning() << TQString(TQString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg( dest.path() )) << endl; + } + + processedSize( buff_src.st_size ); + finished(); +} + +void FileProtocol::rename( const KURL &src, const KURL &dest, + bool _overwrite ) +{ + TQCString _src( TQFile::encodeName(src.path())); + TQCString _dest( TQFile::encodeName(dest.path())); + KDE_struct_stat buff_src; + if ( KDE_lstat( _src.data(), &buff_src ) == -1 ) { + if ( errno == EACCES ) + error( TDEIO::ERR_ACCESS_DENIED, src.path() ); + else + error( TDEIO::ERR_DOES_NOT_EXIST, src.path() ); + return; + } + + KDE_struct_stat buff_dest; + bool dest_exists = ( KDE_stat( _dest.data(), &buff_dest ) != -1 ); + if ( dest_exists ) + { + if (S_ISDIR(buff_dest.st_mode)) + { + error( TDEIO::ERR_DIR_ALREADY_EXIST, dest.path() ); + return; + } + + if ( same_inode( buff_dest, buff_src) ) + { + error( TDEIO::ERR_IDENTICAL_FILES, dest.path() ); + return; + } + + if (!_overwrite) + { + error( TDEIO::ERR_FILE_ALREADY_EXIST, dest.path() ); + return; + } + } + + if ( ::rename( _src.data(), _dest.data())) + { + if (( errno == EACCES ) || (errno == EPERM)) { + error( TDEIO::ERR_ACCESS_DENIED, dest.path() ); + } + else if (errno == EXDEV) { + error( TDEIO::ERR_UNSUPPORTED_ACTION, TQString::fromLatin1("rename")); + } + else if (errno == EROFS) { // The file is on a read-only filesystem + error( TDEIO::ERR_CANNOT_DELETE, src.path() ); + } + else { + error( TDEIO::ERR_CANNOT_RENAME, src.path() ); + } + return; + } + + finished(); +} + +void FileProtocol::symlink( const TQString &target, const KURL &dest, bool overwrite ) +{ + // Assume dest is local too (wouldn't be here otherwise) + if ( ::symlink( TQFile::encodeName( target ), TQFile::encodeName( dest.path() ) ) == -1 ) + { + // Does the destination already exist ? + if ( errno == EEXIST ) + { + if ( overwrite ) + { + // Try to delete the destination + if ( unlink( TQFile::encodeName( dest.path() ) ) != 0 ) + { + error( TDEIO::ERR_CANNOT_DELETE, dest.path() ); + return; + } + // Try again - this won't loop forever since unlink succeeded + symlink( target, dest, overwrite ); + } + else + { + KDE_struct_stat buff_dest; + KDE_lstat( TQFile::encodeName( dest.path() ), &buff_dest ); + if (S_ISDIR(buff_dest.st_mode)) + error( TDEIO::ERR_DIR_ALREADY_EXIST, dest.path() ); + else + error( TDEIO::ERR_FILE_ALREADY_EXIST, dest.path() ); + return; + } + } + else + { + // Some error occurred while we tried to symlink + error( TDEIO::ERR_CANNOT_SYMLINK, dest.path() ); + return; + } + } + finished(); +} + +void FileProtocol::del( const KURL& url, bool isfile) +{ + TQCString _path( TQFile::encodeName(url.path())); + /***** + * Delete files + *****/ + + if (isfile) { + kdDebug( 7101 ) << "Deleting file "<< url.url() << endl; + + // TODO deletingFile( source ); + + if ( unlink( _path.data() ) == -1 ) { + if ((errno == EACCES) || (errno == EPERM)) + error( TDEIO::ERR_ACCESS_DENIED, url.path()); + else if (errno == EISDIR) + error( TDEIO::ERR_IS_DIRECTORY, url.path()); + else + error( TDEIO::ERR_CANNOT_DELETE, url.path() ); + return; + } + } else { + + /***** + * Delete empty directory + *****/ + + kdDebug( 7101 ) << "Deleting directory " << url.url() << endl; + + if ( ::rmdir( _path.data() ) == -1 ) { + if ((errno == EACCES) || (errno == EPERM)) + error( TDEIO::ERR_ACCESS_DENIED, url.path()); + else { + kdDebug( 7101 ) << "could not rmdir " << perror << endl; + error( TDEIO::ERR_COULD_NOT_RMDIR, url.path() ); + return; + } + } + } + + finished(); +} + + +TQString FileProtocol::getUserName( uid_t uid ) +{ + TQString *temp; + temp = usercache.find( uid ); + if ( !temp ) { + struct passwd *user = getpwuid( uid ); + if ( user ) { + usercache.insert( uid, new TQString(TQString::fromLatin1(user->pw_name)) ); + return TQString::fromLatin1( user->pw_name ); + } + else + return TQString::number( uid ); + } + else + return *temp; +} + +TQString FileProtocol::getGroupName( gid_t gid ) +{ + TQString *temp; + temp = groupcache.find( gid ); + if ( !temp ) { + struct group *grp = getgrgid( gid ); + if ( grp ) { + groupcache.insert( gid, new TQString(TQString::fromLatin1(grp->gr_name)) ); + return TQString::fromLatin1( grp->gr_name ); + } + else + return TQString::number( gid ); + } + else + return *temp; +} + + + +bool FileProtocol::createUDSEntry( const TQString & filename, const TQCString & path, UDSEntry & entry, + short int details, bool withACL ) +{ + assert(entry.count() == 0); // by contract :-) + // Note: details = 0 (only "file or directory or symlink or doesn't exist") isn't implemented + // because there's no real performance penalty in kio_file for returning the complete + // details. Please consider doing it in your tdeioslave if you're using this one as a model :) + UDSAtom atom; + atom.m_uds = TDEIO::UDS_NAME; + atom.m_str = filename; + entry.append( atom ); + + mode_t type; + mode_t access; + KDE_struct_stat buff; + + if ( KDE_lstat( path.data(), &buff ) == 0 ) { + + if (S_ISLNK(buff.st_mode)) { + + char buffer2[ 1000 ]; + int n = readlink( path.data(), buffer2, 1000 ); + if ( n != -1 ) { + buffer2[ n ] = 0; + } + + atom.m_uds = TDEIO::UDS_LINK_DEST; + atom.m_str = TQFile::decodeName( buffer2 ); + entry.append( atom ); + + // A symlink -> follow it only if details>1 + if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) { + // It is a link pointing to nowhere + type = S_IFMT - 1; + access = S_IRWXU | S_IRWXG | S_IRWXO; + + atom.m_uds = TDEIO::UDS_FILE_TYPE; + atom.m_long = type; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_ACCESS; + atom.m_long = access; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_SIZE; + atom.m_long = 0L; + entry.append( atom ); + + goto notype; + + } + } + } else { + // kdWarning() << "lstat didn't work on " << path.data() << endl; + return false; + } + + type = buff.st_mode & S_IFMT; // extract file type + access = buff.st_mode & 07777; // extract permissions + + atom.m_uds = TDEIO::UDS_FILE_TYPE; + atom.m_long = type; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_ACCESS; + atom.m_long = access; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_SIZE; + atom.m_long = buff.st_size; + entry.append( atom ); + +#ifdef USE_POSIX_ACL + /* Append an atom indicating whether the file has extended acl information + * and if withACL is specified also one with the acl itself. If it's a directory + * and it has a default ACL, also append that. */ + appendACLAtoms( path, entry, type, withACL ); +#endif + + notype: + atom.m_uds = TDEIO::UDS_MODIFICATION_TIME; + atom.m_long = buff.st_mtime; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_USER; + atom.m_str = getUserName( buff.st_uid ); + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_GROUP; + atom.m_str = getGroupName( buff.st_gid ); + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_ACCESS_TIME; + atom.m_long = buff.st_atime; + entry.append( atom ); + + // Note: buff.st_ctime isn't the creation time ! + // We made that mistake for KDE 2.0, but it's in fact the + // "file status" change time, which we don't care about. + + return true; +} + +void FileProtocol::stat( const KURL & url ) +{ + if (!url.isLocalFile()) { + KURL redir(url); + redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb")); + redirection(redir); + kdDebug(7101) << "redirecting to " << redir.url() << endl; + finished(); + return; + } + + /* directories may not have a slash at the end if + * we want to stat() them; it requires that we + * change into it .. which may not be allowed + * stat("/is/unaccessible") -> rwx------ + * stat("/is/unaccessible/") -> EPERM H.Z. + * This is the reason for the -1 + */ + TQCString _path( TQFile::encodeName(url.path(-1))); + + TQString sDetails = metaData(TQString::fromLatin1("details")); + int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); + kdDebug(7101) << "FileProtocol::stat details=" << details << endl; + + UDSEntry entry; + if ( !createUDSEntry( url.fileName(), _path, entry, details, true /*with acls*/ ) ) + { + error( TDEIO::ERR_DOES_NOT_EXIST, url.path(-1) ); + return; + } +#if 0 +///////// debug code + TDEIO::UDSEntry::ConstIterator it = entry.begin(); + for( ; it != entry.end(); it++ ) { + switch ((*it).m_uds) { + case TDEIO::UDS_FILE_TYPE: + kdDebug(7101) << "File Type : " << (mode_t)((*it).m_long) << endl; + break; + case TDEIO::UDS_ACCESS: + kdDebug(7101) << "Access permissions : " << (mode_t)((*it).m_long) << endl; + break; + case TDEIO::UDS_USER: + kdDebug(7101) << "User : " << ((*it).m_str.ascii() ) << endl; + break; + case TDEIO::UDS_GROUP: + kdDebug(7101) << "Group : " << ((*it).m_str.ascii() ) << endl; + break; + case TDEIO::UDS_NAME: + kdDebug(7101) << "Name : " << ((*it).m_str.ascii() ) << endl; + //m_strText = decodeFileName( (*it).m_str ); + break; + case TDEIO::UDS_URL: + kdDebug(7101) << "URL : " << ((*it).m_str.ascii() ) << endl; + break; + case TDEIO::UDS_MIME_TYPE: + kdDebug(7101) << "MimeType : " << ((*it).m_str.ascii() ) << endl; + break; + case TDEIO::UDS_LINK_DEST: + kdDebug(7101) << "LinkDest : " << ((*it).m_str.ascii() ) << endl; + break; + case TDEIO::UDS_EXTENDED_ACL: + kdDebug(7101) << "Contains extended ACL " << endl; + break; + } + } + MetaData::iterator it1 = mOutgoingMetaData.begin(); + for ( ; it1 != mOutgoingMetaData.end(); it1++ ) { + kdDebug(7101) << it1.key() << " = " << it1.data() << endl; + } +///////// +#endif + statEntry( entry ); + + finished(); +} + +void FileProtocol::listDir( const KURL& url) +{ + kdDebug(7101) << "========= LIST " << url.url() << " =========" << endl; + if (!url.isLocalFile()) { + KURL redir(url); + redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb")); + redirection(redir); + kdDebug(7101) << "redirecting to " << redir.url() << endl; + finished(); + return; + } + + TQCString _path( TQFile::encodeName(url.path())); + + KDE_struct_stat buff; + if ( KDE_stat( _path.data(), &buff ) == -1 ) { + error( TDEIO::ERR_DOES_NOT_EXIST, url.path() ); + return; + } + + if ( !S_ISDIR( buff.st_mode ) ) { + error( TDEIO::ERR_IS_FILE, url.path() ); + return; + } + + DIR *dp = 0L; + KDE_struct_dirent *ep; + + dp = opendir( _path.data() ); + if ( dp == 0 ) { + switch (errno) + { +#ifdef ENOMEDIUM + case ENOMEDIUM: + error( ERR_SLAVE_DEFINED, + i18n( "No media in device for %1" ).arg( url.path() ) ); + break; +#endif + default: + error( TDEIO::ERR_CANNOT_ENTER_DIRECTORY, url.path() ); + break; + } + return; + } + + // Don't make this a TQStringList. The locale file name we get here + // should be passed intact to createUDSEntry to avoid problems with + // files where TQFile::encodeName(TQFile::decodeName(a)) != a. + TQStrList entryNames; + + while ( ( ep = KDE_readdir( dp ) ) != 0L ) + entryNames.append( ep->d_name ); + + closedir( dp ); + totalSize( entryNames.count() ); + + /* set the current dir to the path to speed up + in not having to pass an absolute path. + We restore the path later to get out of the + path - the kernel wouldn't unmount or delete + directories we keep as active directory. And + as the slave runs in the background, it's hard + to see for the user what the problem would be */ +#if !defined(PATH_MAX) && defined(__GLIBC__) + char *path_buffer; + path_buffer = getcwd(NULL, 0); +#else + char path_buffer[PATH_MAX]; + (void) getcwd(path_buffer, PATH_MAX - 1); +#endif + if ( chdir( _path.data() ) ) { + if (errno == EACCES) + error(ERR_ACCESS_DENIED, _path); + else + error(ERR_CANNOT_ENTER_DIRECTORY, _path); + finished(); + } + + UDSEntry entry; + TQStrListIterator it(entryNames); + for (; it.current(); ++it) { + entry.clear(); + if ( createUDSEntry( TQFile::decodeName(*it), + *it /* we can use the filename as relative path*/, + entry, 2, true ) ) + listEntry( entry, false); + //else + // ;//Well, this should never happen... but with wrong encoding names + } + + listEntry( entry, true ); // ready + + kdDebug(7101) << "============= COMPLETED LIST ============" << endl; + + chdir(path_buffer); +#if !defined(PATH_MAX) && defined(__GLIBC__) + free(path_buffer); +#endif + + finished(); +} + +/* +void FileProtocol::testDir( const TQString& path ) +{ + TQCString _path( TQFile::encodeName(path)); + KDE_struct_stat buff; + if ( KDE_stat( _path.data(), &buff ) == -1 ) { + error( TDEIO::ERR_DOES_NOT_EXIST, path ); + return; + } + + if ( S_ISDIR( buff.st_mode ) ) + isDirectory(); + else + isFile(); + + finished(); +} +*/ + +void FileProtocol::special( const TQByteArray &data) +{ + int tmp; + TQDataStream stream(data, IO_ReadOnly); + + stream >> tmp; + switch (tmp) { + case 1: + { + TQString fstype, dev, point; + TQ_INT8 iRo; + + stream >> iRo >> fstype >> dev >> point; + + bool ro = ( iRo != 0 ); + + kdDebug(7101) << "MOUNTING fstype=" << fstype << " dev=" << dev << " point=" << point << " ro=" << ro << endl; + bool ok = pmount( dev ); + if (ok) + finished(); + else + mount( ro, fstype.ascii(), dev, point ); + + } + break; + case 2: + { + TQString point; + stream >> point; + bool ok = pumount( point ); + if (ok) + finished(); + else + unmount( point ); + } + break; + + case 3: + { + TQString filename; + stream >> filename; + KShred shred( filename ); + connect( &shred, TQT_SIGNAL( processedSize( TDEIO::filesize_t ) ), + this, TQT_SLOT( slotProcessedSize( TDEIO::filesize_t ) ) ); + connect( &shred, TQT_SIGNAL( infoMessage( const TQString & ) ), + this, TQT_SLOT( slotInfoMessage( const TQString & ) ) ); + if (!shred.shred()) + error( TDEIO::ERR_CANNOT_DELETE, filename ); + else + finished(); + break; + } + default: + break; + } +} + +// Connected to KShred +void FileProtocol::slotProcessedSize( TDEIO::filesize_t bytes ) +{ + kdDebug(7101) << "FileProtocol::slotProcessedSize (" << (unsigned int) bytes << ")" << endl; + processedSize( bytes ); +} + +// Connected to KShred +void FileProtocol::slotInfoMessage( const TQString & msg ) +{ + kdDebug(7101) << "FileProtocol::slotInfoMessage (" << msg << ")" << endl; + infoMessage( msg ); +} + +void FileProtocol::mount( bool _ro, const char *_fstype, const TQString& _dev, const TQString& _point ) +{ + kdDebug(7101) << "FileProtocol::mount _fstype=" << _fstype << endl; + TQCString buffer; + +#ifdef HAVE_VOLMGT + /* + * support for Solaris volume management + */ + TQString err; + TQCString devname = TQFile::encodeName( _dev ); + + if( volmgt_running() ) { +// kdDebug(7101) << "VOLMGT: vold ok." << endl; + if( volmgt_check( devname.data() ) == 0 ) { + kdDebug(7101) << "VOLMGT: no media in " + << devname.data() << endl; + err = i18n("No Media inserted or Media not recognized."); + error( TDEIO::ERR_COULD_NOT_MOUNT, err ); + return; + } else { + kdDebug(7101) << "VOLMGT: " << devname.data() + << ": media ok" << endl; + finished(); + return; + } + } else { + err = i18n("\"vold\" is not running."); + kdDebug(7101) << "VOLMGT: " << err << endl; + error( TDEIO::ERR_COULD_NOT_MOUNT, err ); + return; + } +#else + + + KTempFile tmpFile; + TQCString tmpFileC = TQFile::encodeName(tmpFile.name()); + const char *tmp = tmpFileC.data(); + TQCString dev; + if ( _dev.startsWith( "LABEL=" ) ) { // turn LABEL=foo into -L foo (#71430) + TQString labelName = _dev.mid( 6 ); + dev = "-L "; + dev += TQFile::encodeName( TDEProcess::quote( labelName ) ); // is it correct to assume same encoding as filesystem? + } else if ( _dev.startsWith( "UUID=" ) ) { // and UUID=bar into -U bar + TQString uuidName = _dev.mid( 5 ); + dev = "-U "; + dev += TQFile::encodeName( TDEProcess::quote( uuidName ) ); + } + else + dev = TQFile::encodeName( TDEProcess::quote(_dev) ); // get those ready to be given to a shell + + TQCString point = TQFile::encodeName( TDEProcess::quote(_point) ); + bool fstype_empty = !_fstype || !*_fstype; + TQCString fstype = TDEProcess::quote(_fstype).latin1(); // good guess + TQCString readonly = _ro ? "-r" : ""; + TQString epath = TQString::fromLatin1(getenv("PATH")); + TQString path = TQString::fromLatin1("/sbin:/bin"); + if(!epath.isEmpty()) + path += TQString::fromLatin1(":") + epath; + TQString mountProg = TDEGlobal::dirs()->findExe("mount", path); + if (mountProg.isEmpty()){ + error( TDEIO::ERR_COULD_NOT_MOUNT, i18n("Could not find program \"mount\"")); + return; + } + + // Two steps, in case mount doesn't like it when we pass all options + for ( int step = 0 ; step <= 1 ; step++ ) + { + // Mount using device only if no fstype nor mountpoint (KDE-1.x like) + if ( !_dev.isEmpty() && _point.isEmpty() && fstype_empty ) + buffer.sprintf( "%s %s 2>%s", mountProg.latin1(), dev.data(), tmp ); + else + // Mount using the mountpoint, if no fstype nor device (impossible in first step) + if ( !_point.isEmpty() && _dev.isEmpty() && fstype_empty ) + buffer.sprintf( "%s %s 2>%s", mountProg.latin1(), point.data(), tmp ); + else + // mount giving device + mountpoint but no fstype + if ( !_point.isEmpty() && !_dev.isEmpty() && fstype_empty ) + buffer.sprintf( "%s %s %s %s 2>%s", mountProg.latin1(), readonly.data(), dev.data(), point.data(), tmp ); + else + // mount giving device + mountpoint + fstype +#if defined(__svr4__) && defined(__sun__) // MARCO for Solaris 8 and I + // believe this is true for SVR4 in general + buffer.sprintf( "%s -F %s %s %s %s 2>%s" + mountProg.latin1() + fstype.data() + _ro ? "-oro" : "" + dev.data() + point.data() + tmp ); +#else + buffer.sprintf( "%s %s -t %s %s %s 2>%s", mountProg.latin1(), readonly.data(), + fstype.data(), dev.data(), point.data(), tmp ); +#endif + + kdDebug(7101) << buffer << endl; + + int mount_ret = system( buffer.data() ); + + TQString err = testLogFile( tmp ); + if ( err.isEmpty() && mount_ret == 0) + { + finished(); + return; + } + else + { + // Didn't work - or maybe we just got a warning + TQString mp = TDEIO::findDeviceMountPoint( _dev ); + // Is the device mounted ? + if ( !mp.isEmpty() && mount_ret == 0) + { + kdDebug(7101) << "mount got a warning: " << err << endl; + warning( err ); + finished(); + return; + } + else + { + if ( (step == 0) && !_point.isEmpty()) + { + kdDebug(7101) << err << endl; + kdDebug(7101) << "Mounting with those options didn't work, trying with only mountpoint" << endl; + fstype = ""; + fstype_empty = true; + dev = ""; + // The reason for trying with only mountpoint (instead of + // only device) is that some people (hi Malte!) have the + // same device associated with two mountpoints + // for different fstypes, like /dev/fd0 /mnt/e2floppy and + // /dev/fd0 /mnt/dosfloppy. + // If the user has the same mountpoint associated with two + // different devices, well they shouldn't specify the + // mountpoint but just the device. + } + else + { + error( TDEIO::ERR_COULD_NOT_MOUNT, err ); + return; + } + } + } + } +#endif /* ! HAVE_VOLMGT */ +} + + +void FileProtocol::unmount( const TQString& _point ) +{ + TQCString buffer; + + KTempFile tmpFile; + TQCString tmpFileC = TQFile::encodeName(tmpFile.name()); + TQString err; + const char *tmp = tmpFileC.data(); + +#ifdef HAVE_VOLMGT + /* + * support for Solaris volume management + */ + char *devname; + char *ptr; + FILE *mnttab; + struct mnttab mnt; + + if( volmgt_running() ) { + kdDebug(7101) << "VOLMGT: looking for " + << _point.local8Bit() << endl; + + if( (mnttab = KDE_fopen( MNTTAB, "r" )) == NULL ) { + err = "couldn't open mnttab"; + kdDebug(7101) << "VOLMGT: " << err << endl; + error( TDEIO::ERR_COULD_NOT_UNMOUNT, err ); + return; + } + + /* + * since there's no way to derive the device name from + * the mount point through the volmgt library (and + * media_findname() won't work in this case), we have to + * look ourselves... + */ + devname = NULL; + rewind( mnttab ); + while( getmntent( mnttab, &mnt ) == 0 ) { + if( strcmp( _point.local8Bit(), mnt.mnt_mountp ) == 0 ){ + devname = mnt.mnt_special; + break; + } + } + fclose( mnttab ); + + if( devname == NULL ) { + err = "not in mnttab"; + kdDebug(7101) << "VOLMGT: " + << TQFile::encodeName(_point).data() + << ": " << err << endl; + error( TDEIO::ERR_COULD_NOT_UNMOUNT, err ); + return; + } + + /* + * strip off the directory name (volume name) + * the eject(1) command will handle unmounting and + * physically eject the media (if possible) + */ + ptr = strrchr( devname, '/' ); + *ptr = '\0'; + TQCString qdevname(TQFile::encodeName(TDEProcess::quote(TQFile::decodeName(TQCString(devname)))).data()); + buffer.sprintf( "/usr/bin/eject %s 2>%s", qdevname.data(), tmp ); + kdDebug(7101) << "VOLMGT: eject " << qdevname << endl; + + /* + * from eject(1): exit status == 0 => need to manually eject + * exit status == 4 => media was ejected + */ +// if( WEXITSTATUS( system( buffer.local8Bit() )) == 4 ) { + if( WEXITSTATUS( system( buffer.data() )) == 4 ) { // Fix for TQString -> QCString? + /* + * this is not an error, so skip "testLogFile()" + * to avoid wrong/confusing error popup + */ + unlink( tmp ); + finished(); + return; + } + } else { + /* + * eject(1) should do its job without vold(1M) running, + * so we probably could call eject anyway, but since the + * media is mounted now, vold must've died for some reason + * during the user's session, so it should be restarted... + */ + err = i18n("\"vold\" is not running."); + kdDebug(7101) << "VOLMGT: " << err << endl; + error( TDEIO::ERR_COULD_NOT_UNMOUNT, err ); + return; + } +#else + TQString epath = getenv("PATH"); + TQString path = TQString::fromLatin1("/sbin:/bin"); + if (!epath.isEmpty()) + path += ":" + epath; + TQString umountProg = TDEGlobal::dirs()->findExe("umount", path); + + if (umountProg.isEmpty()) { + error( TDEIO::ERR_COULD_NOT_UNMOUNT, i18n("Could not find program \"umount\"")); + return; + } + buffer.sprintf( "%s %s 2>%s", umountProg.latin1(), TQFile::encodeName(TDEProcess::quote(_point)).data(), tmp ); + system( buffer.data() ); +#endif /* HAVE_VOLMGT */ + + err = testLogFile( tmp ); + + if (err.contains("fstab") || err.contains("root")) { + TQString olderr; + err = TQString::null; + + DCOPRef d("kded", "mediamanager"); + d.setDCOPClient ( dcopClient() ); + DCOPReply reply = d.call("properties", _point); + TQString udi; + + if ( reply.isValid() ) { + TQStringList list = reply; + if (list.size()) + udi = list[0]; + } + + if (!udi.isEmpty()) + reply = d.call("unmount", udi); + + if (udi.isEmpty() || !reply.isValid()) + err = olderr; + else if (reply.isValid()) + reply.get(err); + } + + if ( err.isEmpty() ) + finished(); + else + error( TDEIO::ERR_COULD_NOT_UNMOUNT, err ); +} + +/************************************* + * + * pmount handling + * + *************************************/ + +bool FileProtocol::pmount(const TQString &dev) +{ + TQString epath = getenv("PATH"); + TQString path = TQString::fromLatin1("/sbin:/bin"); + if (!epath.isEmpty()) + path += ":" + epath; + TQString pmountProg = TDEGlobal::dirs()->findExe("pmount", path); + + if (pmountProg.isEmpty()) + return false; + + TQCString buffer; + buffer.sprintf( "%s %s", TQFile::encodeName(pmountProg).data(), + TQFile::encodeName(TDEProcess::quote(dev)).data() ); + + int res = system( buffer.data() ); + + return res==0; +} + +bool FileProtocol::pumount(const TQString &point) +{ + TQString real_point = KStandardDirs::realPath(point); + + KMountPoint::List mtab = KMountPoint::currentMountPoints(); + + KMountPoint::List::const_iterator it = mtab.begin(); + KMountPoint::List::const_iterator end = mtab.end(); + + TQString dev; + + for (; it!=end; ++it) + { + TQString tmp = (*it)->mountedFrom(); + TQString mp = (*it)->mountPoint(); + mp = KStandardDirs::realPath(mp); + + if (mp==real_point) + dev = KStandardDirs::realPath(tmp); + } + + if (dev.isEmpty()) return false; + if (dev.endsWith("/")) dev.truncate(dev.length()-1); + + TQString epath = getenv("PATH"); + TQString path = TQString::fromLatin1("/sbin:/bin"); + if (!epath.isEmpty()) + path += ":" + epath; + TQString pumountProg = TDEGlobal::dirs()->findExe("pumount", path); + + if (pumountProg.isEmpty()) + return false; + + TQCString buffer; + buffer.sprintf( "%s %s", TQFile::encodeName(pumountProg).data(), + TQFile::encodeName(TDEProcess::quote(dev)).data() ); + + int res = system( buffer.data() ); + + return res==0; +} + +/************************************* + * + * Utilities + * + *************************************/ + +static TQString testLogFile( const char *_filename ) +{ + char buffer[ 1024 ]; + KDE_struct_stat buff; + + TQString result; + + KDE_stat( _filename, &buff ); + int size = buff.st_size; + if ( size == 0 ) { + unlink( _filename ); + return result; + } + + FILE * f = KDE_fopen( _filename, "rb" ); + if ( f == 0L ) { + unlink( _filename ); + result = i18n("Could not read %1").arg(TQFile::decodeName(_filename)); + return result; + } + + result = ""; + const char *p = ""; + while ( p != 0L ) { + p = fgets( buffer, sizeof(buffer)-1, f ); + if ( p != 0L ) + result += TQString::fromLocal8Bit(buffer); + } + + fclose( f ); + + unlink( _filename ); + + return result; +} + +/************************************* + * + * ACL handling helpers + * + *************************************/ +#ifdef USE_POSIX_ACL + +static bool isExtendedACL( acl_t acl ) +{ + return ( acl_equiv_mode( acl, 0 ) != 0 ); +} + +static TQString aclAsString( acl_t acl ) +{ + char *aclString = acl_to_text( acl, 0 ); + TQString ret = TQString::fromLatin1( aclString ); + acl_free( (void*)aclString ); + return ret; +} + +static void appendACLAtoms( const TQCString & path, UDSEntry& entry, mode_t type, bool withACL ) +{ + // first check for a noop +#ifdef HAVE_NON_POSIX_ACL_EXTENSIONS + if ( acl_extended_file( path.data() ) == 0 ) return; +#endif + + acl_t acl = 0; + acl_t defaultAcl = 0; + UDSAtom atom; + bool isDir = S_ISDIR( type ); + // do we have an acl for the file, and/or a default acl for the dir, if it is one? + if ( ( acl = acl_get_file( path.data(), ACL_TYPE_ACCESS ) ) ) { + if ( !isExtendedACL( acl ) ) { + acl_free( acl ); + acl = 0; + } + } + + /* Sadly libacl does not provided a means of checking for extended ACL and default + * ACL separately. Since a directory can have both, we need to check again. */ + if ( isDir ) + defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT ); + + if ( acl || defaultAcl ) { + kdDebug(7101) << path.data() << " has extended ACL entries " << endl; + atom.m_uds = TDEIO::UDS_EXTENDED_ACL; + atom.m_long = 1; + entry.append( atom ); + } + if ( withACL ) { + if ( acl ) { + atom.m_uds = TDEIO::UDS_ACL_STRING; + atom.m_str = aclAsString( acl ); + entry.append( atom ); + kdDebug(7101) << path.data() << "ACL: " << atom.m_str << endl; + } + if ( defaultAcl ) { + atom.m_uds = TDEIO::UDS_DEFAULT_ACL_STRING; + atom.m_str = aclAsString( defaultAcl ); + entry.append( atom ); + kdDebug(7101) << path.data() << "DEFAULT ACL: " << atom.m_str << endl; + } + } + if ( acl ) acl_free( acl ); + if ( defaultAcl ) acl_free( defaultAcl ); +} +#endif + +#include "file.moc" diff --git a/tdeioslave/file/file.h b/tdeioslave/file/file.h new file mode 100644 index 000000000..226e0a428 --- /dev/null +++ b/tdeioslave/file/file.h @@ -0,0 +1,98 @@ +/* + Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org> + Copyright (C) 2000-2002 David Faure <faure@kde.org> + Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License (LGPL) as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later + version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __file_h__ +#define __file_h__ "$Id$" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdio.h> +#include <unistd.h> + +#include <tqobject.h> +#include <tqintdict.h> +#include <tqstring.h> +#include <tqvaluelist.h> + +#include <tdeio/global.h> +#include <tdeio/slavebase.h> + +// Note that this header file is installed, so think twice +// before breaking binary compatibility (read: it is forbidden :) + +class FileProtocol : public TQObject, public TDEIO::SlaveBase +{ + Q_OBJECT +public: + FileProtocol( const TQCString &pool, const TQCString &app); + virtual ~FileProtocol() { } + + virtual void get( const KURL& url ); + virtual void put( const KURL& url, int permissions, + bool overwrite, bool resume ); + virtual void copy( const KURL &src, const KURL &dest, + int permissions, bool overwrite ); + virtual void rename( const KURL &src, const KURL &dest, + bool overwrite ); + virtual void symlink( const TQString &target, const KURL &dest, + bool overwrite ); + + virtual void stat( const KURL& url ); + virtual void listDir( const KURL& url ); + virtual void mkdir( const KURL& url, int permissions ); + virtual void chmod( const KURL& url, int permissions ); + virtual void del( const KURL& url, bool isfile); + + /** + * Special commands supported by this slave: + * 1 - mount + * 2 - unmount + * 3 - shred + */ + virtual void special( const TQByteArray &data); + void unmount( const TQString& point ); + void mount( bool _ro, const char *_fstype, const TQString& dev, const TQString& point ); + bool pumount( const TQString &point ); + bool pmount( const TQString &dev ); + +protected slots: + void slotProcessedSize( TDEIO::filesize_t _bytes ); + void slotInfoMessage( const TQString & msg ); + +protected: + + bool createUDSEntry( const TQString & filename, const TQCString & path, TDEIO::UDSEntry & entry, + short int details, bool withACL ); + int setACL( const char *path, mode_t perm, bool _directoryDefault ); + + TQString getUserName( uid_t uid ); + TQString getGroupName( gid_t gid ); + + TQIntDict<TQString> usercache; // maps long ==> TQString * + TQIntDict<TQString> groupcache; + + class FileProtocolPrivate; + FileProtocolPrivate *d; +}; + +#endif diff --git a/tdeioslave/file/file.protocol b/tdeioslave/file/file.protocol new file mode 100644 index 000000000..be7c5d587 --- /dev/null +++ b/tdeioslave/file/file.protocol @@ -0,0 +1,15 @@ +[Protocol] +exec=kio_file +protocol=file +input=none +output=filesystem +listing=Name,Type,Size,Date,AccessDate,Access,Owner,Group,Link +reading=true +writing=true +makedir=true +deleting=true +linking=true +moving=true +maxInstances=4 +DocPath=tdeioslave/file.html +Class=:local |