Add basic extended attributes support
This commit adds extended attributes support to TDEIO, tdeio_file and a read-write plugin for the file properties dialog. Signed-off-by: Philippe Mavridis <philippe.mavridis@yandex.com>feat/extended-attributes
parent
2e76346c68
commit
c17df85bda
@ -0,0 +1,355 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
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;
|
@ -0,0 +1,96 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
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
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef __TDEXATTR_H
|
||||||
|
#define __TDEXATTR_H
|
||||||
|
|
||||||
|
#include <kurl.h>
|
||||||
|
#include <tdeio/global.h>
|
||||||
|
#include <tqvaluelist.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended attributes support class for TDE.
|
||||||
|
*
|
||||||
|
* This class implements extended attributes support for TDE. Static methods are
|
||||||
|
* provided which can list, read and write user-defined extended attributes of
|
||||||
|
* local files.
|
||||||
|
*
|
||||||
|
* @author Philippe Mavridis <philippe.mavridis@yandex.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TDEIO_EXPORT TDEXAttr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* List all the extended attributes of a file.
|
||||||
|
* @param url the URL of the file
|
||||||
|
* @param list pointer to a TQCString list where the list of attributes will be stored
|
||||||
|
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
|
||||||
|
*/
|
||||||
|
static uint list(const KURL& url, TQValueList<TQCString> *list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the value of an extended attribute.
|
||||||
|
* @param url the URL of the file
|
||||||
|
* @param attr attribute name
|
||||||
|
* @param value pointer to TQCString where the value of the attribute @p attr will be stored
|
||||||
|
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
|
||||||
|
*/
|
||||||
|
static uint read(const KURL& url, const TQCString& attribute, TQCString *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify the value of an extended attribute.
|
||||||
|
*
|
||||||
|
* @param url the URL of the file
|
||||||
|
* @param attr attribute name
|
||||||
|
* @param value new value of the attribute @p attr
|
||||||
|
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
|
||||||
|
*/
|
||||||
|
static uint write(const KURL& url, const TQCString& attribute, const TQCString& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an extended attribute.
|
||||||
|
*
|
||||||
|
* @param url the URL of the file
|
||||||
|
* @param attr attribute name
|
||||||
|
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
|
||||||
|
*/
|
||||||
|
static uint remove(const KURL& url, const TQCString& attribute);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if extended attributes are supported for the given URL.
|
||||||
|
*
|
||||||
|
* @param url the URL of the file
|
||||||
|
* @return true if extended attributes are supported, false otherwise
|
||||||
|
*/
|
||||||
|
static bool supported(const KURL& url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TQCString getLocalAttributeName(const TQCString& attr);
|
||||||
|
static TQCString getVisibleAttributeName(const TQCString& attr);
|
||||||
|
static uint parseError(int _errno, int command);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __TDEXATTR_H
|
||||||
|
// kate: replace-tabs true; tab-width 2;
|
Loading…
Reference in New Issue