From 8fed5c3721b742930a3789f58c104d2a7a234dcf Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Thu, 29 Mar 2012 18:58:59 +0200 Subject: [PATCH] Automatic unescaping of CAP hexencoded '/' and '.' - when accessed from AFP client in enumerat() - also updates CNID db BUGS: - dbd not working --- etc/afpd/enumerate.c | 25 ++-- etc/afpd/volume.c | 2 +- etc/cnid_dbd/cmd_dbd_scanvol.c | 4 +- include/atalk/adouble.h | 5 +- include/atalk/directory.h | 1 + libatalk/adouble/Makefile.am | 14 +- libatalk/adouble/ad_conv.c | 247 +++++++++++++++++++++++++++++++++ libatalk/adouble/ad_open.c | 132 ------------------ 8 files changed, 284 insertions(+), 146 deletions(-) create mode 100644 libatalk/adouble/ad_conv.c diff --git a/etc/afpd/enumerate.c b/etc/afpd/enumerate.c index 80759a43..021862e7 100644 --- a/etc/afpd/enumerate.c +++ b/etc/afpd/enumerate.c @@ -347,13 +347,16 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, continue; } memset(&s_path, 0, sizeof(s_path)); + + /* conversions on the fly */ + const char *convname; s_path.u_name = sd.sd_last; - if (of_stat( &s_path) < 0 ) { - /* - * Somebody else plays with the dir, well it can be us with - * "Empty Trash..." - */ + if (ad_convert(sd.sd_last, &s_path.st, vol, &convname) == 0 && convname) { + s_path.u_name = (char *)convname; + s_path.unconverted_name = sd.sd_last; /* Needed for CNID fixup */ + } + if (of_stat( &s_path) < 0 ) { /* so the next time it won't try to stat it again * another solution would be to invalidate the cache with * sd.sd_did = 0 but if it's not ENOENT error it will start again @@ -364,12 +367,18 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, continue; } + /* Fixup CNID db if ad_convert resulted in a rename (then convname != NULL) */ + if (convname) { + s_path.id = cnid_lookup(vol->v_cdb, &s_path.st, curdir->d_did, sd.sd_last, strlen(sd.sd_last)); + if (s_path.id != CNID_INVALID) { + if (cnid_update(vol->v_cdb, s_path.id, &s_path.st, curdir->d_did, convname, strlen(convname)) != 0) + LOG(log_error, logtype_afpd, "enumerate: error updating CNID of \"%s\"", fullpathname(convname)); + } + } + sd.sd_last += len + 1; s_path.m_name = NULL; - /* Convert adouble:v2 to adouble:ea on the fly */ - (void)ad_convert(s_path.u_name, &s_path.st, vol); - /* * If a fil/dir is not a dir, it's a file. This is slightly * inaccurate, since that means /dev/null is a file, /dev/printer diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c index 1d4081e2..61d744ca 100644 --- a/etc/afpd/volume.c +++ b/etc/afpd/volume.c @@ -321,7 +321,7 @@ static int getvolparams(const AFPObj *obj, uint16_t bitmap, struct vol *vol, str * .Parent file here if it doesn't exist. */ /* Convert adouble:v2 to adouble:ea on the fly */ - (void)ad_convert(vol->v_path, st, vol); + (void)ad_convert(vol->v_path, st, vol, NULL); ad_init(&ad, vol); if (ad_open(&ad, vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0 ) { diff --git a/etc/cnid_dbd/cmd_dbd_scanvol.c b/etc/cnid_dbd/cmd_dbd_scanvol.c index 9685c3d2..4baea635 100644 --- a/etc/cnid_dbd/cmd_dbd_scanvol.c +++ b/etc/cnid_dbd/cmd_dbd_scanvol.c @@ -221,7 +221,7 @@ static int check_adfile(const char *fname, const struct stat *st) if (myvol->v_adouble == AD_VERSION_EA) { if (!(dbd_flags & DBD_FLAGS_V2TOEA)) return 0; - if (ad_convert(fname, st, myvol) != 0) { + if (ad_convert(fname, st, myvol, NULL) != 0) { switch (errno) { case ENOENT: break; @@ -1002,7 +1002,7 @@ static int scanvol(struct vol *vol, dbd_flags_t flags) if ((myvol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) { if (lstat(".", &st) != 0) return -1; - if (ad_convert(".", &st, vol) != 0) { + if (ad_convert(".", &st, vol, NULL) != 0) { switch (errno) { case ENOENT: break; diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h index 5b276a5a..f95279d7 100644 --- a/include/atalk/adouble.h +++ b/include/atalk/adouble.h @@ -403,8 +403,11 @@ 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 *); extern mode_t ad_hf_mode(mode_t mode); -extern int ad_convert(const char *path, const struct stat *sp, const struct vol *vol); extern int ad_valid_header_osx(const char *path); + +/* ad_conv.c */ +extern int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, const char **newpath); + /* ad_read.c/ad_write.c */ extern int sys_ftruncate(int fd, off_t length); extern ssize_t ad_read(struct adouble *, uint32_t, off_t, char *, size_t); diff --git a/include/atalk/directory.h b/include/atalk/directory.h index a2759aa1..06a56ac9 100644 --- a/include/atalk/directory.h +++ b/include/atalk/directory.h @@ -83,6 +83,7 @@ struct path { int m_type; /* mac name type (long name, unicode */ char *m_name; /* mac name */ char *u_name; /* unix name */ + char *unconverted_name; /* NULL or u_name before ad_convert() conversion */ cnid_t id; /* file id (only for getmetadata) */ struct dir *d_dir; /* */ int st_valid; /* does st_errno and st set */ diff --git a/libatalk/adouble/Makefile.am b/libatalk/adouble/Makefile.am index 6602926b..49144a2a 100644 --- a/libatalk/adouble/Makefile.am +++ b/libatalk/adouble/Makefile.am @@ -2,7 +2,17 @@ noinst_LTLIBRARIES = libadouble.la -libadouble_la_SOURCES = ad_open.c ad_flush.c ad_read.c ad_write.c ad_size.c \ - ad_mmap.c ad_lock.c ad_date.c ad_attr.c ad_sendfile.c +libadouble_la_SOURCES = \ + ad_attr.c \ + ad_conv.c \ + ad_date.c \ + ad_flush.c \ + ad_lock.c \ + ad_mmap.c \ + ad_open.c \ + ad_read.c \ + ad_sendfile.c \ + ad_size.c \ + ad_write.c noinst_HEADERS = ad_lock.h diff --git a/libatalk/adouble/ad_conv.c b/libatalk/adouble/ad_conv.c new file mode 100644 index 00000000..10284dbb --- /dev/null +++ b/libatalk/adouble/ad_conv.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2012 Frank Lahm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/*! + * @file + * Part of Netatalk's AppleDouble implementatation + * @sa include/atalk/adouble.h + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ad_lock.h" + +static char emptyfilad[32] = {0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0}; + +static char emptydirad[32] = {0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0}; + +static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struct vol *vol) +{ + EC_INIT; + struct adouble adv2; + struct adouble adea; + const char *adpath; + int adflags; + uint32_t ctime, mtime, afpinfo = 0; + char *emptyad; + + LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path)); + + ad_init(&adea, vol); + ad_init_old(&adv2, AD_VERSION2, adea.ad_options); + adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0; + + /* Open and lock adouble:v2 file */ + EC_ZERO( ad_open(&adv2, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR) ); + + EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) ); + EC_NEG1_LOG( adv2.ad_ops->ad_header_read(path, &adv2, sp) ); + + /* Check if it's a non-empty header */ + if (S_ISREG(sp->st_mode)) + emptyad = &emptyfilad[0]; + else + emptyad = &emptydirad[0]; + + if (ad_getentrylen(&adv2, ADEID_COMMENT) != 0) + goto copy; + if (ad_getentryoff(&adv2, ADEID_FINDERI) + && (ad_getentrylen(&adv2, ADEID_FINDERI) == ADEDLEN_FINDERI) + && (memcmp(ad_entry(&adv2, ADEID_FINDERI), emptyad, ADEDLEN_FINDERI) != 0)) + goto copy; + if (ad_getentryoff(&adv2, ADEID_FILEDATESI)) { + EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_CREATE | AD_DATE_UNIX, &ctime) ); + EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_MODIFY | AD_DATE_UNIX, &mtime) ); + if ((ctime != mtime) || (mtime != sp->st_mtime)) + goto copy; + } + if (ad_getentryoff(&adv2, ADEID_AFPFILEI)) { + if (memcmp(ad_entry(&adv2, ADEID_AFPFILEI), &afpinfo, ADEDLEN_AFPFILEI) != 0) + goto copy; + } + + LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret); + goto EC_CLEANUP; + +copy: + /* Create a adouble:ea meta EA */ + LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret); + EC_ZERO_LOG( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE) ); + EC_ZERO_LOG( ad_copy_header(&adea, &adv2) ); + ad_flush(&adea); + +EC_CLEANUP: + EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_SETSHRMD) ); + EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_SETSHRMD) ); + LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret); + EC_EXIT; +} + +static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struct vol *vol) +{ + EC_INIT; + struct adouble adv2; + struct adouble adea; + + LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path)); + + if (S_ISDIR(sp->st_mode)) + return 0; + + ad_init(&adea, vol); + ad_init_old(&adv2, AD_VERSION2, adea.ad_options); + + /* Open and lock adouble:v2 file */ + EC_ZERO( ad_open(&adv2, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR) ); + + if (adv2.ad_rlen > 0) { + EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) ); + + /* Create a adouble:ea resource fork */ + EC_ZERO_LOG( ad_open(&adea, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) ); + + EC_ZERO_LOG( copy_fork(ADEID_RFORK, &adea, &adv2) ); + adea.ad_rlen = adv2.ad_rlen; + ad_flush(&adea); + } + +EC_CLEANUP: + EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_RF) ); + EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_RF) ); + LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret); + EC_EXIT; +} + +static int ad_conv_v22ea(const char *path, const struct stat *sp, const struct vol *vol) +{ + EC_INIT; + const char *adpath; + int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0; + + EC_ZERO( ad_conv_v22ea_hf(path, sp, vol) ); + EC_ZERO( ad_conv_v22ea_rf(path, sp, vol) ); + + EC_NULL( adpath = ad_path(path, adflags) ); + LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"", + path, fullpathname(adpath)); + + become_root(); + EC_ZERO_LOG( unlink(adpath) ); + unbecome_root(); + +EC_CLEANUP: + if (errno == ENOENT) + EC_STATUS(0); + EC_EXIT; +} + +/*! + * Remove hexencoded dots and slashes (":2e" and ":2f") + */ +static int ad_conv_dehex(const char *path, const struct stat *sp, const struct vol *vol, const char **newpathp) +{ + EC_INIT; + static char buf[MAXPATHLEN]; + const char *adpath, *p; + int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0; + bstring newpath = NULL; + + LOG(log_debug, logtype_default,"ad_conv_dehex(\"%s\"): BEGIN", fullpathname(path)); + + *newpathp = NULL; + + if ((p = strchr(path, ':')) == NULL) + goto EC_CLEANUP; + + EC_NULL( newpath = bfromcstr(path) ); + + EC_ZERO( bfindreplace(newpath, bfromcstr(":2e"), bfromcstr("."), 0) ); + EC_ZERO( bfindreplace(newpath, bfromcstr(":2f"), bfromcstr(":"), 0) ); + + become_root(); + if (adflags != ADFLAGS_DIR) + rename(vol->ad_path(path, 0), vol->ad_path(bdata(newpath), 0)); + rename(path, bdata(newpath)); + unbecome_root(); + + strlcpy(buf, bdata(newpath), sizeof(buf)); + *newpathp = buf; + +EC_CLEANUP: + if (newpath) + bdestroy(newpath); + EC_EXIT; +} + +/*! + * AppleDouble and encoding conversion on the fly + * + * @param path (r) path to file or directory + * @param sp (r) stat(path) + * @param vol (r) volume handle + * @param newpath (w) if encoding changed, new name. Can be NULL. + * + * @returns -1 on internal error, otherwise 0. newpath is NULL if no character conversion was done, + * otherwise newpath points to a static string with the converted name + */ +int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, const char **newpath) +{ + EC_INIT; + const char *p; + + LOG(log_debug, logtype_default,"ad_convert(\"%s\"): BEGIN", fullpathname(path)); + + if (newpath) + *newpath = NULL; + + if ((vol->v_adouble == AD_VERSION_EA) && !(vol->v_flags & AFPVOL_NOV2TOEACONV)) + EC_ZERO( ad_conv_v22ea(path, sp, vol) ); + + if (vol->v_adouble == AD_VERSION_EA) { + EC_ZERO( ad_conv_dehex(path, sp, vol, &p) ); + if (p && newpath) + *newpath = p; + } + +EC_CLEANUP: + LOG(log_debug, logtype_default,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret); + EC_EXIT; +} + diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index 196053ee..79016b5f 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -809,111 +809,6 @@ static int ad2openflags(const struct adouble *ad, int adfile, int adflags) return oflags; } -static char emptyfilad[32] = {0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0}; - -static char emptydirad[32] = {0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,1,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0}; - -static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struct vol *vol) -{ - EC_INIT; - struct adouble adv2; - struct adouble adea; - const char *adpath; - int adflags; - uint32_t ctime, mtime, afpinfo = 0; - char *emptyad; - - LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path)); - - ad_init(&adea, vol); - ad_init_old(&adv2, AD_VERSION2, adea.ad_options); - adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0; - - /* Open and lock adouble:v2 file */ - EC_ZERO( ad_open(&adv2, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR) ); - EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) ); - EC_NEG1_LOG( adv2.ad_ops->ad_header_read(path, &adv2, sp) ); - - /* Check if it's a non-empty header */ - if (S_ISREG(sp->st_mode)) - emptyad = &emptyfilad[0]; - else - emptyad = &emptydirad[0]; - - if (ad_getentrylen(&adv2, ADEID_COMMENT) != 0) - goto copy; - if (ad_getentryoff(&adv2, ADEID_FINDERI) - && (ad_getentrylen(&adv2, ADEID_FINDERI) == ADEDLEN_FINDERI) - && (memcmp(ad_entry(&adv2, ADEID_FINDERI), emptyad, ADEDLEN_FINDERI) != 0)) - goto copy; - if (ad_getentryoff(&adv2, ADEID_FILEDATESI)) { - EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_CREATE | AD_DATE_UNIX, &ctime) ); - EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_MODIFY | AD_DATE_UNIX, &mtime) ); - if ((ctime != mtime) || (mtime != sp->st_mtime)) - goto copy; - } - if (ad_getentryoff(&adv2, ADEID_AFPFILEI)) { - if (memcmp(ad_entry(&adv2, ADEID_AFPFILEI), &afpinfo, ADEDLEN_AFPFILEI) != 0) - goto copy; - } - - LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret); - goto EC_CLEANUP; - -copy: - /* Create a adouble:ea meta EA */ - LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret); - EC_ZERO_LOG( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE) ); - EC_ZERO_LOG( ad_copy_header(&adea, &adv2) ); - ad_flush(&adea); - -EC_CLEANUP: - EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_SETSHRMD) ); - EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_SETSHRMD) ); - LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret); - EC_EXIT; -} - -static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struct vol *vol) -{ - EC_INIT; - struct adouble adv2; - struct adouble adea; - - LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path)); - - if (S_ISDIR(sp->st_mode)) - return 0; - - ad_init(&adea, vol); - ad_init_old(&adv2, AD_VERSION2, adea.ad_options); - - /* Open and lock adouble:v2 file */ - EC_ZERO( ad_open(&adv2, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR) ); - if (adv2.ad_rlen > 0) { - EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) ); - - /* Create a adouble:ea resource fork */ - EC_ZERO_LOG( ad_open(&adea, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) ); - - EC_ZERO_LOG( copy_fork(ADEID_RFORK, &adea, &adv2) ); - adea.ad_rlen = adv2.ad_rlen; - ad_flush(&adea); - } - -EC_CLEANUP: - EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_RF) ); - EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_RF) ); - LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret); - EC_EXIT; -} - static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble *ad) { EC_INIT; @@ -1929,33 +1824,6 @@ EC_CLEANUP: return ret; } -int ad_convert(const char *path, const struct stat *sp, const struct vol *vol) -{ - EC_INIT; - const char *adpath; - int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0; - - LOG(log_debug, logtype_default,"ad_convert(\"%s\"): BEGIN", fullpathname(path)); - - if (!(vol->v_adouble == AD_VERSION_EA) || (vol->v_flags & AFPVOL_NOV2TOEACONV)) - goto EC_CLEANUP; - - EC_ZERO( ad_conv_v22ea_hf(path, sp, vol) ); - EC_ZERO( ad_conv_v22ea_rf(path, sp, vol) ); - - EC_NULL( adpath = ad_path(path, adflags) ); - LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"", - path, fullpathname(adpath)); - - become_root(); - EC_ZERO_LOG( unlink(adpath) ); - unbecome_root(); - -EC_CLEANUP: - LOG(log_debug, logtype_default,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret); - EC_EXIT; -} - /* build a resource fork mode from the data fork mode: * remove X mode and extend header to RW if R or W (W if R for locking), */ -- 2.39.2