2 $Id: ea.c,v 1.6 2009-10-15 15:35:05 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 * Function: unpack_header
57 * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
61 * ea (rw) handle to struct ea
63 * Returns: 0 on success, -1 on error
67 * Verifies magic and version.
69 static int unpack_header(struct ea * restrict ea)
71 int ret = 0, count = 0;
75 /* Check magic and version */
77 if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
78 LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
83 if (*(uint16_t *)buf != htons(EA_VERSION)) {
84 LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
91 ea->ea_count = ntohs(*(uint16_t *)buf);
92 LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
95 if (ea->ea_count == 0)
98 /* Allocate storage for the ea_entries array */
99 ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
100 if ( ! ea->ea_entries) {
101 LOG(log_error, logtype_afpd, "unpack_header: OOM");
106 buf = ea->ea_data + EA_HEADER_SIZE;
107 while (count < ea->ea_count) {
108 memcpy(&uint32, buf, 4); /* EA size */
110 (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
111 (*(ea->ea_entries))[count].ea_name = strdup(buf);
112 if (! (*(ea->ea_entries))[count].ea_name) {
113 LOG(log_error, logtype_afpd, "unpack_header: OOM");
117 (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
118 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
120 LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
121 (*(ea->ea_entries))[count].ea_name,
122 (*(ea->ea_entries))[count].ea_size,
123 (*(ea->ea_entries))[count].ea_namelen);
133 * Function: pack_header
135 * Purpose: pack everything from struct ea into buffer at ea->ea_data
139 * ea (rw) handle to struct ea
141 * Returns: 0 on success, -1 on error
145 * adjust ea->ea_count in case an ea entry deletetion is detected
147 static int pack_header(struct ea * restrict ea)
149 int count = 0, eacount = 0;
152 size_t bufsize = EA_HEADER_SIZE;
154 char *buf = ea->ea_data + EA_HEADER_SIZE;
156 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
157 ea->filename, ea->ea_count, ea->ea_size);
159 if (ea->ea_count == 0)
160 /* nothing to do, magic, version and count are still valid in buffer */
163 while(count < ea->ea_count) { /* the names */
164 /* Check if its a deleted entry */
165 if ( ! ((*ea->ea_entries)[count].ea_name)) {
170 bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
175 bufsize += (eacount * 4); /* header + ea_size for each EA */
176 if (bufsize > ea->ea_size) {
177 /* we must realloc */
178 if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
179 LOG(log_error, logtype_afpd, "pack_header: OOM");
184 ea->ea_size = bufsize;
187 uint16 = htons(eacount);
188 memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
191 buf = ea->ea_data + EA_HEADER_SIZE;
192 while (count < ea->ea_count) {
193 /* Check if its a deleted entry */
194 if ( ! ((*ea->ea_entries)[count].ea_name)) {
200 uint32 = htonl((*(ea->ea_entries))[count].ea_size);
201 memcpy(buf, &uint32, 4);
204 /* Second: EA name as C-string */
205 strcpy(buf, (*(ea->ea_entries))[count].ea_name);
206 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
208 LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
209 (*(ea->ea_entries))[count].ea_name,
210 (*(ea->ea_entries))[count].ea_size,
211 (*(ea->ea_entries))[count].ea_namelen);
216 ea->ea_count = eacount;
218 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
219 ea->filename, ea->ea_count, ea->ea_size);
227 * Purpose: return name of ea header filename
232 * eaname (r) name of EA or NULL
234 * Returns: pointer to name in static buffer
238 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
239 * Files: "file" -> "file/.AppleDouble/file::EA"
240 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
241 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
243 static char * ea_path(const struct ea * restrict ea,
244 const char * restrict eaname)
247 static char pathbuf[MAXPATHLEN + 1];
249 /* get name of a adouble file from uname */
250 adname = ea->vol->vfs->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
251 /* copy it so we can work with it */
252 strlcpy(pathbuf, adname, MAXPATHLEN + 1);
254 strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
257 strlcat(pathbuf, "::", MAXPATHLEN + 1);
258 strlcat(pathbuf, eaname, MAXPATHLEN + 1);
265 * Function: ea_addentry
267 * Purpose: add one EA into ea->ea_entries[]
271 * ea (rw) pointer to struct ea
272 * attruname (r) name of EA
273 * attrsize (r) size of ea
274 * bitmap (r) bitmap from FP func
276 * Returns: new number of EA entries, -1 on error
280 * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
281 * Otherwise realloc and put entry at the end. Increments ea->ea_count.
283 static int ea_addentry(struct ea * restrict ea,
284 const char * restrict attruname,
291 /* First check if an EA of the requested name already exist */
292 if (ea->ea_count > 0) {
293 while (count < ea->ea_count) {
294 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
295 LOG(log_debug, logtype_afpd, "ea_addentry('%s'): exists", attruname);
296 if (bitmap & kXAttrCreate)
297 /* its like O_CREAT|O_EXCL -> fail */
299 if ( ! (bitmap & kXAttrReplace))
300 /* replace was not requested, then its an error */
308 if (ea->ea_count == 0) {
309 ea->ea_entries = malloc(sizeof(struct ea_entry));
310 if ( ! ea->ea_entries) {
311 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
315 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
317 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
320 ea->ea_entries = tmprealloc;
323 /* We've grown the array, now store the entry */
324 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
325 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
326 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
327 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
330 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
336 if (ea->ea_count == 0 && ea->ea_entries) {
337 /* We just allocated storage but had an error somewhere -> free storage*/
338 free(ea->ea_entries);
339 ea->ea_entries = NULL;
346 * Function: ea_delentry
348 * Purpose: delete one EA from ea->ea_entries[]
352 * ea (rw) pointer to struct ea
353 * attruname (r) EA name
355 * Returns: new number of EA entries, -1 on error
359 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
360 * Marks it as unused just by freeing name and setting it to NULL.
361 * ea_close and pack_buffer must honor this.
363 static int ea_delentry(struct ea * restrict ea,
364 const char * restrict attruname)
366 int ret = 0, count = 0;
368 if (ea->ea_count == 0) {
369 LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
373 while (count < ea->ea_count) {
374 /* search matching EA */
375 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
376 free((*ea->ea_entries)[count].ea_name);
377 (*ea->ea_entries)[count].ea_name = NULL;
379 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
380 attruname, count + 1, ea->ea_count);
391 * Function: create_ea_header
393 * Purpose: create EA header file, only called from ea_open
397 * uname (r) filename for which we have to create a header
398 * ea (rw) ea handle with already allocated storage pointed to
401 * Returns: fd of open header file on success, -1 on error, errno semantics:
402 * EEXIST: open with O_CREAT | O_EXCL failed
406 * Creates EA header file and initialize ea->ea_data buffer.
407 * Possibe race condition with other afpd processes:
408 * we were called because header file didn't exist in eg. ea_open. We then
409 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
410 * What do we do then? Someone else is in the process of creating the header too, but
411 * it might not have finished it. That means we cant just open, read and use it!
412 * We therefor currently just break with an error.
413 * On return the header file is still r/w locked.
415 static int create_ea_header(const char * restrict uname,
416 struct ea * restrict ea)
418 int fd = -1, err = 0;
421 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
422 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
427 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
428 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
435 *(uint32_t *)ptr = htonl(EA_MAGIC);
437 *(uint16_t *)ptr = htons(EA_VERSION);
438 ptr += EA_VERSION_LEN;
439 *(uint16_t *)ptr = 0; /* count */
441 ea->ea_size = EA_HEADER_SIZE;
442 ea->ea_inited = EA_INITED;
455 * Purpose: write an EA to disk
459 * ea (r) struct ea handle
460 * attruname (r) EA name
461 * ibuf (r) buffer with EA content
462 * attrsize (r) size of EA
464 * Returns: 0 on success, -1 on error
468 * Creates/overwrites EA file.
471 static int write_ea(const struct ea * restrict ea,
472 const char * restrict attruname,
473 const char * restrict ibuf,
476 int fd = -1, ret = AFP_OK;
480 eaname = ea_path(ea, attruname);
481 LOG(log_maxdebug, logtype_afpd, "write_ea: ea_apth: %s", eaname);
483 /* Check if it exists, remove if yes*/
484 if ((stat(eaname, &st)) == 0) {
485 if ((unlink(eaname)) != 0) {
487 return AFPERR_ACCESS;
493 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
494 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
499 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
500 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
505 if ((write(fd, ibuf, attrsize)) != attrsize) {
506 LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
513 close(fd); /* and unlock */
518 * Function: delete_ea_file
520 * Purpose: delete EA file from disk
524 * ea (r) struct ea handle
525 * attruname (r) EA name
527 * Returns: 0 on success, -1 on error
529 static int delete_ea_file(const struct ea * restrict ea,
536 eafile = ea_path(ea, eaname);
538 /* Check if it exists, remove if yes*/
539 if ((stat(eafile, &st)) == 0) {
540 if ((unlink(eafile)) != 0) {
541 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
542 eafile, strerror(errno));
545 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
554 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
558 * vol (r) current volume
559 * uname (r) filename for which we have to open a header
560 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
561 * EA_RDONLY: open read only
562 * EA_RDWR: open read/write
563 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
564 * ea (w) pointer to a struct ea that we fill
566 * Returns: 0 on success, -1 on error
570 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
571 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
572 * file is either read or write locked depending on the open flags.
573 * When you're done with struct ea you must call ea_close on it.
575 static int ea_open(const struct vol * restrict vol,
576 const char * restrict uname,
578 struct ea * restrict ea)
584 /* Enforce usage rules! */
585 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
586 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
590 /* Set it all to 0 */
591 memset(ea, 0, sizeof(struct ea));
593 ea->vol = vol; /* ea_close needs it */
594 ea->ea_flags = eaflags;
595 /* Dont check for errors, eg when removing the file is already gone */
597 if (S_ISDIR(st.st_mode))
598 ea->ea_flags |= EA_DIR;
600 if ( ! (ea->filename = strdup(uname))) {
601 LOG(log_error, logtype_afpd, "ea_open: OOM");
605 eaname = ea_path(ea, NULL);
606 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
608 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
609 if ((stat(eaname, &st)) != 0) {
610 if (errno == ENOENT) {
612 /* It doesnt exist */
614 if ( ! (eaflags & EA_CREATE)) {
615 /* creation was not requested, so return with error */
620 /* Now create a header file */
622 /* malloc buffer for minimal on disk data */
623 ea->ea_data = malloc(EA_HEADER_SIZE);
625 LOG(log_error, logtype_afpd, "ea_open: OOM");
631 ea->ea_fd = create_ea_header(eaname, ea);
632 if (ea->ea_fd == -1) {
639 } else {/* errno != ENOENT */
645 /* header file exists, so read and parse it */
647 /* malloc buffer where we read disk file into */
648 ea->ea_size = st.st_size;
649 ea->ea_data = malloc(st.st_size);
651 LOG(log_error, logtype_afpd, "ea_open: OOM");
656 /* Now lock, open and read header file from disk */
657 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
658 LOG(log_error, logtype_afpd, "ea_open: error on open for header: %s", eaname);
664 if (ea->ea_flags & EA_RDONLY) {
666 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
667 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
671 } else { /* EA_RDWR */
673 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
674 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
681 if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
682 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
687 if ((unpack_header(ea)) != 0) {
688 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
695 ea->ea_inited = EA_INITED;
713 * Purpose: flushes and closes an ea handle
717 * ea (rw) pointer to ea handle
719 * Returns: 0 on success, -1 on error
723 * Flushes and then closes and frees all resouces held by ea handle.
724 * Pack data in ea into ea_data, then write ea_data to disk
726 static int ea_close(struct ea * restrict ea)
728 int ret = 0, count = 0;
732 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
734 if (ea->ea_inited != EA_INITED) {
735 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
739 /* pack header and write it to disk if it was opened EA_RDWR*/
740 if (ea->ea_flags & EA_RDWR) {
741 if ((pack_header(ea)) != 0) {
742 LOG(log_error, logtype_afpd, "ea_close: pack header");
745 if (ea->ea_count == 0) {
746 /* Check if EA header exists and remove it */
747 eaname = ea_path(ea, NULL);
748 if ((stat(eaname, &st)) == 0) {
749 if ((unlink(eaname)) != 0) {
750 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
751 eaname, strerror(errno));
755 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
758 if (errno != ENOENT) {
759 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
760 eaname, strerror(errno));
764 } else { /* ea->ea_count > 0 */
765 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
766 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
771 if ((ftruncate(ea->ea_fd, 0)) == -1) {
772 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
777 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
778 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
787 while(count < ea->ea_count) {
788 if ( (*ea->ea_entries)[count].ea_name ) {
789 free((*ea->ea_entries)[count].ea_name);
790 (*ea->ea_entries)[count].ea_name = NULL;
801 if (ea->ea_entries) {
802 free(ea->ea_entries);
803 ea->ea_entries = NULL;
810 if (ea->ea_fd != -1) {
811 close(ea->ea_fd); /* also releases the fcntl lock */
820 /************************************************************************************
821 * VFS funcs called from afp_ea* funcs
822 ************************************************************************************/
825 * Function: get_easize
827 * Purpose: get size of an EA
831 * vol (r) current volume
832 * rbuf (w) DSI reply buffer
833 * rbuflen (rw) current length of data in reply buffer
835 * oflag (r) link and create flag
836 * attruname (r) name of attribute
838 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
842 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
844 int get_easize(const struct vol * restrict vol,
845 char * restrict rbuf,
846 int * restrict rbuflen,
847 const char * restrict uname,
849 const char * restrict attruname)
851 int ret = AFPERR_MISC, count = 0;
855 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
857 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
858 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
862 while (count < ea.ea_count) {
863 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
864 uint32 = htonl((*ea.ea_entries)[count].ea_size);
865 memcpy(rbuf, &uint32, 4);
869 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
870 attruname, (*ea.ea_entries)[count].ea_size);
876 if ((ea_close(&ea)) != 0) {
877 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
885 * Function: get_eacontent
887 * Purpose: copy EA into rbuf
891 * vol (r) current volume
892 * rbuf (w) DSI reply buffer
893 * rbuflen (rw) current length of data in reply buffer
895 * oflag (r) link and create flag
896 * attruname (r) name of attribute
897 * maxreply (r) maximum EA size as of current specs/real-life
899 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
903 * Copies EA into rbuf. Increments *rbuflen accordingly.
905 int get_eacontent(const struct vol * restrict vol,
906 char * restrict rbuf,
907 int * restrict rbuflen,
908 const char * restrict uname,
910 const char * restrict attruname,
913 int ret = AFPERR_MISC, count = 0, fd = -1;
918 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
920 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
921 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
925 while (count < ea.ea_count) {
926 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
927 if ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
932 /* Check how much the client wants, give him what we think is right */
933 maxreply -= MAX_REPLY_EXTRA_BYTES;
934 if (maxreply > MAX_EA_SIZE)
935 maxreply = MAX_EA_SIZE;
936 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
937 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
939 /* Put length of EA data in reply buffer */
940 uint32 = htonl(toread);
941 memcpy(rbuf, &uint32, 4);
945 if ((read(fd, rbuf, toread)) != toread) {
946 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
959 if ((ea_close(&ea)) != 0) {
960 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
971 * Purpose: copy names of EAs into attrnamebuf
975 * vol (r) current volume
976 * attrnamebuf (w) store names a consecutive C strings here
977 * buflen (rw) length of names in attrnamebuf
979 * oflag (r) link and create flag
981 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
985 * Copies names of all EAs of uname as consecutive C strings into rbuf.
986 * Increments *buflen accordingly.
988 int list_eas(const struct vol * restrict vol,
989 char * restrict attrnamebuf,
990 int * restrict buflen,
991 const char * restrict uname,
994 int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
995 char *buf = attrnamebuf;
998 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1000 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1001 if (errno != ENOENT) {
1002 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1009 while (count < ea.ea_count) {
1010 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1011 if ( ( len = convert_string(vol->v_volcharset,
1013 (*ea.ea_entries)[count].ea_name,
1014 (*ea.ea_entries)[count].ea_namelen,
1022 /* convert_string didn't 0-terminate */
1023 attrnamebuf[attrbuflen + 255] = 0;
1025 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1026 uname, (*ea.ea_entries)[count].ea_name);
1028 attrbuflen += len + 1;
1029 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1030 /* Next EA name could overflow, so bail out with error.
1031 FIXME: evantually malloc/memcpy/realloc whatever.
1033 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1041 *buflen = attrbuflen;
1043 if ((ea_close(&ea)) != 0) {
1044 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1054 * Purpose: set a Solaris native EA
1058 * vol (r) current volume
1059 * uname (r) filename
1060 * attruname (r) EA name
1061 * ibuf (r) buffer with EA content
1062 * attrsize (r) length EA in ibuf
1063 * oflag (r) link and create flag
1065 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1069 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1070 * Increments *rbuflen accordingly.
1072 int set_ea(const struct vol * restrict vol,
1073 const char * restrict uname,
1074 const char * restrict attruname,
1075 const char * restrict ibuf,
1082 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1084 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1085 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1089 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1090 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1095 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1096 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1102 if ((ea_close(&ea)) != 0) {
1103 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1112 * Function: remove_ea
1114 * Purpose: remove a EA from a file
1118 * vol (r) current volume
1119 * uname (r) filename
1120 * attruname (r) EA name
1121 * oflag (r) link and create flag
1123 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1127 * Removes EA attruname from file uname.
1129 int remove_ea(const struct vol * restrict vol,
1130 const char * restrict uname,
1131 const char * restrict attruname,
1137 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1139 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1140 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1144 if ((ea_delentry(&ea, attruname)) == -1) {
1145 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1150 if ((delete_ea_file(&ea, attruname)) != 0) {
1151 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1157 if ((ea_close(&ea)) != 0) {
1158 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1166 /**********************************************************************************
1167 * Solaris EA VFS funcs
1168 **********************************************************************************/
1171 * Function: sol_get_easize
1173 * Purpose: get size of an EA on Solaris native EA
1177 * vol (r) current volume
1178 * rbuf (w) DSI reply buffer
1179 * rbuflen (rw) current length of data in reply buffer
1180 * uname (r) filename
1181 * oflag (r) link and create flag
1182 * attruname (r) name of attribute
1184 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1188 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
1190 #ifdef HAVE_SOLARIS_EAS
1191 int sol_get_easize(const struct vol * restrict vol,
1192 char * restrict rbuf,
1193 int * restrict rbuflen,
1194 const char * restrict uname,
1196 cons char * restrict attruname)
1202 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
1204 if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
1205 if (errno == ELOOP) {
1206 /* its a symlink and client requested O_NOFOLLOW */
1207 LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
1214 LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
1218 if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
1219 LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
1223 attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
1225 /* Start building reply packet */
1227 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
1229 /* length of attribute data */
1230 attrsize = htonl(attrsize);
1231 memcpy(rbuf, &attrsize, 4);
1240 #endif /* HAVE_SOLARIS_EAS */
1243 * Function: sol_get_eacontent
1245 * Purpose: copy Solaris native EA into rbuf
1249 * vol (r) current volume
1250 * rbuf (w) DSI reply buffer
1251 * rbuflen (rw) current length of data in reply buffer
1252 * uname (r) filename
1253 * oflag (r) link and create flag
1254 * attruname (r) name of attribute
1255 * maxreply (r) maximum EA size as of current specs/real-life
1257 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1261 * Copies EA into rbuf. Increments *rbuflen accordingly.
1263 #ifdef HAVE_SOLARIS_EAS
1264 int sol_get_eacontent(const struct vol * restrict vol,
1265 char * restrict rbuf,
1266 int * restrict rbuflen,
1267 const char * restrict uname,
1269 char * restrict attruname,
1273 size_t toread, okread = 0, len;
1277 if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
1278 if (errno == ELOOP) {
1279 /* its a symlink and client requested O_NOFOLLOW */
1280 LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
1287 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
1291 if ( -1 == (fstat(attrdirfd, &st))) {
1292 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
1297 /* Start building reply packet */
1299 maxreply -= MAX_REPLY_EXTRA_BYTES;
1300 if (maxreply > MAX_EA_SIZE)
1301 maxreply = MAX_EA_SIZE;
1303 /* But never send more than the client requested */
1304 toread = (maxreply < st.st_size) ? maxreply : st.st_size;
1306 LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
1308 /* remember where we must store length of attribute data in rbuf */
1314 len = read(attrdirfd, rbuf, toread);
1316 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
1323 if ((len == 0) || (okread == toread))
1327 okread = htonl((uint32_t)okread);
1328 memcpy(datalength, &okread, 4);
1336 #endif /* HAVE_SOLARIS_EAS */
1339 * Function: sol_list_eas
1341 * Purpose: copy names of Solaris native EA into attrnamebuf
1345 * vol (r) current volume
1346 * attrnamebuf (w) store names a consecutive C strings here
1347 * buflen (rw) length of names in attrnamebuf
1348 * uname (r) filename
1349 * oflag (r) link and create flag
1351 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1355 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1356 * Increments *rbuflen accordingly.
1358 #ifdef HAVE_SOLARIS_EAS
1359 int sol_list_eas(const struct vol * restrict vol,
1360 char * restrict attrnamebuf,
1361 int * restrict buflen,
1362 const char * restrict uname,
1365 int ret, attrbuflen = *buflen, len, attrdirfd = 0;
1369 /* Now list file attribute dir */
1370 if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
1371 if (errno == ELOOP) {
1372 /* its a symlink and client requested O_NOFOLLOW */
1373 ret = AFPERR_BADTYPE;
1376 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1381 if (NULL == (dirp = fdopendir(attrdirfd))) {
1382 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1387 while ((dp = readdir(dirp))) {
1388 /* check if its "." or ".." */
1389 if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
1390 (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
1393 len = strlen(dp->d_name);
1395 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1396 if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
1401 /* convert_string didn't 0-terminate */
1402 attrnamebuf[attrbuflen + 255] = 0;
1404 LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
1406 attrbuflen += len + 1;
1407 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1408 /* Next EA name could overflow, so bail out with error.
1409 FIXME: evantually malloc/memcpy/realloc whatever.
1411 LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
1426 *buflen = attrbuflen;
1429 #endif /* HAVE_SOLARIS_EAS */
1432 * Function: sol_set_ea
1434 * Purpose: set a Solaris native EA
1438 * vol (r) current volume
1439 * uname (r) filename
1440 * attruname (r) EA name
1441 * ibuf (r) buffer with EA content
1442 * attrsize (r) length EA in ibuf
1443 * oflag (r) link and create flag
1445 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1449 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1450 * Increments *rbuflen accordingly.
1452 #ifdef HAVE_SOLARIS_EAS
1453 int sol_set_ea(const struct vol * restrict vol,
1454 const char * restrict u_name,
1455 const char * restrict attruname,
1456 const char * restrict ibuf,
1462 if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
1463 if (errno == ELOOP) {
1464 /* its a symlink and client requested O_NOFOLLOW */
1465 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1468 LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1472 if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
1473 LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
1479 #endif /* HAVE_SOLARIS_EAS */
1482 * Function: sol_remove_ea
1484 * Purpose: remove a Solaris native EA
1488 * vol (r) current volume
1489 * uname (r) filename
1490 * attruname (r) EA name
1491 * oflag (r) link and create flag
1493 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1497 * Removes EA attruname from file uname.
1499 #ifdef HAVE_SOLARIS_EAS
1500 int sol_remove_ea(const struct vol * restrict vol,
1501 const char * restrict uname,
1502 const char * restrict attruname,
1507 if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
1510 /* its a symlink and client requested O_NOFOLLOW */
1511 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1514 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1515 return AFPERR_ACCESS;
1517 LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1522 if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
1523 if (errno == EACCES) {
1524 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1525 return AFPERR_ACCESS;
1527 LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1532 #endif /* HAVE_SOLARIS_EAS */
1534 /******************************************************************************************
1535 * EA VFS funcs that deal with file/dir cp/mv/rm
1536 ******************************************************************************************/
1538 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1540 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1542 int count = 0, ret = AFP_OK;
1546 if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
1547 if (errno == ENOENT)
1548 /* no EA files, nothing to do */
1551 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1556 while (count < ea.ea_count) {
1557 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1561 free((*ea.ea_entries)[count].ea_name);
1562 (*ea.ea_entries)[count].ea_name = NULL;
1566 /* ea_close removes the EA header file for us because all names are NULL */
1567 if ((ea_close(&ea)) != 0) {
1568 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1575 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1580 char srceapath[ MAXPATHLEN + 1];
1587 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1591 if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1592 if (errno == ENOENT)
1593 /* no EA files, nothing to do */
1596 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1601 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1602 if (errno == ENOENT) {
1603 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1604 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1605 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1606 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1610 ad_close(&ad, ADFLAGS_HF);
1611 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1618 /* Loop through all EAs: */
1619 while (count < srcea.ea_count) {
1621 eaname = (*srcea.ea_entries)[count].ea_name;
1622 easize = (*srcea.ea_entries)[count].ea_size;
1624 /* Build src and dst paths for rename() */
1625 eapath = ea_path(&srcea, eaname);
1626 strcpy(srceapath, eapath);
1627 eapath = ea_path(&dstea, eaname);
1629 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1630 src, dst, srceapath, eapath);
1632 /* Add EA to dstea */
1633 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1634 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1635 src, dst, srceapath, eapath);
1640 /* Remove EA entry from srcea */
1641 if ((ea_delentry(&srcea, eaname)) == -1) {
1642 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1643 src, dst, srceapath, eapath);
1644 ea_delentry(&dstea, eaname);
1649 /* Now rename the EA */
1650 if ((rename( srceapath, eapath)) < 0) {
1651 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1652 src, dst, srceapath, eapath);
1667 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1672 char srceapath[ MAXPATHLEN + 1];
1679 LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1682 if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1683 if (errno == ENOENT)
1684 /* no EA files, nothing to do */
1687 LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1692 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1693 if (errno == ENOENT) {
1694 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1695 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1696 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1697 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1701 ad_close(&ad, ADFLAGS_HF);
1702 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1709 /* Loop through all EAs: */
1710 while (count < srcea.ea_count) {
1712 eaname = (*srcea.ea_entries)[count].ea_name;
1713 easize = (*srcea.ea_entries)[count].ea_size;
1715 /* Build src and dst paths for copy_file() */
1716 eapath = ea_path(&srcea, eaname);
1717 strcpy(srceapath, eapath);
1718 eapath = ea_path(&dstea, eaname);
1720 LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1721 src, dst, srceapath, eapath);
1723 /* Add EA to dstea */
1724 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1725 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1731 /* Now copy the EA */
1732 if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1733 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1734 src, dst, srceapath, eapath);
1748 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1750 LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1752 int count = 0, ret = AFP_OK;
1757 if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1758 if (errno == ENOENT)
1759 /* no EA files, nothing to do */
1762 LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1767 if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
1771 ret = AFPERR_ACCESS;
1779 while (count < ea.ea_count) {
1780 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name);
1781 if ((chown(eaname, uid, gid)) != 0) {
1785 ret = AFPERR_ACCESS;
1798 if ((ea_close(&ea)) != 0) {
1799 LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);