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