3

I'm working on a project where i rely mostly on the file's extended attribute that i set when i create the file. But i'm facing problem while modifying the file.

Example:

When i edit a text file in Sublime Text, all the extended attributes are preserved, also the inode value remains the same as before. But when i do the same in Gedit, it is creating new file instead of modifying the existing one. So inode value, user defined/extended attributes are not preserved.

I see this problem is due to the copying behavior of Gedit.

Is there a way to preserve them while using Gedit?

1

The answer is no. You will have to file a bug report, or fork the project and implement an extended attributes protocol.

In C++/Qt, I wrote a Library that accomplishes this using the <sys/xattr.h>. It is something you would have to attach to a QFile, and if its something you want to merge into Gedit, you would have to convert it to C code. In any case, it gives an example of an implementation. Basic 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

Basic 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 ;
    }
}

You will need to add more templates in order to handle serialization of the different data types. The original functions take void pointers so really you can put anything in it. How this would end up looking in C? Can't help you there.

https://wiki.gnome.org/Apps/Gedit/ReportingBugs

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.