1 /*
2 * Copyright (c) 2001-2003,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation, either version 2.1 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/xattr.h>
27
28 #include <attr/attributes.h>
29
30 #ifndef ENOATTR
31 # define ENOATTR ENODATA
32 #endif
33
34 #undef MAXNAMELEN
35 #define MAXNAMELEN 256
36 #undef MAXLISTLEN
37 #define MAXLISTLEN 65536
38
39 #undef roundup
40 #define roundup(x,y) ((((x)+((y)-1))/(y))*(y))
41
42 static const char *user_name = "user.";
43 static const char *secure_name = "security.";
44 static const char *trusted_name = "trusted.";
45 static const char *xfsroot_name = "xfsroot.";
46
47 /*
48 * Convert IRIX API components into Linux/XFS API components,
49 * and vice-versa.
50 */
51 static int
52 api_convert(char *name, const char *irixname, int irixflags, int compat)
53 {
54 if (strlen(irixname) >= MAXNAMELEN) {
55 errno = EINVAL;
56 return -1;
57 }
58 if (irixflags & ATTR_ROOT) {
59 if (compat)
60 strcpy(name, xfsroot_name);
61 else
62 strcpy(name, trusted_name);
63 } else if (irixflags & ATTR_SECURE) {
64 strcpy(name, secure_name);
65 } else {
66 strcpy(name, user_name);
67 }
68 strcat(name, irixname);
69 return 0;
70 }
71
72 static int
73 api_unconvert(char *name, const char *linuxname, int irixflags)
74 {
75 int type, length;
76
77 length = strlen(user_name);
78 if (strncmp(linuxname, user_name, length) == 0) {
79 type = 0; /*ATTR_USER*/
80 goto found;
81 }
82 length = strlen(secure_name);
83 if (strncmp(linuxname, secure_name, length) == 0) {
84 type = ATTR_SECURE;
85 goto found;
86 }
87 length = strlen(trusted_name);
88 if (strncmp(linuxname, trusted_name, length) == 0) {
89 type = ATTR_ROOT;
90 goto found;
91 }
92 length = strlen(xfsroot_name);
93 if (strncmp(linuxname, xfsroot_name, length) == 0) {
94 type = ATTR_ROOT;
95 goto found;
96 }
97 return 1;
98
99 found:
100 if ((irixflags & ATTR_SECURE) != 0 && (type != ATTR_SECURE))
101 return 1;
102 if ((irixflags & ATTR_ROOT) != 0 && (type != ATTR_ROOT))
103 return 1;
104 strcpy(name, linuxname + length);
105 return 0;
106 }
107
108
109 int
110 attr_get(const char *path, const char *attrname, char *attrvalue,
111 int *valuelength, int flags)
112 {
113 ssize_t (*get)(const char *, const char *, void *, size_t) =
114 flags & ATTR_DONTFOLLOW ? lgetxattr : getxattr;
115 int c, compat;
116 char name[MAXNAMELEN+16];
117
118 for (compat = 0; compat < 2; compat++) {
119 if ((c = api_convert(name, attrname, flags, compat)) < 0)
120 return c;
121 c = get(path, name, attrvalue, *valuelength);
122 if (c < 0 && (errno == ENOATTR || errno == ENOTSUP))
123 continue;
124 break;
125 }
126 if (c < 0 && errno == ERANGE) {
127 int size = get(path, name, NULL, 0);
128 if (size >= 0) {
129 *valuelength = size;
130 errno = E2BIG;
131 }
132 return c;
133 }
134 if (c < 0)
135 return c;
136 *valuelength = c;
137 return 0;
138 }
139
140 int
141 attr_getf(int fd, const char *attrname, char *attrvalue,
142 int *valuelength, int flags)
143 {
144 int c, compat;
145 char name[MAXNAMELEN+16];
146
147 for (compat = 0; compat < 2; compat++) {
148 if ((c = api_convert(name, attrname, flags, compat)) < 0)
149 return c;
150 c = fgetxattr(fd, name, attrvalue, *valuelength);
151 if (c < 0 && (errno == ENOATTR || errno == ENOTSUP))
152 continue;
153 break;
154 }
155 if (c < 0 && errno == ERANGE) {
156 int size = fgetxattr(fd, name, NULL, 0);
157 if (size >= 0) {
158 *valuelength = size;
159 errno = E2BIG;
160 }
161 return c;
162 }
163 if (c < 0)
164 return c;
165 *valuelength = c;
166 return 0;
167 }
168
169 int
170 attr_set(const char *path, const char *attrname, const char *attrvalue,
171 const int valuelength, int flags)
172 {
173 int c, compat, lflags = 0;
174 char name[MAXNAMELEN+16];
175 void *buffer = (void *)attrvalue;
176
177 if (flags & ATTR_CREATE)
178 lflags = XATTR_CREATE;
179 else if (flags & ATTR_REPLACE)
180 lflags = XATTR_REPLACE;
181
182 for (compat = 0; compat < 2; compat++) {
183 if ((c = api_convert(name, attrname, flags, compat)) < 0)
184 return c;
185 if (flags & ATTR_DONTFOLLOW)
186 c = lsetxattr(path, name, buffer, valuelength, lflags);
187 else
188 c = setxattr(path, name, buffer, valuelength, lflags);
189 if (c < 0 && (errno == ENOATTR || errno == ENOTSUP))
190 continue;
191 break;
192 }
193 return c;
194 }
195
196 int
197 attr_setf(int fd, const char *attrname,
198 const char *attrvalue, const int valuelength, int flags)
199 {
200 int c, compat, lflags = 0;
201 char name[MAXNAMELEN+16];
202 void *buffer = (void *)attrvalue;
203
204 if (flags & ATTR_CREATE)
205 lflags = XATTR_CREATE;
206 else if (flags & ATTR_REPLACE)
207 lflags = XATTR_REPLACE;
208
209 for (compat = 0; compat < 2; compat++) {
210 if ((c = api_convert(name, attrname, flags, compat)) < 0)
211 return c;
212 c = fsetxattr(fd, name, buffer, valuelength, lflags);
213 if (c < 0 && (errno == ENOATTR || errno == ENOTSUP))
214 continue;
215 break;
216 }
217 return c;
218 }
219
220 int
221 attr_remove(const char *path, const char *attrname, int flags)
222 {
223 int c, compat;
224 char name[MAXNAMELEN+16];
225
226 for (compat = 0; compat < 2; compat++) {
227 if ((c = api_convert(name, attrname, flags, compat)) < 0)
228 return c;
229 if (flags & ATTR_DONTFOLLOW)
230 c = lremovexattr(path, name);
231 else
232 c = removexattr(path, name);
233 if (c < 0 && (errno == ENOATTR || errno == ENOTSUP))
234 continue;
235 break;
236 }
237 return c;
238 }
239
240 int
241 attr_removef(int fd, const char *attrname, int flags)
242 {
243 int c, compat;
244 char name[MAXNAMELEN+16];
245
246 for (compat = 0; compat < 2; compat++) {
247 if ((c = api_convert(name, attrname, flags, compat)) < 0)
248 return c;
249 c = fremovexattr(fd, name);
250 if (c < 0 && (errno == ENOATTR || errno == ENOTSUP))
251 continue;
252 break;
253 }
254 return c;
255 }
256
257
258 /*
259 * Helper routine for attr_list functions.
260 */
261
262 static int
263 attr_list_pack(const char *name, const int valuelen,
264 char *buffer, const int buffersize,
265 int *start_offset, int *end_offset)
266 {
267 attrlist_ent_t *aentp;
268 attrlist_t *alist = (attrlist_t *)buffer;
269 int size = roundup(strlen(name) + 1 + sizeof(aentp->a_valuelen), 8);
270
271 if ((*end_offset - size) < (*start_offset + sizeof(alist->al_count))) {
272 alist->al_more = 1;
273 return 1;
274 }
275
276 *end_offset -= size;
277 aentp = (attrlist_ent_t *)&buffer[ *end_offset ];
278 aentp->a_valuelen = valuelen;
279 strncpy(aentp->a_name, name, size - sizeof(aentp->a_valuelen));
280
281 *start_offset += sizeof(alist->al_offset);
282 alist->al_offset[alist->al_count] = *end_offset;
283 alist->al_count++;
284 return 0;
285 }
286
287 int
288 attr_list(const char *path, char *buffer, const int buffersize, int flags,
289 attrlist_cursor_t *cursor)
290 {
291 const char *l;
292 int length, vlength, count = 0;
293 char lbuf[MAXLISTLEN+1];
294 char name[MAXNAMELEN+16];
295 int start_offset, end_offset;
296
297 if (buffersize < sizeof(attrlist_t)) {
298 errno = EINVAL;
299 return -1;
300 }
301 memset(buffer, 0, sizeof(attrlist_t));
302
303 if (flags & ATTR_DONTFOLLOW)
304 length = llistxattr(path, lbuf, sizeof(lbuf) - 1);
305 else
306 length = listxattr(path, lbuf, sizeof(lbuf) - 1);
307 if (length <= 0)
308 return length;
309 lbuf[length] = 0; /* not supposed to be necessary */
310
311 start_offset = sizeof(attrlist_t);
312 end_offset = buffersize & ~(8-1); /* 8 byte align */
313
314 for (l = lbuf; l != lbuf + length; l = strchr(l, '\0') + 1) {
315 if (api_unconvert(name, l, flags))
316 continue;
317 if (flags & ATTR_DONTFOLLOW)
318 vlength = lgetxattr(path, l, NULL, 0);
319 else
320 vlength = getxattr(path, l, NULL, 0);
321 if (vlength < 0 && (errno == ENOATTR || errno == ENOTSUP))
322 continue;
323 if (count++ < cursor->opaque[0])
324 continue;
325 if (attr_list_pack(name, vlength, buffer, buffersize,
326 &start_offset, &end_offset)) {
327 if (cursor->opaque[0] == count - 1) {
328 errno = EINVAL;
329 return -1;
330 }
331 cursor->opaque[0] = count - 1;
332 break;
333 }
334 }
335 return 0;
336 }
337
338 int
339 attr_listf(int fd, char *buffer, const int buffersize, int flags,
340 attrlist_cursor_t *cursor)
341 {
342 const char *l;
343 int length, vlength, count = 0;
344 char lbuf[MAXLISTLEN+1];
345 char name[MAXNAMELEN+16];
346 int start_offset, end_offset;
347
348 if (buffersize < sizeof(attrlist_t)) {
349 errno = EINVAL;
350 return -1;
351 }
352 memset(buffer, 0, sizeof(attrlist_t));
353
354 length = flistxattr(fd, lbuf, sizeof(lbuf) - 1);
355 if (length < 0)
356 return length;
357 lbuf[length] = 0; /* not supposed to be necessary */
358
359 start_offset = sizeof(attrlist_t);
360 end_offset = buffersize & ~(8-1); /* 8 byte align */
361
362 for (l = lbuf; l != lbuf + length; l = strchr(l, '\0') + 1) {
363 if (api_unconvert(name, l, flags))
364 continue;
365 vlength = fgetxattr(fd, l, NULL, 0);
366 if (vlength < 0 && (errno == ENOATTR || errno == ENOTSUP))
367 continue;
368 if (count++ < cursor->opaque[0])
369 continue;
370 if (attr_list_pack(name, vlength, buffer, buffersize,
371 &start_offset, &end_offset)) {
372 if (cursor->opaque[0] == count - 1) {
373 errno = EINVAL;
374 return -1;
375 }
376 cursor->opaque[0] = count - 1;
377 break;
378 }
379 }
380 return 0;
381 }
382
383
384 /*
385 * Helper routines for the attr_multi functions. In IRIX, the
386 * multi routines are a single syscall - in Linux, we break em
387 * apart in userspace and make individual syscalls for each.
388 */
389
390 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
391 static int
392 attr_single(const char *path, attr_multiop_t *op, int flags)
393 {
394 int r = -1;
395
396 errno = EINVAL;
397 flags |= op->am_flags;
398 if (op->am_opcode == ATTR_OP_GET)
399 r = attr_get(path, op->am_attrname, op->am_attrvalue,
400 &op->am_length, flags);
401 else if (op->am_opcode == ATTR_OP_SET)
402 r = attr_set(path, op->am_attrname, op->am_attrvalue,
403 op->am_length, flags);
404 else if (op->am_opcode == ATTR_OP_REMOVE)
405 r = attr_remove(path, op->am_attrname, flags);
406 return r;
407 }
408
409 static int
410 attr_singlef(const int fd, attr_multiop_t *op, int flags)
411 {
412 int r = -1;
413
414 errno = EINVAL;
415 flags |= op->am_flags;
416 if (op->am_opcode == ATTR_OP_GET)
417 r = attr_getf(fd, op->am_attrname, op->am_attrvalue,
418 &op->am_length, flags);
419 else if (op->am_opcode == ATTR_OP_SET)
420 r = attr_setf(fd, op->am_attrname, op->am_attrvalue,
421 op->am_length, flags);
422 else if (op->am_opcode == ATTR_OP_REMOVE)
423 r = attr_removef(fd, op->am_attrname, flags);
424 return r;
425 }
426 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
427
428 /*
429 * Operate on multiple attributes of the same object simultaneously
430 *
431 * From the manpage: "attr_multi will fail if ... a bit other than
432 * ATTR_DONTFOLLOW was set in the flag argument." flags must be
433 * checked here as they are not passed into the kernel.
434 */
435 int
436 attr_multi(const char *path, attr_multiop_t *multiops, int count, int flags)
437 {
438 int i, tmp, r = -1;
439
440 errno = EINVAL;
441 if ((flags & ATTR_DONTFOLLOW) != flags)
442 return r;
443
444 r = errno = 0;
445 for (i = 0; i < count; i++) {
446 tmp = attr_single(path, &multiops[i], flags);
447 if (tmp) r = tmp;
448 }
449 return r;
450 }
451
452 int
453 attr_multif(int fd, attr_multiop_t *multiops, int count, int flags)
454 {
455 int i, tmp, r = -1;
456
457 errno = EINVAL;
458 if ((flags & ATTR_DONTFOLLOW) != flags)
459 return r;
460
461 r = errno = 0;
462 for (i = 0; i < count; i++) {
463 tmp = attr_singlef(fd, &multiops[i], flags);
464 if (tmp) r = tmp;
465 }
466 return r;
467 }