From a3e43549642d11f4bc86324f9a09c294f6a3f74b Mon Sep 17 00:00:00 2001 From: franklahm Date: Fri, 12 Mar 2010 15:16:48 +0000 Subject: [PATCH] Convert afp_moveandrename and all called funcs to XXXat semantics if available --- configure.in | 3 +- etc/afpd/directory.c | 49 +++++---- etc/afpd/directory.h | 6 +- etc/afpd/file.c | 88 +++++++-------- etc/afpd/file.h | 8 +- etc/afpd/filedir.c | 111 +++++++++++++------ etc/afpd/fork.h | 9 +- etc/afpd/ofork.c | 64 +++++++++-- include/atalk/adouble.h | 4 +- include/atalk/ea.h | 8 +- include/atalk/unix.h | 17 +-- include/atalk/vfs.h | 16 +-- libatalk/adouble/ad_open.c | 73 ++++++++++++- libatalk/vfs/ea.c | 99 +++++++++++++++-- libatalk/vfs/ea_sys.c | 60 ++++++++++- libatalk/vfs/unix.c | 212 +++++++++++++++++++++++++++++-------- libatalk/vfs/vfs.c | 63 +++++++---- 17 files changed, 677 insertions(+), 213 deletions(-) diff --git a/configure.in b/configure.in index 1c107098..7dfab7a1 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -dnl $Id: configure.in,v 1.238 2010-02-10 14:05:36 franklahm Exp $ +dnl $Id: configure.in,v 1.239 2010-03-12 15:16:48 franklahm Exp $ dnl configure.in for netatalk AC_INIT(etc/afpd/main.c) @@ -138,6 +138,7 @@ AC_CHECK_FUNCS(getcwd gethostname gettimeofday getusershell mkdir rmdir select s AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo) AC_CHECK_FUNCS(waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64) AC_CHECK_FUNCS(strlcpy strlcat setlinebuf gethostid dirfd) +AC_CHECK_FUNC(renameat, AC_DEFINE([_ATFILE_SOURCE], 1, AT file source)) AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include ]) AC_CACHE_SAVE diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index 14edeeb4..6480ca7f 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -1,5 +1,5 @@ /* - * $Id: directory.c,v 1.139 2010-03-02 18:07:13 didg Exp $ + * $Id: directory.c,v 1.140 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -98,7 +98,7 @@ extern void addir_inherit_acl(const struct vol *vol); */ struct dir *curdir; -int afp_errno; +int afp_errno; #define SENTINEL (&sentinel) static struct dir sentinel = { SENTINEL, SENTINEL, NULL, /* left, right, back */ @@ -787,7 +787,7 @@ static int netatalk_mkdir(const char *name) } /* ------------------- */ -static int deletedir(char *dir) +static int deletedir(int dirfd, char *dir) { char path[MAXPATHLEN + 1]; DIR *dp; @@ -801,7 +801,7 @@ static int deletedir(char *dir) return AFPERR_PARAM; /* already gone */ - if ((dp = opendir(dir)) == NULL) + if ((dp = opendirat(dirfd, dir)) == NULL) return AFP_OK; strcpy(path, dir); @@ -818,13 +818,13 @@ static int deletedir(char *dir) break; } strcpy(path + len, de->d_name); - if (lstat(path, &st)) { + if (lstatat(dirfd, path, &st)) { continue; } if (S_ISDIR(st.st_mode)) { - err = deletedir(path); + err = deletedir(dirfd, path); } else { - err = netatalk_unlink(path); + err = netatalk_unlinkat(dirfd, path); } } closedir(dp); @@ -832,13 +832,13 @@ static int deletedir(char *dir) /* okay. the directory is empty. delete it. note: we already got rid of .AppleDouble. */ if (err == AFP_OK) { - err = netatalk_rmdir(dir); + err = netatalk_rmdir(dirfd, dir); } return err; } /* do a recursive copy. */ -static int copydir(const struct vol *vol, char *src, char *dst) +static int copydir(const struct vol *vol, int dirfd, char *src, char *dst) { char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1]; DIR *dp; @@ -852,7 +852,7 @@ static int copydir(const struct vol *vol, char *src, char *dst) /* doesn't exist or the path is too long. */ if (((slen = strlen(src)) > sizeof(spath) - 2) || ((dlen = strlen(dst)) > sizeof(dpath) - 2) || - ((dp = opendir(src)) == NULL)) + ((dp = opendirat(dirfd, src)) == NULL)) return AFPERR_PARAM; /* try to create the destination directory */ @@ -884,7 +884,7 @@ static int copydir(const struct vol *vol, char *src, char *dst) } strcpy(spath + slen, de->d_name); - if (lstat(spath, &st) == 0) { + if (lstatat(dirfd, spath, &st) == 0) { if (strlen(de->d_name) > drem) { err = AFPERR_PARAM; break; @@ -892,9 +892,9 @@ static int copydir(const struct vol *vol, char *src, char *dst) strcpy(dpath + dlen, de->d_name); if (S_ISDIR(st.st_mode)) { - if (AFP_OK != (err = copydir(vol, spath, dpath))) + if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath))) goto copydir_done; - } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) { + } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) { goto copydir_done; } else { @@ -906,7 +906,7 @@ static int copydir(const struct vol *vol, char *src, char *dst) } /* keep the same time stamp. */ - if (lstat(src, &st) == 0) { + if (lstatat(dirfd, src, &st) == 0) { ut.actime = ut.modtime = st.st_mtime; utime(dst, &ut); } @@ -1122,6 +1122,7 @@ struct dir *dirnew(const char *m_name, const char *u_name) return dir; } +#if 0 /* ------------------ */ static hash_val_t hash_fun_dir(const void *key) { @@ -1147,6 +1148,7 @@ static hash_val_t hash_fun_dir(const void *key) } return acc; } +#endif #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ @@ -2532,9 +2534,12 @@ createdir_done: * dst new unix filename (not a pathname) * newname new mac name * newparent curdir - * + * dirfd -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd */ -int renamedir(const struct vol *vol, char *src, char *dst, +int renamedir(const struct vol *vol, + int dirfd, + char *src, + char *dst, struct dir *dir, struct dir *newparent, char *newname) @@ -2545,7 +2550,7 @@ int renamedir(const struct vol *vol, char *src, char *dst, int len, err; /* existence check moved to afp_moveandrename */ - if ( unix_rename( src, dst ) < 0 ) { + if ( unix_rename(dirfd, src, -1, dst ) < 0 ) { switch ( errno ) { case ENOENT : return( AFPERR_NOOBJ ); @@ -2559,11 +2564,11 @@ int renamedir(const struct vol *vol, char *src, char *dst, case EXDEV: /* this needs to copy and delete. bleah. that means we have * to deal with entire directory hierarchies. */ - if ((err = copydir(vol, src, dst)) < 0) { - deletedir(dst); + if ((err = copydir(vol, dirfd, src, dst)) < 0) { + deletedir(-1, dst); return err; } - if ((err = deletedir(src)) < 0) + if ((err = deletedir(dirfd, src)) < 0) return err; break; default : @@ -2571,7 +2576,7 @@ int renamedir(const struct vol *vol, char *src, char *dst, } } - vol->vfs->vfs_renamedir(vol, src, dst); + vol->vfs->vfs_renamedir(vol, dirfd, src, dst); len = strlen( newname ); /* rename() succeeded so we need to update our tree even if we can't open @@ -2690,7 +2695,7 @@ int deletecurdir(struct vol *vol) goto delete_done; } - err = netatalk_rmdir_all_errors(fdir->d_u_name); + err = netatalk_rmdir_all_errors(-1, fdir->d_u_name); if ( err == AFP_OK || err == AFPERR_NOOBJ) { dirchildremove(curdir, fdir); cnid_delete(vol->v_cdb, fdir->d_did); diff --git a/etc/afpd/directory.h b/etc/afpd/directory.h index 0a699a2a..5334be5d 100644 --- a/etc/afpd/directory.h +++ b/etc/afpd/directory.h @@ -1,5 +1,5 @@ /* - * $Id: directory.h,v 1.33 2009-11-13 00:27:35 didg Exp $ + * $Id: directory.h,v 1.34 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1990,1991 Regents of The University of Michigan. * All Rights Reserved. @@ -132,8 +132,8 @@ extern void utommode (struct stat *, struct maccess *); extern int getdirparams (const struct vol *, u_int16_t, struct path *, struct dir *, char *, size_t *); extern int setdirparams (struct vol *, struct path *, u_int16_t, char *); -extern int renamedir (const struct vol *, char *, char *, struct dir *, - struct dir *, char *); +extern int renamedir(const struct vol *, int, char *, char *, struct dir *, + struct dir *, char *); extern int path_error (struct path *, int error); extern void setdiroffcnt (struct dir *dir, struct stat *st, u_int32_t count); diff --git a/etc/afpd/file.c b/etc/afpd/file.c index 94b4f91a..aa692842 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -1,5 +1,5 @@ /* - * $Id: file.c,v 1.140 2010-03-02 12:45:31 didg Exp $ + * $Id: file.c,v 1.141 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -1003,21 +1003,19 @@ setfilparam_done: * renamefile and copyfile take the old and new unix pathnames * and the new mac name. * + * sdir_fd source dir fd to which src path is relative (for openat et al semantics) + * passing -1 means this is not used, src path is a full path * src the source path * dst the dest filename in current dir * newname the dest mac name * adp adouble struct of src file, if open, or & zeroed one * */ -int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp) +int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp) { int rc; -#ifdef DEBUG - LOG(log_debug9, logtype_afpd, "begin renamefile:"); -#endif /* DEBUG */ - - if ( unix_rename( src, dst ) < 0 ) { + if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) { switch ( errno ) { case ENOENT : return( AFPERR_NOOBJ ); @@ -1035,17 +1033,17 @@ int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struc /* FIXME warning in syslog so admin'd know there's a conflict ?*/ return AFPERR_OLOCK; /* little lie */ } - if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) { + if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) { /* on error copyfile delete dest */ return( rc ); } - return deletefile(vol, src, 0); + return deletefile(vol, sdir_fd, src, 0); default : return( AFPERR_PARAM ); } } - if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) { + if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) { int err; err = errno; @@ -1053,7 +1051,7 @@ int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struc * we know we are on the same device */ if (err) { - unix_rename( dst, src ); + unix_rename(-1, dst, sdir_fd, src ); /* return the first error */ switch ( err) { case ENOENT : @@ -1076,9 +1074,6 @@ int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struc ad_flush( adp ); ad_close( adp, ADFLAGS_HF ); } -#ifdef DEBUG - LOG(log_debug9, logtype_afpd, "end renamefile:"); -#endif /* DEBUG */ return( AFP_OK ); } @@ -1263,7 +1258,7 @@ int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si goto copy_exit; } - if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) { + if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) { retvalue = err; goto copy_exit; } @@ -1385,8 +1380,13 @@ static int copy_fork(int eid, struct adouble *add, struct adouble *ads) * because we are doing it elsewhere. * currently if newname is NULL then adp is NULL. */ -int copyfile(const struct vol *s_vol, const struct vol*d_vol, - char *src, char *dst, char *newname, struct adouble *adp) +int copyfile(const struct vol *s_vol, + const struct vol *d_vol, + int sfd, + char *src, + char *dst, + char *newname, + struct adouble *adp) { struct adouble ads, add; int err = 0; @@ -1395,9 +1395,8 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol, int stat_result; struct stat st; -#ifdef DEBUG - LOG(log_debug9, logtype_afpd, "begin copyfile:"); -#endif /* DEBUG */ + LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')", + sfd, src, dst, newname); if (adp == NULL) { ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); @@ -1409,7 +1408,7 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol, adflags |= ADFLAGS_HF; } - if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) { + if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) { ret_err = errno; goto done; } @@ -1431,7 +1430,7 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol, ret_err = errno; ad_close( adp, adflags ); if (EEXIST != ret_err) { - deletefile(d_vol, dst, 0); + deletefile(d_vol, -1, dst, 0); goto done; } return AFPERR_EXIST; @@ -1443,7 +1442,7 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol, if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){ /* copy the data fork */ if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) { - err = d_vol->vfs->vfs_copyfile(d_vol, src, dst); + err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst); } } @@ -1464,7 +1463,7 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol, } if (ret_err) { - deletefile(d_vol, dst, 0); + deletefile(d_vol, -1, dst, 0); } else if (stat_result == 0) { /* set dest modification date to src date */ @@ -1477,10 +1476,6 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol, */ } -#ifdef DEBUG - LOG(log_debug9, logtype_afpd, "end copyfile:"); -#endif /* DEBUG */ - done: switch ( ret_err ) { case 0: @@ -1527,17 +1522,17 @@ u_int16_t bshort = 0; } return 0; } - -int deletefile(const struct vol *vol, char *file, int checkAttrib) +/* + * dirfd can be used for unlinkat semantics + */ +int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib) { struct adouble ad; struct adouble *adp = NULL; int adflags, err = AFP_OK; int meta = 0; -#ifdef DEBUG - LOG(log_debug9, logtype_afpd, "begin deletefile:"); -#endif /* DEBUG */ + LOG(log_debug, logtype_afpd, "deletefile('%s')", file); ad_init(&ad, vol->v_adouble, vol->v_ad_options); if (checkAttrib) { @@ -1546,7 +1541,7 @@ int deletefile(const struct vol *vol, char *file, int checkAttrib) * moreover sometimes deletefile is called with a no existent file and * ad_open would create a 0 byte resource fork */ - if ( ad_metadata( file, ADFLAGS_OPENFORKS, &ad) == 0 ) { + if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) { if ((err = check_attrib(&ad))) { ad_close_metadata(&ad); return err; @@ -1557,7 +1552,7 @@ int deletefile(const struct vol *vol, char *file, int checkAttrib) /* try to open both forks at once */ adflags = ADFLAGS_DF; - if ( ad_open( file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) { + if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) { switch (errno) { case ENOENT: err = AFPERR_NOOBJ; @@ -1595,11 +1590,9 @@ int deletefile(const struct vol *vol, char *file, int checkAttrib) if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) { err = AFPERR_BUSY; - } - else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) { + } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) { cnid_t id; - if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) - { + if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) { cnid_delete(vol->v_cdb, id); } } @@ -1611,11 +1604,6 @@ end: if (adp) ad_close( &ad, adflags ); /* ad_close removes locks if any */ - -#ifdef DEBUG - LOG(log_debug9, logtype_afpd, "end deletefile:"); -#endif /* DEBUG */ - return err; } @@ -2144,17 +2132,17 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U } /* now, quickly rename the file. we error if we can't. */ - if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK) + if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK) goto err_exchangefile; of_rename(vol, s_of, sdir, spath, curdir, temp); /* rename destination to source */ - if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK) + if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK) goto err_src_to_tmp; of_rename(vol, d_of, curdir, path->m_name, sdir, spath); /* rename temp to destination */ - if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK) + if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK) goto err_dest_to_src; of_rename(vol, s_of, curdir, temp, curdir, path->m_name); @@ -2239,17 +2227,17 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U * properly. */ err_temp_to_dest: /* rename dest to temp */ - renamefile(vol, upath, temp, temp, adsp); + renamefile(vol, -1, upath, temp, temp, adsp); of_rename(vol, s_of, curdir, upath, curdir, temp); err_dest_to_src: /* rename source back to dest */ - renamefile(vol, p, upath, path->m_name, addp); + renamefile(vol, -1, p, upath, path->m_name, addp); of_rename(vol, d_of, sdir, spath, curdir, path->m_name); err_src_to_tmp: /* rename temp back to source */ - renamefile(vol, temp, p, spath, adsp); + renamefile(vol, -1, temp, p, spath, adsp); of_rename(vol, s_of, curdir, temp, sdir, spath); err_exchangefile: diff --git a/etc/afpd/file.h b/etc/afpd/file.h index 9d3ca116..6cf4ae22 100644 --- a/etc/afpd/file.h +++ b/etc/afpd/file.h @@ -1,5 +1,5 @@ /* - * $Id: file.h,v 1.25 2010-02-10 14:05:37 franklahm Exp $ + * $Id: file.h,v 1.26 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1990,1991 Regents of The University of Michigan. * All Rights Reserved. @@ -115,9 +115,9 @@ extern int getfilparams (struct vol *, u_int16_t, struct path *, struct dir *, char *buf, size_t *); extern int setfilparams (struct vol *, struct path *, u_int16_t, char *); -extern int renamefile (const struct vol *, char *, char *, char *, struct adouble *); -extern int copyfile (const struct vol *, const struct vol *, char *, char *, char *, struct adouble *); -extern int deletefile (const struct vol *, char *, int); +extern int renamefile (const struct vol *, int, char *, char *, char *, struct adouble *); +extern int copyfile (const struct vol *, const struct vol *, int, char *, char *, char *, struct adouble *); +extern int deletefile (const struct vol *, int, char *, int); extern int getmetadata (struct vol *vol, u_int16_t bitmap, struct path *path, struct dir *dir, char *buf, size_t *buflen, struct adouble *adp); diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c index 203622f4..3e5cb2f5 100644 --- a/etc/afpd/filedir.c +++ b/etc/afpd/filedir.c @@ -1,5 +1,5 @@ /* - * $Id: filedir.c,v 1.72 2010-02-19 10:51:59 franklahm Exp $ + * $Id: filedir.c,v 1.73 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -313,10 +313,14 @@ int check_name(const struct vol *vol, char *name) /* ------------------------- move and rename sdir:oldname to curdir:newname in volume vol - special care is needed for lock */ -static int moveandrename(const struct vol *vol, struct dir *sdir, char *oldname, char *newname, int isdir) +static int moveandrename(const struct vol *vol, + struct dir *sdir, + int sdir_fd, + char *oldname, + char *newname, + int isdir) { char *p; char *upath; @@ -326,31 +330,38 @@ static int moveandrename(const struct vol *vol, struct dir *sdir, char *oldname, struct adouble ad; struct adouble *adp; struct ofork *opened = NULL; - struct path path; - cnid_t id; + struct path path; + cnid_t id; + int cwd_fd; ad_init(&ad, vol->v_adouble, vol->v_ad_options); adp = &ad; adflags = 0; - + if (!isdir) { - p = mtoupath(vol, oldname, sdir->d_did, utf8_encoding()); - if (!p) { + if ((p = mtoupath(vol, oldname, sdir->d_did, utf8_encoding())) == NULL) return AFPERR_PARAM; /* can't convert */ - } + +#ifndef HAVE_RENAMEAT + /* Need full path */ id = cnid_get(vol->v_cdb, sdir->d_did, p, strlen(p)); p = ctoupath( vol, sdir, oldname ); - if (!p) { + if (!p) return AFPERR_PARAM; /* pathname too long */ - } +#endif /* HAVE_RENAMEAT */ + path.st_valid = 0; path.u_name = p; - if ((opened = of_findname(&path))) { +#ifdef HAVE_RENAMEAT + opened = of_findnameat(sdir_fd, &path); +#else + opened = of_findname(&path); +#endif /* HAVE_RENAMEAT */ + if (opened) { /* reuse struct adouble so it won't break locks */ adp = opened->of_ad; } - } - else { + } else { id = sdir->d_did; /* we already have the CNID */ p = ctoupath( vol, sdir->d_parent, oldname ); if (!p) { @@ -358,12 +369,23 @@ static int moveandrename(const struct vol *vol, struct dir *sdir, char *oldname, } adflags = ADFLAGS_DIR; } + + /* - * p now points to the full pathname of the source fs object. - * - * we are in the dest folder so we need to use p for ad_open - */ - + * p now points to either + * a) full pathname of the source fs object (if renameat is not available) + * b) the oldname (renameat is available) + * we are in the dest folder so we need to use + * a) p for ad_open + * b) fchdir sdir_fd before eg ad_open or use *at functions where appropiate + */ + + if (sdir_fd != -1) { + if ((cwd_fd = open(".", O_RDONLY)) == -1) + return AFPERR_MISC; + if (fchdir(sdir_fd) != 0) + return AFPERR_MISC; + } if (!ad_metadata(p, adflags, adp)) { u_int16_t bshort; @@ -372,6 +394,12 @@ static int moveandrename(const struct vol *vol, struct dir *sdir, char *oldname, if ((bshort & htons(ATTRBIT_NORENAME))) return(AFPERR_OLOCK); } + if (sdir_fd != -1) { + if (fchdir(cwd_fd) != 0) { + LOG(log_error, logtype_afpd, "moveandrename: %s", strerror(errno) ); + return AFPERR_MISC; + } + } if (NULL == (upath = mtoupath(vol, newname, curdir->d_did, utf8_encoding()))){ return AFPERR_PARAM; @@ -403,12 +431,12 @@ static int moveandrename(const struct vol *vol, struct dir *sdir, char *oldname, if (of_findname(&path)) { rc = AFPERR_EXIST; /* was AFPERR_BUSY; */ } else { - rc = renamefile(vol, p, upath, newname, adp ); + rc = renamefile(vol, sdir_fd, p, upath, newname, adp ); if (rc == AFP_OK) of_rename(vol, opened, sdir, oldname, curdir, newname); } } else { - rc = renamedir(vol, p, upath, sdir, curdir, newname); + rc = renamedir(vol, sdir_fd, p, upath, sdir, curdir, newname); } if ( rc == AFP_OK && id ) { /* renaming may have moved the file/dir across a filesystem */ @@ -489,8 +517,7 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size return AFP_OK; /* newname == oldname same dir */ } - rc = moveandrename(vol, sdir, oldname, newname, isdir); - + rc = moveandrename(vol, sdir, -1, oldname, newname, isdir); if ( rc == AFP_OK ) { setvoltime(obj, vol ); } @@ -549,7 +576,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size rc = AFPERR_NOOBJ; } else { - rc = deletefile(vol, upath, 1); + rc = deletefile(vol, -1, upath, 1); } } if ( rc == AFP_OK ) { @@ -623,6 +650,8 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U #ifdef DROPKLUDGE int retvalue; #endif /* DROPKLUDGE */ + int sdir_fd = -1; + *rbuflen = 0; ibuf += 2; @@ -659,40 +688,51 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U if ( *path->m_name != '\0' ) { if (isdir) { sdir = path->d_dir; - } + } strcpy(oldname, path->m_name); /* an extra copy for of_rename */ } else { strcpy(oldname, sdir->d_m_name); } +#ifdef HAVE_RENAMEAT + if ((sdir_fd = open(".", O_RDONLY)) == -1) + return AFPERR_MISC; +#endif + /* get the destination directory */ if (NULL == ( ddir = dirlookup( vol, did )) ) { - return afp_errno; /* was AFPERR_PARAM */ + rc = afp_errno; /* was AFPERR_PARAM */ + goto exit; } if (NULL == ( path = cname( vol, ddir, &ibuf ))) { - return( AFPERR_NOOBJ ); + rc = AFPERR_NOOBJ; + goto exit; } pdid = curdir->d_did; if ( *path->m_name != '\0' ) { - return path_error(path, AFPERR_NOOBJ); + rc = path_error(path, AFPERR_NOOBJ); + goto exit; } /* one more place where we know about path type */ if ((plen = copy_path_name(vol, newname, ibuf)) < 0) { - return( AFPERR_PARAM ); + rc = AFPERR_PARAM; + goto exit; } if (!plen) { strcpy(newname, oldname); } - rc = moveandrename(vol, sdir, oldname, newname, isdir); + /* This does the work */ + rc = moveandrename(vol, sdir, sdir_fd, oldname, newname, isdir); if ( rc == AFP_OK ) { char *upath = mtoupath(vol, newname, pdid, utf8_encoding()); if (NULL == upath) { - return AFPERR_PARAM; + rc = AFPERR_PARAM; + goto exit; } curdir->offcnt++; sdir->offcnt--; @@ -700,7 +740,8 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U if (vol->v_flags & AFPVOL_DROPBOX) { /* FIXME did is not always the source id */ if ((retvalue=matchfile2dirperms (upath, vol, did)) != AFP_OK) { - return retvalue; + rc = retvalue; + goto exit; } } else @@ -715,6 +756,12 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U setvoltime(obj, vol ); } +exit: +#ifdef HAVE_RENAMEAT + if (sdir_fd != -1) + close(sdir_fd); +#endif + return( rc ); } diff --git a/etc/afpd/fork.h b/etc/afpd/fork.h index 36576b39..5a42afc7 100644 --- a/etc/afpd/fork.h +++ b/etc/afpd/fork.h @@ -1,5 +1,5 @@ /* - * $Id: fork.h,v 1.17 2009-11-13 00:27:35 didg Exp $ + * $Id: fork.h,v 1.18 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -75,6 +75,13 @@ extern int of_statdir (struct vol *vol, struct path *); extern int of_closefork (struct ofork *ofork); extern void of_closevol (const struct vol *vol); extern struct adouble *of_ad (const struct vol *, struct path *, struct adouble *); + +#ifdef HAVE_RENAMEAT +extern struct ofork *of_findnameat(int dirfd, struct path *path); +extern int of_fstatat(int dirfd, struct path *path); +#endif /* HAVE_RENAMEAT */ + + /* in fork.c */ extern int flushfork (struct ofork *); extern int getforkmode (struct adouble *, int , int ); diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c index d9c4f31e..49c014cc 100644 --- a/etc/afpd/ofork.c +++ b/etc/afpd/ofork.c @@ -1,5 +1,5 @@ /* - * $Id: ofork.c,v 1.31 2010-02-10 14:05:37 franklahm Exp $ + * $Id: ofork.c,v 1.32 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1996 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -101,11 +101,10 @@ int of_flush(const struct vol *vol) return( 0 ); } -int of_rename( - const struct vol *vol, - struct ofork *s_of, - struct dir *olddir, const char *oldpath _U_, - struct dir *newdir, const char *newpath) +int of_rename(const struct vol *vol, + struct ofork *s_of, + struct dir *olddir, const char *oldpath _U_, + struct dir *newdir, const char *newpath) { struct ofork *of, *next, *d_ofork; int done = 0; @@ -290,6 +289,21 @@ int ret; return ret; } +#ifdef HAVE_RENAMEAT +int of_fstatat(int dirfd, struct path *path) +{ + int ret; + + path->st_errno = 0; + path->st_valid = 1; + + if ((ret = fstatat(dirfd, path->u_name, &path->st, AT_SYMLINK_NOFOLLOW)) < 0) + path->st_errno = errno; + + return ret; +} +#endif /* HAVE_RENAMEAT */ + /* -------------------------- stat the current directory. stat(".") works even if "." is deleted thus @@ -325,8 +339,7 @@ int ret; } /* -------------------------- */ -struct ofork * - of_findname(struct path *path) +struct ofork *of_findname(struct path *path) { struct ofork *of; struct file_key key; @@ -350,6 +363,41 @@ struct ofork * return NULL; } +/*! + * @brief Search for open fork by dirfd/name + * + * Function call of_fstatat with dirfd and path and uses dev and ino + * to search the open fork table. + * + * @param dirfd (r) directory fd + * @param path (rw) pointer to struct path + */ +#ifdef HAVE_RENAMEAT +struct ofork *of_findnameat(int dirfd, struct path *path) +{ + struct ofork *of; + struct file_key key; + + if ( ! path->st_valid) { + of_fstatat(dirfd, path); + } + + if (path->st_errno) + return NULL; + + key.dev = path->st.st_dev; + key.inode = path->st.st_ino; + + for (of = ofork_table[hashfn(&key)]; of; of = of->next) { + if (key.dev == of->key.dev && key.inode == of->key.inode ) { + return of; + } + } + + return NULL; +} +#endif + void of_dealloc( struct ofork *of) { if (!oforks) diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h index fbf53a1c..aaf21227 100644 --- a/include/atalk/adouble.h +++ b/include/atalk/adouble.h @@ -1,5 +1,5 @@ /* - * $Id: adouble.h,v 1.53 2010-02-10 14:05:37 franklahm Exp $ + * $Id: adouble.h,v 1.54 2010-03-12 15:16:49 franklahm Exp $ * Copyright (c) 1990,1991 Regents of The University of Michigan. * All Rights Reserved. * @@ -474,9 +474,11 @@ extern int ad_mode (const char *, int); extern int ad_mkdir (const char *, int); extern void ad_init (struct adouble *, int, int ); extern int ad_open (const char *, int, int, int, struct adouble *); +extern int ad_openat (int dirfd, const char *, int, int, int, struct adouble *); extern int ad_refresh (struct adouble *); extern int ad_stat (const char *, struct stat *); extern int ad_metadata (const char *, int, struct adouble *); +extern int ad_metadataat (int, const char *, int, struct adouble *); #define ad_open_metadata(name, flags, mode, adp)\ ad_open(name, ADFLAGS_MD|(flags), O_RDWR |(mode), 0666, (adp)) diff --git a/include/atalk/ea.h b/include/atalk/ea.h index 48432ef6..00d987de 100644 --- a/include/atalk/ea.h +++ b/include/atalk/ea.h @@ -1,5 +1,5 @@ /* - $Id: ea.h,v 1.10 2009-12-10 17:40:25 franklahm Exp $ + $Id: ea.h,v 1.11 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 @@ -99,6 +99,7 @@ struct ea_entry { struct ea { uint32_t ea_inited; /* needed for interfacing ea_open w. ea_close */ const struct vol *vol; /* vol handle, ea_close needs it */ + int dirfd; /* for *at (cf openat) semantics, -1 means ignore */ char *filename; /* name of file, needed by ea_close too */ unsigned int ea_count; /* number of EAs in ea_entries array */ struct ea_entry (*ea_entries)[]; /* malloced and realloced as needed by ea_count*/ @@ -154,6 +155,11 @@ extern int ea_open(const struct vol * restrict vol, const char * restrict uname, eaflags_t eaflags, struct ea * restrict ea); +extern int ea_openat(const struct vol * restrict vol, + int dirfd, + const char * restrict uname, + eaflags_t eaflags, + struct ea * restrict ea); extern int ea_close(struct ea * restrict ea); extern char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname); diff --git a/include/atalk/unix.h b/include/atalk/unix.h index 697d2a7f..06e3f493 100644 --- a/include/atalk/unix.h +++ b/include/atalk/unix.h @@ -1,5 +1,5 @@ /* - $Id: unix.h,v 1.2 2010-01-26 08:14:09 didg Exp $ + $Id: unix.h,v 1.3 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 @@ -21,20 +21,23 @@ #endif #include +#include /* vfs/unix.c */ extern int netatalk_unlink(const char *name); +extern int netatalk_unlinkat(int dirfd, const char *name); extern char *fullpathname(const char *); +extern int statat(int dirfd, const char *path, struct stat *st); +extern int lstatat(int dirfd, const char *path, struct stat *st); +extern DIR *opendirat(int dirfd, const char *path); /* rmdir ENOENT not an error */ -extern int netatalk_rmdir(const char *name); - -extern int netatalk_rmdir_all_errors(const char *name); +extern int netatalk_rmdir(int dirfd, const char *name); +extern int netatalk_rmdir_all_errors(int dirfd, const char *name); extern int setfilmode(const char *, mode_t, struct stat *, mode_t); extern int dir_rx_set(mode_t mode); extern int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask); -extern int unix_rename(const char *oldpath, const char *newpath); -extern int copy_file(const char *src, const char *dst, mode_t mode); - +extern int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath); +extern int copy_file(int sfd, const char *src, const char *dst, mode_t mode); #endif /* ATALK_UNIX_H */ diff --git a/include/atalk/vfs.h b/include/atalk/vfs.h index a252909d..de1c008c 100644 --- a/include/atalk/vfs.h +++ b/include/atalk/vfs.h @@ -34,8 +34,8 @@ #define VFS_FUNC_ARGS_CHOWN const struct vol *vol, const char *path, uid_t uid, gid_t gid #define VFS_FUNC_VARS_CHOWN vol, path, uid, gid -#define VFS_FUNC_ARGS_RENAMEDIR const struct vol *vol, const char *oldpath, const char *newpath -#define VFS_FUNC_VARS_RENAMEDIR vol, oldpath, newpath +#define VFS_FUNC_ARGS_RENAMEDIR const struct vol *vol, int dirfd, const char *oldpath, const char *newpath +#define VFS_FUNC_VARS_RENAMEDIR vol, dirfd, oldpath, newpath #define VFS_FUNC_ARGS_DELETECURDIR const struct vol *vol #define VFS_FUNC_VARS_DELETECURDIR vol @@ -52,14 +52,14 @@ #define VFS_FUNC_ARGS_SETDIROWNER const struct vol *vol, const char *name, uid_t uid, gid_t gid #define VFS_FUNC_VARS_SETDIROWNER vol, name, uid, gid -#define VFS_FUNC_ARGS_DELETEFILE const struct vol *vol, const char *file -#define VFS_FUNC_VARS_DELETEFILE vol, file +#define VFS_FUNC_ARGS_DELETEFILE const struct vol *vol, int dirfd, const char *file +#define VFS_FUNC_VARS_DELETEFILE vol, dirfd, file -#define VFS_FUNC_ARGS_RENAMEFILE const struct vol *vol, const char *src, const char *dst -#define VFS_FUNC_VARS_RENAMEFILE vol, src, dst +#define VFS_FUNC_ARGS_RENAMEFILE const struct vol *vol, int dirfd, const char *src, const char *dst +#define VFS_FUNC_VARS_RENAMEFILE vol, dirfd, src, dst -#define VFS_FUNC_ARGS_COPYFILE const struct vol *vol, const char *src, const char *dst -#define VFS_FUNC_VARS_COPYFILE vol, src, dst +#define VFS_FUNC_ARGS_COPYFILE const struct vol *vol, int sfd, const char *src, const char *dst +#define VFS_FUNC_VARS_COPYFILE vol, sfd, src, dst #define VFS_FUNC_ARGS_ACL const struct vol *vol, const char *path, int cmd, int count, void *aces #define VFS_FUNC_VARS_ACL vol, path, cmd, count, aces diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index 6e9dc85c..b4e7c3a5 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -1,5 +1,5 @@ /* - * $Id: ad_open.c,v 1.71 2010-03-07 18:27:59 didg Exp $ + * $Id: ad_open.c,v 1.72 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) * Copyright (c) 1990,1991 Regents of The University of Michigan. @@ -1586,6 +1586,41 @@ int ad_metadata(const char *name, int flags, struct adouble *adp) return ret; } +/* + * @brief openat like wrapper for ad_metadata + */ +int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp) +{ + int ret = 0; + int cwdfd = -1; + + if (dirfd != -1) { + if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) { + ret = -1; + goto exit; + } + } + + if (ad_metadata(name, flags, adp) < 0) { + ret = -1; + goto exit; + } + + if (dirfd != -1) { + if (fchdir(cwdfd) != 0) { + LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting"); + exit(EXITERR_SYS); + } + } + +exit: + if (cwdfd != -1) + close(cwdfd); + + return ret; + +} + /* ----------------------------------- */ static int new_rfork(const char *path, struct adouble *ad, int adflags) { @@ -1665,3 +1700,39 @@ int ad_refresh(struct adouble *ad) return ad->ad_ops->ad_header_read(ad, NULL); } + +int ad_openat(int dirfd, /* dir fd openat like */ + const char *path, + int adflags, + int oflags, + int mode, + struct adouble *ad) +{ + int ret = 0; + int cwdfd = -1; + + if (dirfd != -1) { + if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) { + ret = -1; + goto exit; + } + } + + if (ad_open(path, adflags, oflags, mode, ad) < 0) { + ret = -1; + goto exit; + } + + if (dirfd != -1) { + if (fchdir(cwdfd) != 0) { + LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting"); + exit(EXITERR_SYS); + } + } + +exit: + if (cwdfd != -1) + close(cwdfd); + + return ret; +} diff --git a/libatalk/vfs/ea.c b/libatalk/vfs/ea.c index 70d28106..f8e85acf 100644 --- a/libatalk/vfs/ea.c +++ b/libatalk/vfs/ea.c @@ -1,5 +1,5 @@ /* - $Id: ea.c,v 1.19 2010-02-10 14:05:37 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 @@ -661,6 +661,8 @@ 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; @@ -786,6 +788,68 @@ exit: 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; + +} + /* * Function: ea_close * @@ -825,8 +889,8 @@ int ea_close(struct ea * restrict ea) if (ea->ea_count == 0) { /* Check if EA header exists and remove it */ eaname = ea_path(ea, NULL, 0); - if ((stat(eaname, &st)) == 0) { - if ((unlink(eaname)) != 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; @@ -1244,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; @@ -1259,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; @@ -1272,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; } @@ -1294,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; @@ -1359,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; @@ -1391,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; @@ -1447,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; diff --git a/libatalk/vfs/ea_sys.c b/libatalk/vfs/ea_sys.c index 02a407cf..07945856 100644 --- a/libatalk/vfs/ea_sys.c +++ b/libatalk/vfs/ea_sys.c @@ -1,5 +1,5 @@ /* - $Id: ea_sys.c,v 1.5 2010-01-23 14:54:43 franklahm Exp $ + $Id: ea_sys.c,v 1.6 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 @@ -380,16 +380,37 @@ int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE) return AFP_OK; } -/* --------------------- - copy EA -*/ +/* + * @brief Copy EAs + * + * @note Supports *at semantics, therfor switches back and forth between sfd and cwd + */ int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE) { int ret = 0; + int cwd = -1; ssize_t size; char *names = NULL, *end_names, *name, *value = NULL; unsigned int setxattr_ENOTSUP = 0; + if (sfd != -1) { + if ((cwd = open(".", O_RDONLY)) == -1) { + LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s", + strerror(errno)); + ret = -1; + goto getout; + } + } + + if (sfd != -1) { + if (fchdir(sfd) == -1) { + LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s", + strerror(errno)); + ret = -1; + goto getout; + } + } + size = sys_listxattr(src, NULL, 0); if (size < 0) { if (errno != ENOSYS && errno != ENOTSUP) { @@ -411,6 +432,15 @@ int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE) end_names = names + size; } + if (sfd != -1) { + if (fchdir(cwd) == -1) { + LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s", + strerror(errno)); + ret = -1; + goto getout; + } + } + for (name = names; name != end_names; name = strchr(name, '\0') + 1) { void *old_value; @@ -418,6 +448,15 @@ int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE) if (!*name) continue; + if (sfd != -1) { + if (fchdir(sfd) == -1) { + LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s", + strerror(errno)); + ret = -1; + goto getout; + } + } + size = sys_getxattr (src, name, NULL, 0); if (size < 0) { ret = -1; @@ -433,6 +472,16 @@ int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE) ret = -1; continue; } + + if (sfd != -1) { + if (fchdir(cwd) == -1) { + LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s", + strerror(errno)); + ret = -1; + goto getout; + } + } + if (sys_setxattr(dst, name, value, size, 0) != 0) { if (errno == ENOTSUP) setxattr_ENOTSUP++; @@ -453,6 +502,9 @@ int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE) } getout: + if (cwd != -1) + close(cwd); + free(value); free(names); diff --git a/libatalk/vfs/unix.c b/libatalk/vfs/unix.c index 991d04a0..d0ea3128 100644 --- a/libatalk/vfs/unix.c +++ b/libatalk/vfs/unix.c @@ -1,5 +1,5 @@ /* - * $Id: unix.c,v 1.9 2010-02-10 14:05:37 franklahm Exp $ + * $Id: unix.c,v 1.10 2010-03-12 15:16:49 franklahm Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -99,12 +99,24 @@ int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask) return 0; } -/* ------------------- - system rmdir with afp error code. -*/ -int netatalk_rmdir_all_errors(const char *name) +/* + * @brief system rmdir with afp error code. + * + * Supports *at semantics (cf openat) if HAVE_RENAMEAT. Pass dirfd=-1 to ignore this. + */ +int netatalk_rmdir_all_errors(int dirfd, const char *name) { - if (rmdir(name) < 0) { + int err; + +#ifdef HAVE_RENAMEAT + if (dirfd == -1) + dirfd = ATFD_CWD; + err = unlinkat(dirfd, name, AT_REMOVEDIR); +#else + err = rmdir(name); +#endif + + if (err < 0) { switch ( errno ) { case ENOENT : return AFPERR_NOOBJ; @@ -122,13 +134,14 @@ int netatalk_rmdir_all_errors(const char *name) return AFP_OK; } -/* ------------------- - system rmdir with afp error code. - ENOENT is not an error. -*/ -int netatalk_rmdir(const char *name) +/* + * @brief System rmdir with afp error code, but ENOENT is not an error. + * + * Supports *at semantics (cf openat) if HAVE_RENAMEAT. Pass dirfd=-1 to ignore this. + */ +int netatalk_rmdir(int dirfd, const char *name) { - int ret = netatalk_rmdir_all_errors(name); + int ret = netatalk_rmdir_all_errors(dirfd, name); if (ret == AFPERR_NOOBJ) return AFP_OK; return ret; @@ -170,7 +183,15 @@ char *fullpathname(const char *name) return wd; } -int copy_file(const char *src, const char *dst, mode_t mode) + +/************************************************************************** + * *at semnatics support functions (like openat, renameat standard funcs) + **************************************************************************/ + +/* + * Supports *at semantics if HAVE_RENAMEAT, pass dirfd=-1 to ignore this + */ +int copy_file(int dirfd, const char *src, const char *dst, mode_t mode) { int ret = 0; int sfd = -1; @@ -179,7 +200,14 @@ int copy_file(const char *src, const char *dst, mode_t mode) size_t buflen; char filebuf[8192]; - if ((sfd = open(src, O_RDONLY)) < 0) { +#ifdef HAVE_RENAMEAT + if (dirfd == -1) + dirfd = ATFD_CWD; + sfd = openat(dirfd, src, O_RDONLY); +#else + sfd = open(src, O_RDONLY); +#endif + if (sfd < 0) { LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s", src, dst, src, strerror(errno)); return -1; @@ -235,44 +263,142 @@ exit: return ret; } -/* This is equivalent of unix rename(). */ -int unix_rename(const char *oldpath, const char *newpath) +/* + * at wrapper for netatalk_unlink + */ +int netatalk_unlinkat(int dirfd, const char *name) { -#if 0 - char pd_name[PATH_MAX+1]; - int i; - struct stat pd_stat; - uid_t uid; +#ifdef HAVE_RENAMEAT + if (dirfd == -1) + dirfd = AT_FDCWD; + + if (unlinkat(dirfd, name, 0) < 0) { + switch (errno) { + case ENOENT : + break; + case EROFS: + return AFPERR_VLOCK; + case EPERM: + case EACCES : + return AFPERR_ACCESS; + default : + return AFPERR_PARAM; + } + } + return AFP_OK; +#else + return netatalk_unlink(name); #endif + /* DEADC0DE */ + return 0; +} + +/* + * @brief This is equivalent of unix rename() + * + * unix_rename mulitplexes rename and renameat. If we dont HAVE_RENAMEAT, sfd and dfd + * are ignored. + * + * @param sfd (r) if we HAVE_RENAMEAT, -1 gives AT_FDCWD + * @param oldpath (r) guess what + * @param dfd (r) same as sfd + * @param newpath (r) guess what + */ +int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath) +{ +#ifdef HAVE_RENAMEAT + if (sfd == -1) + sfd = AT_FDCWD; + if (dfd == -1) + dfd = AT_FDCWD; + + if (renameat(sfd, oldpath, dfd, newpath) < 0) + return -1; +#else if (rename(oldpath, newpath) < 0) return -1; -#if 0 - for (i = 0; i <= PATH_MAX && newpath[i] != '\0'; i++) - pd_name[i] = newpath[i]; - pd_name[i] = '\0'; +#endif /* HAVE_RENAMEAT */ - while (i > 0 && pd_name[i] != '/') i--; - if (pd_name[i] == '/') i++; + return 0; +} - pd_name[i++] = '.'; pd_name[i++] = '\0'; +/* + * @brief stat/fsstatat multiplexer + * + * statat mulitplexes stat and fstatat. If we dont HAVE_RENAMEAT, dirfd is ignored. + * + * @param dirfd (r) Only used if HAVE_RENAMEAT, ignored else, -1 gives AT_FDCWD + * @param path (r) pathname + * @param st (rw) pointer to struct stat + */ +int statat(int dirfd, const char *path, struct stat *st) +{ +#ifdef HAVE_RENAMEAT + if (dirfd == -1) + dirfd = AT_FDCWD; + return (fstatat(dirfd, path, st, 0)); +#else + return (stat(path, st)); +#endif + + /* DEADC0DE */ + return -1; +} - if (stat(pd_name, &pd_stat) < 0) { - LOG(log_error, logtype_afpd, "stat() of parent dir failed: pd_name = %s, uid = %d: %s", - pd_name, geteuid(), strerror(errno)); - return 0; +/* + * @brief lstat/fsstatat multiplexer + * + * lstatat mulitplexes lstat and fstatat. If we dont HAVE_RENAMEAT, dirfd is ignored. + * + * @param dirfd (r) Only used if HAVE_RENAMEAT, ignored else, -1 gives AT_FDCWD + * @param path (r) pathname + * @param st (rw) pointer to struct stat + */ +int lstatat(int dirfd, const char *path, struct stat *st) +{ +#ifdef HAVE_RENAMEAT + if (dirfd == -1) + dirfd = AT_FDCWD; + return (fstatat(dirfd, path, st, AT_SYMLINK_NOFOLLOW)); +#else + return (lstat(path, st)); +#endif + + /* DEADC0DE */ + return -1; +} + +/* + * @brief opendir wrapper for *at semantics support + * + * opendirat chdirs to dirfd if dirfd != -1 before calling opendir on path. + * + * @param dirfd (r) if != -1, chdir(dirfd) before opendir(path) + * @param path (r) pathname + */ +DIR *opendirat(int dirfd, const char *path) +{ + DIR *ret; + int cwd = -1; + + if (dirfd != -1) { + if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { + ret = NULL; + goto exit; + } } - /* So we have SGID bit set... */ - if ((S_ISGID & pd_stat.st_mode) != 0) { - uid = geteuid(); - if (seteuid(0) < 0) - LOG(log_error, logtype_afpd, "seteuid() failed: %s", strerror(errno)); - if (recursive_chown(newpath, uid, pd_stat.st_gid) < 0) - LOG(log_error, logtype_afpd, "chown() of parent dir failed: newpath=%s, uid=%d: %s", - pd_name, geteuid(), strerror(errno)); - seteuid(uid); + ret = opendir(path); + + if (dirfd != -1 && fchdir(cwd) != 0) { + LOG(log_error, logtype_afpd, "opendirat: cant chdir back. exit!"); + exit(EXITERR_SYS); } -#endif - return 0; + +exit: + if (cwd != -1) + close(cwd); + + return ret; } diff --git a/libatalk/vfs/vfs.c b/libatalk/vfs/vfs.c index b43cfebe..dadedc5b 100644 --- a/libatalk/vfs/vfs.c +++ b/libatalk/vfs/vfs.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -148,7 +149,7 @@ static int RF_deletecurdir_adouble(VFS_FUNC_ARGS_DELETECURDIR) as well. */ if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1, vol->v_umask))) return err; - return netatalk_rmdir( ".AppleDouble" ); + return netatalk_rmdir(-1, ".AppleDouble" ); } /* ----------------- */ @@ -272,7 +273,7 @@ static int RF_setdirowner_adouble(VFS_FUNC_ARGS_SETDIROWNER) /* ----------------- */ static int RF_deletefile_adouble(VFS_FUNC_ARGS_DELETEFILE) { - return netatalk_unlink(vol->ad_path(file, ADFLAGS_HF)); + return netatalk_unlinkat(dirfd, vol->ad_path(file, ADFLAGS_HF)); } /* ----------------- */ @@ -282,16 +283,16 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE) int err = 0; strcpy( adsrc, vol->ad_path(src, 0 )); - if (unix_rename( adsrc, vol->ad_path(dst, 0 )) < 0) { + if (unix_rename(dirfd, adsrc, -1, vol->ad_path(dst, 0 )) < 0) { struct stat st; err = errno; if (errno == ENOENT) { struct adouble ad; - if (stat(adsrc, &st)) /* source has no ressource fork, */ + if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */ return 0; - + /* We are here because : * -there's no dest folder. * -there's no .AppleDouble in the dest folder. @@ -302,7 +303,7 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE) ad_init(&ad, vol->v_adouble, vol->v_ad_options); if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) { ad_close(&ad, ADFLAGS_HF); - if (!unix_rename( adsrc, vol->ad_path(dst, 0 )) ) + if (!unix_rename(dirfd, adsrc, -1, vol->ad_path(dst, 0 )) ) err = 0; else err = errno; @@ -426,7 +427,7 @@ static int ads_delete_rf(char *name) */ if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0))) return err; - return netatalk_rmdir(name); + return netatalk_rmdir(-1, name); } static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_) @@ -445,11 +446,12 @@ static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data _U_, static int RF_deletecurdir_ads(VFS_FUNC_ARGS_DELETECURDIR) { int err; - + /* delete stray .AppleDouble files. this happens to get .Parent files as well. */ if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1, 0))) return err; - return netatalk_rmdir( ".AppleDouble" ); + + return netatalk_rmdir(-1, ".AppleDouble" ); } /* ------------------- */ @@ -658,9 +660,31 @@ static int RF_setdirowner_ads(VFS_FUNC_ARGS_SETDIROWNER) /* ------------------- */ static int RF_deletefile_ads(VFS_FUNC_ARGS_DELETEFILE) { - char *ad_p = ad_dir(vol->ad_path(file, ADFLAGS_HF )); + int ret = 0; + int cwd = -1; + char *ad_p; + + ad_p = ad_dir(vol->ad_path(file, ADFLAGS_HF )); + + if (dirfd != -1) { + if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { + ret = AFPERR_MISC; + goto exit; + } + } - return ads_delete_rf(ad_p); + ret = ads_delete_rf(ad_p); + + if (dirfd != -1 && fchdir(cwd) != 0) { + LOG(log_error, logtype_afpd, "RF_deletefile_ads: cant chdir back. exit!"); + exit(EXITERR_SYS); + } + +exit: + if (cwd != -1) + close(cwd); + + return ret; } /* --------------------------- */ @@ -670,14 +694,14 @@ static int RF_renamefile_ads(VFS_FUNC_ARGS_RENAMEFILE) int err = 0; strcpy( adsrc, ad_dir(vol->ad_path(src, 0 ))); - if (unix_rename( adsrc, ad_dir(vol->ad_path(dst, 0 ))) < 0) { + if (unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) < 0) { struct stat st; err = errno; if (errno == ENOENT) { struct adouble ad; - if (stat(adsrc, &st)) /* source has no ressource fork, */ + if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */ return 0; /* We are here because : @@ -692,8 +716,8 @@ static int RF_renamefile_ads(VFS_FUNC_ARGS_RENAMEFILE) ad_close(&ad, ADFLAGS_HF); /* We must delete it */ - RF_deletefile_ads(vol, dst ); - if (!unix_rename( adsrc, ad_dir(vol->ad_path(dst, 0 ))) ) + RF_deletefile_ads(vol, -1, dst ); + if (!unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) ) err = 0; else err = errno; @@ -724,7 +748,7 @@ static int RF_renamedir_osx(VFS_FUNC_ARGS_RENAMEDIR) { /* We simply move the corresponding ad file as well */ char tempbuf[258]="._"; - return rename(vol->ad_path(oldpath,0),strcat(tempbuf,newpath)); + return unix_rename(dirfd, vol->ad_path(oldpath,0), -1, strcat(tempbuf,newpath)); } /* ---------------- */ @@ -759,11 +783,11 @@ static int RF_renamefile_osx(VFS_FUNC_ARGS_RENAMEFILE) strcpy( adsrc, vol->ad_path(src, 0 )); - if (unix_rename( adsrc, vol->ad_path(dst, 0 )) < 0) { + if (unix_rename(dirfd, adsrc, -1, vol->ad_path(dst, 0 )) < 0) { struct stat st; err = errno; - if (errno == ENOENT && stat(adsrc, &st)) /* source has no ressource fork, */ + if (errno == ENOENT && lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */ return 0; errno = err; return -1; @@ -868,6 +892,7 @@ static struct vfs_ops netatalk_adouble = { /* vfs_setdirowner: */ RF_setdirowner_adouble, /* vfs_deletefile: */ RF_deletefile_adouble, /* vfs_renamefile: */ RF_renamefile_adouble, + /* vfs_copyfile: */ NULL, NULL }; @@ -882,6 +907,7 @@ static struct vfs_ops netatalk_adouble_osx = { /* vfs_setdirowner: */ RF_setdirowner_osx, /* vfs_deletefile: */ RF_deletefile_adouble, /* vfs_renamefile: */ RF_renamefile_osx, + /* vfs_copyfile: */ NULL, NULL }; @@ -897,6 +923,7 @@ static struct vfs_ops netatalk_adouble_sfm = { /* vfs_setdirowner: */ RF_setdirowner_ads, /* vfs_deletefile: */ RF_deletefile_ads, /* vfs_renamefile: */ RF_renamefile_ads, + /* vfs_copyfile: */ NULL, NULL }; -- 2.39.2