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