Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-80835

Integrate xattr into QFile

    XMLWordPrintable

Details

    • Suggestion
    • Resolution: Unresolved
    • P3: Somewhat important
    • None
    • None
    • Core: I/O
    • None
    • Android, Linux/Wayland, Linux/X11, Linux/Other display system, macOS

    Description

      QFile or a another class should integrate xattr.

      http://man7.org/linux/man-pages/man7/xattr.7.html

      "Extended attributes are name:value pairs associated permanently with
      files and directories, similar to the environment strings associated
      with a process."

      I am going to keep it light on details in case it is outright rejected.  What I would like to see though is the ability to read and write key value pairs to extended attributes on QFile, that would be mindful of of the filesystem capabilities. A structure similar to QMap would make sense to me:

      QFile f_Foobar("~/foobar.txt");
      
      f_Foobar.insert("foo"     , "bar"); // returns enum or ssize_t error
      f_Foobar.insert("user.foo", "bar"); // returns 0 for no error
      f_Foobar["user.foo"]; // returns "bar"
      f_Foobar.keys();      // returns QStringList() << "foo";
      

      The benefit of this is this provides a simple and accessible way to maintain state between sessions.

      The downside I see to this, is limited portability. However, we do have functionality already in Qt that is limited to certain platforms, so maybe this is fine.

       

      edit:

      I went ahead and created my own functional class.

      The long and short of it, is that we should create a class for handling xattr, "QXattr", and make it a member of QFile. I am guessing a pointer who's default value is "nullptr", but in the constructor, will initiate it if it detects a filesystem with xattr capabilities.

      Hence:

       

      	QFile f("/home/akiva/foobar.txt");
      	f->xattr.insert("user.foo", "foo");
      	f->xattr.insert("user.bar", "bar");
      	qDebug() << f.xattr.value<QByteArray>("user.foo");
      

       

       

      Here is the header:

       

      #ifndef QXATTR_H
      #define QXATTR_H
      
      #include <QObject>
      #include <QFile>
      #include <sys/xattr.h>
      
      class QXattr : public QObject
      {
      	Q_OBJECT
      public:
      	explicit QXattr(QFile &parent);	
      
      	template<typename T> bool        insert  ( const QString &name, T value ); // Returns true if properly serialized
      	template<typename T> T           value   ( const QString &name ); // Gets value of name. Returns Null pointer if none found.
      	template<typename T> QString     name    ( T value ); // Returns first name of given value
      	template<typename T> QStringList names   ( T value ); // Returns all names of a given value
      	                     QStringList names   ();          // Returns all names
      	                     bool        contains( const QString &name ); // Returns true if name exists.
      	                     bool        remove  ( const QString &name ); // Returns true if something was removed.	
      	QString errorString(); // Last registered errno
      
      protected:
      	inline const char *fileName();
      	void setErrorString(const QString &error);
      	void getNames(); // Should trigger on a move action in case the filesystem changed
      
      signals:
      	void namesChanged();
      	void errorStringChanged( QString error );
      	// More signals will be placed here
      
      private:
      	QStringList m_Names;
      	QFile &m_File;
      	QString m_ErrorString;
      };
      #endif // QXATTR_H
      
      

      Instead of "Key:Value" pairs, I used the wording "Name:Value" pairs, as that is what is used in xattr's documentation, and key implies that different types other than a string can be used.

       

      In the source, I included my own library qconsoletoolkit. It accounts for the `ct_Check( ... )` lines, and can be easily changed. Anyway, here is the source:

      #include "qxattr.h"
      #include "qconsoletoolkit.h"
      
      QXattr::QXattr(QFile &parent)
      	: QObject(&parent)
              , m_File(parent)
      {}
      
      /* Insertion Templates */
      template<typename T> bool QXattr::insert(const QString &name, T value)
      {
      	Q_UNUSED(name)
      	Q_UNUSED(value)
      	return false;
      }
      template<> bool QXattr::insert<QByteArray> (const QString &name, QByteArray  value)
      {
      	int error = setxattr(fileName(), name.toLocal8Bit().data(), value.data(), size_t(value.size()), 0 );
      	ct_Check(error == -1, strerror(errno));
      	this->getNames();
      	return true;
      }
      template<> bool QXattr::insert<QString>    (const QString &name, QString     value)
      {
      	return this->insert<QByteArray>(name, value.toLocal8Bit());
      }
      template<> bool QXattr::insert<const char*>(const QString &name, const char* value)
      {
      	return this->insert<QByteArray>(name, QByteArray(value));
      }
      
      /* Value Templates */
      template<typename T> T QXattr::value(const QString &name)
      {
      	Q_UNUSED(name)
      }
      template<> QByteArray QXattr::value<QByteArray>(const QString &name)
      {
      	ssize_t valueLength;
      	valueLength = getxattr(fileName(), name.toLocal8Bit().data(), nullptr, 0);
      	ct_Check(valueLength == -1, strerror(errno));
      	if (!valueLength) { return QByteArray(); }
      	QByteArray val(int(valueLength), ' ');
      	valueLength = getxattr(fileName(), name.toLocal8Bit().data(), val.data(), size_t(valueLength));
      	ct_Check(valueLength == -1, strerror(errno));
      	return val;
      }
      
      /* Name Templates */
      template<typename T> QString QXattr::name(T value)
      {
      	Q_UNUSED(value)
      	return QString();
      }
      template<> QString QXattr::name<const char *>(const char* value)
      {
      	getNames();
      	QByteArray ba(value);
      	for ( int i = 0; i < m_Names.length(); i++ ) {
      		if (ba == this->value<QByteArray>(m_Names.at(i))) { return m_Names.at(i); }
      	}
      	return QString();
      }
      
      /* Names Templates */
      template<class T> QStringList QXattr::names(T value)
      {
      	Q_UNUSED(value)
      	return QStringList();
      }
      template<> QStringList QXattr::names<const char *>(const char* value)
      {
      	getNames();
      	QByteArray ba(value);
      	QStringList names;
      	for ( int i = 0; i < m_Names.length(); i++ ) {
      		if (ba == this->value<QByteArray>(m_Names.at(i))) { names << m_Names.at(i); }
      	}
      	return names;
      }
      QStringList QXattr::names()
      {
      	getNames();
      	return m_Names;
      }
      
      /* Regular Functions */
      bool QXattr::contains(const QString &name)
      {
      	getNames();
      	return m_Names.contains(name);
      }
      bool QXattr::remove(const QString &name)
      {
      	// Returns false if error.
      	// Error can mean, that the name simply did not exist.
      	if ( removexattr(fileName(), name.toLocal8Bit().data()) == -1 ) {
      		setErrorString(strerror(errno));
      		return false;
      	}
      	return true;
      }
      
      /* Convenience Functions */
      const char* QXattr::fileName()
      {
      	return m_File.fileName().toLocal8Bit().data();
      }
      void QXattr::setErrorString(const QString &error)
      {
      	m_ErrorString = error;
      	emit errorStringChanged( error );
      }
      void QXattr::getNames()
      {
      	m_Names.clear();
      	ssize_t bufferLength(0);
      	ssize_t nameLength;
      	char *name;
      
      	/* Determine the length of the buffer needed. */
      	bufferLength = listxattr(fileName(), nullptr, 0);
      	ct_Check(bufferLength == -1, strerror(errno));
      	if (!bufferLength) { return; }
      
      	/* Allocate the buffer.  */
      	QByteArray buffer(int(bufferLength), ' ');
      
      	/* Copy the list of attribute keys to the buffer. */
      	bufferLength = listxattr(fileName(), buffer.data(), size_t(bufferLength));
      	ct_Check(bufferLength == -1, strerror(errno));
      
      	/* Loop over the list of zero terminated strings with the attribute keys.
      	 * Use the remaining buffer length to determine the end of the list. */
      	name = buffer.data();
      	QStringList names;
      	while (bufferLength > 0) {
      		m_Names << name;
      		/* Forward to next attribute name. */
      		nameLength = ssize_t(strlen(name) + 1);
      		bufferLength -= nameLength;
      		name += nameLength ;
      	}
      }
      
      
      
      

       

      No issues thus far.

       

       

       

       

       

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            thiago Thiago Macieira
            akiva Akiva Avraham
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes