mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00

if the slaves do not know what URL is being stat()-ed, listed, etc. then what? this may make the URL different than the one originally requested (as it should be) in case of redirection(s) for example Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
773 lines
24 KiB
C++
773 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 QString &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);
|
|
entry.insert(KIO::UDSEntry::UDS_URL, path);
|
|
|
|
mode_t type;
|
|
mode_t access;
|
|
KDE_struct_stat buff;
|
|
|
|
const QByteArray _path(QFile::encodeName(path));
|
|
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"
|