aboutsummaryrefslogtreecommitdiffstats
path: root/ports/linux/xattr/pseudo_wrappers.c
blob: 7fe44a653f957e160e22cc0eded4effa8666767d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* shared functionality for the xattr code */
/* Each of these functions is expecting to get an optional name, and
 * a populated statbuf to use for sending messages to the server.
 */

#define RC_AND_BUF \
	int rc; \
	PSEUDO_STATBUF buf; \
	if (path) { \
		rc = base_lstat(path, &buf); \
	} else { \
		rc = base_fstat(fd, &buf); \
	} \
	if (rc == -1) { \
		return rc; \
	}
	
static ssize_t shared_getxattr(const char *path, int fd, const char *name, void *value, size_t size) {
	RC_AND_BUF

	pseudo_debug(PDBGF_XATTR, "getxattr(%s/%d, %s)\n",
		path ? path : "<no path>", fd, name);
	pseudo_msg_t *result = pseudo_client_op(OP_GET_XATTR, 0, fd, -1, path, &buf, name);
	if (result->result != RESULT_SUCCEED) {
		errno = ENOATTR;
		return -1;
	}

	if (value) {
		pseudo_debug(PDBGF_XATTR, "returned attributes: '%s' (%d bytes)\n",
			result->path, result->pathlen);
		if (size >= result->pathlen) {
			memcpy(value, result->path, result->pathlen);
		} else {
			memcpy(value, result->path, size);
			errno = ERANGE;
		}
	}
	return result->pathlen;
}

static int shared_setxattr(const char *path, int fd, const char *name, const void *value, size_t size, int flags) {
	RC_AND_BUF

	char *combined;
	size_t nlen = strlen(name);
	size_t combined_len = nlen + size + 1;
	combined = malloc(combined_len + 1);
	memcpy(combined, name, nlen);
	combined[nlen] = '\0';
	memcpy(combined + nlen + 1, value, size);
	combined[combined_len] = '\0';

	pseudo_debug(PDBGF_XATTR, "setxattr(%s/%d, %s, %s => %s [%d])\n",
		path ? path : "<no path>", fd, name, (char *) value, combined + nlen + 1, (int) size);

	pseudo_op_t op;
	switch (flags) {
	case XATTR_CREATE:
		op = OP_CREATE_XATTR;
		break;
	case XATTR_REPLACE:
		op = OP_REPLACE_XATTR;
		break;
	default:
		op = OP_SET_XATTR;
		break;
	}

	pseudo_msg_t *result = pseudo_client_op(op, 0, fd, -1, path, &buf, combined, combined_len);
	
	/* we automatically assume success */
	if (op == OP_SET_XATTR) {
		return 0;
	}

	/* CREATE/REPLACE operations can report failure */
	if (!result || result->result == RESULT_FAIL) {
		return -1;
	}

	return 0;
}

static ssize_t shared_listxattr(const char *path, int fd, char *list, size_t size) {
	RC_AND_BUF
	pseudo_msg_t *result = pseudo_client_op(OP_LIST_XATTR, 0, fd, -1, path, &buf);
	if (result->result != RESULT_SUCCEED) {
		pseudo_debug(PDBGF_XATTR, "listxattr: no success.\n");
		errno = ENOATTR;
		return -1;
	}
	if (list) {
		pseudo_debug(PDBGF_XATTR, "listxattr: %d bytes of names, starting '%.*s'\n",
			(int) result->pathlen, (int) result->pathlen, result->path);
		if (size >= result->pathlen) {
			memcpy(list, result->path, result->pathlen);
		} else {
			memcpy(list, result->path, size);
			errno = ERANGE;
		}
	}
	return result->pathlen;
}

static int shared_removexattr(const char *path, int fd, const char *name) {
	RC_AND_BUF
	pseudo_msg_t *result = pseudo_client_op(OP_REMOVE_XATTR, 0, fd, -1, path, &buf, name);

	if (result->result != RESULT_SUCCEED) {
		/* docs say ENOATTR, but I don't have one */
		errno = ENOENT;
		return -1;
	}
	return 0;
}