2 $Id: ea.c,v 1.3 2009-10-14 15:04:01 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>
44 * Store Extended Attributes inside .AppleDouble folders as follows:
46 * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
48 * - create header with with the format struct adouble_ea_ondisk, the file is written to
49 * ".AppleDouble/fileWithEAs::EA"
50 * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2"
54 * Function: unpack_header
56 * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
60 * ea (rw) handle to struct ea
62 * Returns: 0 on success, -1 on error
66 * Verifies magic and version.
68 static int unpack_header(struct ea * restrict ea)
70 int ret = 0, count = 0;
74 /* Check magic and version */
76 if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
77 LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
82 if (*(uint16_t *)buf != htons(EA_VERSION)) {
83 LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
90 ea->ea_count = ntohs(*(uint16_t *)buf);
91 LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
94 if (ea->ea_count == 0)
97 /* Allocate storage for the ea_entries array */
98 ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
99 if ( ! ea->ea_entries) {
100 LOG(log_error, logtype_afpd, "unpack_header: OOM");
105 buf = ea->ea_data + EA_HEADER_SIZE;
106 while (count < ea->ea_count) {
107 memcpy(&uint32, buf, 4); /* EA size */
109 (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
110 (*(ea->ea_entries))[count].ea_name = strdup(buf);
111 if (! (*(ea->ea_entries))[count].ea_name) {
112 LOG(log_error, logtype_afpd, "unpack_header: OOM");
116 (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
117 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
119 LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
120 (*(ea->ea_entries))[count].ea_name,
121 (*(ea->ea_entries))[count].ea_size,
122 (*(ea->ea_entries))[count].ea_namelen);
132 * Function: pack_header
134 * Purpose: pack everything from struct ea into buffer at ea->ea_data
138 * ea (rw) handle to struct ea
140 * Returns: 0 on success, -1 on error
144 * adjust ea->ea_count in case an ea entry deletetion is detected
146 static int pack_header(struct ea * restrict ea)
148 int count = 0, eacount = 0;
151 size_t bufsize = EA_HEADER_SIZE;
153 char *buf = ea->ea_data + EA_HEADER_SIZE;
155 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
156 ea->filename, ea->ea_count, ea->ea_size);
158 if (ea->ea_count == 0)
159 /* nothing to do, magic, version and count are still valid in buffer */
162 while(count < ea->ea_count) { /* the names */
163 /* Check if its a deleted entry */
164 if ( ! ((*ea->ea_entries)[count].ea_name)) {
169 bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
174 bufsize += (eacount * 4); /* header + ea_size for each EA */
175 if (bufsize > ea->ea_size) {
176 /* we must realloc */
177 if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
178 LOG(log_error, logtype_afpd, "pack_header: OOM");
183 ea->ea_size = bufsize;
186 uint16 = htons(eacount);
187 memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
190 buf = ea->ea_data + EA_HEADER_SIZE;
191 while (count < ea->ea_count) {
192 /* Check if its a deleted entry */
193 if ( ! ((*ea->ea_entries)[count].ea_name)) {
199 uint32 = htonl((*(ea->ea_entries))[count].ea_size);
200 memcpy(buf, &uint32, 4);
203 /* Second: EA name as C-string */
204 strcpy(buf, (*(ea->ea_entries))[count].ea_name);
205 buf += (*(ea->ea_entries))[count].ea_namelen + 1;
207 LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
208 (*(ea->ea_entries))[count].ea_name,
209 (*(ea->ea_entries))[count].ea_size,
210 (*(ea->ea_entries))[count].ea_namelen);
215 ea->ea_count = eacount;
217 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
218 ea->filename, ea->ea_count, ea->ea_size);
226 * Purpose: return name of ea header filename
231 * eaname (r) name of EA or NULL
233 * Returns: pointer to name in static buffer
237 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
238 * Files: "file" -> "file/.AppleDouble/file::EA"
239 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
240 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
242 static char * ea_path(const struct ea * restrict ea,
243 const char * restrict eaname)
246 static char pathbuf[MAXPATHLEN + 1];
248 /* get name of a adouble file from uname */
249 adname = ea->vol->vfs->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
250 /* copy it so we can work with it */
251 strlcpy(pathbuf, adname, MAXPATHLEN + 1);
253 strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
256 strlcat(pathbuf, "::", MAXPATHLEN + 1);
257 strlcat(pathbuf, eaname, MAXPATHLEN + 1);
264 * Function: ea_addentry
266 * Purpose: add one EA into ea->ea_entries[]
270 * ea (rw) pointer to struct ea
271 * attruname (r) name of EA
272 * attrsize (r) size of ea
273 * bitmap (r) bitmap from FP func
275 * Returns: new number of EA entries, -1 on error
279 * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
280 * Otherwise realloc and put entry at the end. Increments ea->ea_count.
282 static int ea_addentry(struct ea * restrict ea,
283 const char * restrict attruname,
290 /* First check if an EA of the requested name already exist */
291 if (ea->ea_count > 0) {
292 while (count < ea->ea_count) {
293 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
294 LOG(log_debug, logtype_afpd, "ea_addentry('%s'): exists", attruname);
295 if (bitmap & kXAttrCreate)
296 /* its like O_CREAT|O_EXCL -> fail */
298 if ( ! (bitmap & kXAttrReplace))
299 /* replace was not requested, then its an error */
307 if (ea->ea_count == 0) {
308 ea->ea_entries = malloc(sizeof(struct ea_entry));
309 if ( ! ea->ea_entries) {
310 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
314 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
316 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
319 ea->ea_entries = tmprealloc;
322 /* We've grown the array, now store the entry */
323 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
324 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
325 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
326 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
329 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
335 if (ea->ea_count == 0 && ea->ea_entries) {
336 /* We just allocated storage but had an error somewhere -> free storage*/
337 free(ea->ea_entries);
338 ea->ea_entries = NULL;
345 * Function: ea_delentry
347 * Purpose: delete one EA from ea->ea_entries[]
351 * ea (rw) pointer to struct ea
352 * attruname (r) EA name
354 * Returns: new number of EA entries, -1 on error
358 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
359 * Marks it as unused just by freeing name and setting it to NULL.
360 * ea_close and pack_buffer must honor this.
362 static int ea_delentry(struct ea * restrict ea,
363 const char * restrict attruname)
365 int ret = 0, count = 0;
367 if (ea->ea_count == 0) {
368 LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
372 while (count < ea->ea_count) {
373 /* search matching EA */
374 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
375 free((*ea->ea_entries)[count].ea_name);
376 (*ea->ea_entries)[count].ea_name = NULL;
378 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
379 attruname, count + 1, ea->ea_count);
390 * Function: create_ea_header
392 * Purpose: create EA header file, only called from ea_open
396 * uname (r) filename for which we have to create a header
397 * ea (rw) ea handle with already allocated storage pointed to
400 * Returns: fd of open header file on success, -1 on error, errno semantics:
401 * EEXIST: open with O_CREAT | O_EXCL failed
405 * Creates EA header file and initialize ea->ea_data buffer.
406 * Possibe race condition with other afpd processes:
407 * we were called because header file didn't exist in eg. ea_open. We then
408 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
409 * What do we do then? Someone else is in the process of creating the header too, but
410 * it might not have finished it. That means we cant just open, read and use it!
411 * We therefor currently just break with an error.
412 * On return the header file is still r/w locked.
414 static int create_ea_header(const char * restrict uname,
415 struct ea * restrict ea)
417 int fd = -1, err = 0;
420 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
421 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
426 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
427 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
434 *(uint32_t *)ptr = htonl(EA_MAGIC);
436 *(uint16_t *)ptr = htons(EA_VERSION);
437 ptr += EA_VERSION_LEN;
438 *(uint16_t *)ptr = 0; /* count */
440 ea->ea_size = EA_HEADER_SIZE;
441 ea->ea_inited = EA_INITED;
454 * Purpose: write an EA to disk
458 * ea (r) struct ea handle
459 * attruname (r) EA name
460 * ibuf (r) buffer with EA content
461 * attrsize (r) size of EA
463 * Returns: 0 on success, -1 on error
467 * Creates/overwrites EA file.
470 static int write_ea(const struct ea * restrict ea,
471 const char * restrict attruname,
472 const char * restrict ibuf,
475 int fd = -1, ret = AFP_OK;
479 eaname = ea_path(ea, attruname);
480 LOG(log_maxdebug, logtype_afpd, "write_ea: ea_apth: %s", eaname);
482 /* Check if it exists, remove if yes*/
483 if ((stat(eaname, &st)) == 0) {
484 if ((unlink(eaname)) != 0) {
486 return AFPERR_ACCESS;
492 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
493 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
498 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
499 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
504 if ((write(fd, ibuf, attrsize)) != attrsize) {
505 LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
512 close(fd); /* and unlock */
517 * Function: delete_ea_file
519 * Purpose: delete EA file from disk
523 * ea (r) struct ea handle
524 * attruname (r) EA name
526 * Returns: 0 on success, -1 on error
528 static int delete_ea_file(const struct ea * restrict ea,
535 eafile = ea_path(ea, eaname);
537 /* Check if it exists, remove if yes*/
538 if ((stat(eafile, &st)) == 0) {
539 if ((unlink(eafile)) != 0) {
540 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
541 eafile, strerror(errno));
544 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
553 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
557 * vol (r) current volume
558 * uname (r) filename for which we have to open a header
559 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
560 * EA_RDONLY: open read only
561 * EA_RDWR: open read/write
562 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
563 * ea (w) pointer to a struct ea that we fill
565 * Returns: 0 on success, -1 on error
569 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
570 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
571 * file is either read or write locked depending on the open flags.
572 * When you're done with struct ea you must call ea_close on it.
574 static int ea_open(const struct vol * restrict vol,
575 const char * restrict uname,
577 struct ea * restrict ea)
583 /* Enforce usage rules! */
584 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
585 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
589 /* Set it all to 0 */
590 memset(ea, 0, sizeof(struct ea));
592 ea->vol = vol; /* ea_close needs it */
594 ea->ea_flags = eaflags;
595 if (S_ISDIR(st.st_mode))
596 ea->ea_flags |= EA_DIR;
598 if ( ! (ea->filename = strdup(uname))) {
599 LOG(log_error, logtype_afpd, "ea_open: OOM");
603 eaname = ea_path(ea, NULL);
604 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
606 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
607 if ((stat(eaname, &st)) != 0) {
608 if (errno == ENOENT) {
610 /* It doesnt exist */
612 if ( ! (eaflags & EA_CREATE)) {
613 /* creation was not requested, so return with error */
618 /* Now create a header file */
620 /* malloc buffer for minimal on disk data */
621 ea->ea_data = malloc(EA_HEADER_SIZE);
623 LOG(log_error, logtype_afpd, "ea_open: OOM");
629 ea->ea_fd = create_ea_header(eaname, ea);
630 if (ea->ea_fd == -1) {
637 } else {/* errno != ENOENT */
643 /* header file exists, so read and parse it */
645 /* malloc buffer where we read disk file into */
646 ea->ea_size = st.st_size;
647 ea->ea_data = malloc(st.st_size);
649 LOG(log_error, logtype_afpd, "ea_open: OOM");
654 /* Now lock, open and read header file from disk */
655 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
656 LOG(log_error, logtype_afpd, "ea_open: error on open for header: %s", eaname);
662 if (ea->ea_flags & EA_RDONLY) {
664 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
665 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
669 } else { /* EA_RDWR */
671 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
672 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
679 if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
680 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
685 if ((unpack_header(ea)) != 0) {
686 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
693 ea->ea_inited = EA_INITED;
711 * Purpose: flushes and closes an ea handle
715 * ea (rw) pointer to ea handle
717 * Returns: 0 on success, -1 on error
721 * Flushes and then closes and frees all resouces held by ea handle.
722 * Pack data in ea into ea_data, then write ea_data to disk
724 static int ea_close(struct ea * restrict ea)
726 int ret = 0, count = 0;
730 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
732 if (ea->ea_inited != EA_INITED) {
733 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
737 /* pack header and write it to disk if it was opened EA_RDWR*/
738 if (ea->ea_flags & EA_RDWR) {
739 if ((pack_header(ea)) != 0) {
740 LOG(log_error, logtype_afpd, "ea_close: pack header");
743 if (ea->ea_count == 0) {
744 /* Check if EA header exists and remove it */
745 eaname = ea_path(ea, NULL);
746 if ((stat(eaname, &st)) == 0) {
747 if ((unlink(eaname)) != 0) {
748 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
749 eaname, strerror(errno));
753 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
756 if (errno != ENOENT) {
757 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
758 eaname, strerror(errno));
762 } else { /* ea->ea_count > 0 */
763 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
764 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
769 if ((ftruncate(ea->ea_fd, 0)) == -1) {
770 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
775 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
776 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
785 while(count < ea->ea_count) {
786 if ( (*ea->ea_entries)[count].ea_name ) {
787 free((*ea->ea_entries)[count].ea_name);
788 (*ea->ea_entries)[count].ea_name = NULL;
799 if (ea->ea_entries) {
800 free(ea->ea_entries);
801 ea->ea_entries = NULL;
808 if (ea->ea_fd != -1) {
809 close(ea->ea_fd); /* also releases the fcntl lock */
818 /************************************************************************************
819 * VFS funcs called from afp_ea* funcs
820 ************************************************************************************/
823 * Function: get_easize
825 * Purpose: get size of an EA
829 * vol (r) current volume
830 * rbuf (w) DSI reply buffer
831 * rbuflen (rw) current length of data in reply buffer
833 * oflag (r) link and create flag
834 * attruname (r) name of attribute
836 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
840 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
842 int get_easize(const struct vol * restrict vol,
843 char * restrict rbuf,
844 int * restrict rbuflen,
845 const char * restrict uname,
847 const char * restrict attruname)
849 int ret = AFPERR_MISC, count = 0;
853 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
855 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
856 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
860 while (count < ea.ea_count) {
861 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
862 uint32 = htonl((*ea.ea_entries)[count].ea_size);
863 memcpy(rbuf, &uint32, 4);
867 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
868 attruname, (*ea.ea_entries)[count].ea_size);
874 if ((ea_close(&ea)) != 0) {
875 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
883 * Function: get_eacontent
885 * Purpose: copy EA into rbuf
889 * vol (r) current volume
890 * rbuf (w) DSI reply buffer
891 * rbuflen (rw) current length of data in reply buffer
893 * oflag (r) link and create flag
894 * attruname (r) name of attribute
895 * maxreply (r) maximum EA size as of current specs/real-life
897 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
901 * Copies EA into rbuf. Increments *rbuflen accordingly.
903 int get_eacontent(const struct vol * restrict vol,
904 char * restrict rbuf,
905 int * restrict rbuflen,
906 const char * restrict uname,
908 const char * restrict attruname,
911 int ret = AFPERR_MISC, count = 0, fd = -1;
916 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
918 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
919 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
923 while (count < ea.ea_count) {
924 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
925 if ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
930 /* Check how much the client wants, give him what we think is right */
931 maxreply -= MAX_REPLY_EXTRA_BYTES;
932 if (maxreply > MAX_EA_SIZE)
933 maxreply = MAX_EA_SIZE;
934 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
935 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
937 /* Put length of EA data in reply buffer */
938 uint32 = htonl(toread);
939 memcpy(rbuf, &uint32, 4);
943 if ((read(fd, rbuf, toread)) != toread) {
944 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
957 if ((ea_close(&ea)) != 0) {
958 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
969 * Purpose: copy names of EAs into attrnamebuf
973 * vol (r) current volume
974 * attrnamebuf (w) store names a consecutive C strings here
975 * buflen (rw) length of names in attrnamebuf
977 * oflag (r) link and create flag
979 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
983 * Copies names of all EAs of uname as consecutive C strings into rbuf.
984 * Increments *buflen accordingly.
986 int list_eas(const struct vol * restrict vol,
987 char * restrict attrnamebuf,
988 int * restrict buflen,
989 const char * restrict uname,
992 int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
993 char *buf = attrnamebuf;
996 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
998 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
999 if (errno != ENOENT) {
1000 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1007 while (count < ea.ea_count) {
1008 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1009 if ( ( len = convert_string(vol->v_volcharset,
1011 (*ea.ea_entries)[count].ea_name,
1012 (*ea.ea_entries)[count].ea_namelen,
1020 /* convert_string didn't 0-terminate */
1021 attrnamebuf[attrbuflen + 255] = 0;
1023 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1024 uname, (*ea.ea_entries)[count].ea_name);
1026 attrbuflen += len + 1;
1027 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1028 /* Next EA name could overflow, so bail out with error.
1029 FIXME: evantually malloc/memcpy/realloc whatever.
1031 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1039 *buflen = attrbuflen;
1041 if ((ea_close(&ea)) != 0) {
1042 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1052 * Purpose: set a Solaris native EA
1056 * vol (r) current volume
1057 * uname (r) filename
1058 * attruname (r) EA name
1059 * ibuf (r) buffer with EA content
1060 * attrsize (r) length EA in ibuf
1061 * oflag (r) link and create flag
1063 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1067 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1068 * Increments *rbuflen accordingly.
1070 int set_ea(const struct vol * restrict vol,
1071 const char * restrict uname,
1072 const char * restrict attruname,
1073 const char * restrict ibuf,
1080 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1082 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1083 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1087 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1088 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1093 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1094 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1100 if ((ea_close(&ea)) != 0) {
1101 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1110 * Function: remove_ea
1112 * Purpose: remove a EA from a file
1116 * vol (r) current volume
1117 * uname (r) filename
1118 * attruname (r) EA name
1119 * oflag (r) link and create flag
1121 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1125 * Removes EA attruname from file uname.
1127 int remove_ea(const struct vol * restrict vol,
1128 const char * restrict uname,
1129 const char * restrict attruname,
1135 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1137 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1138 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1142 if ((ea_delentry(&ea, attruname)) == -1) {
1143 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1148 if ((delete_ea_file(&ea, attruname)) != 0) {
1149 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1155 if ((ea_close(&ea)) != 0) {
1156 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1164 /**********************************************************************************
1165 * Solaris EA VFS funcs
1166 **********************************************************************************/
1169 * Function: sol_get_easize
1171 * Purpose: get size of an EA on Solaris native EA
1175 * vol (r) current volume
1176 * rbuf (w) DSI reply buffer
1177 * rbuflen (rw) current length of data in reply buffer
1178 * uname (r) filename
1179 * oflag (r) link and create flag
1180 * attruname (r) name of attribute
1182 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1186 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
1188 #ifdef HAVE_SOLARIS_EAS
1189 int sol_get_easize(const struct vol * restrict vol,
1190 char * restrict rbuf,
1191 int * restrict rbuflen,
1192 const char * restrict uname,
1194 cons char * restrict attruname)
1200 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
1202 if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
1203 if (errno == ELOOP) {
1204 /* its a symlink and client requested O_NOFOLLOW */
1205 LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
1212 LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
1216 if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
1217 LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
1221 attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
1223 /* Start building reply packet */
1225 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
1227 /* length of attribute data */
1228 attrsize = htonl(attrsize);
1229 memcpy(rbuf, &attrsize, 4);
1238 #endif /* HAVE_SOLARIS_EAS */
1241 * Function: sol_get_eacontent
1243 * Purpose: copy Solaris native EA into rbuf
1247 * vol (r) current volume
1248 * rbuf (w) DSI reply buffer
1249 * rbuflen (rw) current length of data in reply buffer
1250 * uname (r) filename
1251 * oflag (r) link and create flag
1252 * attruname (r) name of attribute
1253 * maxreply (r) maximum EA size as of current specs/real-life
1255 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1259 * Copies EA into rbuf. Increments *rbuflen accordingly.
1261 #ifdef HAVE_SOLARIS_EAS
1262 int sol_get_eacontent(const struct vol * restrict vol,
1263 char * restrict rbuf,
1264 int * restrict rbuflen,
1265 const char * restrict uname,
1267 char * restrict attruname,
1271 size_t toread, okread = 0, len;
1275 if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
1276 if (errno == ELOOP) {
1277 /* its a symlink and client requested O_NOFOLLOW */
1278 LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
1285 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
1289 if ( -1 == (fstat(attrdirfd, &st))) {
1290 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
1295 /* Start building reply packet */
1297 maxreply -= MAX_REPLY_EXTRA_BYTES;
1298 if (maxreply > MAX_EA_SIZE)
1299 maxreply = MAX_EA_SIZE;
1301 /* But never send more than the client requested */
1302 toread = (maxreply < st.st_size) ? maxreply : st.st_size;
1304 LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
1306 /* remember where we must store length of attribute data in rbuf */
1312 len = read(attrdirfd, rbuf, toread);
1314 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
1321 if ((len == 0) || (okread == toread))
1325 okread = htonl((uint32_t)okread);
1326 memcpy(datalength, &okread, 4);
1334 #endif /* HAVE_SOLARIS_EAS */
1337 * Function: sol_list_eas
1339 * Purpose: copy names of Solaris native EA into attrnamebuf
1343 * vol (r) current volume
1344 * attrnamebuf (w) store names a consecutive C strings here
1345 * buflen (rw) length of names in attrnamebuf
1346 * uname (r) filename
1347 * oflag (r) link and create flag
1349 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1353 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1354 * Increments *rbuflen accordingly.
1356 #ifdef HAVE_SOLARIS_EAS
1357 int sol_list_eas(const struct vol * restrict vol,
1358 char * restrict attrnamebuf,
1359 int * restrict buflen,
1360 const char * restrict uname,
1363 int ret, attrbuflen = *buflen, len, attrdirfd = 0;
1367 /* Now list file attribute dir */
1368 if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
1369 if (errno == ELOOP) {
1370 /* its a symlink and client requested O_NOFOLLOW */
1371 ret = AFPERR_BADTYPE;
1374 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1379 if (NULL == (dirp = fdopendir(attrdirfd))) {
1380 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1385 while ((dp = readdir(dirp))) {
1386 /* check if its "." or ".." */
1387 if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
1388 (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
1391 len = strlen(dp->d_name);
1393 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1394 if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
1399 /* convert_string didn't 0-terminate */
1400 attrnamebuf[attrbuflen + 255] = 0;
1402 LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
1404 attrbuflen += len + 1;
1405 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1406 /* Next EA name could overflow, so bail out with error.
1407 FIXME: evantually malloc/memcpy/realloc whatever.
1409 LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
1424 *buflen = attrbuflen;
1427 #endif /* HAVE_SOLARIS_EAS */
1430 * Function: sol_set_ea
1432 * Purpose: set a Solaris native EA
1436 * vol (r) current volume
1437 * uname (r) filename
1438 * attruname (r) EA name
1439 * ibuf (r) buffer with EA content
1440 * attrsize (r) length EA in ibuf
1441 * oflag (r) link and create flag
1443 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1447 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1448 * Increments *rbuflen accordingly.
1450 #ifdef HAVE_SOLARIS_EAS
1451 int sol_set_ea(const struct vol * restrict vol,
1452 const char * restrict u_name,
1453 const char * restrict attruname,
1454 const char * restrict ibuf,
1460 if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
1461 if (errno == ELOOP) {
1462 /* its a symlink and client requested O_NOFOLLOW */
1463 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1466 LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1470 if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
1471 LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
1477 #endif /* HAVE_SOLARIS_EAS */
1480 * Function: sol_remove_ea
1482 * Purpose: remove a Solaris native EA
1486 * vol (r) current volume
1487 * uname (r) filename
1488 * attruname (r) EA name
1489 * oflag (r) link and create flag
1491 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1495 * Removes EA attruname from file uname.
1497 #ifdef HAVE_SOLARIS_EAS
1498 int sol_remove_ea(const struct vol * restrict vol,
1499 const char * restrict uname,
1500 const char * restrict attruname,
1505 if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
1508 /* its a symlink and client requested O_NOFOLLOW */
1509 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1512 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1513 return AFPERR_ACCESS;
1515 LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1520 if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
1521 if (errno == EACCES) {
1522 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1523 return AFPERR_ACCESS;
1525 LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1530 #endif /* HAVE_SOLARIS_EAS */
1532 /******************************************************************************************
1533 * EA VFS funcs that deal with file/dir cp/mv/rm
1534 ******************************************************************************************/
1536 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1538 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1540 int count = 0, ret = AFP_OK;
1544 if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
1545 if (errno == ENOENT)
1546 /* no EA files, nothing to do */
1549 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1554 while (count < ea.ea_count) {
1555 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1559 free((*ea.ea_entries)[count].ea_name);
1560 (*ea.ea_entries)[count].ea_name = NULL;
1564 /* ea_close removes the EA header file for us because all names are NULL */
1565 if ((ea_close(&ea)) != 0) {
1566 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1573 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1578 char srceapath[ MAXPATHLEN + 1];
1585 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1589 if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1590 if (errno == ENOENT)
1591 /* no EA files, nothing to do */
1594 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1599 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1600 if (errno == ENOENT) {
1601 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1602 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1603 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1604 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1608 ad_close(&ad, ADFLAGS_HF);
1609 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1616 /* Loop through all EAs: */
1617 while (count < srcea.ea_count) {
1619 eaname = (*srcea.ea_entries)[count].ea_name;
1620 easize = (*srcea.ea_entries)[count].ea_size;
1622 /* Build src and dst paths for rename() */
1623 eapath = ea_path(&srcea, eaname);
1624 strcpy(srceapath, eapath);
1625 eapath = ea_path(&dstea, eaname);
1627 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1628 src, dst, srceapath, eapath);
1630 /* Add EA to dstea */
1631 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1632 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1633 src, dst, srceapath, eapath);
1638 /* Remove EA entry from srcea */
1639 if ((ea_delentry(&srcea, eaname)) == -1) {
1640 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1641 src, dst, srceapath, eapath);
1642 ea_delentry(&dstea, eaname);
1647 /* Now rename the EA */
1648 if ((rename( srceapath, eapath)) < 0) {
1649 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1650 src, dst, srceapath, eapath);