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>
41 * Store Extended Attributes inside .AppleDouble folders as follows:
43 * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
45 * - create header with with the format struct adouble_ea_ondisk, the file is written to
46 * ".AppleDouble/fileWithEAs::EA"
47 * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2"
51 * Build mode for EA header from file mode
53 static inline mode_t ea_header_mode(mode_t mode)
55 /* Same as ad_hf_mode(mode) */
56 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
57 /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/
58 mode |= S_IRUSR | S_IWUSR;
63 * Build mode for EA file from file mode
65 static inline mode_t ea_mode(mode_t mode)
67 /* Same as ad_hf_mode(mode) */
68 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
73 Taken form afpd/desktop.c
75 static char *mtoupath(const struct vol *vol, const char *mpath)
77 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
82 uint16_t flags = CONV_ESCAPEHEX;
87 if ( *mpath == '\0' ) {
97 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
100 m, inplen, u, outlen, &flags)) ) {
109 * Function: unpack_header
111 * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
115 * ea (rw) handle to struct ea
117 * Returns: 0 on success, -1 on error
121 * Verifies magic and version.
123 static int unpack_header(struct ea * restrict ea)
126 unsigned int count = 0;
131 /* Check magic and version */
133 memcpy(&uint32, buf, sizeof(uint32_t));
134 if (uint32 != htonl(EA_MAGIC)) {
135 LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", uint32);
140 memcpy(&uint16, buf, sizeof(uint16_t));
141 if (uint16 != htons(EA_VERSION)) {
142 LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", uint16);
149 memcpy(&uint16, buf, sizeof(uint16_t));
150 ea->ea_count = ntohs(uint16);
151 LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
154 if (ea->ea_count == 0)
157 /* Allocate storage for the ea_entries array */
158 ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
159 if ( ! ea->ea_entries) {
160 LOG(log_error, logtype_afpd, "unpack_header: OOM");
165 buf = ea->ea_data + EA_HEADER_SIZE;
166 while (count < ea->ea_count) {
167 memcpy(&uint32, buf, 4); /* EA size */
169 (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
170 (*(ea->ea_entries))[count].ea_name = strdup(buf);
171 if (! (*(ea->ea_entries))[count].ea_name) {
172 LOG(log_error, logtype_afpd, "unpack_header: OOM");
176 (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
177 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
179 LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
180 (*(ea->ea_entries))[count].ea_name,
181 (*(ea->ea_entries))[count].ea_size,
182 (*(ea->ea_entries))[count].ea_namelen);
192 * Function: pack_header
194 * Purpose: pack everything from struct ea into buffer at ea->ea_data
198 * ea (rw) handle to struct ea
200 * Returns: 0 on success, -1 on error
204 * adjust ea->ea_count in case an ea entry deletetion is detected
206 static int pack_header(struct ea * restrict ea)
208 unsigned int count = 0, eacount = 0;
211 size_t bufsize = EA_HEADER_SIZE;
213 char *buf = ea->ea_data + EA_HEADER_SIZE;
215 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
216 ea->filename, ea->ea_count, ea->ea_size);
218 if (ea->ea_count == 0)
219 /* nothing to do, magic, version and count are still valid in buffer */
222 while(count < ea->ea_count) { /* the names */
223 /* Check if its a deleted entry */
224 if ( ! ((*ea->ea_entries)[count].ea_name)) {
229 bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
234 bufsize += (eacount * 4); /* header + ea_size for each EA */
235 if (bufsize > ea->ea_size) {
236 /* we must realloc */
237 if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
238 LOG(log_error, logtype_afpd, "pack_header: OOM");
243 ea->ea_size = bufsize;
246 uint16 = htons(eacount);
247 memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
250 buf = ea->ea_data + EA_HEADER_SIZE;
251 while (count < ea->ea_count) {
252 /* Check if its a deleted entry */
253 if ( ! ((*ea->ea_entries)[count].ea_name)) {
259 uint32 = htonl((*(ea->ea_entries))[count].ea_size);
260 memcpy(buf, &uint32, 4);
263 /* Second: EA name as C-string */
264 strcpy(buf, (*(ea->ea_entries))[count].ea_name);
265 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
267 LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
268 (*(ea->ea_entries))[count].ea_name,
269 (*(ea->ea_entries))[count].ea_size,
270 (*(ea->ea_entries))[count].ea_namelen);
275 ea->ea_count = eacount;
277 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
278 ea->filename, ea->ea_count, ea->ea_size);
284 * Function: ea_addentry
286 * Purpose: add one EA into ea->ea_entries[]
290 * ea (rw) pointer to struct ea
291 * attruname (r) name of EA
292 * attrsize (r) size of ea
293 * bitmap (r) bitmap from FP func
295 * Returns: new number of EA entries, -1 on error
299 * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
300 * Otherwise realloc and put entry at the end. Increments ea->ea_count.
302 static int ea_addentry(struct ea * restrict ea,
303 const char * restrict attruname,
308 unsigned int count = 0;
311 /* First check if an EA of the requested name already exist */
312 if (ea->ea_count > 0) {
313 while (count < ea->ea_count) {
314 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
316 LOG(log_debug, logtype_afpd, "ea_addentry('%s', bitmap:0x%x): exists", attruname, bitmap);
317 if (bitmap & kXAttrCreate)
318 /* its like O_CREAT|O_EXCL -> fail */
320 (*(ea->ea_entries))[count].ea_size = attrsize;
327 if ((bitmap & kXAttrReplace) && ! ea_existed)
328 /* replace was requested, but EA didn't exist */
331 if (ea->ea_count == 0) {
332 ea->ea_entries = malloc(sizeof(struct ea_entry));
333 if ( ! ea->ea_entries) {
334 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
337 } else if (! ea_existed) {
338 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
340 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
343 ea->ea_entries = tmprealloc;
346 /* We've grown the array, now store the entry */
347 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
348 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
349 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
350 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
353 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
359 if (ea->ea_count == 0 && ea->ea_entries) {
360 /* We just allocated storage but had an error somewhere -> free storage*/
361 free(ea->ea_entries);
362 ea->ea_entries = NULL;
369 * Function: create_ea_header
371 * Purpose: create EA header file, only called from ea_open
375 * uname (r) filename for which we have to create a header
376 * ea (rw) ea handle with already allocated storage pointed to
379 * Returns: fd of open header file on success, -1 on error, errno semantics:
380 * EEXIST: open with O_CREAT | O_EXCL failed
384 * Creates EA header file and initialize ea->ea_data buffer.
385 * Possibe race condition with other afpd processes:
386 * we were called because header file didn't exist in eg. ea_open. We then
387 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
388 * What do we do then? Someone else is in the process of creating the header too, but
389 * it might not have finished it. That means we cant just open, read and use it!
390 * We therefor currently just break with an error.
391 * On return the header file is still r/w locked.
393 static int create_ea_header(const char * restrict uname,
394 struct ea * restrict ea)
396 int fd = -1, err = 0;
401 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
402 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
407 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
408 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
415 uint32 = htonl(EA_MAGIC);
416 memcpy(ptr, &uint32, sizeof(uint32_t));
419 uint16 = htons(EA_VERSION);
420 memcpy(ptr, &uint16, sizeof(uint16_t));
421 ptr += EA_VERSION_LEN;
423 memset(ptr, 0, 2); /* count */
425 ea->ea_size = EA_HEADER_SIZE;
426 ea->ea_inited = EA_INITED;
439 * Purpose: write an EA to disk
443 * ea (r) struct ea handle
444 * attruname (r) EA name
445 * ibuf (r) buffer with EA content
446 * attrsize (r) size of EA
448 * Returns: 0 on success, -1 on error
452 * Creates/overwrites EA file.
455 static int write_ea(const struct ea * restrict ea,
456 const char * restrict attruname,
457 const char * restrict ibuf,
460 int fd = -1, ret = AFP_OK;
464 if ((eaname = ea_path(ea, attruname, 1)) == NULL) {
465 LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
469 LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname);
471 /* Check if it exists, remove if yes*/
472 if ((stat(eaname, &st)) == 0) {
473 if ((unlink(eaname)) != 0) {
475 return AFPERR_ACCESS;
481 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
482 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
487 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
488 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
493 if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) {
494 LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
501 close(fd); /* and unlock */
506 * Function: ea_delentry
508 * Purpose: delete one EA from ea->ea_entries[]
512 * ea (rw) pointer to struct ea
513 * attruname (r) EA name
515 * Returns: new number of EA entries, -1 on error
519 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
520 * Marks it as unused just by freeing name and setting it to NULL.
521 * ea_close and pack_buffer must honor this.
523 static int ea_delentry(struct ea * restrict ea, const char * restrict attruname)
526 unsigned int count = 0;
528 if (ea->ea_count == 0) {
529 LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion",
534 while (count < ea->ea_count) {
535 /* search matching EA */
536 if ((*ea->ea_entries)[count].ea_name &&
537 strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
538 free((*ea->ea_entries)[count].ea_name);
539 (*ea->ea_entries)[count].ea_name = NULL;
541 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
542 attruname, count + 1, ea->ea_count);
553 * Function: delete_ea_file
555 * Purpose: delete EA file from disk
559 * ea (r) struct ea handle
560 * attruname (r) EA name
562 * Returns: 0 on success, -1 on error
564 static int delete_ea_file(const struct ea * restrict ea, const char *eaname)
570 if ((eafile = ea_path(ea, eaname, 1)) == NULL) {
571 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
575 /* Check if it exists, remove if yes*/
576 if ((stat(eafile, &st)) == 0) {
577 if ((unlink(eafile)) != 0) {
578 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
579 eafile, strerror(errno));
582 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
588 /*************************************************************************************
589 * ea_path, ea_open and ea_close are only global so that dbd can call them
590 *************************************************************************************/
595 * Purpose: return name of ea header filename
600 * eaname (r) name of EA or NULL
601 * macname (r) if != 0 call mtoupath on eaname
603 * Returns: pointer to name in static buffer, NULL on error
607 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
608 * Files: "file" -> "file/.AppleDouble/file::EA"
609 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
610 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
612 char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname)
615 static char pathbuf[MAXPATHLEN + 1];
617 /* get name of a adouble file from uname */
618 adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
619 /* copy it so we can work with it */
620 strlcpy(pathbuf, adname, MAXPATHLEN + 1);
622 strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
625 strlcat(pathbuf, "::", MAXPATHLEN + 1);
627 if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
629 strlcat(pathbuf, eaname, MAXPATHLEN + 1);
638 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
642 * vol (r) current volume
643 * uname (r) filename for which we have to open a header
644 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
645 * EA_RDONLY: open read only
646 * EA_RDWR: open read/write
647 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
648 * ea (w) pointer to a struct ea that we fill
650 * Returns: 0 on success
651 * -1 on misc error with errno = EFAULT
652 * -2 if no EA header exists with errno = ENOENT
656 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
657 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
658 * file is either read or write locked depending on the open flags.
659 * When you're done with struct ea you must call ea_close on it.
661 int ea_open(const struct vol * restrict vol,
662 const char * restrict uname,
664 struct ea * restrict ea)
670 /* Enforce usage rules! */
671 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
672 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
676 /* Set it all to 0 */
677 memset(ea, 0, sizeof(struct ea));
679 ea->vol = vol; /* ea_close needs it */
680 ea->ea_flags = eaflags;
681 ea->dirfd = -1; /* no *at (cf openat) semantics by default */
683 /* Dont care for errors, eg when removing the file is already gone */
684 if (!stat(uname, &st) && S_ISDIR(st.st_mode))
685 ea->ea_flags |= EA_DIR;
687 if ( ! (ea->filename = strdup(uname))) {
688 LOG(log_error, logtype_afpd, "ea_open: OOM");
692 eaname = ea_path(ea, NULL, 0);
693 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
695 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
696 if ((stat(eaname, &st)) != 0) {
697 if (errno == ENOENT) {
699 /* It doesnt exist */
701 if ( ! (eaflags & EA_CREATE)) {
702 /* creation was not requested, so return with error */
707 /* Now create a header file */
709 /* malloc buffer for minimal on disk data */
710 ea->ea_data = malloc(EA_HEADER_SIZE);
712 LOG(log_error, logtype_afpd, "ea_open: OOM");
718 ea->ea_fd = create_ea_header(eaname, ea);
719 if (ea->ea_fd == -1) {
726 } else {/* errno != ENOENT */
732 /* header file exists, so read and parse it */
734 /* malloc buffer where we read disk file into */
735 if (st.st_size < EA_HEADER_SIZE) {
736 LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname);
740 ea->ea_size = st.st_size;
741 ea->ea_data = malloc(st.st_size);
743 LOG(log_error, logtype_afpd, "ea_open: OOM");
748 /* Now lock, open and read header file from disk */
749 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
750 LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
756 if (ea->ea_flags & EA_RDONLY) {
758 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
759 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
763 } else { /* EA_RDWR */
765 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
766 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
773 if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
774 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
779 if ((unpack_header(ea)) != 0) {
780 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
788 ea->ea_inited = EA_INITED;
791 errno = EFAULT; /* force some errno distinguishable from ENOENT */
809 * Function: ea_openat
811 * Purpose: openat like wrapper for ea_open, takes a additional file descriptor
815 * vol (r) current volume
816 * sfd (r) openat like file descriptor
817 * uname (r) filename for which we have to open a header
818 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
819 * EA_RDONLY: open read only
820 * EA_RDWR: open read/write
821 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
822 * ea (w) pointer to a struct ea that we fill
824 * Returns: 0 on success
825 * -1 on misc error with errno = EFAULT
826 * -2 if no EA header exists with errno = ENOENT
830 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
831 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
832 * file is either read or write locked depending on the open flags.
833 * When you're done with struct ea you must call ea_close on it.
835 int ea_openat(const struct vol * restrict vol,
837 const char * restrict uname,
839 struct ea * restrict ea)
845 if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
851 ret = ea_open(vol, uname, eaflags, ea);
855 if (fchdir(cwdfd) != 0) {
856 LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting");
873 * Purpose: flushes and closes an ea handle
877 * ea (rw) pointer to ea handle
879 * Returns: 0 on success, -1 on error
883 * Flushes and then closes and frees all resouces held by ea handle.
884 * Pack data in ea into ea_data, then write ea_data to disk
886 int ea_close(struct ea * restrict ea)
889 unsigned int count = 0;
893 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
895 if (ea->ea_inited != EA_INITED) {
896 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
900 /* pack header and write it to disk if it was opened EA_RDWR*/
901 if (ea->ea_flags & EA_RDWR) {
902 if ((pack_header(ea)) != 0) {
903 LOG(log_error, logtype_afpd, "ea_close: pack header");
906 if (ea->ea_count == 0) {
907 /* Check if EA header exists and remove it */
908 eaname = ea_path(ea, NULL, 0);
909 if ((statat(ea->dirfd, eaname, &st)) == 0) {
910 if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
911 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
912 eaname, strerror(errno));
916 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
919 if (errno != ENOENT) {
920 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
921 eaname, strerror(errno));
925 } else { /* ea->ea_count > 0 */
926 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
927 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
932 if ((ftruncate(ea->ea_fd, 0)) == -1) {
933 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
938 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
939 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
948 while(count < ea->ea_count) {
949 if ( (*ea->ea_entries)[count].ea_name ) {
950 free((*ea->ea_entries)[count].ea_name);
951 (*ea->ea_entries)[count].ea_name = NULL;
962 if (ea->ea_entries) {
963 free(ea->ea_entries);
964 ea->ea_entries = NULL;
971 if (ea->ea_fd != -1) {
972 close(ea->ea_fd); /* also releases the fcntl lock */
981 /************************************************************************************
982 * VFS funcs called from afp_ea* funcs
983 ************************************************************************************/
986 * Function: get_easize
988 * Purpose: get size of an EA
992 * vol (r) current volume
993 * rbuf (w) DSI reply buffer
994 * rbuflen (rw) current length of data in reply buffer
996 * oflag (r) link and create flag
997 * attruname (r) name of attribute
999 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1003 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
1005 int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
1007 int ret = AFPERR_MISC;
1008 unsigned int count = 0;
1012 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
1014 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1015 if (errno != ENOENT)
1016 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
1023 while (count < ea.ea_count) {
1024 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1025 uint32 = htonl((*ea.ea_entries)[count].ea_size);
1026 memcpy(rbuf, &uint32, 4);
1030 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
1031 attruname, (*ea.ea_entries)[count].ea_size);
1037 if ((ea_close(&ea)) != 0) {
1038 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
1046 * Function: get_eacontent
1048 * Purpose: copy EA into rbuf
1052 * vol (r) current volume
1053 * rbuf (w) DSI reply buffer
1054 * rbuflen (rw) current length of data in reply buffer
1055 * uname (r) filename
1056 * oflag (r) link and create flag
1057 * attruname (r) name of attribute
1058 * maxreply (r) maximum EA size as of current specs/real-life
1060 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1064 * Copies EA into rbuf. Increments *rbuflen accordingly.
1066 int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
1068 int ret = AFPERR_MISC, fd = -1;
1069 unsigned int count = 0;
1075 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
1077 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1078 if (errno != ENOENT)
1079 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
1085 while (count < ea.ea_count) {
1086 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1087 if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) {
1092 if ((fd = open(eafile, O_RDONLY)) == -1) {
1093 LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno));
1098 /* Check how much the client wants, give him what we think is right */
1099 maxreply -= MAX_REPLY_EXTRA_BYTES;
1100 if (maxreply > MAX_EA_SIZE)
1101 maxreply = MAX_EA_SIZE;
1102 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
1103 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
1105 /* Put length of EA data in reply buffer */
1106 uint32 = htonl(toread);
1107 memcpy(rbuf, &uint32, 4);
1111 if (read(fd, rbuf, toread) != (ssize_t)toread) {
1112 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1126 if ((ea_close(&ea)) != 0) {
1127 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1136 * Function: list_eas
1138 * Purpose: copy names of EAs into attrnamebuf
1142 * vol (r) current volume
1143 * attrnamebuf (w) store names a consecutive C strings here
1144 * buflen (rw) length of names in attrnamebuf
1145 * uname (r) filename
1146 * oflag (r) link and create flag
1148 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1152 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1153 * Increments *buflen accordingly.
1155 int list_eas(VFS_FUNC_ARGS_EA_LIST)
1157 unsigned int count = 0;
1158 int attrbuflen = *buflen, ret = AFP_OK, len;
1159 char *buf = attrnamebuf;
1162 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1164 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1165 if (errno != ENOENT) {
1166 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1173 while (count < ea.ea_count) {
1174 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1175 if ( ( len = convert_string(vol->v_volcharset,
1177 (*ea.ea_entries)[count].ea_name,
1178 (*ea.ea_entries)[count].ea_namelen,
1186 /* convert_string didn't 0-terminate */
1187 attrnamebuf[attrbuflen + 255] = 0;
1189 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1190 uname, (*ea.ea_entries)[count].ea_name);
1192 attrbuflen += len + 1;
1193 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1194 /* Next EA name could overflow, so bail out with error.
1195 FIXME: evantually malloc/memcpy/realloc whatever.
1197 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1205 *buflen = attrbuflen;
1207 if ((ea_close(&ea)) != 0) {
1208 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1218 * Purpose: set a Solaris native EA
1222 * vol (r) current volume
1223 * uname (r) filename
1224 * attruname (r) EA name
1225 * ibuf (r) buffer with EA content
1226 * attrsize (r) length EA in ibuf
1227 * oflag (r) link and create flag
1229 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1233 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1234 * Increments *rbuflen accordingly.
1236 int set_ea(VFS_FUNC_ARGS_EA_SET)
1241 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1243 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1244 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1248 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1249 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1254 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1255 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1261 if ((ea_close(&ea)) != 0) {
1262 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1271 * Function: remove_ea
1273 * Purpose: remove a EA from a file
1277 * vol (r) current volume
1278 * uname (r) filename
1279 * attruname (r) EA name
1280 * oflag (r) link and create flag
1282 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1286 * Removes EA attruname from file uname.
1288 int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
1293 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1295 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1296 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1300 if ((ea_delentry(&ea, attruname)) == -1) {
1301 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1306 if ((delete_ea_file(&ea, attruname)) != 0) {
1307 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1313 if ((ea_close(&ea)) != 0) {
1314 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1322 /******************************************************************************************
1323 * EA VFS funcs that deal with file/dir cp/mv/rm
1324 ******************************************************************************************/
1326 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1328 unsigned int count = 0;
1333 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1336 if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) {
1337 if (errno == ENOENT)
1338 /* no EA files, nothing to do */
1341 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1347 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
1353 while (count < ea.ea_count) {
1354 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1358 free((*ea.ea_entries)[count].ea_name);
1359 (*ea.ea_entries)[count].ea_name = NULL;
1363 /* ea_close removes the EA header file for us because all names are NULL */
1364 if ((ea_close(&ea)) != 0) {
1365 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1369 if (dirfd != -1 && fchdir(cwd) != 0) {
1370 LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!");
1381 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1383 unsigned int count = 0;
1386 char srceapath[ MAXPATHLEN + 1];
1393 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1397 if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) {
1398 if (errno == ENOENT)
1399 /* no EA files, nothing to do */
1402 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1407 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1408 if (errno == ENOENT) {
1409 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1411 if ((ad_open(&ad, dst, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666)) != 0) {
1412 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1416 ad_close(&ad, ADFLAGS_HF);
1417 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1424 /* Loop through all EAs: */
1425 while (count < srcea.ea_count) {
1427 eaname = (*srcea.ea_entries)[count].ea_name;
1428 easize = (*srcea.ea_entries)[count].ea_size;
1430 /* Build src and dst paths for rename() */
1431 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
1435 strcpy(srceapath, eapath);
1436 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
1441 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1442 src, dst, srceapath, eapath);
1444 /* Add EA to dstea */
1445 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1446 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1447 src, dst, srceapath, eapath);
1452 /* Remove EA entry from srcea */
1453 if ((ea_delentry(&srcea, eaname)) == -1) {
1454 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1455 src, dst, srceapath, eapath);
1456 ea_delentry(&dstea, eaname);
1461 /* Now rename the EA */
1462 if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) {
1463 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1464 src, dst, srceapath, eapath);
1479 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1481 unsigned int count = 0;
1484 char srceapath[ MAXPATHLEN + 1];
1491 LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1494 if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) {
1495 if (errno == ENOENT)
1496 /* no EA files, nothing to do */
1499 LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1504 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1505 if (errno == ENOENT) {
1506 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1508 if ((ad_open(&ad, dst, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666)) != 0) {
1509 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1513 ad_close(&ad, ADFLAGS_HF);
1514 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1521 /* Loop through all EAs: */
1522 while (count < srcea.ea_count) {
1524 eaname = (*srcea.ea_entries)[count].ea_name;
1525 easize = (*srcea.ea_entries)[count].ea_size;
1527 /* Build src and dst paths for copy_file() */
1528 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
1532 strcpy(srceapath, eapath);
1533 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
1538 LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1539 src, dst, srceapath, eapath);
1541 /* Add EA to dstea */
1542 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1543 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1549 /* Now copy the EA */
1550 if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1551 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1552 src, dst, srceapath, eapath);
1566 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1569 unsigned int count = 0;
1574 LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1576 if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1577 if (errno == ENOENT)
1578 /* no EA files, nothing to do */
1581 LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1586 if ((ochown(ea_path(&ea, NULL, 0), uid, gid, vol_syml_opt(vol))) != 0) {
1590 ret = AFPERR_ACCESS;
1598 while (count < ea.ea_count) {
1599 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
1603 if ((ochown(eaname, uid, gid, vol_syml_opt(vol))) != 0) {
1607 ret = AFPERR_ACCESS;
1620 if ((ea_close(&ea)) != 0) {
1621 LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1628 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1631 unsigned int count = 0;
1636 LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1638 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1639 if (errno == ENOENT)
1640 /* no EA files, nothing to do */
1646 /* Set mode on EA header file */
1647 if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
1648 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
1652 ret = AFPERR_ACCESS;
1660 /* Set mode on EA files */
1661 while (count < ea.ea_count) {
1662 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
1666 if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
1667 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1671 ret = AFPERR_ACCESS;
1684 if ((ea_close(&ea)) != 0) {
1685 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1692 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1696 unsigned int count = 0;
1698 const char *eaname_safe = NULL;
1701 LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1702 /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1706 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1707 /* ENOENT --> no EA files, nothing to do */
1708 if (errno != ENOENT)
1714 /* Set mode on EA header */
1715 if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
1716 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
1720 ret = AFPERR_ACCESS;
1728 /* Set mode on EA files */
1729 while (count < ea.ea_count) {
1730 eaname = (*ea.ea_entries)[count].ea_name;
1732 * Be careful with EA names from the EA header!
1733 * Eg NFS users might have access to them, can inject paths using ../ or /.....
1735 * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
1737 if ((eaname_safe = strrchr(eaname, '/'))) {
1738 LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
1739 eaname = eaname_safe;
1741 if ((eaname = ea_path(&ea, eaname, 1)) == NULL) {
1745 if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
1746 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
1750 ret = AFPERR_ACCESS;
1765 if ((ea_close(&ea)) != 0) {
1766 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);