2 $Id: ea.c,v 1.2 2009-10-02 14:57:57 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
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 uname,
285 const char * restrict attruname,
292 /* First check if an EA of the requested name already exist */
293 if (ea->ea_count > 0) {
294 while (count < ea->ea_count) {
295 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
296 LOG(log_debug, logtype_afpd, "ea_addentry('%s'): exists", attruname);
297 if (bitmap & kXAttrCreate)
298 /* its like O_CREAT|O_EXCL -> fail */
300 if ( ! (bitmap & kXAttrReplace))
301 /* replace was not requested, then its an error */
309 if (ea->ea_count == 0) {
310 ea->ea_entries = malloc(sizeof(struct ea_entry));
311 if ( ! ea->ea_entries) {
312 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
316 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
318 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
321 ea->ea_entries = tmprealloc;
324 /* We've grown the array, now store the entry */
325 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
326 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
327 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
328 LOG(log_error, logtype_afpd, "ea_addentry: OOM");
331 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
337 if (ea->ea_count == 0 && ea->ea_entries) {
338 /* We just allocated storage but had an error somewhere -> free storage*/
339 free(ea->ea_entries);
340 ea->ea_entries = NULL;
347 * Function: ea_delentry
349 * Purpose: delete one EA from ea->ea_entries[]
353 * ea (rw) pointer to struct ea
354 * uname (r) name of EA
355 * attruname (r) size of ea
357 * Returns: new number of EA entries, -1 on error
361 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
362 * Marks it as unused just by freeing name and setting it to NULL.
363 * ea_close and pack_buffer must honor this.
365 static int ea_delentry(struct ea * restrict ea,
366 const char * restrict uname,
367 const char * restrict attruname)
369 int ret = 0, count = 0;
371 if (ea->ea_count == 0) {
375 while (count < ea->ea_count) {
376 /* search matching EA */
377 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
378 free((*ea->ea_entries)[count].ea_name);
379 (*ea->ea_entries)[count].ea_name = NULL;
381 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
382 attruname, count + 1, ea->ea_count);
393 * Function: create_ea_header
395 * Purpose: create EA header file, only called from ea_open
399 * uname (r) filename for which we have to create a header
400 * ea (rw) ea handle with already allocated storage pointed to
403 * Returns: fd of open header file on success, -1 on error, errno semantics:
404 * EEXIST: open with O_CREAT | O_EXCL failed
408 * Creates EA header file and initialize ea->ea_data buffer.
409 * Possibe race condition with other afpd processes:
410 * we were called because header file didn't exist in eg. ea_open. We then
411 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
412 * What do we do then? Someone else is in the process of creating the header too, but
413 * it might not have finished it. That means we cant just open, read and use it!
414 * We therefor currently just break with an error.
415 * On return the header file is still r/w locked.
417 static int create_ea_header(const char * restrict uname,
418 struct ea * restrict ea)
420 int fd = -1, err = 0;
423 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
424 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
429 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
430 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
437 *(uint32_t *)ptr = htonl(EA_MAGIC);
439 *(uint16_t *)ptr = htons(EA_VERSION);
440 ptr += EA_VERSION_LEN;
441 *(uint16_t *)ptr = 0; /* count */
443 ea->ea_size = EA_HEADER_SIZE;
456 * Purpose: write an EA to disk
460 * ea (r) struct ea handle
461 * attruname (r) EA name
462 * ibuf (r) buffer with EA content
463 * attrsize (r) size of EA
465 * Returns: 0 on success, -1 on error
469 * Creates/overwrites EA file.
472 static int write_ea(const struct ea * restrict ea,
473 const char * restrict attruname,
474 const char * restrict ibuf,
477 int fd = -1, ret = AFP_OK;
481 eaname = ea_path(ea, attruname);
482 LOG(log_maxdebug, logtype_afpd, "write_ea: ea_apth: %s", eaname);
484 /* Check if it exists, remove if yes*/
485 if ((stat(eaname, &st)) == 0) {
486 if ((unlink(eaname)) != 0) {
488 return AFPERR_ACCESS;
494 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
495 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
500 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
501 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
506 if ((write(fd, ibuf, attrsize)) != attrsize) {
507 LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
514 close(fd); /* and unlock */
519 * Function: delete_ea_file
521 * Purpose: delete EA file from disk
525 * ea (r) struct ea handle
526 * attruname (r) EA name
528 * Returns: 0 on success, -1 on error
530 static int delete_ea_file(const struct ea * restrict ea,
537 eafile = ea_path(ea, eaname);
539 /* Check if it exists, remove if yes*/
540 if ((stat(eafile, &st)) == 0) {
541 if ((unlink(eafile)) != 0) {
542 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
543 eafile, strerror(errno));
546 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
555 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
559 * vol (r) current volume
560 * uname (r) filename for which we have to open a header
561 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created)
562 * EA_RDONLY: open read only
563 * EA_RDWR: open read/write
564 * Eiterh EA_RDONLY or EA_RDWR MUST be requested
565 * ea (w) pointer to a struct ea that we fill
567 * Returns: 0 on success, -1 on error
571 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
572 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
573 * file is either read or write locked depending on the open flags.
574 * When you're done with struct ea you must call ea_close on it.
576 static int ea_open(const struct vol * restrict vol,
577 const char * restrict uname,
579 struct ea * restrict ea)
585 /* Enforce usage rules! */
586 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
587 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
591 if ((stat(uname, &st)) != 0) {
592 LOG(log_debug, logtype_afpd, "ea_open: cant stat: %s", uname);
596 /* set it all to 0 */
597 memset(ea, 0, sizeof(struct ea));
599 ea->vol = vol; /* ea_close needs it */
601 ea->ea_flags = eaflags;
602 if (S_ISDIR(st.st_mode))
603 ea->ea_flags |= EA_DIR;
605 if ( ! (ea->filename = strdup(uname))) {
606 LOG(log_error, logtype_afpd, "ea_open: OOM");
610 eaname = ea_path(ea, NULL);
611 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
613 /* Check if it exists, if not create it if EA_CREATE is in eaflags */
614 if ((stat(eaname, &st)) != 0) {
615 if (errno == ENOENT) {
617 /* It doesnt exist */
619 if ( ! (eaflags & EA_CREATE)) {
620 /* creation was not requested, so return with error */
625 /* Now create a header file */
627 /* malloc buffer for minimal on disk data */
628 ea->ea_data = malloc(EA_HEADER_SIZE);
630 LOG(log_error, logtype_afpd, "ea_open: OOM");
636 ea->ea_fd = create_ea_header(eaname, ea);
637 if (ea->ea_fd == -1) {
644 } else {/* errno != ENOENT */
650 /* header file exists, so read and parse it */
652 /* malloc buffer where we read disk file into */
653 ea->ea_size = st.st_size;
654 ea->ea_data = malloc(st.st_size);
656 LOG(log_error, logtype_afpd, "ea_open: OOM");
661 /* Now lock, open and read header file from disk */
662 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
663 LOG(log_error, logtype_afpd, "ea_open: error on open for header: %s", eaname);
669 if (ea->ea_flags & EA_RDONLY) {
671 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
672 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
676 } else { /* EA_RDWR */
678 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
679 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname);
686 if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
687 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
692 if ((unpack_header(ea)) != 0) {
693 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
715 * Purpose: flushes and closes an ea handle
719 * ea (rw) pointer to ea handle
721 * Returns: 0 on success, -1 on error
725 * Flushes and then closes and frees all resouces held by ea handle.
726 * Pack data in ea into ea_data, then write ea_data to disk
728 static int ea_close(struct ea * restrict ea)
730 int ret = 0, count = 0;
734 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
736 /* pack header and write it to disk if it was opened EA_RDWR*/
737 if (ea->ea_flags & EA_RDWR) {
738 if ((pack_header(ea)) != 0) {
739 LOG(log_error, logtype_afpd, "ea_close: pack header");
742 if (ea->ea_count == 0) {
743 /* Check if EA header exists and remove it */
744 eaname = ea_path(ea, NULL);
745 if ((stat(eaname, &st)) == 0) {
746 if ((unlink(eaname)) != 0) {
747 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
748 eaname, strerror(errno));
752 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
755 if (errno != ENOENT) {
756 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
757 eaname, strerror(errno));
761 } else { /* ea->ea_count > 0 */
762 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
763 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
768 if ((ftruncate(ea->ea_fd, 0)) == -1) {
769 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
774 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
775 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
784 while(count < ea->ea_count) {
785 if ( (*ea->ea_entries)[count].ea_name ) {
786 free((*ea->ea_entries)[count].ea_name);
787 (*ea->ea_entries)[count].ea_name = NULL;
798 if (ea->ea_entries) {
799 free(ea->ea_entries);
800 ea->ea_entries = NULL;
807 if (ea->ea_fd != -1) {
808 close(ea->ea_fd); /* also releases the fcntl lock */
817 /************************************************************************************
818 * VFS funcs called from afp_ea* funcs
819 ************************************************************************************/
822 * Function: get_easize
824 * Purpose: get size of an EA
828 * vol (r) current volume
829 * rbuf (w) DSI reply buffer
830 * rbuflen (rw) current length of data in reply buffer
832 * oflag (r) link and create flag
833 * attruname (r) name of attribute
835 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
839 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
841 int get_easize(const struct vol * restrict vol,
842 char * restrict rbuf,
843 int * restrict rbuflen,
844 const char * restrict uname,
846 const char * restrict attruname)
848 int ret = AFPERR_MISC, count = 0;
852 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
854 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
855 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
859 while (count < ea.ea_count) {
860 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
861 uint32 = htonl((*ea.ea_entries)[count].ea_size);
862 memcpy(rbuf, &uint32, 4);
866 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
867 attruname, (*ea.ea_entries)[count].ea_size);
873 if ((ea_close(&ea)) != 0) {
874 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
882 * Function: get_eacontent
884 * Purpose: copy EA into rbuf
888 * vol (r) current volume
889 * rbuf (w) DSI reply buffer
890 * rbuflen (rw) current length of data in reply buffer
892 * oflag (r) link and create flag
893 * attruname (r) name of attribute
894 * maxreply (r) maximum EA size as of current specs/real-life
896 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
900 * Copies EA into rbuf. Increments *rbuflen accordingly.
902 int get_eacontent(const struct vol * restrict vol,
903 char * restrict rbuf,
904 int * restrict rbuflen,
905 const char * restrict uname,
907 const char * restrict attruname,
910 int ret = AFPERR_MISC, count = 0, fd = -1;
915 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
917 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
918 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
922 while (count < ea.ea_count) {
923 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
924 if ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
929 /* Check how much the client wants, give him what we think is right */
930 maxreply -= MAX_REPLY_EXTRA_BYTES;
931 if (maxreply > MAX_EA_SIZE)
932 maxreply = MAX_EA_SIZE;
933 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
934 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
936 /* Put length of EA data in reply buffer */
937 uint32 = htonl(toread);
938 memcpy(rbuf, &uint32, 4);
942 if ((read(fd, rbuf, toread)) != toread) {
943 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
956 if ((ea_close(&ea)) != 0) {
957 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
968 * Purpose: copy names of EAs into attrnamebuf
972 * vol (r) current volume
973 * attrnamebuf (w) store names a consecutive C strings here
974 * buflen (rw) length of names in attrnamebuf
976 * oflag (r) link and create flag
978 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
982 * Copies names of all EAs of uname as consecutive C strings into rbuf.
983 * Increments *buflen accordingly.
985 int list_eas(const struct vol * restrict vol,
986 char * restrict attrnamebuf,
987 int * restrict buflen,
988 const char * restrict uname,
991 int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
992 char *buf = attrnamebuf;
995 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
997 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
998 if (errno != ENOENT) {
999 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1006 while (count < ea.ea_count) {
1007 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1008 if ( ( len = convert_string(vol->v_volcharset,
1010 (*ea.ea_entries)[count].ea_name,
1011 (*ea.ea_entries)[count].ea_namelen,
1019 /* convert_string didn't 0-terminate */
1020 attrnamebuf[attrbuflen + 255] = 0;
1022 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1023 uname, (*ea.ea_entries)[count].ea_name);
1025 attrbuflen += len + 1;
1026 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1027 /* Next EA name could overflow, so bail out with error.
1028 FIXME: evantually malloc/memcpy/realloc whatever.
1030 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1038 *buflen = attrbuflen;
1040 if ((ea_close(&ea)) != 0) {
1041 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1051 * Purpose: set a Solaris native EA
1055 * vol (r) current volume
1056 * uname (r) filename
1057 * attruname (r) EA name
1058 * ibuf (r) buffer with EA content
1059 * attrsize (r) length EA in ibuf
1060 * oflag (r) link and create flag
1062 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1066 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1067 * Increments *rbuflen accordingly.
1069 int set_ea(const struct vol * restrict vol,
1070 const char * restrict uname,
1071 const char * restrict attruname,
1072 const char * restrict ibuf,
1079 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1081 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1082 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1086 if ((ea_addentry(&ea, uname, attruname, attrsize, oflag)) == -1) {
1087 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1092 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1093 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1099 if ((ea_close(&ea)) != 0) {
1100 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1109 * Function: remove_ea
1111 * Purpose: remove a EA from a file
1115 * vol (r) current volume
1116 * uname (r) filename
1117 * attruname (r) EA name
1118 * oflag (r) link and create flag
1120 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1124 * Removes EA attruname from file uname.
1126 int remove_ea(const struct vol * restrict vol,
1127 const char * restrict uname,
1128 const char * restrict attruname,
1134 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1136 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1137 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1141 if ((ea_delentry(&ea, uname, attruname)) == -1) {
1142 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1147 if ((delete_ea_file(&ea, attruname)) != 0) {
1148 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1154 if ((ea_close(&ea)) != 0) {
1155 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1163 /**********************************************************************************
1164 * Solaris EA VFS funcs
1165 **********************************************************************************/
1168 * Function: sol_get_easize
1170 * Purpose: get size of an EA on Solaris native EA
1174 * vol (r) current volume
1175 * rbuf (w) DSI reply buffer
1176 * rbuflen (rw) current length of data in reply buffer
1177 * uname (r) filename
1178 * oflag (r) link and create flag
1179 * attruname (r) name of attribute
1181 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1185 * Copies EA size into rbuf in network order. Increments *rbuflen +4.
1187 #ifdef HAVE_SOLARIS_EAS
1188 int sol_get_easize(const struct vol * restrict vol,
1189 char * restrict rbuf,
1190 int * restrict rbuflen,
1191 const char * restrict uname,
1193 cons char * restrict attruname)
1199 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
1201 if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
1202 if (errno == ELOOP) {
1203 /* its a symlink and client requested O_NOFOLLOW */
1204 LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
1211 LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
1215 if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
1216 LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
1220 attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
1222 /* Start building reply packet */
1224 LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
1226 /* length of attribute data */
1227 attrsize = htonl(attrsize);
1228 memcpy(rbuf, &attrsize, 4);
1237 #endif /* HAVE_SOLARIS_EAS */
1240 * Function: sol_get_eacontent
1242 * Purpose: copy Solaris native EA into rbuf
1246 * vol (r) current volume
1247 * rbuf (w) DSI reply buffer
1248 * rbuflen (rw) current length of data in reply buffer
1249 * uname (r) filename
1250 * oflag (r) link and create flag
1251 * attruname (r) name of attribute
1252 * maxreply (r) maximum EA size as of current specs/real-life
1254 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1258 * Copies EA into rbuf. Increments *rbuflen accordingly.
1260 #ifdef HAVE_SOLARIS_EAS
1261 int sol_get_eacontent(const struct vol * restrict vol,
1262 char * restrict rbuf,
1263 int * restrict rbuflen,
1264 const char * restrict uname,
1266 char * restrict attruname,
1270 size_t toread, okread = 0, len;
1274 if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
1275 if (errno == ELOOP) {
1276 /* its a symlink and client requested O_NOFOLLOW */
1277 LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
1284 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
1288 if ( -1 == (fstat(attrdirfd, &st))) {
1289 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
1294 /* Start building reply packet */
1296 maxreply -= MAX_REPLY_EXTRA_BYTES;
1297 if (maxreply > MAX_EA_SIZE)
1298 maxreply = MAX_EA_SIZE;
1300 /* But never send more than the client requested */
1301 toread = (maxreply < st.st_size) ? maxreply : st.st_size;
1303 LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
1305 /* remember where we must store length of attribute data in rbuf */
1311 len = read(attrdirfd, rbuf, toread);
1313 LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
1320 if ((len == 0) || (okread == toread))
1324 okread = htonl((uint32_t)okread);
1325 memcpy(datalength, &okread, 4);
1333 #endif /* HAVE_SOLARIS_EAS */
1336 * Function: sol_list_eas
1338 * Purpose: copy names of Solaris native EA into attrnamebuf
1342 * vol (r) current volume
1343 * attrnamebuf (w) store names a consecutive C strings here
1344 * buflen (rw) length of names in attrnamebuf
1345 * uname (r) filename
1346 * oflag (r) link and create flag
1348 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1352 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1353 * Increments *rbuflen accordingly.
1355 #ifdef HAVE_SOLARIS_EAS
1356 int sol_list_eas(const struct vol * restrict vol,
1357 char * restrict attrnamebuf,
1358 int * restrict buflen,
1359 const char * restrict uname,
1362 int ret, attrbuflen = *buflen, len, attrdirfd = 0;
1366 /* Now list file attribute dir */
1367 if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
1368 if (errno == ELOOP) {
1369 /* its a symlink and client requested O_NOFOLLOW */
1370 ret = AFPERR_BADTYPE;
1373 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1378 if (NULL == (dirp = fdopendir(attrdirfd))) {
1379 LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1384 while ((dp = readdir(dirp))) {
1385 /* check if its "." or ".." */
1386 if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
1387 (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
1390 len = strlen(dp->d_name);
1392 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1393 if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
1398 /* convert_string didn't 0-terminate */
1399 attrnamebuf[attrbuflen + 255] = 0;
1401 LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
1403 attrbuflen += len + 1;
1404 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1405 /* Next EA name could overflow, so bail out with error.
1406 FIXME: evantually malloc/memcpy/realloc whatever.
1408 LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
1423 *buflen = attrbuflen;
1426 #endif /* HAVE_SOLARIS_EAS */
1429 * Function: sol_set_ea
1431 * Purpose: set a Solaris native EA
1435 * vol (r) current volume
1436 * uname (r) filename
1437 * attruname (r) EA name
1438 * ibuf (r) buffer with EA content
1439 * attrsize (r) length EA in ibuf
1440 * oflag (r) link and create flag
1442 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1446 * Copies names of all EAs of uname as consecutive C strings into rbuf.
1447 * Increments *rbuflen accordingly.
1449 #ifdef HAVE_SOLARIS_EAS
1450 int sol_set_ea(const struct vol * restrict vol,
1451 const char * restrict u_name,
1452 const char * restrict attruname,
1453 const char * restrict ibuf,
1459 if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
1460 if (errno == ELOOP) {
1461 /* its a symlink and client requested O_NOFOLLOW */
1462 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1465 LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1469 if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
1470 LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
1476 #endif /* HAVE_SOLARIS_EAS */
1479 * Function: sol_remove_ea
1481 * Purpose: remove a Solaris native EA
1485 * vol (r) current volume
1486 * uname (r) filename
1487 * attruname (r) EA name
1488 * oflag (r) link and create flag
1490 * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1494 * Removes EA attruname from file uname.
1496 #ifdef HAVE_SOLARIS_EAS
1497 int sol_remove_ea(const struct vol * restrict vol,
1498 const char * restrict uname,
1499 const char * restrict attruname,
1504 if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
1507 /* its a symlink and client requested O_NOFOLLOW */
1508 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1511 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1512 return AFPERR_ACCESS;
1514 LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1519 if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
1520 if (errno == EACCES) {
1521 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1522 return AFPERR_ACCESS;
1524 LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1529 #endif /* HAVE_SOLARIS_EAS */