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