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>
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.
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.
18 #endif /* HAVE_CONFIG_H */
25 #include <sys/types.h>
31 #include <attr/xattr.h>
32 #elif HAVE_SYS_XATTR_H
33 #include <sys/xattr.h>
40 #ifdef HAVE_SYS_EXTATTR_H
41 #include <sys/extattr.h>
44 #include <atalk/adouble.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>
55 #define ENOATTR ENODATA
59 /**********************************************************************************
60 * EA VFS funcs for storing EAs in nativa filesystem EAs
61 **********************************************************************************/
64 * Function: sys_get_easize
66 * Purpose: get size of a native EA
70 * vol (r) current volume
71 * rbuf (w) DSI reply buffer
72 * rbuflen (rw) current length of data in reply buffer
74 * oflag (r) link and create flag
75 * attruname (r) name of attribute
77 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
81 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
83 int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
88 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
90 if ((oflag & O_NOFOLLOW) ) {
91 ret = sys_lgetxattr(uname, attruname, rbuf +4, 0);
94 ret = sys_getxattr(uname, attruname, rbuf +4, 0);
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);
110 LOG(log_error, logtype_afpd, "sys_getextattr_size: error: %s", strerror(errno));
115 if (ret > MAX_EA_SIZE)
118 /* Start building reply packet */
119 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, ret);
121 /* length of attribute data */
122 attrsize = htonl((uint32_t)ret);
123 memcpy(rbuf, &attrsize, 4);
131 * Function: sys_get_eacontent
133 * Purpose: copy native EA into rbuf
137 * vol (r) current volume
138 * rbuf (w) DSI reply buffer
139 * rbuflen (rw) current length of data in reply buffer
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
145 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
149 * Copies EA into rbuf. Increments *rbuflen accordingly.
151 int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
156 /* Start building reply packet */
158 maxreply -= MAX_REPLY_EXTRA_BYTES;
160 if (maxreply > MAX_EA_SIZE)
161 maxreply = MAX_EA_SIZE;
163 LOG(log_debug7, logtype_afpd, "sys_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
165 if ((oflag & O_NOFOLLOW) ) {
166 ret = sys_lgetxattr(uname, attruname, rbuf +4, maxreply);
169 ret = sys_getxattr(uname, attruname, rbuf +4, maxreply);
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);
185 LOG(log_error, logtype_afpd, "sys_getextattr_content(%s): error: %s", attruname, strerror(errno));
190 /* remember where we must store length of attribute data in rbuf */
193 attrsize = htonl((uint32_t)ret);
194 memcpy(rbuf, &attrsize, 4);
200 * Function: sys_list_eas
202 * Purpose: copy names of native EAs into attrnamebuf
206 * vol (r) current volume
207 * attrnamebuf (w) store names a consecutive C strings here
208 * buflen (rw) length of names in attrnamebuf
210 * oflag (r) link and create flag
212 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
216 * Copies names of all EAs of uname as consecutive C strings into rbuf.
217 * Increments *rbuflen accordingly.
219 int sys_list_eas(VFS_FUNC_ARGS_EA_LIST)
221 ssize_t attrbuflen = *buflen;
226 buf = malloc(ATTRNAMEBUFSIZ);
230 if ((oflag & O_NOFOLLOW)) {
231 ret = sys_llistxattr(uname, buf, ATTRNAMEBUFSIZ);
234 ret = sys_listxattr(uname, buf, ATTRNAMEBUFSIZ);
237 if (ret == -1) switch(errno) {
238 case OPEN_NOFOLLOW_ERRNO:
239 /* its a symlink and client requested O_NOFOLLOW */
240 ret = AFPERR_BADTYPE;
242 #ifdef HAVE_ATTROPEN /* Solaris */
248 LOG(log_error, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
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)) ) {
263 LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr);
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.
270 LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname);
282 *buflen = attrbuflen;
287 * Function: sys_set_ea
289 * Purpose: set a native EA
293 * vol (r) current volume
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
300 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
305 int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
311 if ((oflag & O_CREAT) )
312 attr_flag |= XATTR_CREATE;
314 else if ((oflag & O_TRUNC) )
315 attr_flag |= XATTR_REPLACE;
317 if ((oflag & O_NOFOLLOW) ) {
318 ret = sys_lsetxattr(uname, attruname, ibuf, attrsize,attr_flag);
321 ret = sys_setxattr(uname, attruname, ibuf, attrsize, attr_flag);
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);
332 LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists",
333 getcwdpath(), uname, attruname);
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" : "-",
350 * Function: sys_remove_ea
352 * Purpose: remove a native EA
356 * vol (r) current volume
358 * attruname (r) EA name
359 * oflag (r) link and create flag
361 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
365 * Removes EA attruname from file uname.
367 int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
371 if ((oflag & O_NOFOLLOW) ) {
372 ret = sys_lremovexattr(uname, attruname);
375 ret = sys_removexattr(uname, attruname);
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);
385 LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
386 return AFPERR_ACCESS;
388 LOG(log_error, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
399 * @note Supports *at semantics, therfor switches back and forth between sfd and cwd
401 int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
406 char *names = NULL, *end_names, *name, *value = NULL;
407 unsigned int setxattr_ENOTSUP = 0;
410 if ((cwd = open(".", O_RDONLY)) == -1) {
411 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s",
419 if (fchdir(sfd) == -1) {
420 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
427 size = sys_listxattr(src, NULL, 0);
429 if (errno != ENOSYS && errno != ENOTSUP) {
434 names = malloc(size+1);
439 size = sys_listxattr(src, names, size);
445 end_names = names + size;
449 if (fchdir(cwd) == -1) {
450 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
457 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
460 /* check if this attribute shall be preserved */
465 if (fchdir(sfd) == -1) {
466 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
473 size = sys_getxattr (src, name, NULL, 0);
478 value = realloc(old_value = value, size);
479 if (size != 0 && value == NULL) {
483 size = sys_getxattr(src, name, value, size);
490 if (fchdir(cwd) == -1) {
491 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s",
498 if (sys_setxattr(dst, name, value, size, 0) != 0) {
499 if (errno == ENOTSUP)
502 if (errno == ENOSYS) {
504 /* no hope of getting any further */
512 if (setxattr_ENOTSUP) {
530 LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));
531 return AFPERR_ACCESS;
533 LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno));