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