]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_sys.c
Fix for incomplete AFP 3.4 commit 70e3fcd8744752d6de0b93d089f41c28dca30bde
[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): symlink with kXAttrNoFollow", uname);
85             return AFPERR_MISC;
86
87         case ENOATTR:
88         case ENOENT:
89             if (vol->v_obj->afp_version >= 34)
90                 return AFPERR_NOITEM;
91             return AFPERR_MISC;
92
93         default:
94             LOG(log_debug, logtype_afpd, "sys_getextattr_size: error: %s", strerror(errno));
95             return AFPERR_MISC;
96         }
97     }
98
99     if (ret > MAX_EA_SIZE) 
100       ret = MAX_EA_SIZE;
101
102     /* Start building reply packet */
103     LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, ret);
104
105     /* length of attribute data */
106     attrsize = htonl((uint32_t)ret);
107     memcpy(rbuf, &attrsize, 4);
108     *rbuflen += 4;
109
110     ret = AFP_OK;
111     return ret;
112 }
113
114 /*
115  * Function: sys_get_eacontent
116  *
117  * Purpose: copy native EA into rbuf
118  *
119  * Arguments:
120  *
121  *    vol          (r) current volume
122  *    rbuf         (w) DSI reply buffer
123  *    rbuflen      (rw) current length of data in reply buffer
124  *    uname        (r) filename
125  *    oflag        (r) link and create flag
126  *    attruname    (r) name of attribute
127  *    maxreply     (r) maximum EA size as of current specs/real-life
128  *
129  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
130  *
131  * Effects:
132  *
133  * Copies EA into rbuf. Increments *rbuflen accordingly.
134  */
135 int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
136 {
137     ssize_t   ret;
138     uint32_t  attrsize;
139
140     /* Start building reply packet */
141
142     maxreply -= MAX_REPLY_EXTRA_BYTES;
143
144     if (maxreply > MAX_EA_SIZE)
145         maxreply = MAX_EA_SIZE;
146
147     LOG(log_debug7, logtype_afpd, "sys_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
148
149     if ((oflag & O_NOFOLLOW) ) {
150         ret = sys_lgetxattr(uname, attruname, rbuf +4, maxreply);
151     }
152     else {
153         ret = sys_getxattr(uname, attruname,  rbuf +4, maxreply);
154     }
155     
156     if (ret == -1) {
157         memset(rbuf, 0, 4);
158         *rbuflen += 4;
159         switch(errno) {
160         case OPEN_NOFOLLOW_ERRNO:
161             /* its a symlink and client requested O_NOFOLLOW  */
162             LOG(log_debug, logtype_afpd, "sys_getextattr_content(%s): symlink with kXAttrNoFollow", uname);
163             return AFPERR_MISC;
164
165         case ENOATTR:
166             if (vol->v_obj->afp_version >= 34)
167                 return AFPERR_NOITEM;
168             return AFPERR_MISC;
169
170         default:
171             LOG(log_debug, logtype_afpd, "sys_getextattr_content(%s): error: %s", attruname, strerror(errno));
172             return AFPERR_MISC;
173         }
174     }
175
176     /* remember where we must store length of attribute data in rbuf */
177     *rbuflen += 4 +ret;
178
179     attrsize = htonl((uint32_t)ret);
180     memcpy(rbuf, &attrsize, 4);
181
182     return AFP_OK;
183 }
184
185 /*
186  * Function: sys_list_eas
187  *
188  * Purpose: copy names of native EAs into attrnamebuf
189  *
190  * Arguments:
191  *
192  *    vol          (r) current volume
193  *    attrnamebuf  (w) store names a consecutive C strings here
194  *    buflen       (rw) length of names in attrnamebuf
195  *    uname        (r) filename
196  *    oflag        (r) link and create flag
197  *
198  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
199  *
200  * Effects:
201  *
202  * Copies names of all EAs of uname as consecutive C strings into rbuf.
203  * Increments *rbuflen accordingly.
204  * We hide the adouble:ea extended attributes here, but we currently
205  * allow reading, writing and deleteting them.
206  */
207 int sys_list_eas(VFS_FUNC_ARGS_EA_LIST)
208 {
209     ssize_t attrbuflen = *buflen;
210     int     ret, len, nlen;
211     char    *buf;
212     char    *ptr;
213         
214     buf = malloc(ATTRNAMEBUFSIZ);
215     if (!buf)
216         return AFPERR_MISC;
217
218     if ((oflag & O_NOFOLLOW)) {
219         ret = sys_llistxattr(uname, buf, ATTRNAMEBUFSIZ);
220     }
221     else {
222         ret = sys_listxattr(uname, buf, ATTRNAMEBUFSIZ);
223     }
224
225     if (ret == -1) switch(errno) {
226         case OPEN_NOFOLLOW_ERRNO:
227             /* its a symlink and client requested O_NOFOLLOW, we pretend 0 EAs */
228             LOG(log_debug, logtype_afpd, "sys_list_extattr(%s): symlink with kXAttrNoFollow", uname);
229             ret = AFP_OK;
230             goto exit;
231         default:
232             LOG(log_debug, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
233             ret = AFPERR_MISC;
234             goto exit;
235     }
236     
237     ptr = buf;
238     while (ret > 0)  {
239         len = strlen(ptr);
240         if (NOT_NETATALK_EA(ptr)) {
241             /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
242             if ( 0 >= ( nlen = convert_string(vol->v_volcharset, CH_UTF8_MAC, ptr, len, attrnamebuf + attrbuflen, 256)) ) {
243                 ret = AFPERR_MISC;
244                 goto exit;
245             }
246
247             LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr);
248
249             attrbuflen += nlen + 1;
250             if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
251                 /* Next EA name could overflow, so bail out with error.
252                    FIXME: evantually malloc/memcpy/realloc whatever.
253                    Is it worth it ? */
254                 LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname);
255                 ret = AFPERR_MISC;
256                 goto exit;
257             }
258         }
259         ret -= len + 1;
260         ptr += len + 1;
261     }
262
263     ret = AFP_OK;
264
265 exit:
266     free(buf);
267     *buflen = attrbuflen;
268     return ret;
269 }
270
271 /*
272  * Function: sys_set_ea
273  *
274  * Purpose: set a native EA
275  *
276  * Arguments:
277  *
278  *    vol          (r) current volume
279  *    uname        (r) filename
280  *    attruname    (r) EA name
281  *    ibuf         (r) buffer with EA content
282  *    attrsize     (r) length EA in ibuf
283  *    oflag        (r) link and create flag
284  *
285  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
286  *
287  * Effects:
288  *
289  */
290 int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
291 {
292     int attr_flag;
293     int ret;
294
295     attr_flag = 0;
296     if ((oflag & O_CREAT) ) 
297         attr_flag |= XATTR_CREATE;
298
299     else if ((oflag & O_TRUNC) ) 
300         attr_flag |= XATTR_REPLACE;
301     
302     if ((oflag & O_NOFOLLOW) ) {
303         ret = sys_lsetxattr(uname, attruname,  ibuf, attrsize,attr_flag);
304     }
305     else {
306         ret = sys_setxattr(uname, attruname,  ibuf, attrsize, attr_flag);
307     }
308
309     if (ret == -1) {
310         switch(errno) {
311         case OPEN_NOFOLLOW_ERRNO:
312             /* its a symlink and client requested O_NOFOLLOW  */
313             LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s\", ea:'%s'): symlink with kXAttrNoFollow",
314                 uname, attruname);
315             return AFP_OK;
316         case EEXIST:
317             LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists",
318                 getcwdpath(), uname, attruname);
319             return AFPERR_EXIST;
320         case ENOATTR:
321         case ENOENT:
322             if ((attr_flag & XATTR_REPLACE) && (vol->v_obj->afp_version >= 34))
323                 return AFPERR_NOITEM;
324             return AFPERR_MISC;
325         default:
326             LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s",
327                 getcwdpath(), uname, attruname, attrsize, 
328                 oflag & O_CREAT ? "XATTR_CREATE" : "-",
329                 oflag & O_TRUNC ? "XATTR_REPLACE" : "-",
330                 oflag & O_NOFOLLOW ? "O_NOFOLLOW" : "-",
331                 strerror(errno));
332             return AFPERR_MISC;
333         }
334     }
335
336     return AFP_OK;
337 }
338
339 /*
340  * Function: sys_remove_ea
341  *
342  * Purpose: remove a native EA
343  *
344  * Arguments:
345  *
346  *    vol          (r) current volume
347  *    uname        (r) filename
348  *    attruname    (r) EA name
349  *    oflag        (r) link and create flag
350  *
351  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
352  *
353  * Effects:
354  *
355  * Removes EA attruname from file uname.
356  */
357 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
358 {
359     int ret;
360
361     if ((oflag & O_NOFOLLOW) ) {
362         ret = sys_lremovexattr(uname, attruname);
363     }
364     else {
365         ret = sys_removexattr(uname, attruname);
366     }
367
368     if (ret == -1) {
369         switch(errno) {
370         case OPEN_NOFOLLOW_ERRNO:
371             /* its a symlink and client requested O_NOFOLLOW  */
372             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): symlink with kXAttrNoFollow", uname);
373             return AFP_OK;
374         default:
375             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
376             return AFPERR_MISC;
377         }
378     }
379
380     return AFP_OK;
381 }
382
383 /*
384  * @brief Copy EAs
385  *
386  * @note Supports *at semantics, therfor switches back and forth between sfd and cwd
387  */
388 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
389 {
390         int ret = 0;
391     int cwd = -1;
392         ssize_t size;
393         char *names = NULL, *end_names, *name, *value = NULL;
394         unsigned int setxattr_ENOTSUP = 0;
395
396     if (sfd != -1) {
397         if ((cwd = open(".", O_RDONLY)) == -1) {
398             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s",
399                 strerror(errno));
400             ret = -1;
401             goto getout;
402         }
403     }
404
405     if (sfd != -1) {
406         if (fchdir(sfd) == -1) {
407             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
408                 strerror(errno));
409             ret = -1;
410             goto getout;
411         }
412     }
413
414         size = sys_listxattr(src, NULL, 0);
415         if (size < 0) {
416                 if (errno != ENOSYS && errno != ENOTSUP) {
417                         ret = -1;
418                 }
419                 goto getout;
420         }
421         names = malloc(size+1);
422         if (names == NULL) {
423                 ret = -1;
424                 goto getout;
425         }
426         size = sys_listxattr(src, names, size);
427         if (size < 0) {
428                 ret = -1;
429                 goto getout;
430         } else {
431                 names[size] = '\0';
432                 end_names = names + size;
433         }
434
435     if (sfd != -1) {
436         if (fchdir(cwd) == -1) {
437             LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
438                 strerror(errno));
439             ret = -1;
440             goto getout;
441         }
442     }
443
444         for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
445                 void *old_value;
446
447                 /* check if this attribute shall be preserved */
448                 if (!*name)
449                         continue;
450
451         if (STRCMP(name, ==, AD_EA_META))
452             continue;
453
454         if (sfd != -1) {
455             if (fchdir(sfd) == -1) {
456                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
457                     strerror(errno));
458                 ret = -1;
459                 goto getout;
460             }
461         }
462
463                 size = sys_getxattr (src, name, NULL, 0);
464                 if (size < 0) {
465                         ret = -1;
466                         continue;
467                 }
468                 value = realloc(old_value = value, size);
469                 if (size != 0 && value == NULL) {
470                         free(old_value);
471                         ret = -1;
472                 }
473                 size = sys_getxattr(src, name, value, size);
474                 if (size < 0) {
475                         ret = -1;
476                         continue;
477                 }
478
479         if (sfd != -1) {
480             if (fchdir(cwd) == -1) {
481                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
482                     strerror(errno));
483                 ret = -1;
484                 goto getout;
485             }
486         }
487
488                 if (sys_setxattr(dst, name, value, size, 0) != 0) {
489                         if (errno == ENOTSUP)
490                                 setxattr_ENOTSUP++;
491                         else {
492                                 if (errno == ENOSYS) {
493                                         ret = -1;
494                                         /* no hope of getting any further */
495                                         break;
496                                 } else {
497                                         ret = -1;
498                                 }
499                         }
500                 }
501         }
502         if (setxattr_ENOTSUP) {
503                 errno = ENOTSUP;
504                 ret = -1;
505         }
506
507 getout:
508     if (cwd != -1)
509         close(cwd);
510         
511         free(value);
512         free(names);
513
514     if (ret == -1) {
515         switch(errno) {
516         case ENOENT:
517             /* no attribute */
518             break;
519         case EACCES:
520             LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
521             return AFPERR_ACCESS;
522         default:
523             LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
524             return AFPERR_MISC;
525         }
526     }
527
528     return AFP_OK;
529 }