From 00ab46135eb7373f41380e60c8054282fc7965ff Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Fri, 20 Jan 2012 17:15:54 +0100 Subject: [PATCH] adouble:ea VFS stuff --- include/atalk/unix.h | 5 + libatalk/adouble/ad_flush.c | 26 ++++-- libatalk/adouble/ad_open.c | 5 +- libatalk/vfs/unix.c | 97 ++++++++++++++----- libatalk/vfs/vfs.c | 181 +++++++++++++++++++++++++++--------- 5 files changed, 238 insertions(+), 76 deletions(-) diff --git a/include/atalk/unix.h b/include/atalk/unix.h index f6d191ca..c7a2bc38 100644 --- a/include/atalk/unix.h +++ b/include/atalk/unix.h @@ -23,6 +23,9 @@ #include #include +#define NETATALK_DIOSZ_STACK 65536 +#define NETATALK_DIOSZ_HEAP (1024*1024) + /* vfs/unix.c */ extern int netatalk_unlink(const char *name); extern int netatalk_unlinkat(int dirfd, const char *name); @@ -39,4 +42,6 @@ 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(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); +extern int copy_file_fd(int sfd, int dfd); +extern int copy_ea(const char *ea, int sfd, const char *src, const char *dst, mode_t mode); #endif /* ATALK_UNIX_H */ diff --git a/libatalk/adouble/ad_flush.c b/libatalk/adouble/ad_flush.c index e0321a39..83febd0e 100644 --- a/libatalk/adouble/ad_flush.c +++ b/libatalk/adouble/ad_flush.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "ad_lock.h" @@ -60,6 +61,8 @@ int ad_rebuild_adouble_header(struct adouble *ad) char *buf, *nentp; int len; + LOG(log_debug, logtype_default, "ad_rebuild_adouble_header"); + buf = ad->ad_data; temp = htonl( ad->ad_magic ); @@ -206,7 +209,9 @@ int ad_copy_header(struct adouble *add, struct adouble *ads) static int ad_flush_hf(struct adouble *ad) { + EC_INIT; int len; + int cwd = -1; LOG(log_debug, logtype_default, "ad_flush_hf(%s)", adflags2logstr(ad->ad_adflags)); @@ -224,7 +229,8 @@ static int ad_flush_hf(struct adouble *ad) return -1; } - if ((adf->adf_flags & O_RDWR)) { + if ((adf->adf_flags & O_RDWR) + || ((ad->ad_adflags & ADFLAGS_DIR) && (ad->ad_adflags & ADFLAGS_RDWR))) { if (ad_getentryoff(ad, ADEID_RFORK)) { if (ad->ad_rlen > 0xffffffff) ad_setentrylen(ad, ADEID_RFORK, 0xffffffff); @@ -243,10 +249,11 @@ static int ad_flush_hf(struct adouble *ad) break; case AD_VERSION_EA: if (AD_META_OPEN(ad)) { - if (sys_fsetxattr(ad_data_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) != 0) { - LOG(log_error, logtype_afpd, "ad_flush: sys_fsetxattr error: %s", - strerror(errno)); - return -1; + if (ad->ad_adflags & ADFLAGS_DIR) { + EC_NEG1_LOG( cwd = open(".", O_RDONLY) ); + EC_ZERO_LOG( sys_lsetxattr(".", AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) ); + } else { + EC_ZERO_LOG( sys_fsetxattr(ad_data_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) ); } } break; @@ -256,7 +263,14 @@ static int ad_flush_hf(struct adouble *ad) } } - return( 0 ); +EC_CLEANUP: + if (cwd != -1) { + if (fchdir(cwd) != 0) { + AFP_PANIC("ad_flush: cant fchdir"); + } + close(cwd); + } + EC_EXIT; } /* Flush resofork adouble file if any (currently adouble:ea and #ifndef HAVE_EAFD eg Linux) */ diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index d3a4c6b3..71a495d5 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -944,7 +944,10 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble } else { if (adflags & ADFLAGS_RDWR) { /* Fo a RDONLY adouble we just use sys_lgetxattr instead if sys_fgetxattr */ - LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): opening for base file for meta adouble EA", path); + if (adflags & ADFLAGS_DIR) + /* For directories we open the directory RDONYL so we can later fchdir() */ + oflags = (oflags & ~O_RDWR) | O_RDONLY; + LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): opening base file for meta adouble EA", path); if ((ad_meta_fileno(ad) = open(path, oflags)) == -1) goto error; ad->ad_mdp->adf_flags = oflags; diff --git a/libatalk/vfs/unix.c b/libatalk/vfs/unix.c index 14a0f164..659cd411 100644 --- a/libatalk/vfs/unix.c +++ b/libatalk/vfs/unix.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include /* ----------------------------- a dropbox is a folder where w is set but not r eg: @@ -150,6 +152,38 @@ int netatalk_unlink(const char *name) * *at semnatics support functions (like openat, renameat standard funcs) **************************************************************************/ +/* Copy all file data from one file fd to another */ +int copy_file_fd(int sfd, int dfd) +{ + EC_INIT; + ssize_t cc; + size_t buflen; + char filebuf[NETATALK_DIOSZ_STACK]; + + while ((cc = read(sfd, filebuf, sizeof(filebuf)))) { + if (cc < 0) { + if (errno == EINTR) + continue; + LOG(log_error, logtype_afpd, "copy_file_fd: %s", strerror(errno)); + EC_FAIL; + } + + buflen = cc; + while (buflen > 0) { + if ((cc = write(dfd, filebuf, buflen)) < 0) { + if (errno == EINTR) + continue; + LOG(log_error, logtype_afpd, "copy_file_fd: %s", strerror(errno)); + EC_FAIL; + } + buflen -= cc; + } + } + +EC_CLEANUP: + EC_EXIT; +} + /* * Supports *at semantics if HAVE_ATFUNCS, pass dirfd=-1 to ignore this */ @@ -160,7 +194,7 @@ int copy_file(int dirfd, const char *src, const char *dst, mode_t mode) int dfd = -1; ssize_t cc; size_t buflen; - char filebuf[8192]; + char filebuf[NETATALK_DIOSZ_STACK]; #ifdef HAVE_ATFUNCS if (dirfd == -1) @@ -182,29 +216,7 @@ int copy_file(int dirfd, const char *src, const char *dst, mode_t mode) goto exit; } - while ((cc = read(sfd, filebuf, sizeof(filebuf)))) { - if (cc < 0) { - if (errno == EINTR) - continue; - LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s", - src, dst, src, strerror(errno)); - ret = -1; - goto exit; - } - - buflen = cc; - while (buflen > 0) { - if ((cc = write(dfd, filebuf, buflen)) < 0) { - if (errno == EINTR) - continue; - LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s", - src, dst, dst, strerror(errno)); - ret = -1; - goto exit; - } - buflen -= cc; - } - } + ret = copy_file_fd(sfd, dfd); exit: if (sfd != -1) @@ -225,6 +237,43 @@ exit: return ret; } +/*! + * Copy an EA from one file to another + * + * Supports *at semantics if HAVE_ATFUNCS, pass dirfd=-1 to ignore this + */ +int copy_ea(const char *ea, int dirfd, const char *src, const char *dst, mode_t mode) +{ + EC_INIT; + int sfd = -1; + int dfd = -1; + size_t easize; + char *eabuf = NULL; + +#ifdef HAVE_ATFUNCS + if (dirfd == -1) + dirfd = AT_FDCWD; + EC_NEG1_LOG( sfd = openat(dirfd, src, O_RDONLY) ); +#else + EC_NEG1_LOG( sfd = open(src, O_RDONLY) ); +#endif + EC_NEG1_LOG( dfd = open(dst, O_WRONLY, mode) ); + + if ((easize = sys_fgetxattr(sfd, ea, NULL, 0)) > 0) { + EC_NULL_LOG( eabuf = malloc(easize)); + EC_NEG1_LOG( easize = sys_fgetxattr(sfd, ea, eabuf, easize) ); + EC_NEG1_LOG( easize = sys_fsetxattr(dfd, ea, eabuf, easize, 0) ); + } + +EC_CLEANUP: + if (sfd != -1) + close(sfd); + if (dfd != -1) + close(dfd); + free(eabuf); + EC_EXIT; +} + /* * at wrapper for netatalk_unlink */ diff --git a/libatalk/vfs/vfs.c b/libatalk/vfs/vfs.c index 75f461ef..04400092 100644 --- a/libatalk/vfs/vfs.c +++ b/libatalk/vfs/vfs.c @@ -140,7 +140,7 @@ static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _ /* bail if the file exists in the current directory. * note: this will not fail with dangling symlinks */ - if (stat(de->d_name, &st) == 0) + if (lstat(de->d_name, &st) == 0) return AFPERR_DIRNEMPT; if ((err = netatalk_unlink(name))) @@ -515,6 +515,9 @@ static int validupath_ea(VFS_FUNC_ARGS_VALIDUPATH) /* ----------------- */ static int RF_chown_ea(VFS_FUNC_ARGS_CHOWN) { +#ifndef HAVE_EAFD + return chown(vol->ad_path(path, ADFLAGS_HF ), uid, gid); +#endif return 0; } @@ -524,95 +527,182 @@ static int RF_renamedir_ea(VFS_FUNC_ARGS_RENAMEDIR) return 0; } +/* Returns 1 if the entry is NOT an ._ file */ +static int deletecurdir_ea_osx_chkifempty_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_) +{ + if (de->d_name[0] != '.' || de->d_name[0] == '_') + return 1; + + return 0; +} + +static int deletecurdir_ea_osx_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_) +{ + int ret; + + if ((ret = netatalk_unlink(name)) != 0) + return ret; + + return 0; +} + /* ---------------- */ static int RF_deletecurdir_ea(VFS_FUNC_ARGS_DELETECURDIR) { +#ifndef HAVE_EAFD + int err; + /* delete stray ._AppleDouble files */ + + /* first check if there's really no other file besides files starting with ._ */ + if ((err = for_each_adouble("deletecurdir_ea_osx", ".", + deletecurdir_ea_osx_chkifempty_loop, + NULL, 0, 0)) != 0) { + if (err == 1) + return AFPERR_DIRNEMPT; + return AFPERR_MISC; + } + + /* Now delete orphaned ._ files */ + if ((err = for_each_adouble("deletecurdir_ea_osx", ".", + deletecurdir_ea_osx_loop, + NULL, 0, 0)) != 0) + return err; + +#endif return 0; } /* ---------------- */ static int RF_setdirunixmode_ea(VFS_FUNC_ARGS_SETDIRUNIXMODE) { +#ifndef HAVE_EAFD +#endif return 0; } static int RF_setfilmode_ea(VFS_FUNC_ARGS_SETFILEMODE) { +#ifndef HAVE_EAFD + return adouble_setfilmode(vol->ad_path(name, ADFLAGS_HF ), mode, st, vol->v_umask); +#endif return 0; } /* ---------------- */ static int RF_setdirmode_ea(VFS_FUNC_ARGS_SETDIRMODE) { +#ifndef HAVE_EAFD +#endif return 0; } /* ---------------- */ static int RF_setdirowner_ea(VFS_FUNC_ARGS_SETDIROWNER) { +#ifndef HAVE_EAFD +#endif return 0; } static int RF_deletefile_ea(VFS_FUNC_ARGS_DELETEFILE) { +#ifndef HAVE_EAFD + return netatalk_unlinkat(dirfd, vol->ad_path(file, ADFLAGS_HF)); +#endif return 0; } static int RF_copyfile_ea(VFS_FUNC_ARGS_COPYFILE) { - return 0; -} + EC_INIT; -/* ---------------- */ -static int RF_renamefile_ea(VFS_FUNC_ARGS_RENAMEFILE) -{ - return 0; -} + /* copy meta EA */ + if (copy_ea(AD_EA_META, sfd, src, dst, 0666) != 0) + return AFPERR_MISC; -#if 0 -/************************************************************************* - * osx adouble format - ************************************************************************/ -static int validupath_osx(VFS_FUNC_ARGS_VALIDUPATH) -{ - return strncmp(name,"._", 2) && ( - (vol->v_flags & AFPVOL_USEDOTS) ? netatalk_name(name): name[0] != '.'); -} + /* copy reso */ +#ifdef HAVE_EAFD + int sfile = -1, dfile = -1, sea = -1, dea = -1; -/* ---------------- */ -static int RF_renamedir_osx(VFS_FUNC_ARGS_RENAMEDIR) -{ - /* We simply move the corresponding ad file as well */ - char tempbuf[258]="._"; - return unix_rename(dirfd, vol->ad_path(oldpath,0), -1, strcat(tempbuf,newpath)); -} + if ((sfile = openat(sfd, src, O_RDONLY)) == -1) { + ret = AFPERR_MISC; + goto copyresoerr; + } -/* ---------------- */ -static int RF_deletecurdir_osx(VFS_FUNC_ARGS_DELETECURDIR) -{ - return netatalk_unlink( vol->ad_path(".",0) ); -} + if ((dfile = open(dts, O_WRONLY)) == -1) { + ret = AFPERR_MISC; + goto copyresoerr; + } -/* ---------------- */ -static int RF_setdirunixmode_osx(VFS_FUNC_ARGS_SETDIRUNIXMODE) -{ - return adouble_setfilmode(vol->ad_path(name, ADFLAGS_DIR ), mode, st, vol->v_umask); -} + if ((sea = openat(sfile, AD_EA_RESO, O_RDONLY | O_XATTR)) == -1) { + ret = AFPERR_MISC; + goto copyresoerr; + } -/* ---------------- */ -static int RF_setdirmode_osx(VFS_FUNC_ARGS_SETDIRMODE) -{ - return 0; -} + if ((dea = openat(dfile, AD_EA_RESO, O_RDWR | O_CREAT | O_XATTR)) == -1) { + ret = AFPERR_MISC; + goto copyresoerr; + } -/* ---------------- */ -static int RF_setdirowner_osx(VFS_FUNC_ARGS_SETDIROWNER) -{ - return 0; + ret = copy_file_fd(sea, dea); + +copyresoerr: + if (sfile != -1) close(sfile); + if (dfile != -1) close(dfile); + if (sea != -1) close(sea); + if (dea != -1) close(dea); + if (ret != 0) + return ret; + +EC_CLEANUP: +#else + bstring s = NULL, d = NULL; + char *dup1 = NULL; + char *dup2 = NULL; + char *dup3 = NULL; + char *dup4 = NULL; + const char *name = NULL; + const char *dir = NULL; + + /* get basename */ + + /* build src path to ._ file*/ + EC_NULL(dup1 = strdup(src)); + EC_NULL(name = basename(strdup(dup1))); + + EC_NULL(dup2 = strdup(src)); + EC_NULL(dir = dirname(dup2)); + EC_NULL(s = bfromcstr(dir)); + EC_ZERO(bcatcstr(s, "/._")); + EC_ZERO(bcatcstr(s, name)); + + /* build dst path to ._file*/ + EC_NULL(dup4 = strdup(dst)); + EC_NULL(name = basename(strdup(dup4))); + + EC_NULL(dup3 = strdup(dst)); + EC_NULL(dir = dirname(dup3)); + EC_NULL(d = bfromcstr(dir)); + EC_ZERO(bcatcstr(d, "/._")); + EC_ZERO(bcatcstr(d, name)); + + EC_ZERO(copy_file(sfd, cfrombstr(s), cfrombstr(d), 0666)); + +EC_CLEANUP: + bdestroy(s); + bdestroy(d); + free(dup1); + free(dup2); + free(dup3); + free(dup4); +#endif +out: + EC_EXIT; } /* ---------------- */ -static int RF_renamefile_osx(VFS_FUNC_ARGS_RENAMEFILE) +static int RF_renamefile_ea(VFS_FUNC_ARGS_RENAMEFILE) { +#ifndef HAVE_EAFD char adsrc[ MAXPATHLEN + 1]; int err = 0; @@ -628,8 +718,9 @@ static int RF_renamefile_osx(VFS_FUNC_ARGS_RENAMEFILE) return -1; } return 0; -} #endif + return 0; +} /******************************************************************************************** * VFS chaining -- 2.39.2