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
a6ea4bf998
@ -0,0 +1,379 @@
|
||||
/*******************************************************************************
|
||||
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 <kdebug.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) // FIXME untested
|
||||
static TQValueList<TQCString>
|
||||
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;
|
||||
}
|
||||
|
||||
#define GET_EXTATTR_NS(ns) \
|
||||
int extattrNS = \
|
||||
ns == "user" ? EXTATTR_NAMESPACE_USER : \
|
||||
ns == "system" ? EXTATTR_NAMESPACE_SYSTEM : -1; \
|
||||
if (extattrNS == -1) return TDEIO::ERR_UNSUPPORTED_ACTION
|
||||
|
||||
#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, const TQCString &ns, 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)
|
||||
GET_EXTATTR_NS(ns);
|
||||
const ssize_t size = extattr_list_file(encodedPath, extattrNS, nullptr, 0);
|
||||
#endif
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (size == -1)
|
||||
{
|
||||
kdWarning() << "(" << errno << ") error while listing attributes of "
|
||||
<< url << endl;
|
||||
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)
|
||||
GET_EXTATTR_NS(ns);
|
||||
const ssize_t r = extattr_list_file(encodedPath, extattrNS, data.data(), data.size());
|
||||
#endif
|
||||
|
||||
if (r == 0) {
|
||||
list->clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r == -1 && errno != ERANGE)
|
||||
{
|
||||
kdWarning() << "(" << errno << ") error while listing attributes of "
|
||||
<< url << endl;
|
||||
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);
|
||||
TQCString nsPrefix = ns + ".";
|
||||
#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(nsPrefix.length()) != nsPrefix)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
*list << attribute;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint
|
||||
TDEXAttr::read(const KURL& url, const TQCString &ns, const TQCString& attribute, TQCString *value)
|
||||
{
|
||||
CHECK_URL(url);
|
||||
GET_ENCODED_PATH(url);
|
||||
|
||||
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr))
|
||||
const ssize_t size = getxattr(encodedPath, ns + "." + attribute, nullptr, 0);
|
||||
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
|
||||
GET_EXTATTR_NS(ns);
|
||||
const ssize_t size = extattr_get_file(encodedPath, extattrNS, attribute, NULL, 0);
|
||||
#endif
|
||||
|
||||
if (size == -1)
|
||||
{
|
||||
kdWarning() << "(" << errno << ") error while reading attribute "
|
||||
<< ns << "." << attribute << " of " << url << endl;
|
||||
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, ns + "." + attribute,
|
||||
data.data(), data.size());
|
||||
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
|
||||
GET_EXTATTR_NS(ns);
|
||||
const ssize_t r = extattr_get_file(encodedPath, extattrNS, attribute,
|
||||
data.data(), data.size());
|
||||
#endif
|
||||
|
||||
if (r == -1 && errno != ERANGE)
|
||||
{
|
||||
if (errno != ENODATA)
|
||||
{
|
||||
kdWarning() << "(" << errno << ") error while getting value of attribute "
|
||||
<< ns << "." << attribute << " of " << url << endl;
|
||||
}
|
||||
*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 &ns, const TQCString& attribute, const TQCString& value)
|
||||
{
|
||||
CHECK_URL(url);
|
||||
GET_ENCODED_PATH(url);
|
||||
|
||||
int r;
|
||||
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_setxattr))
|
||||
r = setxattr(encodedPath, ns + "." + attribute, value.data(), value.size(), 0);
|
||||
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
|
||||
GET_EXTATTR_NS(ns);
|
||||
r = extattr_set_file(encodedPath, extattrNS, attribute, value.data(), value.size());
|
||||
#endif
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
kdWarning() << "(" << errno << ") error while writing attribute "
|
||||
<< ns << "." << attribute << " of " << url << endl;
|
||||
}
|
||||
return r == -1 ? parseError(errno, TDEIO::CMD_WRITEATTR) : 0;
|
||||
}
|
||||
|
||||
uint
|
||||
TDEXAttr::remove(const KURL& url, const TQCString &ns, const TQCString& attribute)
|
||||
{
|
||||
CHECK_URL(url);
|
||||
GET_ENCODED_PATH(url);
|
||||
|
||||
int r;
|
||||
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_removexattr))
|
||||
r = removexattr(encodedPath, ns + "." + attribute);
|
||||
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
|
||||
GET_EXTATTR_NS(ns);
|
||||
r = extattr_delete_file (encodedPath, extattrNS, attribute);
|
||||
#else
|
||||
return TDEIO::ERR_UNSUPPORTED_ACTION;
|
||||
#endif
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
kdWarning() << "(" << errno << ") error while removing attribute "
|
||||
<< ns << "." << attribute << " of " << url << endl;
|
||||
}
|
||||
|
||||
return r == -1 ? parseError(errno, TDEIO::CMD_REMOVEATTR) : 0;
|
||||
}
|
||||
|
||||
bool
|
||||
TDEXAttr::readSupported(const KURL& url, const TQCString &ns)
|
||||
{
|
||||
return read(url, ns, "x-tde-test", nullptr) != TDEIO::ERR_UNSUPPORTED_ACTION;
|
||||
}
|
||||
|
||||
bool
|
||||
TDEXAttr::writeSupported(const KURL& url, const TQCString &ns)
|
||||
{
|
||||
return write(url, ns, "x-tde-test", nullptr) != TDEIO::ERR_UNSUPPORTED_ACTION
|
||||
&& remove(url, ns, "x-tde-test") != TDEIO::ERR_UNSUPPORTED_ACTION;
|
||||
}
|
||||
|
||||
#else // stubs for unsupported platforms
|
||||
|
||||
uint
|
||||
TDEXAttr::list(const KURL& url, const TQCString &ns, TQValueList<TQCString> *list)
|
||||
{
|
||||
return TDEIO::ERR_UNSUPPORTED_ACTION;
|
||||
}
|
||||
|
||||
uint
|
||||
TDEXAttr::read(const KURL& url, const TQCString &ns, const TQCString& attr, TQCString *value)
|
||||
{
|
||||
return TDEIO::ERR_UNSUPPORTED_ACTION;
|
||||
}
|
||||
|
||||
uint
|
||||
TDEXAttr::write(const KURL& url, const TQCString &ns, const TQCString& attr, const TQCString& value)
|
||||
{
|
||||
return TDEIO::ERR_UNSUPPORTED_ACTION;
|
||||
}
|
||||
|
||||
TDEIO::Error
|
||||
TDEXAttr::remove(const KURL& url, const TQCString &ns, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// kate: replace-tabs true; tab-width 4;
|
@ -0,0 +1,105 @@
|
||||
/*******************************************************************************
|
||||
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, const TQCString &ns, 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 &ns, 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 &ns, 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 &ns, const TQCString& attribute);
|
||||
|
||||
/**
|
||||
* Check if reading extended attributes is supported for the given URL
|
||||
* and namespace.
|
||||
*
|
||||
* @param url the URL of the file
|
||||
* @return true if extended attributes are supported, false otherwise
|
||||
*/
|
||||
static bool readSupported(const KURL& url, const TQCString &ns);
|
||||
|
||||
/**
|
||||
* Check if writing extended attributes is supported for the given URL
|
||||
* and namespace.
|
||||
*
|
||||
* @param url the URL of the file
|
||||
* @return true if extended attributes are supported, false otherwise
|
||||
*/
|
||||
static bool writeSupported(const KURL& url, const TQCString &ns);
|
||||
|
||||
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