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