1#include "extattr_os.h"
2
3#ifdef EXTATTR_BSD
4
5#include <errno.h>
6
7#include "EXTERN.h"
8#include "perl.h"
9#include "XSUB.h"
10
11#include "flags.h"
12
13static int
14valid_namespace (struct hv *flags, int *pattrnamespace)
15{
16  const size_t NAMESPACE_KEYLEN = strlen(NAMESPACE_KEY);
17  SV **psv_ns;
18  char *ns = NULL;
19  int ok = 1; /* Default is valid */
20  int attrnamespace = EXTATTR_NAMESPACE_USER;
21
22  if (flags && (psv_ns = hv_fetch(flags, NAMESPACE_KEY, NAMESPACE_KEYLEN, 0)))
23  {
24    /*
25     * Undefined => default. Otherwise "user" and "system" are valid.
26     */
27    if (SvOK(*psv_ns))
28    {
29      char *s;
30      STRLEN len = 0;
31
32      s = SvPV(*psv_ns, len);
33
34      if (len)
35      {
36	if (memcmp(NAMESPACE_USER, s, len) == 0)
37	  attrnamespace = EXTATTR_NAMESPACE_USER;
38	else if (memcmp(NAMESPACE_SYSTEM, s, len) == 0)
39	  attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
40	else
41	  ok = 0;
42      }
43      else
44      {
45	ok = 0;
46      }
47    }
48  }
49
50  if (ok)
51    *pattrnamespace = attrnamespace;
52
53  return ok;
54}
55
56/* Helper to convert number of bytes written into success/failure code. */
57static inline int
58bsd_extattr_set_succeeded (const int expected, const int actual)
59{
60  int ret = 0;
61
62  if (actual == -1) {
63    ret = -errno;
64  } else if (actual != expected) {
65    /* Pretend there's not enough space for the data. */
66    ret = -ENOBUFS;
67  }
68
69  return ret;
70}
71
72int
73bsd_setxattr (const char *path,
74	      const char *attrname,
75	      const char *attrvalue,
76	      const size_t slen,
77	      struct hv *flags)
78{
79  int attrnamespace = -1;
80  int ret = 0;
81
82  if (!valid_namespace(flags, &attrnamespace))
83  {
84    ret = -EOPNOTSUPP;
85  }
86
87  if (ret == 0)
88  {
89    File_ExtAttr_setflags_t setflags = File_ExtAttr_flags2setflags(flags);
90    switch (setflags)
91    {
92    case SET_CREATEIFNEEDED:
93    case SET_REPLACE:
94      /* Default behaviour */
95      break;
96
97    case SET_CREATE:
98      /*
99       * This needs to be emulated, since the default *BSD calls
100       * don't provide a way of failing if the attribute exists.
101       * This emulation is inherently racy.
102       */
103      {
104	ssize_t sz = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
105	if (sz >= 0)
106	{
107	  /* Attribute already exists => fail. */
108	  ret = -EEXIST;
109	}
110      }
111      break;
112    }
113  }
114
115  if (ret == 0)
116  {
117    ret = extattr_set_file(path, attrnamespace, attrname, attrvalue, slen);
118    ret = bsd_extattr_set_succeeded(slen, ret);
119  }
120
121  return ret;
122}
123
124int
125bsd_fsetxattr (const int fd,
126	       const char *attrname,
127	       const char *attrvalue,
128	       const size_t slen,
129	       struct hv *flags)
130{
131  int attrnamespace = -1;
132  int ret = 0;
133
134  if (!valid_namespace(flags, &attrnamespace))
135  {
136    ret = -EOPNOTSUPP;
137  }
138
139  if (ret == 0)
140  {
141    File_ExtAttr_setflags_t setflags = File_ExtAttr_flags2setflags(flags);
142    switch (setflags)
143    {
144    case SET_CREATEIFNEEDED:
145    case SET_REPLACE:
146      /* Default behaviour */
147      break;
148
149    case SET_CREATE:
150      /*
151       * This needs to be emulated, since the default *BSD calls
152       * don't provide a way of failing if the attribute exists.
153       * This emulation is inherently racy.
154       */
155      {
156	ssize_t sz = extattr_get_fd(fd, attrnamespace, attrname, NULL, 0);
157	if (sz >= 0)
158	{
159	  /* Attribute already exists => fail. */
160	  ret = -EEXIST;
161	}
162      }
163      break;
164    }
165  }
166
167  if (ret == 0)
168  {
169    ret = extattr_set_fd(fd, attrnamespace, attrname, attrvalue, slen);
170    ret = bsd_extattr_set_succeeded(slen, ret);
171  }
172
173  return ret;
174}
175
176int
177bsd_getxattr (const char *path,
178	      const char *attrname,
179	      void *attrvalue,
180	      const size_t slen,
181	      struct hv *flags)
182{
183  int attrnamespace = -1;
184  int ret = 0;
185
186  if (!valid_namespace(flags, &attrnamespace))
187  {
188    ret = -EOPNOTSUPP;
189  }
190
191  if (ret == 0) {
192    ret = extattr_get_file(path, attrnamespace, attrname, attrvalue, slen);
193    if (ret < 0) {
194      ret = -errno;
195    }
196  }
197
198  return ret;
199}
200
201int
202bsd_fgetxattr (const int fd,
203	       const char *attrname,
204	       void *attrvalue,
205	       const size_t slen,
206	       struct hv *flags)
207{
208  int attrnamespace = -1;
209  int ret = 0;
210
211  if (!valid_namespace(flags, &attrnamespace))
212  {
213    ret = -EOPNOTSUPP;
214  }
215
216  if (ret == 0) {
217    ret = extattr_get_fd(fd, attrnamespace, attrname, attrvalue, slen);
218    if (ret < 0) {
219      ret = -errno;
220    }
221  }
222
223  return ret;
224}
225
226int
227bsd_removexattr (const char *path,
228		 const char *attrname,
229		 struct hv *flags)
230{
231  int attrnamespace = -1;
232  int ret = 0;
233
234  if (!valid_namespace(flags, &attrnamespace))
235  {
236    ret = -EOPNOTSUPP;
237  }
238
239  if (ret == 0) {
240    ret = extattr_delete_file(path, attrnamespace, attrname);
241    if (ret < 0) {
242      ret = -errno;
243    }
244  }
245
246  return ret;
247}
248
249int
250bsd_fremovexattr (const int fd,
251		  const char *attrname,
252		  struct hv *flags)
253{
254  int attrnamespace = -1;
255  int ret = 0;
256
257  if (!valid_namespace(flags, &attrnamespace))
258  {
259    ret = -EOPNOTSUPP;
260  }
261
262  if (ret == 0) {
263    ret = extattr_delete_fd(fd, attrnamespace, attrname);
264    if (ret < 0) {
265      ret = -errno;
266    }
267  }
268
269  return ret;
270}
271
272/* Convert the BSD-style list to a nul-separated list. */
273static void
274reformat_list (char *buf, const ssize_t len)
275{
276  ssize_t pos = 0;
277  ssize_t attrlen;
278
279  while (pos < len)
280  {
281    attrlen = (unsigned char) buf[pos];
282    memmove(buf + pos, buf + pos + 1, attrlen);
283    buf[pos + attrlen] = '\0';
284    pos += attrlen + 1;
285  }
286}
287
288ssize_t
289bsd_listxattr (const char *path,
290	       char *buf,
291	       const size_t buflen,
292	       struct hv *flags)
293{
294  int attrnamespace = -1;
295  ssize_t ret = 0;
296
297  if (!valid_namespace(flags, &attrnamespace))
298  {
299    ret = -EOPNOTSUPP;
300  }
301
302  if (ret == 0)
303  {
304    ret = extattr_list_file(path,
305			    attrnamespace,
306			    /* To get the length on *BSD, pass NULL here. */
307			    buflen ? buf : NULL,
308			    buflen);
309
310    if (buflen && (ret > 0))
311      reformat_list(buf, ret);
312
313    if (ret < 0) {
314      ret = -errno;
315    }
316  }
317
318  return ret;
319}
320
321ssize_t
322bsd_flistxattr (const int fd,
323		char *buf,
324		const size_t buflen,
325		struct hv *flags)
326{
327  int attrnamespace = -1;
328  ssize_t ret = 0;
329
330  if (!valid_namespace(flags, &attrnamespace))
331  {
332    ret = -EOPNOTSUPP;
333  }
334
335  if (ret == 0)
336  {
337    ret = extattr_list_fd(fd,
338			  attrnamespace,
339			  /* To get the length on *BSD, pass NULL here. */
340			  buflen ? buf : NULL,
341			  buflen);
342
343    if (buflen && (ret > 0))
344      reformat_list(buf, ret);
345
346    if (ret < 0) {
347      ret = -errno;
348    }
349  }
350
351  return ret;
352}
353
354static ssize_t
355listxattrns (char *buf, const size_t buflen,
356	     const int iHasUser, const int iHasSystem)
357{
358  size_t len = 0;
359  ssize_t ret = 0;
360
361  if (iHasUser)
362    len += sizeof(NAMESPACE_USER);
363  if (iHasSystem)
364    len += sizeof(NAMESPACE_SYSTEM);
365
366  if (buflen >= len)
367  {
368    char *p = buf;
369
370    if (iHasUser)
371    {
372      memcpy(p, NAMESPACE_USER, sizeof(NAMESPACE_USER));
373      p += sizeof(NAMESPACE_USER);
374    }
375    if (iHasSystem)
376    {
377      memcpy(p, NAMESPACE_SYSTEM, sizeof(NAMESPACE_SYSTEM));
378      p += sizeof(NAMESPACE_SYSTEM);
379    }
380
381    ret = len;
382  }
383  else if (buflen == 0)
384  {
385    ret = len;
386  }
387  else
388  {
389    ret = -ERANGE;
390  }
391
392  return ret;
393}
394
395ssize_t
396bsd_listxattrns (const char *path,
397		 char *buf,
398		 const size_t buflen,
399		 struct hv *flags)
400{
401  int iHasUser = 0;
402  int iHasSystem = 0;
403  ssize_t ret = 0;
404
405  ret = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0);
406  if (ret > 0)
407    iHasUser = 1;
408
409  if (ret >= 0)
410  {
411    ret = extattr_list_file(path, EXTATTR_NAMESPACE_SYSTEM, NULL, 0);
412    if (ret > 0)
413      iHasSystem = 1;
414
415    /*
416     * XXX: How do we cope with EPERM? Throw an exception.
417     * For now ignore it, although this could cause problems.
418     */
419    if (ret == -1 && errno == EPERM)
420      ret = 0;
421  }
422
423  if (ret >= 0)
424    ret = listxattrns(buf, buflen, iHasUser, iHasSystem);
425
426  return ret;
427}
428
429ssize_t
430bsd_flistxattrns (const int fd,
431		  char *buf,
432		  const size_t buflen,
433		  struct hv *flags)
434{
435  int iHasUser = 0;
436  int iHasSystem = 0;
437  ssize_t ret;
438
439  ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, NULL, 0);
440  if (ret > 0)
441    iHasUser = 1;
442
443  if (ret >= 0)
444  {
445    ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_SYSTEM, NULL, 0);
446    if (ret > 0)
447      iHasSystem = 1;
448
449    /*
450     * XXX: How do we cope with EPERM? Throw an exception.
451     * For now ignore it, although this could cause problems.
452     */
453    if (ret == -1 && errno == EPERM)
454      ret = 0;
455  }
456
457  if (ret >= 0)
458    ret = listxattrns(buf, buflen, iHasUser, iHasSystem);
459
460  return ret;
461}
462
463#endif /* EXTATTR_BSD */
464