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=35048a3cc5cfa8dd43e2989aa103ded410508e83;hb=a3e43549642d11f4bc86324f9a09c294f6a3f74b;hpb=9805e97bfd2b1c57d704eb54762cf684db0c8674 diff --git a/libatalk/vfs/ea.c b/libatalk/vfs/ea.c index 35048a3c..f8e85acf 100644 --- a/libatalk/vfs/ea.c +++ b/libatalk/vfs/ea.c @@ -1,5 +1,5 @@ /* - $Id: ea.c,v 1.15 2009-11-18 08:02:33 didg 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 @@ -78,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 | CONV_FORCE; + uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON; if (!mpath) return NULL; @@ -275,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->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 * @@ -398,52 +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; - 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: create_ea_header * @@ -534,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; } @@ -575,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 * @@ -587,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; } @@ -612,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 * @@ -627,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: * @@ -636,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; @@ -656,6 +661,8 @@ static int ea_open(const struct vol * restrict vol, ea->vol = vol; /* ea_close needs it */ ea->ea_flags = eaflags; + 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; @@ -665,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 */ @@ -676,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; } @@ -708,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) { @@ -754,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; @@ -765,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; + } /* @@ -786,7 +866,7 @@ 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; unsigned int count = 0; @@ -808,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; @@ -915,8 +995,12 @@ int get_easize(VFS_FUNC_ARGS_EA_GETSIZE) 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) { @@ -974,13 +1058,16 @@ int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT) 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; } @@ -1221,12 +1308,13 @@ int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) { 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; @@ -1236,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; @@ -1249,9 +1344,18 @@ 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; } @@ -1271,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; @@ -1305,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; } @@ -1336,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; @@ -1368,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; @@ -1402,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; } @@ -1424,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; @@ -1460,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: @@ -1473,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: @@ -1521,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: @@ -1536,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; } @@ -1597,8 +1701,8 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) } /* 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: @@ -1623,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; }