Files
libguestfs/daemon/xattr.c
Pino Toscano 7630a7bbbd daemon: xattr: simplify the enabling of the linuxxattrs features
Instead of enable them when having one of the two headers for it but
still checking for the HAVE_* availability of each *xattr() function
used, just enable the linuxxattrs as a whole when having any of the
needed headers (like before) and all the needed functions.

This might cause the linuxxattrs to not be available anymore on systems
without the whole set of *xattr() functions implemented, but OTOH it
simplifies the xattr.c implementations.
2014-01-09 18:59:19 +01:00

526 lines
13 KiB
C

/* libguestfs - the guestfsd daemon
* Copyright (C) 2009-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <config.h>
#include <stdio.h>
#include <limits.h>
#include <unistd.h>
#include "guestfs_protocol.h"
#include "daemon.h"
#include "actions.h"
#include "optgroups.h"
#if (defined(HAVE_ATTR_XATTR_H) || defined(HAVE_SYS_XATTR_H)) && \
defined(HAVE_LISTXATTR) && defined(HAVE_LLISTXATTR) && \
defined(HAVE_GETXATTR) && defined(HAVE_LGETXATTR) && \
defined(HAVE_REMOVEXATTR) && defined(HAVE_LREMOVEXATTR) && \
defined(HAVE_SETXATTR) && defined(HAVE_LSETXATTR)
# define HAVE_LINUX_XATTRS
#endif
#ifdef HAVE_LINUX_XATTRS
# ifdef HAVE_ATTR_XATTR_H
# include <attr/xattr.h>
# else
# ifdef HAVE_SYS_XATTR_H
# include <sys/xattr.h>
# endif
# endif
int
optgroup_linuxxattrs_available (void)
{
return 1;
}
static guestfs_int_xattr_list *getxattrs (const 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 (const char *xattr, const char *val, int vallen, const char *path, int (*setxattr) (const char *path, const char *name, const void *value, size_t size, int flags));
static int _removexattr (const char *xattr, const char *path, int (*removexattr) (const char *path, const char *name));
guestfs_int_xattr_list *
do_getxattrs (const char *path)
{
return getxattrs (path, listxattr, getxattr);
}
guestfs_int_xattr_list *
do_lgetxattrs (const char *path)
{
return getxattrs (path, llistxattr, lgetxattr);
}
int
do_setxattr (const char *xattr, const char *val, int vallen, const char *path)
{
return _setxattr (xattr, val, vallen, path, setxattr);
}
int
do_lsetxattr (const char *xattr, const char *val, int vallen, const char *path)
{
return _setxattr (xattr, val, vallen, path, lsetxattr);
}
int
do_removexattr (const char *xattr, const char *path)
{
return _removexattr (xattr, path, removexattr);
}
int
do_lremovexattr (const char *xattr, const char *path)
{
return _removexattr (xattr, path, lremovexattr);
}
static int
compare_xattrs (const void *vxa1, const void *vxa2)
{
const guestfs_int_xattr *xa1 = vxa1;
const guestfs_int_xattr *xa2 = vxa2;
return strcmp (xa1->attrname, xa2->attrname);
}
static guestfs_int_xattr_list *
getxattrs (const 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;
CLEANUP_FREE char *buf = NULL;
size_t i, j;
guestfs_int_xattr_list *r = NULL;
CHROOT_IN;
len = listxattr (path, NULL, 0);
CHROOT_OUT;
if (len == -1) {
reply_with_perror ("listxattr: %s", path);
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: %s", path);
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 < (size_t) 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 < (size_t) 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;
}
if (vlen > XATTR_SIZE_MAX) {
/* The next call to getxattr will fail anyway, so ... */
reply_with_error ("extended attribute is too large");
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;
}
}
/* Sort the entries by attrname. */
qsort (&r->guestfs_int_xattr_list_val[0],
(size_t) r->guestfs_int_xattr_list_len,
sizeof (guestfs_int_xattr),
compare_xattrs);
return r;
error:
if (r) {
if (r->guestfs_int_xattr_list_val) {
size_t k;
for (k = 0; k < r->guestfs_int_xattr_list_len; ++k) {
free (r->guestfs_int_xattr_list_val[k].attrname);
free (r->guestfs_int_xattr_list_val[k].attrval.attrval_val);
}
}
free (r->guestfs_int_xattr_list_val);
}
free (r);
return NULL;
}
static int
_setxattr (const char *xattr, const char *val, int vallen, const char *path,
int (*setxattr) (const char *path, const char *name,
const void *value, size_t size, int flags))
{
int r;
if (vallen > XATTR_SIZE_MAX) {
reply_with_error ("extended attribute is too large");
return -1;
}
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 (const char *xattr, const 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;
}
guestfs_int_xattr_list *
do_internal_lxattrlist (const char *path, char *const *names)
{
guestfs_int_xattr_list *ret = NULL;
size_t i, j;
size_t k, m, nr_attrs;
ssize_t len, vlen;
ret = malloc (sizeof (*ret));
if (ret == NULL) {
reply_with_perror ("malloc");
goto error;
}
ret->guestfs_int_xattr_list_len = 0;
ret->guestfs_int_xattr_list_val = NULL;
for (k = 0; names[k] != NULL; ++k) {
void *newptr;
CLEANUP_FREE char *pathname = NULL;
CLEANUP_FREE char *buf = NULL;
/* Be careful in this loop about which errors cause the whole call
* to abort, and which errors allow us to continue processing
* the call, recording a special "error attribute" in the
* outgoing struct list.
*/
/* XXX This would be easier if the kernel had lgetxattrat. In the
* meantime we use the 'pathname' buffer to store the whole path
* name.
*/
if (asprintf (&pathname, "%s/%s", path, names[k]) == -1) {
reply_with_perror ("asprintf");
goto error;
}
/* Reserve space for the special attribute. */
newptr = realloc (ret->guestfs_int_xattr_list_val,
(ret->guestfs_int_xattr_list_len+1) *
sizeof (guestfs_int_xattr));
if (newptr == NULL) {
reply_with_perror ("realloc");
goto error;
}
ret->guestfs_int_xattr_list_val = newptr;
ret->guestfs_int_xattr_list_len++;
guestfs_int_xattr *entry =
&ret->guestfs_int_xattr_list_val[ret->guestfs_int_xattr_list_len-1];
entry->attrname = NULL;
entry->attrval.attrval_len = 0;
entry->attrval.attrval_val = NULL;
entry->attrname = strdup ("");
if (entry->attrname == NULL) {
reply_with_perror ("strdup");
goto error;
}
CHROOT_IN;
len = llistxattr (pathname, NULL, 0);
CHROOT_OUT;
if (len == -1)
continue; /* not fatal */
buf = malloc (len);
if (buf == NULL) {
reply_with_perror ("malloc");
goto error;
}
CHROOT_IN;
len = llistxattr (pathname, buf, len);
CHROOT_OUT;
if (len == -1)
continue; /* not fatal */
/* What we get from the kernel is a string "foo\0bar\0baz" of length
* len. First count the strings.
*/
nr_attrs = 0;
for (i = 0; i < (size_t) len; i += strlen (&buf[i]) + 1)
nr_attrs++;
newptr =
realloc (ret->guestfs_int_xattr_list_val,
(ret->guestfs_int_xattr_list_len+nr_attrs) *
sizeof (guestfs_int_xattr));
if (newptr == NULL) {
reply_with_perror ("realloc");
goto error;
}
ret->guestfs_int_xattr_list_val = newptr;
ret->guestfs_int_xattr_list_len += nr_attrs;
/* entry[0] is the special attribute,
* entry[1..nr_attrs] are the attributes.
*/
entry = &ret->guestfs_int_xattr_list_val[ret->guestfs_int_xattr_list_len-nr_attrs-1];
for (m = 1; m <= nr_attrs; ++m) {
entry[m].attrname = NULL;
entry[m].attrval.attrval_len = 0;
entry[m].attrval.attrval_val = NULL;
}
for (i = 0, j = 0; i < (size_t) len; i += strlen (&buf[i]) + 1, ++j) {
CHROOT_IN;
vlen = lgetxattr (pathname, &buf[i], NULL, 0);
CHROOT_OUT;
if (vlen == -1) {
reply_with_perror ("getxattr");
goto error;
}
if (vlen > XATTR_SIZE_MAX) {
reply_with_error ("extended attribute is too large");
goto error;
}
entry[j+1].attrname = strdup (&buf[i]);
entry[j+1].attrval.attrval_val = malloc (vlen);
entry[j+1].attrval.attrval_len = vlen;
if (entry[j+1].attrname == NULL ||
entry[j+1].attrval.attrval_val == NULL) {
reply_with_perror ("malloc");
goto error;
}
CHROOT_IN;
vlen = lgetxattr (pathname, &buf[i],
entry[j+1].attrval.attrval_val, vlen);
CHROOT_OUT;
if (vlen == -1) {
reply_with_perror ("getxattr");
goto error;
}
}
char num[16];
snprintf (num, sizeof num, "%zu", nr_attrs);
entry[0].attrval.attrval_len = strlen (num) + 1;
entry[0].attrval.attrval_val = strdup (num);
if (entry[0].attrval.attrval_val == NULL) {
reply_with_perror ("strdup");
goto error;
}
/* Sort the entries by attrname (only for this single file). */
qsort (&entry[1], nr_attrs, sizeof (guestfs_int_xattr), compare_xattrs);
}
return ret;
error:
if (ret) {
if (ret->guestfs_int_xattr_list_val) {
for (k = 0; k < ret->guestfs_int_xattr_list_len; ++k) {
free (ret->guestfs_int_xattr_list_val[k].attrname);
free (ret->guestfs_int_xattr_list_val[k].attrval.attrval_val);
}
free (ret->guestfs_int_xattr_list_val);
}
free (ret);
}
return NULL;
}
char *
do_getxattr (const char *path, const char *name, size_t *size_r)
{
ssize_t r;
char *buf;
size_t len;
CHROOT_IN;
r = getxattr (path, name, NULL, 0);
CHROOT_OUT;
if (r == -1) {
reply_with_perror ("getxattr");
return NULL;
}
len = r;
if (len > XATTR_SIZE_MAX) {
reply_with_error ("extended attribute is too large");
return NULL;
}
buf = malloc (len);
if (buf == NULL) {
reply_with_perror ("malloc");
return NULL;
}
CHROOT_IN;
r = getxattr (path, name, buf, len);
CHROOT_OUT;
if (r == -1) {
reply_with_perror ("getxattr");
free (buf);
return NULL;
}
if (len != (size_t) r) {
reply_with_error ("getxattr: unexpected size (%zu/%zd)", len, r);
free (buf);
return NULL;
}
/* Must set size_r last thing before returning. */
*size_r = len;
return buf; /* caller frees */
}
char *
do_lgetxattr (const char *path, const char *name, size_t *size_r)
{
ssize_t r;
char *buf;
size_t len;
CHROOT_IN;
r = lgetxattr (path, name, NULL, 0);
CHROOT_OUT;
if (r == -1) {
reply_with_perror ("lgetxattr");
return NULL;
}
len = r;
if (len > XATTR_SIZE_MAX) {
reply_with_error ("extended attribute is too large");
return NULL;
}
buf = malloc (len);
if (buf == NULL) {
reply_with_perror ("malloc");
return NULL;
}
CHROOT_IN;
r = lgetxattr (path, name, buf, len);
CHROOT_OUT;
if (r == -1) {
reply_with_perror ("lgetxattr");
free (buf);
return NULL;
}
if (len != (size_t) r) {
reply_with_error ("lgetxattr: unexpected size (%zu/%zd)", len, r);
free (buf);
return NULL;
}
/* Must set size_r last thing before returning. */
*size_r = len;
return buf; /* caller frees */
}
#else /* no HAVE_LINUX_XATTRS */
OPTGROUP_LINUXXATTRS_NOT_AVAILABLE
#endif /* no HAVE_LINUX_XATTRS */