From 1b304a03acb48cfc3baec7d33a05cad954325468 Mon Sep 17 00:00:00 2001 From: jmarcus Date: Wed, 29 May 2002 18:02:57 +0000 Subject: [PATCH] Add support for long filename mangling. Basically, tis code will take filenames greater than MACFILELEN characters, and mangle them much like DOS does. The mangled filenames are stored in a Berkeley database per volume. NOTE: This code is _not_ tested, and may cause data loss. Use at your own risk. To enable it, configure with --with-mangling. --- NEWS | 1 + acconfig.h | 1 + configure.in | 17 ++++- etc/afpd/Makefile.am | 10 +-- etc/afpd/desktop.c | 13 +++- etc/afpd/mangle.c | 115 ++++++++++++++++++++++++++++++++ etc/afpd/mangle.h | 29 ++++++++ etc/afpd/ofork.c | 6 +- include/atalk/cnid.h | 4 ++ libatalk/cnid/Makefile.am | 2 +- libatalk/cnid/cnid_close.c | 5 +- libatalk/cnid/cnid_mangle_add.c | 70 +++++++++++++++++++ libatalk/cnid/cnid_mangle_get.c | 92 +++++++++++++++++++++++++ libatalk/cnid/cnid_open.c | 32 ++++++++- libatalk/cnid/cnid_private.h | 3 +- 15 files changed, 385 insertions(+), 15 deletions(-) create mode 100644 etc/afpd/mangle.c create mode 100644 etc/afpd/mangle.h create mode 100644 libatalk/cnid/cnid_mangle_add.c create mode 100644 libatalk/cnid/cnid_mangle_get.c diff --git a/NEWS b/NEWS index 4cccc98f..876e283c 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ Changes from 1.5.3 * NEW: Add the ability to enable debugging at runtime by sending afpd a SIGUSR1. +* NEW: Add support for long filename mangling. * NEW: Add a -noslp option to disable SLP per afpd instance. * NEW: Added -d option for pap to enable debugging at runtime rather than compile time. diff --git a/acconfig.h b/acconfig.h index a200ec1c..19bd4f5b 100644 --- a/acconfig.h +++ b/acconfig.h @@ -5,6 +5,7 @@ #undef AFS #undef BSD4_4 #undef CNID_DB +#undef FILE_MANGLING #undef DISABLE_LOGGER #undef LOGFILEPATH #undef DEBUG diff --git a/configure.in b/configure.in index d15bc05c..9764e236 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -dnl $Id: configure.in,v 1.157 2002-04-29 06:23:57 morgana Exp $ +dnl $Id: configure.in,v 1.158 2002-05-29 18:02:57 jmarcus Exp $ dnl configure.in for netatalk AC_INIT(bin/adv1tov2/adv1tov2.c) @@ -208,7 +208,6 @@ AC_ARG_ENABLE(force-uidgid, dnl Don't use DB3 unless it's needed db3_required=no - dnl Determine DID scheme AC_MSG_CHECKING([for DID scheme to use]) AC_ARG_WITH(did, @@ -234,6 +233,20 @@ else fi AM_CONDITIONAL(COMPILE_CNID, test "x$did_scheme" = "xcnid") +dnl Determine whether or not to use filename mangling +AC_MSG_CHECKING([whether or not to use filename mangling]) +AC_ARG_WITH(mangling, + [ --with-mangling enable filename mangling], + if test "$withval" = "yes"; then + if test "x$did_scheme" != "xcnid"; then + AC_MSG_ERROR([DID scheme must be CNID to use filename mangling]) + else + AC_DEFINE(FILE_MANGLING, 1) + AC_MSG_RESULT([yes]) + fi + fi +) + dnl Check for Berkeley DB3 library if test "x$db3_required" = "xyes"; then AC_PATH_DB3(, [AC_MSG_ERROR([Berkeley DB3 library not found!])]) diff --git a/etc/afpd/Makefile.am b/etc/afpd/Makefile.am index 1f13c62d..03282b55 100644 --- a/etc/afpd/Makefile.am +++ b/etc/afpd/Makefile.am @@ -11,9 +11,9 @@ bin_PROGRAMS = test_parse_mtab afpd_SOURCES = unix.c ofork.c main.c switch.c auth.c volume.c directory.c \ file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \ - status.c afp_options.c afp_asp.c afp_dsi.c messages.c afp_config.c \ - nfsquota.c codepage.c quota.c uam.c afs.c uid.c parse_mtab.c \ - afp_util.c + mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c \ + afp_config.c nfsquota.c codepage.c quota.c uam.c afs.c uid.c \ + parse_mtab.c afp_util.c test_parse_mtab_SOURCES = test_parse_mtab.c parse_mtab.c test_parse_mtab_LDADD = $(top_builddir)/libatalk/libatalk.la @@ -22,8 +22,8 @@ afpd_LDADD = $(top_builddir)/libatalk/libatalk.la afpd_LDFLAGS = -export-dynamic noinst_HEADERS = auth.h codepage.h afp_config.h desktop.h directory.h file.h \ - filedir.h fork.h globals.h icon.h misc.h status.h switch.h uam_auth.h \ - uid.h unix.h volume.h parse_mtab.h + filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \ + uam_auth.h uid.h unix.h volume.h parse_mtab.h LIBS = @LIBS@ @AFPD_LIBS@ @QUOTA_LIBS@ @SLP_LIBS@ diff --git a/etc/afpd/desktop.c b/etc/afpd/desktop.c index 344eb08b..864cf26a 100644 --- a/etc/afpd/desktop.c +++ b/etc/afpd/desktop.c @@ -1,5 +1,5 @@ /* - * $Id: desktop.c,v 1.12 2002-03-24 01:23:40 sibaz Exp $ + * $Id: desktop.c,v 1.13 2002-05-29 18:02:59 jmarcus Exp $ * * See COPYRIGHT. */ @@ -40,6 +40,9 @@ #include "fork.h" #include "globals.h" #include "desktop.h" +#ifdef FILE_MANGLING +#include "mangle.h" +#endif /* CNID_DB */ int afp_opendt(obj, ibuf, ibuflen, rbuf, rbuflen ) AFPObj *obj; @@ -611,6 +614,10 @@ char *mtoupath(const struct vol *vol, char *mpath) return( "." ); } +#ifdef FILE_MANGLING + mpath = demangle(vol, mpath); +#endif /* FILE_MANGLING */ + m = mpath; u = upath; while ( *m != '\0' ) { @@ -663,6 +670,10 @@ char *utompath(const struct vol *vol, char *upath) char *m, *u; int h; +#ifdef FILE_MANGLING + upath = mangle(vol, upath); +#endif /* FILE_MANGLING */ + /* do the hex conversion */ u = upath; m = mpath; diff --git a/etc/afpd/mangle.c b/etc/afpd/mangle.c new file mode 100644 index 00000000..0a6146d6 --- /dev/null +++ b/etc/afpd/mangle.c @@ -0,0 +1,115 @@ +/* + * $Id: mangle.c,v 1.1 2002-05-29 18:02:59 jmarcus Exp $ + * + * Copyright (c) 2002. Joe Marcus Clarke (marcus@marcuscom.com) + * All Rights Reserved. See COPYRIGHT. + * + * mangle, demangle (filename): + * mangle or demangle filenames if they are greater than the max allowed + * characters for a given version of AFP. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef FILE_MANGLING +#include "mangle.h" + +char * +demangle(const struct vol *vol, char *mfilename) { + char *filename = NULL; + char *ext = NULL; + char dir[MAXPATHLEN+1]; + char *mangle; + + /* Is this really a mangled file? */ + mangle = strstr(mfilename, MANGLE_CHAR); + if (!mangle) { + return mfilename; + } + + ext = strrchr(mfilename, '.'); + if (strlen(mangle) != strlen(MANGLE_CHAR) + MANGLE_LENGTH + strlen(ext)) { + return mfilename; + } + + getcwd(dir, MAXPATHLEN); + + if (strlen(dir) > MAXPATHLEN - strlen(mfilename) - 1) { + LOG(log_error, logtype_default, "demangle: Path is too large"); + return mfilename; + } + + strcat(dir, "/"); + strcat(dir, mfilename); + + filename = cnid_mangle_get(vol->v_db, dir); + + /* No unmangled filename was found. */ + if (filename == NULL) { + LOG(log_debug, logtype_default, "demangle: Unable to lookup %s in the mangle database", dir); + return mfilename; + } + + return filename; +} +char * +mangle(const struct vol *vol, char *filename) { + char *ext = NULL; + char mfilename[MAX_LENGTH+1]; + char mangle_suffix[MANGLE_LENGTH+1]; + char dir[MAXPATHLEN+1]; + char tmp[MAXPATHLEN+1]; + int mangle_suffix_int = 0; + + /* Do we really need to mangle this filename? */ + if (strlen(filename) <= MAX_LENGTH) { + return filename; + } + + /* First, attmept to locate a file extension. */ + ext = strrchr(filename, '.'); + + getcwd(dir, MAXPATHLEN); + + if (strlen(dir) > MAXPATHLEN - strlen(filename) - 1) { + LOG(log_error, logtype_default, "mangle: path is too large"); + return filename; + } + + /* Check to see if we already have a mangled filename by this name. */ + while (1) { + strcpy(tmp, dir); + strncpy(mfilename, filename, MAX_LENGTH - strlen(MANGLE_CHAR) - MANGLE_LENGTH - ((ext != NULL)?strlen(ext):0)); + strcat(mfilename, MANGLE_CHAR); + (void)sprintf(mangle_suffix, "%03d", mangle_suffix_int); + strcat(mfilename, mangle_suffix); + + if (ext) { + strcat(mfilename, ext); + } + + strcat(tmp, "/"); + strcat(tmp, mfilename); + + if (!cnid_mangle_get(vol->v_db, tmp)) { + break; + } + else { + if (++mangle_suffix_int > MAX_MANGLE_SUFFIX_LENGTH) { + LOG(log_error, logtype_default, "mangle: Failed to find a free mangle suffix; returning original filename"); + return filename; + } + } + } + + if (cnid_mangle_add(vol->v_db, mfilename, tmp) < 0) { + return filename; + } + + filename = mfilename; + + return filename; +} +#endif /* FILE_MANGLING */ diff --git a/etc/afpd/mangle.h b/etc/afpd/mangle.h new file mode 100644 index 00000000..6010c442 --- /dev/null +++ b/etc/afpd/mangle.h @@ -0,0 +1,29 @@ +/* + * $Id: mangle.h,v 1.1 2002-05-29 18:02:59 jmarcus Exp $ + * + */ + +#ifndef AFPD_MANGLE_H +#define AFPD_MANGLE_H 1 + +#include +#include +#include +#ifdef CNID_DB +#include +#endif /* CNID_DB */ +#include + +#include "globals.h" +#include "volume.h" +#include "directory.h" + +#define MANGLE_CHAR "~" +#define MANGLE_LENGTH 3 /* XXX This really can't be changed. */ +#define MAX_MANGLE_SUFFIX_LENGTH 999 +#define MAX_LENGTH 31 + +extern char *mangle __P((const struct vol *, char *)); +extern char *demangle __P((const struct vol *, char *)); + +#endif /* AFPD_MANGLE_H */ diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c index d30d1af9..ef2c8583 100644 --- a/etc/afpd/ofork.c +++ b/etc/afpd/ofork.c @@ -1,5 +1,5 @@ /* - * $Id: ofork.c,v 1.14 2002-05-13 04:59:36 jmarcus Exp $ + * $Id: ofork.c,v 1.15 2002-05-29 18:02:59 jmarcus Exp $ * * Copyright (c) 1996 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -230,7 +230,7 @@ struct adouble *ad; /* here's the deal: we allocate enough for the standard mac file length. * in the future, we'll reallocate in fairly large jumps in case * of long unicode names */ - if (( of->of_name =(char *)malloc(MACFILELEN + 1)) == + if (( of->of_name =(char *)malloc(255 + 1)) == NULL ) { LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) ); if (!ad) @@ -239,7 +239,7 @@ struct adouble *ad; oforks[ of_refnum ] = NULL; return NULL; } - strncpy( of->of_name, path, of->of_namelen = MACFILELEN + 1); + strncpy( of->of_name, path, of->of_namelen = 255 + 1); *ofrefnum = refnum; of->of_refnum = refnum; of_hash(of); diff --git a/include/atalk/cnid.h b/include/atalk/cnid.h index d96e8a85..d112eeef 100644 --- a/include/atalk/cnid.h +++ b/include/atalk/cnid.h @@ -48,4 +48,8 @@ extern int cnid_delete __P((void *, const cnid_t)); /* cnid_nextid.c */ extern cnid_t cnid_nextid __P((void *)); +/* cnid_mangle_* */ +extern int cnid_mangle_add __P((void *, char *, char *)); +extern char *cnid_mangle_get __P((void *, char *)); + #endif /* include/atalk/cnid.h */ diff --git a/libatalk/cnid/Makefile.am b/libatalk/cnid/Makefile.am index 91875f0e..2d04daaa 100644 --- a/libatalk/cnid/Makefile.am +++ b/libatalk/cnid/Makefile.am @@ -5,7 +5,7 @@ LIBS = @LIBS@ @DB3_LIBS@ noinst_LTLIBRARIES = libcnid.la -libcnid_la_SOURCES = cnid_add.c cnid_close.c cnid_delete.c cnid_get.c cnid_lookup.c cnid_open.c cnid_resolve.c cnid_update.c +libcnid_la_SOURCES = cnid_add.c cnid_close.c cnid_delete.c cnid_get.c cnid_lookup.c cnid_mangle_add.c cnid_mangle_get.c cnid_open.c cnid_resolve.c cnid_update.c noinst_HEADERS = cnid_meta.h cnid_private.h diff --git a/libatalk/cnid/cnid_close.c b/libatalk/cnid/cnid_close.c index 882cd838..2d649cfc 100644 --- a/libatalk/cnid/cnid_close.c +++ b/libatalk/cnid/cnid_close.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_close.c,v 1.22 2002-02-01 19:51:09 jmarcus Exp $ + * $Id: cnid_close.c,v 1.23 2002-05-29 18:02:59 jmarcus Exp $ */ #ifdef HAVE_CONFIG_H @@ -75,6 +75,9 @@ void cnid_close(void *CNID) { db->db_didname->close(db->db_didname, 0); db->db_devino->close(db->db_devino, 0); db->db_cnid->close(db->db_cnid, 0); +#ifdef FILE_MANGLING + db->db_mangle->close(db->db_mangle, 0); +#endif /* FILE_MANGLING */ db->dbenv->close(db->dbenv, 0); if (db->lockfd > -1) { diff --git a/libatalk/cnid/cnid_mangle_add.c b/libatalk/cnid/cnid_mangle_add.c new file mode 100644 index 00000000..bc88d564 --- /dev/null +++ b/libatalk/cnid/cnid_mangle_add.c @@ -0,0 +1,70 @@ +/* + * $Id: cnid_mangle_add.c,v 1.1 2002-05-29 18:02:59 jmarcus Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef FILE_MANGLING +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cnid_private.h" + +/* Add a mangled filename. */ +int +cnid_mangle_add(void *CNID, char *mfilename, char *filename) +{ + CNID_private *db; + DBT key, data; + DB_TXN *tid; + cnid_t id; + struct stat st; + int rc, ret; + + if (!(db = CNID)) { + return -1; + } + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.data = mfilename; + key.size = strlen(mfilename) + 1; + data.data = filename; + data.size = strlen(filename) + 1; + +retry: + if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) { + LOG(log_error, logtype_default, "cnid_mangle_add: Failed to begin transaction: %s", db_strerror(rc)); + return -1; + } + + if ((rc = db->db_mangle->put(db->db_mangle, tid, &key, &data, 0))) { + if ((ret = txn_abort(tid)) != 0) { + LOG(log_error, logtype_default, "cnid_mangle_add: txn_abort: %s", db_strerror(ret)); + return -1; + } + switch (rc) { + case DB_LOCK_DEADLOCK: + goto retry; + default: + LOG(log_error, logtype_default, "cnid_mangle_add: Failed to add mangled filename to the database: %s", db_strerror(rc)); + return -1; + } + } + + return 0; +} +#endif /* FILE_MANGLING */ diff --git a/libatalk/cnid/cnid_mangle_get.c b/libatalk/cnid/cnid_mangle_get.c new file mode 100644 index 00000000..6f80189c --- /dev/null +++ b/libatalk/cnid/cnid_mangle_get.c @@ -0,0 +1,92 @@ +/* + * $Id: cnid_mangle_get.c,v 1.1 2002-05-29 18:02:59 jmarcus Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef FILE_MANGLING +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cnid_private.h" + +/* Find a mangled filename entry. */ +char * +cnid_mangle_get(void *CNID, char *mfilename) +{ + CNID_private *db; + DBT key, data; + DB_TXN *tid; + cnid_t id; + struct stat st; + char *filename; + int rc; + + if (!(db = CNID)) { + return NULL; + } + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.data = mfilename; + key.size = strlen(mfilename) + 1; + + while ((rc = db->db_didname->get(db->db_mangle, NULL, &key, &data, 0))) { + if (rc == DB_LOCK_DEADLOCK) { + continue; + } + + if (rc == DB_NOTFOUND) { + return NULL; + + } + + return NULL; + } + + filename = strrchr(data.data, '/'); + filename++; /* Skip the leading '/' */ + + if (stat(data.data, &st) < 0) { + if (errno == ENOENT) { + /* The file no longer exists. Purge it from the database. */ +/*retry: + if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) { + LOG(log_error, logtype_default, "cnid_mangle_get: Failed to begin transaction: %s", db_strerror(rc)); + return filename; + } + + if ((rc = db->db_mangle->del(db->db_mangle, tid, &key, 0))) { + int ret; + if ((ret = txn_abort(tid)) != 0) { + LOG(log_error, logtype_default, "cnid_mangle_get: txn_abort: %s", db_strerror(ret)); + return filename; + } + switch (rc) { + case DB_LOCK_DEADLOCK: + goto retry; + default: + LOG(log_error, logtype_default, "cnid_mangle_get: Unable to delete mangled file entry: %s", mfilename); + return filename; + } + }*/ + return NULL; + } + } + + return filename; +} +#endif /* FILE_MANGLING */ diff --git a/libatalk/cnid/cnid_open.c b/libatalk/cnid/cnid_open.c index 15e07707..7f24b51d 100644 --- a/libatalk/cnid/cnid_open.c +++ b/libatalk/cnid/cnid_open.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_open.c,v 1.38 2002-02-01 19:51:09 jmarcus Exp $ + * $Id: cnid_open.c,v 1.39 2002-05-29 18:02:59 jmarcus Exp $ * * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu) * All Rights Reserved. See COPYRIGHT. @@ -73,6 +73,7 @@ #define DBDIDNAME "didname.db" /* did/full name mapping */ #define DBSHORTNAME "shortname.db" /* did/8+3 mapping */ #define DBMACNAME "macname.db" /* did/31 mapping */ +#define DBMANGLE "mangle.db" /* filename mangling */ #define DBLONGNAME "longname.db" /* did/unicode mapping */ #define DBLOCKFILE "cnid.lock" #define DBRECOVERFILE "cnid.dbrecover" @@ -508,6 +509,35 @@ dbversion_retry: goto fail_appinit; } +#ifdef FILE_MANGLING + /* filename mangling database. Use a hash for this one. */ + if ((rc = db_create(&db->db_mangle, db->dbenv, 0)) != 0) { + LOG(log_error, logtype_default, "cnid_open: Failed to create mangle database: %s", db_strerror(rc)); + db->db_didname->close(db->db_didname, 0); + db->db_devino->close(db->db_devino, 0); + db->db_cnid->close(db->db_cnid, 0); +#ifdef EXTENDED_DB + db->db_macname->close(db->db_macname, 0); + db->db_shortname->close(db->db_shortname, 0); + db->db_longname->close(db->db_longname, 0); +#endif /* EXTENDED_DB */ + goto fail_appinit; + } + + if ((rc = db->db_mangle->open(db->db_mangle, DBMANGLE, NULL, DB_HASH, open_flag, 0666)) != 0) { + LOG(log_error, logtype_default, "cnid_open: Failed to open mangle database: %s", db_strerror(rc)); + db->db_didname->close(db->db_didname, 0); + db->db_devino->close(db->db_devino, 0); + db->db_cnid->close(db->db_cnid, 0); +#ifdef EXTENDED_DB + db->db_macname->close(db->db_macname, 0); + db->db_shortname->close(db->db_shortname, 0); + db->db_longname->close(db->db_longname, 0); +#endif /* EXTENDED_DB */ + goto fail_appinit; + } +#endif /* FILE_MANGLING */ + /* Print out the version of DB3 we're linked against. */ LOG(log_info, logtype_default, "CNID DB initialized using %s", db_version(NULL, NULL, NULL)); diff --git a/libatalk/cnid/cnid_private.h b/libatalk/cnid/cnid_private.h index cf1c3d4f..b7f7683d 100644 --- a/libatalk/cnid/cnid_private.h +++ b/libatalk/cnid/cnid_private.h @@ -1,5 +1,5 @@ /* - * $Id: cnid_private.h,v 1.9 2002-02-02 19:12:31 jmarcus Exp $ + * $Id: cnid_private.h,v 1.10 2002-05-29 18:02:59 jmarcus Exp $ */ #ifndef LIBATALK_CNID_PRIVATE_H @@ -39,6 +39,7 @@ typedef struct CNID_private { DB *db_cnid; DB *db_didname; DB *db_devino; + DB *db_mangle; #ifdef EXTENDED_DB DB *db_shortname; DB *db_macname; -- 2.39.2