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