diff --git a/src/core/config-kiocore.h.cmake b/src/core/config-kiocore.h.cmake --- a/src/core/config-kiocore.h.cmake +++ b/src/core/config-kiocore.h.cmake @@ -4,6 +4,8 @@ #cmakedefine01 HAVE_POSIX_ACL /* Defined if acl/libacl.h exists */ #cmakedefine01 HAVE_ACL_LIBACL_H +/* Defined if system has extended file attributes support. */ +#cmakedefine01 HAVE_SYS_XATTR_H #define CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "${CMAKE_INSTALL_FULL_LIBEXECDIR_KF5}" diff --git a/src/core/copyjob.cpp b/src/core/copyjob.cpp --- a/src/core/copyjob.cpp +++ b/src/core/copyjob.cpp @@ -19,9 +19,18 @@ Boston, MA 02110-1301, USA. */ +#include + #include "copyjob.h" #include "kiocoredebug.h" #include +#if HAVE_SYS_XATTR_H +#if defined(Q_OS_LINUX) || defined(__GLIBC__) || defined(Q_OS_MAC) +#include +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) +#include +#endif +#endif #include "kcoredirlister.h" #include "kfileitem.h" #include "job.h" // buildErrorString @@ -234,6 +243,7 @@ void slotResultRenaming(KJob *job); void slotResultSettingDirAttributes(KJob *job); void setNextDirAttribute(); + void copyXattrs(const QUrl &source, const QUrl &dest); void startRenameJob(const QUrl &slave_url); bool shouldOverwriteDir(const QString &path) const; @@ -1115,6 +1125,7 @@ } } else { // no error : remove from list, to move on to next dir //this is required for the undo feature + copyXattrs((*it).uSource, (*it).uDest); emit q->copyingDone(q, (*it).uSource, finalDestUrl((*it).uSource, (*it).uDest), (*it).mtime, true, false); m_directoriesCopied.append(*it); dirs.erase(it); @@ -1358,6 +1369,7 @@ emit q->copyingLinkDone(q, (*it).uSource, target, finalUrl); } else { //required for the undo feature + copyXattrs((*it).uSource, (*it).uDest); emit q->copyingDone(q, (*it).uSource, finalUrl, (*it).mtime, false, false); if (m_mode == CopyJob::Move) { org::kde::KDirNotify::emitFileMoved((*it).uSource, finalUrl); @@ -2173,6 +2185,82 @@ } } +// copy xattr to new dir +void CopyJobPrivate::copyXattrs(const QUrl &source, const QUrl &dest) +{ +#if !(HAVE_SYS_XATTR_H) + return; +#endif + long listlen, valuelen, destlen; + const QByteArray sourcearray = source.path().toLocal8Bit().data(); + const char *xattrsrc = sourcearray.data(); + // get size of key list +#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) + listlen = listxattr(xattrsrc, nullptr, 0); +#elif defined(Q_OS_MAC) + listlen = listxattr(xattrsrc, NULL, 0, 0); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + listlen = extattr_list_file(xattr_src, EXTATTR_NAMESPACE_USER, NULL, 0); +#endif + if (listlen < 0) { + qCWarning(KIO_COPYJOB_DEBUG) << "Glibc failed to extract xattr."; + } else if (listlen == 0) { + qCDebug(KIO_COPYJOB_DEBUG) << "File don't have any xattr."; + } else { + QByteArray keylist(listlen, Qt::Uninitialized); + // get the key list +#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) + listlen = listxattr(xattrsrc, keylist.data(), listlen); +#elif defined(Q_OS_MAC) + listlen = listxattr(xattrsrc, keylist.data(), listlen, 0); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + listlen = extattr_list_file(xattrsrc, EXTATTR_NAMESPACE_USER, keylist.data(), listlen); +#endif + qCWarning(KIO_COPYJOB_DEBUG) << listlen << xattrsrc << keylist.data(); + QList xattrkeys = keylist.split('\0'); + xattrkeys.removeLast(); // the last item is alwys empty + for (const auto & xattrkey : xattrkeys) { + // get the size of value for key +#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) + valuelen = getxattr(xattrsrc, xattrkey.data(), nullptr, 0); +#elif defined(Q_OS_MAC) + valuelen = listxattr(xattrsrc, xattrkey.data(), NULL, 0, 0, 0); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + valuelen = extattr_get_file(xattrsrc, EXTATTR_NAMESPACE_USER, xattrkey.data(), NULL, 0); +#endif + if (valuelen < 1) { + qCWarning(KIO_COPYJOB_DEBUG) << "Glibc failed to extract value for a xattr key."; + continue; + } + if (valuelen == 0) { + qCDebug(KIO_COPYJOB_DEBUG) << "empty xattr key."; // They are legal, but... Baloo don't use it + } + QByteArray xattrval(valuelen + 1, Qt::Uninitialized); + //get the value of the key +#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) + valuelen = getxattr(xattrsrc, xattrkey.data(), xattrval.data(), valuelen); +#elif defined(Q_OS_MAC) + vallen = getxattr(xattr_src, xattrkey.data(), xattrkey.data(), xattrval.data(), valuelen, 0, 0); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + vallen = extattr_get_file(xattr_src, EXTATTR_NAMESPACE_USER, xattrkey.data(), xattrval.data(), valuelen); +#endif + //write key:value pair on dest file + const QByteArray destarray = source.path().toLocal8Bit().data(); + const char *xattrdest = sourcearray.data(); +#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) + destlen = setxattr(xattrdest, xattrkey.data(), xattrval.data(), valuelen, 0); +#elif defined(Q_OS_MAC) + destlen = setxattr(xattrdest, xattrkey.data(), xattrval.data(), valuelen, 0, 0); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + destlen = extattr_set_file(xattrdest, EXTATTR_NAMESPACE_USER, xattr_key.data(), xattrval.data(), valuelen); +#endif + if (destlen < 0) { + qCWarning(KIO_COPYJOB_DEBUG) << "Glibc failed to write xattr on a file."; + } + } + } +} + void KIO::CopyJob::setDefaultPermissions(bool b) { d_func()->m_defaultPermissions = b; diff --git a/src/ioslaves/file/file_unix.cpp b/src/ioslaves/file/file_unix.cpp --- a/src/ioslaves/file/file_unix.cpp +++ b/src/ioslaves/file/file_unix.cpp @@ -39,7 +39,11 @@ #include #if HAVE_SYS_XATTR_H +#if defined(Q_OS_LINUX) || defined(__GLIBC__) || defined(Q_OS_MAC) #include +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) +#include +#endif #endif #include