added optional extended file attributes support
This commit is contained in:
parent
420e258478
commit
229645a0e2
@ -34,3 +34,6 @@
|
||||
#undef HAVE_SYS_STATFS_H
|
||||
#undef HAVE_SYS_STATVFS_H
|
||||
#undef HAVE_SYS_VFS_H
|
||||
|
||||
/* Use file extended attributes */
|
||||
#undef RCL_USE_XATTR
|
||||
|
||||
@ -530,6 +530,15 @@ bool RclConfig::readFieldsConfig(const string& cnferrloc)
|
||||
}
|
||||
}
|
||||
|
||||
// Extended file attribute to field translations
|
||||
list<string>xattrs = m_fields->getNames("xattrtofields");
|
||||
for (list<string>::const_iterator it = xattrs.begin();
|
||||
it != xattrs.end(); it++) {
|
||||
string val;
|
||||
m_fields->get(*it, val, "xattrtofields");
|
||||
m_xattrtofld[*it] = val;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -881,7 +890,8 @@ void RclConfig::initFrom(const RclConfig& r)
|
||||
m_reason = r.m_reason;
|
||||
m_confdir = r.m_confdir;
|
||||
m_datadir = r.m_datadir;
|
||||
m_keydir = r.m_datadir;
|
||||
m_keydir = r.m_keydir;
|
||||
m_cdirs = r.m_cdirs;
|
||||
// We should use reference-counted objects instead!
|
||||
if (r.m_conf)
|
||||
m_conf = new ConfStack<ConfTree>(*(r.m_conf));
|
||||
@ -891,6 +901,12 @@ void RclConfig::initFrom(const RclConfig& r)
|
||||
mimeconf = new ConfStack<ConfSimple>(*(r.mimeconf));
|
||||
if (r.mimeview)
|
||||
mimeview = new ConfStack<ConfSimple>(*(r.mimeview));
|
||||
if (r.m_fields)
|
||||
m_fields = new ConfStack<ConfSimple>(*(r.m_fields));
|
||||
m_fldtopfx = r.m_fldtopfx;
|
||||
m_aliastocanon = r.m_aliastocanon;
|
||||
m_storedFields = r.m_storedFields;
|
||||
m_xattrtofld = r.m_xattrtofld;
|
||||
if (r.m_stopsuffixes)
|
||||
m_stopsuffixes = new SuffixStore(*((SuffixStore*)r.m_stopsuffixes));
|
||||
m_maxsufflen = r.m_maxsufflen;
|
||||
|
||||
@ -157,7 +157,9 @@ class RclConfig {
|
||||
const set<string>& getStoredFields() {return m_storedFields;}
|
||||
/** Get canonic name for possible alias */
|
||||
string fieldCanon(const string& fld);
|
||||
|
||||
/** Get xattr name to field names translations */
|
||||
const map<string, string>& getXattrToField() {return m_xattrtofld;}
|
||||
|
||||
/** mimeview: get/set external viewer exec string(s) for mimetype(s) */
|
||||
string getMimeViewerDef(const string &mimetype);
|
||||
bool getMimeViewerDefs(vector<pair<string, string> >&);
|
||||
@ -205,6 +207,7 @@ class RclConfig {
|
||||
map<string, string> m_fldtopfx;
|
||||
map<string, string> m_aliastocanon;
|
||||
set<string> m_storedFields;
|
||||
map<string, string> m_xattrtofld;
|
||||
|
||||
void *m_stopsuffixes;
|
||||
unsigned int m_maxsufflen;
|
||||
|
||||
28
src/configure
vendored
28
src/configure
vendored
@ -1269,6 +1269,15 @@ if test -n "$ac_init_help"; then
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
Optional Features:
|
||||
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
|
||||
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
|
||||
--enable-xattr Enable fetching metadata from file extended
|
||||
attributes. This is only useful if some application
|
||||
creates them on (part of) your data set. You also
|
||||
need to set up appropriate mappings in the
|
||||
configuration.
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
@ -3633,6 +3642,25 @@ else
|
||||
echo "$as_me: inotify monitoring disabled" >&6;}
|
||||
fi
|
||||
|
||||
# Enable use of file extended attributes.
|
||||
# Not by default as these are little used for now.
|
||||
# Check whether --enable-xattr was given.
|
||||
if test "${enable_xattr+set}" = set; then
|
||||
enableval=$enable_xattr; xattrEnabled=$enableval
|
||||
else
|
||||
xattrEnabled=no
|
||||
fi
|
||||
|
||||
|
||||
if test X$xattrEnabled = Xyes ; then
|
||||
|
||||
cat >>confdefs.h <<\_ACEOF
|
||||
#define RCL_USE_XATTR 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
for ac_func in mkdtemp
|
||||
do
|
||||
|
||||
@ -112,6 +112,20 @@ else
|
||||
AC_MSG_NOTICE([inotify monitoring disabled])
|
||||
fi
|
||||
|
||||
# Enable use of file extended attributes.
|
||||
# Not by default as these are little used for now.
|
||||
AC_ARG_ENABLE(xattr,
|
||||
AC_HELP_STRING([--enable-xattr],
|
||||
[Enable fetching metadata from file extended attributes. This is only
|
||||
useful if some application creates them on (part of) your data set. You also
|
||||
need to set up appropriate mappings in the configuration.]),
|
||||
xattrEnabled=$enableval, xattrEnabled=no)
|
||||
|
||||
if test X$xattrEnabled = Xyes ; then
|
||||
AC_DEFINE(RCL_USE_XATTR, 1, [Use file extended attributes])
|
||||
fi
|
||||
|
||||
|
||||
AC_CHECK_FUNCS(mkdtemp)
|
||||
|
||||
##### Look for iconv. We first look for libiconv in /usr/local/lib:/usr/lib
|
||||
|
||||
@ -19,6 +19,7 @@ static char rcsid[] = "@(#$Id: internfile.cpp,v 1.46 2008-10-10 08:04:54 dockes
|
||||
*/
|
||||
|
||||
#ifndef TEST_INTERNFILE
|
||||
#include "autoconfig.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@ -132,7 +133,7 @@ void FileInterner::tmpcleanup()
|
||||
//
|
||||
// Empty handler on return says that we're in error, this will be
|
||||
// processed by the first call to internfile().
|
||||
FileInterner::FileInterner(const std::string &f, const struct stat *stp,
|
||||
FileInterner::FileInterner(const string &f, const struct stat *stp,
|
||||
RclConfig *cnf,
|
||||
const string& td, const string *imime)
|
||||
: m_cfg(cnf), m_fn(f), m_forPreview(imime?true:false), m_tdir(td)
|
||||
@ -337,7 +338,12 @@ static const string keytt("title");
|
||||
bool FileInterner::dijontorcl(Rcl::Doc& doc)
|
||||
{
|
||||
Dijon::Filter *df = m_handlers.back();
|
||||
const std::map<std::string, std::string>& docdata = df->get_meta_data();
|
||||
if (df == 0) {
|
||||
//??
|
||||
LOGERR(("FileInterner::dijontorcl: null top handler ??\n"));
|
||||
return false;
|
||||
}
|
||||
const map<string, string>& docdata = df->get_meta_data();
|
||||
|
||||
for (map<string,string>::const_iterator it = docdata.begin();
|
||||
it != docdata.end(); it++) {
|
||||
@ -357,6 +363,18 @@ bool FileInterner::dijontorcl(Rcl::Doc& doc)
|
||||
doc.meta[Rcl::Doc::keyabs] = doc.meta[keyds];
|
||||
doc.meta.erase(keyds);
|
||||
}
|
||||
#ifdef RCL_USE_XATTR
|
||||
// Finally set any data possibly coming out of the extended file attributes
|
||||
// these override any values from inside the file.
|
||||
RecollFilter *rf = dynamic_cast<RecollFilter*>(df);
|
||||
if (rf != 0) {
|
||||
const map<string, string>& ffa = rf->getFieldsFromAttrs();
|
||||
for (map<string,string>::const_iterator it = ffa.begin();
|
||||
it != ffa.end(); it++) {
|
||||
doc.meta[it->first] = it->second;
|
||||
}
|
||||
}
|
||||
#endif //RCL_USE_XATTR
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -425,7 +443,7 @@ enum addResols {ADD_OK, ADD_CONTINUE, ADD_BREAK, ADD_ERROR};
|
||||
// and possibly add a filter/handler to the stack
|
||||
int FileInterner::addHandler()
|
||||
{
|
||||
const std::map<std::string, std::string>& docdata =
|
||||
const map<string, string>& docdata =
|
||||
m_handlers.back()->get_meta_data();
|
||||
string charset, mimetype;
|
||||
getKeyValue(docdata, keycs, charset);
|
||||
|
||||
@ -50,6 +50,7 @@ class MimeHandlerExec : public RecollFilter {
|
||||
{}
|
||||
virtual ~MimeHandlerExec() {}
|
||||
virtual bool set_document_file(const string &file_path) {
|
||||
RecollFilter::set_document_file(file_path);
|
||||
m_fn = file_path;
|
||||
m_havedoc = true;
|
||||
return true;
|
||||
|
||||
@ -41,6 +41,7 @@ using namespace std;
|
||||
bool MimeHandlerHtml::set_document_file(const string &fn)
|
||||
{
|
||||
LOGDEB0(("textHtmlToDoc: %s\n", fn.c_str()));
|
||||
RecollFilter::set_document_file(fn);
|
||||
string otext;
|
||||
if (!file_to_string(fn, otext)) {
|
||||
LOGINFO(("textHtmlToDoc: cant read: %s\n", fn.c_str()));
|
||||
|
||||
@ -78,6 +78,7 @@ void MimeHandlerMail::clear()
|
||||
bool MimeHandlerMail::set_document_file(const string &fn)
|
||||
{
|
||||
LOGDEB(("MimeHandlerMail::set_document_file(%s)\n", fn.c_str()));
|
||||
RecollFilter::set_document_file(fn);
|
||||
if (m_fd >= 0) {
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
|
||||
@ -57,6 +57,7 @@ void MimeHandlerMbox::clear()
|
||||
bool MimeHandlerMbox::set_document_file(const string &fn)
|
||||
{
|
||||
LOGDEB(("MimeHandlerMbox::set_document_file(%s)\n", fn.c_str()));
|
||||
RecollFilter::set_document_file(fn);
|
||||
m_fn = fn;
|
||||
if (m_vfp) {
|
||||
fclose((FILE *)m_vfp);
|
||||
|
||||
@ -34,6 +34,7 @@ using namespace std;
|
||||
// Process a plain text file
|
||||
bool MimeHandlerText::set_document_file(const string &fn)
|
||||
{
|
||||
RecollFilter::set_document_file(fn);
|
||||
string otext;
|
||||
string reason;
|
||||
if (!file_to_string(fn, otext, &reason)) {
|
||||
|
||||
@ -30,7 +30,8 @@ class MimeHandlerUnknown : public RecollFilter {
|
||||
public:
|
||||
MimeHandlerUnknown(const string& mt) : RecollFilter(mt) {}
|
||||
virtual ~MimeHandlerUnknown() {}
|
||||
virtual bool set_document_string(const string&) {
|
||||
virtual bool set_document_string(const string& fn) {
|
||||
RecollFilter::set_document_file(fn);
|
||||
return m_havedoc = true;
|
||||
}
|
||||
virtual bool set_document_file(const string&) {
|
||||
|
||||
@ -17,16 +17,21 @@ static char rcsid[] = "@(#$Id: mimehandler.cpp,v 1.25 2008-10-09 09:19:37 dockes
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include "autoconfig.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#include "mimehandler.h"
|
||||
#include "debuglog.h"
|
||||
#include "rclconfig.h"
|
||||
#include "smallut.h"
|
||||
#ifdef RCL_USE_XATTR
|
||||
#include "pxattr.h"
|
||||
#endif // RCL_USE_XATTR
|
||||
|
||||
#include "mh_exec.h"
|
||||
#include "mh_html.h"
|
||||
@ -35,6 +40,40 @@ using namespace std;
|
||||
#include "mh_text.h"
|
||||
#include "mh_unknown.h"
|
||||
|
||||
// Common code for all docs that are a file (not subdocs). If extended
|
||||
// attributes support is enabled, fetch the data.
|
||||
bool RecollFilter::set_document_file(const string& path)
|
||||
{
|
||||
#ifdef RCL_USE_XATTR
|
||||
RclConfig* rclconfig = RclConfig::getMainConfig();
|
||||
if (rclconfig == 0) {
|
||||
LOGERR(("RecollFilter::set_document_file: no config\n"));
|
||||
return false;
|
||||
}
|
||||
vector<string> xnames;
|
||||
if (!pxattr::list(path, &xnames)) {
|
||||
LOGERR(("xattrToMeta: pxattr::list failed, errno %d\n", errno));
|
||||
return false;
|
||||
}
|
||||
const map<string, string>& xtof = rclconfig->getXattrToField();
|
||||
for (vector<string>::const_iterator it = xnames.begin();
|
||||
it != xnames.end(); it++) {
|
||||
map<string, string>::const_iterator mit;
|
||||
if ((mit = xtof.find(*it)) != xtof.end()) {
|
||||
string value;
|
||||
if (!pxattr::get(path, *it, &value, pxattr::PXATTR_NOFOLLOW)) {
|
||||
LOGERR(("xattrToMeta: pxattr::get failed for %s, errno %d\n",
|
||||
(*it).c_str(), errno));
|
||||
continue;
|
||||
}
|
||||
// Encode should we ?
|
||||
m_fieldsFromAttrs[mit->second] = value;
|
||||
}
|
||||
}
|
||||
#endif // RCL_USE_XATTR
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pool of already known and created handlers. There can be several instance
|
||||
// for a given mime type (think email attachment in email message)
|
||||
static multimap<string, Dijon::Filter*> o_handlers;
|
||||
|
||||
@ -17,11 +17,16 @@
|
||||
#ifndef _MIMEHANDLER_H_INCLUDED_
|
||||
#define _MIMEHANDLER_H_INCLUDED_
|
||||
/* @(#$Id: mimehandler.h,v 1.16 2008-10-04 14:26:59 dockes Exp $ (C) 2004 J.F.Dockes */
|
||||
#include "autoconfig.h"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
using std::string;
|
||||
using std::list;
|
||||
#ifdef RCL_USE_XATTR
|
||||
#include <map>
|
||||
using std::map;
|
||||
#endif // RCL_USE_XATTR
|
||||
|
||||
#include <Filter.h>
|
||||
|
||||
@ -51,6 +56,10 @@ public:
|
||||
// We don't use this for now
|
||||
virtual bool set_document_uri(const std::string &) {return false;}
|
||||
|
||||
/// This does the extended attributes thing if enabled and should
|
||||
/// be called from subclasses.
|
||||
virtual bool set_document_file(const string &file_path);
|
||||
|
||||
// Default implementations
|
||||
virtual bool set_document_string(const std::string &) {return false;}
|
||||
virtual bool set_document_data(const char *cp, unsigned int sz) {
|
||||
@ -77,15 +86,26 @@ public:
|
||||
}
|
||||
|
||||
virtual void clear() {
|
||||
m_forPreview = m_havedoc = false;
|
||||
Dijon::Filter::clear();
|
||||
m_forPreview = m_havedoc = false;
|
||||
m_defcharset.clear();
|
||||
m_reason.clear();
|
||||
#ifdef RCL_USE_XATTR
|
||||
m_fieldsFromAttrs.clear();
|
||||
#endif // RCL_USE_XATTR
|
||||
}
|
||||
#ifdef RCL_USE_XATTR
|
||||
const map<string, string>& getFieldsFromAttrs() {return m_fieldsFromAttrs;}
|
||||
#endif // RCL_USE_XATTR
|
||||
|
||||
protected:
|
||||
bool m_forPreview;
|
||||
string m_defcharset;
|
||||
string m_reason;
|
||||
bool m_havedoc;
|
||||
#ifdef RCL_USE_XATTR
|
||||
map<string, string> m_fieldsFromAttrs;
|
||||
#endif // RCL_USE_XATTR
|
||||
};
|
||||
|
||||
/**
|
||||
@ -97,11 +117,12 @@ protected:
|
||||
* indexedmimetypes (if this is set at all).
|
||||
*/
|
||||
extern Dijon::Filter *getMimeHandler(const std::string &mtyp, RclConfig *cfg,
|
||||
|
||||
bool filtertypes=false);
|
||||
|
||||
/// Free up filter for reuse (you can also delete it)
|
||||
extern void returnMimeHandler(Dijon::Filter *);
|
||||
|
||||
/// Can this mime type be interned ?
|
||||
extern bool canIntern(const std::string mimetype, RclConfig *cfg);
|
||||
|
||||
#endif /* _MIMEHANDLER_H_INCLUDED_ */
|
||||
|
||||
@ -6,8 +6,8 @@ LIBS = librcl.a
|
||||
|
||||
all: $(LIBS)
|
||||
|
||||
OBJS = rclaspell.o rclconfig.o rclinit.o textsplit.o unacpp.o csguess.o indexer.o mimetype.o htmlparse.o myhtmlparse.o mimehandler.o internfile.o mh_exec.o mh_html.o mh_mail.o mh_mbox.o mh_text.o docseq.o docseqdb.o docseqhist.o filtseq.o history.o plaintorich.o recollq.o reslistpager.o sortseq.o wasastringtoquery.o wasatorcl.o rcldb.o rcldoc.o rclquery.o searchdata.o stemdb.o stoplist.o base64.o conftree.o copyfile.o debuglog.o execmd.o fstreewalk.o idfile.o fileudi.o md5.o mimeparse.o pathut.o readfile.o smallut.o transcode.o wipedir.o x11mon.o mime-getpart.o mime-parsefull.o mime-parseonlyheader.o mime-printbody.o mime-printdoc.o mime-printheader.o mime.o convert.o iodevice.o iofactory.o
|
||||
DEPS = rclaspell.dep.stamp rclconfig.dep.stamp rclinit.dep.stamp textsplit.dep.stamp unacpp.dep.stamp csguess.dep.stamp indexer.dep.stamp mimetype.dep.stamp htmlparse.dep.stamp myhtmlparse.dep.stamp mimehandler.dep.stamp internfile.dep.stamp mh_exec.dep.stamp mh_html.dep.stamp mh_mail.dep.stamp mh_mbox.dep.stamp mh_text.dep.stamp docseq.dep.stamp docseqdb.dep.stamp docseqhist.dep.stamp filtseq.dep.stamp history.dep.stamp plaintorich.dep.stamp recollq.dep.stamp reslistpager.dep.stamp sortseq.dep.stamp wasastringtoquery.dep.stamp wasatorcl.dep.stamp rcldb.dep.stamp rcldoc.dep.stamp rclquery.dep.stamp searchdata.dep.stamp stemdb.dep.stamp stoplist.dep.stamp base64.dep.stamp conftree.dep.stamp copyfile.dep.stamp debuglog.dep.stamp execmd.dep.stamp fstreewalk.dep.stamp idfile.dep.stamp fileudi.dep.stamp md5.dep.stamp mimeparse.dep.stamp pathut.dep.stamp readfile.dep.stamp smallut.dep.stamp transcode.dep.stamp wipedir.dep.stamp x11mon.dep.stamp mime-getpart.dep.stamp mime-parsefull.dep.stamp mime-parseonlyheader.dep.stamp mime-printbody.dep.stamp mime-printdoc.dep.stamp mime-printheader.dep.stamp mime.dep.stamp convert.dep.stamp iodevice.dep.stamp iofactory.dep.stamp
|
||||
OBJS = rclaspell.o rclconfig.o rclinit.o textsplit.o unacpp.o csguess.o indexer.o mimetype.o htmlparse.o myhtmlparse.o mimehandler.o internfile.o mh_exec.o mh_html.o mh_mail.o mh_mbox.o mh_text.o docseq.o docseqdb.o docseqhist.o filtseq.o history.o plaintorich.o recollq.o reslistpager.o sortseq.o wasastringtoquery.o wasatorcl.o rcldb.o rcldoc.o rclquery.o searchdata.o stemdb.o stoplist.o base64.o conftree.o copyfile.o debuglog.o execmd.o fstreewalk.o idfile.o fileudi.o md5.o mimeparse.o pathut.o pxattr.o readfile.o smallut.o transcode.o wipedir.o x11mon.o mime-getpart.o mime-parsefull.o mime-parseonlyheader.o mime-printbody.o mime-printdoc.o mime-printheader.o mime.o convert.o iodevice.o iofactory.o
|
||||
DEPS = rclaspell.dep.stamp rclconfig.dep.stamp rclinit.dep.stamp textsplit.dep.stamp unacpp.dep.stamp csguess.dep.stamp indexer.dep.stamp mimetype.dep.stamp htmlparse.dep.stamp myhtmlparse.dep.stamp mimehandler.dep.stamp internfile.dep.stamp mh_exec.dep.stamp mh_html.dep.stamp mh_mail.dep.stamp mh_mbox.dep.stamp mh_text.dep.stamp docseq.dep.stamp docseqdb.dep.stamp docseqhist.dep.stamp filtseq.dep.stamp history.dep.stamp plaintorich.dep.stamp recollq.dep.stamp reslistpager.dep.stamp sortseq.dep.stamp wasastringtoquery.dep.stamp wasatorcl.dep.stamp rcldb.dep.stamp rcldoc.dep.stamp rclquery.dep.stamp searchdata.dep.stamp stemdb.dep.stamp stoplist.dep.stamp base64.dep.stamp conftree.dep.stamp copyfile.dep.stamp debuglog.dep.stamp execmd.dep.stamp fstreewalk.dep.stamp idfile.dep.stamp fileudi.dep.stamp md5.dep.stamp mimeparse.dep.stamp pathut.dep.stamp pxattr.dep.stamp readfile.dep.stamp smallut.dep.stamp transcode.dep.stamp wipedir.dep.stamp x11mon.dep.stamp mime-getpart.dep.stamp mime-parsefull.dep.stamp mime-parseonlyheader.dep.stamp mime-printbody.dep.stamp mime-printdoc.dep.stamp mime-printheader.dep.stamp mime.dep.stamp convert.dep.stamp iodevice.dep.stamp iofactory.dep.stamp
|
||||
|
||||
librcl.a : $(DEPS) $(OBJS) unac.o
|
||||
ar ru librcl.a $(OBJS) unac.o
|
||||
@ -105,6 +105,8 @@ mimeparse.o : ../utils/mimeparse.cpp
|
||||
$(CXX) $(ALL_CXXFLAGS) -c ../utils/mimeparse.cpp
|
||||
pathut.o : ../utils/pathut.cpp
|
||||
$(CXX) $(ALL_CXXFLAGS) -c ../utils/pathut.cpp
|
||||
pxattr.o : ../utils/pxattr.cpp
|
||||
$(CXX) $(ALL_CXXFLAGS) -c ../utils/pxattr.cpp
|
||||
readfile.o : ../utils/readfile.cpp
|
||||
$(CXX) $(ALL_CXXFLAGS) -c ../utils/readfile.cpp
|
||||
smallut.o : ../utils/smallut.cpp
|
||||
@ -276,6 +278,9 @@ mimeparse.dep.stamp : ../utils/mimeparse.cpp
|
||||
pathut.dep.stamp : ../utils/pathut.cpp
|
||||
$(CXX) -M $(ALL_CXXFLAGS) ../utils/pathut.cpp > pathut.dep
|
||||
touch pathut.dep.stamp
|
||||
pxattr.dep.stamp : ../utils/pxattr.cpp
|
||||
$(CXX) -M $(ALL_CXXFLAGS) ../utils/pxattr.cpp > pxattr.dep
|
||||
touch pxattr.dep.stamp
|
||||
readfile.dep.stamp : ../utils/readfile.cpp
|
||||
$(CXX) -M $(ALL_CXXFLAGS) ../utils/readfile.cpp > readfile.dep
|
||||
touch readfile.dep.stamp
|
||||
@ -336,6 +341,7 @@ include fileudi.dep
|
||||
include md5.dep
|
||||
include mimeparse.dep
|
||||
include pathut.dep
|
||||
include pxattr.dep
|
||||
include readfile.dep
|
||||
include smallut.dep
|
||||
include transcode.dep
|
||||
|
||||
@ -49,6 +49,7 @@ ${depth}/utils/fileudi.cpp \
|
||||
${depth}/utils/md5.cpp \
|
||||
${depth}/utils/mimeparse.cpp \
|
||||
${depth}/utils/pathut.cpp \
|
||||
${depth}/utils/pxattr.cpp \
|
||||
${depth}/utils/readfile.cpp \
|
||||
${depth}/utils/smallut.cpp \
|
||||
${depth}/utils/transcode.cpp \
|
||||
|
||||
@ -72,3 +72,9 @@ url = dc:identifier xesam:url
|
||||
# This is not used for now
|
||||
[specialisations]
|
||||
author = from
|
||||
|
||||
######################
|
||||
# Section to define translations from extended file attribute names to
|
||||
# field names. xattr use must be enabled at compile time for this to be
|
||||
# used. Enter translations as "xattrname = fieldname". Case matters.
|
||||
[xattrtofields]
|
||||
|
||||
736
src/utils/pxattr.cpp
Normal file
736
src/utils/pxattr.cpp
Normal file
@ -0,0 +1,736 @@
|
||||
/* @(#$Id: pxattr.cpp,v 1.9 2009-01-20 13:48:34 dockes Exp $ (C) 2009 J.F.Dockes
|
||||
Copyright (c) 2009 Jean-Francois Dockes
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// We want this to compile even to empty on non-supported systems. makes
|
||||
// things easier for autoconf
|
||||
#if defined(__FreeBSD__) || defined(__gnu_linux__) || defined(__APPLE__)
|
||||
|
||||
#ifndef TEST_PXATTR
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/extattr.h>
|
||||
#include <sys/uio.h>
|
||||
#elif defined(__gnu_linux__)
|
||||
#include <attr/xattr.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/xattr.h>
|
||||
#else
|
||||
#error "Unknown system can't compile"
|
||||
#endif
|
||||
|
||||
#include "pxattr.h"
|
||||
|
||||
namespace pxattr {
|
||||
|
||||
class AutoBuf {
|
||||
public:
|
||||
char *buf;
|
||||
AutoBuf() : buf(0) {}
|
||||
~AutoBuf() {if (buf) free(buf); buf = 0;}
|
||||
bool alloc(int n)
|
||||
{
|
||||
if (buf) {
|
||||
free(buf);
|
||||
buf = 0;
|
||||
}
|
||||
buf = (char *)malloc(n);
|
||||
return buf != 0;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
get(int fd, const string& path, const string& _name, string *value,
|
||||
flags flags, nspace dom)
|
||||
{
|
||||
string name;
|
||||
if (!sysname(dom, _name, &name))
|
||||
return false;
|
||||
|
||||
ssize_t ret = -1;
|
||||
AutoBuf buf;
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = extattr_get_link(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), 0, 0);
|
||||
} else {
|
||||
ret = extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name.c_str(), 0, 0);
|
||||
}
|
||||
if (ret < 0)
|
||||
return false;
|
||||
if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0
|
||||
return false;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = extattr_get_link(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), buf.buf, ret);
|
||||
} else {
|
||||
ret = extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), buf.buf, ret);
|
||||
}
|
||||
} else {
|
||||
ret = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), buf.buf, ret);
|
||||
}
|
||||
#elif defined(__gnu_linux__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = lgetxattr(path.c_str(), name.c_str(), 0, 0);
|
||||
} else {
|
||||
ret = getxattr(path.c_str(), name.c_str(), 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret = fgetxattr(fd, name.c_str(), 0, 0);
|
||||
}
|
||||
if (ret < 0)
|
||||
return false;
|
||||
if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0
|
||||
return false;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = lgetxattr(path.c_str(), name.c_str(), buf.buf, ret);
|
||||
} else {
|
||||
ret = getxattr(path.c_str(), name.c_str(), buf.buf, ret);
|
||||
}
|
||||
} else {
|
||||
ret = fgetxattr(fd, name.c_str(), buf.buf, ret);
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = getxattr(path.c_str(), name.c_str(), 0, 0, 0, XATTR_NOFOLLOW);
|
||||
} else {
|
||||
ret = getxattr(path.c_str(), name.c_str(), 0, 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret = fgetxattr(fd, name.c_str(), 0, 0, 0, 0);
|
||||
}
|
||||
if (ret < 0)
|
||||
return false;
|
||||
if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0
|
||||
return false;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = getxattr(path.c_str(), name.c_str(), buf.buf, ret, 0,
|
||||
XATTR_NOFOLLOW);
|
||||
} else {
|
||||
ret = getxattr(path.c_str(), name.c_str(), buf.buf, ret, 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret = fgetxattr(fd, name.c_str(), buf.buf, ret, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret >= 0)
|
||||
value->assign(buf.buf, ret);
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
set(int fd, const string& path, const string& _name,
|
||||
const string& value, flags flags, nspace dom)
|
||||
{
|
||||
string name;
|
||||
if (!sysname(dom, _name, &name))
|
||||
return false;
|
||||
|
||||
ssize_t ret = -1;
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
|
||||
if (flags & (PXATTR_CREATE|PXATTR_REPLACE)) {
|
||||
// Need to test existence
|
||||
bool exists = false;
|
||||
ssize_t eret;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
eret = extattr_get_link(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), 0, 0);
|
||||
} else {
|
||||
eret = extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), 0, 0);
|
||||
}
|
||||
} else {
|
||||
eret = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), 0, 0);
|
||||
}
|
||||
if (eret >= 0)
|
||||
exists = true;
|
||||
if (eret < 0 && errno != ENOATTR)
|
||||
return false;
|
||||
if ((flags & PXATTR_CREATE) && exists) {
|
||||
errno = EEXIST;
|
||||
return false;
|
||||
}
|
||||
if ((flags & PXATTR_REPLACE) && !exists) {
|
||||
errno = ENOATTR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = extattr_set_link(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), value.c_str(), value.length());
|
||||
} else {
|
||||
ret = extattr_set_file(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), value.c_str(), value.length());
|
||||
}
|
||||
} else {
|
||||
ret = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER,
|
||||
name.c_str(), value.c_str(), value.length());
|
||||
}
|
||||
#elif defined(__gnu_linux__)
|
||||
int opts = 0;
|
||||
if (flags & PXATTR_CREATE)
|
||||
opts = XATTR_CREATE;
|
||||
else if (flags & PXATTR_REPLACE)
|
||||
opts = XATTR_REPLACE;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = lsetxattr(path.c_str(), name.c_str(), value.c_str(),
|
||||
value.length(), opts);
|
||||
} else {
|
||||
ret = setxattr(path.c_str(), name.c_str(), value.c_str(),
|
||||
value.length(), opts);
|
||||
}
|
||||
} else {
|
||||
ret = fsetxattr(fd, name.c_str(), value.c_str(), value.length(), opts);
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int opts = 0;
|
||||
if (flags & PXATTR_CREATE)
|
||||
opts = XATTR_CREATE;
|
||||
else if (flags & PXATTR_REPLACE)
|
||||
opts = XATTR_REPLACE;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = setxattr(path.c_str(), name.c_str(), value.c_str(),
|
||||
value.length(), 0, XATTR_NOFOLLOW|opts);
|
||||
} else {
|
||||
ret = setxattr(path.c_str(), name.c_str(), value.c_str(),
|
||||
value.length(), 0, opts);
|
||||
}
|
||||
} else {
|
||||
ret = fsetxattr(fd, name.c_str(), value.c_str(),
|
||||
value.length(), 0, opts);
|
||||
}
|
||||
#endif
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
del(int fd, const string& path, const string& _name, flags flags, nspace dom)
|
||||
{
|
||||
string name;
|
||||
if (!sysname(dom, _name, &name))
|
||||
return false;
|
||||
|
||||
int ret = -1;
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = extattr_delete_link(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str());
|
||||
} else {
|
||||
ret = extattr_delete_file(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
name.c_str());
|
||||
}
|
||||
} else {
|
||||
ret = extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, name.c_str());
|
||||
}
|
||||
#elif defined(__gnu_linux__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = lremovexattr(path.c_str(), name.c_str());
|
||||
} else {
|
||||
ret = removexattr(path.c_str(), name.c_str());
|
||||
}
|
||||
} else {
|
||||
ret = fremovexattr(fd, name.c_str());
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = removexattr(path.c_str(), name.c_str(), XATTR_NOFOLLOW);
|
||||
} else {
|
||||
ret = removexattr(path.c_str(), name.c_str(), 0);
|
||||
}
|
||||
} else {
|
||||
ret = fremovexattr(fd, name.c_str(), 0);
|
||||
}
|
||||
#endif
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
list(int fd, const string& path, vector<string>* names, flags flags, nspace dom)
|
||||
{
|
||||
ssize_t ret = -1;
|
||||
AutoBuf buf;
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = extattr_list_link(path.c_str(), EXTATTR_NAMESPACE_USER, 0, 0);
|
||||
} else {
|
||||
ret = extattr_list_file(path.c_str(), EXTATTR_NAMESPACE_USER, 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, 0, 0);
|
||||
}
|
||||
if (ret < 0)
|
||||
return false;
|
||||
if (!buf.alloc(ret+1)) // NEEDED on FreeBSD (no ending null)
|
||||
return false;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = extattr_list_link(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
buf.buf, ret);
|
||||
} else {
|
||||
ret = extattr_list_file(path.c_str(), EXTATTR_NAMESPACE_USER,
|
||||
buf.buf, ret);
|
||||
}
|
||||
} else {
|
||||
ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, buf.buf, ret);
|
||||
}
|
||||
#elif defined(__gnu_linux__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = llistxattr(path.c_str(), 0, 0);
|
||||
} else {
|
||||
ret = listxattr(path.c_str(), 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret = flistxattr(fd, 0, 0);
|
||||
}
|
||||
if (ret < 0)
|
||||
return false;
|
||||
if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0
|
||||
return false;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = llistxattr(path.c_str(), buf.buf, ret);
|
||||
} else {
|
||||
ret = listxattr(path.c_str(), buf.buf, ret);
|
||||
}
|
||||
} else {
|
||||
ret = flistxattr(fd, buf.buf, ret);
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = listxattr(path.c_str(), 0, 0, XATTR_NOFOLLOW);
|
||||
} else {
|
||||
ret = listxattr(path.c_str(), 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret = flistxattr(fd, 0, 0, 0);
|
||||
}
|
||||
if (ret < 0)
|
||||
return false;
|
||||
if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0
|
||||
return false;
|
||||
if (fd < 0) {
|
||||
if (flags & PXATTR_NOFOLLOW) {
|
||||
ret = listxattr(path.c_str(), buf.buf, ret, XATTR_NOFOLLOW);
|
||||
} else {
|
||||
ret = listxattr(path.c_str(), buf.buf, ret, 0);
|
||||
}
|
||||
} else {
|
||||
ret = flistxattr(fd, buf.buf, ret, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
char *bufstart = buf.buf;
|
||||
|
||||
// All systems return a 0-separated string list except FreeBSD
|
||||
// which has length, value pairs, length is a byte.
|
||||
#if defined(__FreeBSD__)
|
||||
char *cp = buf.buf;
|
||||
unsigned int len;
|
||||
while (cp < buf.buf + ret + 1) {
|
||||
len = *cp;
|
||||
*cp = 0;
|
||||
cp += len + 1;
|
||||
}
|
||||
bufstart = buf.buf + 1;
|
||||
*cp = 0; // don't forget, we allocated one more
|
||||
#endif
|
||||
|
||||
|
||||
if (ret > 0) {
|
||||
int pos = 0;
|
||||
while (pos < ret) {
|
||||
string n = string(bufstart + pos);
|
||||
string n1;
|
||||
if (pxname(PXATTR_USER, n, &n1)) {
|
||||
names->push_back(n1);
|
||||
}
|
||||
pos += n.length() + 1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const string nullstring("");
|
||||
|
||||
bool get(const string& path, const string& _name, string *value,
|
||||
flags flags, nspace dom)
|
||||
{
|
||||
return get(-1, path, _name, value, flags, dom);
|
||||
}
|
||||
bool get(int fd, const string& _name, string *value, flags flags, nspace dom)
|
||||
{
|
||||
return get(fd, nullstring, _name, value, flags, dom);
|
||||
}
|
||||
bool set(const string& path, const string& _name, const string& value,
|
||||
flags flags, nspace dom)
|
||||
{
|
||||
return set(-1, path, _name, value, flags, dom);
|
||||
}
|
||||
bool set(int fd, const string& _name, const string& value,
|
||||
flags flags, nspace dom)
|
||||
{
|
||||
return set(fd, nullstring, _name, value, flags, dom);
|
||||
}
|
||||
bool del(const string& path, const string& _name, flags flags, nspace dom)
|
||||
{
|
||||
return del(-1, path, _name, flags, dom);
|
||||
}
|
||||
bool del(int fd, const string& _name, flags flags, nspace dom)
|
||||
{
|
||||
return del(fd, nullstring, _name, flags, dom);
|
||||
}
|
||||
bool list(const string& path, vector<string>* names, flags flags, nspace dom)
|
||||
{
|
||||
return list(-1, path, names, flags, dom);
|
||||
}
|
||||
bool list(int fd, vector<string>* names, flags flags, nspace dom)
|
||||
{
|
||||
return list(fd, nullstring, names, flags, dom);
|
||||
}
|
||||
|
||||
static const string userstring("user.");
|
||||
bool sysname(nspace dom, const string& pname, string* sname)
|
||||
{
|
||||
if (dom != PXATTR_USER) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
*sname = userstring + pname;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pxname(nspace dom, const string& sname, string* pname)
|
||||
{
|
||||
if (sname.find("user.") != 0) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
*pname = sname.substr(userstring.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace pxattr
|
||||
|
||||
#else // Testing / driver ->
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "pxattr.h"
|
||||
|
||||
static char *thisprog;
|
||||
static char usage [] =
|
||||
"pxattr [-h] -n name [-v value] pathname...\n"
|
||||
"pxattr [-h] -x name pathname...\n"
|
||||
"pxattr [-h] -l pathname...\n"
|
||||
" [-h] : don't follow symbolic links (act on link itself)\n"
|
||||
"pxattr -T: run tests on temp file in current directory"
|
||||
"\n"
|
||||
;
|
||||
static void
|
||||
Usage(void)
|
||||
{
|
||||
fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int op_flags;
|
||||
#define OPT_MOINS 0x1
|
||||
#define OPT_n 0x2
|
||||
#define OPT_v 0x4
|
||||
#define OPT_h 0x8
|
||||
#define OPT_x 0x10
|
||||
#define OPT_l 0x20
|
||||
#define OPT_T 0x40
|
||||
|
||||
static void dotests()
|
||||
{
|
||||
static const char *tfn = "pxattr_testtmp.xyz";
|
||||
static const char *NAMES[] = {"ORG.PXATTR.NAME1", "ORG.PXATTR.N2",
|
||||
"ORG.PXATTR.LONGGGGGGGGisSSSHHHHHHHHHNAME3"};
|
||||
static const char *VALUES[] = {"VALUE1", "VALUE2", "VALUE3"};
|
||||
static bool verbose = true;
|
||||
|
||||
/* Create test file if it doesn't exist, remove all attributes */
|
||||
int fd = open(tfn, O_RDWR|O_CREAT, 0755);
|
||||
if (fd < 0) {
|
||||
perror("open/create");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
if (verbose)
|
||||
fprintf(stdout, "Cleanup old attrs\n");
|
||||
vector<string> names;
|
||||
if (!pxattr::list(tfn, &names)) {
|
||||
perror("pxattr::list");
|
||||
exit(1);
|
||||
}
|
||||
for (vector<string>::const_iterator it = names.begin();
|
||||
it != names.end(); it++) {
|
||||
string value;
|
||||
if (!pxattr::del(fd, *it)) {
|
||||
perror("pxattr::del");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
/* Check that there are no attributes left */
|
||||
names.clear();
|
||||
if (!pxattr::list(tfn, &names)) {
|
||||
perror("pxattr::list");
|
||||
exit(1);
|
||||
}
|
||||
if (names.size() != 0) {
|
||||
fprintf(stderr, "Attributes remain after initial cleanup !\n");
|
||||
for (vector<string>::const_iterator it = names.begin();
|
||||
it != names.end(); it++) {
|
||||
fprintf(stderr, "%s\n", (*it).c_str());
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Create attributes, check existence and value */
|
||||
if (verbose)
|
||||
fprintf(stdout, "Creating extended attributes\n");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!pxattr::set(fd, NAMES[i], VALUES[i])) {
|
||||
perror("pxattr::set");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (verbose)
|
||||
fprintf(stdout, "Checking creation\n");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
string value;
|
||||
if (!pxattr::get(tfn, NAMES[i], &value)) {
|
||||
perror("pxattr::get");
|
||||
exit(1);
|
||||
}
|
||||
if (value.compare(VALUES[i])) {
|
||||
fprintf(stderr, "Wrong value after create !\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete one, check list */
|
||||
if (verbose)
|
||||
fprintf(stdout, "Delete one\n");
|
||||
if (!pxattr::del(tfn, NAMES[1])) {
|
||||
perror("pxattr::del one name");
|
||||
exit(1);
|
||||
}
|
||||
if (verbose)
|
||||
fprintf(stdout, "Check list\n");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
string value;
|
||||
if (!pxattr::get(fd, NAMES[i], &value)) {
|
||||
if (i == 1)
|
||||
continue;
|
||||
perror("pxattr::get");
|
||||
exit(1);
|
||||
} else if (i == 1) {
|
||||
fprintf(stderr, "Name at index 1 still exists after deletion\n");
|
||||
exit(1);
|
||||
}
|
||||
if (value.compare(VALUES[i])) {
|
||||
fprintf(stderr, "Wrong value after delete 1 !\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test the CREATE/REPLACE flags */
|
||||
// Set existing with flag CREATE should fail
|
||||
if (verbose)
|
||||
fprintf(stdout, "Testing CREATE/REPLACE flags use\n");
|
||||
if (pxattr::set(tfn, NAMES[0], VALUES[0], pxattr::PXATTR_CREATE)) {
|
||||
fprintf(stderr, "Create existing with flag CREATE succeeded !\n");
|
||||
exit(1);
|
||||
}
|
||||
// Set new with flag REPLACE should fail
|
||||
if (pxattr::set(tfn, NAMES[1], VALUES[1], pxattr::PXATTR_REPLACE)) {
|
||||
fprintf(stderr, "Create new with flag REPLACE succeeded !\n");
|
||||
exit(1);
|
||||
}
|
||||
// Set new with flag CREATE should succeed
|
||||
if (!pxattr::set(fd, NAMES[1], VALUES[1], pxattr::PXATTR_CREATE)) {
|
||||
fprintf(stderr, "Create new with flag CREATE failed !\n");
|
||||
exit(1);
|
||||
}
|
||||
// Set existing with flag REPLACE should succeed
|
||||
if (!pxattr::set(fd, NAMES[0], VALUES[0], pxattr::PXATTR_REPLACE)) {
|
||||
fprintf(stderr, "Create existing with flag REPLACE failed !\n");
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
unlink(tfn);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void listattrs(const string& path)
|
||||
{
|
||||
std::cout << "Path: " << path << std::endl;
|
||||
vector<string> names;
|
||||
if (!pxattr::list(path, &names)) {
|
||||
perror("pxattr::list");
|
||||
exit(1);
|
||||
}
|
||||
for (vector<string>::const_iterator it = names.begin();
|
||||
it != names.end(); it++) {
|
||||
string value;
|
||||
if (!pxattr::get(path, *it, &value)) {
|
||||
perror("pxattr::get");
|
||||
exit(1);
|
||||
}
|
||||
std::cout << " " << *it << " => " << value << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void setxattr(const string& path, const string& name, const string& value)
|
||||
{
|
||||
if (!pxattr::set(path, name, value)) {
|
||||
perror("pxattr::set");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void printxattr(const string &path, const string& name)
|
||||
{
|
||||
std::cout << "Path: " << path << std::endl;
|
||||
string value;
|
||||
if (!pxattr::get(path, name, &value)) {
|
||||
perror("pxattr::get");
|
||||
exit(1);
|
||||
}
|
||||
std::cout << " " << name << " => " << value << std::endl;
|
||||
}
|
||||
|
||||
void delxattr(const string &path, const string& name)
|
||||
{
|
||||
if (pxattr::del(path, name) < 0) {
|
||||
perror("pxattr::del");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
thisprog = argv[0];
|
||||
argc--; argv++;
|
||||
|
||||
string name, value;
|
||||
|
||||
while (argc > 0 && **argv == '-') {
|
||||
(*argv)++;
|
||||
if (!(**argv))
|
||||
/* Cas du "adb - core" */
|
||||
Usage();
|
||||
while (**argv)
|
||||
switch (*(*argv)++) {
|
||||
case 'T': op_flags |= OPT_T; break;
|
||||
case 'l': op_flags |= OPT_l; break;
|
||||
case 'x': op_flags |= OPT_x; if (argc < 2) Usage();
|
||||
name = *(++argv); argc--;
|
||||
goto b1;
|
||||
case 'n': op_flags |= OPT_n; if (argc < 2) Usage();
|
||||
name = *(++argv); argc--;
|
||||
goto b1;
|
||||
case 'v': op_flags |= OPT_v; if (argc < 2) Usage();
|
||||
value = *(++argv); argc--;
|
||||
goto b1;
|
||||
default: Usage(); break;
|
||||
}
|
||||
b1: argc--; argv++;
|
||||
}
|
||||
|
||||
if (argc < 1 && !(op_flags & OPT_T))
|
||||
Usage();
|
||||
if (op_flags & OPT_l) {
|
||||
while (argc > 0) {
|
||||
listattrs(*argv++);argc--;
|
||||
}
|
||||
} else if (op_flags & OPT_n) {
|
||||
if (op_flags & OPT_v) {
|
||||
while (argc > 0) {
|
||||
setxattr(*argv++, name, value);argc--;
|
||||
}
|
||||
} else {
|
||||
while (argc > 0) {
|
||||
printxattr(*argv++, name);argc--;
|
||||
}
|
||||
}
|
||||
} else if (op_flags & OPT_x) {
|
||||
while (argc > 0) {
|
||||
delxattr(*argv++, name);argc--;
|
||||
}
|
||||
} else if (op_flags & OPT_T) {
|
||||
dotests();
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
#endif // Testing pxattr
|
||||
|
||||
#endif // Supported systems.
|
||||
136
src/utils/pxattr.h
Normal file
136
src/utils/pxattr.h
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef _pxattr_h_included_
|
||||
#define _pxattr_h_included_
|
||||
|
||||
/* @(#$Id: pxattr.h,v 1.5 2009-01-20 13:48:34 dockes Exp $ (C) 2009 J.F.Dockes
|
||||
Copyright (c) 2009 Jean-Francois Dockes
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* Provide a uniform C++ API for extended file attributes on Linux/FreeBSD
|
||||
* and MacOSX.
|
||||
*
|
||||
* We only deal with user attributes. Other namespaces are very
|
||||
* system-specific and would be difficult to use in a portable way.
|
||||
*
|
||||
* Linux and FreeBSD treat differently the attributes name space
|
||||
* segmentation: Linux uses the first name segment ("user.", "system.", ...),
|
||||
* FreeBSD uses an enumeration.
|
||||
*
|
||||
* We handle this by using only domain-internal names in the interface:
|
||||
* that is, the caller specifies the names as, ie, 'org.myapp.somename'
|
||||
* not 'user.org.myapp.somename'. pxattr will deal with adding/removing
|
||||
* the 'user.' part as needed.
|
||||
*
|
||||
* MacOsX does not segment the attribute name space.
|
||||
*
|
||||
* In order to avoid conflicts, it is recommended that attributes
|
||||
* names be chosen in a "reverse dns" fashion, ie:
|
||||
* org.recoll.indexing.status
|
||||
*
|
||||
* The interface provided should work the same way on all 3 systems,
|
||||
* it papers over such differences as the "list" output format,
|
||||
* the existence of CREATE/UPDATE distinctions, etc.
|
||||
*
|
||||
* Diagnostics: all functions return false on error, and preserve the errno
|
||||
* value or set it as appropriate.
|
||||
*
|
||||
* For path-based interfaces, the PXATTR_NOFOLLOW flag can be set to decide if
|
||||
* symbolic links will be acted on or followed.
|
||||
*/
|
||||
namespace pxattr {
|
||||
/** nspace might be used in the future if we support multiple namespaces.*/
|
||||
enum nspace {
|
||||
/** User name space */
|
||||
PXATTR_USER
|
||||
};
|
||||
|
||||
/** Flags can modify the behaviour of some methods */
|
||||
enum flags {PXATTR_NONE=0,
|
||||
/** Act on link instead of following it */
|
||||
PXATTR_NOFOLLOW = 1,
|
||||
/** Fail if existing */
|
||||
PXATTR_CREATE=2,
|
||||
/** Fail if new */
|
||||
PXATTR_REPLACE=4
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the named attribute from path.
|
||||
*/
|
||||
bool get(const string& path, const string& name, string* value,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
/**
|
||||
* Retrieve the named attribute from open file.
|
||||
*/
|
||||
bool get(int fd, const string& name, string* value,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
/**
|
||||
* Set the named attribute on path.
|
||||
*/
|
||||
bool set(const string& path, const string& name, const string& value,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
/**
|
||||
* Set the named attribute on open file.
|
||||
*/
|
||||
bool set(int fd, const string& name, const string& value,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
/**
|
||||
* Delete the named attribute from path.
|
||||
*/
|
||||
bool del(const string& path, const string& name,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
/**
|
||||
* Delete the named attribute from open file.
|
||||
*/
|
||||
bool del(int fd, const string& name,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
/**
|
||||
* List attribute names from path.
|
||||
*/
|
||||
bool list(const string& path, vector<string>* names,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
/**
|
||||
* List attribute names from open file.
|
||||
*/
|
||||
bool list(int fd, vector<string>* names,
|
||||
flags flags = PXATTR_NONE, nspace dom = PXATTR_USER);
|
||||
|
||||
/**
|
||||
* Compute actual/system attribute name from external name
|
||||
* (ie: myattr->user.myattr)
|
||||
*/
|
||||
bool sysname(nspace dom, const string& pname, string* sname);
|
||||
/**
|
||||
* Compute external name from actual/system name
|
||||
* (ie: user.myattr->myattr)
|
||||
*/
|
||||
bool pxname(nspace dom, const string& sname, string* pname);
|
||||
}
|
||||
|
||||
|
||||
#endif /* _pxattr_h_included_ */
|
||||
Loading…
x
Reference in New Issue
Block a user