mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-24 10:52:49 +00:00

KRun already had such optimization in place for example, that makes the file slave kinda special tho as it will determine MIME type only from mode but the goal is to not even spin a job and a slave for local files Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
771 lines
24 KiB
C++
771 lines
24 KiB
C++
/*
|
|
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>
|
|
Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
|
|
Copyright (C) 2007 Thiago Macieira <thiago@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.
|
|
*/
|
|
|
|
#define QT_NO_CAST_FROM_ASCII
|
|
|
|
#include "file.h"
|
|
|
|
#include <config.h>
|
|
#include <config-acl.h>
|
|
#include <config-kioslave-file.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <utime.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_POSIX_ACL
|
|
# include <sys/acl.h>
|
|
# include <acl/libacl.h>
|
|
#endif
|
|
|
|
#include <QByteArray>
|
|
#include <QDateTime>
|
|
#include <QCoreApplication>
|
|
#include <QRegExp>
|
|
#include <QFile>
|
|
#include <QDirIterator>
|
|
|
|
#include <kdebug.h>
|
|
#include <kurl.h>
|
|
#include <kcomponentdata.h>
|
|
#include <kconfig.h>
|
|
#include <kconfiggroup.h>
|
|
#include <ktemporaryfile.h>
|
|
#include <klocale.h>
|
|
#include <limits.h>
|
|
#include <kshell.h>
|
|
#include <kmountpoint.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kdirnotify.h>
|
|
#include <kio/ioslave_defaults.h>
|
|
#include <kde_file.h>
|
|
#include <kglobal.h>
|
|
#include <kuser.h>
|
|
|
|
using namespace KIO;
|
|
|
|
#define MAX_IPC_SIZE (1024*32)
|
|
|
|
#ifdef HAVE_POSIX_ACL
|
|
static void appendACLAtoms(const QByteArray & path, UDSEntry& entry, mode_t type);
|
|
#endif
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
QCoreApplication app(argc, argv); // needed for QSocketNotifier
|
|
KComponentData componentData("kio_file", "kdelibs4");
|
|
(void) KGlobal::locale();
|
|
|
|
kDebug(7101) << "Starting" << getpid();
|
|
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: kio_file app-socket\n");
|
|
exit(-1);
|
|
}
|
|
|
|
FileProtocol slave(argv[1]);
|
|
slave.dispatchLoop();
|
|
|
|
kDebug(7101) << "Done";
|
|
return 0;
|
|
}
|
|
|
|
FileProtocol::FileProtocol(const QByteArray &app)
|
|
: SlaveBase("file", app)
|
|
{
|
|
}
|
|
|
|
FileProtocol::~FileProtocol()
|
|
{
|
|
}
|
|
|
|
#ifdef HAVE_POSIX_ACL
|
|
static QString aclToText(acl_t acl)
|
|
{
|
|
ssize_t size = 0;
|
|
char* txt = acl_to_text(acl, &size);
|
|
const QString ret = QString::fromLatin1(txt, size);
|
|
acl_free(txt);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int FileProtocol::setACL(const char *path, mode_t perm, bool directoryDefault)
|
|
{
|
|
int ret = 0;
|
|
#ifdef HAVE_POSIX_ACL
|
|
|
|
const QString ACLString = metaData(QLatin1String("ACL_STRING"));
|
|
const QString defaultACLString = metaData(QLatin1String("DEFAULT_ACL_STRING"));
|
|
// Empty strings mean leave as is
|
|
if ( !ACLString.isEmpty() ) {
|
|
acl_t acl = 0;
|
|
if (ACLString == QLatin1String("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.toLatin1());
|
|
if (acl_valid(acl) == 0) { // let's be safe
|
|
ret = acl_set_file(path, ACL_TYPE_ACCESS, acl);
|
|
kDebug(7101) << "Set ACL on:" << path << "to:" << aclToText(acl);
|
|
}
|
|
acl_free(acl);
|
|
if (ret != 0) {
|
|
return ret; // better stop trying right away
|
|
}
|
|
}
|
|
|
|
if (directoryDefault && !defaultACLString.isEmpty()) {
|
|
if (defaultACLString == QLatin1String("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.toLatin1());
|
|
if (acl_valid(acl) == 0) { // let's be safe
|
|
ret += acl_set_file(path, ACL_TYPE_DEFAULT, acl);
|
|
kDebug(7101) << "Set Default ACL on:" << path << "to:" << aclToText(acl);
|
|
}
|
|
acl_free(acl);
|
|
}
|
|
}
|
|
#else
|
|
Q_UNUSED(path);
|
|
Q_UNUSED(perm);
|
|
Q_UNUSED(directoryDefault);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void FileProtocol::chmod(const KUrl& url, int permissions)
|
|
{
|
|
const QString path(url.toLocalFile());
|
|
const QByteArray _path(QFile::encodeName(path));
|
|
/* FIXME: Should be atomic */
|
|
if (KDE::chmod(path, 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(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;
|
|
#endif
|
|
case ENOSPC:
|
|
error(KIO::ERR_DISK_FULL, path);
|
|
break;
|
|
default:
|
|
error(KIO::ERR_CANNOT_CHMOD, path);
|
|
}
|
|
} else {
|
|
finished();
|
|
}
|
|
}
|
|
|
|
void FileProtocol::setModificationTime(const KUrl &url, const QDateTime &mtime)
|
|
{
|
|
const QString path(url.toLocalFile());
|
|
KDE_struct_stat statbuf;
|
|
if (KDE::lstat(path, &statbuf) == 0) {
|
|
struct utimbuf utbuf;
|
|
utbuf.actime = statbuf.st_atime; // access time, unchanged
|
|
utbuf.modtime = mtime.toTime_t(); // modification time
|
|
if (KDE::utime(path, &utbuf) != 0) {
|
|
// TODO: errno could be EACCES, EPERM, EROFS
|
|
error(KIO::ERR_CANNOT_SETTIME, path);
|
|
} else {
|
|
finished();
|
|
}
|
|
} else {
|
|
error(KIO::ERR_DOES_NOT_EXIST, path);
|
|
}
|
|
}
|
|
|
|
void FileProtocol::mkdir(const KUrl &url, int permissions)
|
|
{
|
|
const QString path(url.toLocalFile());
|
|
|
|
kDebug(7101) << path << "permission=" << permissions;
|
|
|
|
// Remove existing file or symlink, if requested (#151851)
|
|
if (metaData(QLatin1String("overwrite")) == QLatin1String("true")) {
|
|
QFile::remove(path);
|
|
}
|
|
|
|
KDE_struct_stat buff;
|
|
if (KDE::lstat(path, &buff) == -1) {
|
|
if (KDE::mkdir(path, 0777 /*umask will be applied*/) != 0) {
|
|
if (errno == EACCES) {
|
|
error(KIO::ERR_ACCESS_DENIED, path);
|
|
return;
|
|
} else if (errno == ENOSPC) {
|
|
error(KIO::ERR_DISK_FULL, path);
|
|
return;
|
|
} else {
|
|
error(KIO::ERR_COULD_NOT_MKDIR, path);
|
|
return;
|
|
}
|
|
} else {
|
|
if (permissions != -1) {
|
|
chmod(url, permissions);
|
|
} else {
|
|
finished();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (S_ISDIR(buff.st_mode)) {
|
|
kDebug(7101) << "ERR_DIR_ALREADY_EXIST";
|
|
error(KIO::ERR_DIR_ALREADY_EXIST, path);
|
|
return;
|
|
}
|
|
error(KIO::ERR_FILE_ALREADY_EXIST, path);
|
|
return;
|
|
}
|
|
|
|
void FileProtocol::get(const KUrl &url)
|
|
{
|
|
if (!url.isLocalFile()) {
|
|
KUrl redir(url);
|
|
redir.setScheme(config()->readEntry("DefaultRemoteProtocol", "sftp"));
|
|
redirection(redir);
|
|
finished();
|
|
return;
|
|
}
|
|
|
|
const QString path(url.toLocalFile());
|
|
KDE_struct_stat buff;
|
|
if (KDE::stat(path, &buff) == -1 ) {
|
|
if (errno == EACCES) {
|
|
error(KIO::ERR_ACCESS_DENIED, path);
|
|
} else {
|
|
error(KIO::ERR_DOES_NOT_EXIST, path);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (S_ISDIR(buff.st_mode)) {
|
|
error(KIO::ERR_IS_DIRECTORY, path);
|
|
return;
|
|
}
|
|
if (!S_ISREG(buff.st_mode)) {
|
|
error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
|
|
return;
|
|
}
|
|
|
|
int fd = KDE::open(path, O_RDONLY);
|
|
if (fd < 0) {
|
|
error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
|
|
return;
|
|
}
|
|
|
|
#if HAVE_FADVISE
|
|
posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
|
|
#endif
|
|
|
|
totalSize(buff.st_size);
|
|
|
|
KIO::filesize_t processed_size = 0;
|
|
|
|
const QString resumeOffset = metaData(QLatin1String("resume"));
|
|
if (!resumeOffset.isEmpty()) {
|
|
bool ok = false;
|
|
KIO::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;
|
|
kDebug(7101) << "Resume offset:" << KIO::number(offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
char buffer[MAX_IPC_SIZE];
|
|
while (true) {
|
|
int n = ::read(fd, buffer, MAX_IPC_SIZE);
|
|
if (n == -1) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
}
|
|
error(KIO::ERR_COULD_NOT_READ, path);
|
|
::close(fd);
|
|
return;
|
|
}
|
|
if (n == 0) {
|
|
break; // Finished
|
|
}
|
|
|
|
data(QByteArray::fromRawData(buffer, n));
|
|
|
|
processed_size += n;
|
|
processedSize(processed_size);
|
|
|
|
// kDebug(7101) << "Processed: " << KIO::number(processed_size);
|
|
}
|
|
|
|
data(QByteArray());
|
|
|
|
::close(fd);
|
|
|
|
processedSize(buff.st_size);
|
|
finished();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 use this partial file for resuming, tell about the size and the app will tell
|
|
// 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 = -1;
|
|
QString dest;
|
|
int fd = -1;
|
|
|
|
// Loop until 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
|
|
if (fd != -1) {
|
|
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) {
|
|
QFile::remove(dest);
|
|
}
|
|
}
|
|
|
|
if (fd != -1) {
|
|
return;
|
|
}
|
|
// falltrough when nothing was opened
|
|
}
|
|
|
|
if (fd == -1) {
|
|
// got nothing to write out, so never opened the file
|
|
finished();
|
|
return;
|
|
}
|
|
|
|
if (::close(fd) == -1) {
|
|
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 );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// job done
|
|
finished();
|
|
}
|
|
|
|
QString FileProtocol::getUserName(uid_t uid) const
|
|
{
|
|
if (!mUsercache.contains(uid)) {
|
|
const KUser kuser(uid);
|
|
if (!kuser.isValid()) {
|
|
return QString::number(uid);
|
|
}
|
|
mUsercache.insert(uid, kuser.loginName());
|
|
}
|
|
return mUsercache[uid];
|
|
}
|
|
|
|
QString FileProtocol::getGroupName(gid_t gid) const
|
|
{
|
|
if (!mGroupcache.contains(gid)) {
|
|
const KUserGroup kusergroup(gid);
|
|
if (!kusergroup.isValid()) {
|
|
return QString::number(gid);
|
|
}
|
|
mGroupcache.insert(gid, kusergroup.name());
|
|
}
|
|
return mGroupcache[gid];
|
|
}
|
|
|
|
bool FileProtocol::createUDSEntry(const QString &filename, const QByteArray &path, UDSEntry &entry,
|
|
short int details)
|
|
{
|
|
assert(entry.count() == 0); // by contract :-)
|
|
// entry.reserve( 8 ); // speed up QHash insertion
|
|
|
|
entry.insert(KIO::UDSEntry::UDS_NAME, filename);
|
|
|
|
mode_t type;
|
|
mode_t access;
|
|
KDE_struct_stat buff;
|
|
|
|
if (KDE_lstat(path.data(), &buff) == 0) {
|
|
if (details > 2) {
|
|
entry.insert(KIO::UDSEntry::UDS_DEVICE_ID, buff.st_dev);
|
|
entry.insert(KIO::UDSEntry::UDS_INODE, buff.st_ino);
|
|
}
|
|
|
|
if (S_ISLNK(buff.st_mode)) {
|
|
char buffer2[1000];
|
|
::memset(buffer2, 0, 1000 * sizeof(char));
|
|
readlink(path.data(), buffer2, 999);
|
|
entry.insert(KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName(buffer2));
|
|
|
|
// 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;
|
|
|
|
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, type);
|
|
entry.insert(KIO::UDSEntry::UDS_ACCESS, access);
|
|
entry.insert(KIO::UDSEntry::UDS_SIZE, 0LL);
|
|
goto notype;
|
|
|
|
}
|
|
}
|
|
} else {
|
|
// kWarning() << "lstat didn't work on " << path.data();
|
|
return false;
|
|
}
|
|
|
|
type = buff.st_mode & S_IFMT; // extract file type
|
|
access = buff.st_mode & 07777; // extract permissions
|
|
|
|
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, type);
|
|
entry.insert(KIO::UDSEntry::UDS_ACCESS, access);
|
|
|
|
entry.insert(KIO::UDSEntry::UDS_SIZE, buff.st_size);
|
|
|
|
#ifdef HAVE_POSIX_ACL
|
|
if (details > 0) {
|
|
/* Append an atom indicating whether the file has extended acl information. If it's a
|
|
* directory and it has a default ACL, also append that. */
|
|
appendACLAtoms(path, entry, type);
|
|
}
|
|
#endif
|
|
|
|
notype:
|
|
if (details > 1) {
|
|
// In real "remote" slaves, this usually depends on the protocol but not here - it is
|
|
// determined only from the mode and only for non-regular files
|
|
if (S_ISDIR(type)) {
|
|
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory"));
|
|
} else if (S_ISCHR(type)) {
|
|
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/chardevice"));
|
|
} else if (S_ISBLK(type)) {
|
|
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/blockdevice"));
|
|
} else if (S_ISFIFO(type)) {
|
|
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/fifo"));
|
|
} else if (S_ISSOCK(type)) {
|
|
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/socket"));
|
|
}
|
|
}
|
|
|
|
if (details > 0) {
|
|
entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime);
|
|
entry.insert(KIO::UDSEntry::UDS_USER, getUserName(buff.st_uid));
|
|
entry.insert(KIO::UDSEntry::UDS_GROUP, getGroupName(buff.st_gid));
|
|
entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
/*************************************
|
|
*
|
|
* ACL handling helpers
|
|
*
|
|
*************************************/
|
|
#ifdef HAVE_POSIX_ACL
|
|
static void appendACLAtoms(const QByteArray &path, UDSEntry &entry, mode_t type)
|
|
{
|
|
// first check for a noop
|
|
if (acl_extended_file(path.data()) == 0) {
|
|
return;
|
|
}
|
|
|
|
acl_t acl = 0;
|
|
acl_t defaultAcl = 0;
|
|
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?
|
|
acl = acl_get_file(path.data(), ACL_TYPE_ACCESS);
|
|
/* 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) {
|
|
if (acl) {
|
|
if (acl_equiv_mode(acl, 0) == 0) {
|
|
acl_free(acl);
|
|
acl = 0;
|
|
}
|
|
}
|
|
defaultAcl = acl_get_file(path.data(), ACL_TYPE_DEFAULT);
|
|
}
|
|
if (acl || defaultAcl) {
|
|
kDebug(7101) << path.constData() << "has extended ACL entries";
|
|
entry.insert(KIO::UDSEntry::UDS_EXTENDED_ACL, 1);
|
|
}
|
|
if (acl) {
|
|
const QString str = aclToText(acl);
|
|
entry.insert(KIO::UDSEntry::UDS_ACL_STRING, str);
|
|
kDebug(7101) << path.constData() << "ACL:" << str;
|
|
}
|
|
if (defaultAcl ) {
|
|
const QString str = aclToText(defaultAcl);
|
|
entry.insert(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, str);
|
|
kDebug(7101) << path.constData() << "DEFAULT ACL:" << str;
|
|
}
|
|
if (acl) {
|
|
acl_free(acl);
|
|
}
|
|
if (defaultAcl) {
|
|
acl_free(defaultAcl);
|
|
}
|
|
}
|
|
#endif // HAVE_POSIX_ACL
|
|
|
|
// We could port this to KTempDir::removeDir but then we wouldn't be able to tell the user
|
|
// where exactly the deletion failed, in case of errors.
|
|
bool FileProtocol::deleteRecursive(const QString &path)
|
|
{
|
|
//kDebug() << path;
|
|
QDirIterator it(
|
|
path,
|
|
QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden,
|
|
QDirIterator::Subdirectories
|
|
);
|
|
QStringList dirsToDelete;
|
|
while (it.hasNext()) {
|
|
const QString itemPath = it.next();
|
|
//kDebug() << "itemPath=" << itemPath;
|
|
const QFileInfo info = it.fileInfo();
|
|
if (info.isDir() && !info.isSymLink()) {
|
|
dirsToDelete.prepend(itemPath);
|
|
} else {
|
|
//kDebug() << "QFile::remove" << itemPath;
|
|
if (!QFile::remove(itemPath)) {
|
|
error(KIO::ERR_CANNOT_DELETE, itemPath);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
QDir dir;
|
|
Q_FOREACH(const QString &itemPath, dirsToDelete) {
|
|
//kDebug() << "QDir::rmdir" << itemPath;
|
|
if (!dir.rmdir(itemPath)) {
|
|
error(KIO::ERR_CANNOT_DELETE, itemPath);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#include "moc_file.cpp"
|