X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=libatalk%2Fvfs%2Fea.c;h=f8e85acf0fe656707df16e9ec1d47334305646f2;hp=a0366cff18c7760ac758aece0e7ea0e242ea2296;hb=a3e43549642d11f4bc86324f9a09c294f6a3f74b;hpb=9fbd12bc7b6a8acd8da00d864aadb5ad9e9d07f1 diff --git a/libatalk/vfs/ea.c b/libatalk/vfs/ea.c index a0366cff..f8e85acf 100644 --- a/libatalk/vfs/ea.c +++ b/libatalk/vfs/ea.c @@ -1,5 +1,5 @@ /* - $Id: ea.c,v 1.8 2009-10-21 17:41:45 franklahm Exp $ + $Id: ea.c,v 1.20 2010-03-12 15:16:49 franklahm Exp $ Copyright (c) 2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -13,11 +13,6 @@ GNU General Public License for more details. */ -/* According to man fsattr.5 we must define _ATFILE_SOURCE */ -#ifdef HAVE_SOLARIS_EAS -#define _ATFILE_SOURCE -#endif - #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ @@ -83,7 +78,7 @@ static char *mtoupath(const struct vol *vol, const char *mpath) char *u; size_t inplen; size_t outlen; - uint16_t flags = CONV_ESCAPEHEX; + uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON; if (!mpath) return NULL; @@ -126,7 +121,8 @@ static char *mtoupath(const struct vol *vol, const char *mpath) */ static int unpack_header(struct ea * restrict ea) { - int ret = 0, count = 0; + int ret = 0; + unsigned int count = 0; uint32_t uint32; char *buf; @@ -204,7 +200,7 @@ exit: */ static int pack_header(struct ea * restrict ea) { - int count = 0, eacount = 0; + unsigned int count = 0, eacount = 0; uint16_t uint16; uint32_t uint32; size_t bufsize = EA_HEADER_SIZE; @@ -279,48 +275,6 @@ static int pack_header(struct ea * restrict ea) return 0; } -/* - * Function: ea_path - * - * Purpose: return name of ea header filename - * - * Arguments: - * - * ea (r) ea handle - * eaname (r) name of EA or NULL - * - * Returns: pointer to name in static buffer, NULL on error - * - * Effects: - * - * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme - * Files: "file" -> "file/.AppleDouble/file::EA" - * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA" - * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA" - */ -static char * ea_path(const struct ea * restrict ea, - const char * restrict eaname) -{ - char *adname; - static char pathbuf[MAXPATHLEN + 1]; - - /* get name of a adouble file from uname */ - adname = ea->vol->vfs->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0); - /* copy it so we can work with it */ - strlcpy(pathbuf, adname, MAXPATHLEN + 1); - /* append "::EA" */ - strlcat(pathbuf, "::EA", MAXPATHLEN + 1); - - if (eaname) { - strlcat(pathbuf, "::", MAXPATHLEN + 1); - if ((eaname = mtoupath(ea->vol, eaname)) == NULL) - return NULL; - strlcat(pathbuf, eaname, MAXPATHLEN + 1); - } - - return pathbuf; -} - /* * Function: ea_addentry * @@ -345,7 +299,7 @@ static int ea_addentry(struct ea * restrict ea, size_t attrsize, int bitmap) { - int count = 0; + unsigned int count = 0; void *tmprealloc; /* First check if an EA of the requested name already exist */ @@ -402,51 +356,6 @@ error: return -1; } -/* - * Function: ea_delentry - * - * Purpose: delete one EA from ea->ea_entries[] - * - * Arguments: - * - * ea (rw) pointer to struct ea - * attruname (r) EA name - * - * Returns: new number of EA entries, -1 on error - * - * Effects: - * - * Remove entry from ea->ea_entries[]. Decrement ea->ea_count. - * Marks it as unused just by freeing name and setting it to NULL. - * ea_close and pack_buffer must honor this. - */ -static int ea_delentry(struct ea * restrict ea, - const char * restrict attruname) -{ - int ret = 0, count = 0; - - if (ea->ea_count == 0) { - LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion"); - return -1; - } - - while (count < ea->ea_count) { - /* search matching EA */ - if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { - free((*ea->ea_entries)[count].ea_name); - (*ea->ea_entries)[count].ea_name = NULL; - - LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u", - attruname, count + 1, ea->ea_count); - - break; - } - count++; - } - - return ret; -} - /* * Function: create_ea_header * @@ -537,7 +446,7 @@ static int write_ea(const struct ea * restrict ea, struct stat st; char *eaname; - if ((eaname = ea_path(ea, attruname)) == NULL) { + if ((eaname = ea_path(ea, attruname, 1)) == NULL) { LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname); return AFPERR_MISC; } @@ -566,7 +475,7 @@ static int write_ea(const struct ea * restrict ea, goto exit; } - if ((write(fd, ibuf, attrsize)) != attrsize) { + if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) { LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno)); ret = -1; goto exit; @@ -578,6 +487,51 @@ exit: return ret; } +/* + * Function: ea_delentry + * + * Purpose: delete one EA from ea->ea_entries[] + * + * Arguments: + * + * ea (rw) pointer to struct ea + * attruname (r) EA name + * + * Returns: new number of EA entries, -1 on error + * + * Effects: + * + * Remove entry from ea->ea_entries[]. Decrement ea->ea_count. + * Marks it as unused just by freeing name and setting it to NULL. + * ea_close and pack_buffer must honor this. + */ +static int ea_delentry(struct ea * restrict ea, const char * restrict attruname) +{ + int ret = 0; + unsigned int count = 0; + + if (ea->ea_count == 0) { + LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion"); + return -1; + } + + while (count < ea->ea_count) { + /* search matching EA */ + if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { + free((*ea->ea_entries)[count].ea_name); + (*ea->ea_entries)[count].ea_name = NULL; + + LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u", + attruname, count + 1, ea->ea_count); + + break; + } + count++; + } + + return ret; +} + /* * Function: delete_ea_file * @@ -590,14 +544,13 @@ exit: * * Returns: 0 on success, -1 on error */ -static int delete_ea_file(const struct ea * restrict ea, - const char *eaname) +static int delete_ea_file(const struct ea * restrict ea, const char *eaname) { int ret = 0; char *eafile; struct stat st; - if ((eafile = ea_path(ea, eaname)) == NULL) { + if ((eafile = ea_path(ea, eaname, 1)) == NULL) { LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname); return -1; } @@ -615,6 +568,53 @@ static int delete_ea_file(const struct ea * restrict ea, return ret; } +/************************************************************************************* + * ea_path, ea_open and ea_close are only global so that dbd can call them + *************************************************************************************/ + +/* + * Function: ea_path + * + * Purpose: return name of ea header filename + * + * Arguments: + * + * ea (r) ea handle + * eaname (r) name of EA or NULL + * macname (r) if != 0 call mtoupath on eaname + * + * Returns: pointer to name in static buffer, NULL on error + * + * Effects: + * + * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme + * Files: "file" -> "file/.AppleDouble/file::EA" + * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA" + * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA" + */ +char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname) +{ + char *adname; + static char pathbuf[MAXPATHLEN + 1]; + + /* get name of a adouble file from uname */ + adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0); + /* copy it so we can work with it */ + strlcpy(pathbuf, adname, MAXPATHLEN + 1); + /* append "::EA" */ + strlcat(pathbuf, "::EA", MAXPATHLEN + 1); + + if (eaname) { + strlcat(pathbuf, "::", MAXPATHLEN + 1); + if (macname) + if ((eaname = mtoupath(ea->vol, eaname)) == NULL) + return NULL; + strlcat(pathbuf, eaname, MAXPATHLEN + 1); + } + + return pathbuf; +} + /* * Function: ea_open * @@ -630,7 +630,9 @@ static int delete_ea_file(const struct ea * restrict ea, * Eiterh EA_RDONLY or EA_RDWR MUST be requested * ea (w) pointer to a struct ea that we fill * - * Returns: 0 on success, -1 on error + * Returns: 0 on success + * -1 on misc error with errno = EFAULT + * -2 if no EA header exists with errno = ENOENT * * Effects: * @@ -639,10 +641,10 @@ static int delete_ea_file(const struct ea * restrict ea, * file is either read or write locked depending on the open flags. * When you're done with struct ea you must call ea_close on it. */ -static int ea_open(const struct vol * restrict vol, - const char * restrict uname, - eaflags_t eaflags, - struct ea * restrict ea) +int ea_open(const struct vol * restrict vol, + const char * restrict uname, + eaflags_t eaflags, + struct ea * restrict ea) { int ret = 0; char *eaname; @@ -659,9 +661,10 @@ static int ea_open(const struct vol * restrict vol, ea->vol = vol; /* ea_close needs it */ ea->ea_flags = eaflags; - /* Dont check for errors, eg when removing the file is already gone */ - stat(uname, &st); - if (S_ISDIR(st.st_mode)) + ea->dirfd = -1; /* no *at (cf openat) semantics by default */ + + /* Dont care for errors, eg when removing the file is already gone */ + if (!stat(uname, &st) && S_ISDIR(st.st_mode)) ea->ea_flags |= EA_DIR; if ( ! (ea->filename = strdup(uname))) { @@ -669,7 +672,7 @@ static int ea_open(const struct vol * restrict vol, return -1; } - eaname = ea_path(ea, NULL); + eaname = ea_path(ea, NULL, 0); LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname); /* Check if it exists, if not create it if EA_CREATE is in eaflags */ @@ -680,7 +683,7 @@ static int ea_open(const struct vol * restrict vol, if ( ! (eaflags & EA_CREATE)) { /* creation was not requested, so return with error */ - ret = -1; + ret = -2; goto exit; } @@ -712,6 +715,11 @@ static int ea_open(const struct vol * restrict vol, /* header file exists, so read and parse it */ /* malloc buffer where we read disk file into */ + if (st.st_size < EA_HEADER_SIZE) { + LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname); + ret = -1; + goto exit; + } ea->ea_size = st.st_size; ea->ea_data = malloc(st.st_size); if (! ea->ea_data) { @@ -745,7 +753,7 @@ static int ea_open(const struct vol * restrict vol, } /* read it */ - if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) { + if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname); ret = -1; goto exit; @@ -758,9 +766,14 @@ static int ea_open(const struct vol * restrict vol, } exit: - if (ret == 0) { + switch (ret) { + case 0: ea->ea_inited = EA_INITED; - } else { + break; + case -1: + errno = EFAULT; /* force some errno distinguishable from ENOENT */ + /* fall through */ + case -2: if (ea->ea_data) { free(ea->ea_data); ea->ea_data = NULL; @@ -769,9 +782,72 @@ exit: close(ea->ea_fd); ea->ea_fd = -1; } + break; + } + + return ret; +} + +/* + * Function: ea_openat + * + * Purpose: openat like wrapper for ea_open, takes a additional file descriptor + * + * Arguments: + * + * vol (r) current volume + * sfd (r) openat like file descriptor + * uname (r) filename for which we have to open a header + * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) + * EA_RDONLY: open read only + * EA_RDWR: open read/write + * Eiterh EA_RDONLY or EA_RDWR MUST be requested + * ea (w) pointer to a struct ea that we fill + * + * Returns: 0 on success + * -1 on misc error with errno = EFAULT + * -2 if no EA header exists with errno = ENOENT + * + * Effects: + * + * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. + * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. + * file is either read or write locked depending on the open flags. + * When you're done with struct ea you must call ea_close on it. + */ +int ea_openat(const struct vol * restrict vol, + int dirfd, + const char * restrict uname, + eaflags_t eaflags, + struct ea * restrict ea) +{ + int ret = 0; + int cwdfd = -1; + + if (dirfd != -1) { + if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { + ret = -1; + goto exit; + } + } + + ret = ea_open(vol, uname, eaflags, ea); + ea->dirfd = dirfd; + + if (dirfd != -1) { + if (fchdir(cwdfd) != 0) { + LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting"); + exit(EXITERR_SYS); + } } + +exit: + if (cwdfd != -1) + close(cwdfd); + return ret; + } /* @@ -790,9 +866,10 @@ exit: * Flushes and then closes and frees all resouces held by ea handle. * Pack data in ea into ea_data, then write ea_data to disk */ -static int ea_close(struct ea * restrict ea) +int ea_close(struct ea * restrict ea) { - int ret = 0, count = 0; + int ret = 0; + unsigned int count = 0; char *eaname; struct stat st; @@ -811,9 +888,9 @@ static int ea_close(struct ea * restrict ea) } else { if (ea->ea_count == 0) { /* Check if EA header exists and remove it */ - eaname = ea_path(ea, NULL); - if ((stat(eaname, &st)) == 0) { - if ((unlink(eaname)) != 0) { + eaname = ea_path(ea, NULL, 0); + if ((lstatat(ea->dirfd, eaname, &st)) == 0) { + if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) { LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s", eaname, strerror(errno)); ret = -1; @@ -841,7 +918,7 @@ static int ea_close(struct ea * restrict ea) goto exit; } - if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) { + if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno)); ret = -1; } @@ -908,22 +985,22 @@ exit: * * Copies EA size into rbuf in network order. Increments *rbuflen +4. */ -int get_easize(const struct vol * restrict vol, - char * restrict rbuf, - int * restrict rbuflen, - const char * restrict uname, - int oflag, - const char * restrict attruname) +int get_easize(VFS_FUNC_ARGS_EA_GETSIZE) { - int ret = AFPERR_MISC, count = 0; + int ret = AFPERR_MISC; + unsigned int count = 0; uint32_t uint32; struct ea ea; LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname); if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { - LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname); - return AFPERR_MISC; + if (errno != ENOENT) + LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname); + + memset(rbuf, 0, 4); + *rbuflen += 4; + return ret; } while (count < ea.ea_count) { @@ -969,15 +1046,10 @@ int get_easize(const struct vol * restrict vol, * * Copies EA into rbuf. Increments *rbuflen accordingly. */ -int get_eacontent(const struct vol * restrict vol, - char * restrict rbuf, - int * restrict rbuflen, - const char * restrict uname, - int oflag, - const char * restrict attruname, - int maxreply) +int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT) { - int ret = AFPERR_MISC, count = 0, fd = -1; + int ret = AFPERR_MISC, fd = -1; + unsigned int count = 0; uint32_t uint32; size_t toread; struct ea ea; @@ -986,13 +1058,16 @@ int get_eacontent(const struct vol * restrict vol, LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname); if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { - LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname); - return AFPERR_MISC; + if (errno != ENOENT) + LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname); + memset(rbuf, 0, 4); + *rbuflen += 4; + return ret; } while (count < ea.ea_count) { if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { - if ( (eafile = ea_path(&ea, attruname)) == NULL) { + if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) { ret = AFPERR_MISC; break; } @@ -1015,7 +1090,7 @@ int get_eacontent(const struct vol * restrict vol, rbuf += 4; *rbuflen += 4; - if ((read(fd, rbuf, toread)) != toread) { + if (read(fd, rbuf, toread) != (ssize_t)toread) { LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname); ret = AFPERR_MISC; break; @@ -1058,13 +1133,10 @@ int get_eacontent(const struct vol * restrict vol, * Copies names of all EAs of uname as consecutive C strings into rbuf. * Increments *buflen accordingly. */ -int list_eas(const struct vol * restrict vol, - char * restrict attrnamebuf, - int * restrict buflen, - const char * restrict uname, - int oflag) +int list_eas(VFS_FUNC_ARGS_EA_LIST) { - int count = 0, attrbuflen = *buflen, ret = AFP_OK, len; + unsigned int count = 0; + int attrbuflen = *buflen, ret = AFP_OK, len; char *buf = attrnamebuf; struct ea ea; @@ -1142,12 +1214,7 @@ exit: * Copies names of all EAs of uname as consecutive C strings into rbuf. * Increments *rbuflen accordingly. */ -int set_ea(const struct vol * restrict vol, - const char * restrict uname, - const char * restrict attruname, - const char * restrict ibuf, - size_t attrsize, - int oflag) +int set_ea(VFS_FUNC_ARGS_EA_SET) { int ret = AFP_OK; struct ea ea; @@ -1199,10 +1266,7 @@ exit: * * Removes EA attruname from file uname. */ -int remove_ea(const struct vol * restrict vol, - const char * restrict uname, - const char * restrict attruname, - int oflag) +int remove_ea(VFS_FUNC_ARGS_EA_REMOVE) { int ret = AFP_OK; struct ea ea; @@ -1236,387 +1300,21 @@ exit: return ret; } -/********************************************************************************** - * Solaris EA VFS funcs - **********************************************************************************/ - -/* - * Function: sol_get_easize - * - * Purpose: get size of an EA on Solaris native EA - * - * Arguments: - * - * vol (r) current volume - * rbuf (w) DSI reply buffer - * rbuflen (rw) current length of data in reply buffer - * uname (r) filename - * oflag (r) link and create flag - * attruname (r) name of attribute - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies EA size into rbuf in network order. Increments *rbuflen +4. - */ -#ifdef HAVE_SOLARIS_EAS -int sol_get_easize(const struct vol * restrict vol, - char * restrict rbuf, - int * restrict rbuflen, - const char * restrict uname, - int oflag, - cons char * restrict attruname) -{ - int ret, attrdirfd; - uint32_t attrsize; - struct stat st; - - LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname); - - if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) { - if (errno == ELOOP) { - /* its a symlink and client requested O_NOFOLLOW */ - LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname); - - memset(rbuf, 0, 4); - *rbuflen += 4; - - return AFP_OK; - } - LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno)); - return AFPERR_MISC; - } - - if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) { - LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno)); - ret = AFPERR_MISC; - goto exit; - } - attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size; - - /* Start building reply packet */ - - LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize); - - /* length of attribute data */ - attrsize = htonl(attrsize); - memcpy(rbuf, &attrsize, 4); - *rbuflen += 4; - - ret = AFP_OK; - -exit: - close(attrdirfd); - return ret; -} -#endif /* HAVE_SOLARIS_EAS */ - -/* - * Function: sol_get_eacontent - * - * Purpose: copy Solaris native EA into rbuf - * - * Arguments: - * - * vol (r) current volume - * rbuf (w) DSI reply buffer - * rbuflen (rw) current length of data in reply buffer - * uname (r) filename - * oflag (r) link and create flag - * attruname (r) name of attribute - * maxreply (r) maximum EA size as of current specs/real-life - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies EA into rbuf. Increments *rbuflen accordingly. - */ -#ifdef HAVE_SOLARIS_EAS -int sol_get_eacontent(const struct vol * restrict vol, - char * restrict rbuf, - int * restrict rbuflen, - const char * restrict uname, - int oflag, - char * restrict attruname, - int maxreply) -{ - int ret, attrdirfd; - size_t toread, okread = 0, len; - char *datalength; - struct stat st; - - if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) { - if (errno == ELOOP) { - /* its a symlink and client requested O_NOFOLLOW */ - LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname); - - memset(rbuf, 0, 4); - *rbuflen += 4; - - return AFP_OK; - } - LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno)); - return AFPERR_MISC; - } - - if ( -1 == (fstat(attrdirfd, &st))) { - LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno)); - ret = AFPERR_MISC; - goto exit; - } - - /* Start building reply packet */ - - maxreply -= MAX_REPLY_EXTRA_BYTES; - if (maxreply > MAX_EA_SIZE) - maxreply = MAX_EA_SIZE; - - /* But never send more than the client requested */ - toread = (maxreply < st.st_size) ? maxreply : st.st_size; - - LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply); - - /* remember where we must store length of attribute data in rbuf */ - datalength = rbuf; - rbuf += 4; - *rbuflen += 4; - - while (1) { - len = read(attrdirfd, rbuf, toread); - if (len == -1) { - LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno)); - ret = AFPERR_MISC; - goto exit; - } - okread += len; - rbuf += len; - *rbuflen += len; - if ((len == 0) || (okread == toread)) - break; - } - - okread = htonl((uint32_t)okread); - memcpy(datalength, &okread, 4); - - ret = AFP_OK; - -exit: - close(attrdirfd); - return ret; -} -#endif /* HAVE_SOLARIS_EAS */ - -/* - * Function: sol_list_eas - * - * Purpose: copy names of Solaris native EA into attrnamebuf - * - * Arguments: - * - * vol (r) current volume - * attrnamebuf (w) store names a consecutive C strings here - * buflen (rw) length of names in attrnamebuf - * uname (r) filename - * oflag (r) link and create flag - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies names of all EAs of uname as consecutive C strings into rbuf. - * Increments *rbuflen accordingly. - */ -#ifdef HAVE_SOLARIS_EAS -int sol_list_eas(const struct vol * restrict vol, - char * restrict attrnamebuf, - int * restrict buflen, - const char * restrict uname, - int oflag) -{ - int ret, attrbuflen = *buflen, len, attrdirfd = 0; - struct dirent *dp; - DIR *dirp = NULL; - - /* Now list file attribute dir */ - if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) { - if (errno == ELOOP) { - /* its a symlink and client requested O_NOFOLLOW */ - ret = AFPERR_BADTYPE; - goto exit; - } - LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno)); - ret = AFPERR_MISC; - goto exit; - } - - if (NULL == (dirp = fdopendir(attrdirfd))) { - LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno)); - ret = AFPERR_MISC; - goto exit; - } - - while ((dp = readdir(dirp))) { - /* check if its "." or ".." */ - if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) || - (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0)) - continue; - - len = strlen(dp->d_name); - - /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */ - if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) { - ret = AFPERR_MISC; - goto exit; - } - if (len == 255) - /* convert_string didn't 0-terminate */ - attrnamebuf[attrbuflen + 255] = 0; - - LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name); - - attrbuflen += len + 1; - if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) { - /* Next EA name could overflow, so bail out with error. - FIXME: evantually malloc/memcpy/realloc whatever. - Is it worth it ? */ - LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname); - ret = AFPERR_MISC; - goto exit; - } - } - - ret = AFP_OK; - -exit: - if (dirp) - closedir(dirp); - - if (attrdirfd > 0) - close(attrdirfd); - - *buflen = attrbuflen; - return ret; -} -#endif /* HAVE_SOLARIS_EAS */ - -/* - * Function: sol_set_ea - * - * Purpose: set a Solaris native EA - * - * Arguments: - * - * vol (r) current volume - * uname (r) filename - * attruname (r) EA name - * ibuf (r) buffer with EA content - * attrsize (r) length EA in ibuf - * oflag (r) link and create flag - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies names of all EAs of uname as consecutive C strings into rbuf. - * Increments *rbuflen accordingly. - */ -#ifdef HAVE_SOLARIS_EAS -int sol_set_ea(const struct vol * restrict vol, - const char * restrict u_name, - const char * restrict attruname, - const char * restrict ibuf, - size_t attrsize, - int oflag) -{ - int attrdirfd; - - if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) { - if (errno == ELOOP) { - /* its a symlink and client requested O_NOFOLLOW */ - LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name); - return AFP_OK; - } - LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno)); - return AFPERR_MISC; - } - - if ((write(attrdirfd, ibuf, attrsize)) != attrsize) { - LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno)); - return AFPERR_MISC; - } - - return AFP_OK; -} -#endif /* HAVE_SOLARIS_EAS */ - -/* - * Function: sol_remove_ea - * - * Purpose: remove a Solaris native EA - * - * Arguments: - * - * vol (r) current volume - * uname (r) filename - * attruname (r) EA name - * oflag (r) link and create flag - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Removes EA attruname from file uname. - */ -#ifdef HAVE_SOLARIS_EAS -int sol_remove_ea(const struct vol * restrict vol, - const char * restrict uname, - const char * restrict attruname, - int oflag) -{ - int attrdirfd; - - if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) { - switch (errno) { - case ELOOP: - /* its a symlink and client requested O_NOFOLLOW */ - LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name); - return AFP_OK; - case EACCES: - LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno)); - return AFPERR_ACCESS; - default: - LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno)); - return AFPERR_MISC; - } - } - - if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) { - if (errno == EACCES) { - LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno)); - return AFPERR_ACCESS; - } - LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno)); - return AFPERR_MISC; - } - -} -#endif /* HAVE_SOLARIS_EAS */ - /****************************************************************************************** * EA VFS funcs that deal with file/dir cp/mv/rm ******************************************************************************************/ int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) { - LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file); - - int count = 0, ret = AFP_OK; + unsigned int count = 0; + int ret = AFP_OK; + int cwd = -1; struct ea ea; + LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file); + /* Open EA stuff */ - if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) { + if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) { if (errno == ENOENT) /* no EA files, nothing to do */ return AFP_OK; @@ -1626,6 +1324,13 @@ int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) } } + if (dirfd != -1) { + if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { + ret = AFPERR_MISC; + goto exit; + } + } + while (count < ea.ea_count) { if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) { ret = AFPERR_MISC; @@ -1639,15 +1344,24 @@ int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) /* ea_close removes the EA header file for us because all names are NULL */ if ((ea_close(&ea)) != 0) { LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file); - return AFPERR_MISC; + ret = AFPERR_MISC; } + if (dirfd != -1 && fchdir(cwd) != 0) { + LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!"); + exit(EXITERR_SYS); + } + +exit: + if (cwd != -1) + close(cwd); + return ret; } int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) { - int count = 0; + unsigned int count = 0; int ret = AFP_OK; size_t easize; char srceapath[ MAXPATHLEN + 1]; @@ -1661,7 +1375,7 @@ int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) /* Open EA stuff */ - if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) { + if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) { if (errno == ENOENT) /* no EA files, nothing to do */ return AFP_OK; @@ -1695,12 +1409,12 @@ int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) easize = (*srcea.ea_entries)[count].ea_size; /* Build src and dst paths for rename() */ - if ((eapath = ea_path(&srcea, eaname)) == NULL) { + if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { ret = AFPERR_MISC; goto exit; } strcpy(srceapath, eapath); - if ((eapath = ea_path(&dstea, eaname)) == NULL) { + if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { ret = AFPERR_MISC; goto exit; } @@ -1726,7 +1440,7 @@ int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) } /* Now rename the EA */ - if ((rename( srceapath, eapath)) < 0) { + if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) { LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", src, dst, srceapath, eapath); ret = AFPERR_MISC; @@ -1745,7 +1459,7 @@ exit: int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) { - int count = 0; + unsigned int count = 0; int ret = AFP_OK; size_t easize; char srceapath[ MAXPATHLEN + 1]; @@ -1758,7 +1472,7 @@ int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst); /* Open EA stuff */ - if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) { + if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) { if (errno == ENOENT) /* no EA files, nothing to do */ return AFP_OK; @@ -1792,12 +1506,12 @@ int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) easize = (*srcea.ea_entries)[count].ea_size; /* Build src and dst paths for copy_file() */ - if ((eapath = ea_path(&srcea, eaname)) == NULL) { + if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { ret = AFPERR_MISC; goto exit; } strcpy(srceapath, eapath); - if ((eapath = ea_path(&dstea, eaname)) == NULL) { + if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { ret = AFPERR_MISC; goto exit; } @@ -1814,7 +1528,7 @@ int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) } /* Now copy the EA */ - if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) { + if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) { LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", src, dst, srceapath, eapath); ret = AFPERR_MISC; @@ -1832,12 +1546,13 @@ exit: int ea_chown(VFS_FUNC_ARGS_CHOWN) { - LOG(log_debug, logtype_afpd, "ea_chown('%s')", path); - int count = 0, ret = AFP_OK; + unsigned int count = 0; + int ret = AFP_OK; char *eaname; struct ea ea; + LOG(log_debug, logtype_afpd, "ea_chown('%s')", path); /* Open EA stuff */ if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) { if (errno == ENOENT) @@ -1849,7 +1564,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN) } } - if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) { + if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) { switch (errno) { case EPERM: case EACCES: @@ -1862,11 +1577,11 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN) } while (count < ea.ea_count) { - if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) { + if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { ret = AFPERR_MISC; goto exit; } - if ((chown(eaname, uid, gid)) != 0) { + if ((lchown(eaname, uid, gid)) != 0) { switch (errno) { case EPERM: case EACCES: @@ -1893,12 +1608,13 @@ exit: int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) { - LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name); - int count = 0, ret = AFP_OK; + unsigned int count = 0; + int ret = AFP_OK; const char *eaname; struct ea ea; + LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name); /* Open EA stuff */ if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { if (errno == ENOENT) @@ -1909,8 +1625,8 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) } /* Set mode on EA header file */ - if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno)); + if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); switch (errno) { case EPERM: case EACCES: @@ -1924,7 +1640,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) /* Set mode on EA files */ while (count < ea.ea_count) { - if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) { + if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { ret = AFPERR_MISC; goto exit; } @@ -1956,34 +1672,37 @@ exit: int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) { - LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name); int ret = AFP_OK; + unsigned int count = 0; uid_t uid; const char *eaname; const char *eaname_safe = NULL; struct ea ea; + LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name); /* .AppleDouble already might be inaccesible, so we must run as id 0 */ uid = geteuid(); if (seteuid(0)) { LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno)); - ret = AFPERR_MISC; - goto exit; + return AFPERR_MISC; } /* Open EA stuff */ if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { - if (errno == ENOENT) - /* no EA files, nothing to do */ - return AFP_OK; - else - return AFPERR_MISC; + /* ENOENT --> no EA files, nothing to do */ + if (errno != ENOENT) + ret = AFPERR_MISC; + if (seteuid(uid) < 0) { + LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); + exit(EXITERR_SYS); + } + return ret; } /* Set mode on EA header */ - if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno)); + if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); switch (errno) { case EPERM: case EACCES: @@ -1996,7 +1715,6 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) } /* Set mode on EA files */ - int count = 0; while (count < ea.ea_count) { eaname = (*ea.ea_entries)[count].ea_name; /* @@ -2009,7 +1727,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname); eaname = eaname_safe; } - if ((eaname = ea_path(&ea, eaname)) == NULL) { + if ((eaname = ea_path(&ea, eaname, 1)) == NULL) { ret = AFPERR_MISC; goto exit; }