/* 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. */ /** \file pxattr.cpp \brief Portable External Attributes API */ // PXALINUX: platforms like kfreebsd which aren't linux but use the // same xattr interface #if defined(__linux__) || \ (defined(__FreeBSD_kernel__)&&defined(__GLIBC__)&&!defined(__FreeBSD__)) ||\ defined(__CYGWIN__) #define PXALINUX #endif // If the platform is not known yet, let this file be empty instead of // breaking the compile, this will let the build work if the rest of // the software is not actually calling us. If it does call us, this // will bring attention to the necessity of a port. // // If the platform is known not to support extattrs (e.g.__OpenBSD__), // just let the methods return errors (like they would on a non-xattr // fs on e.g. linux) #if defined(__DragonFly__) || defined(__OpenBSD__) #define HAS_NO_XATTR #endif #if defined(__FreeBSD__) || defined(PXALINUX) || defined(__APPLE__) \ || defined(HAS_NO_XATTR) #ifndef TEST_PXATTR #include #include #include #include #if defined(__FreeBSD__) #include #include #elif defined(PXALINUX) #include #elif defined(__APPLE__) #include #elif defined(HAS_NO_XATTR) #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(PXALINUX) 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); } #else errno = ENOTSUP; #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(PXALINUX) 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); } #else errno = ENOTSUP; #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(PXALINUX) 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); } #else errno = ENOTSUP; #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; buf.buf[ret] = 0; 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(PXALINUX) 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); } #else errno = ENOTSUP; #endif if (ret < 0) return false; 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); } #if defined(PXALINUX) || defined(COMPAT1) static const string userstring("user."); #else static const string userstring(""); #endif 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 (!userstring.empty() && sname.find(userstring) != 0) { errno = EINVAL; return false; } *pname = sname.substr(userstring.length()); return true; } } // namespace pxattr #else // TEST_PXATTR Testing / driver -> #include "pxattr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; static int antiverbose; static void printsyserr(const string& msg) { if (antiverbose >= 2) return; cerr << msg << " " << strerror(errno) << endl; } #define message(X) \ { \ if (antiverbose == 0) { \ cout << X; \ } \ } static void dotests(); // \-quote character c in input \ -> \\, nl -> \n cr -> \rc -> \c static void quote(const string& in, string& out, int c) { out.clear(); for (string::const_iterator it = in.begin(); it != in.end(); it++) { if (*it == '\\') { out += "\\\\"; } else if (*it == "\n"[0]) { out += "\\n"; } else if (*it == "\r"[0]) { out += "\\r"; } else if (*it == c) { out += "\\"; out += c; } else { out += *it; } } } // \-unquote input \n -> nl, \r -> cr, \c -> c static void unquote(const string& in, string& out) { out.clear(); for (unsigned int i = 0; i < in.size(); i++) { if (in[i] == '\\') { if (i == in.size() -1) { out += in[i]; } else { int c = in[++i]; switch (c) { case 'n': out += "\n";break; case 'r': out += "\r";break; default: out += c; } } } else { out += in[i]; } } } // Find first unquoted c in input: c preceded by odd number of backslashes string::size_type find_first_unquoted(const string& in, int c) { int q = 0; for (unsigned int i = 0;i < in.size(); i++) { if (in[i] == '\\') { q++; } else if (in[i] == c) { if (q&1) { // quoted q = 0; } else { return i; } } else { q = 0; } } return string::npos; } static const string PATH_START("Path: "); static bool listattrs(const string& path) { vector names; if (!pxattr::list(path, &names)) { if (errno == ENOENT) { return false; } printsyserr("pxattr::list"); exit(1); } if (names.empty()) { return true; } // Sorting the names would not be necessary but it makes easier comparing // backups sort(names.begin(), names.end()); string quoted; quote(path, quoted, 0); message(PATH_START << quoted << endl); for (vector::const_iterator it = names.begin(); it != names.end(); it++) { string value; if (!pxattr::get(path, *it, &value)) { if (errno == ENOENT) { return false; } printsyserr("pxattr::get"); exit(1); } quote(*it, quoted, '='); message(" " << quoted << "="); quote(value, quoted, 0); message(quoted << endl); } return true; } bool setxattr(const string& path, const string& name, const string& value) { if (!pxattr::set(path, name, value)) { printsyserr("pxattr::set"); return false; } return true; } bool printxattr(const string &path, const string& name) { string value; if (!pxattr::get(path, name, &value)) { if (errno == ENOENT) { return false; } printsyserr("pxattr::get"); return false; } message(PATH_START << path << endl); message(" " << name << " => " << value << endl); return true; } bool delxattr(const string &path, const string& name) { if (pxattr::del(path, name) < 0) { printsyserr("pxattr::del"); return false; } return true; } // Restore xattrs stored in file created by pxattr -lR output static void restore(const char *backupnm) { istream *input; ifstream fin; if (!strcmp(backupnm, "stdin")) { input = &cin; } else { fin.open(backupnm, ios::in); input = &fin; } bool done = false; int linenum = 0; string path; map attrs; while (!done) { string line; getline(*input, line); if (!input->good()) { if (input->bad()) { cerr << "Input I/O error" << endl; exit(1); } done = true; } else { linenum++; } // message("Got line " << linenum << " : [" << line << "] done " << // done << endl); if (line.find(PATH_START) == 0 || done) { if (!path.empty() && !attrs.empty()) { for (map::const_iterator it = attrs.begin(); it != attrs.end(); it++) { setxattr(path, it->first, it->second); } } if (!done) { line = line.substr(PATH_START.size(), string::npos); unquote(line, path); attrs.clear(); } } else if (line.empty()) { continue; } else { // Should be attribute line if (line[0] != ' ') { cerr << "Found bad line (no space) at " << linenum << endl; exit(1); } string::size_type pos = find_first_unquoted(line, '='); if (pos == string::npos || pos < 2 || pos >= line.size()) { cerr << "Found bad line at " << linenum << endl; exit(1); } string qname = line.substr(1, pos-1); pair entry; unquote(qname, entry.first); unquote(line.substr(pos+1), entry.second); attrs.insert(entry); } } } static char *thisprog; static char usage [] = "pxattr [-hs] -n name pathname [...] : show value for name\n" "pxattr [-hs] -n name -r regexp pathname [...] : test value against regexp\n" "pxattr [-hs] -n name -v value pathname [...] : add/replace attribute\n" "pxattr [-hs] -x name pathname [...] : delete attribute\n" "pxattr [-hs] [-l] [-R] pathname [...] : list attribute names and values\n" " For all the options above, if no pathname arguments are given, pxattr\n" " will read file names on stdin, one per line.\n" " [-h] : don't follow symbolic links (act on link itself)\n" " [-R] : recursive listing. Args should be directory(ies)\n" " [-s] : be silent. With one option stdout is suppressed, with 2 stderr too\n" "pxattr -S Restore xattrs from file created by pxattr -lR output\n" " if backupfile is 'stdin', reads from stdin\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_h 0x2 #define OPT_l 0x4 #define OPT_n 0x8 #define OPT_r 0x10 #define OPT_R 0x20 #define OPT_S 0x40 #define OPT_T 0x80 #define OPT_s 0x100 #define OPT_v 0x200 #define OPT_x 0x400 // Static values for ftw static string name, value; bool regex_test(const char *path, regex_t *preg) { string value; if (!pxattr::get(path, name, &value)) { if (errno == ENOENT) { return false; } printsyserr("pxattr::get"); return false; } int ret = regexec(preg, value.c_str(), 0, 0, 0); if (ret == 0) { message(path << endl); return true; } else if (ret == REG_NOMATCH) { return false; } else { char errmsg[200]; regerror(ret, preg, errmsg, 200); errno = 0; printsyserr("regexec"); return false; } } bool processfile(const char* fn, const struct stat *, int) { //message("processfile " << fn << " opflags " << op_flags << endl); if (op_flags & OPT_l) { return listattrs(fn); } else if (op_flags & OPT_n) { if (op_flags & OPT_v) { return setxattr(fn, name, value); } else { return printxattr(fn, name); } } else if (op_flags & OPT_x) { return delxattr(fn, name); } Usage(); } int ftwprocessfile(const char* fn, const struct stat *sb, int typeflag) { processfile(fn, sb, typeflag); return 0; } int main(int argc, char **argv) { const char *regexp_string; thisprog = argv[0]; argc--; argv++; while (argc > 0 && **argv == '-') { (*argv)++; if (!(**argv)) /* Cas du "adb - core" */ Usage(); while (**argv) switch (*(*argv)++) { case 'l': op_flags |= OPT_l; break; case 'n': op_flags |= OPT_n; if (argc < 2) Usage(); name = *(++argv); argc--; goto b1; case 'R': op_flags |= OPT_R; break; case 'r': op_flags |= OPT_r; if (argc < 2) Usage(); regexp_string = *(++argv); argc--; goto b1; case 's': antiverbose++; break; case 'S': op_flags |= OPT_S; break; case 'T': op_flags |= OPT_T; break; case 'v': op_flags |= OPT_v; if (argc < 2) Usage(); value = *(++argv); argc--; goto b1; case 'x': op_flags |= OPT_x; if (argc < 2) Usage(); name = *(++argv); argc--; goto b1; default: Usage(); break; } b1: argc--; argv++; } if (op_flags & OPT_T) { if (argc > 0) Usage(); dotests(); exit(0); } if ((op_flags & OPT_r) && !(op_flags & OPT_n)) { Usage(); } if (op_flags & OPT_S) { if (argc != 1) Usage(); restore(argv[0]); exit(0); } regex_t regexp; if (op_flags & OPT_r) { int err = regcomp(®exp, regexp_string, REG_NOSUB|REG_EXTENDED); if (err) { char errmsg[200]; regerror(err, ®exp, errmsg, 200); cerr << "regcomp(" << regexp_string << ") error: " << errmsg << endl; exit(1); } } // Default option is 'list' if ((op_flags&(OPT_l|OPT_n|OPT_x)) == 0) op_flags |= OPT_l; bool readstdin = false; if (argc == 0) readstdin = true; int exitvalue = 0; for (;;) { const char *fn = 0; if (argc > 0) { fn = *argv++; argc--; } else if (readstdin) { static char filename[1025]; if (!fgets(filename, 1024, stdin)) break; filename[strlen(filename)-1] = 0; fn = filename; } else break; if (op_flags & OPT_R) { if (ftw(fn, ftwprocessfile, 20)) exit(1); } else if (op_flags & OPT_r) { if (!regex_test(fn, ®exp)) { exitvalue = 1; } } else { if (!processfile(fn, 0, 0)) { exitvalue = 1; } } } exit(exitvalue); } static void fatal(const string& s) { printsyserr(s.c_str()); exit(1); } static bool testbackups() { static const char *top = "ttop"; static const char *d1 = "d1"; static const char *d2 = "d2"; static const char *tfn1 = "tpxattr1.txt"; static const char *tfn2 = "tpxattr2.txt"; static const char *dump = "attrdump.txt"; static const char *NAMES[] = {"ORG.PXATTR.NAME1", "ORG=PXATTR\"=\\=\n", "=", "Name4"}; static const char *VALUES[] = {"VALUE1", "VALUE2", "VALUE3=VALUE3equal", "VALUE4\n is more like" " normal text\n with new lines and \"\\\" \\\" backslashes"}; static const int nattrs = sizeof(NAMES) / sizeof(char *); if (mkdir(top, 0777)) fatal("Cant mkdir ttop"); if (chdir(top)) fatal("cant chdir ttop"); if (mkdir(d1, 0777) || mkdir(d2, 0777)) fatal("Can't mkdir ttdop/dx\n"); if (chdir(d1)) fatal("chdir d1"); int fd; if ((fd = open(tfn1, O_RDWR|O_CREAT, 0755)) < 0) fatal("create d1/tpxattr1.txt"); /* Set attrs */ for (int i = 0; i < nattrs; i++) { if (!pxattr::set(fd, NAMES[i], VALUES[i])) fatal("pxattr::set"); } close(fd); if ((fd = open(tfn2, O_RDWR|O_CREAT, 0755)) < 0) fatal("create d1/tpxattr2.txt"); /* Set attrs */ for (int i = 0; i < nattrs; i++) { if (!pxattr::set(fd, NAMES[i], VALUES[i])) fatal("pxattr::set"); } close(fd); /* Create dump */ string cmd; cmd = string("pxattr -lR . > " ) + dump; if (system(cmd.c_str())) fatal(cmd + " in d1"); if (chdir("../d2")) fatal("chdir ../d2"); if (close(open(tfn1, O_RDWR|O_CREAT, 0755))) fatal("create d2/tpxattr.txt"); if (close(open(tfn2, O_RDWR|O_CREAT, 0755))) fatal("create d2/tpxattr.txt"); cmd = string("pxattr -S ../d1/" ) + dump; if (system(cmd.c_str())) fatal(cmd); cmd = string("pxattr -lR . > " ) + dump; if (system(cmd.c_str())) fatal(cmd + " in d2"); cmd = string("diff ../d1/") + dump + " " + dump; if (system(cmd.c_str())) fatal(cmd); cmd = string("cat ") + dump; system(cmd.c_str()); if (1) { unlink(dump); unlink(tfn1); unlink(tfn2); if (chdir("../d1")) fatal("chdir ../d1"); unlink(dump); unlink(tfn1); unlink(tfn2); if (chdir("../")) fatal("chdir .. 1"); if (rmdir(d1)) fatal("rmdir d1"); if (rmdir(d2)) fatal("rmdir d2"); if (chdir("../")) fatal("chdir .. 2"); if (rmdir(top)) fatal("rmdir ttop"); } return true; } 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"}; /* Create test file if it doesn't exist, remove all attributes */ int fd = open(tfn, O_RDWR|O_CREAT, 0755); if (fd < 0) { printsyserr("open/create"); exit(1); } if (!antiverbose) message("Cleanup old attrs\n"); vector names; if (!pxattr::list(tfn, &names)) { printsyserr("pxattr::list"); exit(1); } for (vector::const_iterator it = names.begin(); it != names.end(); it++) { string value; if (!pxattr::del(fd, *it)) { printsyserr("pxattr::del"); exit(1); } } /* Check that there are no attributes left */ names.clear(); if (!pxattr::list(tfn, &names)) { printsyserr("pxattr::list"); exit(1); } if (names.size() != 0) { errno=0;printsyserr("Attributes remain after initial cleanup !\n"); for (vector::const_iterator it = names.begin(); it != names.end(); it++) { if (antiverbose < 2) cerr << *it << endl; } exit(1); } /* Create attributes, check existence and value */ message("Creating extended attributes\n"); for (int i = 0; i < 3; i++) { if (!pxattr::set(fd, NAMES[i], VALUES[i])) { printsyserr("pxattr::set"); exit(1); } } message("Checking creation\n"); for (int i = 0; i < 3; i++) { string value; if (!pxattr::get(tfn, NAMES[i], &value)) { printsyserr("pxattr::get"); exit(1); } if (value.compare(VALUES[i])) { errno = 0; printsyserr("Wrong value after create !"); exit(1); } } /* Delete one, check list */ message("Delete one\n"); if (!pxattr::del(tfn, NAMES[1])) { printsyserr("pxattr::del one name"); exit(1); } message("Check list\n"); for (int i = 0; i < 3; i++) { string value; if (!pxattr::get(fd, NAMES[i], &value)) { if (i == 1) continue; printsyserr("pxattr::get"); exit(1); } else if (i == 1) { errno=0; printsyserr("Name at index 1 still exists after deletion\n"); exit(1); } if (value.compare(VALUES[i])) { errno = 0; printsyserr("Wrong value after delete 1 !"); exit(1); } } /* Test the CREATE/REPLACE flags */ // Set existing with flag CREATE should fail message("Testing CREATE/REPLACE flags use\n"); if (pxattr::set(tfn, NAMES[0], VALUES[0], pxattr::PXATTR_CREATE)) { errno=0;printsyserr("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)) { errno=0;printsyserr("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)) { errno=0;printsyserr("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)) { errno=0;printsyserr("Create existing with flag REPLACE failed !\n"); exit(1); } close(fd); unlink(tfn); if (testbackups()) exit(0); exit(1); } #endif // Testing pxattr #endif // Supported systems.