2 $Id: ea.c,v 1.1 2009-10-02 09:32:41 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 * uname (r) name of file
272 * attruname (r) name of EA
273 * attrsize (r) size of ea
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 uname,
284 const char * restrict attruname,
289 if (ea->ea_count == 0) {
290 ea->ea_entries = malloc(sizeof(struct ea_entry));
291 if ( ! ea->ea_entries) {
292 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
296 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
298 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
301 ea->ea_entries = tmprealloc;
304 /* We've grown the array, now store the entry */
305 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
306 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
307 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
308 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
311 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
317 if (ea->ea_count == 0 && ea->ea_entries) {
318 /* We just allocated storage but had an error somewhere -> free storage*/
319 free(ea->ea_entries);
320 ea->ea_entries = NULL;
327 * Function: ea_delentry
329 * Purpose: delete one EA from ea->ea_entries[]
333 * ea (rw) pointer to struct ea
334 * uname (r) name of EA
335 * attruname (r) size of ea
337 * Returns: new number of EA entries, -1 on error
341 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
342 * Marks it as unused just by freeing name and setting it to NULL.
343 * ea_close and pack_buffer must honor this.
345 static int ea_delentry(struct ea * restrict ea,
346 const char * restrict uname,
347 const char * restrict attruname)
349 int ret = 0, count = 0;
351 if (ea->ea_count == 0) {
355 while (count < ea->ea_count) {
356 /* search matching EA */
357 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
358 free((*ea->ea_entries)[count].ea_name);
359 (*ea->ea_entries)[count].ea_name = NULL;
361 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
362 attruname, count + 1, ea->ea_count);
373 * Function: create_ea_header
375 * Purpose: create EA header file, only called from ea_open
379 * uname (r) filename for which we have to create a header
380 * ea (rw) ea handle with already allocated storage pointed to
383 * Returns: fd of open header file on success, -1 on error, errno semantics:
384 * EEXIST: open with O_CREAT | O_EXCL failed
388 * Creates EA header file and initialize ea->ea_data buffer.
389 * Possibe race condition with other afpd processes:
390 * we were called because header file didn't exist in eg. ea_open. We then
391 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
392 * What do we do then? Someone else is in the process of creating the header too, but
393 * it might not have finished it. That means we cant just open, read and use it!
394 * We therefor currently just break with an error.
395 * On return the header file is still r/w locked.
397 static int create_ea_header(const char * restrict uname,
398 struct ea * restrict ea)
400 int fd = -1, err = 0;
403 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
404 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
409 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
410 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
417 *(uint32_t *)ptr = htonl(EA_MAGIC);
419 *(uint16_t *)ptr = htons(EA_VERSION);
420 ptr += EA_VERSION_LEN;
421 *(uint16_t *)ptr = 0; /* count */
423 ea->ea_size = EA_HEADER_SIZE;
436 * Purpose: write an EA to disk
440 * ea (r) struct ea handle
441 * attruname (r) EA name
442 * ibuf (r) buffer with EA content
443 * attrsize (r) size of EA
445 * Returns: 0 on success, -1 on error
449 * Creates/overwrites EA file.
452 static int write_ea(const struct ea * restrict ea,
453 const char * restrict attruname,
454 const char * restrict ibuf,
457 int fd = -1, ret = AFP_OK;
461 eaname = ea_path(ea, attruname);
462 LOG(log_maxdebug, logtype_afpd, "write_ea: ea_apth: %s", eaname);
464 /* Check if it exists, remove if yes*/
465 if ((stat(eaname, &st)) == 0) {
466 if ((unlink(eaname)) != 0) {
468 return AFPERR_ACCESS;
474 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
475 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
480 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
481 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
486 if ((write(fd, ibuf, attrsize)) != attrsize) {
487 LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
494 close(fd); /* and unlock */
499 * Function: delete_ea_file
501 * Purpose: delete EA file from disk
505 * ea (r) struct ea handle
506 * attruname (r) EA name
508 * Returns: 0 on success, -1 on error
510 static int delete_ea_file(const struct ea * restrict ea,
517 eafile = ea_path(ea, eaname);
519 /* Check if it exists, remove if yes*/
520 if ((stat(eafile, &st)) == 0) {
521 if ((unlink(eafile)) != 0) {
522 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
523 eafile, strerror(errno));
526 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
535 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
539 * vol (r) current volume
540 * uname (r) filename for which we have to open a header
541 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
542 * EA_RDONLY: open read only
543 * EA_RDWR: open read/write
544 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
545 * ea (w) pointer to a struct ea that we fill
547 * Returns: 0 on success, -1 on error
551 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
552 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
553 * file is either read or write locked depending on the open flags.
554 * When you're done with struct ea you must call ea_close on it.
556 static int ea_open(const struct vol * restrict vol,
557 const char * restrict uname,
559 struct ea * restrict ea)
565 /* Enforce usage rules! */
566 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
567 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
571 if ((stat(uname, &st)) != 0) {
572 LOG(log_debug, logtype_afpd, "ea_open: cant stat: %s", uname);
576 /* set it all to 0 */
577 memset(ea, 0, sizeof(struct ea));
579 ea->vol = vol; /* ea_close needs it */
581 ea->ea_flags = eaflags;
582 if (S_ISDIR(st.st_mode))
583 ea->ea_flags |= EA_DIR;
585 if ( ! (ea->filename = strdup(uname))) {
586 LOG(log_error, logtype_afpd, "ea_open: OOM");
590 eaname = ea_path(ea, NULL);
591 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
593 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
594 if ((stat(eaname, &st)) != 0) {
595 if (errno == ENOENT) {
597 /* It doesnt exist */
599 if ( ! (eaflags & EA_CREATE)) {
600 /* creation was not requested, so return with error */
605 /* Now create a header file */
607 /* malloc buffer for minimal on disk data */
608 ea->ea_data = malloc(EA_HEADER_SIZE);
610 LOG(log_error, logtype_afpd, "ea_open: OOM");
616 ea->ea_fd = create_ea_header(eaname, ea);
617 if (ea->ea_fd == -1) {
624 } else {/* errno != ENOENT */
630 /* header file exists, so read and parse it */
632 /* malloc buffer where we read disk file into */
633 ea->ea_size = st.st_size;
634 ea->ea_data = malloc(st.st_size);
636 LOG(log_error, logtype_afpd, "ea_open: OOM");
641 /* Now lock, open and read header file from disk */
642 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
643 LOG(log_error, logtype_afpd, "ea_open: error on open for header: %s", eaname);
649 if (ea->ea_flags & EA_RDONLY) {
651 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
652 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
656 } else { /* EA_RDWR */
658 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
659 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
666 if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
667 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
672 if ((unpack_header(ea)) != 0) {
673 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
695 * Purpose: flushes and closes an ea handle
699 * ea (rw) pointer to ea handle
701 * Returns: 0 on success, -1 on error
705 * Flushes and then closes and frees all resouces held by ea handle.
706 * Pack data in ea into ea_data, then write ea_data to disk
708 static int ea_close(struct ea * restrict ea)
710 int ret = 0, count = 0;
714 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
716 /* pack header and write it to disk if it was opened EA_RDWR*/
717 if (ea->ea_flags & EA_RDWR) {
718 if ((pack_header(ea)) != 0) {
719 LOG(log_error, logtype_afpd, "ea_close: pack header");
722 if (ea->ea_count == 0) {
723 /* Check if EA header exists and remove it */
724 eaname = ea_path(ea, NULL);
725 if ((stat(eaname, &st)) == 0) {
726 if ((unlink(eaname)) != 0) {
727 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
728 eaname, strerror(errno));
732 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
735 if (errno != ENOENT) {
736 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
737 eaname, strerror(errno));
741 } else { /* ea->ea_count > 0 */
742 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
743 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
748 if ((ftruncate(ea->ea_fd, 0)) == -1) {
749 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
754 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
755 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
764 while(count < ea->ea_count) {
765 if ( (*ea->ea_entries)[count].ea_name ) {
766 free((*ea->ea_entries)[count].ea_name);
767 (*ea->ea_entries)[count].ea_name = NULL;
778 if (ea->ea_entries) {
779 free(ea->ea_entries);
780 ea->ea_entries = NULL;
787 if (ea->ea_fd != -1) {
788 close(ea->ea_fd); /* also releases the fcntl lock */
797 /************************************************************************************
798 * VFS funcs called from afp_ea* funcs
799 ************************************************************************************/
802 * Function: get_easize
804 * Purpose: get size of an EA
808 * vol (r) current volume
809 * rbuf (w) DSI reply buffer
810 * rbuflen (rw) current length of data in reply buffer
812 * oflag (r) link and create flag
813 * attruname (r) name of attribute
815 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
819 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
821 int get_easize(const struct vol * restrict vol,
822 char * restrict rbuf,
823 int * restrict rbuflen,
824 const char * restrict uname,
826 const char * restrict attruname)
828 int ret = AFPERR_MISC, count = 0;
832 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
834 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
835 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
839 while (count < ea.ea_count) {
840 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
841 uint32 = htonl((*ea.ea_entries)[count].ea_size);
842 memcpy(rbuf, &uint32, 4);
846 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
847 attruname, (*ea.ea_entries)[count].ea_size);
853 if ((ea_close(&ea)) != 0) {
854 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
862 * Function: get_eacontent
864 * Purpose: copy EA into rbuf
868 * vol (r) current volume
869 * rbuf (w) DSI reply buffer
870 * rbuflen (rw) current length of data in reply buffer
872 * oflag (r) link and create flag
873 * attruname (r) name of attribute
874 * maxreply (r) maximum EA size as of current specs/real-life
876 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
880 * Copies EA into rbuf. Increments *rbuflen accordingly.
882 int get_eacontent(const struct vol * restrict vol,
883 char * restrict rbuf,
884 int * restrict rbuflen,
885 const char * restrict uname,
887 const char * restrict attruname,
890 int ret = AFPERR_MISC, count = 0, fd = -1;
895 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
897 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
898 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
902 while (count < ea.ea_count) {
903 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
904 if ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
909 /* Check how much the client wants, give him what we think is right */
910 maxreply -= MAX_REPLY_EXTRA_BYTES;
911 if (maxreply > MAX_EA_SIZE)
912 maxreply = MAX_EA_SIZE;
913 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
914 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
916 /* Put length of EA data in reply buffer */
917 uint32 = htonl(toread);
918 memcpy(rbuf, &uint32, 4);
922 if ((read(fd, rbuf, toread)) != toread) {
923 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
936 if ((ea_close(&ea)) != 0) {
937 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
948 * Purpose: copy names of EAs into attrnamebuf
952 * vol (r) current volume
953 * attrnamebuf (w) store names a consecutive C strings here
954 * buflen (rw) length of names in attrnamebuf
956 * oflag (r) link and create flag
958 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
962 * Copies names of all EAs of uname as consecutive C strings into rbuf.
963 * Increments *buflen accordingly.
965 int list_eas(const struct vol * restrict vol,
966 char * restrict attrnamebuf,
967 int * restrict buflen,
968 const char * restrict uname,
971 int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
972 char *buf = attrnamebuf;
975 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
977 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
978 if (errno != ENOENT) {
979 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
986 while (count < ea.ea_count) {
987 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
988 if ( ( len = convert_string(vol->v_volcharset,
990 (*ea.ea_entries)[count].ea_name,
991 (*ea.ea_entries)[count].ea_namelen,
999 /* convert_string didn't 0-terminate */
1000 attrnamebuf[attrbuflen + 255] = 0;
1002 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1003 uname, (*ea.ea_entries)[count].ea_name);
1005 attrbuflen += len + 1;
1006 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1007 /* Next EA name could overflow, so bail out with error.
1008 FIXME: evantually malloc/memcpy/realloc whatever.
1010 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1018 *buflen += attrbuflen;
1020 if ((ea_close(&ea)) != 0) {
1021 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1031 * Purpose: set a Solaris native EA
1035 * vol (r) current volume
1036 * uname (r) filename
1037 * attruname (r) EA name
1038 * ibuf (r) buffer with EA content
1039 * attrsize (r) length EA in ibuf
1040 * oflag (r) link and create flag
1042 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1046 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1047 * Increments *rbuflen accordingly.
1049 int set_ea(const struct vol * restrict vol,
1050 const char * restrict uname,
1051 const char * restrict attruname,
1052 const char * restrict ibuf,
1059 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1061 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1062 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1066 if ((ea_addentry(&ea, uname, attruname, attrsize)) == -1) {
1067 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1072 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1073 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1079 if ((ea_close(&ea)) != 0) {
1080 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1089 * Function: remove_ea
1091 * Purpose: remove a EA from a file
1095 * vol (r) current volume
1096 * uname (r) filename
1097 * attruname (r) EA name
1098 * oflag (r) link and create flag
1100 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1104 * Removes EA attruname from file uname.
1106 int remove_ea(const struct vol * restrict vol,
1107 const char * restrict uname,
1108 const char * restrict attruname,
1114 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1116 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1117 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1121 if ((ea_delentry(&ea, uname, attruname)) == -1) {
1122 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1127 if ((delete_ea_file(&ea, attruname)) != 0) {
1128 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1134 if ((ea_close(&ea)) != 0) {
1135 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1143 /**********************************************************************************
1144 * Solaris EA VFS funcs
1145 **********************************************************************************/
1148 * Function: sol_get_easize
1150 * Purpose: get size of an EA on Solaris native EA
1154 * vol (r) current volume
1155 * rbuf (w) DSI reply buffer
1156 * rbuflen (rw) current length of data in reply buffer
1157 * uname (r) filename
1158 * oflag (r) link and create flag
1159 * attruname (r) name of attribute
1161 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1165 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
1167 #ifdef HAVE_SOLARIS_EAS
1168 int sol_get_easize(const struct vol * restrict vol,
1169 char * restrict rbuf,
1170 int * restrict rbuflen,
1171 const char * restrict uname,
1173 cons char * restrict attruname)
1179 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
1181 if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
1182 if (errno == ELOOP) {
1183 /* its a symlink and client requested O_NOFOLLOW */
1184 LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
1191 LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
1195 if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
1196 LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
1200 attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
1202 /* Start building reply packet */
1204 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
1206 /* length of attribute data */
1207 attrsize = htonl(attrsize);
1208 memcpy(rbuf, &attrsize, 4);
1217 #endif /* HAVE_SOLARIS_EAS */
1220 * Function: sol_get_eacontent
1222 * Purpose: copy Solaris native EA into rbuf
1226 * vol (r) current volume
1227 * rbuf (w) DSI reply buffer
1228 * rbuflen (rw) current length of data in reply buffer
1229 * uname (r) filename
1230 * oflag (r) link and create flag
1231 * attruname (r) name of attribute
1232 * maxreply (r) maximum EA size as of current specs/real-life
1234 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1238 * Copies EA into rbuf. Increments *rbuflen accordingly.
1240 #ifdef HAVE_SOLARIS_EAS
1241 int sol_get_eacontent(const struct vol * restrict vol,
1242 char * restrict rbuf,
1243 int * restrict rbuflen,
1244 const char * restrict uname,
1246 char * restrict attruname,
1250 size_t toread, okread = 0, len;
1254 if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
1255 if (errno == ELOOP) {
1256 /* its a symlink and client requested O_NOFOLLOW */
1257 LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
1264 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
1268 if ( -1 == (fstat(attrdirfd, &st))) {
1269 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
1274 /* Start building reply packet */
1276 maxreply -= MAX_REPLY_EXTRA_BYTES;
1277 if (maxreply > MAX_EA_SIZE)
1278 maxreply = MAX_EA_SIZE;
1280 /* But never send more than the client requested */
1281 toread = (maxreply < st.st_size) ? maxreply : st.st_size;
1283 LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
1285 /* remember where we must store length of attribute data in rbuf */
1291 len = read(attrdirfd, rbuf, toread);
1293 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
1300 if ((len == 0) || (okread == toread))
1304 okread = htonl((uint32_t)okread);
1305 memcpy(datalength, &okread, 4);
1313 #endif /* HAVE_SOLARIS_EAS */
1316 * Function: sol_list_eas
1318 * Purpose: copy names of Solaris native EA into attrnamebuf
1322 * vol (r) current volume
1323 * attrnamebuf (w) store names a consecutive C strings here
1324 * buflen (rw) length of names in attrnamebuf
1325 * uname (r) filename
1326 * oflag (r) link and create flag
1328 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1332 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1333 * Increments *rbuflen accordingly.
1335 #ifdef HAVE_SOLARIS_EAS
1336 int sol_list_eas(const struct vol * restrict vol,
1337 char * restrict attrnamebuf,
1338 int * restrict buflen,
1339 const char * restrict uname,
1342 int ret, attrbuflen = *buflen, len, attrdirfd = 0;
1346 /* Now list file attribute dir */
1347 if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
1348 if (errno == ELOOP) {
1349 /* its a symlink and client requested O_NOFOLLOW */
1350 ret = AFPERR_BADTYPE;
1353 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1358 if (NULL == (dirp = fdopendir(attrdirfd))) {
1359 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1364 while ((dp = readdir(dirp))) {
1365 /* check if its "." or ".." */
1366 if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
1367 (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
1370 len = strlen(dp->d_name);
1372 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1373 if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
1378 /* convert_string didn't 0-terminate */
1379 attrnamebuf[attrbuflen + 255] = 0;
1381 LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
1383 attrbuflen += len + 1;
1384 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1385 /* Next EA name could overflow, so bail out with error.
1386 FIXME: evantually malloc/memcpy/realloc whatever.
1388 LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
1403 *buflen = attrbuflen;
1406 #endif /* HAVE_SOLARIS_EAS */
1409 * Function: sol_set_ea
1411 * Purpose: set a Solaris native EA
1415 * vol (r) current volume
1416 * uname (r) filename
1417 * attruname (r) EA name
1418 * ibuf (r) buffer with EA content
1419 * attrsize (r) length EA in ibuf
1420 * oflag (r) link and create flag
1422 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1426 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1427 * Increments *rbuflen accordingly.
1429 #ifdef HAVE_SOLARIS_EAS
1430 int sol_set_ea(const struct vol * restrict vol,
1431 const char * restrict u_name,
1432 const char * restrict attruname,
1433 const char * restrict ibuf,
1439 if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
1440 if (errno == ELOOP) {
1441 /* its a symlink and client requested O_NOFOLLOW */
1442 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1445 LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1449 if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
1450 LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
1456 #endif /* HAVE_SOLARIS_EAS */
1459 * Function: sol_remove_ea
1461 * Purpose: remove a Solaris native EA
1465 * vol (r) current volume
1466 * uname (r) filename
1467 * attruname (r) EA name
1468 * oflag (r) link and create flag
1470 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1474 * Removes EA attruname from file uname.
1476 #ifdef HAVE_SOLARIS_EAS
1477 int sol_remove_ea(const struct vol * restrict vol,
1478 const char * restrict uname,
1479 const char * restrict attruname,
1484 if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
1487 /* its a symlink and client requested O_NOFOLLOW */
1488 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1491 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1492 return AFPERR_ACCESS;
1494 LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1499 if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
1500 if (errno == EACCES) {
1501 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1502 return AFPERR_ACCESS;
1504 LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1509 #endif /* HAVE_SOLARIS_EAS */