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>
39 * Store Extended Attributes inside .AppleDouble folders as follows:
41 * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
43 * - create header with with the format struct adouble_ea_ondisk, the file is written to
44 * ".AppleDouble/fileWithEAs::EA"
45 * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2"
49 * Build mode for EA header from file mode
51 static inline mode_t ea_header_mode(mode_t mode)
53 /* Same as ad_hf_mode(mode) */
54 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
55 /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/
56 mode |= S_IRUSR | S_IWUSR;
61 * Build mode for EA file from file mode
63 static inline mode_t ea_mode(mode_t mode)
65 /* Same as ad_hf_mode(mode) */
66 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
71 Taken form afpd/desktop.c
73 static char *mtoupath(const struct vol *vol, const char *mpath)
75 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
80 uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON;
85 if ( *mpath == '\0' ) {
95 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
98 m, inplen, u, outlen, &flags)) ) {
107 * Function: unpack_header
109 * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
113 * ea (rw) handle to struct ea
115 * Returns: 0 on success, -1 on error
119 * Verifies magic and version.
121 static int unpack_header(struct ea * restrict ea)
124 unsigned int count = 0;
128 /* Check magic and version */
130 if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
131 LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
136 if (*(uint16_t *)buf != htons(EA_VERSION)) {
137 LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
144 ea->ea_count = ntohs(*(uint16_t *)buf);
145 LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
148 if (ea->ea_count == 0)
151 /* Allocate storage for the ea_entries array */
152 ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
153 if ( ! ea->ea_entries) {
154 LOG(log_error, logtype_afpd, "unpack_header: OOM");
159 buf = ea->ea_data + EA_HEADER_SIZE;
160 while (count < ea->ea_count) {
161 memcpy(&uint32, buf, 4); /* EA size */
163 (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
164 (*(ea->ea_entries))[count].ea_name = strdup(buf);
165 if (! (*(ea->ea_entries))[count].ea_name) {
166 LOG(log_error, logtype_afpd, "unpack_header: OOM");
170 (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
171 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
173 LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
174 (*(ea->ea_entries))[count].ea_name,
175 (*(ea->ea_entries))[count].ea_size,
176 (*(ea->ea_entries))[count].ea_namelen);
186 * Function: pack_header
188 * Purpose: pack everything from struct ea into buffer at ea->ea_data
192 * ea (rw) handle to struct ea
194 * Returns: 0 on success, -1 on error
198 * adjust ea->ea_count in case an ea entry deletetion is detected
200 static int pack_header(struct ea * restrict ea)
202 unsigned int count = 0, eacount = 0;
205 size_t bufsize = EA_HEADER_SIZE;
207 char *buf = ea->ea_data + EA_HEADER_SIZE;
209 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
210 ea->filename, ea->ea_count, ea->ea_size);
212 if (ea->ea_count == 0)
213 /* nothing to do, magic, version and count are still valid in buffer */
216 while(count < ea->ea_count) { /* the names */
217 /* Check if its a deleted entry */
218 if ( ! ((*ea->ea_entries)[count].ea_name)) {
223 bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
228 bufsize += (eacount * 4); /* header + ea_size for each EA */
229 if (bufsize > ea->ea_size) {
230 /* we must realloc */
231 if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
232 LOG(log_error, logtype_afpd, "pack_header: OOM");
237 ea->ea_size = bufsize;
240 uint16 = htons(eacount);
241 memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
244 buf = ea->ea_data + EA_HEADER_SIZE;
245 while (count < ea->ea_count) {
246 /* Check if its a deleted entry */
247 if ( ! ((*ea->ea_entries)[count].ea_name)) {
253 uint32 = htonl((*(ea->ea_entries))[count].ea_size);
254 memcpy(buf, &uint32, 4);
257 /* Second: EA name as C-string */
258 strcpy(buf, (*(ea->ea_entries))[count].ea_name);
259 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
261 LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
262 (*(ea->ea_entries))[count].ea_name,
263 (*(ea->ea_entries))[count].ea_size,
264 (*(ea->ea_entries))[count].ea_namelen);
269 ea->ea_count = eacount;
271 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
272 ea->filename, ea->ea_count, ea->ea_size);
278 * Function: ea_addentry
280 * Purpose: add one EA into ea->ea_entries[]
284 * ea (rw) pointer to struct ea
285 * attruname (r) name of EA
286 * attrsize (r) size of ea
287 * bitmap (r) bitmap from FP func
289 * Returns: new number of EA entries, -1 on error
293 * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
294 * Otherwise realloc and put entry at the end. Increments ea->ea_count.
296 static int ea_addentry(struct ea * restrict ea,
297 const char * restrict attruname,
302 unsigned int count = 0;
305 /* First check if an EA of the requested name already exist */
306 if (ea->ea_count > 0) {
307 while (count < ea->ea_count) {
308 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
310 LOG(log_debug, logtype_afpd, "ea_addentry('%s', bitmap:0x%x): exists", attruname, bitmap);
311 if (bitmap & kXAttrCreate)
312 /* its like O_CREAT|O_EXCL -> fail */
314 (*(ea->ea_entries))[count].ea_size = attrsize;
321 if ((bitmap & kXAttrReplace) && ! ea_existed)
322 /* replace was requested, but EA didn't exist */
325 if (ea->ea_count == 0) {
326 ea->ea_entries = malloc(sizeof(struct ea_entry));
327 if ( ! ea->ea_entries) {
328 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
331 } else if (! ea_existed) {
332 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
334 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
337 ea->ea_entries = tmprealloc;
340 /* We've grown the array, now store the entry */
341 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
342 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
343 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
344 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
347 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
353 if (ea->ea_count == 0 && ea->ea_entries) {
354 /* We just allocated storage but had an error somewhere -> free storage*/
355 free(ea->ea_entries);
356 ea->ea_entries = NULL;
363 * Function: create_ea_header
365 * Purpose: create EA header file, only called from ea_open
369 * uname (r) filename for which we have to create a header
370 * ea (rw) ea handle with already allocated storage pointed to
373 * Returns: fd of open header file on success, -1 on error, errno semantics:
374 * EEXIST: open with O_CREAT | O_EXCL failed
378 * Creates EA header file and initialize ea->ea_data buffer.
379 * Possibe race condition with other afpd processes:
380 * we were called because header file didn't exist in eg. ea_open. We then
381 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
382 * What do we do then? Someone else is in the process of creating the header too, but
383 * it might not have finished it. That means we cant just open, read and use it!
384 * We therefor currently just break with an error.
385 * On return the header file is still r/w locked.
387 static int create_ea_header(const char * restrict uname,
388 struct ea * restrict ea)
390 int fd = -1, err = 0;
393 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
394 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
399 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
400 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
407 *(uint32_t *)ptr = htonl(EA_MAGIC);
409 *(uint16_t *)ptr = htons(EA_VERSION);
410 ptr += EA_VERSION_LEN;
411 *(uint16_t *)ptr = 0; /* count */
413 ea->ea_size = EA_HEADER_SIZE;
414 ea->ea_inited = EA_INITED;
427 * Purpose: write an EA to disk
431 * ea (r) struct ea handle
432 * attruname (r) EA name
433 * ibuf (r) buffer with EA content
434 * attrsize (r) size of EA
436 * Returns: 0 on success, -1 on error
440 * Creates/overwrites EA file.
443 static int write_ea(const struct ea * restrict ea,
444 const char * restrict attruname,
445 const char * restrict ibuf,
448 int fd = -1, ret = AFP_OK;
452 if ((eaname = ea_path(ea, attruname, 1)) == NULL) {
453 LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
457 LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname);
459 /* Check if it exists, remove if yes*/
460 if ((stat(eaname, &st)) == 0) {
461 if ((unlink(eaname)) != 0) {
463 return AFPERR_ACCESS;
469 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
470 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
475 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
476 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
481 if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) {
482 LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
489 close(fd); /* and unlock */
494 * Function: ea_delentry
496 * Purpose: delete one EA from ea->ea_entries[]
500 * ea (rw) pointer to struct ea
501 * attruname (r) EA name
503 * Returns: new number of EA entries, -1 on error
507 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
508 * Marks it as unused just by freeing name and setting it to NULL.
509 * ea_close and pack_buffer must honor this.
511 static int ea_delentry(struct ea * restrict ea, const char * restrict attruname)
514 unsigned int count = 0;
516 if (ea->ea_count == 0) {
517 LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion",
522 while (count < ea->ea_count) {
523 /* search matching EA */
524 if ((*ea->ea_entries)[count].ea_name &&
525 strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
526 free((*ea->ea_entries)[count].ea_name);
527 (*ea->ea_entries)[count].ea_name = NULL;
529 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
530 attruname, count + 1, ea->ea_count);
541 * Function: delete_ea_file
543 * Purpose: delete EA file from disk
547 * ea (r) struct ea handle
548 * attruname (r) EA name
550 * Returns: 0 on success, -1 on error
552 static int delete_ea_file(const struct ea * restrict ea, const char *eaname)
558 if ((eafile = ea_path(ea, eaname, 1)) == NULL) {
559 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
563 /* Check if it exists, remove if yes*/
564 if ((stat(eafile, &st)) == 0) {
565 if ((unlink(eafile)) != 0) {
566 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
567 eafile, strerror(errno));
570 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
576 /*************************************************************************************
577 * ea_path, ea_open and ea_close are only global so that dbd can call them
578 *************************************************************************************/
583 * Purpose: return name of ea header filename
588 * eaname (r) name of EA or NULL
589 * macname (r) if != 0 call mtoupath on eaname
591 * Returns: pointer to name in static buffer, NULL on error
595 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
596 * Files: "file" -> "file/.AppleDouble/file::EA"
597 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
598 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
600 char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname)
603 static char pathbuf[MAXPATHLEN + 1];
605 /* get name of a adouble file from uname */
606 adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
607 /* copy it so we can work with it */
608 strlcpy(pathbuf, adname, MAXPATHLEN + 1);
610 strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
613 strlcat(pathbuf, "::", MAXPATHLEN + 1);
615 if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
617 strlcat(pathbuf, eaname, MAXPATHLEN + 1);
626 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
630 * vol (r) current volume
631 * uname (r) filename for which we have to open a header
632 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
633 * EA_RDONLY: open read only
634 * EA_RDWR: open read/write
635 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
636 * ea (w) pointer to a struct ea that we fill
638 * Returns: 0 on success
639 * -1 on misc error with errno = EFAULT
640 * -2 if no EA header exists with errno = ENOENT
644 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
645 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
646 * file is either read or write locked depending on the open flags.
647 * When you're done with struct ea you must call ea_close on it.
649 int ea_open(const struct vol * restrict vol,
650 const char * restrict uname,
652 struct ea * restrict ea)
658 /* Enforce usage rules! */
659 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
660 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
664 /* Set it all to 0 */
665 memset(ea, 0, sizeof(struct ea));
667 ea->vol = vol; /* ea_close needs it */
668 ea->ea_flags = eaflags;
669 ea->dirfd = -1; /* no *at (cf openat) semantics by default */
671 /* Dont care for errors, eg when removing the file is already gone */
672 if (!stat(uname, &st) && S_ISDIR(st.st_mode))
673 ea->ea_flags |= EA_DIR;
675 if ( ! (ea->filename = strdup(uname))) {
676 LOG(log_error, logtype_afpd, "ea_open: OOM");
680 eaname = ea_path(ea, NULL, 0);
681 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
683 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
684 if ((stat(eaname, &st)) != 0) {
685 if (errno == ENOENT) {
687 /* It doesnt exist */
689 if ( ! (eaflags & EA_CREATE)) {
690 /* creation was not requested, so return with error */
695 /* Now create a header file */
697 /* malloc buffer for minimal on disk data */
698 ea->ea_data = malloc(EA_HEADER_SIZE);
700 LOG(log_error, logtype_afpd, "ea_open: OOM");
706 ea->ea_fd = create_ea_header(eaname, ea);
707 if (ea->ea_fd == -1) {
714 } else {/* errno != ENOENT */
720 /* header file exists, so read and parse it */
722 /* malloc buffer where we read disk file into */
723 if (st.st_size < EA_HEADER_SIZE) {
724 LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname);
728 ea->ea_size = st.st_size;
729 ea->ea_data = malloc(st.st_size);
731 LOG(log_error, logtype_afpd, "ea_open: OOM");
736 /* Now lock, open and read header file from disk */
737 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
738 LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
744 if (ea->ea_flags & EA_RDONLY) {
746 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
747 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
751 } else { /* EA_RDWR */
753 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
754 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
761 if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
762 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
767 if ((unpack_header(ea)) != 0) {
768 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
776 ea->ea_inited = EA_INITED;
779 errno = EFAULT; /* force some errno distinguishable from ENOENT */
797 * Function: ea_openat
799 * Purpose: openat like wrapper for ea_open, takes a additional file descriptor
803 * vol (r) current volume
804 * sfd (r) openat like file descriptor
805 * uname (r) filename for which we have to open a header
806 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
807 * EA_RDONLY: open read only
808 * EA_RDWR: open read/write
809 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
810 * ea (w) pointer to a struct ea that we fill
812 * Returns: 0 on success
813 * -1 on misc error with errno = EFAULT
814 * -2 if no EA header exists with errno = ENOENT
818 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
819 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
820 * file is either read or write locked depending on the open flags.
821 * When you're done with struct ea you must call ea_close on it.
823 int ea_openat(const struct vol * restrict vol,
825 const char * restrict uname,
827 struct ea * restrict ea)
833 if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
839 ret = ea_open(vol, uname, eaflags, ea);
843 if (fchdir(cwdfd) != 0) {
844 LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting");
861 * Purpose: flushes and closes an ea handle
865 * ea (rw) pointer to ea handle
867 * Returns: 0 on success, -1 on error
871 * Flushes and then closes and frees all resouces held by ea handle.
872 * Pack data in ea into ea_data, then write ea_data to disk
874 int ea_close(struct ea * restrict ea)
877 unsigned int count = 0;
881 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
883 if (ea->ea_inited != EA_INITED) {
884 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
888 /* pack header and write it to disk if it was opened EA_RDWR*/
889 if (ea->ea_flags & EA_RDWR) {
890 if ((pack_header(ea)) != 0) {
891 LOG(log_error, logtype_afpd, "ea_close: pack header");
894 if (ea->ea_count == 0) {
895 /* Check if EA header exists and remove it */
896 eaname = ea_path(ea, NULL, 0);
897 if ((lstatat(ea->dirfd, eaname, &st)) == 0) {
898 if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
899 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
900 eaname, strerror(errno));
904 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
907 if (errno != ENOENT) {
908 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
909 eaname, strerror(errno));
913 } else { /* ea->ea_count > 0 */
914 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
915 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
920 if ((ftruncate(ea->ea_fd, 0)) == -1) {
921 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
926 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
927 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
936 while(count < ea->ea_count) {
937 if ( (*ea->ea_entries)[count].ea_name ) {
938 free((*ea->ea_entries)[count].ea_name);
939 (*ea->ea_entries)[count].ea_name = NULL;
950 if (ea->ea_entries) {
951 free(ea->ea_entries);
952 ea->ea_entries = NULL;
959 if (ea->ea_fd != -1) {
960 close(ea->ea_fd); /* also releases the fcntl lock */
969 /************************************************************************************
970 * VFS funcs called from afp_ea* funcs
971 ************************************************************************************/
974 * Function: get_easize
976 * Purpose: get size of an EA
980 * vol (r) current volume
981 * rbuf (w) DSI reply buffer
982 * rbuflen (rw) current length of data in reply buffer
984 * oflag (r) link and create flag
985 * attruname (r) name of attribute
987 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
991 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
993 int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
995 int ret = AFPERR_MISC;
996 unsigned int count = 0;
1000 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
1002 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1003 if (errno != ENOENT)
1004 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
1011 while (count < ea.ea_count) {
1012 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1013 uint32 = htonl((*ea.ea_entries)[count].ea_size);
1014 memcpy(rbuf, &uint32, 4);
1018 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
1019 attruname, (*ea.ea_entries)[count].ea_size);
1025 if ((ea_close(&ea)) != 0) {
1026 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
1034 * Function: get_eacontent
1036 * Purpose: copy EA into rbuf
1040 * vol (r) current volume
1041 * rbuf (w) DSI reply buffer
1042 * rbuflen (rw) current length of data in reply buffer
1043 * uname (r) filename
1044 * oflag (r) link and create flag
1045 * attruname (r) name of attribute
1046 * maxreply (r) maximum EA size as of current specs/real-life
1048 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1052 * Copies EA into rbuf. Increments *rbuflen accordingly.
1054 int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
1056 int ret = AFPERR_MISC, fd = -1;
1057 unsigned int count = 0;
1063 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
1065 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1066 if (errno != ENOENT)
1067 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
1073 while (count < ea.ea_count) {
1074 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1075 if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) {
1080 if ((fd = open(eafile, O_RDONLY)) == -1) {
1081 LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno));
1086 /* Check how much the client wants, give him what we think is right */
1087 maxreply -= MAX_REPLY_EXTRA_BYTES;
1088 if (maxreply > MAX_EA_SIZE)
1089 maxreply = MAX_EA_SIZE;
1090 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
1091 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
1093 /* Put length of EA data in reply buffer */
1094 uint32 = htonl(toread);
1095 memcpy(rbuf, &uint32, 4);
1099 if (read(fd, rbuf, toread) != (ssize_t)toread) {
1100 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1114 if ((ea_close(&ea)) != 0) {
1115 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1124 * Function: list_eas
1126 * Purpose: copy names of EAs into attrnamebuf
1130 * vol (r) current volume
1131 * attrnamebuf (w) store names a consecutive C strings here
1132 * buflen (rw) length of names in attrnamebuf
1133 * uname (r) filename
1134 * oflag (r) link and create flag
1136 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1140 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1141 * Increments *buflen accordingly.
1143 int list_eas(VFS_FUNC_ARGS_EA_LIST)
1145 unsigned int count = 0;
1146 int attrbuflen = *buflen, ret = AFP_OK, len;
1147 char *buf = attrnamebuf;
1150 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1152 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1153 if (errno != ENOENT) {
1154 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1161 while (count < ea.ea_count) {
1162 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1163 if ( ( len = convert_string(vol->v_volcharset,
1165 (*ea.ea_entries)[count].ea_name,
1166 (*ea.ea_entries)[count].ea_namelen,
1174 /* convert_string didn't 0-terminate */
1175 attrnamebuf[attrbuflen + 255] = 0;
1177 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1178 uname, (*ea.ea_entries)[count].ea_name);
1180 attrbuflen += len + 1;
1181 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1182 /* Next EA name could overflow, so bail out with error.
1183 FIXME: evantually malloc/memcpy/realloc whatever.
1185 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1193 *buflen = attrbuflen;
1195 if ((ea_close(&ea)) != 0) {
1196 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1206 * Purpose: set a Solaris native EA
1210 * vol (r) current volume
1211 * uname (r) filename
1212 * attruname (r) EA name
1213 * ibuf (r) buffer with EA content
1214 * attrsize (r) length EA in ibuf
1215 * oflag (r) link and create flag
1217 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1221 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1222 * Increments *rbuflen accordingly.
1224 int set_ea(VFS_FUNC_ARGS_EA_SET)
1229 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1231 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1232 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1236 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1237 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1242 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1243 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1249 if ((ea_close(&ea)) != 0) {
1250 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1259 * Function: remove_ea
1261 * Purpose: remove a EA from a file
1265 * vol (r) current volume
1266 * uname (r) filename
1267 * attruname (r) EA name
1268 * oflag (r) link and create flag
1270 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1274 * Removes EA attruname from file uname.
1276 int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
1281 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1283 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1284 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1288 if ((ea_delentry(&ea, attruname)) == -1) {
1289 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1294 if ((delete_ea_file(&ea, attruname)) != 0) {
1295 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1301 if ((ea_close(&ea)) != 0) {
1302 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1310 /******************************************************************************************
1311 * EA VFS funcs that deal with file/dir cp/mv/rm
1312 ******************************************************************************************/
1314 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1316 unsigned int count = 0;
1321 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1324 if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) {
1325 if (errno == ENOENT)
1326 /* no EA files, nothing to do */
1329 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1335 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
1341 while (count < ea.ea_count) {
1342 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1346 free((*ea.ea_entries)[count].ea_name);
1347 (*ea.ea_entries)[count].ea_name = NULL;
1351 /* ea_close removes the EA header file for us because all names are NULL */
1352 if ((ea_close(&ea)) != 0) {
1353 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1357 if (dirfd != -1 && fchdir(cwd) != 0) {
1358 LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!");
1369 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1371 unsigned int count = 0;
1374 char srceapath[ MAXPATHLEN + 1];
1381 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1385 if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) {
1386 if (errno == ENOENT)
1387 /* no EA files, nothing to do */
1390 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1395 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1396 if (errno == ENOENT) {
1397 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1398 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1399 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1400 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1404 ad_close(&ad, ADFLAGS_HF);
1405 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1412 /* Loop through all EAs: */
1413 while (count < srcea.ea_count) {
1415 eaname = (*srcea.ea_entries)[count].ea_name;
1416 easize = (*srcea.ea_entries)[count].ea_size;
1418 /* Build src and dst paths for rename() */
1419 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
1423 strcpy(srceapath, eapath);
1424 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
1429 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1430 src, dst, srceapath, eapath);
1432 /* Add EA to dstea */
1433 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1434 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1435 src, dst, srceapath, eapath);
1440 /* Remove EA entry from srcea */
1441 if ((ea_delentry(&srcea, eaname)) == -1) {
1442 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1443 src, dst, srceapath, eapath);
1444 ea_delentry(&dstea, eaname);
1449 /* Now rename the EA */
1450 if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) {
1451 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1452 src, dst, srceapath, eapath);
1467 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1469 unsigned int count = 0;
1472 char srceapath[ MAXPATHLEN + 1];
1479 LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1482 if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) {
1483 if (errno == ENOENT)
1484 /* no EA files, nothing to do */
1487 LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1492 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1493 if (errno == ENOENT) {
1494 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1495 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1496 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1497 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1501 ad_close(&ad, ADFLAGS_HF);
1502 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1509 /* Loop through all EAs: */
1510 while (count < srcea.ea_count) {
1512 eaname = (*srcea.ea_entries)[count].ea_name;
1513 easize = (*srcea.ea_entries)[count].ea_size;
1515 /* Build src and dst paths for copy_file() */
1516 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
1520 strcpy(srceapath, eapath);
1521 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
1526 LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1527 src, dst, srceapath, eapath);
1529 /* Add EA to dstea */
1530 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1531 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1537 /* Now copy the EA */
1538 if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1539 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1540 src, dst, srceapath, eapath);
1554 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1557 unsigned int count = 0;
1562 LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1564 if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1565 if (errno == ENOENT)
1566 /* no EA files, nothing to do */
1569 LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1574 if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) {
1578 ret = AFPERR_ACCESS;
1586 while (count < ea.ea_count) {
1587 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
1591 if ((lchown(eaname, uid, gid)) != 0) {
1595 ret = AFPERR_ACCESS;
1608 if ((ea_close(&ea)) != 0) {
1609 LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1616 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1619 unsigned int count = 0;
1624 LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1626 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1627 if (errno == ENOENT)
1628 /* no EA files, nothing to do */
1634 /* Set mode on EA header file */
1635 if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1636 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
1640 ret = AFPERR_ACCESS;
1648 /* Set mode on EA files */
1649 while (count < ea.ea_count) {
1650 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
1654 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1655 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1659 ret = AFPERR_ACCESS;
1672 if ((ea_close(&ea)) != 0) {
1673 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1680 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1684 unsigned int count = 0;
1687 const char *eaname_safe = NULL;
1690 LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1691 /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1694 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
1699 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1700 /* ENOENT --> no EA files, nothing to do */
1701 if (errno != ENOENT)
1703 if (seteuid(uid) < 0) {
1704 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1710 /* Set mode on EA header */
1711 if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1712 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
1716 ret = AFPERR_ACCESS;
1724 /* Set mode on EA files */
1725 while (count < ea.ea_count) {
1726 eaname = (*ea.ea_entries)[count].ea_name;
1728 * Be careful with EA names from the EA header!
1729 * Eg NFS users might have access to them, can inject paths using ../ or /.....
1731 * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
1733 if ((eaname_safe = strrchr(eaname, '/'))) {
1734 LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
1735 eaname = eaname_safe;
1737 if ((eaname = ea_path(&ea, eaname, 1)) == NULL) {
1741 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1742 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
1746 ret = AFPERR_ACCESS;
1759 if (seteuid(uid) < 0) {
1760 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1764 if ((ea_close(&ea)) != 0) {
1765 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);