#include "extattr_os.h" #ifdef EXTATTR_LINUX #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "flags.h" static void * memstr (void *buf, const char *str, const size_t buflen) { void *p = buf; size_t len = buflen; const size_t slen = strlen(str); /* Ignore empty strings and buffers. */ if ((slen == 0) || (buflen == 0)) p = NULL; while (p && (len >= slen)) { /* * Find the first character of the string, then see if the rest * matches. */ p = memchr(p, str[0], len); if (!p) break; if (memcmp(p, str, slen) == 0) break; /* Next! */ ++p; --len; } return p; } static char * flags2namespace (struct hv *flags) { const char *NAMESPACE_DEFAULT = NAMESPACE_USER; const size_t NAMESPACE_KEYLEN = strlen(NAMESPACE_KEY); SV **psv_ns; char *ns = NULL; if (flags && (psv_ns = hv_fetch(flags, NAMESPACE_KEY, NAMESPACE_KEYLEN, 0))) { char *s; STRLEN len; s = SvPV(*psv_ns, len); ns = malloc(len + 1); if (ns) { strncpy(ns, s, len); ns[len] = '\0'; } } else { ns = strdup(NAMESPACE_DEFAULT); } return ns; } static char * qualify_attrname (const char *attrname, struct hv *flags) { char *res = NULL; char *pNS; size_t reslen; pNS = flags2namespace(flags); if (pNS) { reslen = strlen(pNS) + strlen(attrname) + 2; /* pNS + "." + attrname + nul */ res = malloc(reslen); } if (res) snprintf(res, reslen, "%s.%s", pNS, attrname); if (pNS) free(pNS); return res; } int linux_setxattr (const char *path, const char *attrname, const char *attrvalue, const size_t slen, struct hv *flags) { int ret; char *q; File_ExtAttr_setflags_t setflags; int xflags = 0; setflags = File_ExtAttr_flags2setflags(flags); switch (setflags) { case SET_CREATEIFNEEDED: break; case SET_CREATE: xflags |= XATTR_CREATE; break; case SET_REPLACE: xflags |= XATTR_REPLACE; break; } q = qualify_attrname(attrname, flags); if (q) { ret = setxattr(path, q, attrvalue, slen, xflags); if (ret == -1) ret = -errno; free(q); } else { ret = -ENOMEM; } return ret; } int linux_fsetxattr (const int fd, const char *attrname, const char *attrvalue, const size_t slen, struct hv *flags) { int ret; char *q; File_ExtAttr_setflags_t setflags; int xflags = 0; setflags = File_ExtAttr_flags2setflags(flags); switch (setflags) { case SET_CREATEIFNEEDED: break; case SET_CREATE: xflags |= XATTR_CREATE; break; case SET_REPLACE: xflags |= XATTR_REPLACE; break; } q = qualify_attrname(attrname, flags); if (q) { ret = fsetxattr(fd, q, attrvalue, slen, xflags); if (ret == -1) ret = -errno; free(q); } else { ret = -ENOMEM; } return ret; } int linux_getxattr (const char *path, const char *attrname, void *attrvalue, const size_t slen, struct hv *flags) { int ret; char *q; q = qualify_attrname(attrname, flags); if (q) { ret = getxattr(path, q, attrvalue, slen); if (ret == -1) ret = -errno; free(q); } else { ret = -ENOMEM; } return ret; } int linux_fgetxattr (const int fd, const char *attrname, void *attrvalue, const size_t slen, struct hv *flags) { int ret; char *q; q = qualify_attrname(attrname, flags); if (q) { ret = fgetxattr(fd, q, attrvalue, slen); if (ret == -1) ret = -errno; free(q); } else { ret = -ENOMEM; } return ret; } int linux_removexattr (const char *path, const char *attrname, struct hv *flags) { int ret; char *q; /* XXX: Other flags? */ q = qualify_attrname(attrname, flags); if (q) { ret = removexattr(path, q); if (ret == -1) ret = -errno; free(q); } else { ret = -ENOMEM; } return ret; } int linux_fremovexattr (const int fd, const char *attrname, struct hv *flags) { int ret; char *q; /* XXX: Other flags? */ q = qualify_attrname(attrname, flags); if (q) { ret = fremovexattr(fd, q); if (ret == -1) ret = -errno; free(q); } else { ret = -ENOMEM; } return ret; } static ssize_t attrlist2list (char *sbuf, const size_t slen, char *buf, const size_t buflen, const int iWantNames, const char *pWantNS) { ssize_t sbuiltlen = 0; ssize_t spos = 0; int ret = -1; for (spos = 0; (spos < slen); ) { const char *psrc; char *pNS, *pname; int src_len; /* Get the namespace. */ pNS = &sbuf[spos]; pname = strchr(pNS, '.'); if (!pname) break; /* Point spos at the next attribute. */ spos += strlen(pNS) + 1; *pname = '\0'; ++pname; if (iWantNames) { psrc = pname; /* Name list wanted. Check this is in the right namespace. */ if (strcmp(pNS, pWantNS) != 0) continue; } else { psrc = pNS; /* * Namespace list wanted. Check we haven't already seen * this namespace. */ if (memstr(sbuf, pNS, sbuiltlen) != NULL) continue; } /* * We build the results in sbuf. So sbuf will contain the list * returned by listxattr and the list of namespaces. * We shift the namespaces from the list to the start of the buffer. */ src_len = strlen(psrc) + 1; memmove(&sbuf[sbuiltlen], psrc, src_len); sbuiltlen += src_len; } if (buflen == 0) { /* Return what space is required. */ ret = sbuiltlen; } else if (sbuiltlen <= buflen) { memcpy(buf, sbuf, sbuiltlen); ret = sbuiltlen; } else { ret = -ERANGE; } return ret; } /* XXX: More common code below */ /* XXX: Just return a Perl list? */ ssize_t linux_listxattr (const char *path, char *buf, const size_t buflen, struct hv *flags) { char *pNS; ssize_t ret = 0; pNS = flags2namespace(flags); if (!pNS) { ret = -ENOMEM; } /* * Get a buffer of nul-delimited "namespace.attribute"s, * then extract the attributes into buf. */ if (ret == 0) { ssize_t slen; slen = listxattr(path, buf, 0); if (slen == -1) { ret = -errno; } else if (slen >= 0) { char *sbuf; sbuf = malloc(slen); if (sbuf) { slen = listxattr(path, sbuf, slen); if (slen >= 0) { ret = attrlist2list(sbuf, slen, buf, buflen, 1, pNS); } else { ret = -errno; } } else { ret = -errno; slen = 0; } if (sbuf) free(sbuf); } } if (pNS) free(pNS); return ret; } ssize_t linux_flistxattr (const int fd, char *buf, const size_t buflen, struct hv *flags) { char *pNS; ssize_t ret = 0; pNS = flags2namespace(flags); if (!pNS) { ret = -ENOMEM; } /* * Get a buffer of nul-delimited "namespace.attribute"s, * then extract the attributes into buf. */ if (ret == 0) { ssize_t slen; slen = flistxattr(fd, buf, 0); if (slen == -1) { ret = -errno; } else if (slen >= 0) { char *sbuf; sbuf = malloc(slen); if (sbuf) { slen = flistxattr(fd, sbuf, slen); if (slen >= 0) { ret = attrlist2list(sbuf, slen, buf, buflen, 1, pNS); } else { ret = -errno; } } else { ret = -errno; } if (sbuf) free(sbuf); } } if (pNS) free(pNS); return ret; } ssize_t linux_listxattrns (const char *path, char *buf, const size_t buflen, struct hv *flags) { ssize_t slen; ssize_t ret; /* * Get a buffer of nul-delimited "namespace.attribute"s, * then extract the namespaces into buf. */ slen = listxattr(path, buf, 0); if (slen >= 0) { char *sbuf; sbuf = malloc(slen); if (sbuf) { slen = listxattr(path, sbuf, slen); if (slen >= 0) { ret = attrlist2list(sbuf, slen, buf, buflen, 0, NULL); } else { ret = -errno; } } else { ret = -errno; } if (sbuf) free(sbuf); } else { ret = -errno; } return ret; } ssize_t linux_flistxattrns (const int fd, char *buf, const size_t buflen, struct hv *flags) { ssize_t slen; ssize_t ret; /* * Get a buffer of nul-delimited "namespace.attribute"s, * then extract the namespaces into buf. */ slen = flistxattr(fd, buf, 0); if (slen >= 0) { char *sbuf; sbuf = malloc(slen); if (sbuf) { slen = flistxattr(fd, sbuf, slen); if (slen >= 0) { ret = attrlist2list(sbuf, slen, buf, buflen, 0, NULL); } else { ret = -errno; } } else { ret = -errno; } if (sbuf) free(sbuf); } else { ret = -errno; } return ret; } #endif /* EXTATTR_LINUX */