From 2600359b9b520ab386f069fbbcd656e5cdbd9b76 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 14 Jul 2009 11:50:55 +0100 Subject: [PATCH] Support for Linux extended attributes. This commit adds six calls to support Linux extended attributes. They are: getxattrs list all extended attributes for a file or directory setxattr add/replace an extended attribute removexattr remove an extended attribute lgetxattrs \ lsetxattr (same as above, but operate on symbolic links) lremovexattr / See attr(5) for more information. This also adds support for the FBuffer field type, which maps to an XDR opaque<> or a C (int, char *) pair. --- .gitignore | 1 + daemon/Makefile.am | 1 + daemon/configure.ac | 5 +- daemon/xattr.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/MAX_PROC_NR | 2 +- src/generator.ml | 128 ++++++++++++++++++++++- 6 files changed, 416 insertions(+), 5 deletions(-) create mode 100644 daemon/xattr.c diff --git a/.gitignore b/.gitignore index 4b56f55..87dab68 100644 --- a/.gitignore +++ b/.gitignore @@ -96,6 +96,7 @@ java/com/redhat/et/libguestfs/Stat.java java/com/redhat/et/libguestfs/StatVFS.java java/com/redhat/et/libguestfs/Version.java java/com/redhat/et/libguestfs/VG.java +java/com/redhat/et/libguestfs/XAttr.java java/doc-stamp java/Makefile.inc *.la diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 846a95c..7b8a87e 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -65,6 +65,7 @@ guestfsd_SOURCES = \ umask.c \ upload.c \ wc.c \ + xattr.c \ zero.c \ zerofree.c \ $(top_builddir)/../src/guestfs_protocol.h \ diff --git a/daemon/configure.ac b/daemon/configure.ac index 238532e..2fd1ae9 100644 --- a/daemon/configure.ac +++ b/daemon/configure.ac @@ -63,7 +63,10 @@ AC_CHECK_LIB([portablexdr],[xdrmem_create],[],[ ]) dnl Functions which may not be available in older distributions. -AC_CHECK_FUNCS([futimens]) +AC_CHECK_FUNCS([futimens listxattr llistxattr getxattr lgetxattr setxattr lsetxattr removexattr lremovexattr]) + +dnl Headers. +AC_CHECK_HEADERS([attr/xattr.h]) dnl Produce output files. AC_CONFIG_HEADERS([config.h]) diff --git a/daemon/xattr.c b/daemon/xattr.c new file mode 100644 index 0000000..76f44bb --- /dev/null +++ b/daemon/xattr.c @@ -0,0 +1,284 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include + +#ifdef HAVE_ATTR_XATTR_H +#include + +#include "../src/guestfs_protocol.h" +#include "daemon.h" +#include "actions.h" + +static guestfs_int_xattr_list *getxattrs (char *path, ssize_t (*listxattr) (const char *path, char *list, size_t size), ssize_t (*getxattr) (const char *path, const char *name, void *value, size_t size)); +static int _setxattr (char *xattr, char *val, int vallen, char *path, int (*setxattr) (const char *path, const char *name, const void *value, size_t size, int flags)); +static int _removexattr (char *xattr, char *path, int (*removexattr) (const char *path, const char *name)); + +guestfs_int_xattr_list * +do_getxattrs (char *path) +{ +#if defined(HAVE_LISTXATTR) && defined(HAVE_GETXATTR) + return getxattrs (path, listxattr, getxattr); +#else + reply_with_error ("getxattrs: no support for listxattr and getxattr"); + return NULL; +#endif +} + +guestfs_int_xattr_list * +do_lgetxattrs (char *path) +{ +#if defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR) + return getxattrs (path, llistxattr, lgetxattr); +#else + reply_with_error ("lgetxattrs: no support for llistxattr and lgetxattr"); + return NULL; +#endif +} + +int +do_setxattr (char *xattr, char *val, int vallen, char *path) +{ +#if defined(HAVE_SETXATTR) + return _setxattr (xattr, val, vallen, path, setxattr); +#else + reply_with_error ("setxattr: no support for setxattr"); + return -1; +#endif +} + +int +do_lsetxattr (char *xattr, char *val, int vallen, char *path) +{ +#if defined(HAVE_LSETXATTR) + return _setxattr (xattr, val, vallen, path, lsetxattr); +#else + reply_with_error ("lsetxattr: no support for lsetxattr"); + return -1; +#endif +} + +int +do_removexattr (char *xattr, char *path) +{ +#if defined(HAVE_REMOVEXATTR) + return _removexattr (xattr, path, removexattr); +#else + reply_with_error ("removexattr: no support for removexattr"); + return -1; +#endif +} + +int +do_lremovexattr (char *xattr, char *path) +{ +#if defined(HAVE_LREMOVEXATTR) + return _removexattr (xattr, path, lremovexattr); +#else + reply_with_error ("lremovexattr: no support for lremovexattr"); + return -1; +#endif +} + +static guestfs_int_xattr_list * +getxattrs (char *path, + ssize_t (*listxattr) (const char *path, char *list, size_t size), + ssize_t (*getxattr) (const char *path, const char *name, + void *value, size_t size)) +{ + ssize_t len, vlen; + char *buf = NULL; + int i, j; + guestfs_int_xattr_list *r = NULL; + + NEED_ROOT (NULL); + ABS_PATH (path, NULL); + + CHROOT_IN; + len = listxattr (path, NULL, 0); + CHROOT_OUT; + if (len == -1) { + reply_with_perror ("listxattr"); + goto error; + } + + buf = malloc (len); + if (buf == NULL) { + reply_with_perror ("malloc"); + goto error; + } + + CHROOT_IN; + len = listxattr (path, buf, len); + CHROOT_OUT; + if (len == -1) { + reply_with_perror ("listxattr"); + goto error; + } + + r = calloc (1, sizeof (*r)); + if (r == NULL) { + reply_with_perror ("malloc"); + goto error; + } + + /* What we get from the kernel is a string "foo\0bar\0baz" of length + * len. First count the strings. + */ + r->guestfs_int_xattr_list_len = 0; + for (i = 0; i < len; i += strlen (&buf[i]) + 1) + r->guestfs_int_xattr_list_len++; + + r->guestfs_int_xattr_list_val = + calloc (r->guestfs_int_xattr_list_len, sizeof (guestfs_int_xattr)); + if (r->guestfs_int_xattr_list_val == NULL) { + reply_with_perror ("calloc"); + goto error; + } + + for (i = 0, j = 0; i < len; i += strlen (&buf[i]) + 1, ++j) { + CHROOT_IN; + vlen = getxattr (path, &buf[i], NULL, 0); + CHROOT_OUT; + if (vlen == -1) { + reply_with_perror ("getxattr"); + goto error; + } + + r->guestfs_int_xattr_list_val[j].attrname = strdup (&buf[i]); + r->guestfs_int_xattr_list_val[j].attrval.attrval_val = malloc (vlen); + r->guestfs_int_xattr_list_val[j].attrval.attrval_len = vlen; + + if (r->guestfs_int_xattr_list_val[j].attrname == NULL || + r->guestfs_int_xattr_list_val[j].attrval.attrval_val == NULL) { + reply_with_perror ("malloc"); + goto error; + } + + CHROOT_IN; + vlen = getxattr (path, &buf[i], + r->guestfs_int_xattr_list_val[j].attrval.attrval_val, + vlen); + CHROOT_OUT; + if (vlen == -1) { + reply_with_perror ("getxattr"); + goto error; + } + } + + free (buf); + + return r; + + error: + free (buf); + if (r) { + if (r->guestfs_int_xattr_list_val) + for (i = 0; i < r->guestfs_int_xattr_list_len; ++i) { + free (r->guestfs_int_xattr_list_val[i].attrname); + free (r->guestfs_int_xattr_list_val[i].attrval.attrval_val); + } + free (r->guestfs_int_xattr_list_val); + } + free (r); + return NULL; +} + +static int +_setxattr (char *xattr, char *val, int vallen, char *path, + int (*setxattr) (const char *path, const char *name, + const void *value, size_t size, int flags)) +{ + int r; + + CHROOT_IN; + r = setxattr (path, xattr, val, vallen, 0); + CHROOT_OUT; + if (r == -1) { + reply_with_perror ("setxattr"); + return -1; + } + + return 0; +} + +static int +_removexattr (char *xattr, char *path, + int (*removexattr) (const char *path, const char *name)) +{ + int r; + + CHROOT_IN; + r = removexattr (path, xattr); + CHROOT_OUT; + if (r == -1) { + reply_with_perror ("removexattr"); + return -1; + } + + return 0; +} + +#else /* !HAVE_ATTR_XATTR_H */ + +guestfs_int_xattr_list * +do_getxattrs (char *path) +{ + reply_with_error ("getxattrs: no support for xattrs"); + return NULL; +} + +guestfs_int_xattr_list * +do_lgetxattrs (char *path) +{ + reply_with_error ("lgetxattrs: no support for xattrs"); + return NULL; +} + +int +do_setxattr (char *xattr, char *val, int vallen, char *path) +{ + reply_with_error ("setxattr: no support for xattrs"); + return -1; +} + +int +do_lsetxattr (char *xattr, char *val, int vallen, char *path) +{ + reply_with_error ("lsetxattr: no support for xattrs"); + return -1; +} + +int +do_removexattr (char *xattr, char *path) +{ + reply_with_error ("removexattr: no support for xattrs"); + return -1; +} + +int +do_lremovexattr (char *xattr, char *path) +{ + reply_with_error ("lremovexattr: no support for xattrs"); + return -1; +} + +#endif /* !HAVE_ATTR_XATTR_H */ diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index dee261d..878d5a0 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -140 +146 diff --git a/src/generator.ml b/src/generator.ml index d3040d5..c163b3a 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -2868,6 +2868,65 @@ C must be one of C, C or C. See also: C"); + ("getxattrs", (RStructList ("xattrs", "xattr"), [String "path"]), 141, [], + [], + "list extended attributes of a file or directory", + "\ +This call lists the extended attributes of the file or directory +C. + +At the system call level, this is a combination of the +L and L calls. + +See also: C, L."); + + ("lgetxattrs", (RStructList ("xattrs", "xattr"), [String "path"]), 142, [], + [], + "list extended attributes of a file or directory", + "\ +This is the same as C, but if C +is a symbolic link, then it returns the extended attributes +of the link itself."); + + ("setxattr", (RErr, [String "xattr"; + String "val"; Int "vallen"; (* will be BufferIn *) + String "path"]), 143, [], + [], + "set extended attribute of a file or directory", + "\ +This call sets the extended attribute named C +of the file C to the value C (of length C). +The value is arbitrary 8 bit data. + +See also: C, L."); + + ("lsetxattr", (RErr, [String "xattr"; + String "val"; Int "vallen"; (* will be BufferIn *) + String "path"]), 144, [], + [], + "set extended attribute of a file or directory", + "\ +This is the same as C, but if C +is a symbolic link, then it sets an extended attribute +of the link itself."); + + ("removexattr", (RErr, [String "xattr"; String "path"]), 145, [], + [], + "remove extended attribute of a file or directory", + "\ +This call removes the extended attribute named C +of the file C. + +See also: C, L."); + + ("lremovexattr", (RErr, [String "xattr"; String "path"]), 146, [], + [], + "remove extended attribute of a file or directory", + "\ +This is the same as C, but if C +is a symbolic link, then it removes an extended attribute +of the link itself."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -2883,6 +2942,7 @@ let all_functions_sorted = type field = | FChar (* C 'char' (really, a 7 bit byte). *) | FString (* nul-terminated ASCII string. *) + | FBuffer (* opaque buffer of bytes, (char *, int) pair *) | FUInt32 | FInt32 | FUInt64 @@ -3021,6 +3081,12 @@ let structs = [ "release", FInt64; "extra", FString; ]; + + (* Extended attribute. *) + "xattr", [ + "attrname", FString; + "attrval", FBuffer; + ]; ] (* end of structs *) (* Ugh, Java has to be different .. @@ -3035,6 +3101,7 @@ let java_structs = [ "statvfs", "StatVFS"; "dirent", "Dirent"; "version", "Version"; + "xattr", "XAttr"; ] (* Used for testing language bindings. *) @@ -3448,6 +3515,10 @@ and generate_structs_pod () = | name, (FUInt64|FBytes) -> pr " uint64_t %s;\n" name | name, FInt64 -> pr " int64_t %s;\n" name | name, FString -> pr " char *%s;\n" name + | name, FBuffer -> + pr " /* The next two fields describe a byte array. */\n"; + pr " uint32_t %s_len;\n" name; + pr " char *%s;\n" name | name, FUUID -> pr " /* The next field is NOT nul-terminated, be careful when printing it: */\n"; pr " char %s[32];\n" name @@ -3491,6 +3562,7 @@ and generate_xdr () = List.iter (function | name, FChar -> pr " char %s;\n" name | name, FString -> pr " string %s<>;\n" name + | name, FBuffer -> pr " opaque %s<>;\n" name | name, FUUID -> pr " opaque %s[32];\n" name | name, (FInt32|FUInt32) -> pr " int %s;\n" name | name, (FInt64|FUInt64|FBytes) -> pr " hyper %s;\n" name @@ -3652,6 +3724,9 @@ and generate_structs_h () = function | name, FChar -> pr " char %s;\n" name | name, FString -> pr " char *%s;\n" name + | name, FBuffer -> + pr " uint32_t %s_len;\n" name; + pr " char *%s;\n" name | name, FUUID -> pr " char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name | name, FUInt32 -> pr " uint32_t %s;\n" name | name, FInt32 -> pr " int32_t %s;\n" name @@ -4269,7 +4344,7 @@ and generate_daemon_actions () = pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name; pr " return -1;\n"; pr " }\n"; - | FInt32 | FUInt32 | FUInt64 | FChar -> + | FBuffer | FInt32 | FUInt32 | FUInt64 | FChar -> assert false (* can never be an LVM column *) ); pr " tok = next;\n"; @@ -5052,6 +5127,7 @@ and generate_fish_cmds () = pr "#include \n"; pr "#include \n"; pr "#include \n"; + pr "#include \n"; pr "\n"; pr "#include \n"; pr "#include \"fish.h\"\n"; @@ -5129,7 +5205,7 @@ and generate_fish_cmds () = List.iter ( fun (typ, cols) -> let needs_i = - List.exists (function (_, FUUID) -> true | _ -> false) cols in + List.exists (function (_, (FUUID|FBuffer)) -> true | _ -> false) cols in pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ; pr "{\n"; @@ -5146,6 +5222,14 @@ and generate_fish_cmds () = pr " for (i = 0; i < 32; ++i)\n"; pr " printf (\"%%c\", %s->%s[i]);\n" typ name; pr " printf (\"\\n\");\n" + | name, FBuffer -> + pr " printf (\"%s: \");\n" name; + pr " for (i = 0; i < %s->%s_len; ++i)\n" typ name; + pr " if (isprint (%s->%s[i]))\n" typ name; + pr " printf (\"%%c\", %s->%s[i]);\n" typ name; + pr " else\n"; + pr " printf (\"\\\\x%%02x\", %s->%s[i]);\n" typ name; + pr " printf (\"\\n\");\n" | name, (FUInt64|FBytes) -> pr " printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name | name, FInt64 -> @@ -5661,6 +5745,10 @@ copy_table (char * const * argv) (match col with | name, FString -> pr " v = caml_copy_string (%s->%s);\n" typ name + | name, FBuffer -> + pr " v = caml_alloc_string (%s->%s_len);\n" typ name; + pr " memcpy (String_val (v), %s->%s, %s->%s_len);\n" + typ name typ name | name, FUUID -> pr " v = caml_alloc_string (32);\n"; pr " memcpy (String_val (v), %s->%s, 32);\n" typ name @@ -5841,6 +5929,7 @@ and generate_ocaml_structure_decls () = List.iter ( function | name, FString -> pr " %s : string;\n" name + | name, FBuffer -> pr " %s : string;\n" name | name, FUUID -> pr " %s : string;\n" name | name, (FBytes|FInt64|FUInt64) -> pr " %s : int64;\n" name | name, (FInt32|FUInt32) -> pr " %s : int32;\n" name @@ -6137,6 +6226,9 @@ and generate_perl_struct_list_code typ cols name style n do_cleanups = | name, FUUID -> pr " (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 32), 0);\n" name (String.length name) n name + | name, FBuffer -> + pr " (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, %s->val[i].%s_len), 0);\n" + name (String.length name) n name n name | name, (FBytes|FUInt64) -> pr " (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n" name (String.length name) n name @@ -6176,6 +6268,9 @@ and generate_perl_struct_code typ cols name style n do_cleanups = | name, FString -> pr " PUSHs (sv_2mortal (newSVpv (%s->%s, 0)));\n" n name + | name, FBuffer -> + pr " PUSHs (sv_2mortal (newSVpv (%s->%s, %s->%s_len)));\n" + n name n name | name, FUUID -> pr " PUSHs (sv_2mortal (newSVpv (%s->%s, 32)));\n" n name @@ -6508,6 +6603,10 @@ py_guestfs_close (PyObject *self, PyObject *args) pr " PyDict_SetItemString (dict, \"%s\",\n" name; pr " PyString_FromString (%s->%s));\n" typ name + | name, FBuffer -> + pr " PyDict_SetItemString (dict, \"%s\",\n" name; + pr " PyString_FromStringAndSize (%s->%s, %s->%s_len));\n" + typ name typ name | name, FUUID -> pr " PyDict_SetItemString (dict, \"%s\",\n" name; pr " PyString_FromStringAndSize (%s->%s, 32));\n" @@ -7051,6 +7150,8 @@ and generate_ruby_struct_code typ cols = function | name, FString -> pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name + | name, FBuffer -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name | name, FUUID -> pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, 32));\n" name name | name, (FBytes|FUInt64) -> @@ -7079,6 +7180,8 @@ and generate_ruby_struct_list_code typ cols = function | name, FString -> pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name + | name, FBuffer -> + pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, r->val[i].%s_len));\n" name name name | name, FUUID -> pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name | name, (FBytes|FUInt64) -> @@ -7290,7 +7393,8 @@ public class %s { List.iter ( function | name, FString - | name, FUUID -> pr " public String %s;\n" name + | name, FUUID + | name, FBuffer -> pr " public String %s;\n" name | name, (FBytes|FUInt64|FInt64) -> pr " public long %s;\n" name | name, (FUInt32|FInt32) -> pr " public int %s;\n" name | name, FChar -> pr " public char %s;\n" name @@ -7554,6 +7658,15 @@ and generate_java_struct_return typ jtyp cols = pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; pr " (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n"; pr " }\n"; + | name, FBuffer -> + pr " {\n"; + pr " int len = r->%s_len;\n" name; + pr " char s[len+1];\n"; + pr " memcpy (s, r->%s, len);\n" name; + pr " s[len] = 0;\n"; + pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; + pr " (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n"; + pr " }\n"; | name, (FBytes|FUInt64|FInt64) -> pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name; pr " (*env)->SetLongField (env, jr, fl, r->%s);\n" name; @@ -7588,6 +7701,15 @@ and generate_java_struct_list_return typ jtyp cols = pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; pr " (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n"; pr " }\n"; + | name, FBuffer -> + pr " {\n"; + pr " int len = r->val[i].%s_len;\n" name; + pr " char s[len+1];\n"; + pr " memcpy (s, r->val[i].%s, len);\n" name; + pr " s[len] = 0;\n"; + pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; + pr " (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n"; + pr " }\n"; | name, (FBytes|FUInt64|FInt64) -> pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name; pr " (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name; -- 1.8.3.1