2 $Id: ea.c,v 1.8 2009-10-21 17:41:45 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.
16 /* According to man fsattr.5 we must define _ATFILE_SOURCE */
17 #ifdef HAVE_SOLARIS_EAS
18 #define _ATFILE_SOURCE
23 #endif /* HAVE_CONFIG_H */
30 #include <sys/types.h>
35 #include <atalk/adouble.h>
37 #include <atalk/afp.h>
38 #include <atalk/logger.h>
39 #include <atalk/volume.h>
40 #include <atalk/vfs.h>
41 #include <atalk/util.h>
42 #include <atalk/unix.h>
45 * Store Extended Attributes inside .AppleDouble folders as follows:
47 * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
49 * - create header with with the format struct adouble_ea_ondisk, the file is written to
50 * ".AppleDouble/fileWithEAs::EA"
51 * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2"
55 * Build mode for EA header from file mode
57 static inline mode_t ea_header_mode(mode_t mode)
59 /* Same as ad_hf_mode(mode) */
60 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
61 /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/
62 mode |= S_IRUSR | S_IWUSR;
67 * Build mode for EA file from file mode
69 static inline mode_t ea_mode(mode_t mode)
71 /* Same as ad_hf_mode(mode) */
72 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
77 Taken form afpd/desktop.c
79 static char *mtoupath(const struct vol *vol, const char *mpath)
81 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
86 uint16_t flags = CONV_ESCAPEHEX;
91 if ( *mpath == '\0' ) {
101 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
104 m, inplen, u, outlen, &flags)) ) {
113 * Function: unpack_header
115 * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
119 * ea (rw) handle to struct ea
121 * Returns: 0 on success, -1 on error
125 * Verifies magic and version.
127 static int unpack_header(struct ea * restrict ea)
129 int ret = 0, count = 0;
133 /* Check magic and version */
135 if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
136 LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
141 if (*(uint16_t *)buf != htons(EA_VERSION)) {
142 LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
149 ea->ea_count = ntohs(*(uint16_t *)buf);
150 LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
153 if (ea->ea_count == 0)
156 /* Allocate storage for the ea_entries array */
157 ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
158 if ( ! ea->ea_entries) {
159 LOG(log_error, logtype_afpd, "unpack_header: OOM");
164 buf = ea->ea_data + EA_HEADER_SIZE;
165 while (count < ea->ea_count) {
166 memcpy(&uint32, buf, 4); /* EA size */
168 (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
169 (*(ea->ea_entries))[count].ea_name = strdup(buf);
170 if (! (*(ea->ea_entries))[count].ea_name) {
171 LOG(log_error, logtype_afpd, "unpack_header: OOM");
175 (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
176 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
178 LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
179 (*(ea->ea_entries))[count].ea_name,
180 (*(ea->ea_entries))[count].ea_size,
181 (*(ea->ea_entries))[count].ea_namelen);
191 * Function: pack_header
193 * Purpose: pack everything from struct ea into buffer at ea->ea_data
197 * ea (rw) handle to struct ea
199 * Returns: 0 on success, -1 on error
203 * adjust ea->ea_count in case an ea entry deletetion is detected
205 static int pack_header(struct ea * restrict ea)
207 int count = 0, eacount = 0;
210 size_t bufsize = EA_HEADER_SIZE;
212 char *buf = ea->ea_data + EA_HEADER_SIZE;
214 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
215 ea->filename, ea->ea_count, ea->ea_size);
217 if (ea->ea_count == 0)
218 /* nothing to do, magic, version and count are still valid in buffer */
221 while(count < ea->ea_count) { /* the names */
222 /* Check if its a deleted entry */
223 if ( ! ((*ea->ea_entries)[count].ea_name)) {
228 bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
233 bufsize += (eacount * 4); /* header + ea_size for each EA */
234 if (bufsize > ea->ea_size) {
235 /* we must realloc */
236 if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
237 LOG(log_error, logtype_afpd, "pack_header: OOM");
242 ea->ea_size = bufsize;
245 uint16 = htons(eacount);
246 memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
249 buf = ea->ea_data + EA_HEADER_SIZE;
250 while (count < ea->ea_count) {
251 /* Check if its a deleted entry */
252 if ( ! ((*ea->ea_entries)[count].ea_name)) {
258 uint32 = htonl((*(ea->ea_entries))[count].ea_size);
259 memcpy(buf, &uint32, 4);
262 /* Second: EA name as C-string */
263 strcpy(buf, (*(ea->ea_entries))[count].ea_name);
264 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
266 LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
267 (*(ea->ea_entries))[count].ea_name,
268 (*(ea->ea_entries))[count].ea_size,
269 (*(ea->ea_entries))[count].ea_namelen);
274 ea->ea_count = eacount;
276 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
277 ea->filename, ea->ea_count, ea->ea_size);
285 * Purpose: return name of ea header filename
290 * eaname (r) name of EA or NULL
292 * Returns: pointer to name in static buffer, NULL on error
296 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
297 * Files: "file" -> "file/.AppleDouble/file::EA"
298 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
299 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
301 static char * ea_path(const struct ea * restrict ea,
302 const char * restrict eaname)
305 static char pathbuf[MAXPATHLEN + 1];
307 /* get name of a adouble file from uname */
308 adname = ea->vol->vfs->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
309 /* copy it so we can work with it */
310 strlcpy(pathbuf, adname, MAXPATHLEN + 1);
312 strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
315 strlcat(pathbuf, "::", MAXPATHLEN + 1);
316 if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
318 strlcat(pathbuf, eaname, MAXPATHLEN + 1);
325 * Function: ea_addentry
327 * Purpose: add one EA into ea->ea_entries[]
331 * ea (rw) pointer to struct ea
332 * attruname (r) name of EA
333 * attrsize (r) size of ea
334 * bitmap (r) bitmap from FP func
336 * Returns: new number of EA entries, -1 on error
340 * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
341 * Otherwise realloc and put entry at the end. Increments ea->ea_count.
343 static int ea_addentry(struct ea * restrict ea,
344 const char * restrict attruname,
351 /* First check if an EA of the requested name already exist */
352 if (ea->ea_count > 0) {
353 while (count < ea->ea_count) {
354 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
355 LOG(log_debug, logtype_afpd, "ea_addentry('%s'): exists", attruname);
356 if (bitmap & kXAttrCreate)
357 /* its like O_CREAT|O_EXCL -> fail */
359 if ( ! (bitmap & kXAttrReplace))
360 /* replace was not requested, then its an error */
368 if (ea->ea_count == 0) {
369 ea->ea_entries = malloc(sizeof(struct ea_entry));
370 if ( ! ea->ea_entries) {
371 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
375 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
377 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
380 ea->ea_entries = tmprealloc;
383 /* We've grown the array, now store the entry */
384 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
385 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
386 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
387 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
390 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
396 if (ea->ea_count == 0 && ea->ea_entries) {
397 /* We just allocated storage but had an error somewhere -> free storage*/
398 free(ea->ea_entries);
399 ea->ea_entries = NULL;
406 * Function: ea_delentry
408 * Purpose: delete one EA from ea->ea_entries[]
412 * ea (rw) pointer to struct ea
413 * attruname (r) EA name
415 * Returns: new number of EA entries, -1 on error
419 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
420 * Marks it as unused just by freeing name and setting it to NULL.
421 * ea_close and pack_buffer must honor this.
423 static int ea_delentry(struct ea * restrict ea,
424 const char * restrict attruname)
426 int ret = 0, count = 0;
428 if (ea->ea_count == 0) {
429 LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
433 while (count < ea->ea_count) {
434 /* search matching EA */
435 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
436 free((*ea->ea_entries)[count].ea_name);
437 (*ea->ea_entries)[count].ea_name = NULL;
439 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
440 attruname, count + 1, ea->ea_count);
451 * Function: create_ea_header
453 * Purpose: create EA header file, only called from ea_open
457 * uname (r) filename for which we have to create a header
458 * ea (rw) ea handle with already allocated storage pointed to
461 * Returns: fd of open header file on success, -1 on error, errno semantics:
462 * EEXIST: open with O_CREAT | O_EXCL failed
466 * Creates EA header file and initialize ea->ea_data buffer.
467 * Possibe race condition with other afpd processes:
468 * we were called because header file didn't exist in eg. ea_open. We then
469 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
470 * What do we do then? Someone else is in the process of creating the header too, but
471 * it might not have finished it. That means we cant just open, read and use it!
472 * We therefor currently just break with an error.
473 * On return the header file is still r/w locked.
475 static int create_ea_header(const char * restrict uname,
476 struct ea * restrict ea)
478 int fd = -1, err = 0;
481 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
482 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
487 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
488 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
495 *(uint32_t *)ptr = htonl(EA_MAGIC);
497 *(uint16_t *)ptr = htons(EA_VERSION);
498 ptr += EA_VERSION_LEN;
499 *(uint16_t *)ptr = 0; /* count */
501 ea->ea_size = EA_HEADER_SIZE;
502 ea->ea_inited = EA_INITED;
515 * Purpose: write an EA to disk
519 * ea (r) struct ea handle
520 * attruname (r) EA name
521 * ibuf (r) buffer with EA content
522 * attrsize (r) size of EA
524 * Returns: 0 on success, -1 on error
528 * Creates/overwrites EA file.
531 static int write_ea(const struct ea * restrict ea,
532 const char * restrict attruname,
533 const char * restrict ibuf,
536 int fd = -1, ret = AFP_OK;
540 if ((eaname = ea_path(ea, attruname)) == NULL) {
541 LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
545 LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname);
547 /* Check if it exists, remove if yes*/
548 if ((stat(eaname, &st)) == 0) {
549 if ((unlink(eaname)) != 0) {
551 return AFPERR_ACCESS;
557 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
558 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
563 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
564 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
569 if ((write(fd, ibuf, attrsize)) != attrsize) {
570 LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
577 close(fd); /* and unlock */
582 * Function: delete_ea_file
584 * Purpose: delete EA file from disk
588 * ea (r) struct ea handle
589 * attruname (r) EA name
591 * Returns: 0 on success, -1 on error
593 static int delete_ea_file(const struct ea * restrict ea,
600 if ((eafile = ea_path(ea, eaname)) == NULL) {
601 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
605 /* Check if it exists, remove if yes*/
606 if ((stat(eafile, &st)) == 0) {
607 if ((unlink(eafile)) != 0) {
608 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
609 eafile, strerror(errno));
612 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
621 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
625 * vol (r) current volume
626 * uname (r) filename for which we have to open a header
627 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
628 * EA_RDONLY: open read only
629 * EA_RDWR: open read/write
630 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
631 * ea (w) pointer to a struct ea that we fill
633 * Returns: 0 on success, -1 on error
637 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
638 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
639 * file is either read or write locked depending on the open flags.
640 * When you're done with struct ea you must call ea_close on it.
642 static int ea_open(const struct vol * restrict vol,
643 const char * restrict uname,
645 struct ea * restrict ea)
651 /* Enforce usage rules! */
652 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
653 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
657 /* Set it all to 0 */
658 memset(ea, 0, sizeof(struct ea));
660 ea->vol = vol; /* ea_close needs it */
661 ea->ea_flags = eaflags;
662 /* Dont check for errors, eg when removing the file is already gone */
664 if (S_ISDIR(st.st_mode))
665 ea->ea_flags |= EA_DIR;
667 if ( ! (ea->filename = strdup(uname))) {
668 LOG(log_error, logtype_afpd, "ea_open: OOM");
672 eaname = ea_path(ea, NULL);
673 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
675 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
676 if ((stat(eaname, &st)) != 0) {
677 if (errno == ENOENT) {
679 /* It doesnt exist */
681 if ( ! (eaflags & EA_CREATE)) {
682 /* creation was not requested, so return with error */
687 /* Now create a header file */
689 /* malloc buffer for minimal on disk data */
690 ea->ea_data = malloc(EA_HEADER_SIZE);
692 LOG(log_error, logtype_afpd, "ea_open: OOM");
698 ea->ea_fd = create_ea_header(eaname, ea);
699 if (ea->ea_fd == -1) {
706 } else {/* errno != ENOENT */
712 /* header file exists, so read and parse it */
714 /* malloc buffer where we read disk file into */
715 ea->ea_size = st.st_size;
716 ea->ea_data = malloc(st.st_size);
718 LOG(log_error, logtype_afpd, "ea_open: OOM");
723 /* Now lock, open and read header file from disk */
724 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
725 LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
731 if (ea->ea_flags & EA_RDONLY) {
733 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
734 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
738 } else { /* EA_RDWR */
740 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
741 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
748 if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
749 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
754 if ((unpack_header(ea)) != 0) {
755 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
762 ea->ea_inited = EA_INITED;
780 * Purpose: flushes and closes an ea handle
784 * ea (rw) pointer to ea handle
786 * Returns: 0 on success, -1 on error
790 * Flushes and then closes and frees all resouces held by ea handle.
791 * Pack data in ea into ea_data, then write ea_data to disk
793 static int ea_close(struct ea * restrict ea)
795 int ret = 0, count = 0;
799 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
801 if (ea->ea_inited != EA_INITED) {
802 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
806 /* pack header and write it to disk if it was opened EA_RDWR*/
807 if (ea->ea_flags & EA_RDWR) {
808 if ((pack_header(ea)) != 0) {
809 LOG(log_error, logtype_afpd, "ea_close: pack header");
812 if (ea->ea_count == 0) {
813 /* Check if EA header exists and remove it */
814 eaname = ea_path(ea, NULL);
815 if ((stat(eaname, &st)) == 0) {
816 if ((unlink(eaname)) != 0) {
817 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
818 eaname, strerror(errno));
822 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
825 if (errno != ENOENT) {
826 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
827 eaname, strerror(errno));
831 } else { /* ea->ea_count > 0 */
832 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
833 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
838 if ((ftruncate(ea->ea_fd, 0)) == -1) {
839 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
844 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
845 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
854 while(count < ea->ea_count) {
855 if ( (*ea->ea_entries)[count].ea_name ) {
856 free((*ea->ea_entries)[count].ea_name);
857 (*ea->ea_entries)[count].ea_name = NULL;
868 if (ea->ea_entries) {
869 free(ea->ea_entries);
870 ea->ea_entries = NULL;
877 if (ea->ea_fd != -1) {
878 close(ea->ea_fd); /* also releases the fcntl lock */
887 /************************************************************************************
888 * VFS funcs called from afp_ea* funcs
889 ************************************************************************************/
892 * Function: get_easize
894 * Purpose: get size of an EA
898 * vol (r) current volume
899 * rbuf (w) DSI reply buffer
900 * rbuflen (rw) current length of data in reply buffer
902 * oflag (r) link and create flag
903 * attruname (r) name of attribute
905 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
909 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
911 int get_easize(const struct vol * restrict vol,
912 char * restrict rbuf,
913 int * restrict rbuflen,
914 const char * restrict uname,
916 const char * restrict attruname)
918 int ret = AFPERR_MISC, count = 0;
922 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
924 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
925 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
929 while (count < ea.ea_count) {
930 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
931 uint32 = htonl((*ea.ea_entries)[count].ea_size);
932 memcpy(rbuf, &uint32, 4);
936 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
937 attruname, (*ea.ea_entries)[count].ea_size);
943 if ((ea_close(&ea)) != 0) {
944 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
952 * Function: get_eacontent
954 * Purpose: copy EA into rbuf
958 * vol (r) current volume
959 * rbuf (w) DSI reply buffer
960 * rbuflen (rw) current length of data in reply buffer
962 * oflag (r) link and create flag
963 * attruname (r) name of attribute
964 * maxreply (r) maximum EA size as of current specs/real-life
966 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
970 * Copies EA into rbuf. Increments *rbuflen accordingly.
972 int get_eacontent(const struct vol * restrict vol,
973 char * restrict rbuf,
974 int * restrict rbuflen,
975 const char * restrict uname,
977 const char * restrict attruname,
980 int ret = AFPERR_MISC, count = 0, fd = -1;
986 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
988 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
989 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
993 while (count < ea.ea_count) {
994 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
995 if ( (eafile = ea_path(&ea, attruname)) == NULL) {
1000 if ((fd = open(eafile, O_RDONLY)) == -1) {
1005 /* Check how much the client wants, give him what we think is right */
1006 maxreply -= MAX_REPLY_EXTRA_BYTES;
1007 if (maxreply > MAX_EA_SIZE)
1008 maxreply = MAX_EA_SIZE;
1009 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
1010 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
1012 /* Put length of EA data in reply buffer */
1013 uint32 = htonl(toread);
1014 memcpy(rbuf, &uint32, 4);
1018 if ((read(fd, rbuf, toread)) != toread) {
1019 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1032 if ((ea_close(&ea)) != 0) {
1033 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1042 * Function: list_eas
1044 * Purpose: copy names of EAs into attrnamebuf
1048 * vol (r) current volume
1049 * attrnamebuf (w) store names a consecutive C strings here
1050 * buflen (rw) length of names in attrnamebuf
1051 * uname (r) filename
1052 * oflag (r) link and create flag
1054 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1058 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1059 * Increments *buflen accordingly.
1061 int list_eas(const struct vol * restrict vol,
1062 char * restrict attrnamebuf,
1063 int * restrict buflen,
1064 const char * restrict uname,
1067 int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
1068 char *buf = attrnamebuf;
1071 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1073 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1074 if (errno != ENOENT) {
1075 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1082 while (count < ea.ea_count) {
1083 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1084 if ( ( len = convert_string(vol->v_volcharset,
1086 (*ea.ea_entries)[count].ea_name,
1087 (*ea.ea_entries)[count].ea_namelen,
1095 /* convert_string didn't 0-terminate */
1096 attrnamebuf[attrbuflen + 255] = 0;
1098 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1099 uname, (*ea.ea_entries)[count].ea_name);
1101 attrbuflen += len + 1;
1102 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1103 /* Next EA name could overflow, so bail out with error.
1104 FIXME: evantually malloc/memcpy/realloc whatever.
1106 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1114 *buflen = attrbuflen;
1116 if ((ea_close(&ea)) != 0) {
1117 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1127 * Purpose: set a Solaris native EA
1131 * vol (r) current volume
1132 * uname (r) filename
1133 * attruname (r) EA name
1134 * ibuf (r) buffer with EA content
1135 * attrsize (r) length EA in ibuf
1136 * oflag (r) link and create flag
1138 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1142 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1143 * Increments *rbuflen accordingly.
1145 int set_ea(const struct vol * restrict vol,
1146 const char * restrict uname,
1147 const char * restrict attruname,
1148 const char * restrict ibuf,
1155 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1157 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1158 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1162 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1163 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1168 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1169 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1175 if ((ea_close(&ea)) != 0) {
1176 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1185 * Function: remove_ea
1187 * Purpose: remove a EA from a file
1191 * vol (r) current volume
1192 * uname (r) filename
1193 * attruname (r) EA name
1194 * oflag (r) link and create flag
1196 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1200 * Removes EA attruname from file uname.
1202 int remove_ea(const struct vol * restrict vol,
1203 const char * restrict uname,
1204 const char * restrict attruname,
1210 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1212 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1213 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1217 if ((ea_delentry(&ea, attruname)) == -1) {
1218 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1223 if ((delete_ea_file(&ea, attruname)) != 0) {
1224 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1230 if ((ea_close(&ea)) != 0) {
1231 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1239 /**********************************************************************************
1240 * Solaris EA VFS funcs
1241 **********************************************************************************/
1244 * Function: sol_get_easize
1246 * Purpose: get size of an EA on Solaris native EA
1250 * vol (r) current volume
1251 * rbuf (w) DSI reply buffer
1252 * rbuflen (rw) current length of data in reply buffer
1253 * uname (r) filename
1254 * oflag (r) link and create flag
1255 * attruname (r) name of attribute
1257 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1261 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
1263 #ifdef HAVE_SOLARIS_EAS
1264 int sol_get_easize(const struct vol * restrict vol,
1265 char * restrict rbuf,
1266 int * restrict rbuflen,
1267 const char * restrict uname,
1269 cons char * restrict attruname)
1275 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
1277 if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
1278 if (errno == ELOOP) {
1279 /* its a symlink and client requested O_NOFOLLOW */
1280 LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
1287 LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
1291 if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
1292 LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
1296 attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
1298 /* Start building reply packet */
1300 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
1302 /* length of attribute data */
1303 attrsize = htonl(attrsize);
1304 memcpy(rbuf, &attrsize, 4);
1313 #endif /* HAVE_SOLARIS_EAS */
1316 * Function: sol_get_eacontent
1318 * Purpose: copy Solaris native EA into rbuf
1322 * vol (r) current volume
1323 * rbuf (w) DSI reply buffer
1324 * rbuflen (rw) current length of data in reply buffer
1325 * uname (r) filename
1326 * oflag (r) link and create flag
1327 * attruname (r) name of attribute
1328 * maxreply (r) maximum EA size as of current specs/real-life
1330 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1334 * Copies EA into rbuf. Increments *rbuflen accordingly.
1336 #ifdef HAVE_SOLARIS_EAS
1337 int sol_get_eacontent(const struct vol * restrict vol,
1338 char * restrict rbuf,
1339 int * restrict rbuflen,
1340 const char * restrict uname,
1342 char * restrict attruname,
1346 size_t toread, okread = 0, len;
1350 if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
1351 if (errno == ELOOP) {
1352 /* its a symlink and client requested O_NOFOLLOW */
1353 LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
1360 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
1364 if ( -1 == (fstat(attrdirfd, &st))) {
1365 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
1370 /* Start building reply packet */
1372 maxreply -= MAX_REPLY_EXTRA_BYTES;
1373 if (maxreply > MAX_EA_SIZE)
1374 maxreply = MAX_EA_SIZE;
1376 /* But never send more than the client requested */
1377 toread = (maxreply < st.st_size) ? maxreply : st.st_size;
1379 LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
1381 /* remember where we must store length of attribute data in rbuf */
1387 len = read(attrdirfd, rbuf, toread);
1389 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
1396 if ((len == 0) || (okread == toread))
1400 okread = htonl((uint32_t)okread);
1401 memcpy(datalength, &okread, 4);
1409 #endif /* HAVE_SOLARIS_EAS */
1412 * Function: sol_list_eas
1414 * Purpose: copy names of Solaris native EA into attrnamebuf
1418 * vol (r) current volume
1419 * attrnamebuf (w) store names a consecutive C strings here
1420 * buflen (rw) length of names in attrnamebuf
1421 * uname (r) filename
1422 * oflag (r) link and create flag
1424 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1428 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1429 * Increments *rbuflen accordingly.
1431 #ifdef HAVE_SOLARIS_EAS
1432 int sol_list_eas(const struct vol * restrict vol,
1433 char * restrict attrnamebuf,
1434 int * restrict buflen,
1435 const char * restrict uname,
1438 int ret, attrbuflen = *buflen, len, attrdirfd = 0;
1442 /* Now list file attribute dir */
1443 if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
1444 if (errno == ELOOP) {
1445 /* its a symlink and client requested O_NOFOLLOW */
1446 ret = AFPERR_BADTYPE;
1449 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1454 if (NULL == (dirp = fdopendir(attrdirfd))) {
1455 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1460 while ((dp = readdir(dirp))) {
1461 /* check if its "." or ".." */
1462 if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
1463 (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
1466 len = strlen(dp->d_name);
1468 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1469 if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
1474 /* convert_string didn't 0-terminate */
1475 attrnamebuf[attrbuflen + 255] = 0;
1477 LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
1479 attrbuflen += len + 1;
1480 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1481 /* Next EA name could overflow, so bail out with error.
1482 FIXME: evantually malloc/memcpy/realloc whatever.
1484 LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
1499 *buflen = attrbuflen;
1502 #endif /* HAVE_SOLARIS_EAS */
1505 * Function: sol_set_ea
1507 * Purpose: set a Solaris native EA
1511 * vol (r) current volume
1512 * uname (r) filename
1513 * attruname (r) EA name
1514 * ibuf (r) buffer with EA content
1515 * attrsize (r) length EA in ibuf
1516 * oflag (r) link and create flag
1518 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1522 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1523 * Increments *rbuflen accordingly.
1525 #ifdef HAVE_SOLARIS_EAS
1526 int sol_set_ea(const struct vol * restrict vol,
1527 const char * restrict u_name,
1528 const char * restrict attruname,
1529 const char * restrict ibuf,
1535 if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
1536 if (errno == ELOOP) {
1537 /* its a symlink and client requested O_NOFOLLOW */
1538 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1541 LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1545 if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
1546 LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
1552 #endif /* HAVE_SOLARIS_EAS */
1555 * Function: sol_remove_ea
1557 * Purpose: remove a Solaris native EA
1561 * vol (r) current volume
1562 * uname (r) filename
1563 * attruname (r) EA name
1564 * oflag (r) link and create flag
1566 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1570 * Removes EA attruname from file uname.
1572 #ifdef HAVE_SOLARIS_EAS
1573 int sol_remove_ea(const struct vol * restrict vol,
1574 const char * restrict uname,
1575 const char * restrict attruname,
1580 if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
1583 /* its a symlink and client requested O_NOFOLLOW */
1584 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1587 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1588 return AFPERR_ACCESS;
1590 LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1595 if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
1596 if (errno == EACCES) {
1597 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1598 return AFPERR_ACCESS;
1600 LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1605 #endif /* HAVE_SOLARIS_EAS */
1607 /******************************************************************************************
1608 * EA VFS funcs that deal with file/dir cp/mv/rm
1609 ******************************************************************************************/
1611 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1613 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1615 int count = 0, ret = AFP_OK;
1619 if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
1620 if (errno == ENOENT)
1621 /* no EA files, nothing to do */
1624 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1629 while (count < ea.ea_count) {
1630 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1634 free((*ea.ea_entries)[count].ea_name);
1635 (*ea.ea_entries)[count].ea_name = NULL;
1639 /* ea_close removes the EA header file for us because all names are NULL */
1640 if ((ea_close(&ea)) != 0) {
1641 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1648 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1653 char srceapath[ MAXPATHLEN + 1];
1660 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1664 if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1665 if (errno == ENOENT)
1666 /* no EA files, nothing to do */
1669 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1674 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1675 if (errno == ENOENT) {
1676 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1677 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1678 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1679 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1683 ad_close(&ad, ADFLAGS_HF);
1684 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1691 /* Loop through all EAs: */
1692 while (count < srcea.ea_count) {
1694 eaname = (*srcea.ea_entries)[count].ea_name;
1695 easize = (*srcea.ea_entries)[count].ea_size;
1697 /* Build src and dst paths for rename() */
1698 if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1702 strcpy(srceapath, eapath);
1703 if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1708 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1709 src, dst, srceapath, eapath);
1711 /* Add EA to dstea */
1712 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1713 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1714 src, dst, srceapath, eapath);
1719 /* Remove EA entry from srcea */
1720 if ((ea_delentry(&srcea, eaname)) == -1) {
1721 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1722 src, dst, srceapath, eapath);
1723 ea_delentry(&dstea, eaname);
1728 /* Now rename the EA */
1729 if ((rename( srceapath, eapath)) < 0) {
1730 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1731 src, dst, srceapath, eapath);
1746 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1751 char srceapath[ MAXPATHLEN + 1];
1758 LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1761 if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1762 if (errno == ENOENT)
1763 /* no EA files, nothing to do */
1766 LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1771 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1772 if (errno == ENOENT) {
1773 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1774 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1775 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1776 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1780 ad_close(&ad, ADFLAGS_HF);
1781 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1788 /* Loop through all EAs: */
1789 while (count < srcea.ea_count) {
1791 eaname = (*srcea.ea_entries)[count].ea_name;
1792 easize = (*srcea.ea_entries)[count].ea_size;
1794 /* Build src and dst paths for copy_file() */
1795 if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1799 strcpy(srceapath, eapath);
1800 if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1805 LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1806 src, dst, srceapath, eapath);
1808 /* Add EA to dstea */
1809 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1810 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1816 /* Now copy the EA */
1817 if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1818 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1819 src, dst, srceapath, eapath);
1833 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1835 LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1837 int count = 0, ret = AFP_OK;
1842 if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1843 if (errno == ENOENT)
1844 /* no EA files, nothing to do */
1847 LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1852 if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
1856 ret = AFPERR_ACCESS;
1864 while (count < ea.ea_count) {
1865 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1869 if ((chown(eaname, uid, gid)) != 0) {
1873 ret = AFPERR_ACCESS;
1886 if ((ea_close(&ea)) != 0) {
1887 LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1894 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1896 LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1898 int count = 0, ret = AFP_OK;
1903 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1904 if (errno == ENOENT)
1905 /* no EA files, nothing to do */
1911 /* Set mode on EA header file */
1912 if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1913 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1917 ret = AFPERR_ACCESS;
1925 /* Set mode on EA files */
1926 while (count < ea.ea_count) {
1927 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1931 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1932 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1936 ret = AFPERR_ACCESS;
1949 if ((ea_close(&ea)) != 0) {
1950 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1957 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1959 LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1964 const char *eaname_safe = NULL;
1967 /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1970 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
1976 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1977 if (errno == ENOENT)
1978 /* no EA files, nothing to do */
1984 /* Set mode on EA header */
1985 if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1986 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1990 ret = AFPERR_ACCESS;
1998 /* Set mode on EA files */
2000 while (count < ea.ea_count) {
2001 eaname = (*ea.ea_entries)[count].ea_name;
2003 * Be careful with EA names from the EA header!
2004 * Eg NFS users might have access to them, can inject paths using ../ or /.....
2006 * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
2008 if ((eaname_safe = strrchr(eaname, '/'))) {
2009 LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
2010 eaname = eaname_safe;
2012 if ((eaname = ea_path(&ea, eaname)) == NULL) {
2016 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
2017 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
2021 ret = AFPERR_ACCESS;
2034 if (seteuid(uid) < 0) {
2035 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
2039 if ((ea_close(&ea)) != 0) {
2040 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);