]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_sys.c
Fix fce merge conflict
[netatalk.git] / libatalk / vfs / ea_sys.c
1 /*
2   $Id: ea_sys.c,v 1.8 2010-04-13 08:05:06 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 OPEN_NOFOLLOW_ERRNO:
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 OPEN_NOFOLLOW_ERRNO:
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 OPEN_NOFOLLOW_ERRNO:
239             /* its a symlink and client requested O_NOFOLLOW */
240             ret = AFPERR_BADTYPE;
241             goto exit;
242 #ifdef HAVE_ATTROPEN            /* Solaris */
243         case ENOATTR:
244             ret = AFP_OK;
245             goto exit;
246 #endif
247         default:
248             LOG(log_error, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
249             ret= AFPERR_MISC;
250             goto exit;
251     }
252     
253     ptr = buf;
254     while (ret > 0)  {
255         len = strlen(ptr);
256
257         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
258         if ( 0 >= ( nlen = convert_string(vol->v_volcharset, CH_UTF8_MAC, ptr, len, attrnamebuf + attrbuflen, 256)) ) {
259             ret = AFPERR_MISC;
260             goto exit;
261         }
262
263         LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr);
264
265         attrbuflen += nlen + 1;
266         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
267             /* Next EA name could overflow, so bail out with error.
268                FIXME: evantually malloc/memcpy/realloc whatever.
269                Is it worth it ? */
270             LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname);
271             ret = AFPERR_MISC;
272             goto exit;
273         }
274         ret -= len +1;
275         ptr += len +1;
276     }
277
278     ret = AFP_OK;
279
280 exit:
281     free(buf);
282     *buflen = attrbuflen;
283     return ret;
284 }
285
286 /*
287  * Function: sys_set_ea
288  *
289  * Purpose: set a native EA
290  *
291  * Arguments:
292  *
293  *    vol          (r) current volume
294  *    uname        (r) filename
295  *    attruname    (r) EA name
296  *    ibuf         (r) buffer with EA content
297  *    attrsize     (r) length EA in ibuf
298  *    oflag        (r) link and create flag
299  *
300  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
301  *
302  * Effects:
303  *
304  */
305 int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
306 {
307     int attr_flag;
308     int ret;
309
310     attr_flag = 0;
311     if ((oflag & O_CREAT) ) 
312         attr_flag |= XATTR_CREATE;
313
314     else if ((oflag & O_TRUNC) ) 
315         attr_flag |= XATTR_REPLACE;
316     
317     if ((oflag & O_NOFOLLOW) ) {
318         ret = sys_lsetxattr(uname, attruname,  ibuf, attrsize,attr_flag);
319     }
320     else {
321         ret = sys_setxattr(uname, attruname,  ibuf, attrsize, attr_flag);
322     }
323
324     if (ret == -1) {
325         switch(errno) {
326         case OPEN_NOFOLLOW_ERRNO:
327             /* its a symlink and client requested O_NOFOLLOW  */
328             LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): encountered symlink with kXAttrNoFollow",
329                 getcwdpath(), uname, attruname);
330             return AFP_OK;
331         case EEXIST:
332             LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists",
333                 getcwdpath(), uname, attruname);
334             return AFPERR_EXIST;
335         default:
336             LOG(log_error, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s",
337                 getcwdpath(), uname, attruname, attrsize, 
338                 oflag & O_CREAT ? "XATTR_CREATE" : "-",
339                 oflag & O_TRUNC ? "XATTR_REPLACE" : "-",
340                 oflag & O_NOFOLLOW ? "O_NOFOLLOW" : "-",
341                 strerror(errno));
342             return AFPERR_MISC;
343         }
344     }
345
346     return AFP_OK;
347 }
348
349 /*
350  * Function: sys_remove_ea
351  *
352  * Purpose: remove a native EA
353  *
354  * Arguments:
355  *
356  *    vol          (r) current volume
357  *    uname        (r) filename
358  *    attruname    (r) EA name
359  *    oflag        (r) link and create flag
360  *
361  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
362  *
363  * Effects:
364  *
365  * Removes EA attruname from file uname.
366  */
367 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
368 {
369     int ret;
370
371     if ((oflag & O_NOFOLLOW) ) {
372         ret = sys_lremovexattr(uname, attruname);
373     }
374     else {
375         ret = sys_removexattr(uname, attruname);
376     }
377
378     if (ret == -1) {
379         switch(errno) {
380         case OPEN_NOFOLLOW_ERRNO:
381             /* its a symlink and client requested O_NOFOLLOW  */
382             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): encountered symlink with kXAttrNoFollow", uname);
383             return AFP_OK;
384         case EACCES:
385             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
386             return AFPERR_ACCESS;
387         default:
388             LOG(log_error, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
389             return AFPERR_MISC;
390         }
391     }
392
393     return AFP_OK;
394 }
395
396 /*
397  * @brief Copy EAs
398  *
399  * @note Supports *at semantics, therfor switches back and forth between sfd and cwd
400  */
401 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
402 {
403         int ret = 0;
404     int cwd = -1;
405         ssize_t size;
406         char *names = NULL, *end_names, *name, *value = NULL;
407         unsigned int setxattr_ENOTSUP = 0;
408
409     if (sfd != -1) {
410         if ((cwd = open(".", O_RDONLY)) == -1) {
411             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s",
412                 strerror(errno));
413             ret = -1;
414             goto getout;
415         }
416     }
417
418     if (sfd != -1) {
419         if (fchdir(sfd) == -1) {
420             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
421                 strerror(errno));
422             ret = -1;
423             goto getout;
424         }
425     }
426
427         size = sys_listxattr(src, NULL, 0);
428         if (size < 0) {
429                 if (errno != ENOSYS && errno != ENOTSUP) {
430                         ret = -1;
431                 }
432                 goto getout;
433         }
434         names = malloc(size+1);
435         if (names == NULL) {
436                 ret = -1;
437                 goto getout;
438         }
439         size = sys_listxattr(src, names, size);
440         if (size < 0) {
441                 ret = -1;
442                 goto getout;
443         } else {
444                 names[size] = '\0';
445                 end_names = names + size;
446         }
447
448     if (sfd != -1) {
449         if (fchdir(cwd) == -1) {
450             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
451                 strerror(errno));
452             ret = -1;
453             goto getout;
454         }
455     }
456
457         for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
458                 void *old_value;
459
460                 /* check if this attribute shall be preserved */
461                 if (!*name)
462                         continue;
463
464         if (sfd != -1) {
465             if (fchdir(sfd) == -1) {
466                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
467                     strerror(errno));
468                 ret = -1;
469                 goto getout;
470             }
471         }
472
473                 size = sys_getxattr (src, name, NULL, 0);
474                 if (size < 0) {
475                         ret = -1;
476                         continue;
477                 }
478                 value = realloc(old_value = value, size);
479                 if (size != 0 && value == NULL) {
480                         free(old_value);
481                         ret = -1;
482                 }
483                 size = sys_getxattr(src, name, value, size);
484                 if (size < 0) {
485                         ret = -1;
486                         continue;
487                 }
488
489         if (sfd != -1) {
490             if (fchdir(cwd) == -1) {
491                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
492                     strerror(errno));
493                 ret = -1;
494                 goto getout;
495             }
496         }
497
498                 if (sys_setxattr(dst, name, value, size, 0) != 0) {
499                         if (errno == ENOTSUP)
500                                 setxattr_ENOTSUP++;
501                         else {
502                                 if (errno == ENOSYS) {
503                                         ret = -1;
504                                         /* no hope of getting any further */
505                                         break;
506                                 } else {
507                                         ret = -1;
508                                 }
509                         }
510                 }
511         }
512         if (setxattr_ENOTSUP) {
513                 errno = ENOTSUP;
514                 ret = -1;
515         }
516
517 getout:
518     if (cwd != -1)
519         close(cwd);
520         
521         free(value);
522         free(names);
523
524     if (ret == -1) {
525         switch(errno) {
526         case ENOENT:
527             /* no attribute */
528             break;
529         case EACCES:
530             LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
531             return AFPERR_ACCESS;
532         default:
533             LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
534             return AFPERR_MISC;
535         }
536     }
537
538     return AFP_OK;
539 }