]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_sys.c
Convert afp_moveandrename and all called funcs to XXXat semantics if available
[netatalk.git] / libatalk / vfs / ea_sys.c
1 /*
2   $Id: ea_sys.c,v 1.6 2010-03-12 15:16:49 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
53 #ifndef ENOATTR
54 #define ENOATTR ENODATA
55 #endif
56
57
58 /**********************************************************************************
59  * EA VFS funcs for storing EAs in nativa filesystem EAs
60  **********************************************************************************/
61
62 /*
63  * Function: sys_get_easize
64  *
65  * Purpose: get size of a native EA
66  *
67  * Arguments:
68  *
69  *    vol          (r) current volume
70  *    rbuf         (w) DSI reply buffer
71  *    rbuflen      (rw) current length of data in reply buffer
72  *    uname        (r) filename
73  *    oflag        (r) link and create flag
74  *    attruname    (r) name of attribute
75  *
76  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
77  *
78  * Effects:
79  *
80  * Copies EA size into rbuf in network order. Increments *rbuflen +4.
81  */
82 int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
83 {
84     ssize_t   ret;
85     uint32_t  attrsize;
86
87     LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
88
89     if ((oflag & O_NOFOLLOW) ) {
90         ret = sys_lgetxattr(uname, attruname, rbuf +4, 0);
91     }
92     else {
93         ret = sys_getxattr(uname, attruname,  rbuf +4, 0);
94     }
95     
96     if (ret == -1) {
97         memset(rbuf, 0, 4);
98         *rbuflen += 4;
99         switch(errno) {
100         case ELOOP:
101             /* its a symlink and client requested O_NOFOLLOW  */
102             LOG(log_debug, logtype_afpd, "sys_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
103             return AFP_OK;
104
105         case ENOATTR:
106             return AFPERR_MISC;
107
108         default:
109             LOG(log_error, logtype_afpd, "sys_getextattr_size: error: %s", strerror(errno));
110             return AFPERR_MISC;
111         }
112     }
113
114     if (ret > MAX_EA_SIZE) 
115       ret = MAX_EA_SIZE;
116
117     /* Start building reply packet */
118     LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, ret);
119
120     /* length of attribute data */
121     attrsize = htonl((uint32_t)ret);
122     memcpy(rbuf, &attrsize, 4);
123     *rbuflen += 4;
124
125     ret = AFP_OK;
126     return ret;
127 }
128
129 /*
130  * Function: sys_get_eacontent
131  *
132  * Purpose: copy native EA into rbuf
133  *
134  * Arguments:
135  *
136  *    vol          (r) current volume
137  *    rbuf         (w) DSI reply buffer
138  *    rbuflen      (rw) current length of data in reply buffer
139  *    uname        (r) filename
140  *    oflag        (r) link and create flag
141  *    attruname    (r) name of attribute
142  *    maxreply     (r) maximum EA size as of current specs/real-life
143  *
144  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
145  *
146  * Effects:
147  *
148  * Copies EA into rbuf. Increments *rbuflen accordingly.
149  */
150 int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
151 {
152     ssize_t   ret;
153     uint32_t  attrsize;
154
155     /* Start building reply packet */
156
157     maxreply -= MAX_REPLY_EXTRA_BYTES;
158
159     if (maxreply > MAX_EA_SIZE)
160         maxreply = MAX_EA_SIZE;
161
162     LOG(log_debug7, logtype_afpd, "sys_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
163
164     if ((oflag & O_NOFOLLOW) ) {
165         ret = sys_lgetxattr(uname, attruname, rbuf +4, maxreply);
166     }
167     else {
168         ret = sys_getxattr(uname, attruname,  rbuf +4, maxreply);
169     }
170     
171     if (ret == -1) {
172         memset(rbuf, 0, 4);
173         *rbuflen += 4;
174         switch(errno) {
175         case ELOOP:
176             /* its a symlink and client requested O_NOFOLLOW  */
177             LOG(log_debug, logtype_afpd, "sys_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
178             return AFP_OK;
179
180         case ENOATTR:
181             return AFPERR_MISC;
182
183         default:
184             LOG(log_error, logtype_afpd, "sys_getextattr_content(%s): error: %s", attruname, strerror(errno));
185             return AFPERR_MISC;
186         }
187     }
188
189     /* remember where we must store length of attribute data in rbuf */
190     *rbuflen += 4 +ret;
191
192     attrsize = htonl((uint32_t)ret);
193     memcpy(rbuf, &attrsize, 4);
194
195     return AFP_OK;
196 }
197
198 /*
199  * Function: sys_list_eas
200  *
201  * Purpose: copy names of native EAs into attrnamebuf
202  *
203  * Arguments:
204  *
205  *    vol          (r) current volume
206  *    attrnamebuf  (w) store names a consecutive C strings here
207  *    buflen       (rw) length of names in attrnamebuf
208  *    uname        (r) filename
209  *    oflag        (r) link and create flag
210  *
211  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
212  *
213  * Effects:
214  *
215  * Copies names of all EAs of uname as consecutive C strings into rbuf.
216  * Increments *rbuflen accordingly.
217  */
218 int sys_list_eas(VFS_FUNC_ARGS_EA_LIST)
219 {
220     ssize_t attrbuflen = *buflen;
221     int     ret, len, nlen;
222     char    *buf;
223     char    *ptr;
224         
225     buf = malloc(ATTRNAMEBUFSIZ);
226     if (!buf)
227         return AFPERR_MISC;
228
229     if ((oflag & O_NOFOLLOW)) {
230         ret = sys_llistxattr(uname, buf, ATTRNAMEBUFSIZ);
231     }
232     else {
233         ret = sys_listxattr(uname, buf, ATTRNAMEBUFSIZ);
234     }
235
236     if (ret == -1) switch(errno) {
237         case ELOOP:
238             /* its a symlink and client requested O_NOFOLLOW */
239             ret = AFPERR_BADTYPE;
240         default:
241             LOG(log_error, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
242             ret= AFPERR_MISC;
243     }
244     
245     ptr = buf;
246     while (ret > 0)  {
247         len = strlen(ptr);
248
249         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
250         if ( 0 >= ( nlen = convert_string(vol->v_volcharset, CH_UTF8_MAC, ptr, len, attrnamebuf + attrbuflen, 256)) ) {
251             ret = AFPERR_MISC;
252             goto exit;
253         }
254
255         LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr);
256
257         attrbuflen += nlen + 1;
258         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
259             /* Next EA name could overflow, so bail out with error.
260                FIXME: evantually malloc/memcpy/realloc whatever.
261                Is it worth it ? */
262             LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname);
263             ret = AFPERR_MISC;
264             goto exit;
265         }
266         ret -= len +1;
267         ptr += len +1;
268     }
269
270     ret = AFP_OK;
271
272 exit:
273     free(buf);
274     *buflen = attrbuflen;
275     return ret;
276 }
277
278 /*
279  * Function: sys_set_ea
280  *
281  * Purpose: set a native EA
282  *
283  * Arguments:
284  *
285  *    vol          (r) current volume
286  *    uname        (r) filename
287  *    attruname    (r) EA name
288  *    ibuf         (r) buffer with EA content
289  *    attrsize     (r) length EA in ibuf
290  *    oflag        (r) link and create flag
291  *
292  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
293  *
294  * Effects:
295  *
296  */
297 int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
298 {
299     int attr_flag;
300     int ret;
301
302     attr_flag = 0;
303     if ((oflag & O_CREAT) ) 
304         attr_flag |= XATTR_CREATE;
305
306     else if ((oflag & O_TRUNC) ) 
307         attr_flag |= XATTR_REPLACE;
308     
309     if ((oflag & O_NOFOLLOW) ) {
310         ret = sys_lsetxattr(uname, attruname,  ibuf, attrsize,attr_flag);
311     }
312     else {
313         ret = sys_setxattr(uname, attruname,  ibuf, attrsize, attr_flag);
314     }
315
316     if (ret == -1) {
317         switch(errno) {
318         case ELOOP:
319             /* its a symlink and client requested O_NOFOLLOW  */
320             LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): encountered symlink with kXAttrNoFollow",
321                 uname, attruname);
322             return AFP_OK;
323         case EEXIST:
324             LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): EA already exists",
325                 uname, attruname);
326             return AFPERR_EXIST;
327         default:
328             LOG(log_error, logtype_afpd, "sys_set_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
329             return AFPERR_MISC;
330         }
331     }
332
333     return AFP_OK;
334 }
335
336 /*
337  * Function: sys_remove_ea
338  *
339  * Purpose: remove a native EA
340  *
341  * Arguments:
342  *
343  *    vol          (r) current volume
344  *    uname        (r) filename
345  *    attruname    (r) EA name
346  *    oflag        (r) link and create flag
347  *
348  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
349  *
350  * Effects:
351  *
352  * Removes EA attruname from file uname.
353  */
354 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
355 {
356     int ret;
357
358     if ((oflag & O_NOFOLLOW) ) {
359         ret = sys_lremovexattr(uname, attruname);
360     }
361     else {
362         ret = sys_removexattr(uname, attruname);
363     }
364
365     if (ret == -1) {
366         switch(errno) {
367         case ELOOP:
368             /* its a symlink and client requested O_NOFOLLOW  */
369             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): encountered symlink with kXAttrNoFollow", uname);
370             return AFP_OK;
371         case EACCES:
372             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
373             return AFPERR_ACCESS;
374         default:
375             LOG(log_error, 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 (sfd != -1) {
452             if (fchdir(sfd) == -1) {
453                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
454                     strerror(errno));
455                 ret = -1;
456                 goto getout;
457             }
458         }
459
460                 size = sys_getxattr (src, name, NULL, 0);
461                 if (size < 0) {
462                         ret = -1;
463                         continue;
464                 }
465                 value = realloc(old_value = value, size);
466                 if (size != 0 && value == NULL) {
467                         free(old_value);
468                         ret = -1;
469                 }
470                 size = sys_getxattr(src, name, value, size);
471                 if (size < 0) {
472                         ret = -1;
473                         continue;
474                 }
475
476         if (sfd != -1) {
477             if (fchdir(cwd) == -1) {
478                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
479                     strerror(errno));
480                 ret = -1;
481                 goto getout;
482             }
483         }
484
485                 if (sys_setxattr(dst, name, value, size, 0) != 0) {
486                         if (errno == ENOTSUP)
487                                 setxattr_ENOTSUP++;
488                         else {
489                                 if (errno == ENOSYS) {
490                                         ret = -1;
491                                         /* no hope of getting any further */
492                                         break;
493                                 } else {
494                                         ret = -1;
495                                 }
496                         }
497                 }
498         }
499         if (setxattr_ENOTSUP) {
500                 errno = ENOTSUP;
501                 ret = -1;
502         }
503
504 getout:
505     if (cwd != -1)
506         close(cwd);
507         
508         free(value);
509         free(names);
510
511     if (ret == -1) {
512         switch(errno) {
513         case ENOENT:
514             /* no attribute */
515             break;
516         case EACCES:
517             LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
518             return AFPERR_ACCESS;
519         default:
520             LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
521             return AFPERR_MISC;
522         }
523     }
524
525     return AFP_OK;
526 }