1/*
2 * attr.c - extended attributes (xattr) manipulation
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 2009 Mikael Magnusson
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Mikael Magnusson or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Andrew Main and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Mikael Magnusson and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Mikael Magnusson and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "attr.mdh"
31#include "attr.pro"
32
33#include <sys/types.h>
34#include <sys/xattr.h>
35
36static ssize_t
37xgetxattr(const char *path, const char *name, void *value, size_t size, int symlink)
38{
39#ifdef XATTR_EXTRA_ARGS
40    return getxattr(path, name, value, size, 0, symlink ? XATTR_NOFOLLOW: 0);
41#else
42    switch (symlink) {
43    case 0:
44        return getxattr(path, name, value, size);
45    default:
46        return lgetxattr(path, name, value, size);
47    }
48#endif
49}
50
51static ssize_t
52xlistxattr(const char *path, char *list, size_t size, int symlink)
53{
54#ifdef XATTR_EXTRA_ARGS
55    return listxattr(path, list, size, symlink ? XATTR_NOFOLLOW : 0);
56#else
57    switch (symlink) {
58    case 0:
59        return listxattr(path, list, size);
60    default:
61        return llistxattr(path, list, size);
62    }
63#endif
64}
65
66static int
67xsetxattr(const char *path, const char *name, const void *value,
68          size_t size, int flags, int symlink)
69{
70#ifdef XATTR_EXTRA_ARGS
71    return setxattr(path, name, value, size, 0, flags | symlink ? XATTR_NOFOLLOW : 0);
72#else
73    switch (symlink) {
74    case 0:
75        return setxattr(path, name, value, size, flags);
76    default:
77        return lsetxattr(path, name, value, size, flags);
78    }
79#endif
80}
81
82static int
83xremovexattr(const char *path, const char *name, int symlink)
84{
85#ifdef XATTR_EXTRA_ARGS
86    return removexattr(path, name, symlink ? XATTR_NOFOLLOW : 0);
87#else
88    switch (symlink) {
89    case 0:
90        return removexattr(path, name);
91    default:
92        return lremovexattr(path, name);
93    }
94#endif
95}
96
97static int
98bin_getattr(char *nam, char **argv, Options ops, UNUSED(int func))
99{
100    int ret = 0;
101    int list_len, val_len = 0, attr_len = 0, slen;
102    char *value, *file = argv[0], *attr = argv[1], *param = argv[2];
103    int symlink = OPT_ISSET(ops, 'h');
104
105    unmetafy(file, &slen);
106    unmetafy(attr, NULL);
107    list_len = xlistxattr(file, NULL, 0, symlink);
108    if (list_len > 0) {
109        val_len = xgetxattr(file, attr, NULL, 0, symlink);
110        if (val_len == 0) {
111            if (param)
112                unsetparam(param);
113            return 0;
114        }
115        if (val_len > 0) {
116            value = (char *)zalloc(val_len+1);
117            attr_len = xgetxattr(file, attr, value, val_len, symlink);
118            if (attr_len > 0 && attr_len <= val_len) {
119                value[attr_len] = '\0';
120                if (param)
121                    setsparam(param, metafy(value, attr_len, META_DUP));
122                else
123                    printf("%s\n", value);
124            }
125            zfree(value, val_len+1);
126        }
127    }
128    if (list_len < 0 || val_len < 0 || attr_len < 0 || attr_len > val_len)  {
129        zwarnnam(nam, "%s: %e", metafy(file, slen, META_NOALLOC), errno);
130        ret = 1 + (attr_len > val_len || attr_len < 0);
131    }
132    return ret;
133}
134
135static int
136bin_setattr(char *nam, char **argv, Options ops, UNUSED(int func))
137{
138    int ret = 0, slen, vlen;
139    int symlink = OPT_ISSET(ops, 'h');
140    char *file = argv[0], *attr = argv[1], *value = argv[2];
141
142    unmetafy(file, &slen);
143    unmetafy(attr, NULL);
144    unmetafy(value, &vlen);
145    if (xsetxattr(file, attr, value, vlen, 0, symlink)) {
146        zwarnnam(nam, "%s: %e", metafy(file, slen, META_NOALLOC), errno);
147        ret = 1;
148    }
149    return ret;
150}
151
152static int
153bin_delattr(char *nam, char **argv, Options ops, UNUSED(int func))
154{
155    int ret = 0, slen;
156    int symlink = OPT_ISSET(ops, 'h');
157    char *file = argv[0], **attr = argv;
158
159    unmetafy(file, &slen);
160    while (*++attr) {
161        unmetafy(*attr, NULL);
162        if (xremovexattr(file, *attr, symlink)) {
163            zwarnnam(nam, "%s: %e", metafy(file, slen, META_NOALLOC), errno);
164            ret = 1;
165            break;
166        }
167    }
168    return ret;
169}
170
171static int
172bin_listattr(char *nam, char **argv, Options ops, UNUSED(int func))
173{
174    int ret = 0;
175    int val_len, list_len = 0, slen;
176    char *value, *file = argv[0], *param = argv[1];
177    int symlink = OPT_ISSET(ops, 'h');
178
179    unmetafy(file, &slen);
180    val_len = xlistxattr(file, NULL, 0, symlink);
181    if (val_len == 0) {
182        if (param)
183            unsetparam(param);
184        return 0;
185    }
186    if (val_len > 0) {
187        value = (char *)zalloc(val_len+1);
188        list_len = xlistxattr(file, value, val_len, symlink);
189        if (list_len > 0 && list_len <= val_len) {
190            char *p = value;
191            if (param) {
192                if (strlen(value) + 1 == list_len)
193                    setsparam(param, metafy(value, list_len-1, META_DUP));
194                else {
195                    int arrlen = 0;
196                    char **array = NULL, **arrptr = NULL;
197
198                    while (p < &value[list_len]) {
199                        arrlen++;
200                        p += strlen(p) + 1;
201                    }
202                    arrptr = array = (char **)zshcalloc((arrlen+1) * sizeof(char *));
203                    p = value;
204                    while (p < &value[list_len]) {
205                        *arrptr++ = metafy(p, -1, META_DUP);
206                        p += strlen(p) + 1;
207                    }
208                    setaparam(param, array);
209                }
210            } else while (p < &value[list_len]) {
211                printf("%s\n", p);
212                p += strlen(p) + 1;
213            }
214        }
215        zfree(value, val_len+1);
216    }
217    if (val_len < 0 || list_len < 0 || list_len > val_len) {
218        zwarnnam(nam, "%s: %e", metafy(file, slen, META_NOALLOC), errno);
219        ret = 1 + (list_len > val_len || list_len < 0);
220    }
221    return ret;
222}
223
224/* module paraphernalia */
225
226static struct builtin bintab[] = {
227    BUILTIN("zgetattr", 0, bin_getattr, 2, 3, 0, "h", NULL),
228    BUILTIN("zsetattr", 0, bin_setattr, 3, 3, 0, "h", NULL),
229    BUILTIN("zdelattr", 0, bin_delattr, 2, -1, 0, "h", NULL),
230    BUILTIN("zlistattr", 0, bin_listattr, 1, 2, 0, "h", NULL),
231};
232
233static struct features module_features = {
234    bintab, sizeof(bintab)/sizeof(*bintab),
235    NULL, 0,
236    NULL, 0,
237    NULL, 0,
238    0
239};
240
241/**/
242int
243setup_(UNUSED(Module m))
244{
245    return 0;
246}
247
248/**/
249int
250features_(Module m, char ***features)
251{
252    *features = featuresarray(m, &module_features);
253    return 0;
254}
255
256/**/
257int
258enables_(Module m, int **enables)
259{
260    return handlefeatures(m, &module_features, enables);
261}
262
263/**/
264int
265boot_(UNUSED(Module m))
266{
267    return 0;
268}
269
270/**/
271int
272cleanup_(Module m)
273{
274    return setfeatureenables(m, &module_features, NULL);
275}
276
277/**/
278int
279finish_(UNUSED(Module m))
280{
281    return 0;
282}
283