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
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
* .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 ) {
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;
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;
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);
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 */
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
--- /dev/null
+/*
+ * 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 <errno.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <arpa/inet.h>
+
+#include <atalk/logger.h>
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+#include <atalk/ea.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/compat.h>
+#include <atalk/errchk.h>
+#include <atalk/volume.h>
+
+#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;
+}
+
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;
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),
*/