xattr_properties.c [plain text]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <xattr_properties.h>
struct defaultList {
const char *eaName;
const char *propList;
int flags; };
#define propFlagsPrefix 0x0001 // The name is a prefix, so only look at that part
static const struct defaultList
defaultPropertyTable[] = {
{ "com.apple.quarantine", "PC", 0 }, { "com.apple.TextEncoding", "PC", 0 }, { "com.apple.metadata:", "P", propFlagsPrefix }, { "com.apple.security.", "N", propFlagsPrefix },
{ XATTR_RESOURCEFORK_NAME, "PC", 0 }, { XATTR_FINDERINFO_NAME, "PC", 0 }, { 0, 0, 0 },
};
struct propertyListMapping {
char enable; char disable; CopyOperationProperties_t value;
};
static const struct propertyListMapping
PropertyListMapTable[] = {
{ 'C', 'c', kCopyOperationPropertyContentDependent },
{ 'P', 'p', kCopyOperationPropertyNoExport },
{ 'N', 'n', kCopyOperationPropertyNeverPreserve },
{ 0, 0, 0 },
};
static const struct divineIntent {
CopyOperationIntent_t intent;
int (^checker)(CopyOperationProperties_t);
} intentTable[] = {
{ CopyOperationIntentCopy, ^(CopyOperationProperties_t props) {
if (props & kCopyOperationPropertyNeverPreserve)
return 0;
return 1;
} },
{ CopyOperationIntentSave, ^(CopyOperationProperties_t props) {
if (props & (kCopyOperationPropertyContentDependent | kCopyOperationPropertyNeverPreserve))
return 0;
return 1;
} },
{ CopyOperationIntentShare, ^(CopyOperationProperties_t props) {
if ((props & (kCopyOperationPropertyNoExport | kCopyOperationPropertyNeverPreserve)) != 0)
return 0;
return 1;
} },
{ 0, 0 },
};
static const char *
nameInDefaultList(const char *eaname)
{
const struct defaultList *retval;
for (retval = defaultPropertyTable; retval->eaName; retval++) {
if ((retval->flags & propFlagsPrefix) != 0 &&
strncmp(retval->eaName, eaname, strlen(retval->eaName)) == 0)
return retval->propList;
if (strcmp(retval->eaName, eaname) == 0)
return retval->propList;
}
return NULL;
}
static const char *
findPropertyList(const char *eaname)
{
const char *ptr = strrchr(eaname, '#');
if (ptr)
return ptr+1;
return NULL;
}
static CopyOperationProperties_t
stringToProperties(const char *proplist)
{
CopyOperationProperties_t retval = 0;
const char *ptr;
for (ptr = proplist; *ptr; ptr++) {
const struct propertyListMapping *mapPtr;
for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) {
if (*ptr == mapPtr->enable) {
retval |= mapPtr->value;
} else if (*ptr == mapPtr->disable) {
retval &= ~mapPtr->value;
}
}
}
return retval;
}
char *
_xattrNameWithProperties(const char *orig, CopyOperationProperties_t propList)
{
char *retval = NULL;
char suffix[66] = { 0 }; char *cur = suffix;
const struct propertyListMapping *mapPtr;
*cur++ = '#';
for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) {
if ((propList & mapPtr->value) != 0) {
*cur++ = mapPtr->enable;
}
if (cur >= (suffix + sizeof(suffix))) {
errno = ENAMETOOLONG;
return NULL;
}
}
if (cur == suffix + 1) {
retval = strdup(orig);
if (retval == NULL)
errno = ENOMEM;
} else {
const char *defaultEntry = NULL;
if ((defaultEntry = nameInDefaultList(orig)) != NULL &&
strcmp(defaultEntry, suffix + 1) == 0) {
retval = strdup(orig);
} else {
asprintf(&retval, "%s%s", orig, suffix);
}
if (retval == NULL) {
errno = ENOMEM;
} else {
if (strlen(retval) > XATTR_MAXNAMELEN) {
free(retval);
retval = NULL;
errno = ENAMETOOLONG;
}
}
}
return retval;
}
CopyOperationProperties_t
_xattrPropertiesFromName(const char *eaname)
{
CopyOperationProperties_t retval = 0;
const char *propList;
propList = findPropertyList(eaname);
if (propList == NULL) {
propList = findPropertyList(eaname);
}
if (propList != NULL) {
retval = stringToProperties(propList);
}
return retval;
}
int
_PreserveEA(const char *eaname, CopyOperationIntent_t intent)
{
const struct divineIntent *ip;
CopyOperationProperties_t props;
const char *propList;
if ((propList = findPropertyList(eaname)) == NULL &&
(propList = nameInDefaultList(eaname)) == NULL)
props = 0;
else
props = stringToProperties(propList);
for (ip = intentTable; ip->intent; ip++) {
if (ip->intent == intent) {
return ip->checker(props);
}
}
if ((props & kCopyOperationPropertyNeverPreserve) != 0)
return 0;
return 1; }