1 /* @(#)xattr.c 1.24 20/10/20 Copyright 2003-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)xattr.c 1.24 20/10/20 Copyright 2003-2020 J. Schilling";
6 #endif
7 /*
8 * Handle Extended File Attributes on Linux
9 *
10 * Copyright (c) 2003-2020 J. Schilling
11 * Thanks to Anreas Gr�nbacher <agruen@suse.de> for the
12 * first implemenation.
13 */
14 /*
15 * The contents of this file are subject to the terms of the
16 * Common Development and Distribution License, Version 1.0 only
17 * (the "License"). You may not use this file except in compliance
18 * with the License.
19 *
20 * See the file CDDL.Schily.txt in this distribution for details.
21 * A copy of the CDDL is also available via the Internet at
22 * http://www.opensource.org/licenses/cddl1.txt
23 *
24 * When distributing Covered Code, include this CDDL HEADER in each
25 * file and include the License file CDDL.Schily.txt from this distribution.
26 */
27
28 #include <schily/stdio.h>
29 #include <schily/stdlib.h>
30 #include <schily/errno.h>
31 #include <schily/string.h>
32 #if defined(HAVE_SYS_XATTR_H)
33 #include <sys/xattr.h>
34 #else
35 #if defined(HAVE_ATTR_XATTR_H)
36 #include <attr/xattr.h>
37 #endif /* HAVE_ATTR_XATTR_H */
38 #endif /* HAVE_SYS_XATTR_H */
39 #include "star.h"
40 #include <schily/standard.h>
41 #include <schily/unistd.h>
42 #include <schily/fcntl.h> /* For open() with hop_dirs() */
43 #define GT_COMERR /* #define comerr gtcomerr */
44 #define GT_ERROR /* #define error gterror */
45 #include <schily/schily.h>
46 #include "starsubs.h"
47 #include "checkerr.h"
48
49 #if defined(USE_XATTR) && \
50 (defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
51 (defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
52
53 LOCAL ssize_t llgetxattr __PR((char *path, const char *name,
54 void *value, size_t size));
55 LOCAL int llsetxattr __PR((char *path, const char *name,
56 const void *value, size_t size,
57 int flags));
58 LOCAL ssize_t lllistxattr __PR((char *path, char *list,
59 size_t size));
60 #endif /* USE_XATTR */
61
62 #if defined(USE_XATTR) && defined(HAVE_LISTXATTR) && defined(HAVE_GETXATTR)
63 /*
64 * Use a global list of extended attributes -- FINFO structs have no
65 * constructors and destructors.
66 */
67 /*
68 * It bad to see new global variables while we are working on a star library.
69 */
70 LOCAL star_xattr_t *static_xattr;
71 #endif /* USE_XATTR && HAVE_LISTXATTR && HAVE_GETXATTR */
72
73 #ifdef USE_SELINUX
74 extern BOOL selinux_enabled;
75 #endif
76
77 #ifdef XATTR_NOFOLLOW /* Max OS X has incompatible prototypes */
78
79 /*
80 * This works since we currently only use lgetxattr()/lsetxattr()/llistxattr()
81 * but not getxattr()/setxattr()/listxattr(). If we ever need the latter,
82 * we would need to use a different macro names to be able to abstract from
83 * the differences between Linux and Mac OS X.
84 */
85 #define lgetxattr(p, n, v, s) getxattr(p, n, v, s, 0, XATTR_NOFOLLOW)
86 #define lsetxattr(p, n, v, s, f) setxattr(p, n, v, s, 0, (f)|XATTR_NOFOLLOW)
87 #define llistxattr(p, nb, s) listxattr(p, nb, s, XATTR_NOFOLLOW)
88
89 #else /* !XATTR_NOFOLLOW */
90 /*
91 * This is for Linux
92 */
93 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
94 #define lgetxattr getxattr
95 #endif
96 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
97 #define lsetxattr setxattr
98 #endif
99 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
100 #define llistxattr listxattr
101 #endif
102 #endif /* !XATTR_NOFOLLOW */
103
104 EXPORT void
opt_xattr()105 opt_xattr()
106 {
107 #if defined(USE_XATTR) && \
108 (defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
109 (defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
110 #if defined(HAVE_SETXATTR) || defined(HAVE_LSETXATTR)
111 printf(" Linux-xattr");
112 #endif
113 #endif
114 }
115
116 EXPORT void
opt_selinux()117 opt_selinux()
118 {
119 #ifdef USE_SELINUX
120 printf(" SELinux");
121 #endif
122 }
123
124 /* ARGSUSED */
125 EXPORT BOOL
get_xattr(info)126 get_xattr(info)
127 register FINFO *info;
128 {
129 #if defined(USE_XATTR) && \
130 (defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
131 (defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
132 ssize_t list_len;
133 size_t size;
134 char *alist;
135 char *lp;
136 int count;
137 int i;
138
139 free_xattr(&static_xattr);
140 info->f_xflags &= ~XF_XATTR;
141 info->f_xattr = NULL;
142
143 list_len = lllistxattr(info->f_sname, NULL, 0);
144 if (list_len < 0) {
145 if (!errhidden(E_GETXATTR, info->f_name)) {
146 if (!errwarnonly(E_GETXATTR, info->f_name))
147 xstats.s_getxattr++;
148 errmsg("Cannot listxattr for '%s'.\n", info->f_name);
149 (void) errabort(E_GETXATTR, info->f_name, TRUE);
150 }
151 return (FALSE);
152 } else if (list_len == 0) {
153 return (FALSE);
154 }
155 alist = ___malloc(list_len+2, "extended attribute");
156 list_len = lllistxattr(info->f_sname, alist, list_len);
157 if (list_len < 0) {
158 if (!errhidden(E_GETXATTR, info->f_name)) {
159 if (!errwarnonly(E_GETXATTR, info->f_name))
160 xstats.s_getxattr++;
161 errmsg("Cannot listxattr for '%s'.\n", info->f_name);
162 (void) errabort(E_GETXATTR, info->f_name, TRUE);
163 }
164 goto fail;
165 }
166 alist[list_len] = alist[list_len+1] = '\0';
167
168 /*
169 * Count the number of attributes
170 */
171 for (lp = alist, count = 0; lp - alist < list_len;
172 lp = strchr(lp, '\0')+1) {
173 if (*lp == '\0' ||
174 strncmp(lp, "system.", 7) == 0 ||
175 strncmp(lp, "xfsroot.", 8) == 0)
176 continue;
177 count++;
178 }
179
180 if (count == 0)
181 goto fail; /* not really a failure, but... */
182
183 size = (count+1) * sizeof (star_xattr_t);
184 static_xattr = ___malloc(size, "extended attribute");
185 fillbytes(static_xattr, size, '\0');
186
187 for (lp = alist, i = 0; lp - alist < list_len;
188 lp = strchr(lp, '\0')+1) {
189 ssize_t len;
190
191 if (*lp == '\0' ||
192 strncmp(lp, "system.", 7) == 0 ||
193 strncmp(lp, "xfsroot.", 8) == 0)
194 continue;
195
196 static_xattr[i].name = ___malloc(strlen(lp)+1,
197 "extended attribute");
198 static_xattr[i].value = NULL;
199 strcpy(static_xattr[i].name, lp);
200
201 len = llgetxattr(info->f_sname, lp, NULL, 0);
202 if (len < 0) {
203 if (!errhidden(E_GETXATTR, info->f_name)) {
204 if (!errwarnonly(E_GETXATTR, info->f_name))
205 xstats.s_getxattr++;
206 errmsg("Cannot getxattr for '%s'.\n",
207 info->f_name);
208 (void) errabort(E_GETXATTR, info->f_name,
209 TRUE);
210 }
211 goto fail2;
212 }
213 static_xattr[i].value_len = len;
214 static_xattr[i].value = ___malloc(len, "extended attribute");
215 len = llgetxattr(info->f_sname, lp, static_xattr[i].value, len);
216 if (len < 0) {
217 if (!errhidden(E_GETXATTR, info->f_name)) {
218 if (!errwarnonly(E_GETXATTR, info->f_name))
219 xstats.s_getxattr++;
220 errmsg("Cannot getxattr for '%s'.\n",
221 info->f_name);
222 (void) errabort(E_GETXATTR, info->f_name,
223 TRUE);
224 }
225 goto fail2;
226 }
227 i++;
228 }
229
230 free(alist);
231 info->f_xflags |= XF_XATTR;
232 info->f_xattr = static_xattr;
233 return (TRUE);
234
235 fail2:
236 for (; i >= 0; i--) {
237 free(static_xattr[i].name);
238 if (static_xattr[i].value != NULL)
239 free(static_xattr[i].value);
240 }
241 free(static_xattr);
242 static_xattr = NULL;
243 fail:
244 free(alist);
245 return (FALSE);
246 #else /* USE_XATTR */
247 return (TRUE);
248 #endif /* USE_XATTR */
249 }
250
251 /* ARGSUSED */
252 EXPORT BOOL
set_xattr(info)253 set_xattr(info)
254 register FINFO *info;
255 {
256 #if defined(USE_XATTR) && \
257 (defined(HAVE_SETXATTR) || defined(HAVE_LSETXATTR))
258 star_xattr_t *xap;
259
260 if (info->f_xattr == NULL || (info->f_xflags & XF_XATTR) == 0)
261 return (TRUE);
262
263 for (xap = info->f_xattr; xap->name != NULL; xap++) {
264 #ifdef USE_SELINUX
265 if (selinux_enabled &&
266 (strcmp(xap->name, "security.selinux") == 0))
267 continue;
268 #endif
269 if (llsetxattr(info->f_name, xap->name, xap->value,
270 xap->value_len, 0) != 0) {
271 if (!errhidden(E_SETXATTR, info->f_name)) {
272 if (!errwarnonly(E_SETXATTR, info->f_name))
273 xstats.s_setxattr++;
274 errmsg("Cannot setxattr for '%s'.\n",
275 info->f_name);
276 (void) errabort(E_SETXATTR, info->f_name,
277 TRUE);
278 }
279 return (FALSE);
280 }
281 }
282 #endif /* USE_XATTR */
283 return (TRUE);
284 }
285
286 #ifdef USE_SELINUX
287 EXPORT BOOL
setselinux(info)288 setselinux(info)
289 register FINFO *info;
290 {
291 star_xattr_t *xap;
292
293 if (info->f_xattr == NULL || (info->f_xflags & XF_XATTR) == 0) {
294 if (setfscreatecon(NULL) < 0)
295 goto err;
296 return (TRUE);
297 }
298
299 for (xap = info->f_xattr; xap->name != NULL; xap++) {
300 if (strcmp(xap->name, "security.selinux") == 0) {
301 if (setfscreatecon(xap->value) < 0)
302 goto err;
303 return (TRUE);
304 }
305 }
306 /*
307 * There was no "security.selinux" label, so we need to clear
308 * the context.
309 */
310 if (setfscreatecon(NULL) < 0)
311 goto err;
312 return (TRUE);
313 err:
314 errmsg("Cannot setup security context for '%s'.\n", info->f_name);
315 return (FALSE);
316 }
317 #endif /* USE_SELINUX */
318
319 EXPORT void
free_xattr(xattr)320 free_xattr(xattr)
321 star_xattr_t **xattr;
322 {
323 #ifdef USE_XATTR
324 star_xattr_t *xap;
325
326 if (*xattr == NULL)
327 return;
328
329 for (xap = *xattr; xap->name != NULL; xap++) {
330 free(xap->name);
331 free(xap->value);
332 }
333 free(*xattr);
334 *xattr = NULL;
335
336 #endif /* USE_XATTR */
337 }
338
339 #if defined(USE_XATTR) && \
340 (defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
341 (defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
342
343 LOCAL ssize_t
llgetxattr(path,name,value,size)344 llgetxattr(path, name, value, size)
345 char *path;
346 const char *name;
347 void *value;
348 size_t size;
349 {
350 #ifdef HAVE_FCHDIR
351 char *p;
352 int fd;
353 int fdh;
354 int err = 0;
355 #endif
356 ssize_t ret;
357
358 if ((ret = lgetxattr(path, name, value, size)) < 0 &&
359 geterrno() != ENAMETOOLONG) {
360 return (ret);
361 }
362
363 #ifdef HAVE_FCHDIR
364 if (ret >= 0)
365 return (ret);
366
367 fd = hop_dirs(path, &p);
368 if (fd >= 0) {
369 fdh = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
370 if (fdh >= 0) {
371 (void) fchdir(fd);
372 ret = lgetxattr(p, name, value, size);
373 err = geterrno();
374 (void) fchdir(fdh);
375 close(fdh);
376 }
377 close(fd);
378 }
379 close(fd);
380 if (err)
381 seterrno(err);
382 #endif
383 return (ret);
384 }
385
386
387 LOCAL int
llsetxattr(path,name,value,size,flags)388 llsetxattr(path, name, value, size, flags)
389 char *path;
390 const char *name;
391 const void *value;
392 size_t size;
393 int flags;
394 {
395 #ifdef HAVE_FCHDIR
396 char *p;
397 int fd;
398 int fdh;
399 int err = 0;
400 #endif
401 int ret;
402
403 if ((ret = lsetxattr(path, name, value, size, flags)) < 0 &&
404 geterrno() != ENAMETOOLONG) {
405 return (ret);
406 }
407
408 #ifdef HAVE_FCHDIR
409 if (ret >= 0)
410 return (ret);
411
412 fd = hop_dirs(path, &p);
413 if (fd >= 0) {
414 fdh = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
415 if (fdh >= 0) {
416 (void) fchdir(fd);
417 ret = lsetxattr(p, name, value, size, flags);
418 err = geterrno();
419 (void) fchdir(fdh);
420 close(fdh);
421 }
422 close(fd);
423 }
424 close(fd);
425 if (err)
426 seterrno(err);
427 #endif
428 return (ret);
429 }
430
431 LOCAL ssize_t
lllistxattr(path,list,size)432 lllistxattr(path, list, size)
433 char *path;
434 char *list;
435 size_t size;
436 {
437 #ifdef HAVE_FCHDIR
438 char *p;
439 int fd;
440 int fdh;
441 int err = 0;
442 #endif
443 ssize_t ret;
444
445 if ((ret = llistxattr(path, list, size)) < 0 &&
446 geterrno() != ENAMETOOLONG) {
447 return (ret);
448 }
449
450 #ifdef HAVE_FCHDIR
451 if (ret >= 0)
452 return (ret);
453
454 fd = hop_dirs(path, &p);
455 if (fd >= 0) {
456 fdh = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
457 if (fdh >= 0) {
458 (void) fchdir(fd);
459 ret = llistxattr(p, list, size);
460 err = geterrno();
461 (void) fchdir(fdh);
462 close(fdh);
463 }
464 close(fd);
465 }
466 close(fd);
467 if (err)
468 seterrno(err);
469 #endif
470 return (ret);
471 }
472 #endif /* USE_XATTR */
473