2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
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.
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.
17 #endif /* HAVE_CONFIG_H */
24 #include <sys/types.h>
28 #include <arpa/inet.h>
30 #include <atalk/adouble.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>
40 /**********************************************************************************
41 * EA VFS funcs for storing EAs in nativa filesystem EAs
42 **********************************************************************************/
45 * Function: sys_get_easize
47 * Purpose: get size of a native EA
51 * vol (r) current volume
52 * rbuf (w) DSI reply buffer
53 * rbuflen (rw) current length of data in reply buffer
55 * oflag (r) link and create flag
56 * attruname (r) name of attribute
58 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
62 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
64 int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
69 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
73 LOG(log_debug, logtype_afpd, "sys_get_easize(%s): file is already opened", uname);
74 ret = sys_fgetxattr(fd, attruname, rbuf +4, 0);
76 if ((oflag & O_NOFOLLOW) ) {
77 ret = sys_lgetxattr(uname, attruname, rbuf +4, 0);
80 ret = sys_getxattr(uname, attruname, rbuf +4, 0);
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);
97 if (vol->v_obj->afp_version >= 34)
102 LOG(log_debug, logtype_afpd, "sys_getextattr_size: error: %s", strerror(errno));
107 if (ret > MAX_EA_SIZE)
110 /* Start building reply packet */
111 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, ret);
113 /* length of attribute data */
114 attrsize = htonl((uint32_t)ret);
115 memcpy(rbuf, &attrsize, 4);
123 * Function: sys_get_eacontent
125 * Purpose: copy native EA into rbuf
129 * vol (r) current volume
130 * rbuf (w) DSI reply buffer
131 * rbuflen (rw) current length of data in reply buffer
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
141 * Copies EA into rbuf. Increments *rbuflen accordingly.
143 int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
148 /* Start building reply packet */
150 maxreply -= MAX_REPLY_EXTRA_BYTES;
152 if (maxreply > MAX_EA_SIZE)
153 maxreply = MAX_EA_SIZE;
155 LOG(log_debug7, logtype_afpd, "sys_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
159 LOG(log_debug, logtype_afpd, "sys_get_eacontent(%s): file is already opened", uname);
160 ret = sys_fgetxattr(fd, attruname, rbuf +4, maxreply);
162 if ((oflag & O_NOFOLLOW) ) {
163 ret = sys_lgetxattr(uname, attruname, rbuf +4, maxreply);
166 ret = sys_getxattr(uname, attruname, rbuf +4, maxreply);
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);
181 if (vol->v_obj->afp_version >= 34)
182 return AFPERR_NOITEM;
186 LOG(log_debug, logtype_afpd, "sys_getextattr_content(%s): error: %s", attruname, strerror(errno));
191 /* remember where we must store length of attribute data in rbuf */
194 attrsize = htonl((uint32_t)ret);
195 memcpy(rbuf, &attrsize, 4);
201 * Function: sys_list_eas
203 * Purpose: copy names of native EAs into attrnamebuf
207 * vol (r) current volume
208 * attrnamebuf (w) store names a consecutive C strings here
209 * buflen (rw) length of names in attrnamebuf
211 * oflag (r) link and create flag
213 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
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.
222 int sys_list_eas(VFS_FUNC_ARGS_EA_LIST)
224 ssize_t attrbuflen = *buflen;
228 struct adouble ad, *adp;
230 buf = malloc(ATTRNAMEBUFSIZ);
236 LOG(log_debug, logtype_afpd, "sys_list_eas(%s): file is already opened", uname);
237 ret = sys_flistxattr(fd, uname, buf, ATTRNAMEBUFSIZ);
239 if ((oflag & O_NOFOLLOW)) {
240 ret = sys_llistxattr(uname, buf, ATTRNAMEBUFSIZ);
243 ret = sys_listxattr(uname, buf, ATTRNAMEBUFSIZ);
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);
255 LOG(log_debug, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
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)) ) {
270 LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr);
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.
277 LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname);
290 *buflen = attrbuflen;
295 * Function: sys_set_ea
297 * Purpose: set a native EA
301 * vol (r) current volume
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
308 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
313 int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
319 if ((oflag & O_CREAT) )
320 attr_flag |= XATTR_CREATE;
322 else if ((oflag & O_TRUNC) )
323 attr_flag |= XATTR_REPLACE;
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);
330 if ((oflag & O_NOFOLLOW) ) {
331 ret = sys_lsetxattr(uname, attruname, ibuf, attrsize,attr_flag);
334 ret = sys_setxattr(uname, attruname, ibuf, attrsize, attr_flag);
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",
347 LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists",
348 getcwdpath(), uname, attruname);
352 if ((attr_flag & XATTR_REPLACE) && (vol->v_obj->afp_version >= 34))
353 return AFPERR_NOITEM;
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" : "-",
370 * Function: sys_remove_ea
372 * Purpose: remove a native EA
376 * vol (r) current volume
378 * attruname (r) EA name
379 * oflag (r) link and create flag
380 * fd (r) file descriptor
382 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
386 * Removes EA attruname from file uname.
388 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
394 LOG(log_debug, logtype_afpd, "sys_remove_ea(%s): file is already opened", uname);
395 ret = sys_fremovexattr(fd, uname, attruname);
397 if ((oflag & O_NOFOLLOW) ) {
398 ret = sys_lremovexattr(uname, attruname);
401 ret = sys_removexattr(uname, attruname);
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);
413 LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
424 * @note Supports *at semantics, therfor switches back and forth between sfd and cwd
426 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
431 char *names = NULL, *end_names, *name, *value = NULL;
432 unsigned int setxattr_ENOTSUP = 0;
435 if ((cwd = open(".", O_RDONLY)) == -1) {
436 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s",
444 if (fchdir(sfd) == -1) {
445 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
452 size = sys_listxattr(src, NULL, 0);
454 if (errno != ENOSYS && errno != ENOTSUP) {
459 names = malloc(size+1);
464 size = sys_listxattr(src, names, size);
470 end_names = names + size;
474 if (fchdir(cwd) == -1) {
475 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
482 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
485 /* check if this attribute shall be preserved */
489 if (STRCMP(name, ==, AD_EA_META))
493 if (fchdir(sfd) == -1) {
494 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
501 size = sys_getxattr (src, name, NULL, 0);
506 value = realloc(old_value = value, size);
507 if (size != 0 && value == NULL) {
511 size = sys_getxattr(src, name, value, size);
518 if (fchdir(cwd) == -1) {
519 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
526 if (sys_setxattr(dst, name, value, size, 0) != 0) {
527 if (errno == ENOTSUP)
530 if (errno == ENOSYS) {
532 /* no hope of getting any further */
540 if (setxattr_ENOTSUP) {
558 LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
559 return AFPERR_ACCESS;
561 LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));