]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_sys.c
e94956c21d44ce2e5c2f75f87133c201c7d81543
[netatalk.git] / libatalk / vfs / ea_sys.c
1 /*
2   $Id: ea_sys.c,v 1.4 2009-12-04 10:26:10 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         default:
324             LOG(log_error, logtype_afpd, "sys_set_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
325             return AFPERR_MISC;
326         }
327     }
328
329     return AFP_OK;
330 }
331
332 /*
333  * Function: sys_remove_ea
334  *
335  * Purpose: remove a native EA
336  *
337  * Arguments:
338  *
339  *    vol          (r) current volume
340  *    uname        (r) filename
341  *    attruname    (r) EA name
342  *    oflag        (r) link and create flag
343  *
344  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
345  *
346  * Effects:
347  *
348  * Removes EA attruname from file uname.
349  */
350 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
351 {
352     int ret;
353
354     if ((oflag & O_NOFOLLOW) ) {
355         ret = sys_lremovexattr(uname, attruname);
356     }
357     else {
358         ret = sys_removexattr(uname, attruname);
359     }
360
361     if (ret == -1) {
362         switch(errno) {
363         case ELOOP:
364             /* its a symlink and client requested O_NOFOLLOW  */
365             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): encountered symlink with kXAttrNoFollow", uname);
366             return AFP_OK;
367         case EACCES:
368             LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
369             return AFPERR_ACCESS;
370         default:
371             LOG(log_error, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
372             return AFPERR_MISC;
373         }
374     }
375
376     return AFP_OK;
377 }
378
379 /* --------------------- 
380    copy EA 
381 */
382 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
383 {
384         int ret = 0;
385         ssize_t size;
386         char *names = NULL, *end_names, *name, *value = NULL;
387         unsigned int setxattr_ENOTSUP = 0;
388
389         size = sys_listxattr(src, NULL, 0);
390         if (size < 0) {
391                 if (errno != ENOSYS && errno != ENOTSUP) {
392                         ret = -1;
393                 }
394                 goto getout;
395         }
396         names = malloc(size+1);
397         if (names == NULL) {
398                 ret = -1;
399                 goto getout;
400         }
401         size = sys_listxattr(src, names, size);
402         if (size < 0) {
403                 ret = -1;
404                 goto getout;
405         } else {
406                 names[size] = '\0';
407                 end_names = names + size;
408         }
409
410         for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
411                 void *old_value;
412
413                 /* check if this attribute shall be preserved */
414                 if (!*name)
415                         continue;
416
417                 size = sys_getxattr (src, name, NULL, 0);
418                 if (size < 0) {
419                         ret = -1;
420                         continue;
421                 }
422                 value = realloc(old_value = value, size);
423                 if (size != 0 && value == NULL) {
424                         free(old_value);
425                         ret = -1;
426                 }
427                 size = sys_getxattr(src, name, value, size);
428                 if (size < 0) {
429                         ret = -1;
430                         continue;
431                 }
432                 if (sys_setxattr(dst, name, value, size, 0) != 0) {
433                         if (errno == ENOTSUP)
434                                 setxattr_ENOTSUP++;
435                         else {
436                                 if (errno == ENOSYS) {
437                                         ret = -1;
438                                         /* no hope of getting any further */
439                                         break;
440                                 } else {
441                                         ret = -1;
442                                 }
443                         }
444                 }
445         }
446         if (setxattr_ENOTSUP) {
447                 errno = ENOTSUP;
448                 ret = -1;
449         }
450
451 getout:
452         free(value);
453         free(names);
454
455     if (ret == -1) {
456         switch(errno) {
457         case ENOENT:
458             /* no attribute */
459             break;
460         case EACCES:
461             LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
462             return AFPERR_ACCESS;
463         default:
464             LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
465             return AFPERR_MISC;
466         }
467     }
468
469     return AFP_OK;
470 }