]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_sys.c
Fix for #3074076, symlinks with EAs and ACLs
[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): encountered symlink with kXAttrNoFollow",
329                 uname, attruname);
330             return AFP_OK;
331         case EEXIST:
332             LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): EA already exists",
333                 uname, attruname);
334             return AFPERR_EXIST;
335         default:
336             LOG(log_error, logtype_afpd, "sys_set_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
337             return AFPERR_MISC;
338         }
339     }
340
341     return AFP_OK;
342 }
343
344 /*
345  * Function: sys_remove_ea
346  *
347  * Purpose: remove a native EA
348  *
349  * Arguments:
350  *
351  *    vol          (r) current volume
352  *    uname        (r) filename
353  *    attruname    (r) EA name
354  *    oflag        (r) link and create flag
355  *
356  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
357  *
358  * Effects:
359  *
360  * Removes EA attruname from file uname.
361  */
362 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
363 {
364     int ret;
365
366     if ((oflag & O_NOFOLLOW) ) {
367         ret = sys_lremovexattr(uname, attruname);
368     }
369     else {
370         ret = sys_removexattr(uname, attruname);
371     }
372
373     if (ret == -1) {
374         switch(errno) {
375         case OPEN_NOFOLLOW_ERRNO:
376             /* its a symlink and client requested O_NOFOLLOW  */
377             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): encountered symlink with kXAttrNoFollow", uname);
378             return AFP_OK;
379         case EACCES:
380             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
381             return AFPERR_ACCESS;
382         default:
383             LOG(log_error, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
384             return AFPERR_MISC;
385         }
386     }
387
388     return AFP_OK;
389 }
390
391 /*
392  * @brief Copy EAs
393  *
394  * @note Supports *at semantics, therfor switches back and forth between sfd and cwd
395  */
396 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
397 {
398         int ret = 0;
399     int cwd = -1;
400         ssize_t size;
401         char *names = NULL, *end_names, *name, *value = NULL;
402         unsigned int setxattr_ENOTSUP = 0;
403
404     if (sfd != -1) {
405         if ((cwd = open(".", O_RDONLY)) == -1) {
406             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s",
407                 strerror(errno));
408             ret = -1;
409             goto getout;
410         }
411     }
412
413     if (sfd != -1) {
414         if (fchdir(sfd) == -1) {
415             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
416                 strerror(errno));
417             ret = -1;
418             goto getout;
419         }
420     }
421
422         size = sys_listxattr(src, NULL, 0);
423         if (size < 0) {
424                 if (errno != ENOSYS && errno != ENOTSUP) {
425                         ret = -1;
426                 }
427                 goto getout;
428         }
429         names = malloc(size+1);
430         if (names == NULL) {
431                 ret = -1;
432                 goto getout;
433         }
434         size = sys_listxattr(src, names, size);
435         if (size < 0) {
436                 ret = -1;
437                 goto getout;
438         } else {
439                 names[size] = '\0';
440                 end_names = names + size;
441         }
442
443     if (sfd != -1) {
444         if (fchdir(cwd) == -1) {
445             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
446                 strerror(errno));
447             ret = -1;
448             goto getout;
449         }
450     }
451
452         for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
453                 void *old_value;
454
455                 /* check if this attribute shall be preserved */
456                 if (!*name)
457                         continue;
458
459         if (sfd != -1) {
460             if (fchdir(sfd) == -1) {
461                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
462                     strerror(errno));
463                 ret = -1;
464                 goto getout;
465             }
466         }
467
468                 size = sys_getxattr (src, name, NULL, 0);
469                 if (size < 0) {
470                         ret = -1;
471                         continue;
472                 }
473                 value = realloc(old_value = value, size);
474                 if (size != 0 && value == NULL) {
475                         free(old_value);
476                         ret = -1;
477                 }
478                 size = sys_getxattr(src, name, value, size);
479                 if (size < 0) {
480                         ret = -1;
481                         continue;
482                 }
483
484         if (sfd != -1) {
485             if (fchdir(cwd) == -1) {
486                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
487                     strerror(errno));
488                 ret = -1;
489                 goto getout;
490             }
491         }
492
493                 if (sys_setxattr(dst, name, value, size, 0) != 0) {
494                         if (errno == ENOTSUP)
495                                 setxattr_ENOTSUP++;
496                         else {
497                                 if (errno == ENOSYS) {
498                                         ret = -1;
499                                         /* no hope of getting any further */
500                                         break;
501                                 } else {
502                                         ret = -1;
503                                 }
504                         }
505                 }
506         }
507         if (setxattr_ENOTSUP) {
508                 errno = ENOTSUP;
509                 ret = -1;
510         }
511
512 getout:
513     if (cwd != -1)
514         close(cwd);
515         
516         free(value);
517         free(names);
518
519     if (ret == -1) {
520         switch(errno) {
521         case ENOENT:
522             /* no attribute */
523             break;
524         case EACCES:
525             LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
526             return AFPERR_ACCESS;
527         default:
528             LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
529             return AFPERR_MISC;
530         }
531     }
532
533     return AFP_OK;
534 }