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