Warning, file /frameworks/kfilemetadata/src/xattr_p.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 This file is part of the KDE Baloo Project 0003 SPDX-FileCopyrightText: 2014 Raphael Kubo da Costa <rakuco@FreeBSD.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #ifndef KFILEMETADATA_XATTR_P_H 0009 #define KFILEMETADATA_XATTR_P_H 0010 0011 #include <QByteArray> 0012 #include <QFile> 0013 #include <QString> 0014 #include <QDebug> 0015 #include <QFileInfo> 0016 0017 #if defined(Q_OS_LINUX) || defined(__GLIBC__) 0018 #include <sys/types.h> 0019 #include <sys/xattr.h> 0020 0021 #if defined(Q_OS_ANDROID) || defined(Q_OS_LINUX) 0022 // attr/xattr.h is not available in the Android NDK so we are defining ENOATTR ourself 0023 #ifndef ENOATTR 0024 # define ENOATTR ENODATA /* No such attribute */ 0025 #endif 0026 #endif 0027 0028 #include <errno.h> 0029 #elif defined(Q_OS_MAC) 0030 #include <sys/types.h> 0031 #include <sys/xattr.h> 0032 #include <errno.h> 0033 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0034 #include <sys/types.h> 0035 #include <sys/extattr.h> 0036 #include <errno.h> 0037 #elif defined(Q_OS_OPENBSD) 0038 #include <errno.h> 0039 #elif defined(Q_OS_WIN) 0040 #include <windows.h> 0041 #define ssize_t SSIZE_T 0042 #endif 0043 0044 #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0045 inline ssize_t k_getxattr(const QString& path, const QString& name, QString* value) 0046 { 0047 const QByteArray p = QFile::encodeName(path); 0048 const char* encodedPath = p.constData(); 0049 0050 const QByteArray n = name.toUtf8(); 0051 const char* attributeName = n.constData(); 0052 0053 // First get the size of the data we are going to get to reserve the right amount of space. 0054 #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) 0055 const ssize_t size = getxattr(encodedPath, attributeName, nullptr, 0); 0056 #elif defined(Q_OS_MAC) 0057 const ssize_t size = getxattr(encodedPath, attributeName, NULL, 0, 0, 0); 0058 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0059 const ssize_t size = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attributeName, NULL, 0); 0060 #endif 0061 0062 if (!value) { 0063 return size; 0064 } 0065 0066 if (size <= 0) { 0067 value->clear(); 0068 return size; 0069 } 0070 0071 QByteArray data(size, Qt::Uninitialized); 0072 0073 while (true) { 0074 #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr)) 0075 const ssize_t r = getxattr(encodedPath, attributeName, data.data(), data.size()); 0076 #elif defined(Q_OS_MAC) 0077 const ssize_t r = getxattr(encodedPath, attributeName, data.data(), data.size(), 0, 0); 0078 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0079 const ssize_t r = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attributeName, data.data(), data.size()); 0080 #endif 0081 0082 if (r < 0 && errno != ERANGE) { 0083 value->clear(); 0084 return r; 0085 } 0086 0087 if (r >= 0) { 0088 data.resize(r); 0089 *value = QString::fromUtf8(data); 0090 return size; 0091 } else { 0092 // ERANGE 0093 data.resize(data.size() * 2); 0094 } 0095 } 0096 } 0097 0098 inline int k_setxattr(const QString& path, const QString& name, const QString& value) 0099 { 0100 const QByteArray p = QFile::encodeName(path); 0101 const char* encodedPath = p.constData(); 0102 0103 const QByteArray n = name.toUtf8(); 0104 const char* attributeName = n.constData(); 0105 0106 const QByteArray v = value.toUtf8(); 0107 const void* attributeValue = v.constData(); 0108 0109 const size_t valueSize = v.size(); 0110 0111 #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_setxattr)) 0112 return setxattr(encodedPath, attributeName, attributeValue, valueSize, 0); 0113 #elif defined(Q_OS_MAC) 0114 return setxattr(encodedPath, attributeName, attributeValue, valueSize, 0, 0); 0115 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0116 const ssize_t count = extattr_set_file(encodedPath, EXTATTR_NAMESPACE_USER, attributeName, attributeValue, valueSize); 0117 return count == -1 ? -1 : 0; 0118 #endif 0119 } 0120 0121 0122 inline int k_removexattr(const QString& path, const QString& name) 0123 { 0124 const QByteArray p = QFile::encodeName(path); 0125 const char* encodedPath = p.constData(); 0126 0127 const QByteArray n = name.toUtf8(); 0128 const char* attributeName = n.constData(); 0129 0130 #if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_removexattr)) 0131 return removexattr(encodedPath, attributeName); 0132 #elif defined(Q_OS_MAC) 0133 return removexattr(encodedPath, attributeName, XATTR_NOFOLLOW ); 0134 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0135 return extattr_delete_file (encodedPath, EXTATTR_NAMESPACE_USER, attributeName); 0136 #endif 0137 } 0138 0139 inline bool k_hasAttribute(const QString& path, const QString& name) 0140 { 0141 auto ret = k_getxattr(path, name, nullptr); 0142 return (ret >= 0); 0143 } 0144 0145 inline bool k_isSupported(const QString& path) 0146 { 0147 auto ret = k_getxattr(path, QStringLiteral("user.test"), nullptr); 0148 return (ret >= 0) || (errno != ENOTSUP); 0149 } 0150 0151 0152 static KFileMetaData::UserMetaData::Attribute _mapAttribute(const QByteArray& key) 0153 { 0154 using KFileMetaData::UserMetaData; 0155 if (key == "user.xdg.tags") { 0156 return UserMetaData::Attribute::Tags; 0157 } 0158 if (key == "user.baloo.rating") { 0159 return UserMetaData::Attribute::Rating; 0160 } 0161 if (key == "user.xdg.comment") { 0162 return UserMetaData::Attribute::Comment; 0163 } 0164 if (key == "user.xdg.origin.url") { 0165 return UserMetaData::Attribute::OriginUrl; 0166 } 0167 if (key == "user.xdg.origin.email.subject") { 0168 return UserMetaData::Attribute::OriginEmailSubject; 0169 } 0170 if (key == "user.xdg.origin.email.sender") { 0171 return UserMetaData::Attribute::OriginEmailSender; 0172 } 0173 if (key == "user.xdg.origin.email.message-id") { 0174 return UserMetaData::Attribute::OriginEmailMessageId; 0175 } 0176 return UserMetaData::Attribute::Other; 0177 } 0178 0179 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0180 static QList<QByteArray> _split_length_value(QByteArray data) 0181 { 0182 int pos = 0; 0183 QList<QByteArray> entries; 0184 0185 while (pos < data.size()) { 0186 unsigned char len = data[pos]; 0187 if (pos + 1 + len <= data.size()) { 0188 auto value = data.mid(pos + 1, len); 0189 entries.append(value); 0190 } 0191 pos += 1 + len; 0192 } 0193 return entries; 0194 } 0195 #endif 0196 0197 KFileMetaData::UserMetaData::Attributes k_queryAttributes(const QString& path, 0198 KFileMetaData::UserMetaData::Attributes attributes) 0199 { 0200 using KFileMetaData::UserMetaData; 0201 0202 const QByteArray p = QFile::encodeName(path); 0203 const char* encodedPath = p.constData(); 0204 0205 #if defined(Q_OS_LINUX) 0206 const ssize_t size = listxattr(encodedPath, nullptr, 0); 0207 #elif defined(Q_OS_MAC) 0208 const ssize_t size = listxattr(encodedPath, nullptr, 0, 0); 0209 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0210 const ssize_t size = extattr_list_file(encodedPath, EXTATTR_NAMESPACE_USER, nullptr, 0); 0211 #endif 0212 0213 if (size == 0) { 0214 return UserMetaData::Attribute::None; 0215 } 0216 0217 if (size < 0) { 0218 if (errno == E2BIG) { 0219 return UserMetaData::Attribute::All; 0220 } 0221 0222 return UserMetaData::Attribute::None; 0223 } 0224 0225 if (attributes == UserMetaData::Attribute::Any) { 0226 return UserMetaData::Attribute::All; 0227 } 0228 0229 QByteArray data(size, Qt::Uninitialized); 0230 0231 while (true) { 0232 #if defined(Q_OS_LINUX) 0233 const ssize_t r = listxattr(encodedPath, data.data(), data.size()); 0234 #elif defined(Q_OS_MAC) 0235 const ssize_t r = listxattr(encodedPath, data.data(), data.size(), 0); 0236 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0237 const ssize_t r = extattr_list_file(encodedPath, EXTATTR_NAMESPACE_USER, data.data(), data.size()); 0238 #endif 0239 0240 if (r == 0) { 0241 return UserMetaData::Attribute::None; 0242 } 0243 0244 if (r < 0 && errno != ERANGE) { 0245 return UserMetaData::Attribute::None; 0246 } 0247 0248 if (r > 0) { 0249 data.resize(r); 0250 break; 0251 } else { 0252 data.resize(data.size() * 2); 0253 } 0254 } 0255 0256 UserMetaData::Attributes fileAttributes = UserMetaData::Attribute::None; 0257 QByteArray prefix = QByteArray::fromRawData("user.", 5); 0258 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0259 const auto entries = _split_length_value(data); 0260 #else 0261 const auto entries = data.split('\0'); 0262 #endif 0263 for (const auto &entry : entries) { 0264 if (!entry.startsWith(prefix)) { 0265 continue; 0266 } 0267 fileAttributes |= _mapAttribute(entry); 0268 fileAttributes &= attributes; 0269 0270 if (fileAttributes == attributes) { 0271 break; 0272 } 0273 } 0274 0275 return fileAttributes; 0276 } 0277 0278 #elif defined(Q_OS_WIN) 0279 0280 inline ssize_t k_getxattr(const QString& path, const QString& name, QString* value) 0281 { 0282 const QString fullADSName = path + QLatin1Char(':') + name; 0283 HANDLE hFile = ::CreateFileW(reinterpret_cast<const WCHAR*>(fullADSName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, 0284 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 0285 0286 if(!hFile) return 0; 0287 0288 LARGE_INTEGER lsize; 0289 BOOL ret = GetFileSizeEx(hFile, &lsize); 0290 0291 if (ret || lsize.QuadPart > 0x7fffffff || lsize.QuadPart == 0) { 0292 CloseHandle(hFile); 0293 value->clear(); 0294 return lsize.QuadPart == 0 ? 0 : -1; 0295 } 0296 0297 DWORD r = 0; 0298 QByteArray data(lsize.QuadPart, Qt::Uninitialized); 0299 // should we care about attributes longer than 2GiB? - unix xattr are restricted to much lower values 0300 ret = ::ReadFile(hFile, data.data(), data.size(), &r, NULL); 0301 CloseHandle(hFile); 0302 0303 if (ret || r == 0) { 0304 value->clear(); 0305 return r == 0 ? 0 : -1; 0306 } 0307 0308 data.resize(r); 0309 0310 *value = QString::fromUtf8(data); 0311 return r; 0312 } 0313 0314 inline int k_setxattr(const QString& path, const QString& name, const QString& value) 0315 { 0316 const QByteArray v = value.toUtf8(); 0317 0318 const QString fullADSName = path + QLatin1Char(':') + name; 0319 HANDLE hFile = ::CreateFileW(reinterpret_cast<const WCHAR*>(fullADSName.utf16()), GENERIC_WRITE, FILE_SHARE_READ, NULL, 0320 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 0321 0322 if(!hFile) return -1; 0323 0324 DWORD count = 0; 0325 0326 if(!::WriteFile(hFile, v.constData(), v.size(), &count, NULL)) { 0327 DWORD dw = GetLastError(); 0328 TCHAR msg[1024]; 0329 FormatMessage( 0330 FORMAT_MESSAGE_ALLOCATE_BUFFER | 0331 FORMAT_MESSAGE_FROM_SYSTEM | 0332 FORMAT_MESSAGE_IGNORE_INSERTS, 0333 NULL, 0334 dw, 0335 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 0336 (LPTSTR) &msg, 0337 0, NULL ); 0338 qWarning() << "failed to write to ADS:" << msg; 0339 CloseHandle(hFile); 0340 return -1; 0341 } 0342 0343 CloseHandle(hFile); 0344 return count; 0345 } 0346 0347 inline bool k_hasAttribute(const QString& path, const QString& name) 0348 { 0349 // enumerate all streams: 0350 const QString streamName = QStringLiteral(":") + name + QStringLiteral(":$DATA"); 0351 HANDLE hFile = ::CreateFileW(reinterpret_cast<const WCHAR*>(path.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, 0352 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 0353 0354 if(!hFile) { 0355 return false; 0356 } 0357 FILE_STREAM_INFO* fi = new FILE_STREAM_INFO[256]; 0358 if(GetFileInformationByHandleEx(hFile, FileStreamInfo, fi, 256 * sizeof(FILE_STREAM_INFO))) { 0359 if(QString::fromUtf16((ushort*)fi->StreamName, fi->StreamNameLength / sizeof(ushort)) == streamName) { 0360 delete[] fi; 0361 CloseHandle(hFile); 0362 return true; 0363 } 0364 FILE_STREAM_INFO* p = fi; 0365 do { 0366 p = (FILE_STREAM_INFO*) ((char*)p + p->NextEntryOffset); 0367 if(QString::fromUtf16((ushort*)p->StreamName, p->StreamNameLength / sizeof(ushort)) == streamName) { 0368 delete[] fi; 0369 CloseHandle(hFile); 0370 return true; 0371 } 0372 } while(p->NextEntryOffset != NULL); 0373 } 0374 delete[] fi; 0375 CloseHandle(hFile); 0376 return false; 0377 } 0378 0379 inline int k_removexattr(const QString& path, const QString& name) 0380 { 0381 const QString fullADSName = path + QLatin1Char(':') + name; 0382 int ret = (DeleteFileW(reinterpret_cast<const WCHAR*>(fullADSName.utf16()))) ? 0 : -1; 0383 return ret; 0384 } 0385 0386 inline bool k_isSupported(const QString& path) 0387 { 0388 QFileInfo f(path); 0389 const QString drive = QString(f.absolutePath().left(2)) + QStringLiteral("\\"); 0390 WCHAR szFSName[MAX_PATH]; 0391 DWORD dwVolFlags; 0392 ::GetVolumeInformationW(reinterpret_cast<const WCHAR*>(drive.utf16()), NULL, 0, NULL, NULL, &dwVolFlags, szFSName, MAX_PATH); 0393 return ((dwVolFlags & FILE_NAMED_STREAMS) && _wcsicmp(szFSName, L"NTFS") == 0); 0394 } 0395 0396 KFileMetaData::UserMetaData::Attributes k_queryAttributes(const QString& path, 0397 KFileMetaData::UserMetaData::Attributes attributes) 0398 { 0399 using KFileMetaData::UserMetaData; 0400 0401 if (!k_isSupported(path)) { 0402 return UserMetaData::Attribute::None; 0403 } 0404 0405 // TODO - this is mostly a stub, streams should be enumerated, see k_hasAttribute above 0406 if (attributes == UserMetaData::Attribute::Any) { 0407 return UserMetaData::Attribute::All; 0408 } 0409 return attributes; 0410 } 0411 0412 #else 0413 inline ssize_t k_getxattr(const QString&, const QString&, QString*) 0414 { 0415 return 0; 0416 } 0417 0418 inline int k_setxattr(const QString&, const QString&, const QString&) 0419 { 0420 return -1; 0421 } 0422 0423 inline int k_removexattr(const QString&, const QString&) 0424 { 0425 return -1; 0426 } 0427 0428 inline bool k_hasAttribute(const QString&, const QString&) 0429 { 0430 return false; 0431 } 0432 0433 inline bool k_isSupported(const QString&) 0434 { 0435 return false; 0436 } 0437 0438 KFileMetaData::UserMetaData::Attributes k_queryAttributes(const QString&, 0439 KFileMetaData::UserMetaData::Attributes attributes) 0440 { 0441 return KFileMetaData::UserMetaData::Attribute::None; 0442 } 0443 0444 #endif 0445 0446 #endif // KFILEMETADATA_XATTR_P_H