added optional extended file attributes support

This commit is contained in:
dockes 2009-01-21 13:55:12 +00:00
parent 420e258478
commit 229645a0e2
19 changed files with 1044 additions and 11 deletions

View File

@ -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

View File

@ -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;

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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()));

View File

@ -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;

View File

@ -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);

View File

@ -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)) {

View File

@ -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&) {

View File

@ -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;

View File

@ -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_ */

View File

@ -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

View File

@ -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 \

View File

@ -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
View 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
View 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_ */