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>
29 #include <atalk/adouble.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>
40 #define ENOATTR ENODATA
44 /**********************************************************************************
45 * EA VFS funcs for storing EAs in nativa filesystem EAs
46 **********************************************************************************/
49 * Function: sys_get_easize
51 * Purpose: get size of a native EA
55 * vol (r) current volume
56 * rbuf (w) DSI reply buffer
57 * rbuflen (rw) current length of data in reply buffer
59 * oflag (r) link and create flag
60 * attruname (r) name of attribute
62 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
66 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
68 int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
73 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
75 if ((oflag & O_NOFOLLOW) ) {
76 ret = sys_lgetxattr(uname, attruname, rbuf +4, 0);
79 ret = sys_getxattr(uname, attruname, rbuf +4, 0);
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);
95 LOG(log_error, logtype_afpd, "sys_getextattr_size: error: %s", strerror(errno));
100 if (ret > MAX_EA_SIZE)
103 /* Start building reply packet */
104 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, ret);
106 /* length of attribute data */
107 attrsize = htonl((uint32_t)ret);
108 memcpy(rbuf, &attrsize, 4);
116 * Function: sys_get_eacontent
118 * Purpose: copy native EA into rbuf
122 * vol (r) current volume
123 * rbuf (w) DSI reply buffer
124 * rbuflen (rw) current length of data in reply buffer
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
130 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
134 * Copies EA into rbuf. Increments *rbuflen accordingly.
136 int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
141 /* Start building reply packet */
143 maxreply -= MAX_REPLY_EXTRA_BYTES;
145 if (maxreply > MAX_EA_SIZE)
146 maxreply = MAX_EA_SIZE;
148 LOG(log_debug7, logtype_afpd, "sys_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
150 if ((oflag & O_NOFOLLOW) ) {
151 ret = sys_lgetxattr(uname, attruname, rbuf +4, maxreply);
154 ret = sys_getxattr(uname, attruname, rbuf +4, maxreply);
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);
170 LOG(log_error, logtype_afpd, "sys_getextattr_content(%s): error: %s", attruname, strerror(errno));
175 /* remember where we must store length of attribute data in rbuf */
178 attrsize = htonl((uint32_t)ret);
179 memcpy(rbuf, &attrsize, 4);
185 * Function: sys_list_eas
187 * Purpose: copy names of native EAs into attrnamebuf
191 * vol (r) current volume
192 * attrnamebuf (w) store names a consecutive C strings here
193 * buflen (rw) length of names in attrnamebuf
195 * oflag (r) link and create flag
197 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
201 * Copies names of all EAs of uname as consecutive C strings into rbuf.
202 * Increments *rbuflen accordingly.
204 int sys_list_eas(VFS_FUNC_ARGS_EA_LIST)
206 ssize_t attrbuflen = *buflen;
211 buf = malloc(ATTRNAMEBUFSIZ);
215 if ((oflag & O_NOFOLLOW)) {
216 ret = sys_llistxattr(uname, buf, ATTRNAMEBUFSIZ);
219 ret = sys_listxattr(uname, buf, ATTRNAMEBUFSIZ);
222 if (ret == -1) switch(errno) {
223 case OPEN_NOFOLLOW_ERRNO:
224 /* its a symlink and client requested O_NOFOLLOW */
225 ret = AFPERR_BADTYPE;
227 #ifdef HAVE_ATTROPEN /* Solaris */
233 LOG(log_error, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
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)) ) {
248 LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr);
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.
255 LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname);
267 *buflen = attrbuflen;
272 * Function: sys_set_ea
274 * Purpose: set a native EA
278 * vol (r) current volume
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
285 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
290 int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
296 if ((oflag & O_CREAT) )
297 attr_flag |= XATTR_CREATE;
299 else if ((oflag & O_TRUNC) )
300 attr_flag |= XATTR_REPLACE;
302 if ((oflag & O_NOFOLLOW) ) {
303 ret = sys_lsetxattr(uname, attruname, ibuf, attrsize,attr_flag);
306 ret = sys_setxattr(uname, attruname, ibuf, attrsize, attr_flag);
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",
317 LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): EA already exists",
321 LOG(log_error, logtype_afpd, "sys_set_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
330 * Function: sys_remove_ea
332 * Purpose: remove a native EA
336 * vol (r) current volume
338 * attruname (r) EA name
339 * oflag (r) link and create flag
341 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
345 * Removes EA attruname from file uname.
347 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
351 if ((oflag & O_NOFOLLOW) ) {
352 ret = sys_lremovexattr(uname, attruname);
355 ret = sys_removexattr(uname, attruname);
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);
365 LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
366 return AFPERR_ACCESS;
368 LOG(log_error, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
379 * @note Supports *at semantics, therfor switches back and forth between sfd and cwd
381 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
386 char *names = NULL, *end_names, *name, *value = NULL;
387 unsigned int setxattr_ENOTSUP = 0;
390 if ((cwd = open(".", O_RDONLY)) == -1) {
391 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s",
399 if (fchdir(sfd) == -1) {
400 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
407 size = sys_listxattr(src, NULL, 0);
409 if (errno != ENOSYS && errno != ENOTSUP) {
414 names = malloc(size+1);
419 size = sys_listxattr(src, names, size);
425 end_names = names + size;
429 if (fchdir(cwd) == -1) {
430 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
437 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
440 /* check if this attribute shall be preserved */
445 if (fchdir(sfd) == -1) {
446 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
453 size = sys_getxattr (src, name, NULL, 0);
458 value = realloc(old_value = value, size);
459 if (size != 0 && value == NULL) {
463 size = sys_getxattr(src, name, value, size);
470 if (fchdir(cwd) == -1) {
471 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
478 if (sys_setxattr(dst, name, value, size, 0) != 0) {
479 if (errno == ENOTSUP)
482 if (errno == ENOSYS) {
484 /* no hope of getting any further */
492 if (setxattr_ENOTSUP) {
510 LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
511 return AFPERR_ACCESS;
513 LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));