2 $Id: ea.c,v 1.20 2010-03-12 15:16:49 franklahm Exp $
3 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
18 #endif /* HAVE_CONFIG_H */
25 #include <sys/types.h>
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>
40 * Store Extended Attributes inside .AppleDouble folders as follows:
42 * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
44 * - create header with with the format struct adouble_ea_ondisk, the file is written to
45 * ".AppleDouble/fileWithEAs::EA"
46 * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2"
50 * Build mode for EA header from file mode
52 static inline mode_t ea_header_mode(mode_t mode)
54 /* Same as ad_hf_mode(mode) */
55 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
56 /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/
57 mode |= S_IRUSR | S_IWUSR;
62 * Build mode for EA file from file mode
64 static inline mode_t ea_mode(mode_t mode)
66 /* Same as ad_hf_mode(mode) */
67 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
72 Taken form afpd/desktop.c
74 static char *mtoupath(const struct vol *vol, const char *mpath)
76 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
81 uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON;
86 if ( *mpath == '\0' ) {
96 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
99 m, inplen, u, outlen, &flags)) ) {
108 * Function: unpack_header
110 * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
114 * ea (rw) handle to struct ea
116 * Returns: 0 on success, -1 on error
120 * Verifies magic and version.
122 static int unpack_header(struct ea * restrict ea)
125 unsigned int count = 0;
129 /* Check magic and version */
131 if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
132 LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
137 if (*(uint16_t *)buf != htons(EA_VERSION)) {
138 LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
145 ea->ea_count = ntohs(*(uint16_t *)buf);
146 LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
149 if (ea->ea_count == 0)
152 /* Allocate storage for the ea_entries array */
153 ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
154 if ( ! ea->ea_entries) {
155 LOG(log_error, logtype_afpd, "unpack_header: OOM");
160 buf = ea->ea_data + EA_HEADER_SIZE;
161 while (count < ea->ea_count) {
162 memcpy(&uint32, buf, 4); /* EA size */
164 (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
165 (*(ea->ea_entries))[count].ea_name = strdup(buf);
166 if (! (*(ea->ea_entries))[count].ea_name) {
167 LOG(log_error, logtype_afpd, "unpack_header: OOM");
171 (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
172 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
174 LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
175 (*(ea->ea_entries))[count].ea_name,
176 (*(ea->ea_entries))[count].ea_size,
177 (*(ea->ea_entries))[count].ea_namelen);
187 * Function: pack_header
189 * Purpose: pack everything from struct ea into buffer at ea->ea_data
193 * ea (rw) handle to struct ea
195 * Returns: 0 on success, -1 on error
199 * adjust ea->ea_count in case an ea entry deletetion is detected
201 static int pack_header(struct ea * restrict ea)
203 unsigned int count = 0, eacount = 0;
206 size_t bufsize = EA_HEADER_SIZE;
208 char *buf = ea->ea_data + EA_HEADER_SIZE;
210 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
211 ea->filename, ea->ea_count, ea->ea_size);
213 if (ea->ea_count == 0)
214 /* nothing to do, magic, version and count are still valid in buffer */
217 while(count < ea->ea_count) { /* the names */
218 /* Check if its a deleted entry */
219 if ( ! ((*ea->ea_entries)[count].ea_name)) {
224 bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
229 bufsize += (eacount * 4); /* header + ea_size for each EA */
230 if (bufsize > ea->ea_size) {
231 /* we must realloc */
232 if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
233 LOG(log_error, logtype_afpd, "pack_header: OOM");
238 ea->ea_size = bufsize;
241 uint16 = htons(eacount);
242 memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
245 buf = ea->ea_data + EA_HEADER_SIZE;
246 while (count < ea->ea_count) {
247 /* Check if its a deleted entry */
248 if ( ! ((*ea->ea_entries)[count].ea_name)) {
254 uint32 = htonl((*(ea->ea_entries))[count].ea_size);
255 memcpy(buf, &uint32, 4);
258 /* Second: EA name as C-string */
259 strcpy(buf, (*(ea->ea_entries))[count].ea_name);
260 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
262 LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
263 (*(ea->ea_entries))[count].ea_name,
264 (*(ea->ea_entries))[count].ea_size,
265 (*(ea->ea_entries))[count].ea_namelen);
270 ea->ea_count = eacount;
272 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
273 ea->filename, ea->ea_count, ea->ea_size);
279 * Function: ea_addentry
281 * Purpose: add one EA into ea->ea_entries[]
285 * ea (rw) pointer to struct ea
286 * attruname (r) name of EA
287 * attrsize (r) size of ea
288 * bitmap (r) bitmap from FP func
290 * Returns: new number of EA entries, -1 on error
294 * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
295 * Otherwise realloc and put entry at the end. Increments ea->ea_count.
297 static int ea_addentry(struct ea * restrict ea,
298 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) {
309 LOG(log_debug, logtype_afpd, "ea_addentry('%s'): exists", attruname);
310 if (bitmap & kXAttrCreate)
311 /* its like O_CREAT|O_EXCL -> fail */
313 if ( ! (bitmap & kXAttrReplace))
314 /* replace was not requested, then its an error */
322 if (ea->ea_count == 0) {
323 ea->ea_entries = malloc(sizeof(struct ea_entry));
324 if ( ! ea->ea_entries) {
325 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
329 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
331 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
334 ea->ea_entries = tmprealloc;
337 /* We've grown the array, now store the entry */
338 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
339 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
340 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
341 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
344 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
350 if (ea->ea_count == 0 && ea->ea_entries) {
351 /* We just allocated storage but had an error somewhere -> free storage*/
352 free(ea->ea_entries);
353 ea->ea_entries = NULL;
360 * Function: create_ea_header
362 * Purpose: create EA header file, only called from ea_open
366 * uname (r) filename for which we have to create a header
367 * ea (rw) ea handle with already allocated storage pointed to
370 * Returns: fd of open header file on success, -1 on error, errno semantics:
371 * EEXIST: open with O_CREAT | O_EXCL failed
375 * Creates EA header file and initialize ea->ea_data buffer.
376 * Possibe race condition with other afpd processes:
377 * we were called because header file didn't exist in eg. ea_open. We then
378 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
379 * What do we do then? Someone else is in the process of creating the header too, but
380 * it might not have finished it. That means we cant just open, read and use it!
381 * We therefor currently just break with an error.
382 * On return the header file is still r/w locked.
384 static int create_ea_header(const char * restrict uname,
385 struct ea * restrict ea)
387 int fd = -1, err = 0;
390 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
391 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
396 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
397 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
404 *(uint32_t *)ptr = htonl(EA_MAGIC);
406 *(uint16_t *)ptr = htons(EA_VERSION);
407 ptr += EA_VERSION_LEN;
408 *(uint16_t *)ptr = 0; /* count */
410 ea->ea_size = EA_HEADER_SIZE;
411 ea->ea_inited = EA_INITED;
424 * Purpose: write an EA to disk
428 * ea (r) struct ea handle
429 * attruname (r) EA name
430 * ibuf (r) buffer with EA content
431 * attrsize (r) size of EA
433 * Returns: 0 on success, -1 on error
437 * Creates/overwrites EA file.
440 static int write_ea(const struct ea * restrict ea,
441 const char * restrict attruname,
442 const char * restrict ibuf,
445 int fd = -1, ret = AFP_OK;
449 if ((eaname = ea_path(ea, attruname, 1)) == NULL) {
450 LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
454 LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname);
456 /* Check if it exists, remove if yes*/
457 if ((stat(eaname, &st)) == 0) {
458 if ((unlink(eaname)) != 0) {
460 return AFPERR_ACCESS;
466 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
467 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
472 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
473 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
478 if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) {
479 LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
486 close(fd); /* and unlock */
491 * Function: ea_delentry
493 * Purpose: delete one EA from ea->ea_entries[]
497 * ea (rw) pointer to struct ea
498 * attruname (r) EA name
500 * Returns: new number of EA entries, -1 on error
504 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
505 * Marks it as unused just by freeing name and setting it to NULL.
506 * ea_close and pack_buffer must honor this.
508 static int ea_delentry(struct ea * restrict ea, const char * restrict attruname)
511 unsigned int count = 0;
513 if (ea->ea_count == 0) {
514 LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
518 while (count < ea->ea_count) {
519 /* search matching EA */
520 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
521 free((*ea->ea_entries)[count].ea_name);
522 (*ea->ea_entries)[count].ea_name = NULL;
524 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
525 attruname, count + 1, ea->ea_count);
536 * Function: delete_ea_file
538 * Purpose: delete EA file from disk
542 * ea (r) struct ea handle
543 * attruname (r) EA name
545 * Returns: 0 on success, -1 on error
547 static int delete_ea_file(const struct ea * restrict ea, const char *eaname)
553 if ((eafile = ea_path(ea, eaname, 1)) == NULL) {
554 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
558 /* Check if it exists, remove if yes*/
559 if ((stat(eafile, &st)) == 0) {
560 if ((unlink(eafile)) != 0) {
561 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
562 eafile, strerror(errno));
565 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
571 /*************************************************************************************
572 * ea_path, ea_open and ea_close are only global so that dbd can call them
573 *************************************************************************************/
578 * Purpose: return name of ea header filename
583 * eaname (r) name of EA or NULL
584 * macname (r) if != 0 call mtoupath on eaname
586 * Returns: pointer to name in static buffer, NULL on error
590 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
591 * Files: "file" -> "file/.AppleDouble/file::EA"
592 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
593 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
595 char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname)
598 static char pathbuf[MAXPATHLEN + 1];
600 /* get name of a adouble file from uname */
601 adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
602 /* copy it so we can work with it */
603 strlcpy(pathbuf, adname, MAXPATHLEN + 1);
605 strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
608 strlcat(pathbuf, "::", MAXPATHLEN + 1);
610 if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
612 strlcat(pathbuf, eaname, MAXPATHLEN + 1);
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
634 * -1 on misc error with errno = EFAULT
635 * -2 if no EA header exists with errno = ENOENT
639 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
640 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
641 * file is either read or write locked depending on the open flags.
642 * When you're done with struct ea you must call ea_close on it.
644 int ea_open(const struct vol * restrict vol,
645 const char * restrict uname,
647 struct ea * restrict ea)
653 /* Enforce usage rules! */
654 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
655 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
659 /* Set it all to 0 */
660 memset(ea, 0, sizeof(struct ea));
662 ea->vol = vol; /* ea_close needs it */
663 ea->ea_flags = eaflags;
664 ea->dirfd = -1; /* no *at (cf openat) semantics by default */
666 /* Dont care for errors, eg when removing the file is already gone */
667 if (!stat(uname, &st) && S_ISDIR(st.st_mode))
668 ea->ea_flags |= EA_DIR;
670 if ( ! (ea->filename = strdup(uname))) {
671 LOG(log_error, logtype_afpd, "ea_open: OOM");
675 eaname = ea_path(ea, NULL, 0);
676 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
678 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
679 if ((stat(eaname, &st)) != 0) {
680 if (errno == ENOENT) {
682 /* It doesnt exist */
684 if ( ! (eaflags & EA_CREATE)) {
685 /* creation was not requested, so return with error */
690 /* Now create a header file */
692 /* malloc buffer for minimal on disk data */
693 ea->ea_data = malloc(EA_HEADER_SIZE);
695 LOG(log_error, logtype_afpd, "ea_open: OOM");
701 ea->ea_fd = create_ea_header(eaname, ea);
702 if (ea->ea_fd == -1) {
709 } else {/* errno != ENOENT */
715 /* header file exists, so read and parse it */
717 /* malloc buffer where we read disk file into */
718 if (st.st_size < EA_HEADER_SIZE) {
719 LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname);
723 ea->ea_size = st.st_size;
724 ea->ea_data = malloc(st.st_size);
726 LOG(log_error, logtype_afpd, "ea_open: OOM");
731 /* Now lock, open and read header file from disk */
732 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
733 LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
739 if (ea->ea_flags & EA_RDONLY) {
741 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
742 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
746 } else { /* EA_RDWR */
748 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
749 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
756 if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
757 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
762 if ((unpack_header(ea)) != 0) {
763 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
771 ea->ea_inited = EA_INITED;
774 errno = EFAULT; /* force some errno distinguishable from ENOENT */
792 * Function: ea_openat
794 * Purpose: openat like wrapper for ea_open, takes a additional file descriptor
798 * vol (r) current volume
799 * sfd (r) openat like file descriptor
800 * uname (r) filename for which we have to open a header
801 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
802 * EA_RDONLY: open read only
803 * EA_RDWR: open read/write
804 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
805 * ea (w) pointer to a struct ea that we fill
807 * Returns: 0 on success
808 * -1 on misc error with errno = EFAULT
809 * -2 if no EA header exists with errno = ENOENT
813 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
814 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
815 * file is either read or write locked depending on the open flags.
816 * When you're done with struct ea you must call ea_close on it.
818 int ea_openat(const struct vol * restrict vol,
820 const char * restrict uname,
822 struct ea * restrict ea)
828 if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
834 ret = ea_open(vol, uname, eaflags, ea);
838 if (fchdir(cwdfd) != 0) {
839 LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting");
856 * Purpose: flushes and closes an ea handle
860 * ea (rw) pointer to ea handle
862 * Returns: 0 on success, -1 on error
866 * Flushes and then closes and frees all resouces held by ea handle.
867 * Pack data in ea into ea_data, then write ea_data to disk
869 int ea_close(struct ea * restrict ea)
872 unsigned int count = 0;
876 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
878 if (ea->ea_inited != EA_INITED) {
879 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
883 /* pack header and write it to disk if it was opened EA_RDWR*/
884 if (ea->ea_flags & EA_RDWR) {
885 if ((pack_header(ea)) != 0) {
886 LOG(log_error, logtype_afpd, "ea_close: pack header");
889 if (ea->ea_count == 0) {
890 /* Check if EA header exists and remove it */
891 eaname = ea_path(ea, NULL, 0);
892 if ((lstatat(ea->dirfd, eaname, &st)) == 0) {
893 if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
894 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
895 eaname, strerror(errno));
899 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
902 if (errno != ENOENT) {
903 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
904 eaname, strerror(errno));
908 } else { /* ea->ea_count > 0 */
909 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
910 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
915 if ((ftruncate(ea->ea_fd, 0)) == -1) {
916 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
921 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
922 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
931 while(count < ea->ea_count) {
932 if ( (*ea->ea_entries)[count].ea_name ) {
933 free((*ea->ea_entries)[count].ea_name);
934 (*ea->ea_entries)[count].ea_name = NULL;
945 if (ea->ea_entries) {
946 free(ea->ea_entries);
947 ea->ea_entries = NULL;
954 if (ea->ea_fd != -1) {
955 close(ea->ea_fd); /* also releases the fcntl lock */
964 /************************************************************************************
965 * VFS funcs called from afp_ea* funcs
966 ************************************************************************************/
969 * Function: get_easize
971 * Purpose: get size of an EA
975 * vol (r) current volume
976 * rbuf (w) DSI reply buffer
977 * rbuflen (rw) current length of data in reply buffer
979 * oflag (r) link and create flag
980 * attruname (r) name of attribute
982 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
986 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
988 int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
990 int ret = AFPERR_MISC;
991 unsigned int count = 0;
995 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
997 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
999 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
1006 while (count < ea.ea_count) {
1007 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1008 uint32 = htonl((*ea.ea_entries)[count].ea_size);
1009 memcpy(rbuf, &uint32, 4);
1013 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
1014 attruname, (*ea.ea_entries)[count].ea_size);
1020 if ((ea_close(&ea)) != 0) {
1021 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
1029 * Function: get_eacontent
1031 * Purpose: copy EA into rbuf
1035 * vol (r) current volume
1036 * rbuf (w) DSI reply buffer
1037 * rbuflen (rw) current length of data in reply buffer
1038 * uname (r) filename
1039 * oflag (r) link and create flag
1040 * attruname (r) name of attribute
1041 * maxreply (r) maximum EA size as of current specs/real-life
1043 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1047 * Copies EA into rbuf. Increments *rbuflen accordingly.
1049 int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
1051 int ret = AFPERR_MISC, fd = -1;
1052 unsigned int count = 0;
1058 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
1060 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1061 if (errno != ENOENT)
1062 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
1068 while (count < ea.ea_count) {
1069 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1070 if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) {
1075 if ((fd = open(eafile, O_RDONLY)) == -1) {
1080 /* Check how much the client wants, give him what we think is right */
1081 maxreply -= MAX_REPLY_EXTRA_BYTES;
1082 if (maxreply > MAX_EA_SIZE)
1083 maxreply = MAX_EA_SIZE;
1084 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
1085 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
1087 /* Put length of EA data in reply buffer */
1088 uint32 = htonl(toread);
1089 memcpy(rbuf, &uint32, 4);
1093 if (read(fd, rbuf, toread) != (ssize_t)toread) {
1094 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1107 if ((ea_close(&ea)) != 0) {
1108 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1117 * Function: list_eas
1119 * Purpose: copy names of EAs into attrnamebuf
1123 * vol (r) current volume
1124 * attrnamebuf (w) store names a consecutive C strings here
1125 * buflen (rw) length of names in attrnamebuf
1126 * uname (r) filename
1127 * oflag (r) link and create flag
1129 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1133 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1134 * Increments *buflen accordingly.
1136 int list_eas(VFS_FUNC_ARGS_EA_LIST)
1138 unsigned int count = 0;
1139 int attrbuflen = *buflen, ret = AFP_OK, len;
1140 char *buf = attrnamebuf;
1143 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1145 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1146 if (errno != ENOENT) {
1147 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1154 while (count < ea.ea_count) {
1155 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1156 if ( ( len = convert_string(vol->v_volcharset,
1158 (*ea.ea_entries)[count].ea_name,
1159 (*ea.ea_entries)[count].ea_namelen,
1167 /* convert_string didn't 0-terminate */
1168 attrnamebuf[attrbuflen + 255] = 0;
1170 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1171 uname, (*ea.ea_entries)[count].ea_name);
1173 attrbuflen += len + 1;
1174 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1175 /* Next EA name could overflow, so bail out with error.
1176 FIXME: evantually malloc/memcpy/realloc whatever.
1178 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1186 *buflen = attrbuflen;
1188 if ((ea_close(&ea)) != 0) {
1189 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1199 * Purpose: set a Solaris native EA
1203 * vol (r) current volume
1204 * uname (r) filename
1205 * attruname (r) EA name
1206 * ibuf (r) buffer with EA content
1207 * attrsize (r) length EA in ibuf
1208 * oflag (r) link and create flag
1210 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1214 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1215 * Increments *rbuflen accordingly.
1217 int set_ea(VFS_FUNC_ARGS_EA_SET)
1222 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1224 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1225 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1229 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1230 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1235 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1236 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1242 if ((ea_close(&ea)) != 0) {
1243 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1252 * Function: remove_ea
1254 * Purpose: remove a EA from a file
1258 * vol (r) current volume
1259 * uname (r) filename
1260 * attruname (r) EA name
1261 * oflag (r) link and create flag
1263 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1267 * Removes EA attruname from file uname.
1269 int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
1274 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1276 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1277 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1281 if ((ea_delentry(&ea, attruname)) == -1) {
1282 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1287 if ((delete_ea_file(&ea, attruname)) != 0) {
1288 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1294 if ((ea_close(&ea)) != 0) {
1295 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1303 /******************************************************************************************
1304 * EA VFS funcs that deal with file/dir cp/mv/rm
1305 ******************************************************************************************/
1307 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1309 unsigned int count = 0;
1314 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1317 if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) {
1318 if (errno == ENOENT)
1319 /* no EA files, nothing to do */
1322 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1328 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
1334 while (count < ea.ea_count) {
1335 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1339 free((*ea.ea_entries)[count].ea_name);
1340 (*ea.ea_entries)[count].ea_name = NULL;
1344 /* ea_close removes the EA header file for us because all names are NULL */
1345 if ((ea_close(&ea)) != 0) {
1346 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1350 if (dirfd != -1 && fchdir(cwd) != 0) {
1351 LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!");
1362 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1364 unsigned int count = 0;
1367 char srceapath[ MAXPATHLEN + 1];
1374 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1378 if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) {
1379 if (errno == ENOENT)
1380 /* no EA files, nothing to do */
1383 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1388 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1389 if (errno == ENOENT) {
1390 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1391 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1392 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1393 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1397 ad_close(&ad, ADFLAGS_HF);
1398 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1405 /* Loop through all EAs: */
1406 while (count < srcea.ea_count) {
1408 eaname = (*srcea.ea_entries)[count].ea_name;
1409 easize = (*srcea.ea_entries)[count].ea_size;
1411 /* Build src and dst paths for rename() */
1412 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
1416 strcpy(srceapath, eapath);
1417 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
1422 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1423 src, dst, srceapath, eapath);
1425 /* Add EA to dstea */
1426 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1427 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1428 src, dst, srceapath, eapath);
1433 /* Remove EA entry from srcea */
1434 if ((ea_delentry(&srcea, eaname)) == -1) {
1435 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1436 src, dst, srceapath, eapath);
1437 ea_delentry(&dstea, eaname);
1442 /* Now rename the EA */
1443 if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) {
1444 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1445 src, dst, srceapath, eapath);
1460 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1462 unsigned int count = 0;
1465 char srceapath[ MAXPATHLEN + 1];
1472 LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1475 if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) {
1476 if (errno == ENOENT)
1477 /* no EA files, nothing to do */
1480 LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1485 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1486 if (errno == ENOENT) {
1487 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1488 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1489 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1490 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1494 ad_close(&ad, ADFLAGS_HF);
1495 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1502 /* Loop through all EAs: */
1503 while (count < srcea.ea_count) {
1505 eaname = (*srcea.ea_entries)[count].ea_name;
1506 easize = (*srcea.ea_entries)[count].ea_size;
1508 /* Build src and dst paths for copy_file() */
1509 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
1513 strcpy(srceapath, eapath);
1514 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
1519 LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1520 src, dst, srceapath, eapath);
1522 /* Add EA to dstea */
1523 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1524 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1530 /* Now copy the EA */
1531 if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1532 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1533 src, dst, srceapath, eapath);
1547 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1550 unsigned int count = 0;
1555 LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1557 if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1558 if (errno == ENOENT)
1559 /* no EA files, nothing to do */
1562 LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1567 if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) {
1571 ret = AFPERR_ACCESS;
1579 while (count < ea.ea_count) {
1580 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
1584 if ((lchown(eaname, uid, gid)) != 0) {
1588 ret = AFPERR_ACCESS;
1601 if ((ea_close(&ea)) != 0) {
1602 LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1609 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1612 unsigned int count = 0;
1617 LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1619 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1620 if (errno == ENOENT)
1621 /* no EA files, nothing to do */
1627 /* Set mode on EA header file */
1628 if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1629 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
1633 ret = AFPERR_ACCESS;
1641 /* Set mode on EA files */
1642 while (count < ea.ea_count) {
1643 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
1647 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1648 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1652 ret = AFPERR_ACCESS;
1665 if ((ea_close(&ea)) != 0) {
1666 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1673 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1677 unsigned int count = 0;
1680 const char *eaname_safe = NULL;
1683 LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1684 /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1687 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
1692 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1693 /* ENOENT --> no EA files, nothing to do */
1694 if (errno != ENOENT)
1696 if (seteuid(uid) < 0) {
1697 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1703 /* Set mode on EA header */
1704 if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1705 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
1709 ret = AFPERR_ACCESS;
1717 /* Set mode on EA files */
1718 while (count < ea.ea_count) {
1719 eaname = (*ea.ea_entries)[count].ea_name;
1721 * Be careful with EA names from the EA header!
1722 * Eg NFS users might have access to them, can inject paths using ../ or /.....
1724 * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
1726 if ((eaname_safe = strrchr(eaname, '/'))) {
1727 LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
1728 eaname = eaname_safe;
1730 if ((eaname = ea_path(&ea, eaname, 1)) == NULL) {
1734 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1735 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
1739 ret = AFPERR_ACCESS;
1752 if (seteuid(uid) < 0) {
1753 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1757 if ((ea_close(&ea)) != 0) {
1758 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);