]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_sys.c
Patches for OpenBSD
[netatalk.git] / libatalk / vfs / ea_sys.c
1 /*
2   $Id: ea_sys.c,v 1.7 2010-04-03 07:11:36 franklahm Exp $
3   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 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 General Public License for more details.
14 */
15
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif /* HAVE_CONFIG_H */
19
20 #include <unistd.h>
21 #include <stdint.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <dirent.h>
29
30 #if HAVE_ATTR_XATTR_H
31 #include <attr/xattr.h>
32 #elif HAVE_SYS_XATTR_H
33 #include <sys/xattr.h>
34 #endif
35
36 #ifdef HAVE_SYS_EA_H
37 #include <sys/ea.h>
38 #endif
39
40 #ifdef HAVE_SYS_EXTATTR_H
41 #include <sys/extattr.h>
42 #endif
43
44 #include <atalk/adouble.h>
45 #include <atalk/ea.h>
46 #include <atalk/afp.h>
47 #include <atalk/logger.h>
48 #include <atalk/volume.h>
49 #include <atalk/vfs.h>
50 #include <atalk/util.h>
51 #include <atalk/unix.h>
52 #include <atalk/compat.h>
53
54 #ifndef ENOATTR
55 #define ENOATTR ENODATA
56 #endif
57
58
59 /**********************************************************************************
60  * EA VFS funcs for storing EAs in nativa filesystem EAs
61  **********************************************************************************/
62
63 /*
64  * Function: sys_get_easize
65  *
66  * Purpose: get size of a native EA
67  *
68  * Arguments:
69  *
70  *    vol          (r) current volume
71  *    rbuf         (w) DSI reply buffer
72  *    rbuflen      (rw) current length of data in reply buffer
73  *    uname        (r) filename
74  *    oflag        (r) link and create flag
75  *    attruname    (r) name of attribute
76  *
77  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
78  *
79  * Effects:
80  *
81  * Copies EA size into rbuf in network order. Increments *rbuflen +4.
82  */
83 int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
84 {
85     ssize_t   ret;
86     uint32_t  attrsize;
87
88     LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
89
90     if ((oflag & O_NOFOLLOW) ) {
91         ret = sys_lgetxattr(uname, attruname, rbuf +4, 0);
92     }
93     else {
94         ret = sys_getxattr(uname, attruname,  rbuf +4, 0);
95     }
96     
97     if (ret == -1) {
98         memset(rbuf, 0, 4);
99         *rbuflen += 4;
100         switch(errno) {
101         case ELOOP:
102             /* its a symlink and client requested O_NOFOLLOW  */
103             LOG(log_debug, logtype_afpd, "sys_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
104             return AFP_OK;
105
106         case ENOATTR:
107             return AFPERR_MISC;
108
109         default:
110             LOG(log_error, logtype_afpd, "sys_getextattr_size: error: %s", strerror(errno));
111             return AFPERR_MISC;
112         }
113     }
114
115     if (ret > MAX_EA_SIZE) 
116       ret = MAX_EA_SIZE;
117
118     /* Start building reply packet */
119     LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, ret);
120
121     /* length of attribute data */
122     attrsize = htonl((uint32_t)ret);
123     memcpy(rbuf, &attrsize, 4);
124     *rbuflen += 4;
125
126     ret = AFP_OK;
127     return ret;
128 }
129
130 /*
131  * Function: sys_get_eacontent
132  *
133  * Purpose: copy native EA into rbuf
134  *
135  * Arguments:
136  *
137  *    vol          (r) current volume
138  *    rbuf         (w) DSI reply buffer
139  *    rbuflen      (rw) current length of data in reply buffer
140  *    uname        (r) filename
141  *    oflag        (r) link and create flag
142  *    attruname    (r) name of attribute
143  *    maxreply     (r) maximum EA size as of current specs/real-life
144  *
145  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
146  *
147  * Effects:
148  *
149  * Copies EA into rbuf. Increments *rbuflen accordingly.
150  */
151 int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
152 {
153     ssize_t   ret;
154     uint32_t  attrsize;
155
156     /* Start building reply packet */
157
158     maxreply -= MAX_REPLY_EXTRA_BYTES;
159
160     if (maxreply > MAX_EA_SIZE)
161         maxreply = MAX_EA_SIZE;
162
163     LOG(log_debug7, logtype_afpd, "sys_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
164
165     if ((oflag & O_NOFOLLOW) ) {
166         ret = sys_lgetxattr(uname, attruname, rbuf +4, maxreply);
167     }
168     else {
169         ret = sys_getxattr(uname, attruname,  rbuf +4, maxreply);
170     }
171     
172     if (ret == -1) {
173         memset(rbuf, 0, 4);
174         *rbuflen += 4;
175         switch(errno) {
176         case ELOOP:
177             /* its a symlink and client requested O_NOFOLLOW  */
178             LOG(log_debug, logtype_afpd, "sys_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
179             return AFP_OK;
180
181         case ENOATTR:
182             return AFPERR_MISC;
183
184         default:
185             LOG(log_error, logtype_afpd, "sys_getextattr_content(%s): error: %s", attruname, strerror(errno));
186             return AFPERR_MISC;
187         }
188     }
189
190     /* remember where we must store length of attribute data in rbuf */
191     *rbuflen += 4 +ret;
192
193     attrsize = htonl((uint32_t)ret);
194     memcpy(rbuf, &attrsize, 4);
195
196     return AFP_OK;
197 }
198
199 /*
200  * Function: sys_list_eas
201  *
202  * Purpose: copy names of native EAs into attrnamebuf
203  *
204  * Arguments:
205  *
206  *    vol          (r) current volume
207  *    attrnamebuf  (w) store names a consecutive C strings here
208  *    buflen       (rw) length of names in attrnamebuf
209  *    uname        (r) filename
210  *    oflag        (r) link and create flag
211  *
212  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
213  *
214  * Effects:
215  *
216  * Copies names of all EAs of uname as consecutive C strings into rbuf.
217  * Increments *rbuflen accordingly.
218  */
219 int sys_list_eas(VFS_FUNC_ARGS_EA_LIST)
220 {
221     ssize_t attrbuflen = *buflen;
222     int     ret, len, nlen;
223     char    *buf;
224     char    *ptr;
225         
226     buf = malloc(ATTRNAMEBUFSIZ);
227     if (!buf)
228         return AFPERR_MISC;
229
230     if ((oflag & O_NOFOLLOW)) {
231         ret = sys_llistxattr(uname, buf, ATTRNAMEBUFSIZ);
232     }
233     else {
234         ret = sys_listxattr(uname, buf, ATTRNAMEBUFSIZ);
235     }
236
237     if (ret == -1) switch(errno) {
238         case ELOOP:
239             /* its a symlink and client requested O_NOFOLLOW */
240             ret = AFPERR_BADTYPE;
241         default:
242             LOG(log_error, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
243             ret= AFPERR_MISC;
244     }
245     
246     ptr = buf;
247     while (ret > 0)  {
248         len = strlen(ptr);
249
250         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
251         if ( 0 >= ( nlen = convert_string(vol->v_volcharset, CH_UTF8_MAC, ptr, len, attrnamebuf + attrbuflen, 256)) ) {
252             ret = AFPERR_MISC;
253             goto exit;
254         }
255
256         LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr);
257
258         attrbuflen += nlen + 1;
259         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
260             /* Next EA name could overflow, so bail out with error.
261                FIXME: evantually malloc/memcpy/realloc whatever.
262                Is it worth it ? */
263             LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname);
264             ret = AFPERR_MISC;
265             goto exit;
266         }
267         ret -= len +1;
268         ptr += len +1;
269     }
270
271     ret = AFP_OK;
272
273 exit:
274     free(buf);
275     *buflen = attrbuflen;
276     return ret;
277 }
278
279 /*
280  * Function: sys_set_ea
281  *
282  * Purpose: set a native EA
283  *
284  * Arguments:
285  *
286  *    vol          (r) current volume
287  *    uname        (r) filename
288  *    attruname    (r) EA name
289  *    ibuf         (r) buffer with EA content
290  *    attrsize     (r) length EA in ibuf
291  *    oflag        (r) link and create flag
292  *
293  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
294  *
295  * Effects:
296  *
297  */
298 int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
299 {
300     int attr_flag;
301     int ret;
302
303     attr_flag = 0;
304     if ((oflag & O_CREAT) ) 
305         attr_flag |= XATTR_CREATE;
306
307     else if ((oflag & O_TRUNC) ) 
308         attr_flag |= XATTR_REPLACE;
309     
310     if ((oflag & O_NOFOLLOW) ) {
311         ret = sys_lsetxattr(uname, attruname,  ibuf, attrsize,attr_flag);
312     }
313     else {
314         ret = sys_setxattr(uname, attruname,  ibuf, attrsize, attr_flag);
315     }
316
317     if (ret == -1) {
318         switch(errno) {
319         case ELOOP:
320             /* its a symlink and client requested O_NOFOLLOW  */
321             LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): encountered symlink with kXAttrNoFollow",
322                 uname, attruname);
323             return AFP_OK;
324         case EEXIST:
325             LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): EA already exists",
326                 uname, attruname);
327             return AFPERR_EXIST;
328         default:
329             LOG(log_error, logtype_afpd, "sys_set_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
330             return AFPERR_MISC;
331         }
332     }
333
334     return AFP_OK;
335 }
336
337 /*
338  * Function: sys_remove_ea
339  *
340  * Purpose: remove a native EA
341  *
342  * Arguments:
343  *
344  *    vol          (r) current volume
345  *    uname        (r) filename
346  *    attruname    (r) EA name
347  *    oflag        (r) link and create flag
348  *
349  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
350  *
351  * Effects:
352  *
353  * Removes EA attruname from file uname.
354  */
355 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
356 {
357     int ret;
358
359     if ((oflag & O_NOFOLLOW) ) {
360         ret = sys_lremovexattr(uname, attruname);
361     }
362     else {
363         ret = sys_removexattr(uname, attruname);
364     }
365
366     if (ret == -1) {
367         switch(errno) {
368         case ELOOP:
369             /* its a symlink and client requested O_NOFOLLOW  */
370             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): encountered symlink with kXAttrNoFollow", uname);
371             return AFP_OK;
372         case EACCES:
373             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
374             return AFPERR_ACCESS;
375         default:
376             LOG(log_error, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
377             return AFPERR_MISC;
378         }
379     }
380
381     return AFP_OK;
382 }
383
384 /*
385  * @brief Copy EAs
386  *
387  * @note Supports *at semantics, therfor switches back and forth between sfd and cwd
388  */
389 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
390 {
391         int ret = 0;
392     int cwd = -1;
393         ssize_t size;
394         char *names = NULL, *end_names, *name, *value = NULL;
395         unsigned int setxattr_ENOTSUP = 0;
396
397     if (sfd != -1) {
398         if ((cwd = open(".", O_RDONLY)) == -1) {
399             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s",
400                 strerror(errno));
401             ret = -1;
402             goto getout;
403         }
404     }
405
406     if (sfd != -1) {
407         if (fchdir(sfd) == -1) {
408             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
409                 strerror(errno));
410             ret = -1;
411             goto getout;
412         }
413     }
414
415         size = sys_listxattr(src, NULL, 0);
416         if (size < 0) {
417                 if (errno != ENOSYS && errno != ENOTSUP) {
418                         ret = -1;
419                 }
420                 goto getout;
421         }
422         names = malloc(size+1);
423         if (names == NULL) {
424                 ret = -1;
425                 goto getout;
426         }
427         size = sys_listxattr(src, names, size);
428         if (size < 0) {
429                 ret = -1;
430                 goto getout;
431         } else {
432                 names[size] = '\0';
433                 end_names = names + size;
434         }
435
436     if (sfd != -1) {
437         if (fchdir(cwd) == -1) {
438             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
439                 strerror(errno));
440             ret = -1;
441             goto getout;
442         }
443     }
444
445         for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
446                 void *old_value;
447
448                 /* check if this attribute shall be preserved */
449                 if (!*name)
450                         continue;
451
452         if (sfd != -1) {
453             if (fchdir(sfd) == -1) {
454                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
455                     strerror(errno));
456                 ret = -1;
457                 goto getout;
458             }
459         }
460
461                 size = sys_getxattr (src, name, NULL, 0);
462                 if (size < 0) {
463                         ret = -1;
464                         continue;
465                 }
466                 value = realloc(old_value = value, size);
467                 if (size != 0 && value == NULL) {
468                         free(old_value);
469                         ret = -1;
470                 }
471                 size = sys_getxattr(src, name, value, size);
472                 if (size < 0) {
473                         ret = -1;
474                         continue;
475                 }
476
477         if (sfd != -1) {
478             if (fchdir(cwd) == -1) {
479                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
480                     strerror(errno));
481                 ret = -1;
482                 goto getout;
483             }
484         }
485
486                 if (sys_setxattr(dst, name, value, size, 0) != 0) {
487                         if (errno == ENOTSUP)
488                                 setxattr_ENOTSUP++;
489                         else {
490                                 if (errno == ENOSYS) {
491                                         ret = -1;
492                                         /* no hope of getting any further */
493                                         break;
494                                 } else {
495                                         ret = -1;
496                                 }
497                         }
498                 }
499         }
500         if (setxattr_ENOTSUP) {
501                 errno = ENOTSUP;
502                 ret = -1;
503         }
504
505 getout:
506     if (cwd != -1)
507         close(cwd);
508         
509         free(value);
510         free(names);
511
512     if (ret == -1) {
513         switch(errno) {
514         case ENOENT:
515             /* no attribute */
516             break;
517         case EACCES:
518             LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
519             return AFPERR_ACCESS;
520         default:
521             LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
522             return AFPERR_MISC;
523         }
524     }
525
526     return AFP_OK;
527 }