From 229645a0e29134808db25b77fba03998a3077e9f Mon Sep 17 00:00:00 2001 From: dockes Date: Wed, 21 Jan 2009 13:55:12 +0000 Subject: [PATCH] added optional extended file attributes support --- src/common/autoconfig.h.in | 3 + src/common/rclconfig.cpp | 18 +- src/common/rclconfig.h | 5 +- src/configure | 28 ++ src/configure.ac | 14 + src/internfile/internfile.cpp | 24 +- src/internfile/mh_exec.h | 1 + src/internfile/mh_html.cpp | 1 + src/internfile/mh_mail.cpp | 1 + src/internfile/mh_mbox.cpp | 1 + src/internfile/mh_text.cpp | 1 + src/internfile/mh_unknown.h | 3 +- src/internfile/mimehandler.cpp | 41 +- src/internfile/mimehandler.h | 25 +- src/lib/Makefile | 10 +- src/lib/mkMake | 1 + src/sampleconf/fields | 6 + src/utils/pxattr.cpp | 736 +++++++++++++++++++++++++++++++++ src/utils/pxattr.h | 136 ++++++ 19 files changed, 1044 insertions(+), 11 deletions(-) create mode 100644 src/utils/pxattr.cpp create mode 100644 src/utils/pxattr.h diff --git a/src/common/autoconfig.h.in b/src/common/autoconfig.h.in index 34ceb011..578a7852 100644 --- a/src/common/autoconfig.h.in +++ b/src/common/autoconfig.h.in @@ -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 diff --git a/src/common/rclconfig.cpp b/src/common/rclconfig.cpp index 5ecf6307..d0ada65c 100644 --- a/src/common/rclconfig.cpp +++ b/src/common/rclconfig.cpp @@ -530,6 +530,15 @@ bool RclConfig::readFieldsConfig(const string& cnferrloc) } } + // Extended file attribute to field translations + listxattrs = m_fields->getNames("xattrtofields"); + for (list::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(*(r.m_conf)); @@ -891,6 +901,12 @@ void RclConfig::initFrom(const RclConfig& r) mimeconf = new ConfStack(*(r.mimeconf)); if (r.mimeview) mimeview = new ConfStack(*(r.mimeview)); + if (r.m_fields) + m_fields = new ConfStack(*(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; diff --git a/src/common/rclconfig.h b/src/common/rclconfig.h index 2c2fd727..4ce03c73 100644 --- a/src/common/rclconfig.h +++ b/src/common/rclconfig.h @@ -157,7 +157,9 @@ class RclConfig { const set& getStoredFields() {return m_storedFields;} /** Get canonic name for possible alias */ string fieldCanon(const string& fld); - + /** Get xattr name to field names translations */ + const map& getXattrToField() {return m_xattrtofld;} + /** mimeview: get/set external viewer exec string(s) for mimetype(s) */ string getMimeViewerDef(const string &mimetype); bool getMimeViewerDefs(vector >&); @@ -205,6 +207,7 @@ class RclConfig { map m_fldtopfx; map m_aliastocanon; set m_storedFields; + map m_xattrtofld; void *m_stopsuffixes; unsigned int m_maxsufflen; diff --git a/src/configure b/src/configure index 043b5f57..c4f4dd74 100755 --- a/src/configure +++ b/src/configure @@ -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 diff --git a/src/configure.ac b/src/configure.ac index ded83238..21cd1eeb 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -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 diff --git a/src/internfile/internfile.cpp b/src/internfile/internfile.cpp index da662584..b435ea11 100644 --- a/src/internfile/internfile.cpp +++ b/src/internfile/internfile.cpp @@ -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 #include @@ -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& docdata = df->get_meta_data(); + if (df == 0) { + //?? + LOGERR(("FileInterner::dijontorcl: null top handler ??\n")); + return false; + } + const map& docdata = df->get_meta_data(); for (map::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(df); + if (rf != 0) { + const map& ffa = rf->getFieldsFromAttrs(); + for (map::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& docdata = + const map& docdata = m_handlers.back()->get_meta_data(); string charset, mimetype; getKeyValue(docdata, keycs, charset); diff --git a/src/internfile/mh_exec.h b/src/internfile/mh_exec.h index d6d2f1b4..e137e9be 100644 --- a/src/internfile/mh_exec.h +++ b/src/internfile/mh_exec.h @@ -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; diff --git a/src/internfile/mh_html.cpp b/src/internfile/mh_html.cpp index 48cc173c..6d9bbe96 100644 --- a/src/internfile/mh_html.cpp +++ b/src/internfile/mh_html.cpp @@ -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())); diff --git a/src/internfile/mh_mail.cpp b/src/internfile/mh_mail.cpp index 789f16af..35b8b56c 100644 --- a/src/internfile/mh_mail.cpp +++ b/src/internfile/mh_mail.cpp @@ -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; diff --git a/src/internfile/mh_mbox.cpp b/src/internfile/mh_mbox.cpp index f238be5b..7410dc31 100644 --- a/src/internfile/mh_mbox.cpp +++ b/src/internfile/mh_mbox.cpp @@ -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); diff --git a/src/internfile/mh_text.cpp b/src/internfile/mh_text.cpp index c41430d6..4b4d1159 100644 --- a/src/internfile/mh_text.cpp +++ b/src/internfile/mh_text.cpp @@ -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)) { diff --git a/src/internfile/mh_unknown.h b/src/internfile/mh_unknown.h index 8d0340c2..ade9e981 100644 --- a/src/internfile/mh_unknown.h +++ b/src/internfile/mh_unknown.h @@ -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&) { diff --git a/src/internfile/mimehandler.cpp b/src/internfile/mimehandler.cpp index 9c821f9a..d704c0ee 100644 --- a/src/internfile/mimehandler.cpp +++ b/src/internfile/mimehandler.cpp @@ -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 #include #include - +#include 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 xnames; + if (!pxattr::list(path, &xnames)) { + LOGERR(("xattrToMeta: pxattr::list failed, errno %d\n", errno)); + return false; + } + const map& xtof = rclconfig->getXattrToField(); + for (vector::const_iterator it = xnames.begin(); + it != xnames.end(); it++) { + map::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 o_handlers; diff --git a/src/internfile/mimehandler.h b/src/internfile/mimehandler.h index 10dd3db8..a0a7d6b1 100644 --- a/src/internfile/mimehandler.h +++ b/src/internfile/mimehandler.h @@ -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 #include using std::string; using std::list; +#ifdef RCL_USE_XATTR +#include +using std::map; +#endif // RCL_USE_XATTR #include @@ -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& 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 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_ */ diff --git a/src/lib/Makefile b/src/lib/Makefile index 9e619cb4..a1f71972 100644 --- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -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 diff --git a/src/lib/mkMake b/src/lib/mkMake index ed5eb2a9..609c7249 100755 --- a/src/lib/mkMake +++ b/src/lib/mkMake @@ -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 \ diff --git a/src/sampleconf/fields b/src/sampleconf/fields index e54f60ab..7c372613 100644 --- a/src/sampleconf/fields +++ b/src/sampleconf/fields @@ -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] diff --git a/src/utils/pxattr.cpp b/src/utils/pxattr.cpp new file mode 100644 index 00000000..18435eee --- /dev/null +++ b/src/utils/pxattr.cpp @@ -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 +#include +#include +#include + +#if defined(__FreeBSD__) +#include +#include +#elif defined(__gnu_linux__) +#include +#elif defined(__APPLE__) +#include +#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* 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* names, flags flags, nspace dom) +{ + return list(-1, path, names, flags, dom); +} +bool list(int fd, vector* 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 +#include +#include +#include +#include +#include + +#include + +#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 names; + if (!pxattr::list(tfn, &names)) { + perror("pxattr::list"); + exit(1); + } + for (vector::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::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 names; + if (!pxattr::list(path, &names)) { + perror("pxattr::list"); + exit(1); + } + for (vector::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. diff --git a/src/utils/pxattr.h b/src/utils/pxattr.h new file mode 100644 index 00000000..71536c76 --- /dev/null +++ b/src/utils/pxattr.h @@ -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 +#include +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* names, + flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); + /** + * List attribute names from open file. + */ + bool list(int fd, vector* 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_ */