Index: src/ioslaves/file/file.cpp =================================================================== --- src/ioslaves/file/file.cpp +++ src/ioslaves/file/file.cpp @@ -77,6 +77,8 @@ #include #include +#include "sharefd.h" + Q_LOGGING_CATEGORY(KIO_FILE, "kf5.kio.kio_file") // Pseudo plugin class to embed meta data @@ -229,22 +231,25 @@ (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(KIO::ERR_ACCESS_DENIED, path); - break; + if (execWithElevatedPrivilege(CHMOD, path, permissions)) { + finished(); + } else { + switch (errno) { + case EPERM: + case EACCES: + error(KIO::ERR_ACCESS_DENIED, path); + break; #if defined(ENOTSUP) - case ENOTSUP: // from setACL since chmod can't return ENOTSUP - error(KIO::ERR_UNSUPPORTED_ACTION, i18n("Setting ACL for %1", path)); - break; + case ENOTSUP: // from setACL since chmod can't return ENOTSUP + error(KIO::ERR_UNSUPPORTED_ACTION, i18n("Setting ACL for %1", path)); + break; #endif - case ENOSPC: - error(KIO::ERR_DISK_FULL, path); - break; - default: - error(KIO::ERR_CANNOT_CHMOD, path); + case ENOSPC: + error(KIO::ERR_DISK_FULL, path); + break; + default: + error(KIO::ERR_CANNOT_CHMOD, path); + } } } else { finished(); @@ -260,11 +265,13 @@ utbuf.actime = statbuf.st_atime; // access time, unchanged utbuf.modtime = mtime.toTime_t(); // modification time if (::utime(QFile::encodeName(path).constData(), &utbuf) != 0) { - // TODO: errno could be EACCES, EPERM, EROFS - error(KIO::ERR_CANNOT_SETTIME, path); - } else { - finished(); + if (!execWithElevatedPrivilege(UTIME, path, qint64(utbuf.actime), qint64(utbuf.modtime))) { + // TODO: errno could be EACCES, EPERM, EROFS + error(KIO::ERR_CANNOT_SETTIME, path); + return; + } } + finished(); } else { error(KIO::ERR_DOES_NOT_EXIST, path); } @@ -278,12 +285,18 @@ // Remove existing file or symlink, if requested (#151851) if (metaData(QStringLiteral("overwrite")) == QLatin1String("true")) { - QFile::remove(path); + if (!QFile::remove(path)) { + execWithElevatedPrivilege(DEL, path); + } } QT_STATBUF buff; if (QT_LSTAT(QFile::encodeName(path).constData(), &buff) == -1) { - if (!QDir().mkdir(path)) { + bool dirCreated = QDir().mkdir(path); + if (!dirCreated) + dirCreated = execWithElevatedPrivilege(MKDIR, path); + + if (!dirCreated) { //TODO: add access denied & disk full (or another reasons) handling (into Qt, possibly) error(KIO::ERR_CANNOT_MKDIR, path); return; @@ -338,8 +351,17 @@ QFile f(path); if (!f.open(QIODevice::ReadOnly)) { - error(KIO::ERR_CANNOT_OPEN_FOR_READING, path); - return; + int src_fd = -1; + FdReceiver fdRecv; + bool _continue = ( fdRecv.startListening(QStringLiteral("org_kde_kio_file_helper_socket")) + && execWithElevatedPrivilege(OPEN, path, O_RDONLY, S_IRUSR) + && (src_fd = fdRecv.fileDescriptor()) != -1 + && f.open(src_fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)); + + if (!_continue) { + error(KIO::ERR_CANNOT_OPEN_FOR_READING, path); + return; + } } #if HAVE_FADVISE @@ -587,14 +609,18 @@ dest = dest_part; if (bPartExists && !(_flags & KIO::Resume)) { // qDebug() << "Deleting partial file" << dest_part; - QFile::remove(dest_part); + if (!QFile::remove(dest_part)) { + execWithElevatedPrivilege(DEL, dest_part); + } // Catch errors when we try to open the file. } } else { dest = dest_orig; if (bOrigExists && !(_flags & KIO::Resume)) { // qDebug() << "Deleting destination file" << dest_orig; - QFile::remove(dest_orig); + if (!QFile::remove(dest_orig)) { + execWithElevatedPrivilege(DEL, dest_orig); + } // Catch errors when we try to open the file. } } @@ -614,6 +640,34 @@ } if (!f.isOpen()) { + int dest_fd = -1; + int oflags = -1; + int filemode = -1; + QFile::OpenMode openMode; + + if (_flags & KIO::Resume) { + oflags = O_RDWR | O_APPEND; + openMode = QIODevice::ReadWrite | QIODevice::Append; + } else { + oflags = O_WRONLY | O_TRUNC | O_CREAT; + filemode = _mode | S_IWUSR | S_IRUSR; + openMode = QIODevice::Truncate | QIODevice::WriteOnly; + } + FdReceiver fdRecv; + bool _continue = ( fdRecv.startListening(QStringLiteral("org_kde_kio_file_helper_socket")) + && execWithElevatedPrivilege(OPEN, dest, oflags, filemode) + && (dest_fd = fdRecv.fileDescriptor()) != -1 + && f.open(dest_fd, openMode, QFileDevice::AutoCloseHandle)); + if (_continue) { + // change ownership as file was created by root. + if (oflags & O_CREAT) { + execWithElevatedPrivilege(CHOWN, dest, getuid(), -1); + } + } + + } + + if (!f.isOpen()) { // qDebug() << "####################### COULD NOT WRITE" << dest << "_mode=" << _mode; // qDebug() << "QFile error==" << f.error() << "(" << f.errorString() << ")"; @@ -650,7 +704,9 @@ if (QT_STAT(QFile::encodeName(dest).constData(), &buff) == 0) { int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE); if (buff.st_size < size) { - QFile::remove(dest); + if (!QFile::remove(dest)) { + execWithElevatedPrivilege(DEL, dest); + } } } } @@ -676,24 +732,30 @@ //QFile::rename() never overwrites the destination file unlike ::remove, //so we must remove it manually first if (_flags & KIO::Overwrite) { - QFile::remove(dest_orig); + if (!QFile::remove(dest_orig)) { + execWithElevatedPrivilege(DEL, dest_orig); + } } if (!QFile::rename(dest, dest_orig)) { - qCWarning(KIO_FILE) << " Couldn't rename " << dest << " to " << dest_orig; - error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig); - return; + if (!execWithElevatedPrivilege(RENAME, dest, dest_orig)) { + qCWarning(KIO_FILE) << " Couldn't rename " << dest << " to " << dest_orig; + error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig); + return; + } } org::kde::KDirNotify::emitFileRenamed(QUrl::fromLocalFile(dest), QUrl::fromLocalFile(dest_orig)); } // set final permissions if (_mode != -1 && !(_flags & KIO::Resume)) { if (!QFile::setPermissions(dest_orig, modeToQFilePermissions(_mode))) { - // 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)); + if (!execWithElevatedPrivilege(CHMOD, dest_orig, _mode)) { + // 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)); + } } } } @@ -718,7 +780,9 @@ struct utimbuf utbuf; utbuf.actime = dest_statbuf.st_atime; utbuf.modtime = dt.toTime_t(); - utime(QFile::encodeName(dest_orig).constData(), &utbuf); + if (utime(QFile::encodeName(dest_orig).constData(), &utbuf) != 0) { + execWithElevatedPrivilege(UTIME, dest_orig, qint64(utbuf.actime), qint64(utbuf.modtime)); + } #endif } } @@ -1330,17 +1394,21 @@ } else { //qDebug() << "QFile::remove" << itemPath; if (!QFile::remove(itemPath)) { - error(KIO::ERR_CANNOT_DELETE, itemPath); - return false; + if (!execWithElevatedPrivilege(DEL, itemPath)) { + error(KIO::ERR_CANNOT_DELETE, itemPath); + return false; + } } } } QDir dir; Q_FOREACH (const QString &itemPath, dirsToDelete) { //qDebug() << "QDir::rmdir" << itemPath; if (!dir.rmdir(itemPath)) { - error(KIO::ERR_CANNOT_DELETE, itemPath); - return false; + if (!execWithElevatedPrivilege(RMDIR, itemPath)) { + error(KIO::ERR_CANNOT_DELETE, itemPath); + return false; + } } } return true; Index: src/ioslaves/file/file_unix.cpp =================================================================== --- src/ioslaves/file/file_unix.cpp +++ src/ioslaves/file/file_unix.cpp @@ -511,16 +511,18 @@ } if (::rename(_src.data(), _dest.data())) { - if ((errno == EACCES) || (errno == EPERM)) { - error(KIO::ERR_ACCESS_DENIED, dest); - } else if (errno == EXDEV) { - error(KIO::ERR_UNSUPPORTED_ACTION, QStringLiteral("rename")); - } else if (errno == EROFS) { // The file is on a read-only filesystem - error(KIO::ERR_CANNOT_DELETE, src); - } else { - error(KIO::ERR_CANNOT_RENAME, src); + if (!execWithElevatedPrivilege(RENAME, _src, _dest)) { + if ((errno == EACCES) || (errno == EPERM)) { + error(KIO::ERR_ACCESS_DENIED, dest); + } else if (errno == EXDEV) { + error(KIO::ERR_UNSUPPORTED_ACTION, QStringLiteral("rename")); + } else if (errno == EROFS) { // The file is on a read-only filesystem + error(KIO::ERR_CANNOT_DELETE, src); + } else { + error(KIO::ERR_CANNOT_RENAME, src); + } + return; } - return; } finished(); @@ -531,30 +533,34 @@ const QString dest = destUrl.toLocalFile(); // Assume dest is local too (wouldn't be here otherwise) if (::symlink(QFile::encodeName(target).constData(), QFile::encodeName(dest).constData()) == -1) { - // Does the destination already exist ? - if (errno == EEXIST) { - if ((flags & KIO::Overwrite)) { - // Try to delete the destination - if (unlink(QFile::encodeName(dest).constData()) != 0) { - error(KIO::ERR_CANNOT_DELETE, dest); + if (!execWithElevatedPrivilege(SYMLINK, dest, target)) { + // Does the destination already exist ? + if (errno == EEXIST) { + if ((flags & KIO::Overwrite)) { + // Try to delete the destination + if (unlink(QFile::encodeName(dest).constData()) != 0) { + if (!execWithElevatedPrivilege(DEL, dest)) { + error(KIO::ERR_CANNOT_DELETE, dest); + return; + } + } + // Try again - this won't loop forever since unlink succeeded + symlink(target, destUrl, flags); return; - } - // Try again - this won't loop forever since unlink succeeded - symlink(target, destUrl, flags); - return; - } else { - QT_STATBUF buff_dest; - if (QT_LSTAT(QFile::encodeName(dest).constData(), &buff_dest) == 0 && ((buff_dest.st_mode & QT_STAT_MASK) == QT_STAT_DIR)) { - error(KIO::ERR_DIR_ALREADY_EXIST, dest); } else { - error(KIO::ERR_FILE_ALREADY_EXIST, dest); + QT_STATBUF buff_dest; + if (QT_LSTAT(QFile::encodeName(dest).constData(), &buff_dest) == 0 && ((buff_dest.st_mode & QT_STAT_MASK) == QT_STAT_DIR)) { + error(KIO::ERR_DIR_ALREADY_EXIST, dest); + } else { + error(KIO::ERR_FILE_ALREADY_EXIST, dest); + } + return; } + } else { + // Some error occurred while we tried to symlink + error(KIO::ERR_CANNOT_SYMLINK, dest); return; } - } else { - // Some error occurred while we tried to symlink - error(KIO::ERR_CANNOT_SYMLINK, dest); - return; } } finished(); @@ -572,14 +578,16 @@ // qDebug() << "Deleting file "<< url; if (unlink(_path.data()) == -1) { - if ((errno == EACCES) || (errno == EPERM)) { - error(KIO::ERR_ACCESS_DENIED, path); - } else if (errno == EISDIR) { - error(KIO::ERR_IS_DIRECTORY, path); - } else { - error(KIO::ERR_CANNOT_DELETE, path); + if (!execWithElevatedPrivilege(DEL, _path)) { + if ((errno == EACCES) || (errno == EPERM)) { + error(KIO::ERR_ACCESS_DENIED, path); + } else if (errno == EISDIR) { + error(KIO::ERR_IS_DIRECTORY, path); + } else { + error(KIO::ERR_CANNOT_DELETE, path); + } + return; } - return; } } else { @@ -593,12 +601,15 @@ return; } } + if (QT_RMDIR(_path.data()) == -1) { - if ((errno == EACCES) || (errno == EPERM)) { - error(KIO::ERR_ACCESS_DENIED, path); - } else { - // qDebug() << "could not rmdir " << perror; - error(KIO::ERR_CANNOT_RMDIR, path); + if (!execWithElevatedPrivilege(RMDIR, _path)) { + if ((errno == EACCES) || (errno == EPERM)) { + error(KIO::ERR_ACCESS_DENIED, path); + } else { + // qDebug() << "could not rmdir " << perror; + error(KIO::ERR_CANNOT_RMDIR, path); + } return; } } @@ -641,16 +652,18 @@ } if (::chown(_path.constData(), uid, gid) == -1) { - switch (errno) { - case EPERM: - case EACCES: - error(KIO::ERR_ACCESS_DENIED, path); - break; - case ENOSPC: - error(KIO::ERR_DISK_FULL, path); - break; - default: - error(KIO::ERR_CANNOT_CHOWN, path); + if (!execWithElevatedPrivilege(CHOWN, _path, uid, gid)) { + switch (errno) { + case EPERM: + case EACCES: + error(KIO::ERR_ACCESS_DENIED, path); + break; + case ENOSPC: + error(KIO::ERR_DISK_FULL, path); + break; + default: + error(KIO::ERR_CANNOT_CHOWN, path); + } } } else { finished();