diff --git a/src/xattr_p.h b/src/xattr_p.h index 4e35eab..632a891 100644 --- a/src/xattr_p.h +++ b/src/xattr_p.h @@ -1,244 +1,287 @@ /* * This file is part of the KDE Baloo Project * Copyright (C) 2014 Raphael Kubo da Costa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef KFILEMETADATA_XATTR_P_H #define KFILEMETADATA_XATTR_P_H #include #include #include #include #include #include #if defined(Q_OS_LINUX) || defined(__GLIBC__) #include #include #if defined(Q_OS_ANDROID) || defined(Q_OS_LINUX) // attr/xattr.h is not available in the Android NDK so we are defining ENOATTR ourself #ifndef ENOATTR # define ENOATTR ENODATA /* No such attribute */ #endif #endif #include #elif defined(Q_OS_MAC) #include #include #include #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) #include #include #include #elif defined(Q_OS_WIN) #include #define ssize_t SSIZE_T #endif +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) inline ssize_t k_getxattr(const QString& path, const QString& name, QString* value) { const QByteArray p = QFile::encodeName(path); const char* encodedPath = p.constData(); const QByteArray n = name.toUtf8(); const char* attributeName = n.constData(); // First get the size of the data we are going to get to reserve the right amount of space. #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) const ssize_t size = getxattr(encodedPath, attributeName, nullptr, 0); #elif defined(Q_OS_MAC) const ssize_t size = getxattr(encodedPath, attributeName, NULL, 0, 0, 0); #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) const ssize_t size = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attributeName, NULL, 0); -#elif defined(Q_OS_WIN) - const QString fullADSName = path + QLatin1Char(':') + name; - HANDLE hFile = ::CreateFileW(reinterpret_cast(fullADSName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - - if(!hFile) return 0; - - LARGE_INTEGER lsize; - lsize.LowPart = GetFileSize(hFile, (DWORD*)&lsize.HighPart); - ssize_t size = (qint64)lsize.QuadPart; -#else - const ssize_t size = 0; #endif if (!value) { return size; } if (size <= 0) { value->clear(); return size; } QByteArray data(size, Qt::Uninitialized); #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) const ssize_t r = getxattr(encodedPath, attributeName, data.data(), size); #elif defined(Q_OS_MAC) const ssize_t r = getxattr(encodedPath, attributeName, data.data(), size, 0, 0); #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) const ssize_t r = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attributeName, data.data(), size); -#elif defined(Q_OS_WIN) - ssize_t r = 0; - // should we care about attributes longer than 2GiB? - unix xattr are restricted to much lower values - ::ReadFile(hFile, data.data(), size, (DWORD*)&r, NULL); - CloseHandle(hFile); -#else - const ssize_t r = 0; #endif *value = QString::fromUtf8(data); return r; } inline int k_setxattr(const QString& path, const QString& name, const QString& value) { const QByteArray p = QFile::encodeName(path); const char* encodedPath = p.constData(); const QByteArray n = name.toUtf8(); const char* attributeName = n.constData(); const QByteArray v = value.toUtf8(); const void* attributeValue = v.constData(); const size_t valueSize = v.size(); #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_setxattr)) return setxattr(encodedPath, attributeName, attributeValue, valueSize, 0); #elif defined(Q_OS_MAC) return setxattr(encodedPath, attributeName, attributeValue, valueSize, 0, 0); #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) const ssize_t count = extattr_set_file(encodedPath, EXTATTR_NAMESPACE_USER, attributeName, attributeValue, valueSize); return count == -1 ? -1 : 0; -#elif defined(Q_OS_WIN) +#endif +} + + +inline int k_removexattr(const QString& path, const QString& name) +{ + const QByteArray p = QFile::encodeName(path); + const char* encodedPath = p.constData(); + + const QByteArray n = name.toUtf8(); + const char* attributeName = n.constData(); + + #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_removexattr)) + return removexattr(encodedPath, attributeName); + #elif defined(Q_OS_MAC) + return removexattr(encodedPath, attributeName, XATTR_NOFOLLOW ); + #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + return extattr_delete_file (encodedPath, EXTATTR_NAMESPACE_USER, attributeName); + #endif +} + +inline bool k_hasAttribute(const QString& path, const QString& name) +{ + auto ret = k_getxattr(path, name, nullptr); + return (ret >= 0); +} + +inline bool k_isSupported(const QString& path) +{ + auto ret = k_getxattr(path, QStringLiteral("user.test"), nullptr); + return (ret >= 0) || (errno != ENOTSUP); +} + +#elif defined(Q_OS_WIN) + +inline ssize_t k_getxattr(const QString& path, const QString& name, QString* value) +{ + const QString fullADSName = path + QLatin1Char(':') + name; + HANDLE hFile = ::CreateFileW(reinterpret_cast(fullADSName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + + if(!hFile) return 0; + + LARGE_INTEGER lsize; + lsize.LowPart = GetFileSize(hFile, (DWORD*)&lsize.HighPart); + + if (size <= 0) { + CloseHandle(hFile); + value->clear(); + return size; + } + + ssize_t r = 0; + // should we care about attributes longer than 2GiB? - unix xattr are restricted to much lower values + ::ReadFile(hFile, data.data(), size, (DWORD*)&r, NULL); + CloseHandle(hFile); + + *value = QString::fromUtf8(data); + return r; +} + +inline int k_setxattr(const QString& path, const QString& name, const QString& value) +{ + const QByteArray v = value.toUtf8(); + const QString fullADSName = path + QLatin1Char(':') + name; HANDLE hFile = ::CreateFileW(reinterpret_cast(fullADSName.utf16()), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if(!hFile) return -1; DWORD count = 0; - if(!::WriteFile(hFile, attributeValue, valueSize, &count, NULL)) { + if(!::WriteFile(hFile, v.constData(), v.size(), &count, NULL)) { DWORD dw = GetLastError(); TCHAR msg[1024]; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL ); qWarning() << "failed to write to ADS:" << msg; + CloseHandle(hFile); + return -1; } CloseHandle(hFile); return count; -#else - return -1; -#endif -} - - -inline int k_removexattr(const QString& path, const QString& name) -{ - const QByteArray p = QFile::encodeName(path); - const char* encodedPath = p.constData(); - - const QByteArray n = name.toUtf8(); - const char* attributeName = n.constData(); - - #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_removexattr)) - return removexattr(encodedPath, attributeName); - #elif defined(Q_OS_MAC) - return removexattr(encodedPath, attributeName, XATTR_NOFOLLOW ); - #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) - return extattr_delete_file (encodedPath, EXTATTR_NAMESPACE_USER, attributeName); - #elif defined(Q_OS_WIN) - const QString fullADSName = path + QLatin1Char(':') + name; - int ret = (DeleteFileW(reinterpret_cast(fullADSName.utf16()))) ? 0 : -1; - return ret; - #else - return -1; - #endif - } inline bool k_hasAttribute(const QString& path, const QString& name) { -#if defined(Q_OS_WIN) // enumerate all streams: const QString streamName = QStringLiteral(":") + name + QStringLiteral(":$DATA"); HANDLE hFile = ::CreateFileW(reinterpret_cast(path.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if(!hFile) { return false; } FILE_STREAM_INFO* fi = new FILE_STREAM_INFO[256]; if(GetFileInformationByHandleEx(hFile, FileStreamInfo, fi, 256 * sizeof(FILE_STREAM_INFO))) { if(QString::fromUtf16((ushort*)fi->StreamName, fi->StreamNameLength / sizeof(ushort)) == streamName) { delete[] fi; CloseHandle(hFile); return true; } FILE_STREAM_INFO* p = fi; do { p = (FILE_STREAM_INFO*) ((char*)p + p->NextEntryOffset); if(QString::fromUtf16((ushort*)p->StreamName, p->StreamNameLength / sizeof(ushort)) == streamName) { delete[] fi; CloseHandle(hFile); return true; } } while(p->NextEntryOffset != NULL); } delete[] fi; CloseHandle(hFile); return false; -#else - auto ret = k_getxattr(path, name, nullptr); - return (ret >= 0); -#endif +} + +inline int k_removexattr(const QString& path, const QString& name) +{ + const QString fullADSName = path + QLatin1Char(':') + name; + int ret = (DeleteFileW(reinterpret_cast(fullADSName.utf16()))) ? 0 : -1; + return ret; } inline bool k_isSupported(const QString& path) { -#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) - auto ret = k_getxattr(path, QStringLiteral("user.test"), nullptr); - return (ret >= 0) || (errno != ENOTSUP); -#elif defined(Q_OS_WIN) QFileInfo f(path); const QString drive = QString(f.absolutePath().left(2)) + QStringLiteral("\\"); WCHAR szFSName[MAX_PATH]; DWORD dwVolFlags; ::GetVolumeInformationW(reinterpret_cast(drive.utf16()), NULL, 0, NULL, NULL, &dwVolFlags, szFSName, MAX_PATH); return ((dwVolFlags & FILE_NAMED_STREAMS) && _wcsicmp(szFSName, L"NTFS") == 0); +} + #else +inline ssize_t k_getxattr(const QString&, const QString&, QString*) +{ + return 0; +} + +inline int k_setxattr(const QString&, const QString&, const QString&) +{ + return -1; +} + +inline int k_removexattr(const QString&, const QString&) +{ + return -1; +} + +inline bool k_hasAttribute(const QString&, const QString&) +{ + return false +} + +inline bool k_isSupported(const QString&) +{ return false; -#endif } +#endif + #endif // KFILEMETADATA_XATTR_P_H