You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdeio/tdeio/tdexattr.cpp

355 lines
8.9 KiB
C++

/*******************************************************************************
Extended attributes support for TDE
Copyright © 2025 Philippe Mavridis <philippe.mavridis@yandex.com>
Based on xattr_p.h from KFileMetaData
Copyright © 2014 Raphael Kubo da Costa <rakuco@FreeBSD.org>
This program or 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
*******************************************************************************/
#include <sys/types.h>
#include <errno.h>
#if defined(Q_OS_LINUX) || defined(__GLIBC__)
# include <sys/xattr.h>
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
# include <sys/extattr.h>
#endif
#include <tqfile.h>
#include "tdexattr.h"
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
# define CHECK_URL(url) \
if (!url.isValid() || url.isEmpty()) return TDEIO::ERR_MALFORMED_URL; \
if (!url.isLocalFile()) return 0; // silently ignore non-local urls
# define GET_ENCODED_PATH(url) \
const TQByteArray p = TQFile::encodeName(url.path()); \
const char* encodedPath = p.data();
#if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
static TQValueList<TQCString> // FIXME untested
splitLengthValue(TQByteArray data)
{
uint pos = 0;
TQValueList<TQCString> entries;
char *s;
while (pos < data.size())
{
uchar len = data[pos];
if (pos + 1 + len <= data.size())
{
strncat(s, &data[pos + 1], len);
entries.append(TQCString(s, len));
}
pos += 1 + len;
}
return entries;
}
#else
static TQValueList<TQCString>
splitZeroSeparator(TQByteArray data)
{
uint pos = 0;
TQValueList<TQCString> entries;
while (pos < data.size())
{
const char *c = &data[pos];
size_t len = strlen(c);
entries.append(TQCString(c));
pos += len + 1;
}
return entries;
}
#endif
uint
TDEXAttr::list(const KURL& url, TQValueList<TQCString> *list)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
#if defined(Q_OS_LINUX)
const ssize_t size = listxattr(encodedPath, nullptr, 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t size = extattr_list_file(encodedPath, EXTATTR_NAMESPACE_USER, nullptr, 0);
#endif
if (size == 0)
{
return 0;
}
else if (size == -1)
{
return parseError(errno, TDEIO::CMD_LISTATTR);
}
else
{
TQByteArray data(size);
for (;;)
{
#if defined(Q_OS_LINUX)
const ssize_t r = listxattr(encodedPath, data.data(), data.size());
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t r = extattr_list_file(encodedPath, EXTATTR_NAMESPACE_USER, data.data(), data.size());
#endif
if (r == 0) {
list->clear();
return 0;
}
if (r == -1 && errno != ERANGE)
{
list->clear();
return parseError(errno, TDEIO::CMD_LISTATTR);
}
if (r > 0)
{
data.resize(r);
break;
}
else // ERANGE
{
data.resize(data.size() * 2);
}
}
#if defined(Q_OS_LINUX)
const TQValueList<TQCString> entries = splitZeroSeparator(data);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const TQValueList<TQCString> entries = splitLengthValue(data);
#endif
list->clear();
TQValueList<TQCString>::const_iterator it;
for (it = entries.begin(); it != entries.end(); ++it)
{
TQCString attribute(*it);
#if defined(Q_OS_LINUX)
if (attribute.left(5) != "user.")
{
continue;
}
#endif
*list << getVisibleAttributeName(attribute);
}
return 0;
}
}
uint
TDEXAttr::read(const KURL& url, const TQCString& attribute, TQCString *value)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
TQCString attr = getLocalAttributeName(attribute);
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr))
const ssize_t size = getxattr(encodedPath, attr, nullptr, 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t size = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attr, NULL, 0);
#endif
if (size == -1)
{
return parseError(errno, TDEIO::CMD_READATTR);
}
else if (size == 0)
{
*value = "";
return 0;
}
else
{
TQByteArray data(size);
for (;;)
{
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr))
const ssize_t r = getxattr(encodedPath, attr, data.data(), data.size());
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t r = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attr, data.data(), data.size());
#endif
if (r == -1 && errno != ERANGE)
{
*value = "";
return parseError(errno, TDEIO::CMD_READATTR);
}
else if (r == 0)
{
*value = "";
return 0;
}
else if (r >= 0)
{
*value = TQCString(data, r + 1);
return 0;
}
else // ERANGE
{
data.resize(data.size() * 2);
}
}
}
}
uint
TDEXAttr::write(const KURL& url, const TQCString& attribute, const TQCString& value)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
TQCString attr = getLocalAttributeName(attribute);
int r;
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_setxattr))
r = setxattr(encodedPath, attr, value.data(), value.size(), 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
r = extattr_set_file(encodedPath, EXTATTR_NAMESPACE_USER, attr, value.data(), value.size());
#endif
return r == -1 ? parseError(errno, TDEIO::CMD_WRITEATTR) : 0;
}
uint
TDEXAttr::remove(const KURL& url, const TQCString& attribute)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
TQCString attr = getLocalAttributeName(attribute);
int r;
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_removexattr))
r = removexattr(encodedPath, attr);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
r = extattr_delete_file (encodedPath, EXTATTR_NAMESPACE_USER, attr);
#else
return TDEIO::ERR_UNSUPPORTED_ACTION;
#endif
return r == -1 ? parseError(errno, TDEIO::CMD_REMOVEATTR) : 0;
}
bool
TDEXAttr::supported(const KURL& url)
{
return read(url, "test", nullptr) != TDEIO::ERR_UNSUPPORTED_ACTION;
}
#else // stubs for unsupported platforms
uint
TDEXAttr::list(const KURL& url, TQValueList<TQCString> *list)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
uint
TDEXAttr::read(const KURL& url, const TQCString& attr, TQCString *value)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
uint
TDEXAttr::write(const KURL& url, const TQCString& attr, const TQCString& value)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
TDEIO::Error
TDEXAttr::remove(const KURL& url, const TQCString& attr)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
bool
TDEXAttr::supported(const KURL& url)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
#endif
uint
TDEXAttr::parseError(int _errno, int command)
{
switch (_errno)
{
case E2BIG:
case ENODATA:
{
return command == TDEIO::CMD_READATTR ?
TDEIO::ERR_COULD_NOT_READ :
TDEIO::ERR_COULD_NOT_WRITE;
}
case ENOTSUP:
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
case ERANGE:
{
return TDEIO::ERR_OUT_OF_MEMORY;
}
case EDQUOT:
case ENOSPC:
{
return TDEIO::ERR_DISK_FULL;
}
case EPERM:
{
return TDEIO::ERR_WRITE_ACCESS_DENIED;
}
default:
{
return TDEIO::ERR_INTERNAL;
}
}
}
TQCString
TDEXAttr::getLocalAttributeName(const TQCString& attr)
{
TQCString a(attr);
#if defined(Q_OS_LINUX)
a = a.prepend("user.");
#endif
return a;
}
TQCString
TDEXAttr::getVisibleAttributeName(const TQCString& attr)
{
TQCString a(attr);
#if defined(Q_OS_LINUX)
a = a.remove(0, 5);
#endif
return a;
}
// kate: replace-tabs true; tab-width 4;