]> arthur.barton.de Git - netatalk.git/commitdiff
Merge branch-2-1
authorFrank Lahm <franklahm@googlemail.com>
Sun, 26 Sep 2010 09:10:22 +0000 (11:10 +0200)
committerFrank Lahm <franklahm@googlemail.com>
Sun, 26 Sep 2010 09:10:22 +0000 (11:10 +0200)
103 files changed:
Makefile.am
NEWS
VERSION
bin/Makefile.am
bin/misc/Makefile.am
bin/misc/uuidtest.c
config/Makefile.am
config/afpd.conf.tmpl
configure.in
contrib/Makefile.am
contrib/patches/patch.afp_vfs [deleted file]
contrib/patches/patch.mangled_trash_with_ip [deleted file]
contrib/patches/patch.vfs [deleted file]
contrib/permtest/add_permtest.patch [deleted file]
contrib/permtest/permtest.cfg [deleted file]
contrib/permtest/permtest.pl.in [deleted file]
contrib/shell_utils/asip-status.pl.in
doc/README.ACLs
etc/Makefile.am
etc/afpd/Makefile.am
etc/afpd/acl_mappings.h
etc/afpd/acls.c
etc/afpd/acls.h
etc/afpd/afp_avahi.c [new file with mode: 0644]
etc/afpd/afp_avahi.h [new file with mode: 0644]
etc/afpd/afp_config.c
etc/afpd/afp_dsi.c
etc/afpd/afp_options.c
etc/afpd/afp_zeroconf.c [new file with mode: 0644]
etc/afpd/afp_zeroconf.h [new file with mode: 0644]
etc/afpd/appl.c
etc/afpd/auth.c
etc/afpd/catsearch.c
etc/afpd/desktop.c
etc/afpd/dircache.c [new file with mode: 0644]
etc/afpd/dircache.h [new file with mode: 0644]
etc/afpd/directory.c
etc/afpd/directory.h
etc/afpd/enumerate.c
etc/afpd/file.c
etc/afpd/file.h
etc/afpd/filedir.c
etc/afpd/globals.h
etc/afpd/main.c
etc/afpd/mangle.c
etc/afpd/ofork.c
etc/afpd/switch.c
etc/afpd/unix.c
etc/afpd/volume.c
etc/afpd/volume.h
etc/cnid_dbd/comm.c
etc/cnid_dbd/main.c
include/atalk/Makefile.am
include/atalk/acl.h
include/atalk/bstradd.h [new file with mode: 0644]
include/atalk/bstrlib.h [new file with mode: 0644]
include/atalk/directory.h
include/atalk/dsi.h
include/atalk/ea.h
include/atalk/ldapconfig.h
include/atalk/queue.h [new file with mode: 0644]
include/atalk/util.h
include/atalk/uuid.h
include/atalk/volume.h
libatalk/Makefile.am
libatalk/acl/Makefile.am
libatalk/acl/cache.c
libatalk/acl/ldap_config.c
libatalk/bstring/.gitignore [new file with mode: 0644]
libatalk/bstring/Makefile.am [new file with mode: 0644]
libatalk/bstring/bstradd.c [new file with mode: 0644]
libatalk/bstring/bstrlib.c [new file with mode: 0644]
libatalk/cnid/cdb/Makefile.am
libatalk/cnid/cnid.c
libatalk/cnid/dbd/cnid_dbd.c
libatalk/compat/pselect.c
libatalk/dsi/dsi_tcp.c
libatalk/util/Makefile.am
libatalk/util/fault.c
libatalk/util/queue.c [new file with mode: 0644]
libatalk/util/server_child.c
libatalk/vfs/Makefile.am
libatalk/vfs/acl.c
libatalk/vfs/vfs.c
macros/summary.m4
macros/zeroconf.m4 [new file with mode: 0644]
man/man1/Makefile.am
man/man3/Makefile.am
man/man4/Makefile.am
man/man5/Makefile.am
man/man8/Makefile.am
man/man8/afp_acls.8.tmpl [deleted file]
test/.gitignore [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/afpd/.gitignore [new file with mode: 0644]
test/afpd/Makefile.am [new file with mode: 0644]
test/afpd/afpfunc_helpers.c [new file with mode: 0644]
test/afpd/afpfunc_helpers.h [new file with mode: 0644]
test/afpd/subtests.c [new file with mode: 0644]
test/afpd/subtests.h [new file with mode: 0644]
test/afpd/test.c [new file with mode: 0644]
test/afpd/test.h [new file with mode: 0644]
test/afpd/test.sh [new file with mode: 0755]

index 44ac045379515e1f79b5a26cf098127308ebf3c8..45abaa1b5e13702b5f24b17df28dd6cff03a579e 100644 (file)
@@ -1,6 +1,6 @@
 # Makefile.am for top level of netatalk package
 
-SUBDIRS = libatalk bin config etc man contrib distrib include sys doc macros
+SUBDIRS = libatalk bin config etc man contrib distrib include sys doc macros test
 
 EXTRA_DIST = CONTRIBUTORS COPYRIGHT COPYING NEWS\
        TODO VERSION services.atalk
diff --git a/NEWS b/NEWS
index b01c403539490a878d05d436c6c3e6b71755ffa2..d9e6bd4562476306be5353968e6cc7cd21ff95b1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,13 @@
+Changes in 2.2
+==============
+
+* UPD: AppleTalk ist disabled by default at configuration time. If needed
+       use --enable-ddp.
+* NEW: afpd: dynamic directoy and CNID cache
+* NEW: afpd: read-only POSIX 1e ACL support
+* NEW: afpd: automagic Zeroconf registration with avahi, registering both
+       the service _afpovertcp._tcp and TimeMachine volumes with _adisk._tcp.
+
 Changes in 2.1.4
 ================
 
diff --git a/VERSION b/VERSION
index 5ed43442e357ec5af98423b346ac65ab92069e04..ed9e076d0649773651d68edf1ecf66b897704894 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.1.4dev
+2.2dev
index d23a5e5e2dd8af358672e56ec114c0af66a70f5e..5e36848600c1dab9d3fcdfafc4e860dc1b99a1c2 100644 (file)
@@ -1,3 +1,7 @@
 # Makefile.am for bin/
 
-SUBDIRS = adv1tov2 aecho afile afppasswd cnid getzones megatron nbp pap psorder uniconv misc
+SUBDIRS = adv1tov2 afile afppasswd cnid megatron uniconv misc
+
+if USE_APPLETALK
+SUBDIRS += aecho getzones nbp pap psorder
+endif
index 9b55ee3621fe40a12364623c764b4e387fa1bd93..e12e782d737d4aae51e2fa6b92e60bf5775725bc 100644 (file)
@@ -1,16 +1,15 @@
 # Makefile.am for bin/misc
 
-bin_PROGRAMS = netacnv
+pkgconfdir = @PKGCONFDIR@
+bin_PROGRAMS =
 
+noinst_PROGRAMS = netacnv
 netacnv_SOURCES = netacnv.c
 netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la
-pkgconfdir = @PKGCONFDIR@
 
-if USE_NFSv4_ACLS
+if HAVE_ACLS
 bin_PROGRAMS += afpldaptest
-
 afpldaptest_SOURCES = uuidtest.c
+afpldaptest_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
 afpldaptest_LDADD =  $(top_builddir)/libatalk/libatalk.la
-
-AM_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
 endif
index 77a7fa793bfb744b276ebb1313f076d454659076..cb9177edd7e26e8c758076447307ba2987c050e9 100644 (file)
@@ -17,7 +17,7 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 
 #include <unistd.h>
 #include <stdlib.h>
@@ -68,8 +68,8 @@ int main( int argc, char **argv)
 {
     int ret, i, c;
     int verbose = 0;
+    atalk_uuid_t uuid;
     int logsetup = 0;
-    uuid_t uuid;
     uuidtype_t type;
     char *uuidstring = NULL;
     char *name = NULL;
@@ -144,4 +144,4 @@ int main( int argc, char **argv)
     return 0;
 }
 
-#endif  /* HAVE_NFSv4_ACLS */
+#endif  /* HAVE_ACLS */
index 1c3699a50c0d4635086890985e5d6a36459ed5ab..001affa9ca0a6f15732bb0621741e2a3eec2817a 100644 (file)
@@ -7,15 +7,19 @@ GENFILES = afpd.conf AppleVolumes.default
 TMPLFILES = afpd.conf.tmpl AppleVolumes.default.tmpl
 
 if USE_DEBIAN
-CONFFILES = AppleVolumes.system atalkd.conf papd.conf
+CONFFILES = AppleVolumes.system
 else
-CONFFILES = AppleVolumes.system atalkd.conf papd.conf netatalk.conf
+CONFFILES = AppleVolumes.system netatalk.conf
 endif
 
-if USE_NFSv4_ACLS
+if HAVE_ACLS
 CONFFILES += afp_ldap.conf
 endif
 
+if USE_APPLETALK
+CONFFILES += atalkd.conf papd.conf
+endif
+
 OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
 EXTRA_DIST = $(CONFFILES) $(TMPLFILES) afp_ldap.conf
 CLEANFILES = $(GENFILES)
@@ -64,6 +68,15 @@ install-config-files: $(CONFFILES) $(GENFILES)
                        echo "not overwriting $$f"; \
                fi; \
        done
+if USE_DEBIAN
+       $(mkinstalldirs) $(DESTDIR)/default
+       if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f /etc/default/netatalk; then \
+               echo "$(INSTALL_DATA) netatalk.conf $(DESTDIR)/etc/default/netatalk"; \
+               $(INSTALL_DATA) netatalk.conf $(DESTDIR)/etc/default/netatalk; \
+       else \
+               echo "not overwriting /etc/default/netatalk"; \
+       fi;
+endif
 
 if USE_DEBIAN
        $(mkinstalldirs) $(DESTDIR)/default
index 4c9ea98dbfa9243565069cdb968bfa0204c8d58f..377592120173b11d26a6d580bf016042fa5f9c19 100644 (file)
@@ -69,6 +69,8 @@
 #                         string.
 #     -slp                Register this server with the Service Location
 #                         Protocol (if SLP support was compiled in).
+#     -nozeroconf         Don't register this server with the Multicats
+#                         DNS Protocol.
 #     -advertise_ssh      Allows Mac OS X clients (10.3.3-10.4) to
 #                         automagically establish a tunneled AFP connection
 #                         through SSH. This option is not so significant
index 9b6e775a6e5408508f6607110148c042c947869d..20d613ca5f250f7ee47f2901a5a4b1befa1ebe58 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: configure.in,v 1.244 2010-04-13 08:05:06 franklahm Exp $
 dnl configure.in for netatalk
 
 AC_INIT(etc/afpd/main.c)
@@ -25,71 +24,11 @@ AC_PROG_PS
 
 AM_PROG_CC_C_O
 
-dnl *********************************************************************
-dnl FIXME! FIXME! These should be selectable properly, and should produce
-dnl the proper flags and defines...
-dnl *********************************************************************
-
-############################################
-# we need dlopen/dlclose/dlsym/dlerror for PAM, the password database plugins and the plugin loading code
-#AC_SEARCH_LIBS(dlopen, [dl])
-# dlopen/dlclose/dlsym/dlerror will be checked again later and defines will be set then
-
-dnl Checks for libraries.
-dnl Replace `main' with a function in -labs:
-dnl AC_CHECK_LIB(abs, main)
-dnl Replace `main' with a function in -laudit:
-dnl AC_CHECK_LIB(audit, main)
-dnl Replace `main' with a function in -lauth:
-dnl AC_CHECK_LIB(auth, main)
-dnl Replace `main' with a function in -lcmd:
-dnl AC_CHECK_LIB(cmd, main)
-dnl Replace `main' with a function in -lcrypt:
-dnl AC_CHECK_LIB(crypt, main)
-dnl Replace `main' with a function in -ld:
-dnl AC_CHECK_LIB(d, main)
-dnl Replace `main' with a function in -ldl:
-dnl AC_CHECK_LIB(dl, dlopen)
-dnl Replace `main' with a function in -lkauth:
-dnl AC_CHECK_LIB(kauth, main)
-dnl Replace `main' with a function in -lkrb:
-dnl AC_CHECK_LIB(krb, main)
-dnl Replace `main' with a function in -llwp:
-dnl AC_CHECK_LIB(lwp, main)
-dnl Replace `main' with a function in -ln:
-dnl AC_CHECK_LIB(n, main)
-
-dnl not the right stuff but should be enough for now
-AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)])
-AC_CHECK_FUNC(connect,,[AC_CHECK_LIB(socket,connect)])
-
-dnl Replace `main' with a function in -lprot:
-dnl AC_CHECK_LIB(prot, main)
-dnl Replace `main' with a function in -lrx:
-dnl AC_CHECK_LIB(rx, main)
-dnl Replace `main' with a function in -lrxkad:
-dnl AC_CHECK_LIB(rxkad, main)
-dnl Replace `main' with a function in -lsys:
-dnl AC_CHECK_LIB(sys, main)
-dnl Replace `main' with a function in -lubik:
-dnl AC_CHECK_LIB(ubik, main)
-
-
-#
-# Check presence of some functions
-#
-# Check for XPG4 access() function
-# Be sure to test before adding AFS libs in LIBS path as AFS lib
-# has such a function that works only on AFS filesystems.
-AC_CHECK_FUNCS(access)
-# 
-AC_CHECK_FUNCS(pread pwrite)
-
 dnl Checks for header files.
 AC_HEADER_DIRENT
 AC_HEADER_STDC
 AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS(fcntl.h limits.h stdint.h strings.h time.h sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h mntent.h syslog.h unistd.h termios.h sys/termios.h netdb.h sgtty.h ufs/quota.h mount.h statfs.h sys/types.h dlfcn.h errno.h sys/errno.h sys/uio.h)
+AC_CHECK_HEADERS(fcntl.h limits.h stdint.h strings.h time.h sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h mntent.h syslog.h unistd.h termios.h sys/termios.h netdb.h sgtty.h ufs/quota.h mount.h statfs.h sys/types.h dlfcn.h errno.h sys/errno.h sys/uio.h langinfo.h locale.h sys/filio.h)
 AC_CHECK_HEADER(sys/cdefs.h,,
        AC_MSG_RESULT([enabling generic cdefs.h from tree])
        CFLAGS="-I\$(top_srcdir)/sys/generic $CFLAGS"
@@ -100,8 +39,6 @@ AC_CHECK_HEADERS([sys/mount.h], , ,
 #endif
 ])
 
-AC_CHECK_HEADERS(langinfo.h locale.h sys/filio.h)
-
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
 AC_TYPE_UID_T
@@ -124,7 +61,6 @@ if test x"$libltdl_cv_need_uscore" = xyes; then
     AC_DEFINE(DLSYM_PREPEND_UNDERSCORE, 1, [BSD compatibility macro])
 fi
 
-
 dnl Checks for library functions.
 AC_TYPE_GETGROUPS
 AC_PROG_GCC_TRADITIONAL
@@ -135,10 +71,20 @@ AC_TYPE_SIGNAL
 AC_FUNC_UTIME_NULL
 AC_FUNC_WAIT3
 AC_CHECK_FUNCS(getcwd gethostname gettimeofday getusershell mkdir rmdir select socket strdup strcasestr strstr strtoul strchr memcpy)
-AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo strlcpy strlcat setlinebuf dirfd pselect)
+AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo strlcpy strlcat setlinebuf dirfd pselect access pread pwrite)
 AC_CHECK_FUNCS(waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64)
 AC_CHECK_FUNC(renameat, AC_DEFINE([_ATFILE_SOURCE], 1, AT file source)) 
 AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include <time.h>])
+
+AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)])
+AC_CHECK_FUNC(connect,,[AC_CHECK_LIB(socket,connect)])
+dnl search for necessary libs for libpthread stuff
+AC_SEARCH_LIBS(pthread_sigmask, pthread,, 
+               [AC_MSG_ERROR([cannot find pthread_sigmask in libc or libpthread])])
+if test x"$ac_cv_search_pthread_sigmask" != x"none required" ; then
+   PTHREAD_LIBS=$ac_cv_search_pthread_sigmask
+fi
+AC_SUBST(PTHREAD_LIBS)
 AC_CACHE_SAVE
 
 dnl Checks for (v)snprintf
@@ -232,19 +178,20 @@ AC_ARG_WITH(cracklib,
 AC_MSG_CHECKING([for cracklib support])
 AC_MSG_RESULT([$netatalk_cv_with_cracklib])
 
-netatalk_cv_ddp_enabled=yes
+netatalk_cv_ddp_enabled=no
 AC_MSG_CHECKING([whether to enable DDP])
 AC_ARG_ENABLE(ddp,
-       [  --disable-ddp           disable DDP],[
-       if test "$enableval" = "no"; then 
-               AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
-               AC_MSG_RESULT([no])
-               netatalk_cv_ddp_enabled=no
+       [  --enable-ddp            enable DDP (AppleTalk)],[
+       if test "$enableval" = "yes"; then 
+               AC_MSG_RESULT([yes])
+               netatalk_cv_ddp_enabled=yes
        else
                AC_MSG_RESULT([yes])
+               AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
        fi
        ],[
-               AC_MSG_RESULT([yes])
+               AC_MSG_RESULT([no])
+               AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
        ]
 )
 
@@ -278,9 +225,11 @@ AC_ARG_ENABLE(debug,
                AC_MSG_RESULT([yes])
        else
                AC_MSG_RESULT([no])
+        AC_DEFINE(NDEBUG, 1, [Disable assertions])
        fi
        ],[
                AC_MSG_RESULT([no])
+        AC_DEFINE(NDEBUG, 1, [Disable assertions])
        ]
 )
 
@@ -387,6 +336,9 @@ AC_CHECK_QUOTA
 dnl Check for optional server location protocol support (used by MacOS X)
 NETATALK_SRVLOC
 
+dnl Check for optional Zeroconf support
+NETATALK_ZEROCONF
+
 dnl Check for PAM libs
 netatalk_cv_use_pam=no
 AC_PATH_PAM([
@@ -1023,48 +975,126 @@ AC_ARG_ENABLE(overwrite,
 AC_MSG_RESULT([$OVERWRITE_CONFIG])
 
 dnl --------------------- check for ACL support
-AC_MSG_CHECKING([if NFSv4 ACL Support should be enabled])
-AC_ARG_ENABLE(nfsv4acls,
-       [  --enable-nfsv4acls      enable NFSv4 ACL Support (auto)],[
-       if test x"$enableval" = x"yes"; then
-          AC_MSG_RESULT([yes])
-          neta_cv_nfsv4acl="yes"
-       else
-          AC_MSG_RESULT([no])
-          neta_cv_nfsv4acl="no"
-       fi],[
-          AC_MSG_RESULT([auto])
-          neta_cv_nfsv4acl="yes"
-       ]
-)
-
-if test x"$this_os" != x"solaris" && test x"$neta_cv_nfsv4acl" = x"yes" ; then
-               AC_MSG_NOTICE([NFSv4 ACL Support only available on (Open)Solaris])
-        neta_cv_nfsv4acl="no"
+AC_MSG_CHECKING(whether to support ACLs)
+AC_ARG_WITH(acls,
+    [AS_HELP_STRING([--with-acls],
+        [Include ACL support (default=auto)])],
+    [ case "$withval" in
+      yes|no)
+          with_acl_support="$withval"
+                 ;;
+      *)
+          with_acl_support=auto
+          ;;
+      esac ],
+    [with_acl_support=auto])
+AC_MSG_RESULT($with_acl_support)
+
+if test x"$with_acl_support" = x"no"; then
+       AC_MSG_RESULT(Disabling ACL support)
+       AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support should be built in])
+else
+    with_acl_support=yes
+    AC_MSG_NOTICE([ACL support requires LDAP support, checking whether that's available])
+       AC_CHECK_HEADER([ldap.h],,
+        [AC_MSG_ERROR([ACL Support prerequisite LDAP client headers not found.])
+                   with_acl_support=no])
+
+       AC_CHECK_LIB(ldap, ldap_init,, 
+        [AC_MSG_ERROR([ACL Support prerequisite LDAP client libs not found.])
+                   with_acl_support=no])
 fi
 
-if test x$neta_cv_nfsv4acl = xyes; then
-       AC_CHECK_HEADER([ldap.h],,[
-               AC_MSG_ERROR([ACL Support need the LDAP client headers not found.])
-               neta_cv_nfsv4acl=no
-               ]
-       )
-       AC_CHECK_LIB(ldap,ldap_init,neta_cv_nfsv4acl=yes,neta_cv_nfsv4acl=no)
-fi
-if test x$neta_cv_nfsv4acl = xyes; then
-       AC_CHECK_HEADER([sys/acl.h],[
-               AC_DEFINE([HAVE_NFSv4_ACLS], 1, [Enable ACL code])
-               AC_MSG_NOTICE([Enabling ACL support])
-               ],
-               neta_cv_nfsv4acl=no
-       )
+if test x"$with_acl_support" = x"yes" ; then
+       AC_MSG_NOTICE(checking whether ACL support is available:)
+       case "$host_os" in
+       *sysv5*)
+               AC_MSG_NOTICE(Using UnixWare ACLs)
+               AC_DEFINE(HAVE_UNIXWARE_ACLS,1,[Whether UnixWare ACLs are available])
+               ;;
+       *solaris*)
+               AC_MSG_NOTICE(Using solaris ACLs)
+               AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether solaris ACLs are available])
+               ACL_LIBS="$ACL_LIBS -lsec"
+               ;;
+       *hpux*)
+               AC_MSG_NOTICE(Using HPUX ACLs)
+               AC_DEFINE(HAVE_HPUX_ACLS,1,[Whether HPUX ACLs are available])
+               ;;
+       *irix*)
+               AC_MSG_NOTICE(Using IRIX ACLs)
+               AC_DEFINE(HAVE_IRIX_ACLS,1,[Whether IRIX ACLs are available])
+               ;;
+       *aix*)
+               AC_MSG_NOTICE(Using AIX ACLs)
+               AC_DEFINE(HAVE_AIX_ACLS,1,[Whether AIX ACLs are available])
+               ;;
+       *osf*)
+               AC_MSG_NOTICE(Using Tru64 ACLs)
+               AC_DEFINE(HAVE_TRU64_ACLS,1,[Whether Tru64 ACLs are available])
+               ACL_LIBS="$ACL_LIBS -lpacl"
+               ;;
+       *darwin*)
+               AC_MSG_NOTICE(ACLs on Darwin currently not supported)
+               AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+               ;;
+       *)
+               AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+               case "$host_os" in
+               *linux*)
+                       AC_CHECK_LIB(attr,getxattr,[ACL_LIBS="$ACL_LIBS -lattr"])
+                       ;;
+               esac
+               AC_CACHE_CHECK([for POSIX ACL support],netatalk_cv_HAVE_POSIX_ACLS,[
+                       acl_LIBS=$LIBS
+                       LIBS="$LIBS $ACL_LIBS"
+                       AC_TRY_LINK([
+                               #include <sys/types.h>
+                               #include <sys/acl.h>
+                       ],[
+                               acl_t acl;
+                               int entry_id;
+                               acl_entry_t *entry_p;
+                               return acl_get_entry(acl, entry_id, entry_p);
+                       ],
+                       [netatalk_cv_HAVE_POSIX_ACLS=yes],
+                       [netatalk_cv_HAVE_POSIX_ACLS=no
+                with_acl_support=no])
+                       LIBS=$acl_LIBS
+               ])
+               if test x"$netatalk_cv_HAVE_POSIX_ACLS" = x"yes"; then
+                       AC_MSG_NOTICE(Using POSIX ACLs)
+                       AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+                       AC_CACHE_CHECK([for acl_get_perm_np],netatalk_cv_HAVE_ACL_GET_PERM_NP,[
+                               acl_LIBS=$LIBS
+                               LIBS="$LIBS $ACL_LIBS"
+                               AC_TRY_LINK([
+                                       #include <sys/types.h>
+                                       #include <sys/acl.h>
+                               ],[
+                                       acl_permset_t permset_d;
+                                       acl_perm_t perm;
+                                       return acl_get_perm_np(permset_d, perm);
+                               ],
+                               [samba_cv_HAVE_ACL_GET_PERM_NP=yes],
+                               [samba_cv_HAVE_ACL_GET_PERM_NP=no])
+                               LIBS=$acl_LIBS
+                       ])
+                       if test x"netatalk_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+                               AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+                       fi
+               else
+                       AC_MSG_NOTICE(ACL support is not avaliable)
+                       AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+               fi
+               ;;
+    esac
 fi
-if test x$neta_cv_nfsv4acl = xyes; then
-       LIBATALK_ACLS="acl/libacl.la"
-else
-       LIBATALK_ACLS=""
+
+if test x"$with_acl_support" = x"yes" ; then
+    AC_DEFINE(HAVE_ACLS,1,[Whether ACLs support is available])
+    AC_SUBST(ACL_LIBS)
 fi
-AC_SUBST(LIBATALK_ACLS)
 
 dnl --------------------- check for Extended Attributes support
 neta_cv_eas="ad"
@@ -1199,6 +1229,14 @@ fi
 dnl --------------------- Netatalk Webmin
 NETATALK_WEBMIN
 
+dnl --------------------- Check for libuuid which is required for TimeMachine
+AC_SEARCH_LIBS([uuid_generate],
+               [uuid], ,
+               AC_MSG_ERROR([missing library libuuid required for TimeMachine]))
+AC_CHECK_HEADER([uuid/uuid.h],
+                AC_DEFINE([HAVE_UUID], 1, [have libuuid]),
+                AC_MSG_ERROR([missing header <uuid/uuid.> from libuuid required for TimeMachine]))
+
 dnl --------------------- last minute substitutions
 
 AC_SUBST(LIBS)
@@ -1210,7 +1248,7 @@ AM_CONDITIONAL(COMPILE_TIMELORD, test x$compile_timelord = xyes)
 AM_CONDITIONAL(COMPILE_A2BOOT, test x$compile_a2boot = xyes)
 AM_CONDITIONAL(HAVE_LIBGCRYPT, test x$neta_cv_have_libgcrypt = xyes)
 AM_CONDITIONAL(HAVE_OPENSSL, test x$neta_cv_have_openssl = xyes)
-AM_CONDITIONAL(USE_NFSv4_ACLS, test x$neta_cv_nfsv4acl = xyes)
+AM_CONDITIONAL(HAVE_ACLS, test x"$with_acl_support" = x"yes")
 AM_CONDITIONAL(USE_DHX, test x$neta_cv_compile_dhx = xyes)
 AM_CONDITIONAL(USE_DHX2, test x$neta_cv_compile_dhx2 = xyes)
 AM_CONDITIONAL(USE_RANDNUM, test x$neta_cv_have_openssl = xyes)
@@ -1231,6 +1269,7 @@ AM_CONDITIONAL(USE_GENTOO, test x$sysv_style = xgentoo)
 AM_CONDITIONAL(USE_DEBIAN, test x$sysv_style = xdebian)
 AM_CONDITIONAL(USE_UNDEF, test x$sysv_style = x)
 AM_CONDITIONAL(USE_BDB, test x$bdb_required = xyes)
+AM_CONDITIONAL(USE_APPLETALK, test x$netatalk_cv_ddp_enabled = xyes)
 
 dnl --------------------- generate files
 
@@ -1285,6 +1324,7 @@ AC_OUTPUT([Makefile
        libatalk/adouble/Makefile
        libatalk/asp/Makefile
        libatalk/atp/Makefile
+       libatalk/bstring/Makefile
        libatalk/cnid/Makefile
        libatalk/cnid/cdb/Makefile
        libatalk/cnid/last/Makefile
@@ -1316,6 +1356,8 @@ AC_OUTPUT([Makefile
        sys/solaris/Makefile
        sys/sunos/Makefile
        sys/ultrix/Makefile
+       test/Makefile
+       test/afpd/Makefile
        ],
        [chmod a+x distrib/config/netatalk-config contrib/shell_utils/apple_*]
 )
index ae0ea4f003f6226870b7cda5e8766b5fbaaad15e..799369aec0960bc19be791cc0d3e792dca9f69a2 100644 (file)
@@ -1,17 +1,17 @@
 # Makefile.am for contrib/
 
+SUBDIRS = macusers shell_utils
+
 if COMPILE_TIMELORD
-TIMELORD = timelord
-else
-TIMELORD =
+SUBDIRS += timelord
 endif
 
 if COMPILE_A2BOOT
-A2BOOT = a2boot
-else
-A2BOOT =
+SUBDIRS += a2boot
 endif
 
-SUBDIRS = macusers printing shell_utils ${TIMELORD} ${A2BOOT}
+if USE_APPLETALK
+SUBDIRS += printing
+endif
 
 EXTRA_DIST = ICDumpSuffixMap
diff --git a/contrib/patches/patch.afp_vfs b/contrib/patches/patch.afp_vfs
deleted file mode 100644 (file)
index f2c08ee..0000000
+++ /dev/null
@@ -1,1678 +0,0 @@
-First try for a netatalk vfs layer
-
-current schemes
-adouble=v1,v2 classic adouble format
-adouble=osx  ._<filename> OSX resource fork.
-adouble=ads  NT like alternate data stream. 
-
-Note for ads:
-* cf. patch.vfs for samba ADS vfs layer and patch.samba.xx for samba tree patch.
-
-* It's using Afp_AfpInfo name (MS SFM name) but it's not yet compatible with SFM.
-  from cdrecord source code Afp_AfpInfo is the raw HFS data, we are storing an appledouble file.
-
-* Server side copy and Macintosh copy only deal with resource fork, other NT ADS are lost.
-  unfixable for Macintosh copy but doable for server side.
-
-* It's ok for rename, delete, chown and chmod.
-
-* Copy from a NT box should work.
-
-* Last but not least : only on a new volume!
-
-TODO
-indirection for metadata, aka stored in EA, a different file, whatever.
-
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/Makefile.am ./etc/afpd/Makefile.am
---- ../src.dev2/etc/afpd/Makefile.am   Mon Feb  9 22:45:51 2004
-+++ ./etc/afpd/Makefile.am     Fri Jun 18 19:15:47 2004
-@@ -8,14 +8,14 @@
-        file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
-        mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c  \
-        afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
--         catsearch.c afprun.c
-+         catsearch.c afprun.c vfs_adouble.c
- afpd_LDADD =  $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
- afpd_LDFLAGS = -export-dynamic
- noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.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
-+       uam_auth.h uid.h unix.h volume.h afp_vfs.h
- LIBS = @LIBS@ @PAM_LIBS@ @QUOTA_LIBS@ @SLP_LIBS@
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/afp_vfs.h ./etc/afpd/afp_vfs.h
---- ../src.dev2/etc/afpd/afp_vfs.h     Thu Jan  1 00:00:00 1970
-+++ ./etc/afpd/afp_vfs.h       Wed Jun 23 03:56:15 2004
-@@ -0,0 +1,49 @@
-+/*
-+   Copyright (c) 2004 Didier Gautheron
-+ 
-+   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.
-+ 
-+   You should have received a copy of the GNU General Public License
-+   along with this program; if not, write to the Free Software
-+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+
-+   vfs layer for afp
-+*/
-+
-+#ifndef _AFP_VFS_H
-+#define _AFP_VFS_H
-+
-+#include <atalk/adouble.h>
-+struct vol;
-+
-+struct vfs_ops {
-+    /* low level adouble fn */
-+    char *(*ad_path)(const char *, int);
-+
-+    /* */
-+    int (*validupath)(const struct vol *, const char *);
-+    int (*rf_chown)(const struct vol *, const char *path, uid_t owner, gid_t group);
-+    int (*rf_renamedir)(const struct vol *, const char *oldpath, const char *newpath);
-+    int (*rf_deletecurdir)(const struct vol *);
-+    int (*rf_setfilmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+    int (*rf_setdirmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+    int (*rf_setdirunixmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+
-+    int (*rf_setdirowner)(const struct vol *, const char *path, uid_t owner, gid_t group);
-+
-+    int (*rf_deletefile)(const struct vol *, const char * );
-+    int (*rf_renamefile)(const struct vol *, const char *oldpath, const char *newpath);
-+
-+};
-+
-+void initvol_vfs(struct vol *vol);
-+
-+#endif
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.c ./etc/afpd/directory.c
---- ../src.dev2/etc/afpd/directory.c   Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/directory.c     Sat Jun 19 14:07:14 2004
-@@ -610,7 +610,7 @@
-    system rmdir with afp error code.
-    ENOENT is not an error.
-  */
--static int netatalk_rmdir(const char *name)
-+int netatalk_rmdir(const char *name)
- {
-     if (rmdir(name) < 0) {
-         switch ( errno ) {
-@@ -2075,15 +2075,11 @@
-         }
-     }
--    if (vol->v_adouble == AD_VERSION2_OSX) {
--        /* We simply move the corresponding ad file as well */
--        char   tempbuf[258]="._";
--        rename(vol->ad_path(src,0),strcat(tempbuf,dst));
--    }
-+    vol->vfs->rf_renamedir(vol, src, dst);
-     len = strlen( newname );
-     /* rename() succeeded so we need to update our tree even if we can't open
--     * .Parent
-+     * metadata
-     */
-     
-     ad_init(&ad, vol->v_adouble);
-@@ -2132,12 +2128,9 @@
-     return( AFP_OK );
- }
--#define DOT_APPLEDOUBLE_LEN 13
- /* delete an empty directory */
--int deletecurdir( vol, path, pathlen )
-+int deletecurdir( vol)
- const struct vol      *vol;
--char *path;
--int pathlen;
- {
-     struct dirent *de;
-     struct stat st;
-@@ -2162,42 +2155,9 @@
-             return  AFPERR_OLOCK;
-         }
-     }
--
--    if (vol->v_adouble == AD_VERSION2_OSX) {
--       
--        if ((err = netatalk_unlink(vol->ad_path(".",0) )) ) {
--            return err;
--        }
--    }
--    else {
--        /* delete stray .AppleDouble files. this happens to get .Parent files
--           as well. */
--        if ((dp = opendir(".AppleDouble"))) {
--            strcpy(path, ".AppleDouble/");
--            while ((de = readdir(dp))) {
--                /* skip this and previous directory */
--                if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
--                    continue;
--
--                /* bail if the file exists in the current directory.
--                 * note: this will not fail with dangling symlinks */
--                if (stat(de->d_name, &st) == 0) {
--                    closedir(dp);
--                    return AFPERR_DIRNEMPT;
--                }
--
--                strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
--                if ((err = netatalk_unlink(path))) {
--                    closedir(dp);
--                    return err;
--                }
--            }
--            closedir(dp);
--        }
--
--        if ( (err = netatalk_rmdir( ".AppleDouble" ))  ) {
--            return err;
--        }
-+    err = vol->vfs->rf_deletecurdir(vol);
-+    if (err) {
-+        return err;
-     }
-     /* now get rid of dangling symlinks */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.h ./etc/afpd/directory.h
---- ../src.dev2/etc/afpd/directory.h   Mon May 10 18:40:32 2004
-+++ ./etc/afpd/directory.h     Sat Jun 19 03:23:18 2004
-@@ -196,7 +196,7 @@
- extern struct dir       *dirinsert __P((struct vol *, struct dir *));
- extern int              movecwd __P((const struct vol *, struct dir *));
--extern int              deletecurdir __P((const struct vol *, char *, int));
-+extern int              deletecurdir __P((const struct vol *));
- extern struct path      *cname __P((const struct vol *, struct dir *,
-                              char **));
- extern mode_t           mtoumode __P((struct maccess *));
-@@ -215,6 +215,7 @@
- extern int  check_access __P((char *name , int mode));
- extern int file_access   __P((struct path *path, int mode));
-+extern int netatalk_rmdir __P((const char *name));
- extern int netatalk_unlink __P((const char *name));
- /* from enumerate.c */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/enumerate.c ./etc/afpd/enumerate.c
---- ../src.dev2/etc/afpd/enumerate.c   Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/enumerate.c     Thu Jun 24 04:26:35 2004
-@@ -166,7 +166,7 @@
-     if (!strcmp(name, "..") || !strcmp(name, "."))
-         return NULL;
--    if (!vol->validupath(vol, name))
-+    if (!vol->vfs->validupath(vol, name))
-         return NULL;
-     /* check for vetoed filenames */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/file.c ./etc/afpd/file.c
---- ../src.dev2/etc/afpd/file.c        Tue Jun 15 22:53:54 2004
-+++ ./etc/afpd/file.c  Mon Jun 21 00:21:24 2004
-@@ -901,7 +901,7 @@
-     /* second try with adouble open 
-     */
--    if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
-+    if ( ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
-                  O_RDWR|O_CREAT, 0666, adp) < 0) {
-         /* for some things, we don't need an adouble header */
-         if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
-@@ -1020,7 +1020,6 @@
- char  *src, *dst, *newname;
- struct adouble    *adp;
- {
--    char      adsrc[ MAXPATHLEN + 1];
-     int               rc;
- #ifdef DEBUG
-@@ -1055,38 +1054,10 @@
-         }
-     }
--    strcpy( adsrc, vol->ad_path( src, 0 ));
--
--    if (unix_rename( adsrc, vol->ad_path( dst, 0 )) < 0 ) {
--        struct stat st;
-+    if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
-         int err;
-         
-         err = errno;        
--      if (errno == ENOENT) {
--          struct adouble    ad;
--
--            if (stat(adsrc, &st)) /* source has no ressource fork, */
--                return AFP_OK;
--            
--            /* We are here  because :
--             * -there's no dest folder. 
--             * -there's no .AppleDouble in the dest folder.
--             * if we use the struct adouble passed in parameter it will not
--             * create .AppleDouble if the file is already opened, so we
--             * use a diff one, it's not a pb,ie it's not the same file, yet.
--             */
--            ad_init(&ad, vol->v_adouble); 
--            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 )) ) 
--                   err = 0;
--                else 
--                   err = errno;
--            }
--            else { /* it's something else, bail out */
--              err = errno;
--          }
--      }
-       /* try to undo the data fork rename,
-        * we know we are on the same device 
-       */
-@@ -1436,6 +1407,7 @@
-     if (ret_err) {
-         deletefile(d_vol, dst, 0);
-     }
-+    /* ADS here */
-     /* set dest modification date to src date */
-     if (!stat(src, &st)) {
-@@ -1562,14 +1534,12 @@
-     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
-         err = AFPERR_BUSY;
-     }
--    else if (!(err = netatalk_unlink( vol->ad_path( file, ADFLAGS_HF)) ) &&
--             !(err = netatalk_unlink( file )) ) {
-+    else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
-         cnid_t id;
-         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
-         {
-             cnid_delete(vol->v_cdb, id);
-         }
--
-     }
-     if (adp)
-         ad_close( &ad, adflags );  /* ad_close removes locks if any */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/filedir.c ./etc/afpd/filedir.c
---- ../src.dev2/etc/afpd/filedir.c     Mon May 10 18:40:32 2004
-+++ ./etc/afpd/filedir.c       Sat Jun 19 15:09:08 2004
-@@ -73,7 +73,7 @@
-         return AFPERR_NOOBJ ;
-     }
--    adpath = vol->ad_path( upath, ADFLAGS_HF );
-+    adpath = vol->vfs->ad_path( upath, ADFLAGS_HF );
-     /* FIXME dirsearch doesn't move cwd to did ! */
-     if (( dir = dirlookup( vol, did )) == NULL ) {
-         LOG(log_error, logtype_afpd, "matchfile2dirperms: Unable to get directory info.");
-@@ -313,7 +313,7 @@
-     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(name, '/'))
-         return AFPERR_PARAM;
--    if (!vol->validupath(vol, name))
-+    if (!vol->vfs->validupath(vol, name))
-         return AFPERR_EXIST;
-     /* check for vetoed filenames */
-@@ -582,7 +582,7 @@
-           rc = AFPERR_ACCESS;
-       }
-       else {
--            rc = deletecurdir( vol, obj->oldtmp, AFPOBJ_TMPSIZ);
-+            rc = deletecurdir( vol);
-         }
-     } else if (of_findname(s_path)) {
-         rc = AFPERR_BUSY;
-@@ -764,7 +764,7 @@
-                 int  admode = ad_mode("", 0777);
-                 setfilmode(upath, admode, NULL);
--                setfilmode(vol->ad_path( upath, ADFLAGS_HF ), ad_hf_mode(admode), NULL);
-+                vol->vfs->rf_setfilmode(vol, upath, admode, NULL);
-             }
-         setvoltime(obj, vol );
-     }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.c ./etc/afpd/unix.c
---- ../src.dev2/etc/afpd/unix.c        Tue Jun 15 22:53:55 2004
-+++ ./etc/afpd/unix.c  Wed Jun 23 04:04:01 2004
-@@ -260,8 +260,8 @@
-    rwx-wx-wx or rwx-wx-- 
-    rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
- */
--static int stickydirmode(name, mode, dropbox)
--char * name;
-+int stickydirmode(name, mode, dropbox)
-+const char * name;
- const mode_t mode;
- const int dropbox;
- {
-@@ -405,12 +405,12 @@
-     if (setfilmode( path->u_name, mode, &path->st) < 0)
-         return -1;
-     /* we need to set write perm if read set for resource fork */
--    return setfilmode(vol->ad_path( path->u_name, ADFLAGS_HF ), ad_hf_mode(mode), &path->st);
-+    return vol->vfs->rf_setfilmode(vol, path->u_name, mode, &path->st);
- }
- /* --------------------- */
- int setfilmode(name, mode, st)
--char * name;
-+const char * name;
- mode_t mode;
- struct stat *st;
- {
-@@ -436,29 +436,18 @@
- const char       *name;
- const mode_t     mode;
- {
--char *adouble = vol->ad_path( name, ADFLAGS_DIR );
-     int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-     if (dir_rx_set(mode)) {
--      /* extending right? dir first then .AppleDouble */
-+      /* extending right? dir first then .AppleDouble in rf_setdirmode */
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-               return -1;
--      if (vol->v_adouble != AD_VERSION2_OSX) {
--            if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
--                return  -1 ;
--            }
--        }
-     }
--    if (setfilmode(adouble, ad_hf_mode(mode), NULL) < 0 && !vol_noadouble(vol)) {
-+    if (vol->vfs->rf_setdirunixmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
-         return  -1 ;
-     }
-     if (!dir_rx_set(mode)) {
--      if (vol->v_adouble != AD_VERSION2_OSX) {
--            if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
--                return  -1 ;
--            }
--        }
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-             return -1;
-     }
-@@ -471,26 +460,17 @@
- const char       *name;
- const mode_t mode;
- {
--    char              buf[ MAXPATHLEN + 1];
-     struct stat               st;
--    char              *m;
-     struct dirent     *dirp;
-     DIR                       *dir;
-     int                 osx = vol->v_adouble == AD_VERSION2_OSX;
-     int                 hf_mode = ad_hf_mode(mode);
-     int                 dropbox = (vol->v_flags & AFPVOL_DROPBOX);
--    char                *adouble = vol->ad_path( name, ADFLAGS_DIR );
--    char                *adouble_p = ad_dir(adouble);
-     
-     if (dir_rx_set(mode)) {
--      /* extending right? dir first then .AppleDouble */
-+      /* extending right? dir first */
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-               return -1;
--      if (!osx) {
--            if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
--                return  -1 ;
--            }
--        }
-     }
-     
-     if (( dir = opendir( name )) == NULL ) {
-@@ -516,61 +496,13 @@
-                 return -1;
-            }
-         }
--#if 0
--        /* Don't change subdir perm */
--        else if (S_ISDIR(st.st_mode)) {
--                if (stickydirmode(dirp->d_name, DIRBITS | mode, dropbox) < 0)
--                    return (-1);
--            } else if (stickydirmode(dirp->d_name, mode, dropbox) < 0)
--                return (-1);
--        }
--#endif
-     }
-     closedir( dir );
-     
--    if (osx) {
--        goto setdirmode_noadouble;
--    }
--    
--    /* change perm of .AppleDouble's files
--    */
--    if (( dir = opendir( adouble_p )) == NULL ) {
--        if (vol_noadouble(vol))
--            goto setdirmode_noadouble;
--        LOG(log_error, logtype_afpd, "setdirmode: opendir %s: %s", fullpathname(".AppleDouble"),strerror(errno) );
--        return( -1 );
--    }
--    strcpy( buf, adouble_p);
--    strcat( buf, "/" );
--    m = strchr( buf, '\0' );
--    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
--        if ( strcmp( dirp->d_name, "." ) == 0 ||
--                strcmp( dirp->d_name, ".." ) == 0 ) {
--            continue;
--        }
--        *m = '\0';
--        strcat( buf, dirp->d_name );
--
--        if ( stat( buf, &st ) < 0 ) {
--            LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", buf, strerror(errno) );
--            continue;
--        }
--        if (!S_ISDIR(st.st_mode)) {
--           if (setfilmode(buf, hf_mode , &st) < 0) {
--               /* FIXME what do we do then? */
--           }
--        }
--    } /* end for */
--    closedir( dir );
--
--    if (!dir_rx_set(mode)) {
--        /* XXX: need to preserve special modes */
--        if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 ) {
--                return  -1 ;
--        }
-+    if (vol->vfs->rf_setdirmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
-+        return  -1 ;
-     }
--setdirmode_noadouble:
-     if (!dir_rx_set(mode)) {
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-               return -1;
-@@ -578,6 +510,7 @@
-     return( 0 );
- }
-+/* ----------------------------- */
- int setdeskowner( uid, gid )
- const uid_t   uid;
- const gid_t   gid;
-@@ -648,8 +581,6 @@
- const gid_t   gid;
- struct path* path;
- {
--    struct stat st;
--    char  *ad_p;
-     if (!path->st_valid) {
-         of_stat(path);
-@@ -665,22 +596,15 @@
-       return -1;
-     }
--    ad_p = vol->ad_path( path->u_name, ADFLAGS_HF );
--
--    if ( stat( ad_p, &st ) < 0 ) {
--      /* ignore */
--        return 0;
--    }
--    if ( chown( ad_p, uid, gid ) < 0 &&
--            errno != EPERM ) {
--        LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s",
--            uid, gid, ad_p, strerror(errno) );
-+    if (vol->vfs->rf_chown(vol, path->u_name, uid, gid ) < 0 && errno != EPERM) {
-+        LOG(log_debug, logtype_afpd, "setfilowner: rf_chown %d/%d %s: %s",
-+            uid, gid, path->u_name, strerror(errno) );
-         return -1;
-     }
-+
-     return 0;
- }
--
- /* --------------------------------- 
-  * uid/gid == 0 need to be handled as special cases. they really mean
-  * that user/group should inherit from other, but that doesn't fit
-@@ -692,15 +616,10 @@
- const uid_t   uid;
- const gid_t   gid;
- {
--    char              buf[ MAXPATHLEN + 1];
-     struct stat               st;
--    char              *m;
-     struct dirent     *dirp;
-     DIR                       *dir;
-     int                 osx = vol->v_adouble == AD_VERSION2_OSX;
--    int                 noadouble = vol_noadouble(vol);
--    char                *adouble; 
--    char                *adouble_p;
-     if (( dir = opendir( name )) == NULL ) {
-         return( -1 );
-@@ -723,56 +642,15 @@
-         }
-     }
-     closedir( dir );
--    
--    if (osx) {
--       goto setdirowner_noadouble;
--    }
--    adouble = vol->ad_path( name, ADFLAGS_DIR );
--    adouble_p = ad_dir(adouble);
--    if (( dir = opendir( adouble_p )) == NULL ) {
--        if (noadouble)
--            goto setdirowner_noadouble;
--        return( -1 );
--    }
--    strcpy( buf, adouble_p );
--    strcat( buf, "/" );
--    m = strchr( buf, '\0' );
--    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
--        if ( strcmp( dirp->d_name, "." ) == 0 ||
--                strcmp( dirp->d_name, ".." ) == 0 ) {
--            continue;
--        }
--        *m = '\0';
--        strcat( buf, dirp->d_name );
--        if ( chown( buf, uid, gid ) < 0 && errno != EPERM ) {
--            LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
--                uid, gid, fullpathname(buf), strerror(errno) );
--            /* return ( -1 ); Sometimes this is okay */
--        }
--    }
--    closedir( dir );
--
--    /*
--     * We cheat: we know that chown doesn't do anything.
--     */
--    if ( stat( ".AppleDouble", &st ) < 0 ) {
--        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
--        return( -1 );
--    }
--    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 &&
--            errno != EPERM ) {
--        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
--            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
--        /* return ( -1 ); Sometimes this is okay */
-+    if (vol->vfs->rf_setdirowner(vol, name, uid, gid) < 0) {
-+        return -1;
-     }
--
--setdirowner_noadouble:
-+    
-     if ( stat( ".", &st ) < 0 ) {
-         return( -1 );
-     }
--    if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 &&
--            errno != EPERM ) {
-+    if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 && errno != EPERM ) {
-         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-             uid, gid, fullpathname("."), strerror(errno) );
-     }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.h ./etc/afpd/unix.h
---- ../src.dev2/etc/afpd/unix.h        Mon May 10 18:40:33 2004
-+++ ./etc/afpd/unix.h  Wed Jun 23 03:43:28 2004
-@@ -221,11 +221,12 @@
- extern int setdirmode       __P((const struct vol *, const char *, const mode_t));
- extern int setdeskowner     __P((const uid_t, const gid_t));
- extern int setdirowner      __P((const struct vol *, const char *, const uid_t, const gid_t));
--extern int setfilmode       __P((char *, mode_t , struct stat *));
-+extern int setfilmode       __P((const char *, mode_t , struct stat *));
- extern int setfilunixmode   __P((const struct vol *, struct path*, const mode_t));
- extern int setfilowner      __P((const struct vol *, const uid_t, const gid_t, struct path*));
- extern int unix_rename      __P((const char *oldpath, const char *newpath));
- extern int dir_rx_set       __P((mode_t mode));
-+extern int stickydirmode    __P((const char * name, const mode_t mode, const int dropbox));
- extern void accessmode      __P((char *, struct maccess *, struct dir *, struct stat *));
- extern char *fullpathname   __P((const char *));
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/vfs_adouble.c ./etc/afpd/vfs_adouble.c
---- ../src.dev2/etc/afpd/vfs_adouble.c Thu Jan  1 00:00:00 1970
-+++ ./etc/afpd/vfs_adouble.c   Wed Jun 30 19:31:49 2004
-@@ -0,0 +1,749 @@
-+/*
-+    Copyright (c) 2004 Didier Gautheron
-+ 
-+   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.
-+ 
-+   You should have received a copy of the GNU General Public License
-+   along with this program; if not, write to the Free Software
-+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ 
-+*/
-+#ifdef HAVE_CONFIG_H
-+#include "config.h"
-+#endif /* HAVE_CONFIG_H */
-+
-+#ifdef STDC_HEADERS
-+#include <string.h>
-+#endif
-+
-+#include <stdio.h>
-+    
-+#include <atalk/adouble.h>
-+#include <atalk/logger.h>
-+#include <atalk/util.h>
-+
-+#include "directory.h"
-+#include "volume.h"
-+#include "unix.h"
-+
-+struct perm {
-+    uid_t uid;
-+    gid_t gid;
-+};
-+
-+typedef int (*rf_loop)(struct dirent *, char *, void *, int );
-+
-+/* ----------------------------- */
-+static int 
-+for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag)
-+{
-+    char            buf[ MAXPATHLEN + 1];
-+    char            *m;
-+    DIR             *dp;
-+    struct dirent   *de;
-+    int             ret;
-+    
-+
-+    if (NULL == ( dp = opendir( name)) ) {
-+        if (!flag) {
-+            LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) );
-+            return -1;
-+        }
-+        return 0;
-+    }
-+    strlcpy( buf, name, sizeof(buf));
-+    strlcat( buf, "/", sizeof(buf) );
-+    m = strchr( buf, '\0' );
-+    ret = 0;
-+    while ((de = readdir(dp))) {
-+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-+                continue;
-+        }
-+        
-+        strlcat(buf, de->d_name, sizeof(buf));
-+        if (fn && (ret = fn(de, buf, data, flag))) {
-+           closedir(dp);
-+           return ret;
-+        }
-+        *m = 0;
-+    }
-+    closedir(dp);
-+    return ret;
-+}
-+
-+/* ------------------------------ */
-+static int ads_chown_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+    
-+    if (chown( name , owner->uid, owner->gid ) < 0) {
-+        return -1;
-+    }
-+    return 0;
-+}
-+
-+static int RF_chown_ads(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+
-+{
-+    struct        stat st;
-+    char          *ad_p;
-+    struct perm   owner;
-+    
-+    owner.uid = uid;
-+    owner.gid = gid;
-+
-+
-+    ad_p = ad_dir(vol->vfs->ad_path(path, ADFLAGS_HF ));
-+
-+    if ( stat( ad_p, &st ) < 0 ) {
-+      /* ignore */
-+        return 0;
-+    }
-+    
-+    if (chown( ad_p, uid, gid ) < 0) {
-+      return -1;
-+    }
-+    return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1);
-+}
-+
-+/* --------------------------------- */
-+static int deletecurdir_ads1_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    return netatalk_unlink(name);
-+}
-+
-+static int ads_delete_rf(char *name) 
-+{
-+    int err;
-+
-+    if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1))) 
-+        return err;
-+    return netatalk_rmdir(name);
-+}
-+
-+static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct stat st;
-+    
-+    /* bail if the file exists in the current directory.
-+     * note: this will not fail with dangling symlinks */
-+    
-+    if (stat(de->d_name, &st) == 0) {
-+        return AFPERR_DIRNEMPT;
-+    }
-+    return ads_delete_rf(name);
-+}
-+
-+static int RF_deletecurdir_ads(const struct vol *vol)
-+{
-+    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))) 
-+        return err;
-+    return netatalk_rmdir( ".AppleDouble" );
-+}
-+
-+/* ------------------- */
-+struct set_mode {
-+    mode_t mode;
-+    struct stat *st;
-+};
-+
-+static int ads_setfilmode_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct set_mode *param = data;
-+
-+    return setfilmode(name, param->mode, param->st);
-+}
-+
-+static int ads_setfilmode(const char * name, mode_t mode, struct stat *st)
-+{
-+    mode_t dir_mode = mode;
-+    mode_t file_mode = ad_hf_mode(mode);
-+    struct set_mode param;
-+
-+    if ((dir_mode & (S_IRUSR | S_IWUSR )))
-+        dir_mode |= S_IXUSR;
-+    if ((dir_mode & (S_IRGRP | S_IWGRP )))
-+        dir_mode |= S_IXGRP;
-+    if ((dir_mode & (S_IROTH | S_IWOTH )))
-+        dir_mode |= S_IXOTH;  
-+    
-+      /* change folder */
-+      dir_mode |= DIRBITS;
-+    if (dir_rx_set(dir_mode)) {
-+        if (chmod( name,  dir_mode ) < 0)
-+            return -1;
-+    }
-+    param.st = st;
-+    param.mode = file_mode;
-+    if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, &param, 0) < 0)
-+        return -1;
-+
-+    if (!dir_rx_set(dir_mode)) {
-+        if (chmod( name,  dir_mode ) < 0)
-+            return -1;
-+    }
-+
-+    return 0;
-+}
-+
-+static int RF_setfilmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_HF )), mode, st);
-+}
-+
-+/* ------------------- */
-+static int RF_setdirunixmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    char   ad_p[ MAXPATHLEN + 1];
-+    int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+    strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1);
-+
-+    if (dir_rx_set(mode)) {
-+
-+        /* .AppleDouble */
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+
-+        /* .AppleDouble/.Parent */
-+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR)), mode, st) < 0)
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return  -1 ;
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+    return 0;
-+}
-+
-+/* ------------------- */
-+struct dir_mode {
-+    mode_t mode;
-+    int    dropbox;
-+};
-+
-+static int setdirmode_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+
-+    struct dir_mode *param = data;
-+    int    ret = 0; /* 0 ignore error, -1 */
-+
-+    if (dir_rx_set(param->mode)) {
-+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
-+            if (flag) {
-+                return 0;
-+            }
-+            return ret;
-+        }
-+    }
-+    if (ads_setfilmode(name, param->mode, NULL) < 0)
-+        return ret;
-+
-+    if (!dir_rx_set(param->mode)) {
-+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
-+            if (flag) {
-+                return 0;
-+            }
-+            return ret;
-+        }
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    char   ad_p[ MAXPATHLEN + 1];
-+    struct dir_mode param;
-+
-+    param.mode = mode;
-+    param.dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+    strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p));
-+
-+    if (dir_rx_set(mode)) {
-+        /* .AppleDouble */
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, &param, vol_noadouble(vol)))
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+    return 0;
-+}
-+
-+/* ------------------- */
-+static int setdirowner_ads1_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+
-+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+         /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+static int setdirowner_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+
-+    if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag) < 0)
-+        return -1;
-+
-+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+         /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirowner_ads(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
-+{
-+    int           noadouble = vol_noadouble(vol);
-+    char          adouble_p[ MAXPATHLEN + 1];
-+    struct stat   st;
-+    struct perm   owner;
-+    
-+    owner.uid = uid;
-+    owner.gid = gid;
-+
-+    strlcpy(adouble_p, ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR )), sizeof(adouble_p));
-+
-+    if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble)) 
-+        return -1;
-+
-+    /*
-+     * We cheat: we know that chown doesn't do anything.
-+     */
-+    if ( stat( ".AppleDouble", &st ) < 0) {
-+        if (errno == ENOENT && noadouble)
-+            return 0;
-+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-+        return -1;
-+    }
-+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
-+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-+        /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+/* ------------------- */
-+static int RF_deletefile_ads(const struct vol *vol, const char *file )
-+{
-+    char *ad_p = ad_dir(vol->vfs->ad_path(file, ADFLAGS_HF ));
-+
-+    return ads_delete_rf(ad_p);
-+}
-+
-+/* --------------------------- */
-+int RF_renamefile_ads(const struct vol *vol, const char *src, const char *dst)
-+{
-+    char  adsrc[ MAXPATHLEN + 1];
-+    int   err = 0;
-+
-+    strcpy( adsrc, ad_dir(vol->vfs->ad_path( src, 0 )));
-+    if (unix_rename( adsrc, ad_dir(vol->vfs->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, */
-+                return AFP_OK;
-+            
-+            /* We are here  because :
-+             * -there's no dest folder. 
-+             * -there's no .AppleDouble in the dest folder.
-+             * if we use the struct adouble passed in parameter it will not
-+             * create .AppleDouble if the file is already opened, so we
-+             * use a diff one, it's not a pb,ie it's not the same file, yet.
-+             */
-+            ad_init(&ad, vol->v_adouble); 
-+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-+              ad_close(&ad, ADFLAGS_HF);
-+
-+              /* We must delete it */
-+              RF_deletefile_ads(vol, dst );
-+              if (!unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) ) 
-+                   err = 0;
-+                else 
-+                   err = errno;
-+            }
-+            else { /* it's something else, bail out */
-+                  err = errno;
-+              }
-+          }
-+      }
-+      if (err) {
-+              errno = err;
-+              return -1;
-+      }
-+      return 0;
-+}
-+
-+/* ===================================================
-+ classic adouble format 
-+*/
-+
-+static int validupath_adouble(const struct vol *vol, const char *name) 
-+{
-+    return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
-+                                           : name[0] != '.';
-+}
-+
-+/* ----------------- */
-+static int RF_chown_adouble(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+
-+{
-+    struct stat st;
-+    char        *ad_p;
-+
-+    ad_p = vol->vfs->ad_path(path, ADFLAGS_HF );
-+
-+    if ( stat( ad_p, &st ) < 0 )
-+        return 0; /* ignore */
-+
-+    return chown( ad_p, uid, gid );
-+}
-+
-+/* ----------------- */
-+int RF_renamedir_adouble(const struct vol *vol, const char *oldpath, const char *newpath)
-+{
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct stat st;
-+    int         err;
-+    
-+    /* bail if the file exists in the current directory.
-+     * note: this will not fail with dangling symlinks */
-+    
-+    if (stat(de->d_name, &st) == 0)
-+        return AFPERR_DIRNEMPT;
-+
-+    if ((err = netatalk_unlink(name)))
-+        return err;
-+
-+    return 0;
-+}
-+
-+static int RF_deletecurdir_adouble(const struct vol *vol)
-+{
-+    int err;
-+
-+    /* delete stray .AppleDouble files. this happens to get .Parent files
-+       as well. */
-+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1))) 
-+        return err;
-+    return netatalk_rmdir( ".AppleDouble" );
-+}
-+
-+/* ----------------- */
-+static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st)
-+{
-+    return setfilmode(name, ad_hf_mode(mode), st);
-+}
-+
-+static int RF_setfilmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_HF ), mode, st);
-+}
-+
-+/* ----------------- */
-+static int RF_setdirunixmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    int  dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+    if (dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st) < 0) 
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return  -1 ;
-+    }
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int setdirmode_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    int         hf_mode = *(int *)data;
-+    struct stat st;
-+
-+    if ( stat( name, &st ) < 0 ) {
-+        if (flag)
-+            return 0;
-+        LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
-+    }
-+    else if (!S_ISDIR(st.st_mode)) {
-+        if (setfilmode(name, hf_mode , &st) < 0) {
-+               /* FIXME what do we do then? */
-+        }
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st1)
-+{
-+    int   dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+    int   hf_mode = ad_hf_mode(mode);
-+    char  *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    char  *adouble_p = ad_dir(adouble);
-+
-+    if (dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol)))
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return  -1 ;
-+    }
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int setdirowner_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+
-+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+         /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirowner_adouble(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
-+
-+{
-+    int           noadouble = vol_noadouble(vol);
-+    char          *adouble_p;
-+    struct stat   st;
-+    struct perm   owner;
-+    
-+    owner.uid = uid;
-+    owner.gid = gid;
-+
-+    adouble_p = ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR ));
-+
-+    if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble)) 
-+        return -1;
-+
-+    /*
-+     * We cheat: we know that chown doesn't do anything.
-+     */
-+    if ( stat( ".AppleDouble", &st ) < 0) {
-+        if (errno == ENOENT && noadouble)
-+            return 0;
-+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-+        return -1;
-+    }
-+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
-+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-+        /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int RF_deletefile_adouble(const struct vol *vol, const char *file )
-+{
-+      return netatalk_unlink(vol->vfs->ad_path( file, ADFLAGS_HF));
-+}
-+
-+/* ----------------- */
-+int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *dst)
-+{
-+    char  adsrc[ MAXPATHLEN + 1];
-+    int   err = 0;
-+
-+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
-+    if (unix_rename( adsrc, vol->vfs->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, */
-+                return AFP_OK;
-+            
-+            /* We are here  because :
-+             * -there's no dest folder. 
-+             * -there's no .AppleDouble in the dest folder.
-+             * if we use the struct adouble passed in parameter it will not
-+             * create .AppleDouble if the file is already opened, so we
-+             * use a diff one, it's not a pb,ie it's not the same file, yet.
-+             */
-+            ad_init(&ad, vol->v_adouble); 
-+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-+              ad_close(&ad, ADFLAGS_HF);
-+              if (!unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) ) 
-+                   err = 0;
-+                else 
-+                   err = errno;
-+            }
-+            else { /* it's something else, bail out */
-+                  err = errno;
-+              }
-+          }
-+      }
-+      if (err) {
-+              errno = err;
-+              return -1;
-+      }
-+      return 0;
-+}
-+
-+struct vfs_ops netatalk_adouble = {
-+    /* ad_path:           */ ad_path,
-+    /* validupath:        */ validupath_adouble,
-+    /* rf_chown:          */ RF_chown_adouble,
-+    /* rf_renamedir:      */ RF_renamedir_adouble,
-+    /* rf_deletecurdir:   */ RF_deletecurdir_adouble,
-+    /* rf_setfilmode:     */ RF_setfilmode_adouble,
-+    /* rf_setdirmode:     */ RF_setdirmode_adouble,
-+    /* rf_setdirunixmode: */ RF_setdirunixmode_adouble,
-+    /* rf_setdirowner:    */ RF_setdirowner_adouble,
-+    /* rf_deletefile:     */ RF_deletefile_adouble,
-+    /* rf_renamefile:     */ RF_renamefile_adouble,
-+};
-+
-+/* =======================================
-+ osx adouble format 
-+ */
-+static int validupath_osx(const struct vol *vol, const char *name) 
-+{
-+    return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
-+}
-+
-+/* ---------------- */
-+int RF_renamedir_osx(const struct vol *vol, const char *oldpath, const char *newpath)
-+{
-+    /* We simply move the corresponding ad file as well */
-+    char   tempbuf[258]="._";
-+    return rename(vol->vfs->ad_path(oldpath,0),strcat(tempbuf,newpath));
-+}
-+
-+/* ---------------- */
-+int RF_deletecurdir_osx(const struct vol *vol)
-+{
-+    return netatalk_unlink( vol->vfs->ad_path(".",0) );
-+}
-+
-+/* ---------------- */
-+static int RF_setdirunixmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st);
-+}
-+
-+/* ---------------- */
-+static int RF_setdirmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return 0;
-+}
-+
-+/* ---------------- */
-+static int RF_setdirowner_osx(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+{
-+      return 0;
-+}
-+
-+/* ---------------- */
-+int RF_renamefile_osx(const struct vol *vol, const char *src, const char *dst)
-+{
-+    char  adsrc[ MAXPATHLEN + 1];
-+
-+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
-+    return unix_rename( adsrc, vol->vfs->ad_path( dst, 0 ));
-+}
-+
-+struct vfs_ops netatalk_adouble_osx = {
-+    /* ad_path:          */ ad_path_osx,
-+    /* validupath:       */ validupath_osx,
-+    /* rf_chown:         */ RF_chown_adouble,
-+    /* rf_renamedir:     */ RF_renamedir_osx,
-+    /* rf_deletecurdir:  */ RF_deletecurdir_osx,
-+    /* rf_setfilmode:    */ RF_setfilmode_adouble,
-+    /* rf_setdirmode:    */ RF_setdirmode_osx,
-+    /* rf_setdirunixmode:*/ RF_setdirunixmode_osx,
-+    /* rf_setdirowner:   */ RF_setdirowner_osx,
-+    /* rf_deletefile:    */ RF_deletefile_adouble,
-+    /* rf_renamefile:    */ RF_renamefile_osx,
-+};
-+
-+/* =======================================
-+   samba ads format 
-+ */
-+struct vfs_ops netatalk_adouble_ads = {
-+    /* ad_path:          */ ad_path_ads,
-+    /* validupath:       */ validupath_adouble,
-+    /* rf_chown:         */ RF_chown_ads,
-+    /* rf_renamedir:     */ RF_renamedir_adouble,
-+    /* rf_deletecurdir:  */ RF_deletecurdir_ads,
-+    /* rf_setfilmode:    */ RF_setfilmode_ads,
-+    /* rf_setdirmode:    */ RF_setdirmode_ads,
-+    /* rf_setdirunixmode:*/ RF_setdirunixmode_ads,
-+    /* rf_setdirowner:   */ RF_setdirowner_ads,
-+    /* rf_deletefile:    */ RF_deletefile_ads,
-+    /* rf_renamefile:    */ RF_renamefile_ads,
-+};
-+
-+/* ---------------- */
-+void initvol_vfs(struct vol *vol)
-+{
-+    if (vol->v_adouble == AD_VERSION2_OSX) {
-+        vol->vfs = &netatalk_adouble_osx;
-+    }
-+    else if (vol->v_adouble == AD_VERSION1_ADS) {
-+        vol->vfs = &netatalk_adouble_ads;
-+    }
-+    else {
-+        vol->vfs = &netatalk_adouble;
-+    }
-+}
-+
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.c ./etc/afpd/volume.c
---- ../src.dev2/etc/afpd/volume.c      Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/volume.c        Mon Jul 12 00:29:11 2004
-@@ -427,6 +427,8 @@
-             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
-         else if (strcasecmp(val + 1, "osx") == 0)
-             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2_OSX;
-+        else if (strcasecmp(val + 1, "ads") == 0)
-+            options[VOLOPT_ADOUBLE].i_value = AD_VERSION1_ADS;
- #endif
-     } else if (optionok(tmp, "options:", val)) {
-         char *p;
-@@ -523,34 +525,6 @@
-     }
- }
--/* ----------------- 
-- * FIXME should be define elsewhere
--*/
--static int validupath_adouble(const struct vol *vol, const char *name) 
--{
--    return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
--                                           : name[0] != '.';
--}                                           
--
--/* ----------------- */
--static int validupath_osx(const struct vol *vol, const char *name) 
--{
--    return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
--}             
--
--/* ---------------- */
--static void initvoladouble(struct vol *vol)
--{
--    if (vol->v_adouble == AD_VERSION2_OSX) {
--        vol->validupath  = validupath_osx;
--        vol->ad_path     = ad_path_osx;
--    }
--    else {
--        vol->validupath  = validupath_adouble;
--        vol->ad_path     = ad_path;
--    }
--}
--
- /* ------------------------------- */
- static int creatvol(AFPObj *obj, struct passwd *pwd, 
-                     char *path, char *name, 
-@@ -653,7 +627,8 @@
-           volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
-       else 
-           volume->v_adouble = AD_VERSION;
--      initvoladouble(volume);
-+
-+      initvol_vfs(volume);
- #ifdef FORCE_UIDGID
-         if (options[VOLOPT_FORCEUID].c_value) {
-             volume->v_forceuid = strdup(options[VOLOPT_FORCEUID].c_value);
-@@ -2231,6 +2206,9 @@
-             break;
-         case AD_VERSION2_OSX:
-             strlcat(buf, "ADOUBLE_VER:osx\n", sizeof(buf));
-+            break;
-+        case AD_VERSION1_ADS:
-+            strlcat(buf, "ADOUBLE_VER:ads\n", sizeof(buf));
-             break;
-     }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.h ./etc/afpd/volume.h
---- ../src.dev2/etc/afpd/volume.h      Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/volume.h        Fri Jun 25 22:01:56 2004
-@@ -14,6 +14,7 @@
- #include "atalk/unicode.h"
- #include "globals.h"
-+#include "afp_vfs.h"
- #define AFPVOL_NAMELEN   27
-@@ -75,6 +76,7 @@
-     int                 v_preexec_close;
-     
-     /* adouble indirection */
-+    struct vfs_ops      *vfs;
-     int                 (*validupath)(const struct vol *, const char *);
-     char                *(*ad_path)(const char *, int);
- };
-diff -Nur -X .cvsignore -x CVS ../src.dev2/include/atalk/adouble.h ./include/atalk/adouble.h
---- ../src.dev2/include/atalk/adouble.h        Tue Jun 15 01:08:28 2004
-+++ ./include/atalk/adouble.h  Sun Jun 20 22:33:26 2004
-@@ -82,6 +82,7 @@
- #define AD_VERSION1   0x00010000
- #define AD_VERSION2   0x00020000
- #define AD_VERSION2_OSX       0x00020001
-+#define AD_VERSION1_ADS       0x00010002
- #define AD_VERSION    AD_VERSION2
- /*
-@@ -252,6 +253,7 @@
-                                         the header parameter size is too small.
-                                      */
-     char                *(*ad_path)(const char *, int);
-+    int                 (*ad_mkrf)(char *);
-                            
- #ifdef USE_MMAPPED_HEADERS
-     char                *ad_data;
-@@ -364,6 +366,7 @@
- extern char *ad_dir       __P((const char *));
- extern char *ad_path      __P((const char *, int));
- extern char *ad_path_osx  __P((const char *, int));
-+extern char *ad_path_ads  __P((const char *, int));
- extern int ad_mode        __P((const char *, int));
- extern int ad_mkdir       __P((const char *, int));
-diff -Nur -X .cvsignore -x CVS ../src.dev2/libatalk/adouble/ad_open.c ./libatalk/adouble/ad_open.c
---- ../src.dev2/libatalk/adouble/ad_open.c     Mon Jul 12 02:01:45 2004
-+++ ./libatalk/adouble/ad_open.c       Mon Jul 12 02:12:25 2004
-@@ -697,6 +697,25 @@
-     return( pathbuf );
- }
-+/* -------------------- */
-+static int ad_mkrf(char *path)
-+{
-+    char *slash;
-+    /*
-+     * Probably .AppleDouble doesn't exist, try to mkdir it.
-+     */
-+     if (NULL == ( slash = strrchr( path, '/' )) ) {
-+         return -1;
-+     }
-+     *slash = '\0';
-+     errno = 0;
-+     if ( ad_mkdir( path, 0777 ) < 0 ) {
-+          return -1;
-+     }
-+     *slash = '/';
-+     return 0;
-+}
-+
- /* ---------------------------------------
-  * Put the resource fork where it needs to be:
-  * ._name
-@@ -729,8 +748,97 @@
-     strlcat( pathbuf, slash, MAXPATHLEN +1);
-     return pathbuf;
- }
-+/* -------------------- */
-+static int ad_mkrf_osx(char *path)
-+{
-+    return 0;
-+}
--/*
-+/* ---------------------------------------
-+ * Put the .AppleDouble where it needs to be:
-+ *
-+ *        /   a/.AppleDouble/b/Afp_AfpInfo
-+ *    a/b     
-+ *        \   b/.AppleDouble/.Parent/Afp_AfpInfo
-+ *
-+ */
-+char *
-+ad_path_ads( path, adflags )
-+    const char        *path;
-+    int               adflags;
-+{
-+    static char       pathbuf[ MAXPATHLEN + 1];
-+    char      c, *slash, buf[MAXPATHLEN + 1];
-+    size_t      l;
-+
-+    l = strlcpy(buf, path, MAXPATHLEN +1);
-+    if ( adflags & ADFLAGS_DIR ) {
-+      strcpy( pathbuf, buf);
-+      if ( *buf != '\0' && l < MAXPATHLEN) {
-+          pathbuf[l++] = '/';
-+          pathbuf[l] = 0;
-+      }
-+      slash = ".Parent";
-+    } else {
-+      if (NULL != ( slash = strrchr( buf, '/' )) ) {
-+          c = *++slash;
-+          *slash = '\0';
-+          strcpy( pathbuf, buf);
-+          *slash = c;
-+      } else {
-+          pathbuf[ 0 ] = '\0';
-+          slash = buf;
-+      }
-+    }
-+    strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
-+    strlcat( pathbuf, slash, MAXPATHLEN +1);
-+
-+    strlcat( pathbuf, "/Afp_AfpInfo", MAXPATHLEN +1);
-+
-+#if 0
-+    if ((adflags & ADFLAGS_HF)) {
-+        strlcat( pathbuf, "Afp_AfpInfo", MAXPATHLEN +1);
-+    else {
-+        strlcat( pathbuf, "Afp_Resource", MAXPATHLEN +1);
-+    }
-+#endif      
-+    return( pathbuf );
-+}
-+
-+/* -------------------- */
-+static int ad_mkrf_ads(char *path)
-+{
-+    char *slash;
-+    /*
-+     * Probably .AppleDouble doesn't exist, try to mkdir it.
-+     */
-+     if (NULL == ( slash = strrchr( path, '/' )) ) {
-+         return -1;
-+     }
-+     *slash = 0;
-+     errno = 0;
-+     if ( ad_mkdir( path, 0777 ) < 0 ) {
-+         if ( errno == ENOENT ) {
-+             char *slash1;
-+             
-+             if (NULL == ( slash1 = strrchr( path, '/' )) ) 
-+                 return -1;
-+             errno = 0;
-+             *slash1 = 0;
-+             if ( ad_mkdir( path, 0777 ) < 0 ) 
-+                  return -1;
-+             *slash1 = '/';
-+             if ( ad_mkdir( path, 0777 ) < 0 )
-+                 return -1;
-+         }
-+         else
-+            return -1;
-+     }     
-+     *slash = '/';
-+     return 0;
-+}
-+
-+/* -------------------------
-  * Support inherited protection modes for AppleDouble files.  The supplied
-  * mode is ANDed with the parent directory's mask value in lieu of "umask",
-  * and that value is returned.
-@@ -914,10 +1022,16 @@
-     memset( ad, 0, sizeof( struct adouble ) );
-     ad->ad_flags = flags;
-     if (flags == AD_VERSION2_OSX) {
--        ad->ad_path     = ad_path_osx;
-+        ad->ad_path = ad_path_osx;
-+        ad->ad_mkrf = ad_mkrf_osx;
-+    }
-+    else if (flags == AD_VERSION1_ADS) {
-+        ad->ad_path = ad_path_ads;
-+        ad->ad_mkrf = ad_mkrf_ads;
-     }
-     else {
--        ad->ad_path     = ad_path;
-+        ad->ad_path = ad_path;
-+        ad->ad_mkrf = ad_mkrf;
-     }
- }
-@@ -931,7 +1045,7 @@
-     struct adouble    *ad;
- {
-     struct stat         st;
--    char              *slash, *ad_p;
-+    char              *ad_p;
-     int                       hoflags, admode;
-     int                 st_invalid;
-     int                 open_df = 0;
-@@ -1031,19 +1145,9 @@
-           st_invalid = ad_mode_st(ad_p, &admode, &st);
-           admode = ad_hf_mode(admode); 
-           if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
--              /*
--               * Probably .AppleDouble doesn't exist, try to
--               * mkdir it.
--               */
--              if (NULL == ( slash = strrchr( ad_p, '/' )) ) {
--                  return ad_error(ad, adflags);
--              }
--              *slash = '\0';
--              errno = 0;
--              if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
-+              if (ad->ad_mkrf( ad_p) < 0) {
-                   return ad_error(ad, adflags);
--              }
--              *slash = '/';
-+              }
-               admode = mode;
-               st_invalid = ad_mode_st(ad_p, &admode, &st);
-               admode = ad_hf_mode(admode); 
diff --git a/contrib/patches/patch.mangled_trash_with_ip b/contrib/patches/patch.mangled_trash_with_ip
deleted file mode 100644 (file)
index 38976fc..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-Workaround for Network Trash and system without byte locking (broken nfs/afs)
-mangle OS9 "Network Trash Folder/Trash Can #2" name to
-"Network Trash Folder/Trash Can #2.<client ip>.<tcp port>"
-So multiple clients can share the same volume and have a working trash.
-
-Index: etc/afpd/directory.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/directory.c,v
-retrieving revision 1.71.2.4.2.12
-diff -u -r1.71.2.4.2.12 directory.c
---- etc/afpd/directory.c       11 Mar 2004 16:16:40 -0000      1.71.2.4.2.12
-+++ etc/afpd/directory.c       21 Apr 2004 12:42:03 -0000
-@@ -554,6 +554,7 @@
-  * attempt to extend the current dir. tree to include path
-  * as a side-effect, movecwd to that point and return the new dir
-  */
-+
- static struct dir *
-             extenddir( vol, dir, path )
- struct vol    *vol;
-@@ -563,7 +564,25 @@
-     char *save_m_name;
-     if ( path->u_name == NULL) {
--        path->u_name = mtoupath(vol, path->m_name, dir->d_did, (path->m_type==3) );
-+#ifdef DISABLE_LOCKING
-+        int l = strlen(TRASH_PREFIX);
-+        /* XXX replace mac name with unix name */
-+      if (vol->v_trash_id && vol->v_trash_id  == dir->d_did && vol->v_ip &&
-+             !strncmp(TRASH_PREFIX , path->m_name, l ) )
-+      {
-+          static char temp[MAXPATHLEN + 1];
-+          char *u;
-+
-+          strcpy(temp, path->m_name);
-+          u = temp +l;
-+          strcat(temp, ".");
-+          strcat(temp, vol->v_ip);
-+          path->u_name = temp;
-+      
-+      }
-+        else 
-+#endif
-+            path->u_name = mtoupath(vol, path->m_name, dir->d_did, (path->m_type==3) );
-     }
-     path->dir = NULL;
-Index: etc/afpd/enumerate.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/enumerate.c,v
-retrieving revision 1.39.2.2.2.4
-diff -u -r1.39.2.2.2.4 enumerate.c
---- etc/afpd/enumerate.c       11 Mar 2004 02:01:59 -0000      1.39.2.2.2.4
-+++ etc/afpd/enumerate.c       21 Apr 2004 12:42:04 -0000
-@@ -54,9 +54,39 @@
-     if (id == 0) {
-         return NULL;
-     }
-+    
-+#ifdef DISABLE_LOCKING
-+    if (!path->m_name) {
-+        int l = strlen(TRASH_PREFIX);
-+        /* XXX */
-+      if (vol->v_trash_id && vol->v_trash_id  == dir->d_did && vol->v_ip &&
-+             !strncmp(TRASH_PREFIX , upath, l ) )
-+      {
-+          static char temp[MAXPATHLEN + 1];
-+          char *u;
-+
-+          strcpy(temp, upath);
-+          u = temp +l;
-+          
-+          while (*u >= '0' && *u <= '9') {
-+              u++;
-+          }
-+          if (*u == '.') {
-+              *u = '\0';
-+          }
-+          path->m_name = temp; 
-+      }
-+          
-+        else if(!(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
-+           return NULL;
-+        }
-+    }
-+#else 
-     if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
--        return NULL;
-+       return NULL;
-     }
-+#endif
-+    
-     name  = path->m_name;    
-     if ((cdir = dirnew(name, upath)) == NULL) {
-         LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
-@@ -185,6 +215,32 @@
-     return name;
- }
-+#ifdef DISABLE_LOCKING
-+/* ----------------------------- */
-+int check_trash(const struct vol *vol, char *name)
-+{
-+int l = strlen(TRASH_PREFIX);
-+char *u;
-+
-+    if (strncmp(TRASH_PREFIX , name, l)) 
-+        return 0;
-+    /* */
-+    u = name +l;
-+    while (*u >= '0' && *u <= '9') {
-+        u++;
-+    }
-+
-+    if (u == name +l)
-+        return 0;
-+
-+    if (*u == '.' && !strcmp(vol->v_ip, u +1)) {
-+        return 0;
-+    }
-+    /* hide this one */
-+    return 1;
-+}
-+#endif
-+
- /* ----------------------------- */
- int 
- for_each_dirent(const struct vol *vol, char *name, dir_loop fn, void *data)
-@@ -193,15 +249,28 @@
-     struct dirent     *de;
-     char            *m_name;
-     int             ret;
-+#ifdef DISABLE_LOCKING
-+    int             mangle_trash = 0;
-+#endif
-     
-     if (NULL == ( dp = opendir( name)) ) {
-         return -1;
-     }
-+
-+#ifdef DISABLE_LOCKING
-+    if (vol->v_trash_id && vol->v_trash_id == curdir->d_did && !strcmp(name, ".")) {
-+        mangle_trash = 1;
-+    }
-+#endif    
-     ret = 0;
-     for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
-         if (!(m_name = check_dirent(vol, de->d_name)))
-             continue;
-+#ifdef DISABLE_LOCKING
-+        if (mangle_trash && check_trash(vol, de->d_name))
-+            continue;
-+#endif
-         ret++;
-         if (fn && fn(de,m_name, data) < 0) {
-            closedir(dp);
-Index: etc/afpd/volume.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/volume.c,v
-retrieving revision 1.51.2.7.2.28
-diff -u -r1.51.2.7.2.28 volume.c
---- etc/afpd/volume.c  6 Apr 2004 23:29:37 -0000       1.51.2.7.2.28
-+++ etc/afpd/volume.c  21 Apr 2004 12:42:05 -0000
-@@ -73,7 +73,11 @@
- static struct vol *Volumes = NULL;
- static u_int16_t      lastvid = 0;
--static char           *Trash = "\02\024Network Trash Folder";
-+
-+/* type, len, name */
-+static char           *Trash2 = "\02\024Network Trash Folder";
-+/* type, hint (4 bytes), len (2bytes), name */
-+static char           *Trash3 = "\03\0\0\0\0\0\024Network Trash Folder";
- static struct extmap  *Extmap = NULL, *Defextmap = NULL;
- static int              Extmap_cnt;
-@@ -1038,6 +1042,10 @@
-     free(vol->v_forceuid);
-     free(vol->v_forcegid);
- #endif /* FORCE_UIDGID */
-+
-+#ifdef DISABLE_LOCKING
-+    free(vol->v_ip);
-+#endif    
- }
- /* ------------------------------- */
-@@ -1730,9 +1738,31 @@
-               goto openvol_err;
-           }
-       }
--      else {
--            p = Trash;
--            cname( volume, volume->v_dir, &p );
-+#ifndef DISABLE_LOCKING       
-+      else 
-+#endif        
-+        {
-+            struct path *s_path;
-+
-+            /* use the right name format */
-+            p = (afp_version>= 30)?Trash3:Trash2;
-+            s_path = cname( volume, volume->v_dir, &p );
-+#ifdef DISABLE_LOCKING
-+            if (s_path && *s_path->m_name == '\0' ) {
-+                /* XXXX should do the same with ASP, could use volxlate but there's ':' in $p */
-+                if (obj->proto == AFPPROTO_DSI) {
-+                    DSI *dsi = obj->handle;
-+                
-+                    /* cname moved into dest folder */
-+                    volume->v_trash_id = curdir->d_did;
-+                    volume->v_ip = malloc(MAXPATHLEN +1);
-+                    if (volume->v_ip) {
-+                        sprintf(volume->v_ip, "%s.%u", inet_ntoa(dsi->client.sin_addr),
-+                              ntohs(dsi->client.sin_port));
-+                    }
-+                }
-+            }
-+#endif
-         }
-         return( AFP_OK );
-     }
-Index: etc/afpd/volume.h
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/volume.h,v
-retrieving revision 1.19.2.5.2.6
-diff -u -r1.19.2.5.2.6 volume.h
---- etc/afpd/volume.h  11 Mar 2004 02:02:04 -0000      1.19.2.5.2.6
-+++ etc/afpd/volume.h  21 Apr 2004 12:42:05 -0000
-@@ -81,6 +81,12 @@
-     /* adouble indirection */
-     int                 (*validupath)(const struct vol *, const char *);
-     char                *(*ad_path)(const char *, int);
-+
-+#ifdef DISABLE_LOCKING
-+    /* for OS 9 trash when there's no working byte locking (afs, nfs) */
-+    cnid_t            v_trash_id;
-+    char              *v_ip;
-+#endif    
- };
- #ifdef NO_LARGE_VOL_SUPPORT
-@@ -167,6 +173,8 @@
- #define VOLPBIT_BSIZE   11        /* block size */
-+#define TRASH_PREFIX "Trash Can #"
-+
- #define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
-                           ADFLAGS_NOADOUBLE : 0)
- #ifdef AFP3x
diff --git a/contrib/patches/patch.vfs b/contrib/patches/patch.vfs
deleted file mode 100644 (file)
index 06295d0..0000000
+++ /dev/null
@@ -1,1115 +0,0 @@
-diff -Nur vfs/Makefile vfs.new/Makefile
---- vfs/Makefile       Thu Jan  1 00:00:00 1970
-+++ vfs.new/Makefile   Mon Jul 12 10:48:56 2004
-@@ -0,0 +1,40 @@
-+##########################################################################
-+# Makefile for Samba VFS modules 
-+###########################################################################
-+
-+CC=gcc -g
-+LIBTOOL=/usr/bin/libtool
-+# REPLACE with samba source 
-+SMB=/u/redhat/paris/cvs/samba/smb3.0a20
-+
-+# REPLACE with samba build folder
-+BUILD=/mnt/hdd/build/smb.1.3
-+
-+CFLAGS=-Wall -I $(BUILD)/include \
-+-I$(SMB)/source -I$(SMB)/source/include -I$(SMB)/source/ubiqx -I$(SMB)/source/smbwrapper
-+
-+
-+LDFLAGS=-shared
-+
-+VFS_OBJS=vfs_ads.so
-+
-+SHELL=/bin/sh
-+
-+default: $(VFS_OBJS)
-+
-+# Pattern rules
-+
-+%.so: %.lo
-+      @echo Linking $<
-+      @$(LIBTOOL) --mode=link $(CC) -o $@ $< $(LDFLAGS)
-+
-+%.lo: %.c
-+      @echo Compiling $<
-+      @$(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
-+
-+# Misc targets
-+
-+clean:
-+      rm -rf .libs */.libs
-+      rm -f core *~ *% *.bak *.o */*.o *.lo $(VFS_OBJS)
-+
-diff -Nur vfs/README vfs.new/README
---- vfs/README Thu Jan  1 00:00:00 1970
-+++ vfs.new/README     Tue Jul 13 02:28:21 2004
-@@ -0,0 +1,34 @@
-+This a vfs for NT ADS 
-+you must set SMB and BUILD variables in Makefile.
-+
-+old smb.conf
-+[test_ads]
-+   comment = test ADS Mac/PC directory
-+   path=/home/test_ads/
-+#  /.AppleD* is mandatory 
-+   veto files = /.AppleD*/Network Trash Folder/Icon\r/
-+   delete veto files = True
-+# full path to vfs_ads.so 
-+   vfs object = /usr/src/samba/vfs/vfs_ads.so
-+   browseable = yes
-+   writable = yes
-+
-+new one (current svn tree)
-+copy vfs_ads.so as ads.so in <prefix>/lib/vfs/
-+eg
-+cp vfs_ads.so /opt/lib/vfs/ads.so
-+
-+smb.conf
-+[test_ads]
-+   comment = test ADS Mac/PC directory
-+   path=/home/test_ads/
-+ 
-+#  /.AppleD* is mandatory 
-+   veto files = /.AppleD*/Network Trash Folder/Icon\r/
-+   delete veto files = True
-+   vfs objects = ads
-+   browseable = yes
-+   writable = yes
-+
-+
-+Didier
-diff -Nur vfs/vfs_ads.c vfs.new/vfs_ads.c
---- vfs/vfs_ads.c      Thu Jan  1 00:00:00 1970
-+++ vfs.new/vfs_ads.c  Wed Jul 14 16:37:15 2004
-@@ -0,0 +1,1029 @@
-+/* 
-+ * CAP VFS module for Samba 3.x Version 0.3
-+ *
-+ * Copyright (C) Tim Potter, 1999-2000
-+ * Copyright (C) Alexander Bokovoy, 2002-2003
-+ * Copyright (C) Stefan (metze) Metzmacher, 2003
-+ * Copyright (C) TAKAHASHI Motonobu (monyo), 2003
-+ *
-+ * 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.
-+ *  
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ *
-+ * modified for alternate data stream
-+ * Copyright (C) Didier Gautheron 2004
-+ * 
-+ * this module should compile with old 3.0 API and 2004-07 svn API
-+ */
-+
-+
-+#include "includes.h"
-+
-+#undef DBGC_CLASS
-+#define DBGC_CLASS DBGC_VFS
-+
-+#define ADS_FOLDER    ".AppleDouble"
-+#define ADOUBLEMODE   0777
-+
-+/* FIXME found a better test */
-+#ifdef        SMB_VFS_OP
-+#define ADS_NEW_MODULE
-+
-+/* for current svn tree */
-+#define ADS_TALLOC_INIT(a) talloc_init(a)
-+
-+#define HANDLE_PARAMETER vfs_handle_struct *handle,
-+#define HANDLE handle,
-+
-+/* ------------------- */
-+#else
-+
-+#define ADS_TALLOC_INIT(a) talloc_init()
-+
-+#define HANDLE_PARAMETER 
-+#define HANDLE
-+
-+/* VFS operations */
-+static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
-+static struct smb_vfs_handle_struct *ads_handle; 
-+
-+#define SMB_VFS_NEXT_DISK_FREE(a,b,c,d,e,f,g)       default_vfs_ops.disk_free(b,c,d,e,f,g)
-+#define SMB_VFS_NEXT_OPENDIR(a,b,c)                 default_vfs_ops.opendir(b,c)
-+#define SMB_VFS_NEXT_READDIR(a,b,c)                 default_vfs_ops.readdir(b,c)
-+#define SMB_VFS_NEXT_MKDIR(a,b,c,d)                 default_vfs_ops.mkdir(b,c,d)
-+#define SMB_VFS_NEXT_RMDIR(a,b,c)                   default_vfs_ops.rmdir(b,c)
-+#define SMB_VFS_NEXT_OPEN(a,b,c,d,e)                default_vfs_ops.open(b,c,d,e)
-+#define SMB_VFS_NEXT_RENAME(a,b,c,d)                default_vfs_ops.rename(b,c,d)
-+#define SMB_VFS_NEXT_STAT(a,b,c,d)                  default_vfs_ops.stat(b,c,d)
-+#define SMB_VFS_NEXT_LSTAT(a,b,c,d)                 default_vfs_ops.lstat(b,c,d)
-+#define SMB_VFS_NEXT_UNLINK(a,b,c)                  default_vfs_ops.unlink(b,c)
-+#define SMB_VFS_NEXT_CHMOD(a,b,c,d)                 default_vfs_ops.chmod(b,c,d)
-+#define SMB_VFS_NEXT_CHOWN(a,b,c,d,e)               default_vfs_ops.chown(b,c,d,e) 
-+#define SMB_VFS_NEXT_CHDIR(a,b,c)                   default_vfs_ops.chdir(b,c)
-+#define SMB_VFS_NEXT_UTIME(a,b,c,d)                 default_vfs_ops.utime(b,c,d)
-+#define SMB_VFS_NEXT_SYMLINK(a,b,c,d)               default_vfs_ops.symlink(b,c,d)
-+#define SMB_VFS_NEXT_READLINK(a,b,c,d,e)            default_vfs_ops.readlink(b,c,d,e)
-+#define SMB_VFS_NEXT_LINK(a,b,c,d)                  default_vfs_ops.link(b,c,d)
-+#define SMB_VFS_NEXT_MKNOD(a,b,c,d,e)               default_vfs_ops.mknod(b,c,d,e)
-+#define SMB_VFS_NEXT_REALPATH(a,b,c,d)              default_vfs_ops.realpath(b,c,d)
-+#define SMB_VFS_NEXT_SET_NT_ACL(a,b,c,d,e)          default_vfs_ops.set_nt_acl(b,c,d,e)
-+#define SMB_VFS_NEXT_CHMOD_ACL(a,b,c,d)             default_vfs_ops.chmod_acl(b,c,d)
-+#define SMB_VFS_NEXT_SYS_ACL_GET_FILE(a,b,c,d)      default_vfs_ops.sys_acl_get_file(b,c,d)
-+#define SMB_VFS_NEXT_SYS_ACL_SET_FILE(a,b,c,d,e)    default_vfs_ops.sys_acl_set_file(b,c,d,e)
-+#define SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(a,b,c) default_vfs_ops.sys_acl_delete_def_file(b,c)
-+/* ads functions */
-+
-+#endif
-+
-+/* ------------------------- 
-+ * format
-+ * .AppleDouble/filename/stream name
-+ * 
-+ * return the *LAST* '/' in path
-+ */
-+static int ads_get_path_ptr(char *path)
-+{
-+      int i   = 0;
-+      int ptr = 0;
-+      
-+      for (i = 0; path[i]; i ++) {
-+              if (path[i] == '/')
-+                      ptr = i;
-+      }
-+      
-+      return ptr;
-+}
-+
-+/* ------------------------------
-+ * return the *FIRST* ':' in path
-+*/
-+static int ads_get_stream_ptr(const char *path)
-+{
-+      int i   = 0;
-+      int ptr = 0;
-+      
-+      for (i = 0; path[i]; i ++) {
-+              if (path[i] == ':') {
-+                      ptr = i;
-+                      break;
-+              }
-+      }
-+      return ptr;
-+}
-+
-+/* ---------------- 
-+ * fname is only a filename
-+*/
-+
-+static char *ads_canonical_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
-+{
-+    if (isdir) {
-+        return talloc_asprintf(ctx, "%s/%s/%s/.Parent", path, fname, ADS_FOLDER);
-+    }
-+    return talloc_asprintf(ctx, "%s/%s/%s", path, ADS_FOLDER, fname);
-+    
-+}
-+
-+/* ---------------- 
-+ * return directory pathname for an alternate data stream
-+ * fname is *NOT* an altername name (ie foo:bar)
-+*/
-+static char *ads_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
-+{
-+    int ptr0 = 0;
-+    int ptr1 = 0;
-+    char *temp;
-+
-+#if 0
-+    if (fname[0] == '.') ptr0 ++;
-+    if (fname[1] == '/') ptr0 ++;
-+#endif    
-+    temp = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
-+    ptr1 = ads_get_path_ptr(temp);
-+    temp[ptr1] = '\0';
-+    return ads_canonical_dir(ctx, temp, &temp[ptr1 + 1], isdir);
-+}
-+
-+/* ----------------------------------
-+ * build the pathname for stream, create folder if (mode & O_CREAT)
-+ * return -1 on error
-+ * 0 it's not a stream
-+ * 1 it's a stream
-+ *
-+ * main_path : file fullpathname with :$DATA removed
-+ * ads_path: unix pathname 
-+ * if it's not an ADS then main_path == ads_path
-+ *
-+ */
-+static int ads_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
-+                              char **ads_path, char **main_path, SMB_STRUCT_STAT **main_info, int flag)
-+{
-+        int ret = 0;
-+      int ptr0 = 0;
-+      int ptr1 = 0;
-+      int ptr2 = 0;
-+      int ptr3 = 0;
-+      char *dname = 0;
-+      char *name  = 0;
-+      SMB_STRUCT_STAT ads_info;
-+
-+      if (!ctx || !path || !fname || !ads_path || !main_path || !main_info || !*main_info)
-+              return -1;
-+#if 1
-+      DEBUG(3, ("ADS: PATH: %s[%s]\n", path, fname));
-+#endif
-+      if (strstr(path, ADS_FOLDER) || strstr(fname, ADS_FOLDER)) {
-+              DEBUG(1, ("ADS: path %s[%s] already contains %s\n", path, fname, ADS_FOLDER));
-+              return -1;
-+      }
-+
-+#if 0
-+      if (fname[0] == '.') ptr0 ++;
-+      if (fname[1] == '/') ptr0 ++;
-+#endif
-+
-+      *main_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
-+      *ads_path = NULL;
-+
-+      /* get pointer to last '/' */
-+      ptr1 = ads_get_path_ptr(*main_path);
-+      ptr2 = ads_get_stream_ptr(*main_path +ptr1 +1);
-+      /* FIXME
-+       * what about ::$DATA or :name:$DATA
-+      */
-+
-+      if (ptr2) {
-+          /* it's an alternate stream */
-+          ptr2 += ptr1 +1;
-+          (*main_path)[ptr2] = 0;
-+          ptr3 = ads_get_stream_ptr(*main_path +ptr2 +1);
-+          if (ptr3) {
-+              ptr3 += ptr2 +1;
-+              /* check it's $DATA */
-+              if (!strcmp("$DATA", &(*main_path)[ptr3+1])) {
-+                  (*main_path)[ptr3] = 0;
-+              }
-+          }
-+
-+          DEBUG(3, ("ADS: MAIN DATA %s\n", *main_path));
-+
-+          if (sys_lstat(*main_path, *main_info) < 0) {
-+              /* if we can't get the main file give up */
-+              return -1;
-+          }
-+          (*main_path)[ptr2] = ':';
-+          dname = talloc_strdup(ctx, *main_path);
-+          dname[ptr1] = '\0'; 
-+          name = *main_path;
-+          name[ptr2] = '\0';
-+          if (S_ISDIR((*main_info)->st_mode)) {
-+              *ads_path = talloc_asprintf(ctx, "%s/%s/%s/.Parent/%s", dname, &name[ptr1 + 1], ADS_FOLDER, &name[ptr2 + 1]);
-+          }
-+          else {
-+              *ads_path = talloc_asprintf(ctx, "%s/%s/%s/%s", dname, ADS_FOLDER, &name[ptr1 + 1], &name[ptr2 + 1]);
-+          }
-+          /* XXX are we always the right user ?*/
-+          if (sys_lstat(*ads_path, &ads_info) < 0) {
-+              int st_ret;
-+              /* */
-+              if (errno == ENOENT && (flag & O_CREAT))  {
-+                  char *ads_base = ads_canonical_dir(ctx, dname, &name[ptr1 + 1], S_ISDIR((*main_info)->st_mode));
-+                  mode_t mode;
-+                  
-+                  st_ret = mkdir(ads_base, 0777);
-+                  if (st_ret < 0) {
-+                      if (errno == ENOENT) {
-+                          char *ads_double;
-+                          if (S_ISDIR((*main_info)->st_mode)) {
-+                              ads_double = talloc_asprintf(ctx, "%s/%s/%s", dname, &name[ptr1 + 1], ADS_FOLDER);
-+                          }
-+                          else {
-+                              ads_double = talloc_asprintf(ctx, "%s/%s", dname, ADS_FOLDER);
-+                          }
-+                          if (mkdir(ads_double, 0777) < 0)
-+                              return -1;
-+                          if ((st_ret = mkdir(ads_base, 0777)) < 0)
-+                              return -1;
-+                          
-+                          /* we just created .AppleDouble/file/ update mode with dir search 
-+                           * XXX what about acl?
-+                          */
-+                          mode = (*main_info)->st_mode;
-+                          if ((mode & (S_IRUSR | S_IWUSR )))
-+                              mode |= S_IXUSR;
-+                          if ((mode & (S_IRGRP | S_IWGRP )))
-+                              mode |= S_IXGRP;
-+                          if ((mode & (S_IROTH | S_IWOTH ))) 
-+                              mode |= S_IXOTH;
-+                          chmod(ads_base, mode);
-+                      }
-+                      else 
-+                          errno = ENOENT;
-+                  }
-+              }
-+              else 
-+                  return -1;
-+          }
-+          ret = 1;
-+      }
-+      else {
-+          *ads_path = *main_path;
-+          if (sys_lstat(*main_path, *main_info) < 0) {
-+              *main_info = NULL;
-+          }
-+      }
-+#if 1
-+      DEBUG(3, ("ADS: DEBUG:[%s] [%s]\n", *main_path, *ads_path)); 
-+#endif
-+      return ret;
-+}
-+
-+/* ------------------------ */
-+static SMB_BIG_UINT ads_disk_free(HANDLE_PARAMETER connection_struct *conn, const char *path,
-+      BOOL small_query, SMB_BIG_UINT *bsize,
-+      SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
-+{
-+      return SMB_VFS_NEXT_DISK_FREE(handle, conn, path, small_query, bsize, dfree, dsize);
-+}
-+
-+static DIR *ads_opendir(HANDLE_PARAMETER connection_struct *conn, const char *fname)
-+{
-+      return SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
-+}
-+
-+static struct dirent *ads_readdir(HANDLE_PARAMETER connection_struct *conn, DIR *dirp)
-+{
-+        struct dirent *result;
-+      DEBUG(3,("ads: ads_readdir\n"));
-+      result = SMB_VFS_NEXT_READDIR(handle, conn, dirp);
-+      if (result) {
-+        DEBUG(3,("ads: ads_readdir: %s\n", result->d_name));
-+        }
-+        return result;
-+}
-+
-+/* ------------------------- */
-+static int ads_mkdir(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
-+{
-+      return SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
-+}
-+
-+/* ------------------------- */
-+static int unlink_file(const char *path)
-+{
-+int ret = 0;
-+         
-+      become_root();
-+        ret = unlink(path);
-+        unbecome_root();
-+        return ret;
-+}
-+
-+/* ------------------------- */
-+static int unlink_folder(const char *path)
-+{
-+int ret = 0;
-+         
-+      become_root();
-+        ret = rmdir(path);
-+        unbecome_root();
-+        return ret;
-+}
-+
-+/* ------------------------- 
-+   remove all files in an AppleDouble folder
-+*/
-+static void rrmdir(TALLOC_CTX *ctx, char *path)
-+{
-+        int n;
-+        char *dpath;
-+        struct dirent **namelist;
-+ 
-+        if (!path) return;
-+ 
-+        n = scandir(path, &namelist, 0, alphasort);
-+        if (n < 0) {
-+                return;
-+        } 
-+        while (n --) {
-+              if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
-+                      free(namelist[n]);
-+                        continue;
-+                }
-+                if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
-+                      unlink_file(dpath);
-+                }
-+                free(namelist[n]);
-+        }
-+        free(namelist);
-+        unlink_folder(path);
-+}
-+
-+/* --------------------------- */
-+static void rrm_adsdir(TALLOC_CTX *ctx, char *path)
-+{
-+        int n;
-+        char *dpath;
-+        struct dirent **namelist;
-+ 
-+        if (!path) return;
-+ 
-+        n = scandir(path, &namelist, 0, alphasort);
-+        if (n < 0) {
-+                return;
-+        } 
-+        while (n --) {
-+              if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
-+                      free(namelist[n]);
-+                        continue;
-+                }
-+                if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
-+                      rrmdir(ctx, dpath);
-+                }
-+                free(namelist[n]);
-+        }
-+        free(namelist);
-+        unlink_folder(path);
-+}
-+
-+/* ------------------------- 
-+ * XXX 
-+ * if in smb.conf there's :
-+ * delete veto files = True
-+ * veto files = /.AppleD* /
-+*/
-+static int ads_rmdir( HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+        BOOL add = False;
-+        TALLOC_CTX *ctx = 0;
-+        char *dpath;
-+        int  ret = 0;
-+ 
-+        if (!conn || !conn->origpath || !path) goto exit_rmdir;
-+
-+      /* .AppleD* */
-+        strstr(path, ADS_FOLDER) ? (add = False) : (add = True);
-+ 
-+        if (!(ctx = ADS_TALLOC_INIT("ads_rmdir")))
-+                goto exit_rmdir;
-+ 
-+        if (!(dpath = talloc_asprintf(ctx, "%s/%s%s",conn->origpath, path, add ? "/"ADS_FOLDER : "")))
-+              goto exit_rmdir;
-+              
-+      /* remove folder .AppleDouble */
-+        rrm_adsdir(ctx, dpath);
-+ 
-+exit_rmdir:
-+      ret = SMB_VFS_NEXT_RMDIR(handle, conn, path);
-+        talloc_destroy(ctx);
-+
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_open(HANDLE_PARAMETER connection_struct *conn, const char *fname, int flags, mode_t mode)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      DEBUG(3,("ads: ads_open for %s %x\n", fname, flags));
-+      if (!(ctx = ADS_TALLOC_INIT("ads_open")))
-+              return -1;
-+      /* convert to */
-+      if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, flags) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_OPEN(handle, conn, ads_path, flags, mode);
-+      talloc_destroy(ctx);
-+      return ret;
-+
-+}
-+
-+static int isDir(SMB_STRUCT_STAT *st)
-+{
-+     if (st == NULL) {
-+         return 0;
-+     }
-+     return S_ISDIR(st->st_mode);
-+}
-+
-+/* ------------------------- */
-+static int ads_rename(HANDLE_PARAMETER connection_struct *conn, const char *old, const char *new)
-+{
-+      int ret = 0;
-+      TALLOC_CTX *ctx;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      DEBUG(3,("ads: ads_rename %s --> %sx\n", old, new));
-+      
-+      if (!(ctx = ADS_TALLOC_INIT("ads_rename")))
-+          return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, old, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      if (ads_path != main_path) {
-+          /* you can't rename an ads ! */
-+          talloc_destroy(ctx);
-+          errno = EINVAL;
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
-+      if (!ret && !isDir(main_info)) {
-+          int  ptr1;
-+          int  ptr2;
-+          
-+          char *ads_old  = ads_dir(ctx, conn->origpath, old, 0);
-+          char *ads_new  = ads_dir(ctx, conn->origpath, new, 0);
-+
-+          /* is dest folder .Adouble there ? */
-+          ptr1 = ads_get_path_ptr(ads_new);
-+          ptr2 = ads_get_path_ptr(ads_old);
-+
-+          ads_new[ptr1] = '\0';
-+          ads_old[ptr2] = '\0';
-+          if (strcmp(ads_new, ads_old)) {
-+              mkdir(ads_new, 0777);
-+          }
-+
-+          ads_new[ptr1] = '/';
-+          ads_old[ptr2] = '/';
-+          
-+          SMB_VFS_NEXT_RENAME(handle, conn, ads_old, ads_new);
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- 
-+ * For an ADS what do we need to return , ADS ? main DATA?
-+*/
-+static int ads_stat(HANDLE_PARAMETER connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+
-+      DEBUG(3,("ads: ads_stat for %s\n", fname));
-+
-+      if (!(ctx = ADS_TALLOC_INIT("ads_stat")))
-+          return -1;
-+      /* which inode ?
-+      */
-+      if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_STAT(handle, conn, ads_path, sbuf);
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_lstat(HANDLE_PARAMETER connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      if (!(ctx = ADS_TALLOC_INIT("ads_lstat")))
-+              return -1;
-+      /* which inode ?
-+      */
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      return SMB_VFS_NEXT_LSTAT(handle, conn, ads_path, sbuf);
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_unlink(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+      int ret = 0;
-+
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      DEBUG(3,("ads: ads_unlink %s\n", path));
-+      if (!(ctx = ADS_TALLOC_INIT("ads_unlink")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_UNLINK(handle, conn, ads_path);
-+      /*
-+         if data stream
-+            for each stream
-+                unlink
-+      */
-+      if (!ret && ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          if (dir) {
-+              char *dpath;
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+                      continue;
-+                  /* XXX need to be root ?  */
-+                  SMB_VFS_NEXT_UNLINK(handle, conn, dpath);               
-+              }
-+              closedir(dir);
-+              rmdir(ads_base);
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chmod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+
-+      DEBUG(3,("ads: ads_chmod %s\n", path));
-+      /* if stream 
-+             error ?, change only the stream
-+      */
-+      if (!(ctx = ADS_TALLOC_INIT("ads_chmod")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_CHMOD(handle, conn, ads_path, mode);
-+      /*
-+         if data stream
-+            for each stream
-+                chmod
-+      */
-+      if (!ret && ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          if (dir) {
-+              char *dpath;
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+                      continue;
-+                  /* XXX need to be root ? */
-+                  SMB_VFS_NEXT_CHMOD(handle, conn, dpath, mode);
-+              }
-+              closedir(dir);
-+              /* XXX need to change ads_base too*/
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chown(HANDLE_PARAMETER connection_struct *conn, const char *path, uid_t uid, gid_t gid)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+
-+      DEBUG(3,("ads: ads_chown %s\n", path));
-+      /* if stream 
-+             error ?, change only the stream
-+      */
-+      if (!(ctx = ADS_TALLOC_INIT("ads_chown")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
-+      /* if data stream
-+            for each stream
-+                chmod
-+      */
-+      if (!ret && ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          if (dir) {
-+              char *dpath;
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+                      continue;
-+                  /* XXX need to be root ?, what do we do in case of error? */
-+                  SMB_VFS_NEXT_CHOWN(handle, conn, dpath, uid, gid);
-+              }
-+              closedir(dir);
-+              SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chdir(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+      DEBUG(3,("ads: ads_chdir for %s\n", path));
-+      return SMB_VFS_NEXT_CHDIR(handle, conn, path);
-+}
-+
-+static int ads_utime(HANDLE_PARAMETER connection_struct *conn, const char *path, struct utimbuf *times)
-+{
-+      return SMB_VFS_NEXT_UTIME(handle, conn, path, times);
-+}
-+
-+
-+static BOOL ads_symlink(HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
-+{
-+      return SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath);
-+}
-+
-+static BOOL ads_readlink(HANDLE_PARAMETER connection_struct *conn, const char *path, char *buf, size_t bufsiz)
-+{
-+      return SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz);
-+}
-+
-+static int ads_link( HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
-+{
-+      return SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath);
-+}
-+
-+static int ads_mknod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
-+{
-+      return SMB_VFS_NEXT_MKNOD(handle, conn, path, mode, dev);
-+}
-+
-+static char *ads_realpath(HANDLE_PARAMETER connection_struct *conn, const char *path, char *resolved_path)
-+{
-+      return SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path);
-+}
-+
-+static BOOL ads_set_nt_acl(HANDLE_PARAMETER files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
-+{
-+      return SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, name, security_info_sent, psd);
-+}
-+
-+static int ads_chmod_acl(HANDLE_PARAMETER connection_struct *conn, const char *name, mode_t mode)
-+{
-+      /* If the underlying VFS doesn't have ACL support... */
-+#ifdef ADS_NEW_MODULE
-+        if (!handle->vfs_next.ops.chmod_acl) {                                                                          
-+#else
-+      if (!default_vfs_ops.chmod_acl) {
-+#endif
-+              errno = ENOSYS;
-+              return -1;
-+      }
-+      return SMB_VFS_NEXT_CHMOD_ACL(handle, conn, name, mode);
-+}
-+
-+static SMB_ACL_T ads_sys_acl_get_file(HANDLE_PARAMETER connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
-+{
-+      return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, path_p, type);
-+}
-+
-+static int ads_sys_acl_set_file(HANDLE_PARAMETER connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
-+{
-+      return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, name, acltype, theacl);
-+}
-+
-+static int ads_sys_acl_delete_def_file(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+      return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, path);
-+}
-+
-+#ifdef        ADS_NEW_MODULE 
-+static ssize_t ads_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
-+{
-+        return SMB_VFS_NEXT_GETXATTR(handle, conn, path, name, value, size);
-+}
-+
-+static ssize_t ads_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
-+size)
-+{
-+        return SMB_VFS_NEXT_LGETXATTR(handle, conn, path, name, value, size);
-+}
-+
-+static ssize_t ads_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
-+{
-+        return SMB_VFS_NEXT_FGETXATTR(handle, fsp, fd, name, value, size);
-+}
-+
-+static ssize_t ads_listxattr(vfs_handle_struct *handle, connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+        return SMB_VFS_NEXT_LISTXATTR(handle, conn, path, list, size);
-+}
-+
-+static ssize_t ads_llistxattr(vfs_handle_struct *handle,struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+        return SMB_VFS_NEXT_LLISTXATTR(handle, conn, path, list, size);
-+}
-+
-+static int ads_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
-+{
-+        return SMB_VFS_NEXT_REMOVEXATTR(handle, conn, path, name);
-+}
-+
-+static int ads_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
-+{
-+        return SMB_VFS_NEXT_LREMOVEXATTR(handle, conn, path, name);
-+}
-+
-+static int ads_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
-+{
-+        return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, fd, name);
-+}
-+
-+static int ads_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
-+{
-+        return SMB_VFS_NEXT_SETXATTR(handle, conn, path, name, value, size, flags);
-+}
-+
-+static int ads_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
-+{
-+        return SMB_VFS_NEXT_LSETXATTR(handle, conn, path, name, value, size, flags);
-+}
-+
-+static int ads_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
-+{
-+        return SMB_VFS_NEXT_FSETXATTR(handle, fsp, fd, name, value, size, flags);
-+}
-+
-+#endif
-+
-+/* ---------------------------------- 
-+ * enumerate 
-+*/
-+static ssize_t ads_listads(HANDLE_PARAMETER struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      size_t     len, total = 0;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+
-+      if (!list || !path) {
-+              /* aka we have ads functionnality */
-+              return 0;
-+      }
-+
-+      DEBUG(3,("ads: ads_listads %s\n", path));
-+
-+      if (!(ctx = ADS_TALLOC_INIT("ads_listads")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      /*
-+         if data stream
-+            for each stream
-+      */
-+      if (ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          /* XXX need to be root ? */
-+          if (dir) {
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  len = strlen(dent->d_name) +8 ;
-+                  total += len;
-+                  if (total >= size) {
-+                      talloc_destroy(ctx);
-+                      errno = ERANGE;
-+                      return -1;
-+                  }
-+                  snprintf (list, len, ":%s:$DATA", dent->d_name);
-+                  list += len;
-+              }
-+              closedir(dir);
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return total;
-+}
-+
-+/* ------------------------------------
-+ * VFS operations structure */
-+
-+#ifndef SMB_VFS_OP
-+#define SMB_VFS_OP(x) ((void *) x)  
-+#endif
-+
-+static vfs_op_tuple ads_op_tuples[] = {
-+
-+      /* Disk operations */
-+
-+      {SMB_VFS_OP(ads_disk_free),                     SMB_VFS_OP_DISK_FREE,           SMB_VFS_LAYER_TRANSPARENT},
-+      
-+      /* Directory operations */
-+
-+      {SMB_VFS_OP(ads_opendir),                       SMB_VFS_OP_OPENDIR,             SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_readdir),                       SMB_VFS_OP_READDIR,             SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_mkdir),                         SMB_VFS_OP_MKDIR,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_rmdir),                         SMB_VFS_OP_RMDIR,               SMB_VFS_LAYER_TRANSPARENT},
-+
-+      /* File operations */
-+
-+      {SMB_VFS_OP(ads_open),                          SMB_VFS_OP_OPEN,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_rename),                        SMB_VFS_OP_RENAME,              SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_stat),                          SMB_VFS_OP_STAT,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lstat),                         SMB_VFS_OP_LSTAT,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_unlink),                        SMB_VFS_OP_UNLINK,              SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_chmod),                         SMB_VFS_OP_CHMOD,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_chown),                         SMB_VFS_OP_CHOWN,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_chdir),                         SMB_VFS_OP_CHDIR,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_utime),                         SMB_VFS_OP_UTIME,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_symlink),                       SMB_VFS_OP_SYMLINK,             SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_readlink),                      SMB_VFS_OP_READLINK,            SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_link),                          SMB_VFS_OP_LINK,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_mknod),                         SMB_VFS_OP_MKNOD,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_realpath),                      SMB_VFS_OP_REALPATH,            SMB_VFS_LAYER_TRANSPARENT},
-+
-+      /* NT File ACL operations */
-+
-+      {SMB_VFS_OP(ads_set_nt_acl),                    SMB_VFS_OP_SET_NT_ACL,          SMB_VFS_LAYER_TRANSPARENT},
-+
-+      /* POSIX ACL operations */
-+
-+      {SMB_VFS_OP(ads_chmod_acl),                     SMB_VFS_OP_CHMOD_ACL,           SMB_VFS_LAYER_TRANSPARENT},
-+
-+      {SMB_VFS_OP(ads_sys_acl_get_file),              SMB_VFS_OP_SYS_ACL_GET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_sys_acl_set_file),              SMB_VFS_OP_SYS_ACL_SET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_sys_acl_delete_def_file),       SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,     SMB_VFS_LAYER_TRANSPARENT},
-+#ifdef        ADS_NEW_MODULE
-+      /* EA operations. */
-+      {SMB_VFS_OP(ads_getxattr),                      SMB_VFS_OP_GETXATTR,                    SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lgetxattr),                     SMB_VFS_OP_LGETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_fgetxattr),                     SMB_VFS_OP_FGETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_listxattr),                     SMB_VFS_OP_LISTXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_llistxattr),                    SMB_VFS_OP_LLISTXATTR,                  SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_removexattr),                   SMB_VFS_OP_REMOVEXATTR,                 SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lremovexattr),                  SMB_VFS_OP_LREMOVEXATTR,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_fremovexattr),                  SMB_VFS_OP_FREMOVEXATTR,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_setxattr),                      SMB_VFS_OP_SETXATTR,                    SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lsetxattr),                     SMB_VFS_OP_LSETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_fsetxattr),                     SMB_VFS_OP_FSETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+#endif
-+      /* ADS operations */
-+      {SMB_VFS_OP(ads_listads),                       SMB_VFS_OP_LISTADS,                     SMB_VFS_LAYER_TRANSPARENT},
-+
-+      {NULL,                                          SMB_VFS_OP_NOOP,                        SMB_VFS_LAYER_NOOP}
-+};
-+
-+#ifdef ADS_NEW_MODULE
-+
-+#if 0
-+NTSTATUS vfs_ads_init(void)
-+{
-+        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ads", ads_op_tuples);
-+}
-+#endif
-+
-+NTSTATUS vfs_ads_init(void)
-+{
-+        DEBUG(3, ("ADS: vfs_ads_init\n"));
-+        return NT_STATUS_OK;
-+}
-+         
-+
-+NTSTATUS init_module(void)
-+{
-+      DEBUG(3, ("ADS: init_module\n" ));
-+        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ads", ads_op_tuples);
-+}
-+
-+#else
-+/* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
-+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,struct smb_vfs_handle_struct *vfs_handle)
-+{
-+      *vfs_version = SMB_VFS_INTERFACE_VERSION;
-+        memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
-+                   
-+        ads_handle = vfs_handle;
-+        DEBUG(3, ("ADS: vfs module loaded\n"));
-+        return ads_op_tuples;
-+}
-+                                             
-+/* VFS finalization function. */
-+void vfs_done(connection_struct *conn)
-+{
-+      DEBUG(3, ("ADS: vfs module unloaded\n"));
-+}
-+
-+#endif
diff --git a/contrib/permtest/add_permtest.patch b/contrib/permtest/add_permtest.patch
deleted file mode 100644 (file)
index 6fdf38c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-Index: configure.in
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/configure.in,v
-retrieving revision 1.205
-diff -u -w -b -r1.205 configure.in
---- configure.in       9 Sep 2006 04:30:01 -0000       1.205
-+++ configure.in       25 Jul 2008 13:48:05 -0000
-@@ -1053,6 +1053,7 @@
-       contrib/shell_utils/apple_rm
-       contrib/shell_utils/asip-status.pl
-       contrib/shell_utils/cleanappledouble.pl
-+      contrib/shell_utils/permtest.pl
-       contrib/timelord/Makefile
-       contrib/a2boot/Makefile
-       distrib/Makefile
-Index: contrib/shell_utils/Makefile.am
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/contrib/shell_utils/Makefile.am,v
-retrieving revision 1.16
-diff -u -w -b -r1.16 Makefile.am
---- contrib/shell_utils/Makefile.am    28 Apr 2005 20:49:36 -0000      1.16
-+++ contrib/shell_utils/Makefile.am    25 Jul 2008 13:48:05 -0000
-@@ -9,6 +9,9 @@
-       apple_cp apple_mv apple_rm      \
-       cleanappledouble.pl             \
-       asip-status.pl
-+EXTRASCRIPTS = \
-+      permtest.pl \
-+      permtest.cfg
- SUFFIXES = .tmpl .
-@@ -22,4 +25,4 @@
- bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES)
--EXTRA_DIST = $(PERLSCRIPTS) $(TEMPLATE_FILES)
-+EXTRA_DIST = $(PERLSCRIPTS) $(TEMPLATE_FILES) $(EXTRASCRIPTS)
diff --git a/contrib/permtest/permtest.cfg b/contrib/permtest/permtest.cfg
deleted file mode 100644 (file)
index 941d4e6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# Exactly follow this layout! Don't put extra white space in config lines !!
-# Order doesn't matter.
-
-# We use a ssh executed stat command to verify uid,gid and mode. Therefore ssh access with
-# PKI authentication must be setup and working!
-sshLogin = USER@HOST
-
-# self explaining
-mountAFPVolume = afp://USER:PASSWORD@HOST/VOLUME
-
-# These files will be created
-createFile = PATH_TO_FILE_ON_CLIENT
-
-# These files will be stat'ed. You can use different server-side paths here for files
-# created with "createFile" directive
-testFile = PATH_TO_FILE_ON_SERVER,user=USERNAME,group=GROUPNAME,mode=MODE
-
-# These dirs will be created
-createDir = PATH_TO_DIR_ON_CLIENT
-
-# These will be verified.
-testDir = PATH_TO_DIR_ON_CLIENT,user=USERNAME,group=GROUPNAME,mode=MODE
-
-# EOF. Leave this as a last line. We delibaretly chop in perl which might otherwise truncate
-# your last "testDir" definition.
\ No newline at end of file
diff --git a/contrib/permtest/permtest.pl.in b/contrib/permtest/permtest.pl.in
deleted file mode 100755 (executable)
index 87de19a..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-#!@PERL@ -w
-
-###########################################################################
-#
-# Characterization testing netatalks permission model
-#
-# (c) 2008 by Frank Lahm <franklahm@googlemail.com>
-#
-#   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.
-# 
-###########################################################################
-
-###########################################################################
-#
-# Usage:
-#
-#  "permtest.cfg" must be in your CWD. Must be run on a OS X host. Tested
-#  with 10.4.11. Uses Applescript through system("osascript ...") to mount
-#  AFP Volumes. Uses `ssh LOGIN@HOST stat FILE|DIR`. Therefor PKI
-#  authentication must be setup and working!
-#  See "permtest.cfg" for more details, it's pretty much self-explaining.
-# 
-###########################################################################
-
-use strict;
-
-my $DEBUG = 0;
-
-###########################################################################
-
-sub parseConfig;
-sub mountAPFVols;
-sub createTestFiles;
-sub createTestDirs;
-sub verifyTestFiles;
-sub verifyTestDirs;
-sub unmountAfp;
-
-my ($sshLogin, $sshResult, %sshStat, @AFPVols, @createFiles, @createDirs, @testFiles, @testDirs);
-my ($dir, $file, $user, $group, $perms, $mode, $cmd);
-
-parseConfig();
-mountAPFVols();
-createTestFiles();
-createTestDirs();
-print "\n";
-verifyTestFiles();
-verifyTestDirs();
-unmountAfp();
-
-exit 0;
-
-###########################################################################
-
-# parse config file
-sub parseConfig
-{
-       open CFG, "permtest.cfg" or die "Config file not found!";
-       while (<CFG>) {
-               chop;
-           if (/^#/) { next; };
-           if (/^sshLogin/) {
-                       $sshLogin = $_;
-                       $sshLogin =~ s/^sshLogin ?= ?// ;
-                       next;
-               }
-               if (/^mountAFPVolume/) {
-                       s/^mountAFPVolume ?= ?// ;
-                       print "Found AFP Volume Definition \"$_\"\n" if $DEBUG;
-                       push @AFPVols, $_;
-                       next;
-               }
-           if (/^createFile/) {
-                       s/^createFile ?= ?// ;
-                       push @createFiles, $_;
-                       next;
-               }
-               if (/^createDir/) {
-                       s/^createDir ?= ?// ;
-                       push @createDirs, $_;
-                       next;
-               }
-               if (/^testFile/) {
-                       push @testFiles, $_;
-                       next;
-               }
-               if (/^testDir/) {
-                       push @testDirs, $_;
-                       next;
-               }
-       }
-       close CFG;
-}
-
-# mount AFP Volumes
-sub mountAPFVols
-{
-       foreach (@AFPVols) {
-               print "Mounting AFP Volume \"$_\"\n";
-               $cmd = "osascript -e 'tell application \"Finder\"' -e 'mount volume \"$_\"' -e 'end tell' &> /dev/null";
-               print "Going to run the following Applescript:\n" . $cmd . "\n\n" if $DEBUG;
-               system($cmd);
-               if ($? != 0) { die "Error mounting \"$_\"\n"; }
-       }
-}
-
-# Create test files
-sub createTestFiles
-{
-       foreach (@createFiles) {
-               s/^createFile ?= ?// ;
-               system("rm \"$_\" &> /dev/null");
-               print "Creating file: \"$_\"\n";
-               system("touch \"$_\"");
-               if ($? != 0) { die "Error creating file \"$_\"\n"; }
-       }
-}
-
-# Create test dirs
-sub createTestDirs
-{
-       foreach (@createDirs) {
-               s/^createDir ?= ?// ;
-               system("rmdir \"$_\" &> /dev/null");
-               print "Creating dir: \"$_\"\n";
-               system("mkdir \"$_\"");
-               if ($? != 0) { die "Error creating dir \"$_\"\n"; }
-       }
-}
-
-# Verify files and dirs
-sub verifyTestFiles
-{
-       foreach (@testFiles) {
-               my @line = split(",");
-               foreach (@line) {
-                       if (/^testFile/) {
-                               $file = $_;
-                               $file =~ s/^testFile ?= ?//;
-                       }
-                       elsif (/^user/) {
-                               $user = $_;
-                               $user =~ s/^user ?= ?//;
-                       }
-                       elsif (/^group/) {
-                               $group = $_;
-                               $group =~ s/^group ?= ?//;
-                       }
-                       elsif (/^mode/) {
-                               $mode = $_;
-                               $mode =~ s/^mode ?= ?//;
-                       }
-               } # foreach (@elems)
-               print "File: $file, User: $user, Group: $group, Perms: $perms\n" if $DEBUG;
-       
-               $sshResult = `ssh $sshLogin stat -c \"user,%U,group,%G,mode,0%a\" \"$file\"`;
-               if ($? != 0) { die "Error stat'ing file \"$file\"\n"; }
-               chop $sshResult;
-               print "ssh stat $file gave us: $sshResult\n" if $DEBUG;
-       
-               %sshStat = split(",", $sshResult);
-               if ( ($sshStat{user} ne $user) or ($sshStat{group} ne $group) or ($sshStat{mode} ne $mode) ) {
-                       print "Creatin error for: \"$file\"!\nExpected:\t$user, $group, $mode.\nGot:\t\t$sshStat{user}, $sshStat{group}, $sshStat{mode}.\n\n";
-               }
-               system("rm \"$file\"");
-               if ($? != 0) { die "Couldn't delete \"$file\"\n"; }
-       }
-}
-
-sub verifyTestDirs
-{
-       foreach (@testDirs) {
-               my @line = split(",");
-               foreach (@line) {
-                       if (/^testDir/) {
-                               $dir = $_;
-                               $dir =~ s/^testDir ?= ?//;
-                       }
-                       elsif (/^user/) {
-                               $user = $_;
-                               $user =~ s/^user ?= ?//;
-                       }
-                       elsif (/^group/) {
-                               $group = $_;
-                               $group =~ s/^group ?= ?//;
-                       }
-                       elsif (/^mode/) {
-                               $mode = $_;
-                               $mode =~ s/^mode ?= ?//;
-                       }
-               } # foreach (@elems)
-               print "Dir: $dir, User: $user, Group: $group, Perms: $perms\n" if $DEBUG;
-       
-               $sshResult = `ssh $sshLogin stat -c \"user,%U,group,%G,mode,0%a\" \"$dir\"`;
-               if ($? != 0) { die "Error stat'ing file \"$dir\"\n"; }
-               chop $sshResult;
-               print "ssh stat $dir gave us: $sshResult\n" if $DEBUG;
-       
-               %sshStat = split(",", $sshResult);
-               if ( ($sshStat{user} ne $user) or ($sshStat{group} ne $group) or ($sshStat{mode} ne $mode) ) {
-                       print "Creatin error for: \"$dir\"!\nExpected:\t$user, $group, $mode.\nGot:\t\t$sshStat{user}, $sshStat{group}, $sshStat{mode}.\n\n";
-               }
-               system("rmdir \"$dir\""); if ($? != 0) { die "Couldn't delete \"$dir\"\n"; }
-       }
-}
-
-sub unmountAfp
-{
-       foreach (@AFPVols) {
-               print "Goint to eject Volume \"$_\"\n";
-               s#^(.*/)## ;
-               $cmd = "osascript -e 'tell application \"Finder\"' -e 'eject \"$_\"' -e 'end tell' &> /dev/null";
-               print "Going to run the following Applescript:\n" . $cmd . "\n\n" if $DEBUG;
-               system($cmd);
-       }
-}
index 226ec9e9d7d94eb76c942f635fad5871010fcfcd..4fc195df3ffcf94c0121a51c61b08f803944ed5c 100755 (executable)
@@ -391,6 +391,7 @@ sub parse_afp_flags
        push (@flags, "SupportsReconnect") if ($flags & (1<<7));
        push (@flags, "SupportsOpenDirectory") if ($flags & (1<<8));
        push (@flags, "SupportsUTF8Servername") if ($flags & (1<<9));
+       push (@flags, "SupportsUUIDs") if ($flags & (1<<10));
        push (@flags, "SupportsSuperClient") if ($flags & (1<<15));
 
        return @flags;
index 6c477251f354a99024eec48fd4eba6ab15084fde..60af9d68531aae17606a1363e5fa61181531d83e 100644 (file)
@@ -2,16 +2,14 @@
                  ACLs - Konfiguration and Infos vor Developpers
                  ==============================================
 
-ACL support for AFP is implemented with NFSv4 ACLs. Few filesystems and fewer OSes support
-these. At the time of implementation its only provided with ZFS on Solaris, Opensolaris and
-derived distributions.
+ACL support for AFP is implemented for Solaris/ZFS/NFSv4 ACLs and POSIX 1e ACLs.
 
   Configuration
   -------------
 
 In order to be able to support ACLs, the following things have to be configured:
 
-1. ZFS Volumes
+1. For Solaris/ZFS: ZFS Volumes
 2. Authentication Domain
 3. Netatalk Volumes
 
@@ -34,18 +32,14 @@ In order to be able to support ACLs, the following things have to be configured:
 
      In other words:
       - you need an Open Directory Server or an LDAP server where you store UUIDs in some
-       attribute
+        attribute
       - your clients must be configured to use this server 
-      - your server should be configured to use this server via nsswitch and PAM. This
-       however is not a strict requirement: 
-       if you create duplicates of every LDAP/OD user and group with identic attributes
-       (name, uid, gid) in your local data store (/etc/[passwd|group]) things will work
-
-             *  as long as user/group names/ids in the filesystem are equal  *
-             *         to their counterparts in the LDAP/OD datastore        *
-
+      - your server should be configured to use this server via nsswitch and PAM
       - configure Netatalk via afp_ldap.conf so that Netatalk is able to retrieve the UUID
-       for users and groups via LDAP search queries
+        for users and groups via LDAP search queries
+
+      ALL USERS AND GROUPS USED ON VOLUMES WHERE ACLS ARE USED SHOULD THEN COME FROM
+      THE SHARED AUTHENTICATION DOMAIN !
 
   3. Netatalk Volumes
 
@@ -66,19 +60,17 @@ Some implementation details that are buried in the code are worthwhile to be doc
      Basically as far as implementing AFP support is concerned they're equivalent.
      Subtleties arise at other places:
 
-     FPAccess
-
-       The AFP client frequently checks the (DARWIN_)ACE_DELETE_CHILD right. This is most
-       often not explicitly granted via an ACE. Therefor the client would get an no access
-        error. The client in turn then declares the object in question read only.
-       Thus we have to the check the mode for every directory and add ACE_DELETE_CHILD if
-       the requestor has write permissions.
+     * FPAccess:
+     The (10.5) AFP client frequently checks the (DARWIN_)ACE_DELETE_CHILD right. This is most
+     often not explicitly granted via an ACE. Therefor the client would get an no access
+     error. The client in turn then declares the object in question read only.
+     Thus we have to the check the mode for every directory and add ACE_DELETE_CHILD if
+     the requestor has write permissions.
 
-     FPGetFileDirParms
-
-       10.5 does not only use unix mode and FPAccess for permission check, but also OS 9
-       access bits from FPGetFileDirParms. Thus we have to adjust the Access Rights bitmap
-       user bits by including any ACL rigths.
+     * FPGetFileDirParms:
+     10.5 does not only use unix mode and FPAccess for permission check, but also OS 9
+     access bits from FPGetFileDirParms. Thus we have to adjust the Access Rights bitmap
+     user bits by including any ACL rigths.
 
   2. .AppleDouble VFS integration
 
@@ -88,7 +80,4 @@ Some implementation details that are buried in the code are worthwhile to be doc
      Thereafter ACLs for created files is taken care of by ACLs own inheritance rules.
 
      For dirs on the other hand whe have to make sure that any ACL the dir inherits is
-     copied verbatim to its .AppleDouble dir. 
-
-
-                                                                    January 2009, Frank Lahm
\ No newline at end of file
+     copied verbatim to its .AppleDouble dir.
index 7ed56ed6612fc957a815944cf3eaf610e0727de5..6ad182a5693bd5b7278f9804bf0c0708e52282ca 100644 (file)
@@ -1,3 +1,7 @@
 # Makefile.am for etc/
 
-SUBDIRS = afpd cnid_dbd atalkd papd psf uams
+SUBDIRS = afpd cnid_dbd uams
+
+if USE_APPLETALK
+SUBDIRS += atalkd papd psf
+endif
index 54ae84fee31e9661e5a88c92f5ec8f8e5d151488..7e93dec2b5b42ab59c6de70dcc5bb2a03914ddb1 100644 (file)
@@ -5,33 +5,73 @@ pkgconfdir = @PKGCONFDIR@
 sbin_PROGRAMS = afpd
 noinst_PROGRAMS = hash
 
-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 \
-        mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c  \
-        afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
-       catsearch.c afprun.c hash.c extattrs.c
+afpd_SOURCES = \
+       afp_asp.c \
+       afp_avahi.c \
+       afp_config.c \
+       afp_dsi.c \
+       afp_options.c \
+       afp_util.c \
+       afp_zeroconf.c \
+       afprun.c \
+       afs.c \
+       appl.c \
+       auth.c \
+       catsearch.c \
+       desktop.c \
+       dircache.c \
+       directory.c \
+       enumerate.c \
+       extattrs.c \
+       file.c \
+       filedir.c \
+       fork.c \
+       gettok.c \
+       hash.c \
+       main.c \
+       mangle.c \
+       messages.c  \
+       nfsquota.c \
+       ofork.c \
+       quota.c \
+       status.c \
+       switch.c \
+       uam.c \
+       uid.c \
+       unix.c \
+       volume.c
 
-if USE_NFSv4_ACLS
+afpd_LDADD =  \
+       $(top_builddir)/libatalk/cnid/libcnid.la \
+       $(top_builddir)/libatalk/libatalk.la \
+       @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@
+
+afpd_LDFLAGS = -export-dynamic 
+
+afpd_CFLAGS = \
+       -I$(top_srcdir)/include \
+       -I$(top_srcdir)/sys \
+       @SLP_CFLAGS@ @ZEROCONF_CFLAGS@ \
+       -DAPPLCNAME \
+       -DSERVERTEXT=\"$(SERVERTEXT)/\" \
+       -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
+       -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
+       -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
+       -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
+       -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
+       -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
+       -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
+       -D_PATH_AFPDUUIDCONF=\"$(pkgconfdir)/afp_voluuid.conf\"
+
+if HAVE_ACLS
 afpd_SOURCES += acls.c
 endif
 
-afpd_LDADD =  $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@
-afpd_LDFLAGS = -export-dynamic 
-afpd_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/sys \
-        @SLP_CFLAGS@ \
-        -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
-        -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
-        -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
-        -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
-        -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
-        -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
-        -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
-        -DAPPLCNAME \
-        -DSERVERTEXT=\"$(SERVERTEXT)/\"
 
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.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 hash.h acls.h acl_mappings.h extattrs.h
+        uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
+        dircache.h afp_zeroconf.h afp_avahi.h
 
 hash_SOURCES = hash.c
 hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
index d1ddbc13344bc45cd8d1fe4c54685b1dbbcd5601..9bab0d76b6cd013ffbabbc4b1be22fa99837f18b 100644 (file)
 #ifndef ACL_MAPPINGS
 #define ACL_MAPPINGS
 
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
+#endif
+
 #include "acls.h"
 
 /* 
@@ -28,6 +31,7 @@ struct ace_rights_map {
     u_int32_t to;
 };
 
+#ifdef HAVE_SOLARIS_ACLS
 struct ace_rights_map nfsv4_to_darwin_rights[] = {
     {ACE_READ_DATA,         DARWIN_ACE_READ_DATA},
     {ACE_WRITE_DATA,        DARWIN_ACE_WRITE_DATA},
@@ -89,5 +93,6 @@ struct darwin_to_nfsv4_flags_map darwin_to_nfsv4_flags[] = {
     {DARWIN_ACE_FLAGS_INHERITED,         ACE_INHERITED_ACE},
     {0,0}
 };
+#endif /* HAVE_SOLARIS_ACLS */
 
 #endif /* ACL_MAPPINGS */
index 4c2dcfafadc6d1aa988f642bb7052084ac362d1a..c8269c6a7fdb2fdf253526744d1d3562b2630805 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+  Copyright (c) 2008, 2009, 2010 Frank Lahm <franklahm@gmail.com>
 
   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
 #include <grp.h>
 #include <pwd.h>
 #include <errno.h>
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
+#endif
+#ifdef HAVE_POSIX_ACLS
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#endif
 
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
 #include "acl_mappings.h"
 
 /* for map_acl() */
-#define SOLARIS_2_DARWIN 1
-#define DARWIN_2_SOLARIS 2
+#define SOLARIS_2_DARWIN       1
+#define DARWIN_2_SOLARIS       2
+#define POSIX_DEFAULT_2_DARWIN 3
+#define POSIX_ACCESS_2_DARWIN  4
+#define DARWIN_2_POSIX         5
+
+#define MAP_MASK               31
+#define IS_DIR                 32
 
 /********************************************************
  * Basic and helper funcs
@@ -54,7 +66,7 @@
   Takes a users name, uid and primary gid and checks if user is member of any group
   Returns -1 if no or error, 0 if yes
 */
-static int check_group(char *name, uid_t uid, gid_t pgid, gid_t path_gid)
+static int check_group(char *name, uid_t uid _U_, gid_t pgid, gid_t path_gid)
 {
     int i;
     struct group *grp;
@@ -78,6 +90,11 @@ static int check_group(char *name, uid_t uid, gid_t pgid, gid_t path_gid)
     return -1;
 }
 
+/********************************************************
+ * Solaris funcs
+ ********************************************************/
+
+#ifdef HAVE_SOLARIS_ACLS
 /*
   Remove any trivial ACE "in-place". Returns no of non-trivial ACEs
 */
@@ -352,31 +369,217 @@ int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int
 
     return mapped_aces;
 }
+#endif /* HAVE_SOLARIS_ACLS */
 
-/********************************************************
- * 2nd level funcs
- ********************************************************/
+/*
+ * Map ACEs from POSIX to Darwin.
+ * type is either POSIX_DEFAULT_2_DARWIN or POSIX_ACCESS_2_DARWIN, cf. acl_get_file.
+ * Return number of mapped ACES, -1 on error.
+ */
+#ifdef HAVE_POSIX_ACLS
+static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darwin_aces)
+{
+    int mapped_aces = 0;
+    int havemask = 0;
+    int entry_id = ACL_FIRST_ENTRY;
+    acl_entry_t e;
+    acl_tag_t tag;
+    acl_permset_t permset, mask;
+    uid_t *uid = NULL;
+    gid_t *gid = NULL;
+    struct passwd *pwd = NULL;
+    struct group *grp = NULL;
+    uint32_t flags;
+    uint32_t rights;
+    darwin_ace_t *saved_darwin_aces = darwin_aces;
+
+    LOG(log_maxdebug, logtype_afpd, "map_aces_posix_to_darwin(%s)",
+        (type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN ?
+        "POSIX_DEFAULT_2_DARWIN" : "POSIX_ACCESS_2_DARWIN");
 
-/*  Map between ACL styles (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS).
-    Reads from 'aces' buffer, writes to 'rbuf' buffer.
-    Caller must provide buffer.
-    Darwin ACEs are read and written in network byte order.
-    Needs to know how many ACEs are in the ACL (ace_count). Ignores trivial ACEs.
-    Return no of mapped ACEs or -1 on error. */
-static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count)
+    /* itereate through all ACEs */
+    while (acl_get_entry(acl, entry_id, &e) == 1) {
+        entry_id = ACL_NEXT_ENTRY;
+
+        /* get ACE type */
+        if (acl_get_tag_type(e, &tag) != 0) {
+            LOG(log_error, logtype_afpd, "map_aces_posix_to_darwin: acl_get_tag_type: %s", strerror(errno));
+            mapped_aces = -1;
+            goto exit;
+        }
+
+        /* we return user and group ACE */
+        switch (tag) {
+        case ACL_USER:
+            if ((uid = (uid_t *)acl_get_qualifier(e)) == NULL) {
+                LOG(log_error, logtype_afpd, "map_aces_posix_to_darwin: acl_get_qualifier: %s",
+                    strerror(errno));
+                mapped_aces = -1;
+                goto exit;
+            }
+            if ((pwd = getpwuid(*uid)) == NULL) {
+                LOG(log_error, logtype_afpd, "map_aces_posix_to_darwin: getpwuid error: %s",
+                    strerror(errno));
+                mapped_aces = -1;
+                goto exit;
+            }
+            LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: uid: %d -> name: %s",
+                *uid, pwd->pw_name);
+            if (getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid) != 0) {
+                LOG(log_error, logtype_afpd, "map_aces_posix_to_darwin: getuuidfromname error");
+                mapped_aces = -1;
+                goto exit;
+            }
+            acl_free(uid);
+            uid = NULL;
+            break;
+
+        case ACL_GROUP:
+            if ((gid = (gid_t *)acl_get_qualifier(e)) == NULL) {
+                LOG(log_error, logtype_afpd, "map_aces_posix_to_darwin: acl_get_qualifier: %s",
+                    strerror(errno));
+                mapped_aces = -1;
+                goto exit;
+            }
+            if ((grp = getgrgid(*gid)) == NULL) {
+                LOG(log_error, logtype_afpd, "map_aces_posix_to_darwin: getgrgid error: %s",
+                    strerror(errno));
+                mapped_aces = -1;
+                goto exit;
+            }
+            LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: gid: %d -> name: %s",
+                *gid, grp->gr_name);
+            if (getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid) != 0) {
+                LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
+                mapped_aces = -1;
+                goto exit;
+            }
+            acl_free(gid);
+            gid = NULL;
+            break;
+
+            /* store mask so we can apply it later in a second loop */
+        case ACL_MASK:
+            if (acl_get_permset(e, &mask) != 0) {
+                LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: acl_get_permset: %s", strerror(errno));
+                return -1;
+            }
+            havemask = 1;
+            continue;
+
+        default:
+            continue;
+        }
+
+        /* flags */
+        flags = DARWIN_ACE_FLAGS_PERMIT;
+        if ((type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN)
+            flags |= DARWIN_ACE_FLAGS_FILE_INHERIT
+                | DARWIN_ACE_FLAGS_DIRECTORY_INHERIT
+                | DARWIN_ACE_FLAGS_ONLY_INHERIT;
+        darwin_aces->darwin_ace_flags = htonl(flags);
+
+        /* rights */
+        if (acl_get_permset(e, &permset) != 0) {
+            LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: acl_get_permset: %s", strerror(errno));
+            return -1;
+        }
+
+        rights = 0;
+        if (acl_get_perm(permset, ACL_READ))
+            rights = DARWIN_ACE_READ_DATA
+                | DARWIN_ACE_READ_EXTATTRIBUTES
+                | DARWIN_ACE_READ_ATTRIBUTES
+                | DARWIN_ACE_READ_SECURITY;
+        if (acl_get_perm(permset, ACL_WRITE)) {
+            rights |= DARWIN_ACE_WRITE_DATA
+                | DARWIN_ACE_APPEND_DATA
+                | DARWIN_ACE_WRITE_EXTATTRIBUTES
+                | DARWIN_ACE_WRITE_ATTRIBUTES;
+            if ((type & ~MAP_MASK) == IS_DIR)
+                rights |= DARWIN_ACE_DELETE;
+        }
+        if (acl_get_perm(permset, ACL_EXECUTE))
+            rights |= DARWIN_ACE_EXECUTE;
+
+        darwin_aces->darwin_ace_rights = htonl(rights);
+
+        darwin_aces++;
+        mapped_aces++;
+    } /* while */
+
+    if (havemask) {
+        /* Loop through the mapped ACE buffer once again, applying the mask */
+        /* Map the mask to Darwin ACE rights first */
+        rights = 0;
+        if (acl_get_perm(mask, ACL_READ))
+            rights = DARWIN_ACE_READ_DATA
+                | DARWIN_ACE_READ_EXTATTRIBUTES
+                | DARWIN_ACE_READ_ATTRIBUTES
+                | DARWIN_ACE_READ_SECURITY;
+        if (acl_get_perm(mask, ACL_WRITE)) {
+            rights |= DARWIN_ACE_WRITE_DATA
+                | DARWIN_ACE_APPEND_DATA
+                | DARWIN_ACE_WRITE_EXTATTRIBUTES
+                | DARWIN_ACE_WRITE_ATTRIBUTES;
+            if ((type & ~MAP_MASK) == IS_DIR)
+                rights |= DARWIN_ACE_DELETE;
+        }
+        if (acl_get_perm(mask, ACL_EXECUTE))
+            rights |= DARWIN_ACE_EXECUTE;
+        for (int i = mapped_aces; i > 0; i--) {
+            saved_darwin_aces->darwin_ace_rights &= htonl(rights);
+            saved_darwin_aces++;
+        }
+    }
+
+exit:
+    if (uid) acl_free(uid);
+    if (gid) acl_free(gid);
+
+    return mapped_aces;
+}
+#endif
+
+/*
+ * Multiplex ACL mapping (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS, POSIX_2_DARWIN, DARWIN_2_POSIX).
+ * Reads from 'aces' buffer, writes to 'rbuf' buffer.
+ * Caller must provide buffer.
+ * Darwin ACEs are read and written in network byte order.
+ * Needs to know how many ACEs are in the ACL (ace_count) for Solaris ACLs.
+ * Ignores trivial ACEs.
+ * Return no of mapped ACEs or -1 on error.
+ */
+static int map_acl(int type, const void *acl, darwin_ace_t *buf, int ace_count)
 {
     int mapped_aces;
 
     LOG(log_debug9, logtype_afpd, "map_acl: BEGIN");
 
-    switch (type) {
+    switch (type & MAP_MASK) {
+
+#ifdef HAVE_SOLARIS_ACLS
     case SOLARIS_2_DARWIN:
-        mapped_aces = map_aces_solaris_to_darwin( nfsv4_aces, buf, ace_count);
+        mapped_aces = map_aces_solaris_to_darwin( acl, buf, ace_count);
         break;
 
     case DARWIN_2_SOLARIS:
-        mapped_aces = map_aces_darwin_to_solaris( buf, nfsv4_aces, ace_count);
+        mapped_aces = map_aces_darwin_to_solaris( buf, acl, ace_count);
+        break;
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+    case POSIX_DEFAULT_2_DARWIN:
+        mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
+        break;
+
+    case POSIX_ACCESS_2_DARWIN:
+        mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
+        break;
+
+    case DARWIN_2_POSIX:
         break;
+#endif /* HAVE_POSIX_ACLS */
 
     default:
         mapped_aces = -1;
@@ -387,20 +590,21 @@ static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count
     return mapped_aces;
 }
 
-/********************************************************
- * 1st level funcs
- ********************************************************/
-
-
 /* Get ACL from object omitting trivial ACEs. Map to Darwin ACL style and
    store Darwin ACL at rbuf. Add length of ACL written to rbuf to *rbuflen.
    Returns 0 on success, -1 on error. */
 static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
 {
-    int ace_count, mapped_aces, err;
-    ace_t *aces;
+    int ace_count = 0;
+    int mapped_aces = 0;
+    int err, dirflag;
     uint32_t *darwin_ace_count = (u_int32_t *)rbuf;
-
+#ifdef HAVE_SOLARIS_ACLS
+    ace_t *aces;
+#endif
+#ifdef HAVE_POSIX_ACLS
+    struct stat st;
+#endif
     LOG(log_debug9, logtype_afpd, "get_and_map_acl: BEGIN");
 
     /* Skip length and flags */
@@ -408,6 +612,7 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
     *rbuf = 0;
     rbuf += 4;
 
+#ifdef HAVE_SOLARIS_ACLS
     if ( (ace_count = get_nfsv4_acl(name, &aces)) == -1) {
         LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get ACL");
         return -1;
@@ -417,6 +622,45 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
         err = -1;
         goto cleanup;
     }
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+    acl_t defacl = NULL , accacl = NULL;
+
+    /* stat to check if its a dir */
+    if (stat(name, &st) != 0) {
+        LOG(log_error, logtype_afpd, "get_and_map_acl: stat: %s", strerror(errno));
+        err = -1;
+        goto cleanup;
+    }
+
+    /* if its a dir, check for default acl too */
+    dirflag = 0;
+    if (S_ISDIR(st.st_mode)) {
+        dirflag = IS_DIR;
+        if ((defacl = acl_get_file(name, ACL_TYPE_DEFAULT)) == NULL) {
+            LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get default ACL"); err = -1; goto cleanup;
+        }        
+        if (defacl && (mapped_aces = map_acl(POSIX_DEFAULT_2_DARWIN | dirflag,
+                                             defacl,
+                                             (darwin_ace_t *)rbuf,
+                                             0)) == -1) {
+            err = -1; goto cleanup;
+        }
+    }
+
+    if ((accacl = acl_get_file(name, ACL_TYPE_ACCESS)) == NULL) {
+        LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get access ACL"); err = -1; goto cleanup;
+    }
+
+    if (accacl && (mapped_aces += map_acl(POSIX_ACCESS_2_DARWIN | dirflag,
+                                          accacl,
+                                          (darwin_ace_t *)(rbuf + mapped_aces * sizeof(darwin_ace_t)),
+                                          0)) == -1) {
+        err = -1; goto cleanup;
+    }
+#endif /* HAVE_POSIX_ACLS */
+
     LOG(log_debug, logtype_afpd, "get_and_map_acl: mapped %d ACEs", mapped_aces);
 
     err = 0;
@@ -424,22 +668,31 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
     *rbuflen += sizeof(darwin_acl_header_t) + (mapped_aces * sizeof(darwin_ace_t));
 
 cleanup:
+#ifdef HAVE_SOLARIS_ACLS
     free(aces);
+#endif
+#ifdef HAVE_POSIX_ACLS
+    if (defacl) acl_free(defacl);
+    if (accacl) acl_free(accacl);
+#endif /* HAVE_POSIX_ACLS */
 
     LOG(log_debug9, logtype_afpd, "get_and_map_acl: END");
     return err;
 }
 
 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
+static int remove_acl(const struct vol *vol,const char *path, int dir)
 {
-    int ret;
+    int ret = AFP_OK;
 
+#ifdef HAVE_SOLARIS_ACLS
     /* Ressource etc. first */
     if ((ret = vol->vfs->vfs_remove_acl(vol, path, dir)) != AFP_OK)
         return ret;
     /* now the data fork or dir */
-    return (remove_acl(path));
+    ret = remove_acl_vfs(path);
+#endif
+    return ret;
 }
 
 /*
@@ -450,7 +703,8 @@ static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
   We will store inherited ACEs first, which is Darwins canonical order.
   - returns AFPerror code
 */
-static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibuf)
+#ifdef HAVE_SOLARIS_ACLS
+static int set_acl(const struct vol *vol, char *name, int inherit, char *ibuf)
 {
     int ret, i, nfsv4_ace_count, tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
     ace_t *old_aces, *new_aces = NULL;
@@ -552,11 +806,20 @@ cleanup:
     LOG(log_debug9, logtype_afpd, "set_acl: END");
     return ret;
 }
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+static int set_acl(const struct vol *vol, char *name, int inherit, char *ibuf)
+{
+    return AFP_OK;
+}
+#endif /* HAVE_POSIX_ACLS */
 
 /*
   Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
   Note: this gets called frequently and is a good place for optimizations !
 */
+#ifdef HAVE_SOLARIS_ACLS
 static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
 {
     int                 ret, i, ace_count, dir, checkgroup;
@@ -720,6 +983,22 @@ exit:
 #endif
     return ret;
 }
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
+{
+    /*
+     * FIXME: for OS X >= 10.6 it seems fp_access isn't called anymore, instead
+     * the client just tries to perform any action, relying on the server
+     * to enforce permission (which the OS does for us), returning appropiate
+     * error codes in case the action failed.
+     * So to summarize: I think it's safe to not implement this function and
+     * just always return AFP_OK.
+     */
+    return AFP_OK;
+}
+#endif /* HAVE_POSIX_ACLS */
 
 /********************************************************
  * Interface
@@ -936,7 +1215,7 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* Remove ACL ? */
     if (bitmap & kFileSec_REMOVEACL) {
         LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
-        if ((ret = remove_acl_vfs(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
+        if ((ret = remove_acl(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
             LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
     }
 
@@ -946,9 +1225,9 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 
         /* Check if its our job to preserve inherited ACEs */
         if (bitmap & kFileSec_Inherit)
-            ret = set_acl_vfs(vol, s_path->u_name, 1, ibuf);
+            ret = set_acl(vol, s_path->u_name, 1, ibuf);
         else
-            ret = set_acl_vfs(vol, s_path->u_name, 0, ibuf);
+            ret = set_acl(vol, s_path->u_name, 0, ibuf);
         if (ret == 0)
             ret = AFP_OK;
         else {
@@ -968,10 +1247,10 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
 {
     struct passwd *pw;
-    uuid_t uuid;
-    int dir, r_ok, w_ok, x_ok;
+    atalk_uuid_t uuid;
+    int r_ok, w_ok, x_ok;
 
-    if ( ! (AFPobj->options.flags & OPTION_UUID))
+    if ( ! (AFPobj->options.flags & OPTION_UUID) || ! (AFPobj->options.flags & OPTION_ACL2OS9MODE))
         return;
 
     LOG(log_maxdebug, logtype_afpd, "acltoownermode('%s')", path);
@@ -1008,6 +1287,7 @@ void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
   We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
   FIXME: add to VFS layer ?
 */
+#ifdef HAVE_SOLARIS_ACLS
 void addir_inherit_acl(const struct vol *vol)
 {
     ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
@@ -1074,3 +1354,11 @@ cleanup:
     free(adaces);
     free(combinedaces);
 }
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+void addir_inherit_acl(const struct vol *vol)
+{
+    return;
+}
+#endif /* HAVE_POSIX_ACLS */
index a1b1e52d4da85a4fcbb08c34f5537905a911c7b6..fb0792c3af19e7b3972284b51fc90b26bdabc1a1 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: acls.h,v 1.3 2009-11-20 17:45:47 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #ifndef AFPD_ACLS_H 
 #define AFPD_ACLS_H
 
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
-#include <atalk/uuid.h>                /* for uuid_t */
+#endif
+
+#include <atalk/uuid.h>                /* for atalk_uuid_t */
 
 /*
  * This is what Apple says about ACL flags in sys/kauth.h:
  * the wire! We will ignore and spoil em.
  */
 
+#ifdef HAVE_SOLARIS_ACLS
 /* Some stuff for the handling of NFSv4 ACLs */
 #define ACE_TRIVIAL (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
+#endif /* HAVE_SOLARIS_ACLS */
 
 /* FPGet|Set Bitmap */
 enum {
@@ -89,7 +93,7 @@ enum {
 
 /* Access Control List Entry (ACE) */
 typedef struct {
-    uuid_t      darwin_ace_uuid;
+    atalk_uuid_t      darwin_ace_uuid;
     uint32_t    darwin_ace_flags;
     uint32_t    darwin_ace_rights;
 } darwin_ace_t;
@@ -107,5 +111,6 @@ int afp_setacl (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rb
 
 /* Parse afp_ldap.conf */
 extern int acl_ldap_readconfig(char *name);
+extern void acltoownermode(char *path, struct stat *st,uid_t uid, struct maccess *ma);
 
 #endif
diff --git a/etc/afpd/afp_avahi.c b/etc/afpd/afp_avahi.c
new file mode 100644 (file)
index 0000000..fc36271
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs:    http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_AVAHI
+
+#include <unistd.h>
+
+#include <avahi-common/strlst.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+
+#include "afp_avahi.h"
+#include "afp_config.h"
+#include "volume.h"
+
+/*****************************************************************
+ * Global variables
+ *****************************************************************/
+struct context *ctx = NULL;
+
+/*****************************************************************
+ * Private functions
+ *****************************************************************/
+
+static void publish_reply(AvahiEntryGroup *g,
+                          AvahiEntryGroupState state,
+                          void *userdata);
+
+/*
+ * This function tries to register the AFP DNS
+ * SRV service type.
+ */
+static void register_stuff(void) {
+    uint port;
+    const AFPConfig *config;
+    const struct vol *volume;
+    DSI *dsi;
+    char name[MAXINSTANCENAMELEN+1];
+    AvahiStringList *strlist = NULL;
+    char tmpname[256];
+
+    assert(ctx->client);
+
+    if (!ctx->group) {
+        if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
+            LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
+                avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+    }
+
+    if (avahi_entry_group_is_empty(ctx->group)) {
+        /* Register our service */
+
+        /* Build AFP volumes list */
+        int i = 0;
+        strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
+               
+        for (volume = getvolumes(); volume; volume = volume->v_next) {
+
+            if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0) {
+                LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
+                goto fail;
+            }
+
+            if (volume->v_flags & AFPVOL_TM) {
+                if (volume->v_uuid) {
+                    LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
+                        volume->v_localname, volume->v_uuid);
+                    strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
+                                                           i++, tmpname, volume->v_uuid);
+                } else {
+                    LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
+                        volume->v_localname);
+                    strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
+                                                           i++, tmpname);
+                }      
+            }
+        }
+
+        /* AFP server */
+        for (config = ctx->configs; config; config = config->next) {
+
+            dsi = (DSI *)config->obj.handle;
+            port = getip_port((struct sockaddr *)&dsi->server);
+            
+            if (convert_string(config->obj.options.unixcharset, CH_UTF8,
+                               config->obj.options.server ? config->obj.options.server : config->obj.options.hostname, -1,
+                               name, MAXINSTANCENAMELEN) <= 0) {
+                LOG ( log_error, logtype_afpd, "Could not set Zeroconf instance name");
+                goto fail;
+            }
+
+            if (avahi_entry_group_add_service(ctx->group,
+                                              AVAHI_IF_UNSPEC,
+                                              AVAHI_PROTO_UNSPEC,
+                                              0,
+                                              name,
+                                              AFP_DNS_SERVICE_TYPE,
+                                              NULL,
+                                              NULL,
+                                              port,
+                                              NULL) < 0) {
+                LOG(log_error, logtype_afpd, "Failed to add service: %s",
+                    avahi_strerror(avahi_client_errno(ctx->client)));
+                goto fail;
+            }
+
+            if (i && avahi_entry_group_add_service_strlst(ctx->group,
+                                                          AVAHI_IF_UNSPEC,
+                                                          AVAHI_PROTO_UNSPEC,
+                                                          0,
+                                                          name,
+                                                          ADISK_SERVICE_TYPE,
+                                                          NULL,
+                                                          NULL,
+                                                          9, /* discard */
+                                                          strlist) < 0) {
+                LOG(log_error, logtype_afpd, "Failed to add service: %s",
+                    avahi_strerror(avahi_client_errno(ctx->client)));
+                goto fail;
+            }  /* if */
+        }      /* for config*/
+
+        if (avahi_entry_group_commit(ctx->group) < 0) {
+            LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
+                avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+
+    }  /* if avahi_entry_group_is_empty*/
+
+    return;
+
+fail:
+    avahi_client_free (ctx->client);
+    avahi_threaded_poll_quit(ctx->threaded_poll);
+}
+
+/* Called when publishing of service data completes */
+static void publish_reply(AvahiEntryGroup *g,
+                          AvahiEntryGroupState state,
+                          AVAHI_GCC_UNUSED void *userdata)
+{
+    assert(ctx->group == NULL || g == ctx->group);
+
+    switch (state) {
+
+    case AVAHI_ENTRY_GROUP_ESTABLISHED :
+        /* The entry group has been established successfully */
+        LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
+        break;
+
+    case AVAHI_ENTRY_GROUP_COLLISION:
+        /* With multiple names there's no way to know which one collided */
+        LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        avahi_client_free(avahi_entry_group_get_client(g));
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+        break;
+               
+    case AVAHI_ENTRY_GROUP_FAILURE:
+        LOG(log_error, logtype_afpd, "Failed to register service: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        avahi_client_free(avahi_entry_group_get_client(g));
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+        break;
+
+    case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        break;
+    case AVAHI_ENTRY_GROUP_REGISTERING:
+        break;
+    }
+}
+
+static void client_callback(AvahiClient *client,
+                            AvahiClientState state,
+                            void *userdata)
+{
+    ctx->client = client;
+
+    switch (state) {
+    case AVAHI_CLIENT_S_RUNNING:
+        /* The server has startup successfully and registered its host
+         * name on the network, so it's time to create our services */
+        if (!ctx->group)
+            register_stuff();
+        break;
+
+    case AVAHI_CLIENT_S_COLLISION:
+        if (ctx->group)
+            avahi_entry_group_reset(ctx->group);
+        break;
+
+    case AVAHI_CLIENT_FAILURE: {
+        if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
+            int error;
+
+            avahi_client_free(ctx->client);
+            ctx->client = NULL;
+            ctx->group = NULL;
+
+            /* Reconnect to the server */
+            if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+                                                 AVAHI_CLIENT_NO_FAIL,
+                                                 client_callback,
+                                                 ctx,
+                                                 &error))) {
+
+                LOG(log_error, logtype_afpd, "Failed to contact server: %s",
+                    avahi_strerror(error));
+
+                avahi_client_free (ctx->client);
+                avahi_threaded_poll_quit(ctx->threaded_poll);
+            }
+
+        } else {
+            LOG(log_error, logtype_afpd, "Client failure: %s",
+                avahi_strerror(avahi_client_errno(client)));
+            avahi_client_free (ctx->client);
+            avahi_threaded_poll_quit(ctx->threaded_poll);
+        }
+        break;
+    }
+
+    case AVAHI_CLIENT_S_REGISTERING:
+        break;
+    case AVAHI_CLIENT_CONNECTING:
+        break;
+    }
+}
+
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
+/*
+ * Tries to setup the Zeroconf thread and any
+ * neccessary config setting.
+ */
+void av_zeroconf_setup(const AFPConfig *configs) {
+    int error, ret;
+
+    /* initialize the struct that holds our config settings. */
+    if (ctx) {
+        LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
+        avahi_entry_group_reset(ctx->group);
+    } else {
+        ctx = calloc(1, sizeof(struct context));
+        ctx->configs = configs;
+        assert(ctx);
+    }
+
+/* first of all we need to initialize our threading env */
+    if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
+        goto fail;
+    }
+
+/* now we need to acquire a client */
+    if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+                                         AVAHI_CLIENT_NO_FAIL,
+                                         client_callback,
+                                         NULL,
+                                         &error))) {
+        LOG(log_error, logtype_afpd, "Failed to create client object: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        goto fail;
+    }
+
+    return;
+
+fail:
+    if (ctx)
+        av_zeroconf_unregister();
+
+    return;
+}
+
+/*
+ * This function finally runs the loop impl.
+ */
+int av_zeroconf_run(void) {
+    int ret;
+
+    /* Finally, start the event loop thread */
+    if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
+        LOG(log_error, logtype_afpd, "Failed to create thread: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        goto fail;
+    } else {
+        LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
+    }
+
+    ctx->thread_running = 1;
+    return 0;
+
+fail:
+    if (ctx)
+        av_zeroconf_unregister();
+
+    return -1;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from outside this thread.
+ */
+void av_zeroconf_shutdown() {
+    /* Call this when the app shuts down */
+    avahi_threaded_poll_stop(ctx->threaded_poll);
+    avahi_client_free(ctx->client);
+    avahi_threaded_poll_free(ctx->threaded_poll);
+    free(ctx);
+    ctx = NULL;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from inside this thread.
+ */
+int av_zeroconf_unregister() {
+    if (ctx->thread_running) {
+        /* First, block the event loop */
+        avahi_threaded_poll_lock(ctx->threaded_poll);
+
+        /* Than, do your stuff */
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+
+        /* Finally, unblock the event loop */
+        avahi_threaded_poll_unlock(ctx->threaded_poll);
+        ctx->thread_running = 0;
+    }
+
+    if (ctx->client)
+        avahi_client_free(ctx->client);
+
+    if (ctx->threaded_poll)
+        avahi_threaded_poll_free(ctx->threaded_poll);
+
+    free(ctx);
+    ctx = NULL;
+
+    return 0;
+}
+
+#endif /* USE_AVAHI */
+
diff --git a/etc/afpd/afp_avahi.h b/etc/afpd/afp_avahi.h
new file mode 100644 (file)
index 0000000..2c6ca70
--- /dev/null
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs:    http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifndef AFPD_AVAHI_H
+#define AFPD_AVAHI_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include <atalk/logger.h>
+
+#include "afp_config.h"
+
+#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
+#define ADISK_SERVICE_TYPE "_adisk._tcp"
+
+#define MAXINSTANCENAMELEN 63
+
+struct context {
+       /* Avahi stuff */
+  int               thread_running;
+  AvahiThreadedPoll *threaded_poll;
+  AvahiClient       *client;
+  AvahiEntryGroup   *group;
+       /* Netatalk stuff */
+       const AFPConfig   *configs;
+};
+
+/* prototype definitions */
+void av_zeroconf_setup(const AFPConfig *configs);
+int av_zeroconf_run(void);
+int av_zeroconf_unregister(void);
+void av_zeroconf_shutdown(void);
+
+#endif   /* AFPD_AVAHI_H */
index 86ee093a0da93a660f751cbfc157aa300982bce6..12a3b494afdb9b83d5cab790aed1de6cd6fd8a7d 100644 (file)
@@ -48,7 +48,7 @@ char *strchr (), *strrchr ();
 #ifdef USE_SRVLOC
 #include <slp.h>
 #endif /* USE_SRVLOC */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 #include <atalk/ldapconfig.h>
 #endif
 
@@ -56,6 +56,8 @@ char *strchr (), *strrchr ();
 #include "afp_config.h"
 #include "uam_auth.h"
 #include "status.h"
+#include "volume.h"
+#include "afp_zeroconf.h"
 
 #define LINESIZE 1024  
 
@@ -94,6 +96,9 @@ void configfree(AFPConfig *configs, const AFPConfig *config)
         }
         free(p);
     }
+
+    /* the master loaded the volumes for zeroconf, get rid of that */
+    unload_volumes_and_extmap();
 }
 
 #ifdef USE_SRVLOC
@@ -155,9 +160,9 @@ static char * srvloc_encode(const struct afp_options *options, const char *name)
 }
 #endif /* USE_SRVLOC */
 
-#ifdef USE_SRVLOC
 static void dsi_cleanup(const AFPConfig *config)
 {
+#ifdef USE_SRVLOC
     SLPError err;
     SLPError callbackerr;
     SLPHandle hslp;
@@ -190,8 +195,8 @@ static void dsi_cleanup(const AFPConfig *config)
 srvloc_dereg_err:
     dsi->srvloc_url[0] = '\0';
     SLPClose(hslp);
-}
 #endif /* USE_SRVLOC */
+}
 
 #ifndef NO_DDP
 static void asp_cleanup(const AFPConfig *config)
@@ -453,7 +458,6 @@ srvloc_reg_err:
     }
 #endif /* USE_SRVLOC */
 
-
     config->fd = dsi->serversock;
     config->obj.handle = dsi;
     config->obj.config = config;
@@ -476,7 +480,7 @@ srvloc_reg_err:
 /* allocate server configurations. this should really store the last
  * entry in config->last or something like that. that would make
  * supporting multiple dsi transports easier. */
-static AFPConfig *AFPConfigInit(const struct afp_options *options,
+static AFPConfig *AFPConfigInit(struct afp_options *options,
                                 const struct afp_options *defoptions)
 {
     AFPConfig *config = NULL, *next = NULL;
@@ -543,12 +547,10 @@ AFPConfig *configinit(struct afp_options *cmdline)
     struct afp_options options;
     AFPConfig *config=NULL, *first = NULL; 
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     /* Parse afp_ldap.conf first so we can set the uuid option */
-    LOG(log_debug, logtype_afpd, "Start parsing afp_ldap.conf");
     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
-    LOG(log_debug, logtype_afpd, "Finished parsing afp_ldap.conf");
-#endif
+#endif /* HAVE_ACLS */
 
     /* if config file doesn't exist, load defaults */
     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
@@ -558,8 +560,6 @@ AFPConfig *configinit(struct afp_options *cmdline)
         return AFPConfigInit(cmdline, cmdline);
     }
 
-    LOG(log_debug, logtype_afpd, "Loading ConfigFile"); 
-
     /* scan in the configuration file */
     len = 0;
     while (!feof(fp)) {
@@ -585,13 +585,15 @@ AFPConfig *configinit(struct afp_options *cmdline)
         if (!afp_options_parseline(p, &options))
             continue;
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
        /* Enable UUID support if LDAP config is complete */
-       if (ldap_config_valid)
-           options.flags |= OPTION_UUID;
-#endif
+        if (ldap_config_valid) {
+            LOG(log_info, logtype_afpd, "Enabling UUID support");
+            options.flags |= OPTION_UUID;
+        }
+#endif /* HAVE_ACLS */
 
-        /* this should really get a head and a tail to simplify things. */
+        /* AFPConfigInit can return two linked configs due to DSI and ASP */
         if (!first) {
             if ((first = AFPConfigInit(&options, cmdline)))
                 config = first->next ? first->next : first;
@@ -606,5 +608,9 @@ AFPConfig *configinit(struct afp_options *cmdline)
     if (!have_option)
         first = AFPConfigInit(cmdline, cmdline);
 
+    /* Now register with zeroconf, we also need the volumes for that */
+    load_volumes(&first->obj);
+    zeroconf_register(first);
+
     return first;
 }
index 8bcfb9c6399ee9aeeeb7aeb0eeca131b229f3073..54e5c4b66ea594603ca3596c2abb04d2aa3ef3f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: afp_dsi.c,v 1.53 2010-03-30 12:55:26 franklahm Exp $
+ * $Id: afp_dsi.c,v 1.53 2010/03/30 12:55:26 franklahm Exp $
  *
  * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
@@ -37,6 +37,7 @@
 #include "switch.h"
 #include "auth.h"
 #include "fork.h"
+#include "dircache.h"
 
 #ifdef FORCE_UIDGID
 #warning UIDGID
@@ -376,6 +377,9 @@ void afp_over_dsi(AFPObj *obj)
     }
 #endif /* DEBUGGING */
 
+    if (dircache_init(obj->options.dircachesize) != 0)
+        afp_dsi_die(EXITERR_SYS);
+
     /* get stuck here until the end */
     while ((cmd = dsi_receive(dsi))) {
         child.tickle = 0;
@@ -385,6 +389,7 @@ void afp_over_dsi(AFPObj *obj)
         if (reload_request) {
             reload_request = 0;
             load_volumes(child.obj);
+            dircache_dump();
         }
 
         if (debug_request) {
index 057a1a32954164ef13330e27398273d27c65c9e9..c1ce4bc98b24970a2e8c0076564733b96e60a365 100644 (file)
@@ -47,6 +47,7 @@ char *strchr (), *strrchr ();
 #include "globals.h"
 #include "status.h"
 #include "auth.h"
+#include "dircache.h"
 
 #include <atalk/compat.h>
 
@@ -164,6 +165,7 @@ void afp_options_init(struct afp_options *options)
     options->systemvol.name = _PATH_AFPDSYSVOL;
     options->configfile = _PATH_AFPDCONF;
     options->sigconffile = _PATH_AFPDSIGCONF;
+    options->uuidconf = _PATH_AFPDUUIDCONF;
     options->uampath = _PATH_AFPDUAMPATH;
     options->uamlist = "uams_dhx.so,uams_dhx2.so";
     options->guest = "nobody";
@@ -194,6 +196,7 @@ void afp_options_init(struct afp_options *options)
     /* don't advertize slp by default */
     options->flags |= OPTION_NOSLP;
 #endif
+    options->dircachesize = DEFAULT_MAX_DIRCACHE_SIZE;
 }
 
 /* parse an afpd.conf line. i'm doing it this way because it's
@@ -219,7 +222,10 @@ int afp_options_parseline(char *buf, struct afp_options *options)
     if (strstr(buf, " -slp"))
         options->flags &= ~OPTION_NOSLP;
 #endif
-
+#ifdef USE_ZEROCONF
+    if (strstr(buf, " -nozeroconf"))
+        options->flags |= OPTION_NOZEROCONF;
+#endif
     if (strstr(buf, " -nouservolfirst"))
         options->flags &= ~OPTION_USERVOLFIRST;
     if (strstr(buf, " -uservolfirst"))
@@ -236,6 +242,8 @@ int afp_options_parseline(char *buf, struct afp_options *options)
         options->flags |= OPTION_CUSTOMICON;
     if (strstr(buf, " -advertise_ssh"))
         options->flags |= OPTION_ANNOUNCESSH;
+    if (strstr(buf, " -acl2os9mode"))
+        options->flags |= OPTION_ACL2OS9MODE;
 
     /* passwd bits */
     if (strstr(buf, " -nosavepassword"))
@@ -448,6 +456,9 @@ int afp_options_parseline(char *buf, struct afp_options *options)
     if ((c = getoption(buf, "-ntseparator")) && (opt = strdup(c)))
        options->ntseparator = opt;
 
+    if ((c = getoption(buf, "-dircachesize")))
+        options->dircachesize = atoi(c);
+     
     return 1;
 }
 
@@ -473,11 +484,14 @@ static void show_version( void )
        puts( "No" );
 #endif
 
-       printf( "      Transport layers:\t" );
+        printf( "        TCP/IP Support:\t" );
+        puts( "Yes" );
+
+       printf( "DDP(AppleTalk) Support:\t" );
 #ifdef NO_DDP
-       puts( "TCP/IP" );
+       puts( "No" );
 #else
-       puts( "TCP/IP DDP" );
+       puts( "Yes" );
 #endif
 
        printf( "         CNID backends:\t" );
@@ -524,6 +538,13 @@ static void show_version_extended(void )
        puts( "No" );
 #endif
 
+       printf( "      Zeroconf support:\t" );
+#ifdef USE_ZEROCONF
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
+
        printf( "  TCP wrappers support:\t" );
 #ifdef TCPWRAP
        puts( "Yes" );
diff --git a/etc/afpd/afp_zeroconf.c b/etc/afpd/afp_zeroconf.c
new file mode 100644 (file)
index 0000000..84f1871
--- /dev/null
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ *          particular Zeroconf implementation
+ * Doc:     http://www.dns-sd.org/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "afp_zeroconf.h"
+#include "afp_config.h"
+
+#ifdef HAVE_AVAHI
+#include "afp_avahi.h"
+#endif
+
+
+/*
+ * Functions (actually they are just facades)
+ */
+void zeroconf_register(const AFPConfig *configs)
+{
+#if defined (HAVE_AVAHI)
+  LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using Avahi");
+
+       av_zeroconf_setup(configs);
+  av_zeroconf_run();
+#endif
+}
+
+void zeroconf_deregister(void)
+{
+#if defined (HAVE_AVAHI)
+  LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using Avahi");
+       av_zeroconf_shutdown();
+#endif
+}
diff --git a/etc/afpd/afp_zeroconf.h b/etc/afpd/afp_zeroconf.h
new file mode 100644 (file)
index 0000000..e2711af
--- /dev/null
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ *          particular Zeroconf implementation
+ * Doc:     http://www.dns-sd.org/
+ *
+ */
+
+#ifndef AFPD_ZEROCONF_H
+#define AFPD_ZEROCONF_H
+
+#include "afp_config.h"
+
+/*
+ * Prototype Definitions
+ */
+
+/*
+ * registers service with a particular Zerconf implemenation.
+ */
+void zeroconf_register(const AFPConfig *configs);
+
+/*
+ * de-registers the ntpd service with a particular Zerconf implemenation.
+ */
+void zeroconf_deregister(void);
+
+#endif /* AFPD_ZEROCONF_H */
index 37850f62c471be718dd60f5046e3ce2325de32eb..d7e72f2395a5e78c9821d83160342eb11fde090b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: appl.c,v 1.18 2009-10-15 10:43:13 didg Exp $
+ * $Id: appl.c,v 1.18.4.1 2010-02-01 10:56:08 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -20,6 +20,8 @@
 
 #include <atalk/adouble.h>
 #include <atalk/afp.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include "volume.h"
 #include "globals.h"
@@ -122,7 +124,7 @@ static int copyapplfile(int sfd, int dfd, char *mpath, u_short mplen)
  * See afp_getappl() for the backward compatiblity code.
  */
 static char *
-makemacpath(char *mpath, int mpathlen, struct dir *dir, char *path)
+makemacpath(const struct vol *vol, char *mpath, int mpathlen, struct dir *dir, char *path)
 {
     char       *p;
 
@@ -130,16 +132,65 @@ makemacpath(char *mpath, int mpathlen, struct dir *dir, char *path)
     p -= strlen( path );
     memcpy( p, path, strlen( path )); 
 
-    while ( dir->d_parent != NULL ) {
-        p -= strlen( dir->d_m_name ) + 1;
+    while ( dir->d_did != DIRDID_ROOT ) {
+        p -= blength(dir->d_m_name) + 1;
         if (p < mpath) {
             /* FIXME: pathname too long */
             return NULL;
         }
-        strcpy( p, dir->d_m_name );
-        dir = dir->d_parent;
+        memcpy(p, cfrombstring(dir->d_m_name), blength(dir->d_m_name) + 1);
+        if ((dir = dirlookup(vol, dir->d_pdid)) == NULL)
+            return NULL;
     }
     return( p );
+
+
+#if 0
+    char buffer[12 + MAXPATHLEN + 1];
+    int buflen = 12 + MAXPATHLEN + 1;
+    char *ret = mpath;
+    char *path = name;
+    char *uname = NULL;
+    struct bstrList *pathlist = NULL;
+    cnid_t cnid = dir->d_pdid;
+
+    /* Create list for path elements, request 16 list elements for now*/
+    if ((pathlist = bstListCreateMin(16)) == NULL) {
+        LOG(log_error, logtype_afpd, "makemacpath: OOM: %s", strerror(errno));
+        return NULL;
+    }
+
+    while ( cnid != DIRDID_ROOT ) {
+
+        /* construct path, copy already found uname to path element list*/
+        if ((bstrListPush(pathlist, bfromcstr(path))) != BSTR_OK) {
+            afp_errno = AFPERR_MISC;
+            ret = NULL;
+            goto exit;
+        }
+
+        /* next part */
+        if ((uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) {
+            afp_errno = AFPERR_NOOBJ;
+            ret = NULL;
+            goto exit;
+        }
+
+        if ((path = utompath(vol, uname, cnid, utf8_encoding())) == NULL) {
+            afp_errno = AFPERR_MISC;
+            ret = NULL;
+            goto exit;
+        }
+    }
+
+
+
+exit:
+    if (pathlist)
+        bstrListDestroy(pathlist);
+
+    return(ret);
+#endif
 }
 
 
@@ -197,7 +248,7 @@ int afp_addappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, siz
         return( AFPERR_PARAM );
     }
     mpath = obj->newtmp;
-    mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
+    mp = makemacpath( vol, mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
     if (!mp) {
         return AFPERR_PARAM;
     }
@@ -280,7 +331,7 @@ int afp_rmvappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, siz
         return( AFPERR_PARAM );
     }
     mpath = obj->newtmp;
-    mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
+    mp = makemacpath( vol, mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
     if (!mp) {
         return AFPERR_PARAM ;
     }
@@ -418,7 +469,7 @@ int afp_getappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     memcpy( q, p, len );
     q = cbuf;
 
-    if (( path = cname( vol, vol->v_dir, &q )) == NULL ) {
+    if (( path = cname( vol, vol->v_root, &q )) == NULL ) {
         *rbuflen = 0;
         return( AFPERR_NOITEM );
     }
index 77e946ce3f02ff165f847e0c86d0c11f83107df3..0a5727fad0304babb30cb3f7a2fd23504adb5eb4 100644 (file)
@@ -46,7 +46,7 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #include "status.h"
 #include "fork.h"
 #include "extattrs.h"
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 #include "acls.h"
 #endif
 
@@ -208,11 +208,11 @@ static int set_auth_switch(int expired)
         afp_switch = postauth_switch;
         switch (afp_version) {
         case 32:
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
             uam_afpserver_action(AFP_GETACL, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
             uam_afpserver_action(AFP_SETACL, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
             uam_afpserver_action(AFP_ACCESS, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
-#endif
+#endif /* HAVE_ACLS */
             uam_afpserver_action(AFP_GETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_getextattr, NULL);
             uam_afpserver_action(AFP_SETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_setextattr, NULL);
             uam_afpserver_action(AFP_REMOVEATTR, UAM_AFPSERVER_POSTAUTH, afp_remextattr, NULL);
@@ -997,10 +997,10 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf,
         *rbuflen += sizeof(id);
     }
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     if (bitmap & USERIBIT_UUID) {
         int ret;
-        uuid_t uuid;
+        atalk_uuid_t uuid;
         char *uuidstring;
 
         if ( ! (obj->options.flags & OPTION_UUID))
@@ -1019,7 +1019,7 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf,
         rbuf += UUID_BINSIZE;
         *rbuflen += UUID_BINSIZE;
     }
-#endif
+#endif /* HAVE_ACLS */
 
     LOG(log_debug, logtype_afpd, "END afp_getuserinfo:");
     return AFP_OK;
index a1e96dfc50ab74649f67c09569f6ea8c99818e37..b266348ca0ca6a15073c1dfb003b70d90a426901 100644 (file)
 #ifdef CNID_DB
 #include <atalk/cnid.h>
 #endif /* CNID_DB */
+
 #include <atalk/util.h>
+#include <atalk/bstradd.h>
 
 #include "desktop.h"
 #include "directory.h"
+#include "dircache.h"
 #include "file.h"
 #include "volume.h"
 #include "globals.h"
@@ -144,6 +147,7 @@ static int addstack(char *uname, struct dir *dir, int pidx)
        /* Put new element. Allocate and copy lname and path. */
        ds = dstack + dsidx++;
        ds->dir = dir;
+    dir->d_flags |= DIRF_CACHELOCK;
        ds->pidx = pidx;
        ds->checked = 0;
        if (pidx >= 0) {
@@ -176,6 +180,7 @@ static int reducestack(void)
        while (dsidx > 0) {
                if (dstack[dsidx-1].checked) {
                        dsidx--;
+            dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK;
                        free(dstack[dsidx].path);
                } else
                        return dsidx - 1;
@@ -189,6 +194,7 @@ static void clearstack(void)
        save_cidx = -1;
        while (dsidx > 0) {
                dsidx--;
+        dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK;
                free(dstack[dsidx].path);
        }
 } 
@@ -503,7 +509,6 @@ static int catsearch(struct vol *vol, struct dir *dir,
        char *rrbuf = rbuf;
     time_t start_time;
     int num_rounds = NUM_ROUNDS;
-    int cached;
     int cwd = -1;
     int error;
         
@@ -540,14 +545,14 @@ static int catsearch(struct vol *vol, struct dir *dir,
     start_time = time(NULL);
 
        while ((cidx = reducestack()) != -1) {
-               cached = 1;
-
                error = lchdir(dstack[cidx].path);
 
-               if (!error && dirpos == NULL) {
+               if (!error && dirpos == NULL)
                        dirpos = opendir(".");
-                       cached = (dstack[cidx].dir->d_child != NULL);
-               }
+
+               if (dirpos == NULL)
+                       dirpos = opendir(dstack[cidx].path);
+
                if (error || dirpos == NULL) {
                        switch (errno) {
                        case EACCES:
@@ -594,18 +599,16 @@ static int catsearch(struct vol *vol, struct dir *dir,
                                   ie if in the same loop the parent dir wasn't in the cache
                                   ALL dirsearch_byname will fail.
                                */
-                               if (cached)
-                       path.d_dir = dirsearch_byname(vol, dstack[cidx].dir, path.u_name);
-               else
-                       path.d_dir = NULL;
-               if (!path.d_dir) {
+                int unlen = strlen(path.u_name);
+                path.d_dir = dircache_search_by_name(vol, dstack[cidx].dir, path.u_name, unlen);
+               if (path.d_dir == NULL) {
                        /* path.m_name is set by adddir */
-                   if (NULL == (path.d_dir = adddir( vol, dstack[cidx].dir, &path) ) ) {
+                   if (NULL == (path.d_dir = dir_add( vol, dstack[cidx].dir, &path, unlen) ) ) {
                                                result = AFPERR_MISC;
                                                goto catsearch_end;
                                        }
                 }
-                path.m_name = path.d_dir->d_m_name; 
+                path.m_name = cfrombstring(path.d_dir->d_m_name);
                        
                                if (addstack(path.u_name, path.d_dir, cidx) == -1) {
                                        result = AFPERR_MISC;
@@ -869,7 +872,7 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
     
     /* Call search */
     *rbuflen = 24;
-    ret = catsearch(vol, vol->v_dir, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+    ret = catsearch(vol, vol->v_root, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
     memcpy(rbuf, catpos, sizeof(catpos));
     rbuf += sizeof(catpos);
 
index 5db36f71925a0cf0dd06f04fab94bca8f14acae0..9f105e13e13c612757386f70c342626e28fe5e77 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: desktop.c,v 1.50 2010-01-22 04:40:38 didg Exp $
+ * $Id: desktop.c,v 1.50.2.1 2010-02-01 10:56:08 franklahm Exp $
  *
  * See COPYRIGHT.
  *
@@ -600,7 +600,8 @@ char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
     u_int16_t   flags;
         
     if ( *mpath == '\0' ) {
-        return( "." );
+        strcpy(upath, ".");
+        return upath;
     }
 
     /* set conversion flags */
@@ -700,7 +701,7 @@ static int ad_addcomment(struct vol *vol, struct path *path, char *ibuf)
     if (ad_getentryoff(adp, ADEID_COMMENT)) {
         if ( (ad_get_MD_flags( adp ) & O_CREAT) ) {
             if ( *path->m_name == '\0' ) {
-                name = curdir->d_m_name;
+                name = (char *)curdir->d_m_name->data;
             } else {
                 name = path->m_name;
             }
diff --git a/etc/afpd/dircache.c b/etc/afpd/dircache.c
new file mode 100644 (file)
index 0000000..145b962
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+
+#include "dircache.h"
+#include "directory.h"
+#include "hash.h"
+#include "globals.h"
+
+/*
+ * Directory Cache
+ * ===============
+ *
+ * Cache files and directories in a LRU cache.
+ *
+ * The directory cache caches directories and files(!). The main reason for having the cache
+ * is to avoid recursively walking up the CNID patch, querying the CNID database each time, when
+ * we have to calculate the location of eg directory with CNID 30, which is located in a dir with
+ * CNID 25, next CNID 20 and then CNID 2 (the volume root as per AFP spec).
+ * If all these dirs where in the cache, each database look up can be avoided. Additionally there's
+ * the element "fullpath" in struct dir, which is used to avoid the recursion in any case. Wheneveer
+ * a struct dir is initialized, the fullpath to the directory is stored there.
+ *
+ * In order to speed up the CNID query for files too, which eg happens when a directory is enumerated,
+ * files are stored too in the dircache. In order to differentiate between files and dirs, we re-use
+ * the element fullpath, which for files is always NULL.
+ *
+ * The most frequent codepatch that leads to caching is directory enumeration (cf enumerate.c):
+ * - if a element is a directory:
+ *   (1) the cache is searched by dircache_search_by_name()
+ *   (2) if it wasn't found a new struct dir is created and cached both from within dir_add()
+ * - for files the caching happens a little bit down the call chain:
+ *   (3) first getfilparams() is called, which calls
+ *   (4) getmetadata() where the cache is searched with dircache_search_by_name()
+ *   (5) if the element is not found
+ *   (6) get_id() queries the CNID from the database
+ *   (7) then a struct dir is initialized via dir_new() (note the fullpath arg is NULL)
+ *   (8) finally added to the cache with dircache_add()
+ * (2) of course does contain the steps 6,7 and 8.
+ *
+ * The dircache is a LRU cache, whenever it fills up we call dircache_evict internally which removes
+ * DIRCACHE_FREE_QUANTUM elements from the cache.
+ *
+ * There is only one cache for all volumes, so of course we use the volume is in hashing calculations.
+ *
+ * Indexes
+ * =======
+ *
+ * The maximum dircache size is:
+ * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
+ * It is a hashtable which we use to store "struct dir"s in. If the cache get full, oldest
+ * entries are evicted in chunks of DIRCACHE_FREE.
+ *
+ * We have/need two indexes:
+ * - a DID/name index on the main dircache, another hashtable
+ * - a queue index on the dircache, for evicting the oldest entries
+ * The cache supports locking of struct dir elements through the DIRF_CACHELOCK flag. A dir
+ * locked this way wont ever be removed from the cache, so be careful.
+ *
+ * Debugging
+ * =========
+ *
+ * Sending SIGHUP to a afpd child causes it to dump the dircache to a file "/tmp/dircache.PID".
+ */
+
+/********************************************************
+ * Local funcs and variables
+ ********************************************************/
+
+/*****************************
+ *       the dircache        */
+
+static hash_t       *dircache;        /* The actual cache */
+static unsigned int dircache_maxsize; /* cache maximum size */
+
+/* FNV 1a */
+static hash_val_t hash_vid_did(const void *key)
+{
+    const struct dir *k = (const struct dir *)key;
+    hash_val_t hash = 2166136261;
+
+    hash ^= k->d_vid >> 8;
+    hash *= 16777619;
+    hash ^= k->d_vid;
+    hash *= 16777619;
+
+    hash ^= k->d_did >> 24;
+    hash *= 16777619;
+    hash ^= (k->d_did >> 16) & 0xff;
+    hash *= 16777619;
+    hash ^= (k->d_did >> 8) & 0xff;
+    hash *= 16777619;
+    hash ^= (k->d_did >> 0) & 0xff;
+    hash *= 16777619;
+
+    return hash;
+}
+
+static int hash_comp_vid_did(const void *key1, const void *key2)
+{
+    const struct dir *k1 = key1;
+    const struct dir *k2 = key2;
+
+    return !(k1->d_did == k2->d_did && k1->d_vid == k2->d_vid);
+}
+
+/**************************************************
+ * DID/name index on dircache (another hashtable) */
+
+static hash_t *index_didname;
+
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)    \
+    || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)    \
+                      +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+static hash_val_t hash_didname(const void *p)
+{
+    const struct dir *key = (const struct dir *)p;
+    const unsigned char *data = key->d_u_name->data;
+    int len = key->d_u_name->slen;
+    hash_val_t hash = key->d_pdid + key->d_vid;
+    hash_val_t tmp;
+
+    int rem = len & 3;
+    len >>= 2;
+
+    /* Main loop */
+    for (;len > 0; len--) {
+        hash  += get16bits (data);
+        tmp    = (get16bits (data+2) << 11) ^ hash;
+        hash   = (hash << 16) ^ tmp;
+        data  += 2*sizeof (uint16_t);
+        hash  += hash >> 11;
+    }
+
+    /* Handle end cases */
+    switch (rem) {
+    case 3: hash += get16bits (data);
+        hash ^= hash << 16;
+        hash ^= data[sizeof (uint16_t)] << 18;
+        hash += hash >> 11;
+        break;
+    case 2: hash += get16bits (data);
+        hash ^= hash << 11;
+        hash += hash >> 17;
+        break;
+    case 1: hash += *data;
+        hash ^= hash << 10;
+        hash += hash >> 1;
+    }
+
+    /* Force "avalanching" of final 127 bits */
+    hash ^= hash << 3;
+    hash += hash >> 5;
+    hash ^= hash << 4;
+    hash += hash >> 17;
+    hash ^= hash << 25;
+    hash += hash >> 6;
+
+    return hash;
+}
+
+static int hash_comp_didname(const void *k1, const void *k2)
+{
+    const struct dir *key1 = (const struct dir *)k1;
+    const struct dir *key2 = (const struct dir *)k2;
+
+    return ! (key1->d_vid == key2->d_vid
+              && key1->d_pdid == key2->d_pdid
+              && (bstrcmp(key1->d_u_name, key2->d_u_name) == 0) );
+}
+
+/***************************
+ * queue index on dircache */
+
+static q_t *index_queue;    /* the index itself */
+static unsigned int queue_count;
+
+/*!
+ * @brief Remove a fixed number of (oldest) entries from the cache and indexes
+ *
+ * The default is to remove the 256 oldest entries from the cache.
+ * 1. Get the oldest entry
+ * 2. If it's in use ie open forks reference it or it's curdir requeue it,
+ *    or it's locked (from catsearch) dont remove it
+ * 3. Remove the dir from the main cache and the didname index
+ * 4. Free the struct dir structure and all its members
+ */
+static void dircache_evict(void)
+{
+    int i = DIRCACHE_FREE_QUANTUM;
+    struct dir *dir;
+
+    LOG(log_debug, logtype_afpd, "dircache: {starting cache eviction}");
+
+    while (i--) {
+        if ((dir = (struct dir *)dequeue(index_queue)) == NULL) { /* 1 */
+            dircache_dump();
+            AFP_PANIC("dircache_evict");
+        }
+        queue_count--;
+
+        if (curdir == dir
+            || dir->d_ofork
+            || (dir->d_flags & DIRF_CACHELOCK)) {     /* 2 */
+            if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
+                dircache_dump();
+                AFP_PANIC("dircache_evict");
+            }
+            queue_count++;
+            continue;
+        }
+
+        dircache_remove(NULL, dir, DIRCACHE | DIDNAME_INDEX); /* 3 */
+        dir_free(dir);                                        /* 4 */
+    }
+
+   AFP_ASSERT(queue_count == dircache->hash_nodecount);
+
+    LOG(log_debug, logtype_afpd, "dircache: {finished cache eviction}");
+}
+
+
+/********************************************************
+ * Interface
+ ********************************************************/
+
+/*!
+ * @brief Search the dircache via a DID
+ *
+ * @param vol    (r) pointer to struct vol
+ * @param did    (r) CNID of the directory
+ *
+ * @returns Pointer to struct dir if found, else NULL
+ */
+struct dir *dircache_search_by_did(const struct vol *vol, cnid_t did)
+{
+    struct dir *cdir = NULL;
+    struct dir key;
+    hnode_t *hn;
+
+   AFP_ASSERT(vol);
+   AFP_ASSERT(ntohl(did) >= CNID_START);
+
+    key.d_vid = vol->v_vid;
+    key.d_did = did;
+    if ((hn = hash_lookup(dircache, &key)))
+        cdir = hnode_get(hn);
+
+    if (cdir)
+        LOG(log_debug, logtype_afpd, "dircache(did:%u): {cached: path:'%s'}",
+            ntohl(did), cfrombstring(cdir->d_u_name));
+    else
+        LOG(log_debug, logtype_afpd, "dircache(did:%u): {not in cache}", ntohl(did));
+
+    return cdir;
+}
+
+/*!
+ * @brief Search the cache via did/name hashtable
+ *
+ * @param vol    (r) volume
+ * @param dir    (r) directory
+ * @param name   (r) name (server side encoding)
+ * @parma len    (r) strlen of name
+ *
+ * @returns pointer to struct dir if found in cache, else NULL
+ */
+struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir, char *name, int len)
+{
+    struct dir *cdir = NULL;
+    struct dir key;
+    hnode_t *hn;
+    static_bstring uname = {-1, len, (unsigned char *)name};
+
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
+    AFP_ASSERT(name);
+    AFP_ASSERT(len == strlen(name));
+    AFP_ASSERT(len < 256);
+
+    LOG(log_debug, logtype_afpd, "dircache_search_by_name(did:%u, \"%s\")",
+        ntohl(dir->d_did), name);
+
+    if (dir->d_did != DIRDID_ROOT_PARENT) {
+        key.d_vid = vol->v_vid;
+        key.d_pdid = dir->d_did;
+        key.d_u_name = &uname;
+
+        if ((hn = hash_lookup(index_didname, &key)))
+            cdir = hnode_get(hn);
+    }
+
+    if (cdir)
+        LOG(log_debug, logtype_afpd, "dircache(did:%u, '%s'): {found in cache}",
+            ntohl(dir->d_did), name);
+    else
+        LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {not in cache}",
+            ntohl(dir->d_did), name);
+
+    return cdir;
+}
+
+/*!
+ * @brief create struct dir from struct path
+ *
+ * Add a struct dir to the cache and its indexes.
+ *
+ * @param dir   (r) pointer to parrent directory
+ *
+ * @returns 0 on success, -1 on error which should result in an abort
+ */
+int dircache_add(struct dir *dir)
+{
+   AFP_ASSERT(dir);
+   AFP_ASSERT(ntohl(dir->d_pdid) >= 2);
+   AFP_ASSERT(ntohl(dir->d_did) >= CNID_START);
+   AFP_ASSERT(dir->d_u_name);
+   AFP_ASSERT(dir->d_vid);
+   AFP_ASSERT(dircache->hash_nodecount <= dircache_maxsize);
+
+    /* Check if cache is full */
+    if (dircache->hash_nodecount == dircache_maxsize)
+        dircache_evict();
+
+    /* Add it to the main dircache */
+    if (hash_alloc_insert(dircache, dir, dir) == 0) {
+        dircache_dump();
+        exit(EXITERR_SYS);
+    }
+
+    /* Add it to the did/name index */
+    if (hash_alloc_insert(index_didname, dir, dir) == 0) {
+        dircache_dump();
+        exit(EXITERR_SYS);
+    }
+
+    /* Add it to the fifo queue index */
+    if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
+        dircache_dump();
+        exit(EXITERR_SYS);
+    } else {
+        queue_count++;
+    }
+
+    LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {added}", ntohl(dir->d_did), cfrombstring(dir->d_u_name));
+
+   AFP_ASSERT(queue_count == index_didname->hash_nodecount 
+           && queue_count == dircache->hash_nodecount);
+
+    return 0;
+}
+
+/*!
+  * @brief Remove an entry from the dircache
+  *
+  * Callers outside of dircache.c should call this with
+  * flags = QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE.
+  */
+void dircache_remove(const struct vol *vol _U_, struct dir *dir, int flags)
+{
+    hnode_t *hn;
+
+   AFP_ASSERT(dir);
+   AFP_ASSERT((flags & ~(QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE)) == 0);
+
+    if (dir->d_flags & DIRF_CACHELOCK)
+        return;
+
+    if (flags & QUEUE_INDEX) {
+        /* remove it from the queue index */
+        dequeue(dir->qidx_node->prev); /* this effectively deletes the dequeued node */
+        queue_count--;
+    }
+
+    if (flags & DIDNAME_INDEX) {
+        if ((hn = hash_lookup(index_didname, dir)) == NULL) {
+            LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in didname index", 
+                ntohl(dir->d_did), dir->d_u_name);
+            dircache_dump();
+            AFP_PANIC("dircache_remove");
+        }
+        hash_delete_free(index_didname, hn);
+    }
+
+    if (flags & DIRCACHE) {
+        if ((hn = hash_lookup(dircache, dir)) == NULL) {
+            LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in dircache", 
+                ntohl(dir->d_did), dir->d_u_name);
+            dircache_dump();
+            AFP_PANIC("dircache_remove");
+        }
+        hash_delete_free(dircache, hn);
+    }
+
+    LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {removed}", ntohl(dir->d_did), dir->d_u_name);
+
+   AFP_ASSERT(queue_count == index_didname->hash_nodecount 
+           && queue_count == dircache->hash_nodecount);
+}
+
+/*!
+ * @brief Initialize the dircache and indexes
+ *
+ * This is called in child afpd initialisation. The maximum cache size will be
+ * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
+ * It initializes a hashtable which we use to store a directory cache in.
+ * It also initializes two indexes:
+ * - a DID/name index on the main dircache
+ * - a queue index on the dircache
+ *
+ * @param size   (r) requested maximum size from afpd.conf
+ *
+ * @return 0 on success, -1 on error
+ */
+int dircache_init(int reqsize)
+{
+    dircache_maxsize = DEFAULT_MAX_DIRCACHE_SIZE;
+
+    /* Initialize the main dircache */
+    if (reqsize > DEFAULT_MAX_DIRCACHE_SIZE && reqsize < MAX_POSSIBLE_DIRCACHE_SIZE) {
+        while ((dircache_maxsize < MAX_POSSIBLE_DIRCACHE_SIZE) && (dircache_maxsize < reqsize))
+               dircache_maxsize *= 2;
+    }
+    if ((dircache = hash_create(dircache_maxsize, hash_comp_vid_did, hash_vid_did)) == NULL)
+        return -1;
+    
+    LOG(log_debug, logtype_afpd, "dircache_init: done. max dircache size: %u", dircache_maxsize);
+
+    /* Initialize did/name index hashtable */
+    if ((index_didname = hash_create(dircache_maxsize, hash_comp_didname, hash_didname)) == NULL)
+        return -1;
+
+    /* Initialize index queue */
+    if ((index_queue = queue_init()) == NULL)
+        return -1;
+    else
+        queue_count = 0;
+
+    /* As long as directory.c hasn't got its own initializer call, we do it for it */
+    rootParent.d_did = DIRDID_ROOT_PARENT;
+    rootParent.d_fullpath = bfromcstr("ROOT_PARENT");
+    rootParent.d_m_name = bfromcstr("ROOT_PARENT");
+    rootParent.d_u_name = rootParent.d_m_name;
+
+    return 0;
+}
+
+/*!
+ * @brief Dump dircache to /tmp/dircache.PID
+ */
+void dircache_dump(void)
+{
+    char tmpnam[64];
+    FILE *dump;
+    qnode_t *n = index_queue->next;
+    const struct dir *dir;
+
+    LOG(log_warning, logtype_afpd, "Dumping directory cache...");
+
+    sprintf(tmpnam, "/tmp/dircache.%u", getpid());
+    if ((dump = fopen(tmpnam, "w+")) == NULL) {
+        LOG(log_error, logtype_afpd, "dircache_dump: %s", strerror(errno));
+        return;
+    }
+    setbuf(dump, NULL);
+
+    fprintf(dump, "Number of cache entries: %u\n", queue_count);
+    fprintf(dump, "Configured maximum cache size: %u\n", dircache_maxsize);
+    fprintf(dump, "==================================================\n\n");
+
+    for (int i = 1; i <= queue_count; i++) {
+        if (n == index_queue)
+            break;
+        dir = (struct dir *)n->data;
+        fprintf(dump, "%05u: vid:%u, pdid:%6u, did:%6u, path:%s, locked:%3s, oforks:%s\n",
+                i, ntohs(dir->d_vid), ntohl(dir->d_pdid), ntohl(dir->d_did),
+                cfrombstring(dir->d_u_name),
+                (dir->d_flags & DIRF_CACHELOCK) ? "yes" : "no",
+                dir->d_ofork ? "yes" : "no");
+        n = n->next;
+    }
+
+    fprintf(dump, "\n");
+    return;
+}
diff --git a/etc/afpd/dircache.h b/etc/afpd/dircache.h
new file mode 100644 (file)
index 0000000..d261ea5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+   $Id: dircache.h,v 1.1.2.5 2010-02-11 14:13:06 franklahm Exp $
+   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+   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.
+ */
+
+#ifndef DIRCACHE_H 
+#define DIRCACHE_H
+
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+
+/* Maximum size of the dircache hashtable */
+#define DEFAULT_MAX_DIRCACHE_SIZE 8192
+#define MAX_POSSIBLE_DIRCACHE_SIZE 131072
+#define DIRCACHE_FREE_QUANTUM 256
+
+/* flags for dircache_remove */
+#define DIRCACHE      (1 << 0)
+#define DIDNAME_INDEX (1 << 1)
+#define QUEUE_INDEX   (1 << 2)
+
+extern int        dircache_init(int reqsize);
+extern int        dircache_add(struct dir *);
+extern void       dircache_remove(const struct vol *, struct dir *, int flag);
+extern struct dir *dircache_search_by_did(const struct vol *vol, cnid_t did);
+extern struct dir *dircache_search_by_name(const struct vol *, const struct dir *dir, char *name, int len);
+extern void       dircache_dump(void);
+
+#endif /* DIRCACHE_H */
index 61339b0882d164c1b4a4dc07ec139e04f2c81ae1..76798d9bf60e39e81b4daef4afc9d046009fa3c3 100644 (file)
@@ -1,40 +1,22 @@
 /*
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
- *
- * 19 jan 2000 implemented red-black trees for directory lookups
- * (asun@cobalt.com).
  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-/* STDC check */
-#if STDC_HEADERS
 #include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#define strrchr index
-#endif /* HAVE_STRCHR */
-char *strchr (), *strrchr ();
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
 #include <stdio.h>
 #include <stdlib.h>
-
 #include <grp.h>
 #include <pwd.h>
 #include <sys/param.h>
+#include <sys/stat.h>
 #include <errno.h>
 #include <utime.h>
+#include <assert.h>
 
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
@@ -44,8 +26,11 @@ char *strchr (), *strrchr ();
 #include <atalk/logger.h>
 #include <atalk/uuid.h>
 #include <atalk/unix.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
@@ -56,583 +41,556 @@ char *strchr (), *strrchr ();
 #include "mangle.h"
 #include "hash.h"
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 extern void addir_inherit_acl(const struct vol *vol);
 #endif
 
-/* 
- * Directory caches
- * ================
- *
- * There are currently two cache structures where afpd caches directory information
- * a) a DID/dirname cache in a hashtable 
- * b) a (red-black) tree with CNIDs as key
- *
- * a) is for searching by DID/dirname
- * b) is for searching by CNID
- *
- * Through additional parent, child, previous and next pointers, b) is also used to
- * represent the on-disk layout of the filesystem. parent and child point to parent
- * and child directory respectively, linking 2 or more subdirectories in one
- * directory with previous and next pointers.
- *
- * Usage examples, highlighting the main functions:
- * 
- * a) is eg used in enumerate():
- * if IS_DIR
- *     dir = dirsearch_byname() // search in cache
- *     if (dir == NULL)         // not found
- *         dir = adddir()       // add to cache
- *      getdirparams()
- *
- * b) is eg used in afp_getfildirparams()
- * dirlookup()             // wrapper for cache and db search
- *   => dir = dirsearch()  // search in cache
- *      if (dir)           // found
- *          return
- *      else               // not found,
- *          cnid_resolve() // resolve with CNID database
- *      cname()            // add to cache
+/*
+ * FIXMEs, loose ends after the dircache rewrite:
+ * o merge dircache_search_by_name and dir_add ??
+ * o case-insensitivity is gone from cname
  */
 
-struct dir  *curdir;
+
+/*******************************************************************************************
+ * Globals
+ ******************************************************************************************/
+
 int         afp_errno;
+struct dir rootParent  = {
+    NULL, NULL, NULL, NULL,          /* path, d_m_name, d_u_name, d_m_name_ucs2 */
+    NULL, NULL, 0, 0,                /* qidx_node, d_ofork, ctime, d_flags */
+    0, 0, 0, 0                       /* pdid, did, offcnt, d_vid */
+};
+struct dir  *curdir = &rootParent;
+struct path Cur_Path = {
+    0,
+    "",  /* mac name */
+    ".", /* unix name */
+    0,   /* id */
+    NULL,/* struct dir * */
+    0,   /* stat is not set */
+    0,   /* errno */
+    {0} /* struct stat */
+};
 
-#define SENTINEL (&sentinel)
-static struct dir sentinel = { SENTINEL, SENTINEL, NULL, /* left, right, back */
-                               DIRTREE_COLOR_BLACK,      /* color */
-                               NULL, NULL,               /* parent, child */
-                               NULL, NULL,               /* previous, next */
-                               NULL, 0, 0,               /* oforks, did, flags */
-                               0, 0,                     /* ctime, offcnt */
-                               NULL, NULL, NULL};        /* mname, uname, ucs2-name */
-static struct dir rootpar  = { SENTINEL, SENTINEL, NULL,
-                               0,
-                               NULL, NULL,
-                               NULL, NULL,
-                               NULL, 0, 0,
-                               0, 0,
-                               NULL, NULL, NULL};
-
-/* (from IM: Toolbox Essentials)
- * dirFinderInfo (DInfo) fields:
- * field        bytes
- * frRect       8    folder's window rectangle
- * frFlags      2    flags
- * frLocation   4    folder's location in window
- * frView       2    folder's view (default == closedView (256))
- *
- * extended dirFinderInfo (DXInfo) fields:
- * frScroll     4    scroll position
- * frOpenChain: 4    directory ID chain of open folders
- * frScript:    1    script flag and code
- * frXFlags:    1    reserved
- * frComment:   2    comment ID
- * frPutAway:   4    home directory ID
- */
 
-static struct dir *
-vol_tree_root(const struct vol *vol, u_int32_t did)
-{
-    struct dir *dir;
+/*******************************************************************************************
+ * Locals
+ ******************************************************************************************/
 
-    if (vol->v_curdir && vol->v_curdir->d_did == did) {
-        dir = vol->v_curdir;
-    }
-    else {
-        dir = vol->v_root;
-    }
-    return dir;
-}
 
-/*
- * redid did assignment for directories. now we use red-black trees.
- * how exciting.
- */
-struct dir *
-dirsearch(const struct vol *vol, u_int32_t did)
+/* -------------------------
+   appledouble mkdir afp error code.
+*/
+static int netatalk_mkdir(const struct vol *vol, const char *name)
 {
-    struct dir  *dir;
+    int ret;
+    struct stat st;
 
+    if (vol->v_flags & AFPVOL_UNIX_PRIV) {
+        if (lstat(".", &st) < 0)
+            return AFPERR_MISC;
+        int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
+        LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
+            name, st.st_mode, vol->v_umask);
 
-    /* check for 0 did */
-    if (!did) {
-        afp_errno = AFPERR_PARAM;
-        return NULL;
-    }
-    if ( did == DIRDID_ROOT_PARENT ) {
-        if (!rootpar.d_did)
-            rootpar.d_did = DIRDID_ROOT_PARENT;
-        rootpar.d_child = vol->v_dir;
-        return( &rootpar );
+        ret = mkdir(name, mode);
+    } else {
+        ret = ad_mkdir(name, DIRBITS | 0777);
     }
 
-    dir = vol_tree_root(vol, did);
-
-    afp_errno = AFPERR_NOOBJ;
-    while ( dir != SENTINEL ) {
-        if (dir->d_did == did)
-            return dir->d_m_name ? dir : NULL;
-        dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
+    if (ret < 0) {
+        switch ( errno ) {
+        case ENOENT :
+            return( AFPERR_NOOBJ );
+        case EROFS :
+            return( AFPERR_VLOCK );
+        case EPERM:
+        case EACCES :
+            return( AFPERR_ACCESS );
+        case EEXIST :
+            return( AFPERR_EXIST );
+        case ENOSPC :
+        case EDQUOT :
+            return( AFPERR_DFULL );
+        default :
+            return( AFPERR_PARAM );
+        }
     }
-    return NULL;
+    return AFP_OK;
 }
 
 /* ------------------- */
-int get_afp_errno(const int param)
+static int deletedir(int dirfd, char *dir)
 {
-    if (afp_errno != AFPERR_DID1)
-        return afp_errno;
-    return param;
-}
+    char path[MAXPATHLEN + 1];
+    DIR *dp;
+    struct dirent   *de;
+    struct stat st;
+    size_t len;
+    int err = AFP_OK;
+    size_t remain;
 
-/* ------------------- */
-struct dir *
-dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
-{
-    struct dir *dir = NULL;
+    if ((len = strlen(dir)) +2 > sizeof(path))
+        return AFPERR_PARAM;
 
-    if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
-        struct dir key;
-        hnode_t *hn;
+    /* already gone */
+    if ((dp = opendirat(dirfd, dir)) == NULL)
+        return AFP_OK;
 
-        key.d_parent = cdir;
-        key.d_u_name = name;
-        hn = hash_lookup(vol->v_hash, &key);
-        if (hn) {
-            dir = hnode_get(hn);
+    strcpy(path, dir);
+    strcat(path, "/");
+    len++;
+    remain = sizeof(path) -len -1;
+    while ((de = readdir(dp)) && err == AFP_OK) {
+        /* skip this and previous directory */
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+
+        if (strlen(de->d_name) > remain) {
+            err = AFPERR_PARAM;
+            break;
+        }
+        strcpy(path + len, de->d_name);
+        if (lstatat(dirfd, path, &st)) {
+            continue;
+        }
+        if (S_ISDIR(st.st_mode)) {
+            err = deletedir(dirfd, path);
+        } else {
+            err = netatalk_unlinkat(dirfd, path);
         }
     }
-    return dir;
+    closedir(dp);
+
+    /* okay. the directory is empty. delete it. note: we already got rid
+       of .AppleDouble.  */
+    if (err == AFP_OK) {
+        err = netatalk_rmdir(dirfd, dir);
+    }
+    return err;
 }
 
-/* -----------------------------------------
- * if did is not in the cache resolve it with cnid
- *
- * FIXME
- * OSX call it with bogus id, ie file ID not folder ID,
- * and we are really bad in this case.
- */
-struct dir *
-dirlookup( struct vol *vol, u_int32_t did)
+/* do a recursive copy. */
+static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
 {
-    struct dir   *ret;
-    char     *upath;
-    cnid_t       id, cnid;
-    static char  path[MAXPATHLEN + 1];
-    size_t len,  pathlen;
-    char         *ptr;
-    static char  buffer[12 + MAXPATHLEN + 1];
-    int          buflen = 12 + MAXPATHLEN + 1;
-    char         *mpath;
-    int          utf8;
-    size_t       maxpath;
+    char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
+    DIR *dp;
+    struct dirent   *de;
+    struct stat st;
+    struct utimbuf      ut;
+    size_t slen, dlen;
+    size_t srem, drem;
+    int err;
 
-    ret = dirsearch(vol, did);
-    if (ret != NULL || afp_errno == AFPERR_PARAM)
-        return ret;
+    /* doesn't exist or the path is too long. */
+    if (((slen = strlen(src)) > sizeof(spath) - 2) ||
+        ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
+        ((dp = opendirat(dirfd, src)) == NULL))
+        return AFPERR_PARAM;
 
-    utf8 = utf8_encoding();
-    maxpath = (utf8)?MAXPATHLEN -7:255;
-    id = did;
-    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
-        afp_errno = AFPERR_NOOBJ;
-        return NULL;
-    }
-    ptr = path + MAXPATHLEN;
-    if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
-        afp_errno = AFPERR_NOOBJ;
-        return NULL;
+    /* try to create the destination directory */
+    if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
+        closedir(dp);
+        return err;
     }
-    len = strlen(mpath);
-    pathlen = len;          /* no 0 in the last part */
-    len++;
-    strcpy(ptr - len, mpath);
-    ptr -= len;
-    while (1) {
-        ret = dirsearch(vol,id);
-        if (ret != NULL) {
-            break;
-        }
-        cnid = id;
-        if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
-             ||
-             NULL == (mpath = utompath(vol, upath, cnid, utf8))
-            ) {
-            afp_errno = AFPERR_NOOBJ;
-            return NULL;
-        }
 
-        len = strlen(mpath) + 1;
-        pathlen += len;
-        if (pathlen > maxpath) {
-            afp_errno = AFPERR_PARAM;
-            return NULL;
+    /* set things up to copy */
+    strcpy(spath, src);
+    strcat(spath, "/");
+    slen++;
+    srem = sizeof(spath) - slen -1;
+
+    strcpy(dpath, dst);
+    strcat(dpath, "/");
+    dlen++;
+    drem = sizeof(dpath) - dlen -1;
+
+    err = AFP_OK;
+    while ((de = readdir(dp))) {
+        /* skip this and previous directory */
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+
+        if (strlen(de->d_name) > srem) {
+            err = AFPERR_PARAM;
+            break;
         }
-        strcpy(ptr - len, mpath);
-        ptr -= len;
-    }
+        strcpy(spath + slen, de->d_name);
 
-    /* fill the cache, another place where we know about the path type */
-    if (utf8) {
-        u_int16_t temp16;
-        u_int32_t temp;
+        if (lstatat(dirfd, spath, &st) == 0) {
+            if (strlen(de->d_name) > drem) {
+                err = AFPERR_PARAM;
+                break;
+            }
+            strcpy(dpath + dlen, de->d_name);
 
-        ptr -= 2;
-        temp16 = htons(pathlen);
-        memcpy(ptr, &temp16, sizeof(temp16));
+            if (S_ISDIR(st.st_mode)) {
+                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
+                    goto copydir_done;
+            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
+                goto copydir_done;
 
-        temp = htonl(kTextEncodingUTF8);
-        ptr -= 4;
-        memcpy(ptr, &temp, sizeof(temp));
-        ptr--;
-        *ptr = 3;
+            } else {
+                /* keep the same time stamp. */
+                ut.actime = ut.modtime = st.st_mtime;
+                utime(dpath, &ut);
+            }
+        }
     }
-    else {
-        ptr--;
-        *ptr = (unsigned char)pathlen;
-        ptr--;
-        *ptr = 2;
+
+    /* keep the same time stamp. */
+    if (lstatat(dirfd, src, &st) == 0) {
+        ut.actime = ut.modtime = st.st_mtime;
+        utime(dst, &ut);
     }
-    /* cname is not efficient */
-    if (cname( vol, ret, &ptr ) == NULL )
-        return NULL;
 
-    return dirsearch(vol, did);
+copydir_done:
+    closedir(dp);
+    return err;
 }
 
-/* child addition/removal */
-static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
+/* ---------------------
+ * is our cached offspring count valid?
+ */
+static int diroffcnt(struct dir *dir, struct stat *st)
 {
-    if (!a->d_child)
-        a->d_child = b;
-    else {
-        b->d_next = a->d_child;
-        b->d_prev = b->d_next->d_prev;
-        b->d_next->d_prev = b;
-        b->d_prev->d_next = b;
-    }
-    if (!hash_alloc_insert(vol->v_hash, b, b)) {
-        LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
-    }
+    return st->st_ctime == dir->ctime;
 }
 
-static void dirchildremove(struct dir *a,struct dir *b)
+/* --------------------- */
+static int invisible_dots(const struct vol *vol, const char *name)
 {
-    if (a->d_child == b)
-        a->d_child = (b == b->d_next) ? NULL : b->d_next;
-    b->d_next->d_prev = b->d_prev;
-    b->d_prev->d_next = b->d_next;
-    b->d_next = b->d_prev = b;
+    return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
 }
 
-/* --------------------------- */
-/* rotate the tree to the left */
-static void dir_leftrotate(struct vol *vol, struct dir *dir)
+/* ------------------ */
+static int set_dir_errors(struct path *path, const char *where, int err)
 {
-    struct dir *right = dir->d_right;
-
-    /* whee. move the right's left tree into dir's right tree */
-    dir->d_right = right->d_left;
-    if (right->d_left != SENTINEL)
-        right->d_left->d_back = dir;
-
-    if (right != SENTINEL) {
-        right->d_back = dir->d_back;
-        right->d_left = dir;
+    switch ( err ) {
+    case EPERM :
+    case EACCES :
+        return AFPERR_ACCESS;
+    case EROFS :
+        return AFPERR_VLOCK;
     }
+    LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
+    return AFPERR_PARAM;
+}
 
-    if (!dir->d_back) /* no parent. move the right tree to the top. */
-        vol->v_root = right;
-    else if (dir == dir->d_back->d_left) /* we were on the left */
-        dir->d_back->d_left = right;
-    else
-        dir->d_back->d_right = right; /* we were on the right */
+/*!
+ * @brief Convert name in client encoding to server encoding
+ *
+ * Convert ret->m_name to ret->u_name from client encoding to server encoding.
+ * This only gets called from cname().
+ *
+ * @returns 0 on success, -1 on error
+ *
+ * @note If the passed ret->m_name is mangled, we'll demangle it
+ */
+static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct path *ret, int toUTF8)
+{
+    static char temp[ MAXPATHLEN + 1];
+    char *t;
+    cnid_t fileid;
+
+    if (afp_version >= 30) {
+        if (toUTF8) {
+            if (dir->d_did == DIRDID_ROOT_PARENT) {
+                /*
+                 * With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
+                 * So we compare it with the longname from the current volume and if they match
+                 * we overwrite the requested path with the utf8 volume name so that the following
+                 * strcmp can match.
+                 */
+                ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
+                if (strcasecmp(ret->m_name, temp) == 0)
+                    ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, ret->m_name, AFPVOL_U8MNAMELEN);
+            } else {
+                /* toUTF8 */
+                if (mtoUTF8(vol, ret->m_name, strlen(ret->m_name), temp, MAXPATHLEN) == (size_t)-1) {
+                    afp_errno = AFPERR_PARAM;
+                    return -1;
+                }
+                strcpy(ret->m_name, temp);
+            }
+        }
 
-    /* re-insert dir on the left tree */
-    if (dir != SENTINEL)
-        dir->d_back = right;
-}
+        /* check for OS X mangled filename :( */
+        t = demangle_osx(vol, ret->m_name, dir->d_did, &fileid);
+        LOG(log_maxdebug, logtype_afpd, "cname_mtouname('%s',did:%u) {demangled:'%s', fileid:%u}",
+            ret->m_name, ntohl(dir->d_did), t, ntohl(fileid));
+
+        if (t != ret->m_name) {
+            ret->u_name = t;
+            /* duplicate work but we can't reuse all convert_char we did in demangle_osx
+             * flags weren't the same
+             */
+            if ( (t = utompath(vol, ret->u_name, fileid, utf8_encoding())) ) {
+                /* at last got our view of mac name */
+                strcpy(ret->m_name, t);
+            }
+        }
+    } /* afp_version >= 30 */
 
+    /* If we haven't got it by now, get it */
+    if (ret->u_name == NULL) {
+        if ((ret->u_name = mtoupath(vol, ret->m_name, dir->d_did, utf8_encoding())) == NULL) {
+            afp_errno = AFPERR_PARAM;
+            return -1;
+        }
+    }
 
+    return 0;
+}
 
-/* rotate the tree to the right */
-static void dir_rightrotate(struct vol *vol, struct dir *dir)
+/*!
+ * @brief Build struct path from struct dir
+ *
+ * The final movecwd in cname failed, possibly with EPERM or ENOENT. We:
+ * 1. move cwd into parent dir (we're often already there, but not always)
+ * 2. set struct path to the dirname
+ * 3. in case of
+ *    AFPERR_ACCESS: the dir is there, we just cant chdir into it
+ *    AFPERR_NOOBJ: the dir was there when we stated it in cname, so we have a race
+ *                  4. indicate there's no dir for this path
+ *                  5. remove the dir
+ */
+static struct path *path_from_dir(struct vol *vol, struct dir *dir, struct path *ret)
 {
-    struct dir *left = dir->d_left;
+    if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+        return NULL;
 
-    /* whee. move the left's right tree into dir's left tree */
-    dir->d_left = left->d_right;
-    if (left->d_right != SENTINEL)
-        left->d_right->d_back = dir;
+    switch (afp_errno) {
 
-    if (left != SENTINEL) {
-        left->d_back = dir->d_back;
-        left->d_right = dir;
-    }
+    case AFPERR_ACCESS:
+        if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+            return NULL;
 
-    if (!dir->d_back) /* no parent. move the left tree to the top. */
-        vol->v_root = left;
-    else if (dir == dir->d_back->d_right) /* we were on the right */
-        dir->d_back->d_right = left;
-    else
-        dir->d_back->d_left = left; /* we were on the left */
+        memcpy(ret->m_name, cfrombstring(dir->d_m_name), blength(dir->d_m_name) + 1); /* 3 */
+        if (dir->d_m_name == dir->d_u_name) {
+            ret->u_name = ret->m_name;
+        } else {
+            ret->u_name =  ret->m_name + blength(dir->d_m_name) + 1;
+            memcpy(ret->u_name, cfrombstring(dir->d_u_name), blength(dir->d_u_name) + 1);
+        }
 
-    /* re-insert dir on the right tree */
-    if (dir != SENTINEL)
-        dir->d_back = left;
-}
+        ret->d_dir = dir;
 
-#if 0
-/* recolor after a removal */
-static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
-{
-    struct dir *leaf;
-
-    while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
-        /* are we on the left tree? */
-        if (dir == dir->d_back->d_left) {
-            leaf = dir->d_back->d_right; /* get right side */
-            if (leaf->d_color == DIRTREE_COLOR_RED) {
-                /* we're red. we need to change to black. */
-                leaf->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_leftrotate(vol, dir->d_back);
-                leaf = dir->d_back->d_right;
-            }
+        LOG(log_debug, logtype_afpd, "cname('%s') {path-from-dir: AFPERR_ACCESS. curdir:'%s', path:'%s'}",
+            cfrombstring(dir->d_fullpath),
+            cfrombstring(curdir->d_fullpath),
+            ret->u_name);
 
-            /* right leaf has black end nodes */
-            if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
-                (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
-                leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
-                dir = dir->d_back; /* ascend */
-            } else {
-                if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
-                    leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
-                    leaf->d_color = DIRTREE_COLOR_RED;
-                    dir_rightrotate(vol, leaf);
-                    leaf = dir->d_back->d_right;
-                }
-                leaf->d_color = dir->d_back->d_color;
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
-                dir_leftrotate(vol, dir->d_back);
-                dir = vol->v_root;
-            }
-        } else { /* right tree */
-            leaf = dir->d_back->d_left; /* left tree */
-            if (leaf->d_color == DIRTREE_COLOR_RED) {
-                leaf->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_rightrotate(vol, dir->d_back);
-                leaf = dir->d_back->d_left;
-            }
+        return ret;
 
-            /* left leaf has black end nodes */
-            if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
-                (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
-                leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
-                dir = dir->d_back; /* ascend */
-            } else {
-                if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
-                    leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
-                    leaf->d_color = DIRTREE_COLOR_RED;
-                    dir_leftrotate(vol, leaf);
-                    leaf = dir->d_back->d_left;
-                }
-                leaf->d_color = dir->d_back->d_color;
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
-                dir_rightrotate(vol, dir->d_back);
-                dir = vol->v_root;
-            }
+    case AFPERR_NOOBJ:
+        if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+            return NULL;
+
+        memcpy(ret->m_name, cfrombstring(dir->d_m_name), blength(dir->d_m_name) + 1);
+        if (dir->d_m_name == dir->d_u_name) {
+            ret->u_name = ret->m_name;
+        } else {
+            ret->u_name =  ret->m_name + blength(dir->d_m_name) + 1;
+            memcpy(ret->u_name, cfrombstring(dir->d_u_name), blength(dir->d_u_name) + 1);
         }
+
+        ret->d_dir = NULL;      /* 4 */
+        dir_remove(vol, dir);   /* 5 */
+        return ret;
+
+    default:
+        return NULL;
     }
-    dir->d_color = DIRTREE_COLOR_BLACK;
 
-    return dir;
+    /* DEADC0DE: never get here */
+    return NULL;
 }
-#endif /* 0 */
 
-/* --------------------- */
-static void dir_hash_del(const struct vol *vol, struct dir *dir)
-{
-    hnode_t *hn;
 
-    hn = hash_lookup(vol->v_hash, dir);
-    if (!hn) {
-        LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
-    }
-    else {
-        hash_delete(vol->v_hash, hn);
-    }
-}
+/*********************************************************************************************
+ * Interface
+ ********************************************************************************************/
 
-/* remove the node from the tree. this is just like insertion, but
- * different. actually, it has to worry about a bunch of things that
- * insertion doesn't care about. */
+int get_afp_errno(const int param)
+{
+    if (afp_errno != AFPERR_DID1)
+        return afp_errno;
+    return param;
+}
 
-static void dir_remove( struct vol *vol, struct dir *dir)
+/*!
+ * @brief Resolve a DID
+ *
+ * Resolve a DID, allocate a struct dir for it
+ * 1. Check for special CNIDs 0 (invalid), 1 and 2.
+ * 2a. Check if the DID is in the cache.
+ * 2b. Check if it's really a dir (d_fullpath != NULL) because we cache files too.
+ * 3. If it's not in the cache resolve it via the database.
+ * 4. Build complete server-side path to the dir.
+ * 5. Check if it exists and is a directory.
+ * 6. Create the struct dir and populate it.
+ * 7. Add it to the cache.
+ *
+ * @param vol   (r) pointer to struct vol
+ * @param did   (r) DID to resolve
+ *
+ * @returns pointer to struct dir
+ *
+ * @note FIXME: OSX calls it with bogus id, ie file ID not folder ID,
+ *       and we are really bad in this case.
+ */
+struct dir *dirlookup(const struct vol *vol, cnid_t did)
 {
-#ifdef REMOVE_NODES
-    struct ofork *of, *last;
-    struct dir *node, *leaf;
-#endif /* REMOVE_NODES */
-
-    if (!dir || (dir == SENTINEL))
-        return;
-
-    /* i'm not sure if it really helps to delete stuff. */
-    dir_hash_del(vol, dir);
-    vol->v_curdir = NULL;
-#ifndef REMOVE_NODES
-    dirfreename(dir);
-    dir->d_m_name = NULL;
-    dir->d_u_name = NULL;
-    dir->d_m_name_ucs2 = NULL;
-#else /* ! REMOVE_NODES */
-
-    /* go searching for a node with at most one child */
-    if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
-        node = dir;
-    } else {
-        node = dir->d_right;
-        while (node->d_left != SENTINEL)
-            node = node->d_left;
-    }
+    static char  buffer[12 + MAXPATHLEN + 1];
+    struct stat  st;
+    struct dir   *ret = NULL, *pdir;
+    bstring      fullpath = NULL;
+    char         *upath = NULL, *mpath;
+    cnid_t       cnid, pdid;
+    size_t       maxpath;
+    int          buflen = 12 + MAXPATHLEN + 1;
+    int          utf8;
+    int          err = 0;
 
-    /* get that child */
-    leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
+    LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {start}", ntohl(did));
 
-    /* detach node */
-    leaf->d_back = node->d_back;
-    if (!node->d_back) {
-        vol->v_root = leaf;
-    } else if (node == node->d_back->d_left) { /* left tree */
-        node->d_back->d_left = leaf;
-    } else {
-        node->d_back->d_right = leaf;
+    /* check for did 0, 1 and 2 */
+    if (did == 0 || vol == NULL) { /* 1 */
+        afp_errno = AFPERR_PARAM;
+        return NULL;
+    } else if (did == DIRDID_ROOT_PARENT) {
+        rootParent.d_vid = vol->v_vid;
+        return (&rootParent);
+    } else if (did == DIRDID_ROOT) {
+        return vol->v_root;
     }
 
-    /* we want to free node, but we also want to free the data in dir.
-     * currently, that's d_name and the directory traversal bits.
-     * we just copy the necessary bits and then fix up all the
-     * various pointers to the directory. needless to say, there are
-     * a bunch of places that store the directory struct. */
-    if (node != dir) {
-        struct dir save, *tmp;
+    /* Search the cache */
+    if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */
+        if (ret->d_fullpath == NULL) {                      /* 2b */
+            afp_errno = AFPERR_BADTYPE;
+            return NULL;
+        }
+        if (lstat(cfrombstring(ret->d_fullpath), &st) != 0) {
+            LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {lstat: %s}", ntohl(did), strerror(errno));
+            switch (errno) {
+            case ENOENT:
+            case ENOTDIR:
+                /* It's not there anymore, so remove it */
+                LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {calling dir_remove()}", ntohl(did));
+                dir_remove(vol, ret);
+                afp_errno = AFPERR_NOOBJ;
+                return NULL;
+            default:
+                return ret;
+            }
+            /* DEADC0DE */
+            return NULL;
+        }
+        return ret;
+    }
 
-        memcpy(&save, dir, sizeof(save));
-        memcpy(dir, node, sizeof(struct dir));
+    utf8 = utf8_encoding();
+    maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
 
-        /* restore the red-black bits */
-        dir->d_left = save.d_left;
-        dir->d_right = save.d_right;
-        dir->d_back = save.d_back;
-        dir->d_color = save.d_color;
+    /* Get it from the database */
+    cnid = did;
+    if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL 
+         || (upath = strdup(upath)) == NULL) { /* 3 */
+        afp_errno = AFPERR_NOOBJ;
+        err = 1;
+        goto exit;
+    }
+    pdid = cnid;
 
-        if (node == vol->v_dir) {/* we may need to fix up this pointer */
-            vol->v_dir = dir;
-            rootpar.d_child = vol->v_dir;
-        } else {
-            /* if we aren't the root directory, we have parents and
-             * siblings to worry about */
-            if (dir->d_parent->d_child == node)
-                dir->d_parent->d_child = dir;
-            dir->d_next->d_prev = dir;
-            dir->d_prev->d_next = dir;
-        }
+    /*
+     * Recurse up the tree, terminates in dirlookup when either
+     * - DIRDID_ROOT is hit
+     * - a cached entry is found
+     */
+    if ((pdir = dirlookup(vol, pdid)) == NULL) {
+        err = 1;
+        goto exit;
+    }
 
-        /* fix up children. */
-        tmp = dir->d_child;
-        while (tmp) {
-            tmp->d_parent = dir;
-            tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
-        }
+    /* build the fullpath */
+    if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
+        || bconchar(fullpath, '/') != BSTR_OK
+        || bcatcstr(fullpath, upath) != BSTR_OK) {
+        err = 1;
+        goto exit;
+    }
 
-        if (node == curdir) /* another pointer to fixup */
-            curdir = dir;
+    /* stat it and check if it's a dir */
+    LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstring(fullpath));
 
-        /* we also need to fix up oforks. bleah */
-        if ((of = dir->d_ofork)) {
-            last = of->of_d_prev;
-            while (of) {
-                of->of_dir = dir;
-                of = (last == of) ? NULL : of->of_d_next;
-            }
+    if (stat(cfrombstring(fullpath), &st) != 0) { /* 5a */
+        switch (errno) {
+        case ENOENT:
+            afp_errno = AFPERR_NOOBJ;
+            err = 1;
+            goto exit;
+        case EPERM:
+            afp_errno = AFPERR_ACCESS;
+            err = 1;
+            goto exit;
+        default:
+            afp_errno = AFPERR_MISC;
+            err = 1;
+            goto exit;
+        }
+    } else {
+        if ( ! S_ISDIR(st.st_mode)) { /* 5b */
+            afp_errno = AFPERR_BADTYPE;
+            err = 1;
+            goto exit;
         }
-
-        /* set the node's d_name */
-        node->d_m_name = save.d_m_name;
-        node->d_u_name = save.d_u_name;
-        node->d_m_name_ucs2 = save.d_m_name_ucs2;
     }
 
-    if (node->d_color == DIRTREE_COLOR_BLACK)
-        dir_rmrecolor(vol, leaf);
+    /* Get macname from unix name */
+    if ( (mpath = utompath(vol, upath, did, utf8)) == NULL ) {
+        afp_errno = AFPERR_NOOBJ;
+        err = 1;
+        goto exit;
+    }
 
-    if (node->d_m_name_ucs2)
-        free(node->d_u_name_ucs2);
-    if (node->d_u_name != node->d_m_name) {
-        free(node->d_u_name);
+    /* Create struct dir */
+    if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath)) == NULL) { /* 6 */
+        LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
+        err = 1;
+        goto exit;
     }
-    free(node->d_m_name);
-    free(node);
-#endif /* ! REMOVE_NODES */
-}
 
-/* ---------------------------------------
- * remove the node and its childs from the tree
- *
- * FIXME what about opened forks with refs to it?
- * it's an afp specs violation because you can't delete
- * an opened forks. Now afpd doesn't care about forks opened by other
- * process. It's fixable within afpd if fnctl_lock, doable with smb and
- * next to impossible for nfs and local filesystem access.
- */
-static void dir_invalidate( struct vol *vol, struct dir *dir)
-{
-    if (curdir == dir) {
-        /* v_root can't be deleted */
-        if (movecwd(vol, vol->v_root) < 0) {
-            LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
-        }
+    /* Add it to the cache only if it's a dir */
+    if (dircache_add(ret) != 0) { /* 7 */
+        err = 1;
+        goto exit;
     }
-    /* FIXME */
-    dirchildremove(dir->d_parent, dir);
-    dir_remove( vol, dir );
-}
 
-/* ------------------------------------ */
-static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
-{
-    struct dir  *pdir;
-
-    pdir = vol_tree_root(vol, dir->d_did);
-    while (pdir->d_did != dir->d_did ) {
-        if ( pdir->d_did > dir->d_did ) {
-            if ( pdir->d_left == SENTINEL ) {
-                pdir->d_left = dir;
-                dir->d_back = pdir;
-                return NULL;
-            }
-            pdir = pdir->d_left;
-        } else {
-            if ( pdir->d_right == SENTINEL ) {
-                pdir->d_right = dir;
-                dir->d_back = pdir;
-                return NULL;
-            }
-            pdir = pdir->d_right;
+    LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {end: did:%u, path:'%s'}",
+        ntohl(did), ntohl(pdid), cfrombstring(ret->d_fullpath));
+
+exit:
+    if (err) {
+        LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}",
+            ntohl(did), AfpErr2name(afp_errno));
+        free(upath);
+        if (fullpath)
+            bdestroy(fullpath);
+        if (ret) {
+            dir_free(ret);
+            ret = NULL;
         }
     }
-    return pdir;
+    return ret;
 }
 
 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
 
-int
-caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
+int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
 {
     DIR               *dp;
     struct dirent     *de;
@@ -712,635 +670,358 @@ caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
 }
 
 
-/*
- * attempt to extend the current dir. tree to include path
- * as a side-effect, movecwd to that point and return the new dir
+/*!
+ * @brief Construct struct dir
+ *
+ * Construct struct dir from parameters.
+ *
+ * @param m_name   (r) directory name in UTF8-dec
+ * @param u_name   (r) directory name in server side encoding
+ * @param vol      (r) pointer to struct vol
+ * @param pdid     (r) Parent CNID
+ * @param did      (r) CNID
+ * @param fullpath (r) Full unix path to dir or NULL for files
+ *
+ * @returns pointer to new struct dir or NULL on error
+ *
+ * @note Most of the time mac name and unix name are the same.
  */
-static struct dir *
-extenddir(struct vol *vol, struct dir *dir, struct path *path)
+struct dir *dir_new(const char *m_name,
+                    const char *u_name,
+                    const struct vol *vol,
+                    cnid_t pdid,
+                    cnid_t did,
+                    bstring path)
 {
-    path->d_dir = NULL;
+    struct dir *dir;
 
-    if ( path->u_name == NULL) {
-        afp_errno = AFPERR_PARAM;
+    dir = (struct dir *) calloc(1, sizeof( struct dir ));
+    if (!dir)
         return NULL;
-    }
 
-    if (check_name(vol, path->u_name)) {
-        /* the name is illegal */
-        LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
-        path->u_name = NULL;
-        afp_errno = AFPERR_PARAM;
+    if ((dir->d_m_name = bfromcstr(m_name)) == NULL) {
+        free(dir);
         return NULL;
     }
 
-    if (of_stat( path ) != 0 ) {
-        if (!(vol->v_flags & AFPVOL_CASEINSEN))
-            return NULL;
-        else if(caseenumerate(vol, path, dir) != 0)
-            return(NULL);
-    }
-
-    if (!S_ISDIR(path->st.st_mode)) {
-        return( NULL );
-    }
-
-    /* mac name is always with the right encoding (from cname()) */
-    if (( dir = adddir( vol, dir, path)) == NULL ) {
-        return( NULL );
-    }
-
-    path->d_dir = dir;
-    if ( movecwd( vol, dir ) < 0 ) {
-        return( NULL );
-    }
-
-    return( dir );
-}
-
-/* -------------------------
-   appledouble mkdir afp error code.
-*/
-static int netatalk_mkdir(const struct vol *vol, const char *name)
-{
-    int ret;
-    struct stat st;
-
-    if (vol->v_flags & AFPVOL_UNIX_PRIV) {
-        if (lstat(".", &st) < 0)
-            return AFPERR_MISC;
-        int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
-        LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
-            name, st.st_mode, vol->v_umask);
-
-        ret = mkdir(name, mode);
-    } else {
-        ret = ad_mkdir(name, DIRBITS | 0777);
-    }
-
-    if (ret < 0) {
-        switch ( errno ) {
-        case ENOENT :
-            return( AFPERR_NOOBJ );
-        case EROFS :
-            return( AFPERR_VLOCK );
-        case EPERM:
-        case EACCES :
-            return( AFPERR_ACCESS );
-        case EEXIST :
-            return( AFPERR_EXIST );
-        case ENOSPC :
-        case EDQUOT :
-            return( AFPERR_DFULL );
-        default :
-            return( AFPERR_PARAM );
-        }
-    }
-    return AFP_OK;
-}
-
-/* ------------------- */
-static int deletedir(int dirfd, char *dir)
-{
-    char path[MAXPATHLEN + 1];
-    DIR *dp;
-    struct dirent   *de;
-    struct stat st;
-    size_t len;
-    int err = AFP_OK;
-    size_t remain;
-
-    if ((len = strlen(dir)) +2 > sizeof(path))
-        return AFPERR_PARAM;
-
-    /* already gone */
-    if ((dp = opendirat(dirfd, dir)) == NULL)
-        return AFP_OK;
-
-    strcpy(path, dir);
-    strcat(path, "/");
-    len++;
-    remain = sizeof(path) -len -1;
-    while ((de = readdir(dp)) && err == AFP_OK) {
-        /* skip this and previous directory */
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-            continue;
-
-        if (strlen(de->d_name) > remain) {
-            err = AFPERR_PARAM;
-            break;
-        }
-        strcpy(path + len, de->d_name);
-        if (lstatat(dirfd, path, &st)) {
-            continue;
-        }
-        if (S_ISDIR(st.st_mode)) {
-            err = deletedir(dirfd, path);
-        } else {
-            err = netatalk_unlinkat(dirfd, path);
-        }
-    }
-    closedir(dp);
-
-    /* okay. the directory is empty. delete it. note: we already got rid
-       of .AppleDouble.  */
-    if (err == AFP_OK) {
-        err = netatalk_rmdir(dirfd, dir);
-    }
-    return err;
-}
-
-/* do a recursive copy. */
-static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
-{
-    char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
-    DIR *dp;
-    struct dirent   *de;
-    struct stat st;
-    struct utimbuf      ut;
-    size_t slen, dlen;
-    size_t srem, drem;
-    int err;
-
-    /* doesn't exist or the path is too long. */
-    if (((slen = strlen(src)) > sizeof(spath) - 2) ||
-        ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
-        ((dp = opendirat(dirfd, src)) == NULL))
-        return AFPERR_PARAM;
-
-    /* try to create the destination directory */
-    if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
-        closedir(dp);
-        return err;
+    if (convert_string_allocate( (utf8_encoding()) ? CH_UTF8_MAC : vol->v_maccharset,
+                                 CH_UCS2,
+                                 m_name,
+                                 -1, (char **)&dir->d_m_name_ucs2) == (size_t)-1 ) {
+        LOG(log_error, logtype_afpd, "dir_new(did: %u) {%s, %s}: couldn't set UCS2 name", ntohl(did), m_name, u_name);
+        dir->d_m_name_ucs2 = NULL;
     }
 
-    /* set things up to copy */
-    strcpy(spath, src);
-    strcat(spath, "/");
-    slen++;
-    srem = sizeof(spath) - slen -1;
-
-    strcpy(dpath, dst);
-    strcat(dpath, "/");
-    dlen++;
-    drem = sizeof(dpath) - dlen -1;
-
-    err = AFP_OK;
-    while ((de = readdir(dp))) {
-        /* skip this and previous directory */
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-            continue;
-
-        if (strlen(de->d_name) > srem) {
-            err = AFPERR_PARAM;
-            break;
-        }
-        strcpy(spath + slen, de->d_name);
-
-        if (lstatat(dirfd, spath, &st) == 0) {
-            if (strlen(de->d_name) > drem) {
-                err = AFPERR_PARAM;
-                break;
-            }
-            strcpy(dpath + dlen, de->d_name);
-
-            if (S_ISDIR(st.st_mode)) {
-                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
-                    goto copydir_done;
-            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
-                goto copydir_done;
-
-            } else {
-                /* keep the same time stamp. */
-                ut.actime = ut.modtime = st.st_mtime;
-                utime(dpath, &ut);
-            }
-        }
+    if (m_name == u_name || !strcmp(m_name, u_name)) {
+        dir->d_u_name = dir->d_m_name;
     }
-
-    /* keep the same time stamp. */
-    if (lstatat(dirfd, src, &st) == 0) {
-        ut.actime = ut.modtime = st.st_mtime;
-        utime(dst, &ut);
+    else if ((dir->d_u_name = bfromcstr(u_name)) == NULL) {
+        bdestroy(dir->d_m_name);
+        free(dir);
+        return NULL;
     }
 
-copydir_done:
-    closedir(dp);
-    return err;
+    dir->d_did = did;
+    dir->d_pdid = pdid;
+    dir->d_vid = vol->v_vid;
+    dir->d_fullpath = path;
+    return dir;
 }
 
-
-/* --- public functions follow --- */
-
-/* NOTE: we start off with at least one node (the root directory). */
-static struct dir *dirinsert(struct vol *vol, struct dir *dir)
+/*!
+ * @brief Free a struct dir and all its members
+ *
+ * @param (rw) pointer to struct dir
+ */
+void dir_free(struct dir *dir)
 {
-    struct dir *node;
-
-    if ((node = dir_insert(vol, dir)))
-        return node;
-
-    /* recolor the tree. the current node is red. */
-    dir->d_color = DIRTREE_COLOR_RED;
-
-    /* parent of this node has to be black. if the parent node
-     * is red, then we have a grandparent. */
-    while ((dir != vol->v_root) &&
-           (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
-        /* are we on the left tree? */
-        if (dir->d_back == dir->d_back->d_back->d_left) {
-            node = dir->d_back->d_back->d_right;  /* get the right node */
-            if (node->d_color == DIRTREE_COLOR_RED) {
-                /* we're red. we need to change to black. */
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                node->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir = dir->d_back->d_back; /* finished. go up. */
-            } else {
-                if (dir == dir->d_back->d_right) {
-                    dir = dir->d_back;
-                    dir_leftrotate(vol, dir);
-                }
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_rightrotate(vol, dir->d_back->d_back);
-            }
-        } else {
-            node = dir->d_back->d_back->d_left;
-            if (node->d_color == DIRTREE_COLOR_RED) {
-                /* we're red. we need to change to black. */
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                node->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir = dir->d_back->d_back; /* finished. ascend */
-            } else {
-                if (dir == dir->d_back->d_left) {
-                    dir = dir->d_back;
-                    dir_rightrotate(vol, dir);
-                }
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_leftrotate(vol, dir->d_back->d_back);
-            }
-        }
+    if (dir->d_u_name != dir->d_m_name) {
+        bdestroy(dir->d_u_name);
     }
-
-    vol->v_root->d_color = DIRTREE_COLOR_BLACK;
-    return NULL;
+    if (dir->d_m_name_ucs2)
+        free(dir->d_m_name_ucs2);
+    bdestroy(dir->d_m_name);
+    bdestroy(dir->d_fullpath);
+    free(dir);
 }
 
-/* ---------------------------- */
-struct dir *
-adddir(struct vol *vol, struct dir *dir, struct path *path)
+/*!
+ * @brief Create struct dir from struct path
+ *
+ * Create a new struct dir from struct path. Then add it to the cache.
+ * The caller must have assured that the dir is not already in the cache,
+ * cf theAFP_ASSERTion.
+ * 1. Open adouble file, get CNID from it.
+ * 2. Search the database, hinting with the CNID from (1).
+ * 3. Build fullpath and create struct dir.
+ * 4. Add it to the cache.
+ *
+ * @param vol   (r) pointer to struct vol
+ * @param dir   (r) pointer to parrent directory
+ * @param path  (rw) pointer to struct path with valid path->u_name
+ * @param len   (r) strlen of path->u_name
+ *
+ * @returns Pointer to new struct dir or NULL on error.
+ *
+ * @note Function also assigns path->m_name from path->u_name.
+ */
+struct dir *dir_add(const struct vol *vol, const struct dir *dir, struct path *path, int len)
 {
-    struct dir  *cdir, *edir;
-    int     upathlen;
-    char        *name;
-    char        *upath;
-    struct stat *st;
-    int         deleted;
+    int err = 0;
+    struct dir  *cdir = NULL;
+    cnid_t      id;
     struct adouble  ad;
     struct adouble *adp = NULL;
-    cnid_t      id;
-
-    upath = path->u_name;
-    st    = &path->st;
-    upathlen = strlen(upath);
+    bstring fullpath;
+
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
+    AFP_ASSERT(path);
+    AFP_ASSERT(len > 0);
+
+    if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name))) != NULL) {
+        /* there's a stray entry in the dircache */
+        LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}",
+            ntohl(dir->d_did), cfrombstring(dir->d_fullpath), path->u_name,
+            ntohl(cdir->d_did), cfrombstring(dir->d_fullpath));
+        if (dir_remove(vol, cdir) != 0) {
+            dircache_dump();
+            AFP_PANIC("dir_add");
+        }
+    }
 
     /* get_id needs adp for reading CNID from adouble file */
     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
-    if ((ad_open_metadata(upath, ADFLAGS_DIR, 0, &ad)) == 0)
+    if ((ad_open_metadata(path->u_name, ADFLAGS_DIR, 0, &ad)) == 0) /* 1 */
         adp = &ad;
 
-    id = get_id(vol, adp, st, dir->d_did, upath, upathlen);
+    /* Get CNID */
+    if ((id = get_id(vol, adp, &path->st, dir->d_did, path->u_name, len)) == 0) { /* 2 */
+        err = 1;
+        goto exit;
+    }
 
     if (adp)
         ad_close_metadata(adp);
 
-    if (id == 0) {
-        return NULL;
-    }
-    if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
-        return NULL;
-    }
-    name  = path->m_name;
-    if ((cdir = dirnew(name, upath)) == NULL) {
-        LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
-        return NULL;
-    }
-    if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, -1, (char **)&cdir->d_m_name_ucs2)) {
-        LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
-        cdir->d_m_name_ucs2 = NULL;
-    }
-
-    cdir->d_did = id;
-
-    if ((edir = dirinsert( vol, cdir ))) {
-        /* it's not possible with LASTDID
-           for CNID:
-           - someone else have moved the directory.
-           - it's a symlink inside the share.
-           - it's an ID reused, the old directory was deleted but not
-           the cnid record and the server've reused the inode for
-           the new dir.
-           for HASH (we should get ride of HASH)
-           - someone else have moved the directory.
-           - it's an ID reused as above
-           - it's a hash duplicate and we are in big trouble
-        */
-        deleted = (edir->d_m_name == NULL);
-        if (!deleted)
-            dir_hash_del(vol, edir);
-        dirfreename(edir);
-        edir->d_m_name = cdir->d_m_name;
-        edir->d_u_name = cdir->d_u_name;
-        edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
-        free(cdir);
-        cdir = edir;
-        LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
-        if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
-            hash_alloc_insert(vol->v_hash, cdir, cdir);
-            return cdir;
+    /* Get macname from unixname */
+    if (path->m_name == NULL) {
+        if ((path->m_name = utompath(vol, path->u_name, id, utf8_encoding())) == NULL) {
+            err = 2;
+            goto exit;
         }
-        /* the old was not in the same folder */
-        if (!deleted)
-            dirchildremove(cdir->d_parent, cdir);
     }
 
-    /* parent/child directories */
-    cdir->d_parent = dir;
-    dirchildadd(vol, dir, cdir);
-    return( cdir );
-}
-
-/* --- public functions follow --- */
-/* free everything down. we don't bother to recolor as this is only
- * called to free the entire tree */
-void dirfreename(struct dir *dir)
-{
-    if (dir->d_u_name != dir->d_m_name) {
-        free(dir->d_u_name);
+    /* Build fullpath */
+    if ( ((fullpath = bstrcpy(dir->d_fullpath)) == NULL) /* 3 */
+         || (bconchar(fullpath, '/') != BSTR_OK)
+         || (bcatcstr(fullpath, path->u_name)) != BSTR_OK) {
+        LOG(log_error, logtype_afpd, "dir_add: fullpath: %s", strerror(errno) );
+        err = 3;
+        goto exit;
     }
-    if (dir->d_m_name_ucs2)
-        free(dir->d_m_name_ucs2);
-    free(dir->d_m_name);
-}
-
-void dirfree(struct dir *dir)
-{
-    if (!dir || (dir == SENTINEL))
-        return;
 
-    if ( dir->d_left != SENTINEL ) {
-        dirfree( dir->d_left );
+    /* Allocate and initialize struct dir */
+    if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath)) == NULL) { /* 3 */
+        err = 4;
+        goto exit;
     }
-    if ( dir->d_right != SENTINEL ) {
-        dirfree( dir->d_right );
+
+    if ((dircache_add(cdir)) != 0) { /* 4 */
+        LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstring(fullpath));
+        exit(EXITERR_SYS);
     }
 
-    if (dir != SENTINEL) {
-        dirfreename(dir);
-        free( dir );
+exit:
+    if (err != 0) {
+        LOG(log_debug, logtype_afpd, "dir_add('%s/%s'): error: %u",
+            cfrombstring(dir->d_u_name), path->u_name, err);
+
+        if (adp)
+            ad_close_metadata(adp);
+        if (!cdir && fullpath)
+            bdestroy(fullpath);
+        if (cdir)
+            dir_free(cdir);
+        cdir = NULL;
+    } else {
+        /* no error */
+        LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {cached: %u,'%s'}",
+            ntohl(dir->d_did), cfrombstring(dir->d_fullpath), path->u_name,
+            ntohl(cdir->d_did), cfrombstring(cdir->d_fullpath));
     }
+
+    return(cdir);
 }
 
-/* --------------------------------------------
- * most of the time mac name and unix name are the same
+/*!
+ * @brief Remove a dir from a cache and free it and any ressources with it
+ *
+ * 1. Check if the dir is locked or has opened forks
+ * 2. If it's a request to remove curdir, just chdir to volume root
+ * 3. Remove it from the cache
+ * 4. Remove the dir plus any allocated resources it references
+ *
+ * @param (r) pointer to struct vol
+ * @param (rw) pointer to struct dir
  */
-struct dir *dirnew(const char *m_name, const char *u_name)
+int dir_remove(const struct vol *vol, struct dir *dir)
 {
-    struct dir *dir;
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
 
-    dir = (struct dir *) calloc(1, sizeof( struct dir ));
-    if (!dir)
-        return NULL;
+    if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+        return 0;
 
-    if ((dir->d_m_name = strdup(m_name)) == NULL) {
-        free(dir);
-        return NULL;
+    if (dir->d_flags & DIRF_CACHELOCK || dir->d_ofork) { /* 1 */
+        LOG(log_warning, logtype_afpd, "dir_remove(did:%u,'%s'): dir is locked or has opened forks",
+            ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
+        return 0;
     }
 
-    if (m_name == u_name || !strcmp(m_name, u_name)) {
-        dir->d_u_name = dir->d_m_name;
-    }
-    else if ((dir->d_u_name = strdup(u_name)) == NULL) {
-        free(dir->d_m_name);
-        free(dir);
-        return NULL;
+    if (curdir == dir) {        /* 2 */
+        if (movecwd(vol, vol->v_root) < 0) {
+            LOG(log_error, logtype_afpd, "dir_remove: can't chdir to : %s", vol->v_root);
+        }
     }
 
-    dir->d_m_name_ucs2 = NULL;
-    dir->d_left = dir->d_right = SENTINEL;
-    dir->d_next = dir->d_prev = dir;
-    return dir;
-}
-
-#if 0
-/* ------------------ */
-static hash_val_t hash_fun_dir(const void *key)
-{
-    const struct dir *k = key;
-
-    static unsigned long randbox[] = {
-        0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
-        0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
-        0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
-        0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
-    };
-
-    const unsigned char *str = (unsigned char *)(k->d_u_name);
-    hash_val_t acc = k->d_parent->d_did;
-
-    while (*str) {
-        acc ^= randbox[(*str + acc) & 0xf];
-        acc = (acc << 1) | (acc >> 31);
-        acc &= 0xffffffffU;
-        acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
-        acc = (acc << 2) | (acc >> 30);
-        acc &= 0xffffffffU;
-    }
-    return acc;
-}
-#endif
+    LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}",
+        ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
 
-#undef get16bits
-#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)    \
-    || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
-#define get16bits(d) (*((const uint16_t *) (d)))
-#endif
+    dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 3 */
+    dir_free(dir);              /* 4 */
 
-#if !defined (get16bits)
-#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)    \
-                      +(uint32_t)(((const uint8_t *)(d))[0]) )
-#endif
+    return 0;
+}
 
-static hash_val_t hash_fun2_dir(const void *key)
+/*!
+ * @brief Modify a struct dir, adjust cache
+ *
+ * Any value that is 0 or NULL is not changed. If new_uname is NULL it is set to new_mname.
+ * If given new_uname == new_mname, new_uname will point to new_mname.
+ *
+ * @param vol       (r) pointer to struct vol
+ * @param dir       (rw) pointer to struct dir
+ * @param pdid      (r) new parent DID
+ * @param did       (r) new DID
+ * @param new_mname (r) new mac-name
+ * @param new_uname (r) new unix-name
+ */
+int dir_modify(const struct vol *vol,
+               struct dir *dir,
+               cnid_t pdid,
+               cnid_t did,
+               const char *new_mname,
+               const char *new_uname,
+               bstring pdir_fullpath)
 {
-    const struct dir *k = key;
-    const char *data = k->d_u_name;
-    int len = strlen(k->d_u_name);
-    hash_val_t hash = k->d_parent->d_did, tmp;
-
-    int rem = len & 3;
-    len >>= 2;
-
-    /* Main loop */
-    for (;len > 0; len--) {
-        hash  += get16bits (data);
-        tmp    = (get16bits (data+2) << 11) ^ hash;
-        hash   = (hash << 16) ^ tmp;
-        data  += 2*sizeof (uint16_t);
-        hash  += hash >> 11;
-    }
-
-    /* Handle end cases */
-    switch (rem) {
-    case 3: hash += get16bits (data);
-        hash ^= hash << 16;
-        hash ^= data[sizeof (uint16_t)] << 18;
-        hash += hash >> 11;
-        break;
-    case 2: hash += get16bits (data);
-        hash ^= hash << 11;
-        hash += hash >> 17;
-        break;
-    case 1: hash += *data;
-        hash ^= hash << 10;
-        hash += hash >> 1;
-    }
+    int ret = 0;
 
-    /* Force "avalanching" of final 127 bits */
-    hash ^= hash << 3;
-    hash += hash >> 5;
-    hash ^= hash << 4;
-    hash += hash >> 17;
-    hash ^= hash << 25;
-    hash += hash >> 6;
-
-    return hash;
-}
+    /* Remove it from the cache */
+    dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
 
-/* ---------------- */
-static int hash_comp_dir(const void *key1, const void *key2)
-{
-    const struct dir *k1 = key1;
-    const struct dir *k2 = key2;
+    if (pdid)
+        dir->d_pdid = pdid;
+    if (did)
+        dir->d_did = did;
 
-    return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
-}
+    if (new_mname) {
+        /* free uname if it's not the same as mname */
+        if (dir->d_m_name != dir->d_u_name)
+            bdestroy(dir->d_u_name);
 
-/* ---------------- */
-hash_t *
-dirhash(void)
-{
-    return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir);
-}
+        if (new_uname == NULL)
+            new_uname = new_mname;
 
-/* ------------------ */
-static struct path *invalidate (struct vol *vol, struct dir *dir, struct path *ret)
-{
-    /* it's tricky:
-       movecwd failed some of dir path are not there anymore.
-       FIXME Is it true with other errors?
-       so we remove dir from the cache
-    */
-    if (dir->d_did == DIRDID_ROOT_PARENT)
-        return NULL;
-    if (afp_errno == AFPERR_ACCESS) {
-        if ( movecwd( vol, dir->d_parent ) < 0 ) {
-            return NULL;
-        }
-        /* FIXME should we set these?, don't need to call stat() after:
-           ret->st_valid = 1;
-           ret->st_errno = EACCES;
-        */
-        ret->m_name = dir->d_m_name;
-        ret->u_name = dir->d_u_name;
-        ret->d_dir = dir;
-        return ret;
-    } else if (afp_errno == AFPERR_NOOBJ) {
-        if ( movecwd( vol, dir->d_parent ) < 0 ) {
-            return NULL;
-        }
-        strcpy(ret->m_name, dir->d_m_name);
-        if (dir->d_m_name == dir->d_u_name) {
-            ret->u_name = ret->m_name;
+        /* assign new name */
+        if ((bassigncstr(dir->d_m_name, new_mname)) != BSTR_OK) {
+            LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) );
+            return -1;
         }
-        else {
-            size_t tp = strlen(ret->m_name)+1;
 
-            ret->u_name =  ret->m_name +tp;
-            strcpy(ret->u_name, dir->d_u_name);
+        if (new_mname == new_uname || (strcmp(new_mname, new_uname) == 0)) {
+            dir->d_u_name = dir->d_m_name;
+        } else {
+            if ((dir->d_u_name = bfromcstr(new_uname)) == NULL) {
+                LOG(log_error, logtype_afpd, "renamedir: bassigncstr: %s", strerror(errno) );
+                return -1;
+            }
         }
-        /* FIXME should we set :
-           ret->st_valid = 1;
-           ret->st_errno = ENOENT;
-        */
-        dir_invalidate(vol, dir);
-        return ret;
     }
-    dir_invalidate(vol, dir);
-    return NULL;
-}
 
-/* -------------------------------------------------- */
-/* cname
-   return
-   if it's a filename:
-   in extenddir:
-   compute unix name
-   stat the file or errno
-   return
-   filename
-   curdir: filename parent directory
-
-   if it's a dirname:
-   not in the cache
-   in extenddir
-   compute unix name
-   stat the dir or errno
-   return
-   if chdir error
-   dirname
-   curdir: dir parent directory
-   sinon
-   dirname: ""
-   curdir: dir
-   in the cache
-   return
-   if chdir error
-   dirname
-   curdir: dir parent directory
-   else
-   dirname: ""
-   curdir: dir
+    if (pdir_fullpath) {
+        if (bassign(dir->d_fullpath, pdir_fullpath) != BSTR_OK)
+            return -1;
+        if (bcatcstr(dir->d_fullpath, "/") != BSTR_OK)
+            return -1;
+        if (bcatcstr(dir->d_fullpath, new_uname) != BSTR_OK)
+            return -1;
+    }
+
+    if (dir->d_m_name_ucs2)
+        free(dir->d_m_name_ucs2);
+    if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2))
+        dir->d_m_name_ucs2 = NULL;
 
-*/
-struct path *
-cname(struct vol *vol, struct dir *dir, char **cpath)
+    /* Re-add it to the cache */
+    if ((dircache_add(dir)) != 0) {
+        dircache_dump();
+        AFP_PANIC("dir_modify");
+    }
+
+    return ret;
+}
+
+/*!
+ * @brief Resolve a catalog node name path
+ *
+ * 1. Evaluate path type
+ * 2. Move to start dir, if we cant, it might eg because of EACCES, build
+ *    path from dirname, so eg getdirparams has sth it can chew on. curdir
+ *    is dir parent then. All this is done in path_from_dir().
+ * 3. Parse next cnode name in path, cases:
+ * 4.   single "\0" -> do nothing
+ * 5.   two or more consecutive "\0" -> chdir("..") one or more times
+ * 6.   cnode name -> copy it to path.m_name
+ * 7. Get unix name from mac name
+ * 8. Special handling of request with did 1
+ * 9. stat the cnode name
+ * 10. If it's not there, it's probably an afp_createfile|dir,
+ *     return with curdir = dir parent, struct path = dirname
+ * 11. If it's there and it's a file, it must should be the last element of the requested
+ *     path. Return with curdir = cnode name parent dir, struct path = filename
+ * 12. Treat symlinks like files, dont follow them
+ * 13. If it's a dir:
+ * 14. Search the dircache for it
+ * 15. If it's not in the cache, create a struct dir for it and add it to the cache
+ * 16. chdir into the dir and
+ * 17. set m_name to the mac equivalent of "."
+ * 18. goto 3
+ */
+struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
 {
-    struct dir         *cdir, *scdir=NULL;
     static char        path[ MAXPATHLEN + 1];
     static struct path ret;
 
+    struct dir  *cdir;
     char        *data, *p;
-    int         extend = 0;
     int         len;
     u_int32_t   hint;
     u_int16_t   len16;
     int         size = 0;
-    char        sep;
     int         toUTF8 = 0;
 
+    LOG(log_maxdebug, logtype_afpd, "came('%s'): {start}", cfrombstring(dir->d_fullpath));
+
     data = *cpath;
     afp_errno = AFPERR_NOOBJ;
     memset(&ret, 0, sizeof(ret));
-    switch (ret.m_type = *data) { /* path type */
+
+    switch (ret.m_type = *data) { /* 1 */
     case 2:
         data++;
         len = (unsigned char) *data++;
         size = 2;
-        sep = 0;
         if (afp_version >= 30) {
             ret.m_type = 3;
             toUTF8 = 1;
@@ -1357,7 +1038,6 @@ cname(struct vol *vol, struct dir *dir, char **cpath)
             len = ntohs(len16);
             data += 2;
             size = 7;
-            sep = 0; /* '/';*/
             break;
         }
         /* else it's an error */
@@ -1366,248 +1046,212 @@ cname(struct vol *vol, struct dir *dir, char **cpath)
         return( NULL );
     }
     *cpath += len + size;
-    *path = '\0';
+
+    path[0] = 0;
     ret.m_name = path;
-    for ( ;; ) {
-        if ( len == 0 ) {
-            if (movecwd( vol, dir ) < 0 ) {
-                return invalidate(vol, dir, &ret );
-            }
-            if (*path == '\0') {
-                ret.u_name = ".";
-                ret.d_dir = dir;
-            }
-            return &ret;
-        }
 
-        if (*data == sep ) {
+    if (movecwd(vol, dir) < 0 ) {
+        LOG(log_debug, logtype_afpd, "cname(did:%u): failed to chdir to '%s'",
+            ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
+        if (len == 0)
+            return path_from_dir(vol, dir, &ret);
+        else
+            return NULL;
+    }
+
+    while (len) {         /* 3 */
+        if (*data == 0) { /* 4 or 5 */
             data++;
             len--;
-        }
-        while (*data == sep && len > 0 ) {
-            if ( dir->d_parent == NULL ) {
-                return NULL;
+            while (len > 0 && *data == 0) { /* 5 */
+                /* chdir to parrent dir */
+                if ((dir = dirlookup(vol, dir->d_pdid)) == NULL)
+                    return NULL;
+                if (movecwd( vol, dir ) < 0 ) {
+                    dir_remove(vol, dir);
+                    return NULL;
+                }
+                data++;
+                len--;
             }
-            dir = dir->d_parent;
-            data++;
-            len--;
+            continue;
         }
 
-        /* would this be faster with strlen + strncpy? */
-        p = path;
-        while ( *data != sep && len > 0 ) {
+        /* 6*/
+        for ( p = path; *data != 0 && len > 0; len-- ) {
             *p++ = *data++;
             if (p > &path[ MAXPATHLEN]) {
                 afp_errno = AFPERR_PARAM;
-                return( NULL );
+                return NULL;
             }
-            len--;
         }
+        *p = 0;            /* Terminate string */
+        ret.u_name = NULL;
 
-        /* short cut bits by chopping off a trailing \0. this also
-           makes the traversal happy w/ filenames at the end of the
-           cname. */
-        if (len == 1)
-            len--;
-
-        *p = '\0';
-
-        if ( p == path ) { /* end of the name parameter */
-            continue;
+        if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { /* 7 */
+            LOG(log_error, logtype_afpd, "cname('%s'): error from cname_mtouname", path);
+            return NULL;
         }
-        ret.u_name = NULL;
-        if (afp_version >= 30) {
-            char *t;
-            cnid_t fileid;
 
-            if (toUTF8) {
-                static char temp[ MAXPATHLEN + 1];
+        LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstring(dir->d_fullpath), ret.u_name);
 
-                if (dir->d_did == DIRDID_ROOT_PARENT) {
-                    /*
-                      With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
-                      So we compare it with the longname from the current volume and if they match
-                      we overwrite the requested path with the utf8 volume name so that the following
-                      strcmp can match.
-                    */
-                    ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
-                    if (strcasecmp( path, temp) == 0)
-                        ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
-                } else {
-                    /* toUTF8 */
-                    if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
-                        afp_errno = AFPERR_PARAM;
-                        return( NULL );
-                    }
-                    strcpy(path, temp);
-                }
-            }
-            /* check for OS X mangled filename :( */
+        /* Prevent access to our special folders like .AppleDouble */
+        if (check_name(vol, ret.u_name)) {
+            /* the name is illegal */
+            LOG(log_info, logtype_afpd, "cname: illegal path: '%s'", ret.u_name);
+            afp_errno = AFPERR_PARAM;
+            return NULL;
+        }
 
-            t = demangle_osx(vol, path, dir->d_did, &fileid);
-            if (t != path) {
-                ret.u_name = t;
-                /* duplicate work but we can't reuse all convert_char we did in demangle_osx
-                 * flags weren't the same
+        if (dir->d_did == DIRDID_ROOT_PARENT) { /* 8 */
+            /*
+             * Special case: CNID 1
+             * root parent (did 1) has one child: the volume. Requests for did=1 with
+             * some <name> must check against the volume name.
+             */
+            if ((strcmp(cfrombstring(vol->v_root->d_m_name), ret.m_name)) == 0)
+                cdir = vol->v_root;
+            else
+                return NULL;
+        } else {
+            /*
+             * CNID != 1, eg. most of the times we take this way.
+             * Now check if current path-part is a file or dir:
+             * o if it's dir we have to step into it
+             * o if it's a file we expect it to be the last part of the requested path
+             *   and thus call continue which should terminate the while loop because
+             *   len = 0. Ok?
+             */
+            if (of_stat(&ret) != 0) { /* 9 */
+                /*
+                 * ret.u_name doesn't exist, might be afp_createfile|dir
+                 * that means it should have been the last part
                  */
-                if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
-                    /* at last got our view of mac name */
-                    strcpy(path,t);
+                if (len > 0) {
+                    /* it wasn't the last part, so we have a bogus path request */
+                    afp_errno = AFPERR_NOOBJ;
+                    return NULL;
                 }
+                /*
+                 * this will terminate clean in while (1) because len == 0,
+                 * probably afp_createfile|dir
+                 */
+                LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}", cfrombstring(dir->d_fullpath), ret.u_name);
+                continue; /* 10 */
             }
-        }
-        if (ret.u_name == NULL) {
-            if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
-                afp_errno = AFPERR_PARAM;
-                return NULL;
-            }
-        }
-        if ( !extend ) {
-            ucs2_t *tmpname;
-            cdir = dir->d_child;
-            scdir = NULL;
-            if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
-                 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
-                                                       CH_UCS2, path, -1, (char **)&tmpname) )
-            {
-                while (cdir) {
-                    if (!cdir->d_m_name_ucs2) {
-                        LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
-                        /* this shouldn't happen !!!! */
-                        goto noucsfallback;
-                    }
 
-                    if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
-                        break;
-                    }
-                    if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
-                        scdir = cdir;
-                    }
-                    cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
-                }
-                free(tmpname);
-            }
-            else {
-            noucsfallback:
-                if (dir->d_did == DIRDID_ROOT_PARENT) {
-                    /*
-                      root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
-                      must check against the volume name.
-                    */
-                    if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
-                        cdir = vol->v_dir;
-                    else
-                        cdir = NULL;
+            switch (ret.st.st_mode & S_IFMT) {
+            case S_IFREG: /* 11 */
+                LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}", cfrombstring(dir->d_fullpath), ret.u_name);
+                if (len > 0) {
+                    /* it wasn't the last part, so we have a bogus path request */
+                    afp_errno = AFPERR_PARAM;
+                    return NULL;
                 }
-                else {
-                    cdir = dirsearch_byname(vol, dir, ret.u_name);
+                continue; /* continues while loop */
+            case S_IFLNK: /* 12 */
+                LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}", cfrombstring(dir->d_fullpath), ret.u_name);
+                if (len > 0) {
+                    LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}", cfrombstring(dir->d_fullpath), ret.u_name);
+                    afp_errno = AFPERR_PARAM;
+                    return NULL;
                 }
+                continue; /* continues while loop */
+            case S_IFDIR: /* 13 */
+                break;
+            default:
+                LOG(log_info, logtype_afpd, "cname: special file: '%s'", ret.u_name);
+                afp_errno = AFPERR_NODIR;
+                return NULL;
             }
 
-            if (cdir == NULL && scdir != NULL) {
-                cdir = scdir;
-                /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
-            }
-
-            if ( cdir == NULL ) {
-                ++extend;
-                /* if dir == curdir it always succeed,
-                   even if curdir is deleted.
-                   it's not a pb because it will fail in extenddir
-                */
-                if ( movecwd( vol, dir ) < 0 ) {
-                    /* dir is not valid anymore
-                       we delete dir from the cache and abort.
-                    */
-                    if ( dir->d_did == DIRDID_ROOT_PARENT) {
-                        afp_errno = AFPERR_NOOBJ;
-                        return NULL;
-                    }
-                    if (afp_errno == AFPERR_ACCESS)
-                        return NULL;
-                    dir_invalidate(vol, dir);
+            /* Search the cache */
+            int unamelen = strlen(ret.u_name);
+            cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen); /* 14 */
+            if (cdir == NULL) {
+                /* Not in cache, create one */
+                if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */
+                    LOG(log_error, logtype_afpd, "cname(did:%u, name:'%s', cwd:'%s'): failed to add dir",
+                        ntohl(dir->d_did), ret.u_name, getcwdpath());
                     return NULL;
                 }
-                cdir = extenddir( vol, dir, &ret );
             }
+        } /* if/else cnid==1 */
+
+        /* Now chdir to the evaluated dir */
+        if (movecwd( vol, cdir ) < 0 ) { /* 16 */
+            LOG(log_debug, logtype_afpd, "cname(cwd:'%s'): failed to chdir to new subdir '%s': %s",
+                cfrombstring(curdir->d_fullpath), cfrombstring(cdir->d_fullpath), strerror(errno));
+            if (len == 0)
+                return path_from_dir(vol, cdir, &ret);
+            else
+                return NULL;
+        }
+        dir = cdir;
+        ret.m_name[0] = 0;      /* 17, so we later know last token was a dir */
+    } /* while (len) */
 
-        } else {
-            cdir = extenddir( vol, dir, &ret );
-        } /* if (!extend) */
+    if (curdir->d_did == DIRDID_ROOT_PARENT) {
+        afp_errno = AFPERR_DID1;
+        return NULL;
+    }
 
-        if ( cdir == NULL ) {
+    if (ret.m_name[0] == 0) {
+        /* Last part was a dir */
+        ret.u_name = mtoupath(vol, ret.m_name, 0, 1); /* Force "." into a useable static buffer */
+        ret.d_dir = dir;
+    }
 
-            if ( len > 0 || !ret.u_name ) {
-                return NULL;
-            }
+    LOG(log_debug, logtype_afpd, "came('%s') {end: curdir:'%s', path:'%s'}",
+        cfrombstring(dir->d_fullpath),
+        cfrombstring(curdir->d_fullpath),
+        ret.u_name);
 
-        } else {
-            dir = cdir;
-            *path = '\0';
-        }
-    } /* for (;;) */
+    return &ret;
 }
 
 /*
- * Move curdir to dir, with a possible chdir()
+ * @brief chdir() to dir
+ *
+ * @param vol   (r) pointer to struct vol
+ * @param dir   (r) pointer to struct dir
+ *
+ * @returns 0 on success, -1 on error with afp_errno set appropiately
  */
-int movecwd(struct vol *vol, struct dir *dir)
+int movecwd(const struct vol *vol, struct dir *dir)
 {
-    char path[MAXPATHLEN + 1];
-    struct dir  *d;
-    char    *p, *u;
-    int     n;
-    int     ret;
+    int ret;
+
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
+
+    LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')",
+        cfrombstring(curdir->d_fullpath), getcwdpath());
 
-    if ( dir == curdir ) {
+    if ( dir == curdir)
         return( 0 );
-    }
-    if ( dir->d_did == DIRDID_ROOT_PARENT) {
-        afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
-        return( -1 );
+    if (dir->d_did == DIRDID_ROOT_PARENT) {
+        curdir = &rootParent;
+        return 0;
     }
 
-    p = path + sizeof(path) - 1;
-    *p = '\0';
-    for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
-        u = d->d_u_name;
-        if (!u) {
-            /* parent directory is deleted */
-            afp_errno = AFPERR_NOOBJ;
-            return -1;
-        }
-        n = strlen( u );
-        if (p -n -1 < path) {
-            afp_errno = AFPERR_PARAM;
-            return -1;
-        }
-        *--p = '/';
-        p -= n;
-        memcpy( p, u, n );
-    }
-    if ( d != curdir ) {
-        n = strlen( vol->v_path );
-        if (p -n -1 < path) {
-            afp_errno = AFPERR_PARAM;
-            return -1;
-        }
-        *--p = '/';
-        p -= n;
-        memcpy( p, vol->v_path, n );
-    }
-    if ( (ret = lchdir(p )) != 0 ) {
-        LOG(log_debug, logtype_afpd, "movecwd('%s'): ret:%d, %u/%s", p, ret, errno, strerror(errno));
+    LOG(log_debug, logtype_afpd, "movecwd(did:%u, '%s')", ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
 
+    if ((ret = lchdir(cfrombstring(dir->d_fullpath))) != 0 ) {
+        LOG(log_debug, logtype_afpd, "movecwd('%s'): ret: %u, %s",
+            cfrombstring(dir->d_fullpath), ret, strerror(errno));
         if (ret == 1) {
             /* p is a symlink or getcwd failed */
             afp_errno = AFPERR_BADTYPE;
-            vol->v_curdir = curdir = vol->v_dir;
+
             if (chdir(vol->v_path ) < 0) {
-                LOG(log_debug, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
+                LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
                 /* XXX what do we do here? */
             }
+            curdir = vol->v_root;
             return -1;
         }
+
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1615,11 +1259,11 @@ int movecwd(struct vol *vol, struct dir *dir)
             break;
         default:
             afp_errno = AFPERR_NOOBJ;
-
         }
         return( -1 );
     }
-    vol->v_curdir = curdir = dir;
+
+    curdir = dir;
     return( 0 );
 }
 
@@ -1671,30 +1315,15 @@ void setdiroffcnt(struct dir *dir, struct stat *st,  u_int32_t count)
     dir->d_flags &= ~DIRF_CNID;
 }
 
-/* ---------------------
- * is our cached offspring count valid?
- */
-
-static int diroffcnt(struct dir *dir, struct stat *st)
-{
-    return st->st_ctime == dir->ctime;
-}
 
 /* ---------------------
  * is our cached also for reenumerate id?
  */
-
 int dirreenumerate(struct dir *dir, struct stat *st)
 {
     return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
 }
 
-/* --------------------- */
-static int invisible_dots(const struct vol *vol, const char *name)
-{
-    return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
-}
-
 /* ------------------------------
    (".", curdir)
    (name, dir) with curdir:name == dir, from afp_enumerate
@@ -1733,20 +1362,14 @@ int getdirparams(const struct vol *vol,
                           s_path->st.st_dev,
                           s_path->st.st_ino,
                           dir->d_did,
-                          dir->d_parent->d_did,
+                          dir->d_pdid,
                           vol->v_stamp);
                 ad_flush( &ad);
             }
         }
     }
 
-    if ( dir->d_did == DIRDID_ROOT) {
-        pdid = DIRDID_ROOT_PARENT;
-    } else if (dir->d_did == DIRDID_ROOT_PARENT) {
-        pdid = 0;
-    } else {
-        pdid = dir->d_parent->d_did;
-    }
+    pdid = dir->d_pdid;
 
     data = buf;
     while ( bitmap != 0 ) {
@@ -1759,7 +1382,7 @@ int getdirparams(const struct vol *vol,
         case DIRPBIT_ATTR :
             if ( isad ) {
                 ad_getattr(&ad, &ashort);
-            } else if (invisible_dots(vol, dir->d_u_name)) {
+            } else if (invisible_dots(vol, cfrombstring(dir->d_u_name))) {
                 ashort = htons(ATTRBIT_INVISIBLE);
             } else
                 ashort = 0;
@@ -1771,6 +1394,8 @@ int getdirparams(const struct vol *vol,
         case DIRPBIT_PDID :
             memcpy( data, &pdid, sizeof( pdid ));
             data += sizeof( pdid );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
+                s_path->u_name, ntohl(pdid));
             break;
 
         case DIRPBIT_CDATE :
@@ -1803,7 +1428,7 @@ int getdirparams(const struct vol *vol,
                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
 
                 /* dot files are by default visible */
-                if (invisible_dots(vol, dir->d_u_name)) {
+                if (invisible_dots(vol, cfrombstring(dir->d_u_name))) {
                     ashort = htons(FINDERINFO_INVISIBLE);
                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
                 }
@@ -1827,6 +1452,8 @@ int getdirparams(const struct vol *vol,
         case DIRPBIT_DID :
             memcpy( data, &dir->d_did, sizeof( aint ));
             data += sizeof( aint );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):            DID: %u",
+                s_path->u_name, ntohl(dir->d_did));
             break;
 
         case DIRPBIT_OFFCNT :
@@ -1924,12 +1551,12 @@ int getdirparams(const struct vol *vol,
     if ( l_nameoff ) {
         ashort = htons( data - buf );
         memcpy( l_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
+        data = set_name(vol, data, pdid, cfrombstring(dir->d_m_name), dir->d_did, 0);
     }
     if ( utf_nameoff ) {
         ashort = htons( data - buf );
         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
+        data = set_name(vol, data, pdid, cfrombstring(dir->d_m_name), dir->d_did, utf8);
     }
     if ( isad ) {
         ad_close_metadata( &ad );
@@ -2014,33 +1641,7 @@ int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
  *
  * assume path == '\0' eg. it's a directory in canonical form
  */
-
-struct path Cur_Path = {
-    0,
-    "",  /* mac name */
-    ".", /* unix name */
-    0,   /* id */
-    NULL,/* struct dir */
-    0,   /* stat is not set */
-};
-
-/* ------------------ */
-static int set_dir_errors(struct path *path, const char *where, int err)
-{
-    switch ( err ) {
-    case EPERM :
-    case EACCES :
-        return AFPERR_ACCESS;
-    case EROFS :
-        return AFPERR_VLOCK;
-    }
-    LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
-    return AFPERR_PARAM;
-}
-
-/* ------------------ */
-int setdirparams(struct vol *vol,
-                 struct path *path, u_int16_t d_bitmap, char *buf )
+int setdirparams(struct vol *vol, struct path *path, u_int16_t d_bitmap, char *buf )
 {
     struct maccess  ma;
     struct adouble  ad;
@@ -2192,7 +1793,7 @@ int setdirparams(struct vol *vol,
          * to set our name, etc.
          */
         if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
-            ad_setname(&ad, curdir->d_m_name);
+            ad_setname(&ad, cfrombstring(curdir->d_m_name));
         }
     }
 
@@ -2349,8 +1950,8 @@ setdirparam_done:
         if (path->st_valid && !path->st_errno) {
             struct stat *st = &path->st;
 
-            if (dir && dir->d_parent) {
-                ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_parent->d_did, vol->v_stamp);
+            if (dir && dir->d_pdid) {
+                ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_pdid, vol->v_stamp);
             }
         }
         ad_flush( &ad);
@@ -2359,7 +1960,7 @@ setdirparam_done:
 
     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
         && gettimeofday(&tv, NULL) == 0) {
-        if (!movecwd(vol, dir->d_parent)) {
+        if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) {
             newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
             /* be careful with bitmap because now dir is null */
             bitmap = 1<<DIRPBIT_MDATE;
@@ -2454,7 +2055,7 @@ int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
 
         if ( fsync(dfd) < 0 )
             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
-                vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
+                vol->ad_path(cfrombstring(dir->d_u_name), ADFLAGS_DIR), strerror(errno) );
         close(dfd);
     }
 
@@ -2510,8 +2111,10 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     if (of_stat(s_path) < 0) {
         return AFPERR_MISC;
     }
+
     curdir->offcnt++;
-    if ((dir = adddir( vol, curdir, s_path)) == NULL) {
+
+    if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) {
         return AFPERR_MISC;
     }
 
@@ -2532,10 +2135,10 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     ad_close_metadata( &ad);
 
 createdir_done:
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     /* FIXME: are we really inside the created dir? */
     addir_inherit_acl(vol);
-#endif
+#endif /* HAVE_ACLS */
 
     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
     *rbuflen = sizeof( u_int32_t );
@@ -2558,9 +2161,7 @@ int renamedir(const struct vol *vol,
               char *newname)
 {
     struct adouble  ad;
-    struct dir      *parent;
-    char                *buf;
-    int         len, err;
+    int             err;
 
     /* existence check moved to afp_moveandrename */
     if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
@@ -2591,11 +2192,6 @@ int renamedir(const struct vol *vol,
 
     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
-     * metadata
-     */
-
     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
 
     if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
@@ -2604,50 +2200,11 @@ int renamedir(const struct vol *vol,
         ad_close_metadata( &ad);
     }
 
-    dir_hash_del(vol, dir);
-    if (dir->d_m_name == dir->d_u_name)
-        dir->d_u_name = NULL;
-
-    if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
-        LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
-        /* FIXME : fatal ? */
+    if (dir_modify(vol, dir, curdir->d_did, 0, newname, dst, curdir->d_fullpath) != 0) {
+        LOG(log_error, logtype_afpd, "renamedir: fatal error from dir_modify: %s -> %s", src, dst);
         return AFPERR_MISC;
     }
-    dir->d_m_name = buf;
-    strcpy( dir->d_m_name, newname );
-
-    if (newname == dst) {
-        free(dir->d_u_name);
-        dir->d_u_name = dir->d_m_name;
-    }
-    else {
-        if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
-            LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
-            return AFPERR_MISC;
-        }
-        dir->d_u_name = buf;
-        strcpy( dir->d_u_name, dst );
-    }
-
-    if (dir->d_m_name_ucs2)
-        free(dir->d_m_name_ucs2);
-
-    dir->d_m_name_ucs2 = NULL;
-    if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2))
-        dir->d_m_name_ucs2 = NULL;
-
-    if (( parent = dir->d_parent ) == NULL ) {
-        return( AFP_OK );
-    }
-    if ( parent == newparent ) {
-        hash_alloc_insert(vol->v_hash, dir, dir);
-        return( AFP_OK );
-    }
 
-    /* detach from old parent and add to new one. */
-    dirchildremove(parent, dir);
-    dir->d_parent = newparent;
-    dirchildadd(vol, newparent, dir);
     return( AFP_OK );
 }
 
@@ -2662,7 +2219,7 @@ int deletecurdir(struct vol *vol)
     u_int16_t       ashort;
     int err;
 
-    if ( curdir->d_parent == NULL ) {
+    if ( dirlookup(vol, curdir->d_pdid) == NULL ) {
         return( AFPERR_ACCESS );
     }
 
@@ -2703,14 +2260,13 @@ int deletecurdir(struct vol *vol)
         }
     }
 
-    if ( movecwd( vol, curdir->d_parent ) < 0 ) {
+    if ( movecwd(vol, dirlookup(vol, curdir->d_pdid)) < 0 ) {
         err = afp_errno;
         goto delete_done;
     }
 
-    err = netatalk_rmdir_all_errors(-1, fdir->d_u_name);
+    err = netatalk_rmdir_all_errors(-1, cfrombstring(fdir->d_u_name));
     if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
-        dirchildremove(curdir, fdir);
         cnid_delete(vol->v_cdb, fdir->d_did);
         dir_remove( vol, fdir );
     }
@@ -2738,7 +2294,6 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
     sfunc = (unsigned char) *ibuf++;
     *rbuflen = 0;
 
-
     if (sfunc >= 3 && sfunc <= 6) {
         if (afp_version < 30) {
             return( AFPERR_PARAM );
@@ -2777,7 +2332,7 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             name = NULL;
         }
         break;
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     case 5 : /* UUID -> username */
     case 6 : /* UUID -> groupname */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
@@ -2811,7 +2366,7 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             *rbuflen = 2 * sizeof( id );
         }
         break;
-#endif
+#endif /* HAVE_ACLS */
     default :
         return( AFPERR_PARAM );
     }
@@ -2865,7 +2420,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
     case 4 :
         len = (unsigned char) *ibuf++;
         break;
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     case 5 : /* username -> UUID  */
     case 6 : /* groupname -> UUID */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
@@ -2874,7 +2429,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
         len = ntohs(ulen);
         ibuf += 2;
         break;
-#endif
+#endif /* HAVE_ACLS */
     default :
         return( AFPERR_PARAM );
     }
@@ -2908,7 +2463,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
             memcpy( rbuf, &id, sizeof( id ));
             *rbuflen = sizeof( id );
             break;
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
         case 5 :        /* username -> UUID */
             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
             if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
@@ -2921,7 +2476,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
                 return AFPERR_NOITEM;
             *rbuflen = UUID_BINSIZE;
             break;
-#endif
+#endif /* HAVE_ACLS */
         }
     }
     return( AFP_OK );
index 5334be5d5610817c7e117f2efd3390c4420f2aff..0fe65e7000af0f02fe5a7e3e7049e013d4c96e16 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.h,v 1.34 2010-03-12 15:16:49 franklahm 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.
 #include "globals.h"
 #include "volume.h"
 
-#define DIRTREE_COLOR_RED    0
-#define DIRTREE_COLOR_BLACK  1
-
 #define DIRF_FSMASK    (3<<0)
 #define DIRF_NOFS      (0<<0)
 #define DIRF_AFS       (1<<0)
 #define DIRF_UFS       (2<<0)
 
-#define DIRF_OFFCNT     (1<<4) /* offsprings count is valid */
-#define DIRF_CNID      (1<<5)  /* renumerate id */
+#define DIRF_OFFCNT    (1<<4) /* offsprings count is valid */
+#define DIRF_CNID         (1<<5) /* renumerate id */
+#define DIRF_CACHELOCK (1<<6) /* lock in cache, don't remove in dircache_eviction, for catsearch */
 
 #define AFPDIR_READ    (1<<0)
 
 #define FILDIRBIT_ISDIR        (1 << 7) /* is a directory */
 #define FILDIRBIT_ISFILE       (0)      /* is a file */
 
-/* reserved directory id's */
-#define DIRDID_ROOT_PARENT    htonl(1)  /* parent directory of root */
-#define DIRDID_ROOT           htonl(2)  /* root directory */
-
 /* file/directory ids. what a mess. we scramble things in a vain attempt
  * to get something meaningful */
 #ifndef AFS
 #define CNID(a,b)     (((a)->st_ino & 0x7fffffff) | CNID_FILE(b))
 #endif /* AFS */
 
-
 struct maccess {
     u_char     ma_user;
     u_char     ma_world;
@@ -113,46 +106,39 @@ struct maccess {
 #define        AR_UWRITE       (1<<2)
 #define        AR_UOWN         (1<<7)
 
-extern struct dir       *dirnew (const char *, const char *);
-extern void             dirfreename (struct dir *);
-extern void             dirfree (struct dir *);
-extern struct dir      *dirsearch (const struct vol *, u_int32_t);
-extern struct dir      *dirlookup (struct vol *, u_int32_t);
-extern struct dir       *dirsearch_byname (const struct vol *, struct dir *,char *);
-
-extern struct dir      *adddir (struct vol *, struct dir *, 
-                                               struct path *);
-
-extern int              movecwd (struct vol *, struct dir *);
-extern int              deletecurdir (struct vol *);
-extern struct path      *cname (struct vol *, struct dir *,
-                             char **);
-extern mode_t           mtoumode (struct maccess *);
-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 *, 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);
-extern int dirreenumerate (struct dir *dir, struct stat *st);
-
 typedef int (*dir_loop)(struct dirent *, char *, void *);
 
-extern int  for_each_dirent (const struct vol *, char *, dir_loop , void *);
-
-extern int  check_access (char *name , int mode);
-extern int file_access   (struct path *path, int mode);
-
-extern int netatalk_unlink (const char *name);
+extern struct dir *dir_new(const char *mname, const char *uname, const struct vol *,
+                           cnid_t pdid, cnid_t did, bstring fullpath); /* volume.c needs it once */
+extern void        dir_free (struct dir *);
+extern struct dir  *dir_add(const struct vol *, const struct dir *, struct path *, int);
+extern int         dir_modify(const struct vol *vol, struct dir *dir, cnid_t pdid, cnid_t did,
+                              const char *new_mname, const char *new_uname, bstring pdir_fullpath);
+extern int         dir_remove(const struct vol *vol, struct dir *dir);
+extern struct dir  *dirlookup (const struct vol *, cnid_t);
+extern int         movecwd (const struct vol *, struct dir *);
+extern struct path *cname (struct vol *, struct dir *, char **);
+
+extern int         deletecurdir (struct vol *);
+extern mode_t      mtoumode (struct maccess *);
+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 caseenumerate (const struct vol *, struct path *, struct dir *);
+extern int         setdirparams(struct vol *, struct path *, u_int16_t, 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);
+extern int         dirreenumerate(struct dir *dir, struct stat *st);
+extern int         for_each_dirent(const struct vol *, char *, dir_loop , void *);
+extern int         check_access(char *name , int mode);
+extern int         file_access(struct path *path, int mode);
+extern int         netatalk_unlink (const char *name);
+extern int         caseenumerate (const struct vol *, struct path *, struct dir *);
 
-extern hash_t *dirhash (void);
 /* from enumerate.c */
-extern char *check_dirent (const struct vol *, char *);
+extern char        *check_dirent (const struct vol *, char *);
 
 /* FP functions */
 int afp_createdir (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index c06ef20300f3f296e4e2b0d41b2b40404a4538b0..b8a403bd4fd958a9b5de04d9c6cca5ccb3ea5bc2 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: enumerate.c,v 1.49 2010-02-10 14:05:37 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
 #include <atalk/cnid.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+
 #include "desktop.h"
 #include "directory.h"
+#include "dircache.h"
 #include "volume.h"
 #include "globals.h"
 #include "file.h"
@@ -266,8 +268,10 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
         return path_error(o_path, AFPERR_NODIR );
     }
 
-    LOG(log_debug, logtype_afpd, "enumerate(vid:%u, did:%u, name:'%s', f/d:%04x/%04x, rc:%u, i:%u, max:%u)",
-        ntohs(vid), ntohl(did), o_path->u_name, fbitmap, dbitmap, reqcnt, sindex, maxsz);
+    LOG(log_debug, logtype_afpd, "enumerate(vid:%u, did:%u, cwddid:%u, cwd:'%s', name:'%s', f/d:%04x/%04x, rc:%u, i:%u, max:%u)",
+        ntohs(vid), ntohl(did), ntohl(curdir->d_did),
+        cfrombstring(curdir->d_fullpath), o_path->u_name,
+        fbitmap, dbitmap, reqcnt, sindex, maxsz);
 
     data = rbuf + 3 * sizeof( u_int16_t );
     sz = 3 * sizeof( u_int16_t );      /* fbitmap, dbitmap, reqcount */
@@ -370,14 +374,16 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
             if ( dbitmap == 0 ) {
                 continue;
             }
-            dir = dirsearch_byname(vol, curdir, s_path.u_name);
-            if (!dir && NULL == (dir = adddir( vol, curdir, &s_path) ) ) {
+            int len = strlen(s_path.u_name);
+            if ((dir = dircache_search_by_name(vol, curdir, s_path.u_name, len)) == NULL) {
+                if ((dir = dir_add(vol, curdir, &s_path, len)) == NULL) {
+                    LOG(log_error, logtype_afpd, "enumerate(vid:%u, did:%u, name:'%s'): error adding dir: '%s'",
+                        ntohs(vid), ntohl(did), o_path->u_name, s_path.u_name);
                     return AFPERR_MISC;
                 }
-            if (AFP_OK != ( ret = getdirparams(vol, dbitmap, &s_path, dir,
-                                     data + header , &esz ))) {
-                return( ret );
             }
+            if ((ret = getdirparams(vol, dbitmap, &s_path, dir, data + header , &esz)) != AFP_OK)
+                return( ret );
 
         } else {
             if ( fbitmap == 0 ) {
index b10330c8aebaeef678a449f46e432988c43b841a..a1674601d3b39bfe40c7b745ccf22f1486755e03 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: file.c,v 1.141 2010-03-12 15:16:49 franklahm 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.
@@ -41,6 +41,7 @@ char *strchr (), *strrchr ();
 #include <atalk/unix.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
@@ -202,8 +203,8 @@ char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t
                                  (1 << FILPBIT_UNIXPR)))
 
 /* -------------------------- */
-u_int32_t get_id(struct vol *vol, struct adouble *adp,  const struct stat *st,
-                 const cnid_t did, char *upath, const int len) 
+uint32_t get_id(struct vol *vol, struct adouble *adp,  const struct stat *st,
+                const cnid_t did, char *upath, const int len) 
 {
     static int first = 1;       /* mark if this func is called the first time */
     u_int32_t adcnid;
@@ -298,24 +299,49 @@ int getmetadata(struct vol *vol,
     struct stat         *st;
     struct maccess     ma;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "begin getmetadata:");
-#endif /* DEBUG */
-
     upath = path->u_name;
     st = &path->st;
-
     data = buf;
 
     if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
          || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
          || (bitmap & (1 << FILPBIT_FNUM))) {
-        if (!path->id)
-            id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
-        else 
+        if (!path->id) {
+            struct dir *cachedfile;
+            int len = strlen(upath);
+            if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
+                id = cachedfile->d_did;
+            else {
+                id = get_id(vol, adp, st, dir->d_did, upath, len);
+
+                /* Add it to the cache */
+                LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
+                    ntohl(dir->d_did), upath, ntohl(id));
+
+                /* Get macname from unixname first */
+                if (path->m_name == NULL) {
+                    if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
+                        LOG(log_error, logtype_afpd, "getmetadata: utompath error");
+                        exit(EXITERR_SYS);
+                    }
+                }
+                
+                if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL)) == NULL) {
+                    LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
+                    exit(EXITERR_SYS);
+                }
+                if ((dircache_add(cachedfile)) != 0) {
+                    LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
+                    exit(EXITERR_SYS);
+                }
+            }
+        } else {
             id = path->id;
+        }
+
         if (id == CNID_INVALID)
             return afp_errno;
+
         if (!path->m_name) {
             path->m_name = utompath(vol, upath, id, utf8_encoding());
         }
@@ -348,11 +374,15 @@ int getmetadata(struct vol *vol,
 #endif
             memcpy(data, &ashort, sizeof( ashort ));
             data += sizeof( ashort );
+            LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
+                path->u_name, ntohs(ashort));
             break;
 
         case FILPBIT_PDID :
             memcpy(data, &dir->d_did, sizeof( u_int32_t ));
             data += sizeof( u_int32_t );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
+                path->u_name, ntohl(dir->d_did));
             break;
 
         case FILPBIT_CDATE :
@@ -399,6 +429,8 @@ int getmetadata(struct vol *vol,
         case FILPBIT_FNUM :
             memcpy(data, &id, sizeof( id ));
             data += sizeof( id );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):           CNID: %u",
+                path->u_name, ntohl(id));
             break;
 
         case FILPBIT_DFLEN :
@@ -563,10 +595,6 @@ int getfilparams(struct vol *vol,
     int                 opened = 0;
     int rc;    
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_default, "begin getfilparams:");
-#endif /* DEBUG */
-
     opened = PARAM_NEED_ADP(bitmap);
     adp = NULL;
 
@@ -597,9 +625,6 @@ int getfilparams(struct vol *vol,
     if ( adp ) {
         ad_close_metadata( adp);
     }
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "end getfilparams:");
-#endif /* DEBUG */
 
     return( rc );
 }
@@ -1945,6 +1970,10 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     }
 
     if (NULL == ( dir = dirlookup( vol, id )) ) {
+        if (afp_errno == AFPERR_NOOBJ) {
+            err = AFPERR_NOOBJ;
+            goto delete;
+        }
         return( AFPERR_PARAM );
     }
 
@@ -1968,6 +1997,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     else if (S_ISDIR(st.st_mode)) /* directories are bad */
         return AFPERR_BADTYPE;
 
+delete:
     if (cnid_delete(vol->v_cdb, fileid)) {
         switch (errno) {
         case EROFS:
index 6cf4ae22f8184d15319dddb26d6d25b75bf5c832..3c64e7ddca603fe46c8f73de206fe3ed69fba262 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: file.h,v 1.26 2010-03-12 15:16:49 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.
@@ -127,8 +127,8 @@ extern void *get_finderinfo (const struct vol *, const char *, struct adouble *,
 extern size_t mtoUTF8   (const struct vol *, const char *, size_t , char *, size_t );
 extern int  copy_path_name (const struct vol *, char *, char *i);
 
-extern u_int32_t get_id  (struct vol *, struct adouble *, const struct stat *,
-                                const cnid_t , char *, const int );
+extern uint32_t get_id  (struct vol *, struct adouble *, const struct stat *,
+                         const cnid_t , char *, const int );
 
 /* FP functions */
 int afp_exchangefiles (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index 3e5cb2f5ee8048597a75d0552c971f7c769821f9..bf3a8c0681c1fea7b5ab371ef53a923923716998 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: filedir.c,v 1.73 2010-03-12 15:16:49 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.
@@ -39,8 +39,11 @@ char *strchr (), *strrchr ();
 #include <atalk/cnid.h>
 #include <atalk/logger.h>
 #include <atalk/unix.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
@@ -52,22 +55,22 @@ char *strchr (), *strrchr ();
 #ifdef DROPKLUDGE
 int matchfile2dirperms(
 /* Since it's kinda' big; I decided against an
-inline function */
-    char       *upath,
+   inline function */
+    char    *upath,
     struct vol  *vol,
-    int                did)
+    int     did)
 /* The below code changes the way file ownership is determined in the name of
-fixing dropboxes.  It has known security problem.  See the netatalk FAQ for
-more information */
+   fixing dropboxes.  It has known security problem.  See the netatalk FAQ for
+   more information */
 {
-    struct stat        st, sb;
-    struct dir *dir;
-    char       *adpath;
+    struct stat st, sb;
+    struct dir  *dir;
+    char    *adpath;
     uid_t       uid;
     int         ret = AFP_OK;
 #ifdef DEBUG
     LOG(log_debug9, logtype_afpd, "begin matchfile2dirperms:");
-#endif 
+#endif
 
     if (stat(upath, &st ) < 0) {
         LOG(log_error, logtype_afpd, "Could not stat %s: %s", upath, strerror(errno));
@@ -119,7 +122,7 @@ more information */
                     adpath, strerror(errno));
                 ret = AFPERR_ACCESS;
             }
-            seteuid(uid); 
+            seteuid(uid);
         }
     } /* end else if stat success */
 
@@ -132,13 +135,13 @@ more information */
 
 int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
 {
-    struct stat                *st;
-    struct vol         *vol;
-    struct dir         *dir;
+    struct stat     *st;
+    struct vol      *vol;
+    struct dir      *dir;
     u_int32_t           did;
-    int                        ret;
-    size_t             buflen;
-    u_int16_t          fbitmap, dbitmap, vid;
+    int         ret;
+    size_t      buflen;
+    u_int16_t       fbitmap, dbitmap, vid;
     struct path         *s_path;
 
     *rbuflen = 0;
@@ -149,7 +152,7 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
     if (NULL == ( vol = getvolbyvid( vid )) ) {
         /* was AFPERR_PARAM but it helps OS 10.3 when a volume has been removed
          * from the list.
-         */ 
+         */
         return( AFPERR_ACCESS );
     }
 
@@ -168,11 +171,12 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
     ibuf += sizeof( dbitmap );
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
-    LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, name:'%s', f/d:%04x/%04x) {cwd: %s}",
-        ntohs(vid), ntohl(dir->d_did), s_path->u_name, fbitmap, dbitmap, getcwdpath());
+    LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, f/d:%04x/%04x) {cwdid:%u, cwd: %s, name:'%s'}",
+        ntohs(vid), ntohl(dir->d_did), fbitmap, dbitmap,
+        ntohl(curdir->d_did), cfrombstring(curdir->d_fullpath), s_path->u_name);
 
     st   = &s_path->st;
     if (!s_path->st_valid) {
@@ -184,7 +188,9 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
         of_statdir(vol, s_path);
     }
     if ( s_path->st_errno != 0 ) {
-        return( AFPERR_NOOBJ );
+        if (afp_errno != AFPERR_ACCESS) {
+            return( AFPERR_NOOBJ );
+        }
     }
 
 
@@ -192,19 +198,19 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
     if (S_ISDIR(st->st_mode)) {
         if (dbitmap) {
             dir = s_path->d_dir;
-            if (!dir) 
+            if (!dir)
                 return AFPERR_NOOBJ;
 
             ret = getdirparams(vol, dbitmap, s_path, dir,
-                                 rbuf + 3 * sizeof( u_int16_t ), &buflen );
+                               rbuf + 3 * sizeof( u_int16_t ), &buflen );
             if (ret != AFP_OK )
                 return( ret );
         }
         /* this is a directory */
         *(rbuf + 2 * sizeof( u_int16_t )) = (char) FILDIRBIT_ISDIR;
     } else {
-        if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir, 
-                                            rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
+        if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir,
+                                                     rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
             return( ret );
         }
         /* this is a file */
@@ -224,12 +230,12 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
 
 int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct stat        *st;
-    struct vol *vol;
-    struct dir *dir;
+    struct stat *st;
+    struct vol  *vol;
+    struct dir  *dir;
     struct path *path;
-    u_int16_t  vid, bitmap;
-    int                did, rc;
+    u_int16_t   vid, bitmap;
+    int     did, rc;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -247,7 +253,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     ibuf += sizeof( did);
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
-       return afp_errno;    
+        return afp_errno;
     }
 
     memcpy( &bitmap, ibuf, sizeof( bitmap ));
@@ -255,7 +261,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     ibuf += sizeof( bitmap );
 
     if (NULL == ( path = cname( vol, dir, &ibuf ))) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     st   = &path->st;
@@ -267,7 +273,8 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     }
 
     if ( path->st_errno != 0 ) {
-        return( AFPERR_NOOBJ );
+        if (afp_errno != AFPERR_ACCESS)
+            return( AFPERR_NOOBJ );
     }
     /*
      * If ibuf is odd, make it even.
@@ -288,7 +295,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     return( rc );
 }
 
-/* -------------------------------------------- 
+/* --------------------------------------------
    Factorise some checks on a pathname
 */
 int check_name(const struct vol *vol, char *name)
@@ -363,14 +370,13 @@ static int moveandrename(const struct vol *vol,
         }
     } else {
         id = sdir->d_did; /* we already have the CNID */
-        p = ctoupath( vol, sdir->d_parent, oldname );
+        p = ctoupath( vol, dirlookup(vol, sdir->d_pdid), oldname );
         if (!p) {
             return AFPERR_PARAM;
         }
         adflags = ADFLAGS_DIR;
     }
 
-
     /*
      * p now points to either
      *   a) full pathname of the source fs object (if renameat is not available)
@@ -391,7 +397,7 @@ static int moveandrename(const struct vol *vol,
 
         ad_getattr(adp, &bshort);
         ad_close_metadata( adp);
-        if ((bshort & htons(ATTRBIT_NORENAME))) 
+        if ((bshort & htons(ATTRBIT_NORENAME)))
             return(AFPERR_OLOCK);
     }
     if (sdir_fd != -1) {
@@ -401,17 +407,17 @@ static int moveandrename(const struct vol *vol,
         }
     }
 
-    if (NULL == (upath = mtoupath(vol, newname, curdir->d_did, utf8_encoding()))){ 
+    if (NULL == (upath = mtoupath(vol, newname, curdir->d_did, utf8_encoding()))){
         return AFPERR_PARAM;
     }
     path.u_name = upath;
-    st = &path.st;    
+    st = &path.st;
     if (0 != (rc = check_name(vol, upath))) {
-            return  rc;
+        return  rc;
     }
 
     /* source == destination. we just silently accept this. */
-    if ((!isdir && curdir == sdir) || (isdir && curdir == sdir->d_parent)) {
+    if ((!isdir && curdir == sdir) || (isdir && curdir->d_did == sdir->d_pdid)) {
         if (strcmp(oldname, newname) == 0)
             return AFP_OK;
 
@@ -453,13 +459,13 @@ static int moveandrename(const struct vol *vol,
 /* -------------------------------------------- */
 int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct vol *vol;
-    struct dir *sdir;
+    struct vol  *vol;
+    struct dir  *sdir;
     char        *oldname, *newname;
     struct path *path;
-    u_int32_t  did;
+    u_int32_t   did;
     int         plen;
-    u_int16_t  vid;
+    u_int16_t   vid;
     int         isdir = 0;
     int         rc;
 
@@ -478,12 +484,12 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     memcpy( &did, ibuf, sizeof( did ));
     ibuf += sizeof( did );
     if (NULL == ( sdir = dirlookup( vol, did )) ) {
-       return afp_errno;    
+        return afp_errno;
     }
 
     /* source pathname */
     if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     sdir = curdir;
@@ -498,14 +504,14 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         }
     }
     else {
-        if ( sdir->d_parent == NULL ) { /* root directory */
+        if ( sdir->d_did == DIRDID_ROOT ) { /* root directory */
             return( AFPERR_NORENAME );
         }
         /* move to destination dir */
-        if ( movecwd( vol, sdir->d_parent ) < 0 ) {
+        if ( movecwd( vol, dirlookup(vol, sdir->d_pdid) ) < 0 ) {
             return afp_errno;
         }
-        strcpy(oldname, sdir->d_m_name);
+        memcpy(oldname, cfrombstring(sdir->d_m_name), blength(sdir->d_m_name) +1);
     }
 
     /* another place where we know about the path type */
@@ -528,12 +534,12 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 /* ------------------------------- */
 int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct vol         *vol;
-    struct dir         *dir;
+    struct vol      *vol;
+    struct dir      *dir;
     struct path         *s_path;
-    char               *upath;
-    int                        did, rc;
-    u_int16_t          vid;
+    char        *upath;
+    int         did, rc;
+    u_int16_t       vid;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -549,22 +555,21 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 
     memcpy( &did, ibuf, sizeof( did ));
     ibuf += sizeof( int );
+
     if (NULL == ( dir = dirlookup( vol, did )) ) {
-       return afp_errno;    
+        return afp_errno;
     }
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     upath = s_path->u_name;
     if ( path_isadir( s_path) ) {
-       if (*s_path->m_name != '\0') {
-           rc = AFPERR_ACCESS;
-       }
-       else {
+        if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT)
+            rc = AFPERR_ACCESS;
+        else
             rc = deletecurdir( vol);
-        }
     } else if (of_findname(s_path)) {
         rc = AFPERR_BUSY;
     } else {
@@ -577,10 +582,16 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         }
         else {
             rc = deletefile(vol, -1, upath, 1);
+
+            struct dir *cachedfile;
+            if (cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath))) {
+                dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
+                dir_free(cachedfile);
+            }
         }
     }
     if ( rc == AFP_OK ) {
-       curdir->offcnt--;
+        curdir->offcnt--;
         setvoltime(obj, vol );
     }
 
@@ -589,66 +600,51 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 /* ------------------------ */
 char *absupath(const struct vol *vol, struct dir *dir, char *u)
 {
-    struct dir *d;
-    static char        path[ MAXPATHLEN + 1];
-    char       *p;
-    int                len;
+    static char pathbuf[MAXPATHLEN + 1];
+    bstring path;
 
-    if (u == NULL)
+    if (u == NULL || dir == NULL || vol == NULL)
         return NULL;
-        
-    p = path + sizeof( path ) - 1;
-    *p = '\0';
-    len = strlen( u );
-    p -= len;
-    memcpy( p, u, len );
-    if (dir) for ( d = dir; d->d_parent; d = d->d_parent ) {
-        u = d->d_u_name;
-        len = strlen( u );
-        if (p -len -1 < path) {
-            /* FIXME 
-               rather rare so LOG error and/or client message ?
-            */
-            return NULL;
-        }
-        *--p = '/';
-        p -= len;
-        memcpy( p, u, len );
-    }
-    len = strlen( vol->v_path );
-    if (p -len -1 < path) {
+
+    if ((path = bstrcpy(dir->d_fullpath)) == NULL)
         return NULL;
-    }
-    *--p = '/';
-    p -= len;
-    memcpy( p, vol->v_path, len );
+    if (bcatcstr(path, "/") != BSTR_OK)
+        return NULL;
+    if (bcatcstr(path, u) != BSTR_OK)
+        return NULL;
+    if (path->slen > MAXPATHLEN)
+        return NULL;
+
+    LOG(log_debug, logtype_afpd, "absupath: %s", cfrombstring(path));
 
-    return( p );
+    strncpy(pathbuf, cfrombstring(path), blength(path) + 1);
+    bdestroy(path);
+
+    return(pathbuf);
 }
 
-/* ------------------------
- * FIXME dir could be NULL
-*/
 char *ctoupath(const struct vol *vol, struct dir *dir, char *name)
 {
+    if (vol == NULL || dir == NULL || name == NULL)
+        return NULL;
     return absupath(vol, dir, mtoupath(vol, name, dir->d_did, utf8_encoding()));
 }
 
 /* ------------------------- */
 int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct vol *vol;
-    struct dir *sdir, *ddir;
+    struct vol  *vol;
+    struct dir  *sdir, *ddir;
     int         isdir;
-    char       *oldname, *newname;
+    char    *oldname, *newname;
     struct path *path;
-    int                did;
-    int                pdid;
+    int     did;
+    int     pdid;
     int         plen;
-    u_int16_t  vid;
+    u_int16_t   vid;
     int         rc;
 #ifdef DROPKLUDGE
-    int                retvalue;
+    int     retvalue;
 #endif /* DROPKLUDGE */
     int     sdir_fd = -1;
 
@@ -677,13 +673,13 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
 
     /* source pathname */
     if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     sdir = curdir;
     newname = obj->newtmp;
     oldname = obj->oldtmp;
-    
+
     isdir = path_isadir(path);
     if ( *path->m_name != '\0' ) {
         if (isdir) {
@@ -691,7 +687,7 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
         }
         strcpy(oldname, path->m_name); /* an extra copy for of_rename */
     } else {
-        strcpy(oldname, sdir->d_m_name);
+        memcpy(oldname, cfrombstring(sdir->d_m_name), blength(sdir->d_m_name) + 1);
     }
 
 #ifdef HAVE_RENAMEAT
@@ -725,11 +721,13 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
 
     /* This does the work */
+    LOG(log_debug, logtype_afpd, "afp_move(oldname:'%s', newname:'%s', isdir:%u)",
+        oldname, newname, isdir);
     rc = moveandrename(vol, sdir, sdir_fd, oldname, newname, isdir);
 
     if ( rc == AFP_OK ) {
         char *upath = mtoupath(vol, newname, pdid, utf8_encoding());
-        
+
         if (NULL == upath) {
             rc = AFPERR_PARAM;
             goto exit;
@@ -772,8 +770,8 @@ int veto_file(const char*veto_str, const char*path)
  * otherwise, 0 is returned.
  */
 {
-    int i;     /* index to veto_str */
-    int j;     /* index to path */
+    int i;  /* index to veto_str */
+    int j;  /* index to path */
 
     if ((veto_str == NULL) || (path == NULL))
         return 0;
@@ -788,7 +786,7 @@ int veto_file(const char*veto_str, const char*path)
         } else {
             if (veto_str[i] != path[j]) {
                 while ((veto_str[i] != '/')
-                        && (veto_str[i] != '\0'))
+                       && (veto_str[i] != '\0'))
                     i++;
                 j = 0;
                 continue;
index 40af2fe1d756b9ea92bb620462ef8d80b4f679fb..5fbb048b1bde766f2304fc59499db22bcdb91763 100644 (file)
@@ -35,6 +35,8 @@
 #define OPTION_NOSLP         (1 << 5)
 #define OPTION_ANNOUNCESSH   (1 << 6)
 #define OPTION_UUID          (1 << 7)
+#define OPTION_ACL2OS9MODE   (1 << 8)
+#define OPTION_NOZEROCONF    (1 << 9)
 
 #ifdef FORCE_UIDGID
 /* set up a structure for this */
@@ -54,7 +56,7 @@ struct afp_volume_name {
 };
 
 struct afp_options {
-    int connections, transports, tickleval, timeout, server_notif, flags;
+    int connections, transports, tickleval, timeout, server_notif, flags, dircachesize;
     unsigned char passwdbits, passwdminlen, loginmaxfail;
     u_int32_t server_quantum;
     char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
@@ -62,6 +64,7 @@ struct afp_options {
     char *uampath, *fqdn;
     char *pidfile;
     char *sigconffile;
+    char *uuidconf;
     struct afp_volume_name defaultvol, systemvol, uservol;
     int  closevol;
 
@@ -89,7 +92,8 @@ struct afp_options {
 typedef struct _AFPObj {
     int proto;
     unsigned long servernum;
-    void *handle, *config;
+    void *handle;               /* either (DSI *) or (ASP *) */
+    void *config; 
     struct afp_options options;
     char *Obj, *Type, *Zone;
     char username[MAXUSERLEN];
@@ -142,6 +146,9 @@ extern int  parseline  (int, char *);
 extern const char *AfpNum2name (int );
 extern const char *AfpErr2name(int err);
 
+/* directory.c */
+extern struct dir rootParent;
+
 #ifndef NO_DDP
 extern void afp_over_asp (AFPObj *);
 #endif /* NO_DDP */
index 8f63fc6e4013ed994d0ad9e89f4916e16db1c3d5..32d44d95614f4fa91d17d9e1ef546f5395180a19 100644 (file)
@@ -38,6 +38,7 @@
 #include "status.h"
 #include "fork.h"
 #include "uam_auth.h"
+#include "afp_zeroconf.h"
 
 #ifdef TRU64
 #include <sys/security.h>
@@ -55,6 +56,7 @@ static AFPConfig *configs;
 static server_child *server_children;
 static fd_set save_rfds;
 static int    Ipc_fd = -1;
+static sig_atomic_t reloadconfig = 0;
 
 #ifdef TRU64
 void afp_get_cmdline( int *ac, char ***av)
@@ -98,50 +100,30 @@ static void afp_goaway(int sig)
 
     dsi_kill(sig);
     switch( sig ) {
+
     case SIGTERM :
         LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
+        AFPConfig *config;
+        for (config = configs; config; config = config->next)
+            if (config->server_cleanup)
+                config->server_cleanup(config);
+        afp_exit(0);
         break;
+
     case SIGUSR1 :
-    case SIGHUP :
-        /* w/ a configuration file, we can force a re-read if we want */
         nologin++;
         auth_unload();
-        if (sig == SIGHUP || ((nologin + 1) & 1)) {
-            AFPConfig *config;
-
-            LOG(log_info, logtype_afpd, "re-reading configuration file");
-            for (config = configs; config; config = config->next)
-                if (config->server_cleanup)
-                    config->server_cleanup(config);
+        LOG(log_info, logtype_afpd, "disallowing logins");        
+        break;
 
-            /* configfree close atp socket used for DDP tickle, there's an issue
-             * with atp tid.
-            */
-            configfree(configs, NULL);
-            if (!(configs = configinit(&default_options))) {
-                LOG(log_error, logtype_afpd, "config re-read: no servers configured");
-                afp_exit(EXITERR_CONF);
-            }
-            set_fd(Ipc_fd);
-        } else {
-            LOG(log_info, logtype_afpd, "disallowing logins");
-        }
-        if (sig == SIGHUP) {
-            nologin = 0;
-        }
+    case SIGHUP :
+        /* w/ a configuration file, we can force a re-read if we want */
+        reloadconfig = 1;
         break;
+
     default :
         LOG(log_error, logtype_afpd, "afp_goaway: bad signal" );
     }
-    if ( sig == SIGTERM ) {
-        AFPConfig *config;
-
-        for (config = configs; config; config = config->next)
-            if (config->server_cleanup)
-                config->server_cleanup(config);
-
-        afp_exit(0);
-    }
     return;
 }
 
@@ -165,9 +147,9 @@ int main(int ac, char **av)
     set_auth_parameters( ac, av );
 #endif /* TRU64 */
 
-#ifdef DEBUG1
+    /* Log SIGBUS/SIGSEGV SBT */
     fault_setup(NULL);
-#endif
+
     afp_options_init(&default_options);
     if (!afp_options_parse(ac, av, &default_options))
         exit(EXITERR_CONF);
@@ -275,12 +257,12 @@ int main(int ac, char **av)
 #endif
     sigaddset(&sigs, SIGCHLD);
 
-    sigprocmask(SIG_BLOCK, &sigs, NULL);
+    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
     if (!(configs = configinit(&default_options))) {
         LOG(log_error, logtype_afpd, "main: no servers configured");
         afp_exit(EXITERR_CONF);
     }
-    sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
 
     /* Register CNID  */
     cnid_init();
@@ -299,9 +281,34 @@ int main(int ac, char **av)
      * solution. */
     while (1) {
         rfds = save_rfds;
-        sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+        pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
         ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
-        sigprocmask(SIG_BLOCK, &sigs, NULL);
+        pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+        int saveerrno = errno;
+
+        if (reloadconfig) {
+            nologin++;
+            auth_unload();
+            AFPConfig *config;
+
+            LOG(log_info, logtype_afpd, "re-reading configuration file");
+            for (config = configs; config; config = config->next)
+                if (config->server_cleanup)
+                    config->server_cleanup(config);
+
+            /* configfree close atp socket used for DDP tickle, there's an issue
+             * with atp tid. */
+            configfree(configs, NULL);
+            if (!(configs = configinit(&default_options))) {
+                LOG(log_error, logtype_afpd, "config re-read: no servers configured");
+                afp_exit(EXITERR_CONF);
+            }
+            set_fd(Ipc_fd);
+            nologin = 0;
+            reloadconfig = 0;
+            errno = saveerrno;
+        }
+        
         if (ret < 0) {
             if (errno == EINTR)
                 continue;
index c00b76ba4c874c51227ef2100e64ce9d8a660bcf..7769542df4a4fe8e9df8a8f94043bbab57b42866 100644 (file)
@@ -1,5 +1,5 @@
 /* 
- * $Id: mangle.c,v 1.19 2006-09-19 01:35:45 didg Exp $ 
+ * $Id: mangle.c,v 1.19.4.2 2010-02-01 14:25:45 franklahm Exp $ 
  *
  * Copyright (c) 2002. Joe Marcus Clarke (marcus@marcuscom.com)
  * All Rights Reserved.  See COPYRIGHT.
 
 #include <stdio.h>
 #include <ctype.h>
+
+#include <atalk/util.h>
+#include <atalk/bstradd.h>
+
 #include "mangle.h"
 #include "desktop.h"
-#include <atalk/util.h>  
+
 
 #define hextoint( c )   ( isdigit( c ) ? c - '0' : c + 10 - 'A' )
 #define isuxdigit(x)    (isdigit(x) || (isupper(x) && isxdigit(x)))
@@ -38,7 +42,7 @@ static size_t mangle_extension(const struct vol *vol, const char* uname,
   return 0;
 }
 
-static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
+static char *demangle_checks(const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
 {
     u_int16_t flags;
     static char buffer[MAXPATHLEN +2];  /* for convert_charset dest_len parameter +2 */
@@ -48,18 +52,18 @@ static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilen
     /* We need to check, whether we really need to demangle the filename       */
     /* i.e. it's not just a file with a valid #HEX in the name ...             */
     /* but we don't want to miss valid demangle as well.                       */
-
     /* check whether file extensions match */
-    {
-      char buf[MAX_EXT_LENGTH + 2];  /* for convert_charset dest_len parameter +2 */
-      size_t ext_len = mangle_extension(vol, uname, buf, CH_UTF8_MAC);
 
-      if (ext_len) {
-       buf[ext_len] = '\0';
-       if (strcmp(ext, buf)) return mfilename;
-      } else {
-       if (*ext) return mfilename;
-      }
+    char buf[MAX_EXT_LENGTH + 2];  /* for convert_charset dest_len parameter +2 */
+    size_t ext_len = mangle_extension(vol, uname, buf, CH_UTF8_MAC);
+
+    if (ext_len) {
+        buf[ext_len] = '\0';
+        if (strcmp(ext, buf))
+            return mfilename;
+    } else {
+        if (*ext)
+            return mfilename;
     }
 
     /* First we convert the unix name to our volume maccharset         */
@@ -169,8 +173,8 @@ private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx
     }
 
     /* is it a dir?, there's a conflict with pre OSX 'trash #2'  */
-    if ((dir = dirsearch(vol, id))) {
-        if (dir->d_parent && dir->d_parent->d_did != did) {
+    if ((dir = dirlookup(vol, id))) {
+        if (dir->d_pdid != did) {
             /* not in the same folder, there's a race with outdate cache
              * but we have to live with it, hopefully client will recover
             */
@@ -178,13 +182,12 @@ private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx
         }
         if (!osx) {
             /* it's not from cname so mfilename and dir must be the same */
-            if (!strcmp(dir->d_m_name, mfilename)) {
-                return dir->d_u_name;
+            if (strcmp(cfrombstring(dir->d_m_name), mfilename) == 0) {
+                return cfrombstring(dir->d_u_name);
             }
-        } 
-        else {
-           return demangle_checks (vol, dir->d_u_name, mfilename, prefix, t);
-       }
+        } else {
+            return demangle_checks(vol, cfrombstring(dir->d_u_name), mfilename, prefix, t);
+        }
     }
     else if (NULL != (u_name = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
         if (id != did) {
index 49c014ccef9befec7be7f15068e5c7039e757139..a6359ad29329373e5db2fdb7b673cfbf3fee6241 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: ofork.c,v 1.32 2010-03-12 15:16:49 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.
 #include <string.h>
 #include <sys/stat.h> /* works around a bug */
 #include <sys/param.h>
-#include <atalk/logger.h>
 #include <errno.h>
 
+#include <atalk/logger.h>
 #include <atalk/util.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include "globals.h"
 #include "volume.h"
@@ -32,9 +34,9 @@
 #define OFORK_HASHSIZE  64
 static struct ofork     *ofork_table[OFORK_HASHSIZE];
 
-static struct ofork    **oforks = NULL;
-static int             nforks = 0;
-static u_short         lastrefnum = 0;
+static struct ofork **oforks = NULL;
+static int          nforks = 0;
+static u_short      lastrefnum = 0;
 
 
 /* OR some of each character for the hash*/
@@ -45,7 +47,7 @@ static unsigned long hashfn(const struct file_key *key)
     while (*name) {
         i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
     }
-#endif    
+#endif
     return key->inode & (OFORK_HASHSIZE - 1);
 }
 
@@ -72,7 +74,7 @@ static void of_unhash(struct ofork *of)
 #ifdef DEBUG1
 void of_pforkdesc( FILE *f)
 {
-    int        ofrefnum;
+    int ofrefnum;
 
     if (!oforks)
         return;
@@ -87,14 +89,14 @@ void of_pforkdesc( FILE *f)
 
 int of_flush(const struct vol *vol)
 {
-    int        refnum;
+    int refnum;
 
     if (!oforks)
         return 0;
 
     for ( refnum = 0; refnum < nforks; refnum++ ) {
         if (oforks[ refnum ] != NULL && (oforks[refnum]->of_vol == vol) &&
-                flushfork( oforks[ refnum ] ) < 0 ) {
+            flushfork( oforks[ refnum ] ) < 0 ) {
             LOG(log_error, logtype_afpd, "of_flush: %s", strerror(errno) );
         }
     }
@@ -111,24 +113,24 @@ int of_rename(const struct vol *vol,
 
     if (!s_of)
         return AFP_OK;
-        
+
     next = ofork_table[hashfn(&s_of->key)];
     while ((of = next)) {
         next = next->next; /* so we can unhash and still be all right. */
 
         if (vol == of->of_vol && olddir == of->of_dir &&
-                s_of->key.dev == of->key.dev && 
-                s_of->key.inode == of->key.inode ) {
-           if (!done) {
-               strlcpy( of_name(of), newpath, of->of_ad->ad_m_namelen);
-               done = 1;
-           }
+            s_of->key.dev == of->key.dev &&
+            s_of->key.inode == of->key.inode ) {
+            if (!done) {
+                strlcpy( of_name(of), newpath, of->of_ad->ad_m_namelen);
+                done = 1;
+            }
             if (newdir != olddir) {
                 of->of_d_prev->of_d_next = of->of_d_next;
                 of->of_d_next->of_d_prev = of->of_d_prev;
                 if (of->of_dir->d_ofork == of) {
                     of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
-                }          
+                }
                 of->of_dir = newdir;
                 if (!(d_ofork = newdir->d_ofork)) {
                     newdir->d_ofork = of;
@@ -146,25 +148,25 @@ int of_rename(const struct vol *vol,
     return AFP_OK;
 }
 
-#define min(a,b)       ((a)<(b)?(a):(b))
+#define min(a,b)    ((a)<(b)?(a):(b))
 
 struct ofork *
 of_alloc(struct vol *vol,
-    struct dir    *dir,
-    char          *path,
-    u_int16_t     *ofrefnum,
-    const int      eid,
-    struct adouble *ad,
-    struct stat    *st)
+         struct dir    *dir,
+         char      *path,
+         u_int16_t     *ofrefnum,
+         const int      eid,
+         struct adouble *ad,
+         struct stat    *st)
 {
     struct ofork        *of, *d_ofork;
-    u_int16_t          refnum, of_refnum;
+    u_int16_t       refnum, of_refnum;
 
-    int                        i;
+    int         i;
 
     if (!oforks) {
         nforks = getdtablesize() - 10;
-       /* protect against insane ulimit -n */
+        /* protect against insane ulimit -n */
         nforks = min(nforks, 0xffff);
         oforks = (struct ofork **) calloc(nforks, sizeof(struct ofork *));
         if (!oforks)
@@ -180,22 +182,22 @@ of_alloc(struct vol *vol,
         }
     }
     /* grr, Apple and their 'uniquely identifies'
-          the next line is a protection against 
-          of_alloc()
-             refnum % nforks = 3 
-             lastrefnum = 3
-             oforks[3] != NULL 
-             refnum = 4
-             oforks[4] == NULL
-             return 4
-         
-          close(oforks[4])
-      
-          of_alloc()
-             refnum % nforks = 4
-             ...
-             return 4
-         same if lastrefnum++ rather than ++lastrefnum. 
+       the next line is a protection against
+       of_alloc()
+       refnum % nforks = 3
+       lastrefnum = 3
+       oforks[3] != NULL
+       refnum = 4
+       oforks[4] == NULL
+       return 4
+
+       close(oforks[4])
+
+       of_alloc()
+       refnum % nforks = 4
+       ...
+       return 4
+       same if lastrefnum++ rather than ++lastrefnum.
     */
     lastrefnum = refnum;
     if ( i == nforks ) {
@@ -205,7 +207,7 @@ of_alloc(struct vol *vol,
 
     of_refnum = refnum % nforks;
     if (( oforks[ of_refnum ] =
-                (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
+          (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
         LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
         return NULL;
     }
@@ -279,16 +281,23 @@ struct ofork *of_find(const u_int16_t ofrefnum )
 }
 
 /* -------------------------- */
-int of_stat  (struct path *path)
+int of_stat(struct path *path)
 {
-int ret;
+    int ret;
+
     path->st_errno = 0;
     path->st_valid = 1;
-    if ((ret = lstat(path->u_name, &path->st)) < 0)
+
+    if ((ret = lstat(path->u_name, &path->st)) < 0) {
+        LOG(log_debug, logtype_afpd, "of_stat('%s/%s': %s)",
+            cfrombstring(curdir->d_fullpath), path->u_name, strerror(errno));
        path->st_errno = errno;
-   return ret;
+    }
+
+    return ret;
 }
 
+
 #ifdef HAVE_RENAMEAT
 int of_fstatat(int dirfd, struct path *path)
 {
@@ -309,10 +318,12 @@ int of_fstatat(int dirfd, struct path *path)
    stat(".") works even if "." is deleted thus
    we have to stat ../name because we want to know if it's there
 */
-int of_statdir  (struct vol *vol, struct path *path)
+int of_statdir(struct vol *vol, struct path *path)
 {
-static char pathname[ MAXPATHLEN + 1] = "../";
-int ret;
+    static char pathname[ MAXPATHLEN + 1] = "../";
+    int ret;
+    size_t len;
+    struct dir *dir;
 
     if (*path->m_name) {
         /* not curdir */
@@ -321,20 +332,28 @@ int ret;
     path->st_errno = 0;
     path->st_valid = 1;
     /* FIXME, what about: we don't have r-x perm anymore ? */
-    strlcpy(pathname +3, path->d_dir->d_u_name, sizeof (pathname) -3);
+    len = blength(path->d_dir->d_u_name);
+    if (len > (MAXPATHLEN - 3))
+        len = MAXPATHLEN - 3;
+    strncpy(pathname + 3, cfrombstring(path->d_dir->d_u_name), len + 1);
+
+    LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
 
     if (!(ret = lstat(pathname, &path->st)))
         return 0;
-        
+
     path->st_errno = errno;
+
     /* hmm, can't stat curdir anymore */
-    if (errno == EACCES && curdir->d_parent ) {
-       if (movecwd(vol, curdir->d_parent)) 
+    if (errno == EACCES && (dir = dirlookup(vol, curdir->d_pdid))) {
+       if (movecwd(vol, dir)) 
            return -1;
        path->st_errno = 0;
-       if ((ret = lstat(path->d_dir->d_u_name, &path->st)) < 0) 
+
+       if ((ret = lstat(cfrombstring(path->d_dir->d_u_name), &path->st)) < 0) 
            path->st_errno = errno;
     }
+
     return ret;
 }
 
@@ -343,11 +362,11 @@ struct ofork *of_findname(struct path *path)
 {
     struct ofork *of;
     struct file_key key;
-    
+
     if (!path->st_valid) {
-       of_stat(path);
+        of_stat(path);
     }
-       
+
     if (path->st_errno)
         return NULL;
 
@@ -431,12 +450,12 @@ void of_dealloc( struct ofork *of)
 int of_closefork(struct ofork *ofork)
 {
     struct timeval      tv;
-    int                        adflags, doflush = 0;
+    int         adflags, doflush = 0;
     int                 ret;
 
     adflags = 0;
     if ((ofork->of_flags & AFPFORK_DATA) && (ad_data_fileno( ofork->of_ad ) != -1)) {
-            adflags |= ADFLAGS_DF;
+        adflags |= ADFLAGS_DF;
     }
     if ( (ofork->of_flags & AFPFORK_OPEN) && ad_reso_fileno( ofork->of_ad ) != -1 ) {
         adflags |= ADFLAGS_HF;
@@ -447,10 +466,10 @@ int of_closefork(struct ofork *ofork)
             ad_refresh( ofork->of_ad );
             if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
                 ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec);
-               doflush++;
+                doflush++;
             }
             if ( doflush ) {
-                 ad_flush( ofork->of_ad );
+                ad_flush( ofork->of_ad );
             }
         }
     }
@@ -458,14 +477,14 @@ int of_closefork(struct ofork *ofork)
     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
         ret = -1;
     }
+
     of_dealloc( ofork );
     return ret;
 }
 
 /* ----------------------
 
-*/
+ */
 struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *ad)
 {
     struct ofork        *of;
@@ -480,12 +499,12 @@ struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *
     return adp;
 }
 
-/* ---------------------- 
+/* ----------------------
    close all forks for a volume
 */
 void of_closevol(const struct vol *vol)
 {
-    int        refnum;
+    int refnum;
 
     if (!oforks)
         return;
index 2e2c69e01212f56e25df986d83c46e478c42ad55..54acae071e31285c52cc53d9ae243e02704f0d32 100644 (file)
@@ -46,7 +46,7 @@
 #include "filedir.h"
 #include "status.h"
 #include "misc.h"
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 #include "acls.h"
 #endif
 
index 57491a7184a200f17b48a2e5a1f9d9959e84f958..2822221e0d9cd145932ceb38802640ce48646447 100644 (file)
@@ -44,12 +44,10 @@ char *strchr (), *strrchr ();
 #include "volume.h"
 #include "unix.h"
 #include "fork.h"
-
-#ifdef HAVE_NFSv4_ACLS
-extern void acltoownermode(char *path, struct stat *st,uid_t uid, struct maccess *ma);
+#ifdef HAVE_ACLS
+#include "acls.h"
 #endif
 
-
 /*
  * Get the free space on a partition.
  */
@@ -172,9 +170,8 @@ mode_t mode;
  * dir parameter is used by AFS
  */
 void accessmode(char *path, struct maccess *ma, struct dir *dir _U_, struct stat *st)
-
 {
-struct stat     sb;
+    struct stat     sb;
 
     ma->ma_user = ma->ma_owner = ma->ma_world = ma->ma_group = 0;
     if (!st) {
@@ -183,7 +180,7 @@ struct stat     sb;
         st = &sb;
     }
     utommode( st, ma );
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     /* 10.5 Finder looks at OS 9 mode, so we must do some mapping */
     acltoownermode( path, st, uuid, ma);
 #endif
index 9a5a4dc771e036443c694b2d3428b8d50843261d..bf90e9a6008997bde965d3dd053c6a8c945ffe71 100644 (file)
@@ -35,6 +35,9 @@ char *strchr (), *strrchr ();
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+
+#include <uuid/uuid.h>
+
 #include <atalk/asp.h>
 #include <atalk/dsi.h>
 #include <atalk/adouble.h>
@@ -74,6 +77,10 @@ extern int afprun(int root, char *cmd, int *outfd);
 #endif /* BYTE_ORDER == BIG_ENDIAN */
 #endif /* ! NO_LARGE_VOL_SUPPORT */
 
+#ifndef UUID_PRINTABLE_STRING_LENGTH
+#define UUID_PRINTABLE_STRING_LENGTH 37
+#endif
+
 static struct vol *Volumes = NULL;
 static u_int16_t    lastvid = 0;
 static char     *Trash = "\02\024Network Trash Folder";
@@ -173,7 +180,10 @@ static void volfree(struct vol_option *options, const struct vol_option *save)
 }
 
 
-/* handle variable substitutions. here's what we understand:
+#define is_var(a, b) (strncmp((a), (b), 2) == 0)
+
+/*
+ * Handle variable substitutions. here's what we understand:
  * $b   -> basename of path
  * $c   -> client ip/appletalk address
  * $d   -> volume pathname on server
@@ -187,17 +197,37 @@ static void volfree(struct vol_option *options, const struct vol_option *save)
  * $z   -> zone (may not exist)
  * $$   -> $
  *
+ * This get's called from readvolfile with
+ * path = NULL, volname = NULL for xlating the volumes path
+ * path = path, volname = NULL for xlating the volumes name
+ * ... and from volumes options parsing code when xlating eg dbpath with
+ * path = path, volname = volname
  *
+ * Using this information we can reject xlation of any variable depeninding on a login
+ * context which is not given in the afp master, where we must evaluate this whole stuff
+ * too for the Zeroconf announcements.
  */
-#define is_var(a, b) (strncmp((a), (b), 2) == 0)
-
-static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
-                      char *src, struct passwd *pwd, char *path, char *volname)
+static char *volxlate(AFPObj *obj,
+                      char *dest,
+                      size_t destlen,
+                      char *src,
+                      struct passwd *pwd,
+                      char *path,
+                      char *volname)
 {
     char *p, *r;
     const char *q;
     int len;
     char *ret;
+    int afpmaster = 0;
+    int xlatevolname = 0;
+
+    if (! ((DSI *)obj->handle)->child)
+        afpmaster = 1;
+
+    if (path && !volname)
+        /* cf above */
+        xlatevolname = 1;
 
     if (!src) {
         return NULL;
@@ -224,6 +254,8 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
         /* now figure out what the variable is */
         q = NULL;
         if (is_var(p, "$b")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (path) {
                 if ((q = strrchr(path, '/')) == NULL)
                     q = path;
@@ -231,6 +263,8 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
                     q++;
             }
         } else if (is_var(p, "$c")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (obj->proto == AFPPROTO_ASP) {
                 ASP asp = obj->handle;
 
@@ -248,18 +282,26 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
                 destlen -= len;
             }
         } else if (is_var(p, "$d")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             q = path;
-        } else if (is_var(p, "$f")) {
+        } else if (pwd && is_var(p, "$f")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if ((r = strchr(pwd->pw_gecos, ',')))
                 *r = '\0';
             q = pwd->pw_gecos;
-        } else if (is_var(p, "$g")) {
+        } else if (pwd && is_var(p, "$g")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             struct group *grp = getgrgid(pwd->pw_gid);
             if (grp)
                 q = grp->gr_name;
         } else if (is_var(p, "$h")) {
             q = obj->options.hostname;
         } else if (is_var(p, "$i")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (obj->proto == AFPPROTO_ASP) {
                 ASP asp = obj->handle;
 
@@ -278,13 +320,17 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
                 q = obj->options.server;
             } else
                 q = obj->options.hostname;
-        } else if (is_var(p, "$u")) {
+        } else if (obj->username && is_var(p, "$u")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             char* sep = NULL;
             if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
                 q = sep+1;
             else
                 q = obj->username;
         } else if (is_var(p, "$v")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (volname) {
                 q = volname;
             }
@@ -457,7 +503,11 @@ static void volset(struct vol_option *options, struct vol_option *save,
                 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_CACHE;
             else if (strcasecmp(p, "tm") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
-
+/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
+#if 0
+            else if (strcasecmp(p, "cdrom") == 0)
+                options[VOLOPT_FLAGS].i_value |= AFPVOL_CDROM | AFPVOL_RO;
+#endif
             p = strtok(NULL, ",");
         }
 
@@ -807,6 +857,19 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
         check_ea_sys_support(volume);
     initvol_vfs(volume);
 
+    /* get/store uuid from file */
+    if (volume->v_flags & AFPVOL_TM) {
+        char *uuid = get_uuid(obj, volume->v_localname);
+        if (!uuid) {
+            LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
+                volume->v_localname);
+        } else {
+            volume->v_uuid = uuid;
+            LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
+                volume->v_localname, volume->v_uuid);
+        }
+    }
+
     volume->v_next = Volumes;
     Volumes = volume;
     return 0;
@@ -1074,7 +1137,10 @@ static int volfile_changed(struct afp_volume_name *p)
 
 /* ----------------------
  * Read a volume configuration file and add the volumes contained within to
- * the global volume list.  If p2 is non-NULL, the file that is opened is
+ * the global volume list. This gets called from the forked afpd childs.
+ * The master now reads this too for Zeroconf announcements.
+ *
+ * If p2 is non-NULL, the file that is opened is
  * p1/p2
  *
  * Lines that begin with # and blank lines are ignored.
@@ -1082,8 +1148,9 @@ static int volfile_changed(struct afp_volume_name *p)
  *      <unix path> [<volume name>] [allow:<user>,<@group>,...] \
  *                           [codepage:<file>] [casefold:<num>]
  *      <extension> TYPE [CREATOR]
+ *
  */
-static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
+int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
 {
     FILE        *fp;
     char        path[MAXPATHLEN + 1];
@@ -1092,12 +1159,12 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
     char        buf[BUFSIZ];
     char        type[5], creator[5];
     char        *u, *p;
+    int         fd;
+    int         i;
     struct passwd   *pw;
     struct vol_option   save_options[VOLOPT_NUM];
     struct vol_option   options[VOLOPT_NUM];
-    int                 i;
     struct stat         st;
-    int                 fd;
 
     if (!p1->name)
         return -1;
@@ -1174,29 +1241,16 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
             /* send path through variable substitution */
             if (*path != '~') /* need to copy path to tmp */
                 strcpy(tmp, path);
-            if (!pwent)
+            if (!pwent && obj->username)
                 pwent = getpwnam(obj->username);
-            volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL);
+
+            if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
+                continue;
 
             /* this is sort of braindead. basically, i want to be
              * able to specify things in any order, but i don't want to
-             * re-write everything.
-             *
-             * currently we have options:
-             *   volname
-             *   codepage:x
-             *   casefold:x
-             *   allow:x,y,@z
-             *   deny:x,y,@z
-             *   rwlist:x,y,@z
-             *   rolist:x,y,@z
-             *   options:prodos,crlf,noadouble,ro...
-             *   dbpath:x
-             *   password:x
-             *   preexec:x
-             *
-             *   namemask:x,y,!z  (not implemented yet)
-             */
+             * re-write everything. */
+
             memcpy(options, save_options, sizeof(options));
             *volname = '\0';
 
@@ -1228,7 +1282,9 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
                     options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
 
                 /* do variable substitution for volname */
-                volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL);
+                if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL)
+                    continue;
+
                 creatvol(obj, pwent, path, tmp, options, p2 != NULL);
             }
             volfree(options, save_options);
@@ -1274,6 +1330,8 @@ static void volume_free(struct vol *vol)
     free(vol->v_forceuid);
     free(vol->v_forcegid);
 #endif /* FORCE_UIDGID */
+    if (vol->v_uuid)
+        free(vol->v_uuid);
 }
 
 /* ------------------------------- */
@@ -1669,6 +1727,12 @@ void load_volumes(AFPObj *obj)
         free_volumes();
     }
 
+    if (! ((DSI *)obj->handle)->child) {
+        LOG(log_debug, logtype_afpd, "load_volumes: AFP MASTER");
+    } else {
+        LOG(log_debug, logtype_afpd, "load_volumes: user: %s", obj->username);
+    }
+
     pwent = getpwnam(obj->username);
     if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent);
@@ -1837,10 +1901,32 @@ static int volume_openDB(struct vol *volume)
             volume->v_path, volume->v_cnidscheme);
     }
 
-    LOG(log_info, logtype_afpd, "CNID server %s:%s",
+    LOG(log_info, logtype_afpd, "CNID server: %s:%s",
         volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
         volume->v_cnidport ? volume->v_cnidport : Cnid_port);
-    
+
+#if 0
+/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
+
+    /* Legacy pre 2.1 way of sharing eg CD-ROM */
+    if (strcmp(volume->v_cnidscheme, "last") == 0) {
+        /* "last" is gone. We support it by switching to in-memory "tdb" */
+        volume->v_cnidscheme = strdup("tdb");
+        flags |= CNID_FLAG_MEMORY;
+    }
+
+    /* New way of sharing CD-ROM */
+    if (volume->v_flags & AFPVOL_CDROM) {
+        flags |= CNID_FLAG_MEMORY;
+        if (strcmp(volume->v_cnidscheme, "tdb") != 0) {
+            free(volume->v_cnidscheme);
+            volume->v_cnidscheme = strdup("tdb");
+            LOG(log_info, logtype_afpd, "Volume %s is ejectable, switching to scheme %s.",
+                volume->v_path, volume->v_cnidscheme);
+        }
+    }
+#endif
+
     volume->v_cdb = cnid_open(volume->v_dbpath ? volume->v_dbpath : volume->v_path,
                               volume->v_umask,
                               volume->v_cnidscheme,
@@ -1848,12 +1934,13 @@ static int volume_openDB(struct vol *volume)
                               volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
                               volume->v_cnidport ? volume->v_cnidport : Cnid_port);
 
-    if (!volume->v_cdb) {
+    if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) {
+        /* The first attempt failed and it wasn't yet an attempt to open in-memory */
         LOG(log_error, logtype_afpd, "Can't open volume \"%s\" CNID backend \"%s\" ",
             volume->v_path, volume->v_cnidscheme);
-        flags |= CNID_FLAG_MEMORY;
         LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
             volume->v_path);
+        flags |= CNID_FLAG_MEMORY;
         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
 #ifdef SERVERTEXT
         /* kill ourself with SIGUSR2 aka msg pending */
@@ -1870,11 +1957,11 @@ static int volume_openDB(struct vol *volume)
     return (!volume->v_cdb)?-1:0;
 }
 
-/* 
-   Check if the underlying filesystem supports EAs for ea:sys volumes.
-   If not, switch to ea:ad.
-   As we can't check (requires write access) on ro-volumes, we switch ea:auto
-   volumes that are options:ro to ea:none.
+/*
+  Check if the underlying filesystem supports EAs for ea:sys volumes.
+  If not, switch to ea:ad.
+  As we can't check (requires write access) on ro-volumes, we switch ea:auto
+  volumes that are options:ro to ea:none.
 */
 static void check_ea_sys_support(struct vol *vol)
 {
@@ -2039,7 +2126,7 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     if ((tmp = strdup(volume->v_path)) == NULL) {
         free(volume->v_path);
         return AFPERR_MISC;
-    } 
+    }
     free(volume->v_path);
     volume->v_path = tmp;
 #endif
@@ -2059,9 +2146,6 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         volume->max_filename = MACFILELEN;
     }
 
-    volume->v_dir = volume->v_root = NULL;
-    volume->v_hash = NULL;
-
     volume->v_flags |= AFPVOL_OPEN;
     volume->v_cdb = NULL;
 
@@ -2080,7 +2164,13 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     else if (*(vol_uname + 1) != '\0')
         vol_uname++;
 
-    if ((dir = dirnew(vol_mname, vol_uname) ) == NULL) {
+    if ((dir = dir_new(vol_mname,
+                       vol_uname,
+                       volume,
+                       DIRDID_ROOT_PARENT,
+                       DIRDID_ROOT,
+                       bfromcstr(volume->v_path))
+            ) == NULL) {
         free(vol_mname);
         LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
         ret = AFPERR_MISC;
@@ -2088,14 +2178,9 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     }
     free(vol_mname);
 
-    dir->d_did = DIRDID_ROOT;
-    dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
-    dir->d_m_name_ucs2 = strdup_w(volume->v_name);
-    volume->v_dir = volume->v_root = dir;
-    volume->v_curdir = NULL;
-    volume->v_hash = dirhash();
+    volume->v_root = dir;
+    curdir = dir;
 
-    curdir = volume->v_dir;
     if (volume_openDB(volume) < 0) {
         LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s",
             volume->v_path, volume->v_cnidscheme);
@@ -2134,16 +2219,15 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         }
         else {
             p = Trash;
-            cname( volume, volume->v_dir, &p );
+            cname( volume, volume->v_root, &p );
         }
         return( AFP_OK );
     }
 
 openvol_err:
-    if (volume->v_dir) {
-        hash_free( volume->v_hash);
-        dirfree( volume->v_dir );
-        volume->v_dir = volume->v_root = NULL;
+    if (volume->v_root) {
+        dir_free( volume->v_root );
+        volume->v_root = NULL;
     }
 
     volume->v_flags &= ~AFPVOL_OPEN;
@@ -2161,9 +2245,8 @@ static void closevol(struct vol *vol)
     if (!vol)
         return;
 
-    hash_free( vol->v_hash);
-    dirfree( vol->v_root );
-    vol->v_dir = NULL;
+    dir_free( vol->v_root );
+    vol->v_root = NULL;
     if (vol->v_cdb != NULL) {
         cnid_close(vol->v_cdb);
         vol->v_cdb = NULL;
@@ -2204,7 +2287,7 @@ static void deletevol(struct vol *vol)
     if ( ovol != NULL ) {
         /* Even if chdir fails, we can't say afp_closevol fails. */
         if ( chdir( ovol->v_path ) == 0 ) {
-            curdir = ovol->v_dir;
+            curdir = ovol->v_root;
         }
     }
 
@@ -2510,7 +2593,7 @@ static int create_special_folder (const struct vol *vol, const struct _special_f
             free(q);
             return (-1);
         }
-        
+
         ad_setname(&ad, folder->name);
 
         ad_getattr(&ad, &attr);
@@ -2544,3 +2627,111 @@ static void handle_special_folders (const struct vol * vol)
     }
 }
 
+const struct vol *getvolumes(void)
+{
+    return Volumes;
+}
+
+void unload_volumes_and_extmap(void)
+{
+    LOG(log_debug, logtype_afpd, "unload_volumes_and_extmap");
+    free_extmap();
+    free_volumes();
+}
+
+/* 
+ * Get a volumes UUID from the config file.
+ * If there is none, it is generated and stored there.
+ *
+ * Returns pointer to allocated storage on success, NULL on error.
+ */
+char *get_uuid(const AFPObj *obj, const char *volname)
+{
+    char *volname_conf;
+    char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p;
+    FILE *fp;
+    struct stat tmpstat;
+    int fd;
+    
+    if ((fp = fopen(obj->options.uuidconf, "r")) != NULL) {  /* read open? */
+        /* scan in the conf file */
+        while (fgets(buf, sizeof(buf), fp) != NULL) { 
+            p = buf;
+            while (p && isblank(*p))
+                p++;
+            if (!p || (*p == '#') || (*p == '\n'))
+                continue;                             /* invalid line */
+            if (*p == '"') {
+                p++;
+                if ((volname_conf = strtok( p, "\"" )) == NULL)
+                    continue;                         /* syntax error */
+            } else {
+                if ((volname_conf = strtok( p, " \t" )) == NULL)
+                    continue;                         /* syntax error: invalid name */
+            }
+            p = strchr(p, '\0');
+            p++;
+            if (*p == '\0')
+                continue;                             /* syntax error */
+            
+            if (strcmp(volname, volname_conf) != 0)
+                continue;                             /* another volume name */
+                
+            while (p && isblank(*p))
+                p++;
+
+            if (sscanf(p, "%36s", uuid) == 1 ) {
+                for (int i=0; uuid[i]; i++)
+                    uuid[i] = toupper(uuid[i]);
+                LOG(log_debug, logtype_afpd, "get_uuid('%s'): UUID: '%s'", volname, uuid);
+                fclose(fp);
+                return strdup(uuid);
+            }
+        }
+    }
+
+    if (fp)
+        fclose(fp);
+
+    /*  not found or no file, reopen in append mode */
+
+    if (stat(obj->options.uuidconf, &tmpstat)) {                /* no file */
+        if (( fd = creat(obj->options.uuidconf, 0644 )) < 0 ) {
+            LOG(log_error, logtype_atalkd, "ERROR: Cannot create %s (%s).",
+                obj->options.uuidconf, strerror(errno));
+            return NULL;
+        }
+        if (( fp = fdopen( fd, "w" )) == NULL ) {
+            LOG(log_error, logtype_atalkd, "ERROR: Cannot fdopen %s (%s).",
+                obj->options.uuidconf, strerror(errno));
+            close(fd);
+            return NULL;
+        }
+    } else if ((fp = fopen(obj->options.uuidconf, "a+")) == NULL) { /* not found */
+        LOG(log_error, logtype_afpd, "Cannot create or append to %s (%s).",
+            obj->options.uuidconf, strerror(errno));
+        return NULL;
+    }
+    fseek(fp, 0L, SEEK_END);
+    if(ftell(fp) == 0) {                     /* size = 0 */
+        fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
+        fprintf(fp, "# This file is auto-generated by afpd\n");
+        fprintf(fp, "# and stores UUIDs for TM volumes.\n\n");
+    } else {
+        fseek(fp, -1L, SEEK_END);
+        if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */
+    }                    
+    
+    /* generate uuid and write to file */
+    uuid_t id;
+    uuid_generate(id);
+    uuid_unparse(id, uuid);
+    for (int i=0; uuid[i]; i++)
+        uuid[i] = toupper(uuid[i]);
+    LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, uuid);
+
+    fprintf(fp, "\"%s\"\t%36s\n", volname, uuid);
+    fclose(fp);
+    
+    return strdup(uuid);
+}
index 63d090036bf713f955df7eecac383c772d7cbc4d..7402a9a450c9793c9d3119664175e5ba855704b1 100644 (file)
 #include "hash.h"
 #endif
 
-extern struct vol      *getvolbyvid (const u_int16_t);
+extern struct vol       *getvolbyvid (const u_int16_t);
 extern int              ustatfs_getvolspace (const struct vol *,
             VolSpace *, VolSpace *,
             u_int32_t *);
 extern void             setvoltime (AFPObj *, struct vol *);
 extern int              pollvoltime (AFPObj *);
 extern void             load_volumes (AFPObj *obj);
+extern int              readvolfile(AFPObj *obj,
+                                    struct afp_volume_name *p1,
+                                    char *p2,
+                                    int user,
+                                    struct passwd *pwent);
+extern const struct vol *getvolumes(void);
+extern void             unload_volumes_and_extmap(void);
+extern char             *get_uuid(const AFPObj *obj, const char *volname);
 
 /* FP functions */
 int afp_openvol      (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index 04e43420707684dcc3f5c974bcac4605094ddf32..0dae7be74f13ded2e1043f975422dafcb1303690 100644 (file)
@@ -264,6 +264,9 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask
 
     if (!cur_fd)
         return 0;
+
+    LOG(log_maxdebug, logtype_cnid, "comm_rcv: got data on fd %u", cur_fd);
+
     nametmp = rqst->name;
     if ((b = read(cur_fd, rqst, sizeof(struct cnid_dbd_rqst))) != sizeof(struct cnid_dbd_rqst)) {
         if (b)
@@ -282,6 +285,8 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask
        needs zero terminated strings. */
     rqst->name[rqst->namelen] = '\0';
 
+    LOG(log_maxdebug, logtype_cnid, "comm_rcv: got %u bytes", b + rqst->namelen);
+
     return 1;
 }
 
index 42cf28489f14e61c055a73a73eb9a75a74742347..a715c19a4cea5e1ad2b1cc048c0218cf96507413 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: main.c,v 1.16 2009-11-25 14:59:15 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (c) Frank Lahm 2009
  * All Rights Reserved.  See COPYING.
@@ -181,7 +179,7 @@ static int loop(struct db_param *dbp)
                 ret = -1;
                 break;
             }
-            
+
             if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
                 dbif_txn_abort(dbd);
                 return -1;
index 690005640a0dff0ec9449e2121798a8998302367..299d86ae9ddcd89f90797c54582939a55cdc7f66 100644 (file)
@@ -2,10 +2,10 @@
 
 atalkincludedir = $(includedir)/atalk
 atalkinclude_HEADERS = \
-       adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h \
+       adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h bstradd.h \
        cnid.h compat.h ddp.h dsi.h ldapconfig.h list.h logger.h \
-       nbp.h netddp.h pap.h paths.h rtmp.h server_child.h \
+       nbp.h netddp.h pap.h paths.h queue.h rtmp.h server_child.h \
        server_ipc.h tdb.h uam.h unicode.h util.h uuid.h volinfo.h \
        zip.h ea.h acl.h unix.h directory.h hash.h volume.h
 
-noinst_HEADERS = cnid_dbd_private.h cnid_private.h
+noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstrlib.h
index edb391598517ddb757f57607719cffd4cfdf340b..6b48e5522c1e365a187fb081f2275a81c2704c3d 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: acl.h,v 1.1 2009-10-14 15:04:01 franklahm Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
-#endif  /* HAVE_NFSv4_ACLS */
+#endif  /* HAVE_SOLARIS_ACLS */
 
-/* Solaris NFSv4 ACL stuff */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
 extern int get_nfsv4_acl(const char *name, ace_t **retAces);
-extern int remove_acl(const char *name);
-#endif /* HAVE_NFSv4_ACLS */
+#endif /* HAVE_SOLARIS_ACLS */
 
+extern int remove_acl_vfs(const char *name);
 
 #endif  /* ATALK_ACL_H */
diff --git a/include/atalk/bstradd.h b/include/atalk/bstradd.h
new file mode 100644 (file)
index 0000000..a402339
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+   $Id: bstradd.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+   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
+ * Additional functions for bstrlib.
+ */
+
+#ifndef ATALK_BSTRADD_H
+#define ATALK_BSTRADD_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <atalk/bstrlib.h>
+
+#define cfrombstring(b) ((char *)((b)->data))
+
+typedef struct tagbstring static_bstring;
+
+extern bstring brefcstr(const char *str);
+extern int bunrefcstr(bstring b);
+
+extern struct bstrList *bstListCreateMin(int min);
+extern int bstrListPush(struct bstrList *sl, bstring bs);
+extern bstring bstrListPop(struct bstrList *sl);
+extern bstring bjoinInv(const struct bstrList * bl, const_bstring sep);
+#endif /* ATALK_BSTRADD_H */
diff --git a/include/atalk/bstrlib.h b/include/atalk/bstrlib.h
new file mode 100644 (file)
index 0000000..858a3fa
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * This source file is part of the bstring string library.  This code was
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
+ */
+
+/*!
+ * @file
+ * This file is the core module for implementing the bstring functions.
+ */
+
+#ifndef BSTRLIB_INCLUDE
+#define BSTRLIB_INCLUDE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+
+#if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP)
+# if defined (__TURBOC__) && !defined (__BORLANDC__)
+#  define BSTRLIB_NOVSNP
+# endif
+#endif
+
+#define BSTR_ERR (-1)
+#define BSTR_OK (0)
+#define BSTR_BS_BUFF_LENGTH_GET (0)
+
+typedef struct tagbstring * bstring;
+typedef const struct tagbstring * const_bstring;
+
+/* Copy functions */
+#define cstr2bstr bfromcstr
+extern bstring bfromcstr (const char * str);
+extern bstring bfromcstralloc (int mlen, const char * str);
+extern bstring blk2bstr (const void * blk, int len);
+extern char * bstr2cstr (const_bstring s, char z);
+extern int bcstrfree (char * s);
+extern bstring bstrcpy (const_bstring b1);
+extern int bassign (bstring a, const_bstring b);
+extern int bassignmidstr (bstring a, const_bstring b, int left, int len);
+extern int bassigncstr (bstring a, const char * str);
+extern int bassignblk (bstring a, const void * s, int len);
+
+/* Destroy function */
+extern int bdestroy (bstring b);
+
+/* Space allocation hinting functions */
+extern int balloc (bstring s, int len);
+extern int ballocmin (bstring b, int len);
+
+/* Substring extraction */
+extern bstring bmidstr (const_bstring b, int left, int len);
+
+/* Various standard manipulations */
+extern int bconcat (bstring b0, const_bstring b1);
+extern int bconchar (bstring b0, char c);
+extern int bcatcstr (bstring b, const char * s);
+extern int bcatblk (bstring b, const void * s, int len);
+extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill);
+extern int binsertch (bstring s1, int pos, int len, unsigned char fill);
+extern int breplace (bstring b1, int pos, int len, const_bstring b2, unsigned char fill);
+extern int bdelete (bstring s1, int pos, int len);
+extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill);
+extern int btrunc (bstring b, int n);
+
+/* Scan/search functions */
+extern int bstricmp (const_bstring b0, const_bstring b1);
+extern int bstrnicmp (const_bstring b0, const_bstring b1, int n);
+extern int biseqcaseless (const_bstring b0, const_bstring b1);
+extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len);
+extern int biseq (const_bstring b0, const_bstring b1);
+extern int bisstemeqblk (const_bstring b0, const void * blk, int len);
+extern int biseqcstr (const_bstring b, const char * s);
+extern int biseqcstrcaseless (const_bstring b, const char * s);
+extern int bstrcmp (const_bstring b0, const_bstring b1);
+extern int bstrncmp (const_bstring b0, const_bstring b1, int n);
+extern int binstr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int bstrchrp (const_bstring b, int c, int pos);
+extern int bstrrchrp (const_bstring b, int c, int pos);
+#define bstrchr(b,c) bstrchrp ((b), (c), 0)
+#define bstrrchr(b,c) bstrrchrp ((b), (c), blength(b)-1)
+extern int binchr (const_bstring b0, int pos, const_bstring b1);
+extern int binchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos);
+extern int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos);
+
+/* List of string container functions */
+struct bstrList {
+    int qty, mlen;
+    bstring * entry;
+};
+extern struct bstrList * bstrListCreate (void);
+extern int bstrListDestroy (struct bstrList * sl);
+extern int bstrListAlloc (struct bstrList * sl, int msz);
+extern int bstrListAllocMin (struct bstrList * sl, int msz);
+
+/* String split and join functions */
+extern struct bstrList * bsplit (const_bstring str, unsigned char splitChar);
+extern struct bstrList * bsplits (const_bstring str, const_bstring splitStr);
+extern struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr);
+extern bstring bjoin (const struct bstrList * bl, const_bstring sep);
+extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm);
+extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm);
+extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm);
+
+/* Miscellaneous functions */
+extern int bpattern (bstring b, int len);
+extern int btoupper (bstring b);
+extern int btolower (bstring b);
+extern int bltrimws (bstring b);
+extern int brtrimws (bstring b);
+extern int btrimws (bstring b);
+
+#if !defined (BSTRLIB_NOVSNP)
+extern bstring bformat (const char * fmt, ...);
+extern int bformata (bstring b, const char * fmt, ...);
+extern int bassignformat (bstring b, const char * fmt, ...);
+extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist);
+
+#define bvformata(ret, b, fmt, lastarg) { \
+bstring bstrtmp_b = (b); \
+const char * bstrtmp_fmt = (fmt); \
+int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \
+       for (;;) { \
+               va_list bstrtmp_arglist; \
+               va_start (bstrtmp_arglist, lastarg); \
+               bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \
+               va_end (bstrtmp_arglist); \
+               if (bstrtmp_r >= 0) { /* Everything went ok */ \
+                       bstrtmp_r = BSTR_OK; \
+                       break; \
+               } else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \
+                       bstrtmp_r = BSTR_ERR; \
+                       break; \
+               } \
+               bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \
+       } \
+       ret = bstrtmp_r; \
+}
+
+#endif
+
+typedef int (*bNgetc) (void *parm);
+typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, void *parm);
+
+/* Input functions */
+extern bstring bgets (bNgetc getcPtr, void * parm, char terminator);
+extern bstring bread (bNread readPtr, void * parm);
+extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int breada (bstring b, bNread readPtr, void * parm);
+
+/* Stream functions */
+extern struct bStream * bsopen (bNread readPtr, void * parm);
+extern void * bsclose (struct bStream * s);
+extern int bsbufflength (struct bStream * s, int sz);
+extern int bsreadln (bstring b, struct bStream * s, char terminator);
+extern int bsreadlns (bstring r, struct bStream * s, const_bstring term);
+extern int bsread (bstring b, struct bStream * s, int n);
+extern int bsreadlna (bstring b, struct bStream * s, char terminator);
+extern int bsreadlnsa (bstring r, struct bStream * s, const_bstring term);
+extern int bsreada (bstring b, struct bStream * s, int n);
+extern int bsunread (struct bStream * s, const_bstring b);
+extern int bspeek (bstring r, const struct bStream * s);
+extern int bssplitscb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
+extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
+extern int bseof (const struct bStream * s);
+
+struct tagbstring {
+       int mlen;
+       int slen;
+       unsigned char * data;
+};
+
+/* Accessor macros */
+#define blengthe(b, e)      (((b) == (void *)0 || (b)->slen < 0) ? (int)(e) : ((b)->slen))
+#define blength(b)          (blengthe ((b), 0))
+#define bdataofse(b, o, e)  (((b) == (void *)0 || (b)->data == (void*)0) ? (char *)(e) : ((char *)(b)->data) + (o))
+#define bdataofs(b, o)      (bdataofse ((b), (o), (void *)0))
+#define bdatae(b, e)        (bdataofse (b, 0, e))
+#define bdata(b)            (bdataofs (b, 0))
+#define bchare(b, p, e)     ((((unsigned)(p)) < (unsigned)blength(b)) ? ((b)->data[(p)]) : (e))
+#define bchar(b, p)         bchare ((b), (p), '\0')
+
+/* Static constant string initialization macro */
+#define bsStaticMlen(q,m)   {(m), (int) sizeof(q)-1, (unsigned char *) ("" q "")}
+#if defined(_MSC_VER)
+# define bsStatic(q)        bsStaticMlen(q,-32)
+#endif
+#ifndef bsStatic
+# define bsStatic(q)        bsStaticMlen(q,-__LINE__)
+#endif
+
+/* Static constant block parameter pair */
+#define bsStaticBlkParms(q) ((void *)("" q "")), ((int) sizeof(q)-1)
+
+/* Reference building macros */
+#define cstr2tbstr btfromcstr
+#define btfromcstr(t,s) {                                            \
+    (t).data = (unsigned char *) (s);                                \
+    (t).slen = ((t).data) ? ((int) (strlen) ((char *)(t).data)) : 0; \
+    (t).mlen = -1;                                                   \
+}
+#define blk2tbstr(t,s,l) {            \
+    (t).data = (unsigned char *) (s); \
+    (t).slen = l;                     \
+    (t).mlen = -1;                    \
+}
+#define btfromblk(t,s,l) blk2tbstr(t,s,l)
+#define bmid2tbstr(t,b,p,l) {                                                \
+    const_bstring bstrtmp_s = (b);                                           \
+    if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) {              \
+        int bstrtmp_left = (p);                                              \
+        int bstrtmp_len  = (l);                                              \
+        if (bstrtmp_left < 0) {                                              \
+            bstrtmp_len += bstrtmp_left;                                     \
+            bstrtmp_left = 0;                                                \
+        }                                                                    \
+        if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left)                    \
+            bstrtmp_len = bstrtmp_s->slen - bstrtmp_left;                    \
+        if (bstrtmp_len <= 0) {                                              \
+            (t).data = (unsigned char *)"";                                  \
+            (t).slen = 0;                                                    \
+        } else {                                                             \
+            (t).data = bstrtmp_s->data + bstrtmp_left;                       \
+            (t).slen = bstrtmp_len;                                          \
+        }                                                                    \
+    } else {                                                                 \
+        (t).data = (unsigned char *)"";                                      \
+        (t).slen = 0;                                                        \
+    }                                                                        \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblkltrimws(t,s,l) {                                            \
+    int bstrtmp_idx = 0, bstrtmp_len = (l);                                  \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) {                   \
+            if (!isspace (bstrtmp_s[bstrtmp_idx])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s + bstrtmp_idx;                                      \
+    (t).slen = bstrtmp_len - bstrtmp_idx;                                    \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblkrtrimws(t,s,l) {                                            \
+    int bstrtmp_len = (l) - 1;                                               \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_len >= 0; bstrtmp_len--) {                            \
+            if (!isspace (bstrtmp_s[bstrtmp_len])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s;                                                    \
+    (t).slen = bstrtmp_len + 1;                                              \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblktrimws(t,s,l) {                                             \
+    int bstrtmp_idx = 0, bstrtmp_len = (l) - 1;                              \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) {                  \
+            if (!isspace (bstrtmp_s[bstrtmp_idx])) break;                    \
+        }                                                                    \
+        for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) {                  \
+            if (!isspace (bstrtmp_s[bstrtmp_len])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s + bstrtmp_idx;                                      \
+    (t).slen = bstrtmp_len + 1 - bstrtmp_idx;                                \
+    (t).mlen = -__LINE__;                                                    \
+}
+
+/* Write protection macros */
+#define bwriteprotect(t)     { if ((t).mlen >=  0) (t).mlen = -1; }
+#define bwriteallow(t)       { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); }
+#define biswriteprotected(t) ((t).mlen <= 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 17e5d08762468e2555d2b2396a5baba3e052da93..dd889dbb874018677e8dcc0861c4e2fe777101bf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.h,v 1.3 2010-01-10 10:58:25 franklahm Exp $
+ * $Id: directory.h,v 1.3.2.1 2010-02-01 10:56:08 franklahm Exp $
  *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
 #include <sys/types.h>
 #include <netatalk/endian.h>
 #include <dirent.h>
+#include <stdint.h>
 
 #include <atalk/cnid.h>
-#include <atalk/directory.h>
+#include <atalk/bstrlib.h>
+#include <atalk/queue.h>
 
 /* setgid directories */
 #ifndef DIRBITS
 # endif /* AFS */
 #endif /* DIRBITS */
 
-/* the did tree is now a red-black tree while the parent/child
- * tree is a circular doubly-linked list. how exciting. */
-struct dir {
-    struct dir *d_left, *d_right, *d_back; /* for red-black tree */
-    int                d_color;
-    struct dir  *d_parent, *d_child; /* parent-child */
-    struct dir  *d_prev, *d_next;    /* siblings */
-    void        *d_ofork;            /* oforks using this directory. */
-    u_int32_t   d_did;
-    int                d_flags;
+/* reserved directory id's */
+#define DIRDID_ROOT_PARENT    htonl(1)  /* parent directory of root */
+#define DIRDID_ROOT           htonl(2)  /* root directory */
 
+struct dir {
+    bstring     d_fullpath;          /* complete unix path to dir */
+    bstring     d_m_name;            /* mac name */
+    bstring     d_u_name;            /* unix name                                          */
+                                     /* be careful here! if d_m_name == d_u_name, d_u_name */
+                                     /* will just point to the same storage as d_m_name !! */
+    ucs2_t      *d_m_name_ucs2;       /* mac name as UCS2 */
+    qnode_t     *qidx_node;           /* pointer to position in queue index */
+    void        *d_ofork;             /* oforks using this directory. */
     time_t      ctime;                /* inode ctime */
-    u_int32_t   offcnt;               /* offspring count */
-
-    char       *d_m_name;            /* mac name */
-    char        *d_u_name;            /* unix name */
-    ucs2_t     *d_m_name_ucs2;       /* mac name as UCS2 */
+    int         d_flags;              /* directory flags */
+    cnid_t      d_pdid;               /* CNID of parent directory */
+    cnid_t      d_did;                /* CNID of directory */
+    uint32_t    offcnt;               /* offspring count */
+    uint16_t    d_vid;                /* only needed in the dircache, because
+                                         we put all directories in one cache. */
 };
 
 struct path {
     int         m_type;             /* mac name type (long name, unicode */
-    char       *m_name;            /* mac name */
+    char        *m_name;            /* mac name */
     char        *u_name;            /* unix name */
-    cnid_t     id;                 /* file id (only for getmetadata) */
+    cnid_t      id;                 /* file id (only for getmetadata) */
     struct dir  *d_dir;             /* */
     int         st_valid;           /* does st_errno and st set */
     int         st_errno;
@@ -83,5 +88,4 @@ static inline int path_isadir(struct path *o_path)
 #endif
 }
 
-
 #endif /* ATALK_DIRECTORY_H */
index 82da7a1595e517b0b0eea4ad761b4cb68d9dbd43..0616774da18da49e2afb74ccc971bd13d40b6d19 100644 (file)
@@ -94,6 +94,10 @@ typedef struct DSI {
   char srvloc_url[512];
 #endif 
 
+#ifdef USE_ZEROCONF
+  int zeroconf_registered;
+#endif
+
   /* buffer for OSX deadlock */
   char *buffer;
   char *start;
index 00d987de77b27bd37fcc69d3949e65416af386b9..c0165a1a257f210999055d8a271bc81dadf9e2a8 100644 (file)
@@ -20,7 +20,7 @@
 #include <config.h>
 #endif
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 #endif
 
index 2fb2ca292113c784141cf3a8f31d0169f7356852..70f9f628c7c61c0c0fe7018b91bfceb884865f43 100644 (file)
@@ -1,4 +1,4 @@
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 
 #ifndef LDAPCONFIG_H
 #define LDAPCONFIG_H
@@ -37,6 +37,6 @@ extern struct ldap_pref ldap_prefs[];
 extern struct pref_array prefs_array[];
 extern int ldap_config_valid;
 
-#endif
+#endif /* LDAPCONFIG_H */
 
-#endif
+#endif /* HAVE_ACLS */
diff --git a/include/atalk/queue.h b/include/atalk/queue.h
new file mode 100644 (file)
index 0000000..7ce4977
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+   Copyright (c) 2010 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.
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef ATALK_QUEUE_H
+#define ATALK_QUEUE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+typedef struct qnode {
+    struct qnode *prev;
+    struct qnode *next;
+    void *data;
+} qnode_t;
+
+typedef qnode_t q_t;
+
+extern q_t *queue_init(void);
+extern void queue_destroy(q_t *q, void (*callback)(void *));
+#define queue_free(q) queue_destroy((q), free)
+extern qnode_t *enqueue(q_t *q, void *data);
+extern void *dequeue(q_t *q);
+
+#endif  /* ATALK_QUEUE_H */
index 0d1f59ae8f1c502811bc4db2edd02ab4a9d2af18..3f10132121a62f7ca79f734477f191e9032b739f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: util.h,v 1.21 2010-02-28 22:29:16 didg Exp $
+ * $Id: util.h,v 1.21 2010/02/28 22:29:16 didg Exp $
  */
 
 /*!
 #define EXITERR_CONF 2  /* error in config files/cmd line parameters */
 #define EXITERR_SYS  3  /* local system error */
 
+/* Print a SBT and exit */
+#define AFP_PANIC(why) \
+    do {                                            \
+        netatalk_panic(why);                        \
+        abort();                                    \
+    } while(0);
+
+/* LOG assert errors */
+#ifndef NDEBUG
+#define AFP_ASSERT(b) \
+    do {                                                                \
+        if (!(b)) {                                                     \
+            AFP_PANIC(#b);                                              \
+        } \
+    } while(0);
+#else
+#define AFP_ASSERT(b)
+#endif /* NDEBUG */
 
 #ifdef WITH_SENDFILE
 extern ssize_t sys_sendfile (int __out_fd, int __in_fd, off_t *__offset,size_t __count);
@@ -44,9 +62,9 @@ extern int atalk_aton     (char *, struct at_addr *);
 extern void bprint        (char *, int);
 extern int strdiacasecmp  (const char *, const char *);
 extern int strndiacasecmp (const char *, const char *, size_t);
-extern pid_t server_lock  (char * /*program*/, char * /*file*/, 
-                              int /*debug*/);
+extern pid_t server_lock  (char * /*program*/, char * /*file*/, int /*debug*/);
 extern void fault_setup          (void (*fn)(void *));
+extern void netatalk_panic(const char *why);
 #define server_unlock(x)  (unlink(x))
 
 /* strlcpy and strlcat are used by pam modules */
index 25db56ec940217a78b14006362ae284a73e87337..7c28603a6c0f573e96506383f40668ba6b15cd0a 100644 (file)
@@ -20,7 +20,7 @@
 #define UUID_STRINGSIZE 36
 
 typedef char *uuidp_t;
-typedef char uuid_t[UUID_BINSIZE];
+typedef char atalk_uuid_t[UUID_BINSIZE];
 
 typedef enum {UUID_USER = 1, UUID_GROUP} uuidtype_t;
 extern char *uuidtype[];
index f1b2084b0c9fb6ebf47e01103fc898d20f110741..993b1a746ecd9b3295d4f33074ef5a063119e1d5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.h,v 1.17 2010-04-10 08:24:54 franklahm Exp $
+ * $Id: volume.h,v 1.16 2010/03/31 09:47:32 franklahm Exp $
  *
  * Copyright (c) 1990,1994 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -27,9 +27,7 @@ struct vol {
     u_int16_t       v_vid;
     int             v_flags;
     char            *v_path;
-    struct dir      *v_dir, *v_root;
-    struct dir      *v_curdir;  /* cache */
-    hash_t          *v_hash;
+    struct dir      *v_root;
     time_t          v_mtime;
 
     charset_t       v_volcharset;
@@ -87,7 +85,7 @@ struct vol {
     char            *v_postexec;
     int             v_root_preexec_close;
     int             v_preexec_close;
-
+    char            *v_uuid;    /* For TimeMachine zeroconf record */
 #ifdef FORCE_UIDGID
     char            *v_forceuid;
     char            *v_forcegid;
@@ -108,7 +106,7 @@ struct vol {
 
 /*
   Flags that alter volume behaviour.
-  Keep in sync with include/atalk/volinfo.h and libatalk/util/volinfo.c
+  Keep in sync with libatalk/util/volinfo.c
 */
 #define AFPVOL_A2VOL     (1 << 5)   /* prodos volume */
 #define AFPVOL_CRLF      (1 << 6)   /* cr/lf translation */
@@ -137,6 +135,7 @@ struct vol {
 #define AFPVOL_INV_DOTS  (1 << 22)   /* dots files are invisible */
 #define AFPVOL_TM        (1 << 24)   /* Supports TimeMachine */
 #define AFPVOL_ACLS      (1 << 25)   /* Volume supports ACLS */
+#define AFPVOL_CDROM     (1 << 25)   /* Ejectable media eg CD -> in memory CNID db */
 
 /* Extended Attributes vfs indirection  */
 #define AFPVOL_EA_NONE           0   /* No EAs */
index 5fcf448399e1064f000a9860cfa09d8f59281bba..e51940e3e0547dbea1596d1d081ff87374d6815c 100644 (file)
@@ -1,7 +1,7 @@
 
 # Makefile.am for libatalk/
 
-SUBDIRS = acl adouble asp atp compat cnid dsi nbp netddp tdb util unicode vfs
+SUBDIRS = adouble asp atp bstring compat cnid dsi nbp netddp tdb util unicode vfs
 
 lib_LTLIBRARIES = libatalk.la
 
@@ -11,6 +11,7 @@ libatalk_la_LIBADD  = \
        adouble/libadouble.la   \
        asp/libasp.la           \
        atp/libatp.la           \
+       bstring/libbstring.la \
        compat/libcompat.la     \
        dsi/libdsi.la           \
        nbp/libnbp.la           \
@@ -18,12 +19,13 @@ libatalk_la_LIBADD  = \
        util/libutil.la         \
        tdb/libtdb.la       \
        unicode/libunicode.la \
-       vfs/libvfs.la @LIBATALK_ACLS@
+       vfs/libvfs.la
 
 libatalk_la_DEPENDENCIES = \
        adouble/libadouble.la   \
        asp/libasp.la           \
        atp/libatp.la           \
+       bstring/libbstring.la \
        compat/libcompat.la     \
        dsi/libdsi.la           \
        nbp/libnbp.la           \
@@ -31,7 +33,13 @@ libatalk_la_DEPENDENCIES = \
        util/libutil.la         \
        tdb/libtdb.la       \
        unicode/libunicode.la \
-       vfs/libvfs.la @LIBATALK_ACLS@
+       vfs/libvfs.la
+
+if HAVE_ACLS
+SUBDIRS += acl
+libatalk_la_DEPENDENCIES += acl/libacl.la
+libatalk_la_LIBADD += acl/libacl.la
+endif
 
 libatalk_la_LDFLAGS = -static
 
index 9e9e63adf0202844858c05159253c4c282014bd7..d9c81990a88026bdcbe540e0ed2dbad7f7ba2744 100644 (file)
@@ -2,8 +2,7 @@
 
 noinst_HEADERS = aclldap.h cache.h
 
-if USE_NFSv4_ACLS
-
+if HAVE_ACLS
 noinst_LTLIBRARIES = libacl.la
 libacl_la_SOURCES = \
        ldap.c          \
@@ -11,6 +10,5 @@ libacl_la_SOURCES = \
        cache.c         \
        ldap_config.c
 libacl_la_LDFLAGS = -lldap
-
 endif
 
index 99f59849272fdff3e45b3aade719a0e3493fcf56..519113ebf704ac2b855bb2c1eb916c6015684ee3 100644 (file)
@@ -103,7 +103,7 @@ static unsigned char hashstring(unsigned char *str) {
     return index;
 }
 
-/* hash uuid_t into unsigned char */
+/* hash atalk_uuid_t into unsigned char */
 static unsigned char hashuuid(uuidp_t uuid) {
     unsigned char index = 83;
     int i;
index c302751b6eb850127b0689dd3bb9c4473eec8b0e..b7c160c169712249818ae65b3eb4c3d200cdb712 100644 (file)
@@ -17,7 +17,7 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 
 #include <stdio.h>
 #include <errno.h>
@@ -145,4 +145,4 @@ int acl_ldap_readconfig(char *name)
     fclose(f);
     return 0;
 }
-#endif
+#endif /* HAVE_ACLS */
diff --git a/libatalk/bstring/.gitignore b/libatalk/bstring/.gitignore
new file mode 100644 (file)
index 0000000..0d0371d
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.lo
+*.la
+.deps
+.libs
diff --git a/libatalk/bstring/Makefile.am b/libatalk/bstring/Makefile.am
new file mode 100644 (file)
index 0000000..39f78c3
--- /dev/null
@@ -0,0 +1,6 @@
+# Makefile.am for libatalk/adouble/
+
+noinst_LTLIBRARIES = libbstring.la
+
+libbstring_la_SOURCES = bstrlib.c bstradd.c
+
diff --git a/libatalk/bstring/bstradd.c b/libatalk/bstring/bstradd.c
new file mode 100644 (file)
index 0000000..9f617eb
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+  $Id: bstradd.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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
+ * Additional functions for bstrlib
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/bstrlib.h>
+
+/* Optionally include a mechanism for debugging memory */
+
+#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
+#include "memdbg.h"
+#endif
+
+#ifndef bstr__alloc
+#define bstr__alloc(x) malloc (x)
+#endif
+
+#ifndef bstr__free
+#define bstr__free(p) free (p)
+#endif
+
+#ifndef bstr__realloc
+#define bstr__realloc(p,x) realloc ((p), (x))
+#endif
+
+#ifndef bstr__memcpy
+#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
+#endif
+
+#ifndef bstr__memmove
+#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
+#endif
+
+#ifndef bstr__memset
+#define bstr__memset(d,c,l) memset ((d), (c), (l))
+#endif
+
+#ifndef bstr__memcmp
+#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
+#endif
+
+#ifndef bstr__memchr
+#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
+#endif
+
+/*************************************************************************
+ * Stuff for making bstrings referencing c-strings
+ ************************************************************************/
+
+/*!
+ * @brief Create a bstring referencing "str"
+ *
+ * This is usefull if a well know code path uses string, often doing strlen on string.
+ * By converting to bstring which carries the strlen, the repeated computation can be avoided.
+ */
+bstring brefcstr (const char *str) {
+    bstring b;
+    int i;
+    size_t j;
+
+       if (str == NULL)
+        return NULL;
+       j = strlen(str);
+
+       b = (bstring)bstr__alloc(sizeof(struct tagbstring));
+       if (NULL == b)
+        return NULL;
+
+       b->slen = (int) j;
+    b->mlen = -1;
+    b->data = str;
+
+       return b;
+}
+
+/*!
+ * @brief Free up the bstring, WITHOUT freeing the pointed to c-string!
+ */
+int bunrefcstr (bstring b) {
+       if (b == NULL || b->slen < 0 || b->mlen > 0 || b->data == NULL)
+               return BSTR_ERR;
+
+       /* In case there is any stale usage, there is one more chance to 
+          notice this error. */
+
+       b->slen = -1;
+       b->mlen = -__LINE__;
+       b->data = NULL;
+
+       bstr__free (b);
+       return BSTR_OK;
+}
+
+/*************************************************************************
+ * stuff for bstrList
+ ************************************************************************/
+
+/*!
+ * @brief Create an empty list with preallocated storage for at least 'min' members
+ */
+struct bstrList *bstListCreateMin(int min)
+{
+    struct bstrList *sl = NULL;
+
+    if ((sl = bstrListCreate()) == NULL)
+        return NULL;
+
+    if ((bstrListAlloc(sl, min)) != BSTR_OK) {
+        bstrListDestroy(sl);
+        return NULL;
+    }
+
+    return sl;
+}
+
+/*!
+ * @brief Push a bstring to the end of a list
+ */
+int bstrListPush(struct bstrList *sl, bstring bs)
+{
+    if (sl->qty == sl->mlen) {
+        if ((bstrListAlloc(sl, sl->qty + 1)) != BSTR_OK)
+            return BSTR_ERR;
+    }
+
+    sl->entry[sl->qty] = bs;
+    sl->qty++;
+    return BSTR_OK;
+}
+
+/*!
+ * @brief Pop a bstring from the end of a list
+ */
+bstring bstrListPop(struct bstrList *sl)
+{
+    return NULL;
+}
+
+/*!
+ * @brief Inverse bjoin
+ */
+bstring bjoinInv(const struct bstrList * bl, const_bstring sep) {
+    bstring b;
+    int i, j, c, v;
+
+    if (bl == NULL || bl->qty < 0)
+        return NULL;
+    if (sep != NULL && (sep->slen < 0 || sep->data == NULL))
+        return NULL;
+
+    for (i = 0, c = 1; i < bl->qty; i++) {
+        v = bl->entry[i]->slen;
+        if (v < 0)
+            return NULL;/* Invalid input */
+        c += v;
+        if (c < 0)
+            return NULL;/* Wrap around ?? */
+    }
+
+    if (sep != NULL)
+        c += (bl->qty - 1) * sep->slen;
+
+    b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+    if (NULL == b)
+        return NULL; /* Out of memory */
+    b->data = (unsigned char *) bstr__alloc (c);
+    if (b->data == NULL) {
+        bstr__free (b);
+        return NULL;
+    }
+
+    b->mlen = c;
+    b->slen = c-1;
+
+    for (i = bl->qty - 1, c = 0, j = 0; i >= 0; i--, j++) {
+        if (j > 0 && sep != NULL) {
+            bstr__memcpy (b->data + c, sep->data, sep->slen);
+            c += sep->slen;
+        }
+        v = bl->entry[i]->slen;
+        bstr__memcpy (b->data + c, bl->entry[i]->data, v);
+        c += v;
+    }
+    b->data[c] = (unsigned char) '\0';
+    return b;
+}
diff --git a/libatalk/bstring/bstrlib.c b/libatalk/bstring/bstrlib.c
new file mode 100644 (file)
index 0000000..9748c00
--- /dev/null
@@ -0,0 +1,2956 @@
+/*
+ * This source file is part of the bstring string library.  This code was
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
+ */
+
+/*
+ * bstrlib.c
+ *
+ * This file is the core module for implementing the bstring functions.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/bstrlib.h>
+
+/* Optionally include a mechanism for debugging memory */
+
+#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
+#include "memdbg.h"
+#endif
+
+#ifndef bstr__alloc
+#define bstr__alloc(x) malloc (x)
+#endif
+
+#ifndef bstr__free
+#define bstr__free(p) free (p)
+#endif
+
+#ifndef bstr__realloc
+#define bstr__realloc(p,x) realloc ((p), (x))
+#endif
+
+#ifndef bstr__memcpy
+#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
+#endif
+
+#ifndef bstr__memmove
+#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
+#endif
+
+#ifndef bstr__memset
+#define bstr__memset(d,c,l) memset ((d), (c), (l))
+#endif
+
+#ifndef bstr__memcmp
+#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
+#endif
+
+#ifndef bstr__memchr
+#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
+#endif
+
+/* Just a length safe wrapper for memmove. */
+
+#define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); }
+
+/* Compute the snapped size for a given requested size.  By snapping to powers
+   of 2 like this, repeated reallocations are avoided. */
+static int snapUpSize (int i) {
+       if (i < 8) {
+               i = 8;
+       } else {
+               unsigned int j;
+               j = (unsigned int) i;
+
+               j |= (j >>  1);
+               j |= (j >>  2);
+               j |= (j >>  4);
+               j |= (j >>  8);         /* Ok, since int >= 16 bits */
+#if (UINT_MAX != 0xffff)
+               j |= (j >> 16);         /* For 32 bit int systems */
+#if (UINT_MAX > 0xffffffffUL)
+               j |= (j >> 32);         /* For 64 bit int systems */
+#endif
+#endif
+               /* Least power of two greater than i */
+               j++;
+               if ((int) j >= i) i = (int) j;
+       }
+       return i;
+}
+
+/*  int balloc (bstring b, int len)
+ *
+ *  Increase the size of the memory backing the bstring b to at least len.
+ */
+int balloc (bstring b, int olen) {
+       int len;
+       if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 || 
+           b->mlen < b->slen || olen <= 0) {
+               return BSTR_ERR;
+       }
+
+       if (olen >= b->mlen) {
+               unsigned char * x;
+
+               if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK;
+
+               /* Assume probability of a non-moving realloc is 0.125 */
+               if (7 * b->mlen < 8 * b->slen) {
+
+                       /* If slen is close to mlen in size then use realloc to reduce
+                          the memory defragmentation */
+
+                       reallocStrategy:;
+
+                       x = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+                       if (x == NULL) {
+
+                               /* Since we failed, try allocating the tighest possible 
+                                  allocation */
+
+                               if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) {
+                                       return BSTR_ERR;
+                               }
+                       }
+               } else {
+
+                       /* If slen is not close to mlen then avoid the penalty of copying
+                          the extra bytes that are allocated, but not considered part of
+                          the string */
+
+                       if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) {
+
+                               /* Perhaps there is no available memory for the two 
+                                  allocations to be in memory at once */
+
+                               goto reallocStrategy;
+
+                       } else {
+                               if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen);
+                               bstr__free (b->data);
+                       }
+               }
+               b->data = x;
+               b->mlen = len;
+               b->data[b->slen] = (unsigned char) '\0';
+       }
+
+       return BSTR_OK;
+}
+
+/*  int ballocmin (bstring b, int len)
+ *
+ *  Set the size of the memory backing the bstring b to len or b->slen+1,
+ *  whichever is larger.  Note that repeated use of this function can degrade
+ *  performance.
+ */
+int ballocmin (bstring b, int len) {
+       unsigned char * s;
+
+       if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 || 
+           b->mlen < b->slen || len <= 0) {
+               return BSTR_ERR;
+       }
+
+       if (len < b->slen + 1) len = b->slen + 1;
+
+       if (len != b->mlen) {
+               s = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+               if (NULL == s) return BSTR_ERR;
+               s[b->slen] = (unsigned char) '\0';
+               b->data = s;
+               b->mlen = len;
+       }
+
+       return BSTR_OK;
+}
+
+/*  bstring bfromcstr (const char * str)
+ *
+ *  Create a bstring which contains the contents of the '\0' terminated char *
+ *  buffer str.
+ */
+bstring bfromcstr (const char * str) {
+bstring b;
+int i;
+size_t j;
+
+       if (str == NULL) return NULL;
+       j = (strlen) (str);
+       i = snapUpSize ((int) (j + (2 - (j != 0))));
+       if (i <= (int) j) return NULL;
+
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (NULL == b) return NULL;
+       b->slen = (int) j;
+       if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       bstr__memcpy (b->data, str, j+1);
+       return b;
+}
+
+/*  bstring bfromcstralloc (int mlen, const char * str)
+ *
+ *  Create a bstring which contains the contents of the '\0' terminated char *
+ *  buffer str.  The memory buffer backing the string is at least len 
+ *  characters in length.
+ */
+bstring bfromcstralloc (int mlen, const char * str) {
+bstring b;
+int i;
+size_t j;
+
+       if (str == NULL) return NULL;
+       j = (strlen) (str);
+       i = snapUpSize ((int) (j + (2 - (j != 0))));
+       if (i <= (int) j) return NULL;
+
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (b == NULL) return NULL;
+       b->slen = (int) j;
+       if (i < mlen) i = mlen;
+
+       if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       bstr__memcpy (b->data, str, j+1);
+       return b;
+}
+
+/*  bstring blk2bstr (const void * blk, int len)
+ *
+ *  Create a bstring which contains the content of the block blk of length 
+ *  len.
+ */
+bstring blk2bstr (const void * blk, int len) {
+bstring b;
+int i;
+
+       if (blk == NULL || len < 0) return NULL;
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (b == NULL) return NULL;
+       b->slen = len;
+
+       i = len + (2 - (len != 0));
+       i = snapUpSize (i);
+
+       b->mlen = i;
+
+       b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen);
+       if (b->data == NULL) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       if (len > 0) bstr__memcpy (b->data, blk, (size_t) len);
+       b->data[len] = (unsigned char) '\0';
+
+       return b;
+}
+
+/*  char * bstr2cstr (const_bstring s, char z)
+ *
+ *  Create a '\0' terminated char * buffer which is equal to the contents of 
+ *  the bstring s, except that any contained '\0' characters are converted 
+ *  to the character in z. This returned value should be freed with a 
+ *  bcstrfree () call, by the calling application.
+ */
+char * bstr2cstr (const_bstring b, char z) {
+int i, l;
+char * r;
+
+       if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+       l = b->slen;
+       r = (char *) bstr__alloc ((size_t) (l + 1));
+       if (r == NULL) return r;
+
+       for (i=0; i < l; i ++) {
+               r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i]));
+       }
+
+       r[l] = (unsigned char) '\0';
+
+       return r;
+}
+
+/*  int bcstrfree (char * s)
+ *
+ *  Frees a C-string generated by bstr2cstr ().  This is normally unnecessary
+ *  since it just wraps a call to bstr__free (), however, if bstr__alloc () 
+ *  and bstr__free () have been redefined as a macros within the bstrlib 
+ *  module (via defining them in memdbg.h after defining 
+ *  BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std 
+ *  library functions, then this allows a correct way of freeing the memory 
+ *  that allows higher level code to be independent from these macro 
+ *  redefinitions.
+ */
+int bcstrfree (char * s) {
+       if (s) {
+               bstr__free (s);
+               return BSTR_OK;
+       }
+       return BSTR_ERR;
+}
+
+/*  int bconcat (bstring b0, const_bstring b1)
+ *
+ *  Concatenate the bstring b1 to the bstring b0.
+ */
+int bconcat (bstring b0, const_bstring b1) {
+int len, d;
+bstring aux = (bstring) b1;
+
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR;
+
+       d = b0->slen;
+       len = b1->slen;
+       if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR;
+
+       if (b0->mlen <= d + len + 1) {
+               ptrdiff_t pd = b1->data - b0->data;
+               if (0 <= pd && pd < b0->mlen) {
+                       if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+               }
+               if (balloc (b0, d + len + 1) != BSTR_OK) {
+                       if (aux != b1) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+       }
+
+       bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len);
+       b0->data[d + len] = (unsigned char) '\0';
+       b0->slen = d + len;
+       if (aux != b1) bdestroy (aux);
+       return BSTR_OK;
+}
+
+/*  int bconchar (bstring b, char c)
+/ *
+ *  Concatenate the single character c to the bstring b.
+ */
+int bconchar (bstring b, char c) {
+int d;
+
+       if (b == NULL) return BSTR_ERR;
+       d = b->slen;
+       if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+       b->data[d] = (unsigned char) c;
+       b->data[d + 1] = (unsigned char) '\0';
+       b->slen++;
+       return BSTR_OK;
+}
+
+/*  int bcatcstr (bstring b, const char * s)
+ *
+ *  Concatenate a char * string to a bstring.
+ */
+int bcatcstr (bstring b, const char * s) {
+char * d;
+int i, l;
+
+       if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+        || b->mlen <= 0 || s == NULL) return BSTR_ERR;
+
+       /* Optimistically concatenate directly */
+       l = b->mlen - b->slen;
+       d = (char *) &b->data[b->slen];
+       for (i=0; i < l; i++) {
+               if ((*d++ = *s++) == '\0') {
+                       b->slen += i;
+                       return BSTR_OK;
+               }
+       }
+       b->slen += i;
+
+       /* Need to explicitely resize and concatenate tail */
+       return bcatblk (b, (const void *) s, (int) strlen (s));
+}
+
+/*  int bcatblk (bstring b, const void * s, int len)
+ *
+ *  Concatenate a fixed length buffer to a bstring.
+ */
+int bcatblk (bstring b, const void * s, int len) {
+int nl;
+
+       if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+        || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR;
+
+       if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */
+       if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR;
+
+       bBlockCopy (&b->data[b->slen], s, (size_t) len);
+       b->slen = nl;
+       b->data[nl] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  bstring bstrcpy (const_bstring b)
+ *
+ *  Create a copy of the bstring b.
+ */
+bstring bstrcpy (const_bstring b) {
+bstring b0;
+int i,j;
+
+       /* Attempted to copy an invalid string? */
+       if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+
+       b0 = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (b0 == NULL) {
+               /* Unable to allocate memory for string header */
+               return NULL;
+       }
+
+       i = b->slen;
+       j = snapUpSize (i + 1);
+
+       b0->data = (unsigned char *) bstr__alloc (j);
+       if (b0->data == NULL) {
+               j = i + 1;
+               b0->data = (unsigned char *) bstr__alloc (j);
+               if (b0->data == NULL) {
+                       /* Unable to allocate memory for string data */
+                       bstr__free (b0);
+                       return NULL;
+               }
+       }
+
+       b0->mlen = j;
+       b0->slen = i;
+
+       if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i);
+       b0->data[b0->slen] = (unsigned char) '\0';
+
+       return b0;
+}
+
+/*  int bassign (bstring a, const_bstring b)
+ *
+ *  Overwrite the string a with the contents of string b.
+ */
+int bassign (bstring a, const_bstring b) {
+       if (b == NULL || b->data == NULL || b->slen < 0)
+               return BSTR_ERR;
+       if (b->slen != 0) {
+               if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR;
+               bstr__memmove (a->data, b->data, b->slen);
+       } else {
+               if (a == NULL || a->data == NULL || a->mlen < a->slen || 
+                   a->slen < 0 || a->mlen == 0) 
+                       return BSTR_ERR;
+       }
+       a->data[b->slen] = (unsigned char) '\0';
+       a->slen = b->slen;
+       return BSTR_OK;
+}
+
+/*  int bassignmidstr (bstring a, const_bstring b, int left, int len)
+ *
+ *  Overwrite the string a with the middle of contents of string b 
+ *  starting from position left and running for a length len.  left and 
+ *  len are clamped to the ends of b as with the function bmidstr.
+ */
+int bassignmidstr (bstring a, const_bstring b, int left, int len) {
+       if (b == NULL || b->data == NULL || b->slen < 0)
+               return BSTR_ERR;
+
+       if (left < 0) {
+               len += left;
+               left = 0;
+       }
+
+       if (len > b->slen - left) len = b->slen - left;
+
+       if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+           a->slen < 0 || a->mlen == 0)
+               return BSTR_ERR;
+
+       if (len > 0) {
+               if (balloc (a, len) != BSTR_OK) return BSTR_ERR;
+               bstr__memmove (a->data, b->data + left, len);
+               a->slen = len;
+       } else {
+               a->slen = 0;
+       }
+       a->data[a->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bassigncstr (bstring a, const char * str)
+ *
+ *  Overwrite the string a with the contents of char * string str.  Note that 
+ *  the bstring a must be a well defined and writable bstring.  If an error 
+ *  occurs BSTR_ERR is returned however a may be partially overwritten.
+ */
+int bassigncstr (bstring a, const char * str) {
+int i;
+size_t len;
+       if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+           a->slen < 0 || a->mlen == 0 || NULL == str) 
+               return BSTR_ERR;
+
+       for (i=0; i < a->mlen; i++) {
+               if ('\0' == (a->data[i] = str[i])) {
+                       a->slen = i;
+                       return BSTR_OK;
+               }
+       }
+
+       a->slen = i;
+       len = strlen (str + i);
+       if (len > INT_MAX || i + len + 1 > INT_MAX ||
+           0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR;
+       bBlockCopy (a->data + i, str + i, (size_t) len + 1);
+       a->slen += (int) len;
+       return BSTR_OK;
+}
+
+/*  int bassignblk (bstring a, const void * s, int len)
+ *
+ *  Overwrite the string a with the contents of the block (s, len).  Note that 
+ *  the bstring a must be a well defined and writable bstring.  If an error 
+ *  occurs BSTR_ERR is returned and a is not overwritten.
+ */
+int bassignblk (bstring a, const void * s, int len) {
+       if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+           a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1) 
+               return BSTR_ERR;
+       if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR;
+       bBlockCopy (a->data, s, (size_t) len);
+       a->data[len] = (unsigned char) '\0';
+       a->slen = len;
+       return BSTR_OK;
+}
+
+/*  int btrunc (bstring b, int n)
+ *
+ *  Truncate the bstring to at most n characters.
+ */
+int btrunc (bstring b, int n) {
+       if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+       if (b->slen > n) {
+               b->slen = n;
+               b->data[n] = (unsigned char) '\0';
+       }
+       return BSTR_OK;
+}
+
+#define   upcase(c) (toupper ((unsigned char) c))
+#define downcase(c) (tolower ((unsigned char) c))
+#define   wspace(c) (isspace ((unsigned char) c))
+
+/*  int btoupper (bstring b)
+ *
+ *  Convert contents of bstring to upper case.
+ */
+int btoupper (bstring b) {
+int i, len;
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+       for (i=0, len = b->slen; i < len; i++) {
+               b->data[i] = (unsigned char) upcase (b->data[i]);
+       }
+       return BSTR_OK;
+}
+
+/*  int btolower (bstring b)
+ *
+ *  Convert contents of bstring to lower case.
+ */
+int btolower (bstring b) {
+int i, len;
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+       for (i=0, len = b->slen; i < len; i++) {
+               b->data[i] = (unsigned char) downcase (b->data[i]);
+       }
+       return BSTR_OK;
+}
+
+/*  int bstricmp (const_bstring b0, const_bstring b1)
+ *
+ *  Compare two strings without differentiating between case.  The return 
+ *  value is the difference of the values of the characters where the two 
+ *  strings first differ after lower case transformation, otherwise 0 is 
+ *  returned indicating that the strings are equal.  If the lengths are 
+ *  different, then a difference from 0 is given, but if the first extra 
+ *  character is '\0', then it is taken to be the value UCHAR_MAX+1.
+ */
+int bstricmp (const_bstring b0, const_bstring b1) {
+int i, v, n;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || 
+           bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN;
+       if ((n = b0->slen) > b1->slen) n = b1->slen;
+       else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK;
+
+       for (i = 0; i < n; i ++) {
+               v  = (char) downcase (b0->data[i])
+                  - (char) downcase (b1->data[i]);
+               if (0 != v) return v;
+       }
+
+       if (b0->slen > n) {
+               v = (char) downcase (b0->data[n]);
+               if (v) return v;
+               return UCHAR_MAX + 1;
+       }
+       if (b1->slen > n) {
+               v = - (char) downcase (b1->data[n]);
+               if (v) return v;
+               return - (int) (UCHAR_MAX + 1);
+       }
+       return BSTR_OK;
+}
+
+/*  int bstrnicmp (const_bstring b0, const_bstring b1, int n)
+ *
+ *  Compare two strings without differentiating between case for at most n
+ *  characters.  If the position where the two strings first differ is
+ *  before the nth position, the return value is the difference of the values
+ *  of the characters, otherwise 0 is returned.  If the lengths are different
+ *  and less than n characters, then a difference from 0 is given, but if the 
+ *  first extra character is '\0', then it is taken to be the value 
+ *  UCHAR_MAX+1.
+ */
+int bstrnicmp (const_bstring b0, const_bstring b1, int n) {
+int i, v, m;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || 
+           bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN;
+       m = n;
+       if (m > b0->slen) m = b0->slen;
+       if (m > b1->slen) m = b1->slen;
+
+       if (b0->data != b1->data) {
+               for (i = 0; i < m; i ++) {
+                       v  = (char) downcase (b0->data[i]);
+                       v -= (char) downcase (b1->data[i]);
+                       if (v != 0) return b0->data[i] - b1->data[i];
+               }
+       }
+
+       if (n == m || b0->slen == b1->slen) return BSTR_OK;
+
+       if (b0->slen > m) {
+               v = (char) downcase (b0->data[m]);
+               if (v) return v;
+               return UCHAR_MAX + 1;
+       }
+
+       v = - (char) downcase (b1->data[m]);
+       if (v) return v;
+       return - (int) (UCHAR_MAX + 1);
+}
+
+/*  int biseqcaseless (const_bstring b0, const_bstring b1)
+ *
+ *  Compare two strings for equality without differentiating between case.  
+ *  If the strings differ other than in case, 0 is returned, if the strings 
+ *  are the same, 1 is returned, if there is an error, -1 is returned.  If 
+ *  the length of the strings are different, this function is O(1).  '\0' 
+ *  termination characters are not treated in any special way.
+ */
+int biseqcaseless (const_bstring b0, const_bstring b1) {
+int i, n;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || 
+           bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR;
+       if (b0->slen != b1->slen) return BSTR_OK;
+       if (b0->data == b1->data || b0->slen == 0) return 1;
+       for (i=0, n=b0->slen; i < n; i++) {
+               if (b0->data[i] != b1->data[i]) {
+                       unsigned char c = (unsigned char) downcase (b0->data[i]);
+                       if (c != (unsigned char) downcase (b1->data[i])) return 0;
+               }
+       }
+       return 1;
+}
+
+/*  int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len)
+ *
+ *  Compare beginning of string b0 with a block of memory of length len 
+ *  without differentiating between case for equality.  If the beginning of b0
+ *  differs from the memory block other than in case (or if b0 is too short), 
+ *  0 is returned, if the strings are the same, 1 is returned, if there is an 
+ *  error, -1 is returned.  '\0' characters are not treated in any special 
+ *  way.
+ */
+int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+               return BSTR_ERR;
+       if (b0->slen < len) return BSTR_OK;
+       if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+       for (i = 0; i < len; i ++) {
+               if (b0->data[i] != ((const unsigned char *) blk)[i]) {
+                       if (downcase (b0->data[i]) != 
+                           downcase (((const unsigned char *) blk)[i])) return 0;
+               }
+       }
+       return 1;
+}
+
+/*
+ * int bltrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the left end of the string.
+ */
+int bltrimws (bstring b) {
+int i, len;
+
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+       for (len = b->slen, i = 0; i < len; i++) {
+               if (!wspace (b->data[i])) {
+                       return bdelete (b, 0, i);
+               }
+       }
+
+       b->data[0] = (unsigned char) '\0';
+       b->slen = 0;
+       return BSTR_OK;
+}
+
+/*
+ * int brtrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the right end of the string.
+ */
+int brtrimws (bstring b) {
+int i;
+
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+       for (i = b->slen - 1; i >= 0; i--) {
+               if (!wspace (b->data[i])) {
+                       if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+                       b->slen = i + 1;
+                       return BSTR_OK;
+               }
+       }
+
+       b->data[0] = (unsigned char) '\0';
+       b->slen = 0;
+       return BSTR_OK;
+}
+
+/*
+ * int btrimws (bstring b)
+ *
+ * Delete whitespace contiguous from both ends of the string.
+ */
+int btrimws (bstring b) {
+int i, j;
+
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+       for (i = b->slen - 1; i >= 0; i--) {
+               if (!wspace (b->data[i])) {
+                       if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+                       b->slen = i + 1;
+                       for (j = 0; wspace (b->data[j]); j++) {}
+                       return bdelete (b, 0, j);
+               }
+       }
+
+       b->data[0] = (unsigned char) '\0';
+       b->slen = 0;
+       return BSTR_OK;
+}
+
+/*  int biseq (const_bstring b0, const_bstring b1)
+ *
+ *  Compare the string b0 and b1.  If the strings differ, 0 is returned, if 
+ *  the strings are the same, 1 is returned, if there is an error, -1 is 
+ *  returned.  If the length of the strings are different, this function is
+ *  O(1).  '\0' termination characters are not treated in any special way.
+ */
+int biseq (const_bstring b0, const_bstring b1) {
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+               b0->slen < 0 || b1->slen < 0) return BSTR_ERR;
+       if (b0->slen != b1->slen) return BSTR_OK;
+       if (b0->data == b1->data || b0->slen == 0) return 1;
+       return !bstr__memcmp (b0->data, b1->data, b0->slen);
+}
+
+/*  int bisstemeqblk (const_bstring b0, const void * blk, int len)
+ *
+ *  Compare beginning of string b0 with a block of memory of length len for 
+ *  equality.  If the beginning of b0 differs from the memory block (or if b0 
+ *  is too short), 0 is returned, if the strings are the same, 1 is returned, 
+ *  if there is an error, -1 is returned.  '\0' characters are not treated in 
+ *  any special way.
+ */
+int bisstemeqblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+               return BSTR_ERR;
+       if (b0->slen < len) return BSTR_OK;
+       if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+       for (i = 0; i < len; i ++) {
+               if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK;
+       }
+       return 1;
+}
+
+/*  int biseqcstr (const_bstring b, const char *s)
+ *
+ *  Compare the bstring b and char * string s.  The C string s must be '\0' 
+ *  terminated at exactly the length of the bstring b, and the contents 
+ *  between the two must be identical with the bstring b with no '\0' 
+ *  characters for the two contents to be considered equal.  This is 
+ *  equivalent to the condition that their current contents will be always be 
+ *  equal when comparing them in the same format after converting one or the 
+ *  other.  If the strings are equal 1 is returned, if they are unequal 0 is 
+ *  returned and if there is a detectable error BSTR_ERR is returned.
+ */
+int biseqcstr (const_bstring b, const char * s) {
+int i;
+       if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
+       for (i=0; i < b->slen; i++) {
+               if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK;
+       }
+       return s[i] == '\0';
+}
+
+/*  int biseqcstrcaseless (const_bstring b, const char *s)
+ *
+ *  Compare the bstring b and char * string s.  The C string s must be '\0' 
+ *  terminated at exactly the length of the bstring b, and the contents 
+ *  between the two must be identical except for case with the bstring b with 
+ *  no '\0' characters for the two contents to be considered equal.  This is 
+ *  equivalent to the condition that their current contents will be always be 
+ *  equal ignoring case when comparing them in the same format after 
+ *  converting one or the other.  If the strings are equal, except for case, 
+ *  1 is returned, if they are unequal regardless of case 0 is returned and 
+ *  if there is a detectable error BSTR_ERR is returned.
+ */
+int biseqcstrcaseless (const_bstring b, const char * s) {
+int i;
+       if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
+       for (i=0; i < b->slen; i++) {
+               if (s[i] == '\0' || 
+                   (b->data[i] != (unsigned char) s[i] && 
+                    downcase (b->data[i]) != (unsigned char) downcase (s[i])))
+                       return BSTR_OK;
+       }
+       return s[i] == '\0';
+}
+
+/*  int bstrcmp (const_bstring b0, const_bstring b1)
+ *
+ *  Compare the string b0 and b1.  If there is an error, SHRT_MIN is returned, 
+ *  otherwise a value less than or greater than zero, indicating that the 
+ *  string pointed to by b0 is lexicographically less than or greater than 
+ *  the string pointed to by b1 is returned.  If the the string lengths are 
+ *  unequal but the characters up until the length of the shorter are equal 
+ *  then a value less than, or greater than zero, indicating that the string 
+ *  pointed to by b0 is shorter or longer than the string pointed to by b1 is 
+ *  returned.  0 is returned if and only if the two strings are the same.  If 
+ *  the length of the strings are different, this function is O(n).  Like its
+ *  standard C library counter part strcmp, the comparison does not proceed 
+ *  past any '\0' termination characters encountered.
+ */
+int bstrcmp (const_bstring b0, const_bstring b1) {
+int i, v, n;
+
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+               b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
+       n = b0->slen; if (n > b1->slen) n = b1->slen;
+       if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0))
+               return BSTR_OK;
+
+       for (i = 0; i < n; i ++) {
+               v = ((char) b0->data[i]) - ((char) b1->data[i]);
+               if (v != 0) return v;
+               if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
+       }
+
+       if (b0->slen > n) return 1;
+       if (b1->slen > n) return -1;
+       return BSTR_OK;
+}
+
+/*  int bstrncmp (const_bstring b0, const_bstring b1, int n)
+ *
+ *  Compare the string b0 and b1 for at most n characters.  If there is an 
+ *  error, SHRT_MIN is returned, otherwise a value is returned as if b0 and 
+ *  b1 were first truncated to at most n characters then bstrcmp was called
+ *  with these new strings are paremeters.  If the length of the strings are 
+ *  different, this function is O(n).  Like its standard C library counter 
+ *  part strcmp, the comparison does not proceed past any '\0' termination 
+ *  characters encountered.
+ */
+int bstrncmp (const_bstring b0, const_bstring b1, int n) {
+int i, v, m;
+
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+               b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
+       m = n;
+       if (m > b0->slen) m = b0->slen;
+       if (m > b1->slen) m = b1->slen;
+
+       if (b0->data != b1->data) {
+               for (i = 0; i < m; i ++) {
+                       v = ((char) b0->data[i]) - ((char) b1->data[i]);
+                       if (v != 0) return v;
+                       if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
+               }
+       }
+
+       if (n == m || b0->slen == b1->slen) return BSTR_OK;
+
+       if (b0->slen > m) return 1;
+       return -1;
+}
+
+/*  bstring bmidstr (const_bstring b, int left, int len)
+ *
+ *  Create a bstring which is the substring of b starting from position left
+ *  and running for a length len (clamped by the end of the bstring b.)  If
+ *  b is detectably invalid, then NULL is returned.  The section described 
+ *  by (left, len) is clamped to the boundaries of b.
+ */
+bstring bmidstr (const_bstring b, int left, int len) {
+
+       if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+
+       if (left < 0) {
+               len += left;
+               left = 0;
+       }
+
+       if (len > b->slen - left) len = b->slen - left;
+
+       if (len <= 0) return bfromcstr ("");
+       return blk2bstr (b->data + left, len);
+}
+
+/*  int bdelete (bstring b, int pos, int len)
+ *
+ *  Removes characters from pos to pos+len-1 inclusive and shifts the tail of 
+ *  the bstring starting from pos+len to pos.  len must be positive for this 
+ *  call to have any effect.  The section of the string described by (pos, 
+ *  len) is clamped to boundaries of the bstring b.
+ */
+int bdelete (bstring b, int pos, int len) {
+       /* Clamp to left side of bstring */
+       if (pos < 0) {
+               len += pos;
+               pos = 0;
+       }
+
+       if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || 
+           b->mlen < b->slen || b->mlen <= 0) 
+               return BSTR_ERR;
+       if (len > 0 && pos < b->slen) {
+               if (pos + len >= b->slen) {
+                       b->slen = pos;
+               } else {
+                       bBlockCopy ((char *) (b->data + pos),
+                                   (char *) (b->data + pos + len), 
+                                   b->slen - (pos+len));
+                       b->slen -= len;
+               }
+               b->data[b->slen] = (unsigned char) '\0';
+       }
+       return BSTR_OK;
+}
+
+/*  int bdestroy (bstring b)
+ *
+ *  Free up the bstring.  Note that if b is detectably invalid or not writable
+ *  then no action is performed and BSTR_ERR is returned.  Like a freed memory
+ *  allocation, dereferences, writes or any other action on b after it has 
+ *  been bdestroyed is undefined.
+ */
+int bdestroy (bstring b) {
+       if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen ||
+           b->data == NULL)
+               return BSTR_ERR;
+
+       bstr__free (b->data);
+
+       /* In case there is any stale usage, there is one more chance to 
+          notice this error. */
+
+       b->slen = -1;
+       b->mlen = -__LINE__;
+       b->data = NULL;
+
+       bstr__free (b);
+       return BSTR_OK;
+}
+
+/*  int binstr (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  forward.  If it is found then return with the first position where it is 
+ *  found, otherwise return BSTR_ERR.  Note that this is just a brute force 
+ *  string searcher that does not attempt clever things like the Boyer-Moore 
+ *  search algorithm.  Because of this there are many degenerate cases where 
+ *  this can take much longer than it needs to.
+ */
+int binstr (const_bstring b1, int pos, const_bstring b2) {
+int j, ii, ll, lf;
+unsigned char * d0;
+unsigned char c0;
+register unsigned char * d1;
+register unsigned char c1;
+register int i;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       /* No space to find such a string? */
+       if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR;
+
+       /* An obvious alias case */
+       if (b1->data == b2->data && pos == 0) return 0;
+
+       i = pos;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       ll = b2->slen;
+
+       /* Peel off the b2->slen == 1 case */
+       c0 = d0[0];
+       if (1 == ll) {
+               for (;i < lf; i++) if (c0 == d1[i]) return i;
+               return BSTR_ERR;
+       }
+
+       c1 = c0;
+       j = 0;
+       lf = b1->slen - 1;
+
+       ii = -1;
+       if (i < lf) do {
+               /* Unrolled current character test */
+               if (c1 != d1[i]) {
+                       if (c1 != d1[1+i]) {
+                               i += 2;
+                               continue;
+                       }
+                       i++;
+               }
+
+               /* Take note if this is the start of a potential match */
+               if (0 == j) ii = i;
+
+               /* Shift the test character down by one */
+               j++;
+               i++;
+
+               /* If this isn't past the last character continue */
+               if (j < ll) {
+                       c1 = d0[j];
+                       continue;
+               }
+
+               N0:;
+
+               /* If no characters mismatched, then we matched */
+               if (i == ii+j) return ii;
+
+               /* Shift back to the beginning */
+               i -= j;
+               j  = 0;
+               c1 = c0;
+       } while (i < lf);
+
+       /* Deal with last case if unrolling caused a misalignment */
+       if (i == lf && ll == j+1 && c1 == d1[i]) goto N0;
+
+       return BSTR_ERR;
+}
+
+/*  int binstrr (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  backward.  If it is found then return with the first position where it is 
+ *  found, otherwise return BSTR_ERR.  Note that this is just a brute force 
+ *  string searcher that does not attempt clever things like the Boyer-Moore 
+ *  search algorithm.  Because of this there are many degenerate cases where 
+ *  this can take much longer than it needs to.
+ */
+int binstrr (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l;
+unsigned char * d0, * d1;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos && b2->slen == 0) return pos;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       /* Obvious alias case */
+       if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0;
+
+       i = pos;
+       if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
+
+       /* If no space to find such a string then snap back */
+       if (l + 1 <= i) i = l;
+       j = 0;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       l  = b2->slen;
+
+       for (;;) {
+               if (d0[j] == d1[i + j]) {
+                       j ++;
+                       if (j >= l) return i;
+               } else {
+                       i --;
+                       if (i < 0) break;
+                       j=0;
+               }
+       }
+
+       return BSTR_ERR;
+}
+
+/*  int binstrcaseless (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  forward but without regard to case.  If it is found then return with the 
+ *  first position where it is found, otherwise return BSTR_ERR.  Note that 
+ *  this is just a brute force string searcher that does not attempt clever 
+ *  things like the Boyer-Moore search algorithm.  Because of this there are 
+ *  many degenerate cases where this can take much longer than it needs to.
+ */
+int binstrcaseless (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l, ll;
+unsigned char * d0, * d1;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       l = b1->slen - b2->slen + 1;
+
+       /* No space to find such a string? */
+       if (l <= pos) return BSTR_ERR;
+
+       /* An obvious alias case */
+       if (b1->data == b2->data && pos == 0) return BSTR_OK;
+
+       i = pos;
+       j = 0;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       ll = b2->slen;
+
+       for (;;) {
+               if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
+                       j ++;
+                       if (j >= ll) return i;
+               } else {
+                       i ++;
+                       if (i >= l) break;
+                       j=0;
+               }
+       }
+
+       return BSTR_ERR;
+}
+
+/*  int binstrrcaseless (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  backward but without regard to case.  If it is found then return with the 
+ *  first position where it is found, otherwise return BSTR_ERR.  Note that 
+ *  this is just a brute force string searcher that does not attempt clever 
+ *  things like the Boyer-Moore search algorithm.  Because of this there are 
+ *  many degenerate cases where this can take much longer than it needs to.
+ */
+int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l;
+unsigned char * d0, * d1;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos && b2->slen == 0) return pos;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       /* Obvious alias case */
+       if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK;
+
+       i = pos;
+       if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
+
+       /* If no space to find such a string then snap back */
+       if (l + 1 <= i) i = l;
+       j = 0;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       l  = b2->slen;
+
+       for (;;) {
+               if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
+                       j ++;
+                       if (j >= l) return i;
+               } else {
+                       i --;
+                       if (i < 0) break;
+                       j=0;
+               }
+       }
+
+       return BSTR_ERR;
+}
+
+
+/*  int bstrchrp (const_bstring b, int c, int pos)
+ *
+ *  Search for the character c in b forwards from the position pos 
+ *  (inclusive).
+ */
+int bstrchrp (const_bstring b, int c, int pos) {
+unsigned char * p;
+
+       if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+       p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos));
+       if (p) return (int) (p - b->data);
+       return BSTR_ERR;
+}
+
+/*  int bstrrchrp (const_bstring b, int c, int pos)
+ *
+ *  Search for the character c in b backwards from the position pos in string 
+ *  (inclusive).
+ */
+int bstrrchrp (const_bstring b, int c, int pos) {
+int i;
+       if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+       for (i=pos; i >= 0; i--) {
+               if (b->data[i] == (unsigned char) c) return i;
+       }
+       return BSTR_ERR;
+}
+
+#if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF)
+#define LONG_LOG_BITS_QTY (3)
+#define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY)
+#define LONG_TYPE unsigned char
+
+#define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY)
+struct charField { LONG_TYPE content[CFCLEN]; };
+#define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1))))
+#define setInCharField(cf,idx) { \
+       unsigned int c = (unsigned int) (idx); \
+       (cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \
+}
+
+#else
+
+#define CFCLEN (1 << CHAR_BIT)
+struct charField { unsigned char content[CFCLEN]; };
+#define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)])
+#define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0
+
+#endif
+
+/* Convert a bstring to charField */
+static int buildCharField (struct charField * cf, const_bstring b) {
+int i;
+       if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR;
+       memset ((void *) cf->content, 0, sizeof (struct charField));
+       for (i=0; i < b->slen; i++) {
+               setInCharField (cf, b->data[i]);
+       }
+       return BSTR_OK;
+}
+
+static void invertCharField (struct charField * cf) {
+int i;
+       for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i];
+}
+
+/* Inner engine for binchr */
+static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) {
+int i;
+       for (i=pos; i < len; i++) {
+               unsigned char c = (unsigned char) data[i];
+               if (testInCharField (cf, c)) return i;
+       }
+       return BSTR_ERR;
+}
+
+/*  int binchr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the first position in b0 starting from pos or after, in which 
+ *  one of the characters in b1 is found and return it.  If such a position 
+ *  does not exist in b0, then BSTR_ERR is returned.
+ */
+int binchr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL ||
+           b0->slen <= pos) return BSTR_ERR;
+       if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos);
+       if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
+       return binchrCF (b0->data, b0->slen, pos, &chrs);
+}
+
+/* Inner engine for binchrr */
+static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) {
+int i;
+       for (i=pos; i >= 0; i--) {
+               unsigned int c = (unsigned int) data[i];
+               if (testInCharField (cf, c)) return i;
+       }
+       return BSTR_ERR;
+}
+
+/*  int binchrr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the last position in b0 no greater than pos, in which one of 
+ *  the characters in b1 is found and return it.  If such a position does not 
+ *  exist in b0, then BSTR_ERR is returned.
+ */
+int binchrr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL ||
+           b0->slen < pos) return BSTR_ERR;
+       if (pos == b0->slen) pos--;
+       if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos);
+       if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
+       return binchrrCF (b0->data, pos, &chrs);
+}
+
+/*  int bninchr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the first position in b0 starting from pos or after, in which 
+ *  none of the characters in b1 is found and return it.  If such a position 
+ *  does not exist in b0, then BSTR_ERR is returned.
+ */
+int bninchr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL || 
+           b0->slen <= pos) return BSTR_ERR;
+       if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+       invertCharField (&chrs);
+       return binchrCF (b0->data, b0->slen, pos, &chrs);
+}
+
+/*  int bninchrr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the last position in b0 no greater than pos, in which none of 
+ *  the characters in b1 is found and return it.  If such a position does not 
+ *  exist in b0, then BSTR_ERR is returned.
+ */
+int bninchrr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL || 
+           b0->slen < pos) return BSTR_ERR;
+       if (pos == b0->slen) pos--;
+       if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+       invertCharField (&chrs);
+       return binchrrCF (b0->data, pos, &chrs);
+}
+
+/*  int bsetstr (bstring b0, int pos, bstring b1, unsigned char fill)
+ *
+ *  Overwrite the string b0 starting at position pos with the string b1. If 
+ *  the position pos is past the end of b0, then the character "fill" is 
+ *  appended as necessary to make up the gap between the end of b0 and pos.
+ *  If b1 is NULL, it behaves as if it were a 0-length string.
+ */
+int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) {
+int d, newlen;
+ptrdiff_t pd;
+bstring aux = (bstring) b1;
+
+       if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data || 
+           b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR;
+       if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR;
+
+       d = pos;
+
+       /* Aliasing case */
+       if (NULL != aux) {
+               if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) {
+                       if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+               }
+               d += aux->slen;
+       }
+
+       /* Increase memory size if necessary */
+       if (balloc (b0, d + 1) != BSTR_OK) {
+               if (aux != b1) bdestroy (aux);
+               return BSTR_ERR;
+       }
+
+       newlen = b0->slen;
+
+       /* Fill in "fill" character as necessary */
+       if (pos > newlen) {
+               bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen));
+               newlen = pos;
+       }
+
+       /* Copy b1 to position pos in b0. */
+       if (aux != NULL) {
+               bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen);
+               if (aux != b1) bdestroy (aux);
+       }
+
+       /* Indicate the potentially increased size of b0 */
+       if (d > newlen) newlen = d;
+
+       b0->slen = newlen;
+       b0->data[newlen] = (unsigned char) '\0';
+
+       return BSTR_OK;
+}
+
+/*  int binsert (bstring b1, int pos, bstring b2, unsigned char fill)
+ *
+ *  Inserts the string b2 into b1 at position pos.  If the position pos is 
+ *  past the end of b1, then the character "fill" is appended as necessary to 
+ *  make up the gap between the end of b1 and pos.  Unlike bsetstr, binsert
+ *  does not allow b2 to be NULL.
+ */
+int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) {
+int d, l;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
+
+       if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 || 
+           b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR;
+
+       /* Aliasing case */
+       if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) {
+               if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
+       }
+
+       /* Compute the two possible end pointers */
+       d = b1->slen + aux->slen;
+       l = pos + aux->slen;
+       if ((d|l) < 0) return BSTR_ERR;
+
+       if (l > d) {
+               /* Inserting past the end of the string */
+               if (balloc (b1, l + 1) != BSTR_OK) {
+                       if (aux != b2) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+               bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen));
+               b1->slen = l;
+       } else {
+               /* Inserting in the middle of the string */
+               if (balloc (b1, d + 1) != BSTR_OK) {
+                       if (aux != b2) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+               bBlockCopy (b1->data + l, b1->data + pos, d - l);
+               b1->slen = d;
+       }
+       bBlockCopy (b1->data + pos, aux->data, aux->slen);
+       b1->data[b1->slen] = (unsigned char) '\0';
+       if (aux != b2) bdestroy (aux);
+       return BSTR_OK;
+}
+
+/*  int breplace (bstring b1, int pos, int len, bstring b2, 
+ *                unsigned char fill)
+ *
+ *  Replace a section of a string from pos for a length len with the string b2.
+ *  fill is used is pos > b1->slen.
+ */
+int breplace (bstring b1, int pos, int len, const_bstring b2, 
+                         unsigned char fill) {
+int pl, ret;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
+
+       if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL || 
+           b2 == NULL || b1->data == NULL || b2->data == NULL || 
+           b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen ||
+           b1->mlen <= 0) return BSTR_ERR;
+
+       /* Straddles the end? */
+       if (pl >= b1->slen) {
+               if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret;
+               if (pos + b2->slen < b1->slen) {
+                       b1->slen = pos + b2->slen;
+                       b1->data[b1->slen] = (unsigned char) '\0';
+               }
+               return ret;
+       }
+
+       /* Aliasing case */
+       if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) {
+               if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
+       }
+
+       if (aux->slen > len) {
+               if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) {
+                       if (aux != b2) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+       }
+
+       if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len));
+       bstr__memcpy (b1->data + pos, aux->data, aux->slen);
+       b1->slen += aux->slen - len;
+       b1->data[b1->slen] = (unsigned char) '\0';
+       if (aux != b2) bdestroy (aux);
+       return BSTR_OK;
+}
+
+/*  int bfindreplace (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string with a replace string after a
+ *  given point in a bstring.
+ */
+
+typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2);
+
+static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) {
+int i, ret, slen, mlen, delta, acc;
+int * d;
+int static_d[32];
+ptrdiff_t pd;
+bstring auxf = (bstring) find;
+bstring auxr = (bstring) repl;
+
+       if (b == NULL || b->data == NULL || find == NULL ||
+           find->data == NULL || repl == NULL || repl->data == NULL || 
+           pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen || 
+           b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return BSTR_ERR;
+       if (pos > b->slen - find->slen) return BSTR_OK;
+
+       /* Alias with find string */
+       pd = (ptrdiff_t) (find->data - b->data);
+       if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) {
+               if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR;
+       }
+
+       /* Alias with repl string */
+       pd = (ptrdiff_t) (repl->data - b->data);
+       if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) {
+               if (NULL == (auxr = bstrcpy (repl))) {
+                       if (auxf != find) bdestroy (auxf);
+                       return BSTR_ERR;
+               }
+       }
+
+       delta = auxf->slen - auxr->slen;
+
+       /* in-place replacement since find and replace strings are of equal 
+          length */
+       if (delta == 0) {
+               while ((pos = instr (b, pos, auxf)) >= 0) {
+                       bstr__memcpy (b->data + pos, auxr->data, auxr->slen);
+                       pos += auxf->slen;
+               }
+               if (auxf != find) bdestroy (auxf);
+               if (auxr != repl) bdestroy (auxr);
+               return BSTR_OK;
+       }
+
+       /* shrinking replacement since auxf->slen > auxr->slen */
+       if (delta > 0) {
+               acc = 0;
+
+               while ((i = instr (b, pos, auxf)) >= 0) {
+                       if (acc && i > pos)
+                               bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
+                       if (auxr->slen)
+                               bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen);
+                       acc += delta;
+                       pos = i + auxf->slen;
+               }
+
+               if (acc) {
+                       i = b->slen;
+                       if (i > pos)
+                               bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
+                       b->slen -= acc;
+                       b->data[b->slen] = (unsigned char) '\0';
+               }
+
+               if (auxf != find) bdestroy (auxf);
+               if (auxr != repl) bdestroy (auxr);
+               return BSTR_OK;
+       }
+
+       /* expanding replacement since find->slen < repl->slen.  Its a lot 
+          more complicated. */
+
+       mlen = 32;
+       d = (int *) static_d; /* Avoid malloc for trivial cases */
+       acc = slen = 0;
+
+       while ((pos = instr (b, pos, auxf)) >= 0) {
+               if (slen + 1 >= mlen) {
+                       int sl;
+                       int * t;
+                       mlen += mlen;
+                       sl = sizeof (int *) * mlen;
+                       if (static_d == d) d = NULL;
+                       if (sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) {
+                               ret = BSTR_ERR;
+                               goto done;
+                       }
+                       if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d));
+                       d = t;
+               }
+               d[slen] = pos;
+               slen++;
+               acc -= delta;
+               pos += auxf->slen;
+               if (pos < 0 || acc < 0) {
+                       ret = BSTR_ERR;
+                       goto done;
+               }
+       }
+       d[slen] = b->slen;
+
+       if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) {
+               b->slen += acc;
+               for (i = slen-1; i >= 0; i--) {
+                       int s, l;
+                       s = d[i] + auxf->slen;
+                       l = d[i+1] - s;
+                       if (l) {
+                               bstr__memmove (b->data + s + acc, b->data + s, l);
+                       }
+                       if (auxr->slen) {
+                               bstr__memmove (b->data + s + acc - auxr->slen, 
+                                        auxr->data, auxr->slen);
+                       }
+                       acc += delta;           
+               }
+               b->data[b->slen] = (unsigned char) '\0';
+       }
+
+       done:;
+       if (static_d == d) d = NULL;
+       bstr__free (d);
+       if (auxf != find) bdestroy (auxf);
+       if (auxr != repl) bdestroy (auxr);
+       return ret;
+}
+
+/*  int bfindreplace (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string with a replace string after a
+ *  given point in a bstring.
+ */
+int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) {
+       return findreplaceengine (b, find, repl, pos, binstr);
+}
+
+/*  int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string, ignoring case, with a replace 
+ *  string after a given point in a bstring.
+ */
+int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) {
+       return findreplaceengine (b, find, repl, pos, binstrcaseless);
+}
+
+/*  int binsertch (bstring b, int pos, int len, unsigned char fill)
+ *
+ *  Inserts the character fill repeatedly into b at position pos for a 
+ *  length len.  If the position pos is past the end of b, then the 
+ *  character "fill" is appended as necessary to make up the gap between the 
+ *  end of b and the position pos + len.
+ */
+int binsertch (bstring b, int pos, int len, unsigned char fill) {
+int d, l, i;
+
+       if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || len < 0) return BSTR_ERR;
+
+       /* Compute the two possible end pointers */
+       d = b->slen + len;
+       l = pos + len;
+       if ((d|l) < 0) return BSTR_ERR;
+
+       if (l > d) {
+               /* Inserting past the end of the string */
+               if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR;
+               pos = b->slen;
+               b->slen = l;
+       } else {
+               /* Inserting in the middle of the string */
+               if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR;
+               for (i = d - 1; i >= l; i--) {
+                       b->data[i] = b->data[i - len];
+               }
+               b->slen = d;
+       }
+
+       for (i=pos; i < l; i++) b->data[i] = fill;
+       b->data[b->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bpattern (bstring b, int len)
+ *
+ *  Replicate the bstring, b in place, end to end repeatedly until it 
+ *  surpasses len characters, then chop the result to exactly len characters. 
+ *  This function operates in-place.  The function will return with BSTR_ERR 
+ *  if b is NULL or of length 0, otherwise BSTR_OK is returned.
+ */
+int bpattern (bstring b, int len) {
+int i, d;
+
+       d = blength (b);
+       if (d <= 0 || len < 0 || balloc (b, len + 1) != BSTR_OK) return BSTR_ERR;
+       if (len > 0) {
+               if (d == 1) return bsetstr (b, len, NULL, b->data[0]);
+               for (i = d; i < len; i++) b->data[i] = b->data[i - d];
+       }
+       b->data[len] = (unsigned char) '\0';
+       b->slen = len;
+       return BSTR_OK;
+}
+
+#define BS_BUFF_SZ (1024)
+
+/*  int breada (bstring b, bNread readPtr, void * parm)
+ *
+ *  Use a finite buffer fread-like function readPtr to concatenate to the 
+ *  bstring b the entire contents of file-like source data in a roughly 
+ *  efficient way.
+ */
+int breada (bstring b, bNread readPtr, void * parm) {
+int i, l, n;
+
+       if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || readPtr == NULL) return BSTR_ERR;
+
+       i = b->slen;
+       for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) {
+               if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR;
+               l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm);
+               i += l;
+               b->slen = i;
+               if (i < n) break;
+       }
+
+       b->data[i] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  bstring bread (bNread readPtr, void * parm)
+ *
+ *  Use a finite buffer fread-like function readPtr to create a bstring 
+ *  filled with the entire contents of file-like source data in a roughly 
+ *  efficient way.
+ */
+bstring bread (bNread readPtr, void * parm) {
+bstring buff;
+
+       if (0 > breada (buff = bfromcstr (""), readPtr, parm)) {
+               bdestroy (buff);
+               return NULL;
+       }
+       return buff;
+}
+
+/*  int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator)
+ *
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated to the end of the
+ *  bstring b.  The stream read is terminated by the passed in terminator 
+ *  parameter.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  function returns with a partial result in b.  If there is an empty partial
+ *  result, 1 is returned.  If no characters are read, or there is some other 
+ *  detectable error, BSTR_ERR is returned.
+ */
+int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) {
+int c, d, e;
+
+       if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
+       d = 0;
+       e = b->mlen - 2;
+
+       while ((c = getcPtr (parm)) >= 0) {
+               if (d > e) {
+                       b->slen = d;
+                       if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+                       e = b->mlen - 2;
+               }
+               b->data[d] = (unsigned char) c;
+               d++;
+               if (c == terminator) break;
+       }
+
+       b->data[d] = (unsigned char) '\0';
+       b->slen = d;
+
+       return d == 0 && c < 0;
+}
+
+/*  int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator)
+ *
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated to the end of the
+ *  bstring b.  The stream read is terminated by the passed in terminator 
+ *  parameter.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  function returns with a partial result concatentated to b.  If there is 
+ *  an empty partial result, 1 is returned.  If no characters are read, or 
+ *  there is some other detectable error, BSTR_ERR is returned.
+ */
+int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) {
+int c, d, e;
+
+       if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
+       d = b->slen;
+       e = b->mlen - 2;
+
+       while ((c = getcPtr (parm)) >= 0) {
+               if (d > e) {
+                       b->slen = d;
+                       if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+                       e = b->mlen - 2;
+               }
+               b->data[d] = (unsigned char) c;
+               d++;
+               if (c == terminator) break;
+       }
+
+       b->data[d] = (unsigned char) '\0';
+       b->slen = d;
+
+       return d == 0 && c < 0;
+}
+
+/*  bstring bgets (bNgetc getcPtr, void * parm, char terminator)
+ *
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated into a bstring.  
+ *  The stream read is terminated by the passed in terminator function.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  result obtained thus far is returned.  If no characters are read, or 
+ *  there is some other detectable error, NULL is returned.
+ */
+bstring bgets (bNgetc getcPtr, void * parm, char terminator) {
+bstring buff;
+
+       if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) {
+               bdestroy (buff);
+               buff = NULL;
+       }
+       return buff;
+}
+
+struct bStream {
+       bstring buff;           /* Buffer for over-reads */
+       void * parm;            /* The stream handle for core stream */
+       bNread readFnPtr;       /* fread compatible fnptr for core stream */
+       int isEOF;              /* track file's EOF state */
+       int maxBuffSz;
+};
+
+/*  struct bStream * bsopen (bNread readPtr, void * parm)
+ *
+ *  Wrap a given open stream (described by a fread compatible function 
+ *  pointer and stream handle) into an open bStream suitable for the bstring 
+ *  library streaming functions.
+ */
+struct bStream * bsopen (bNread readPtr, void * parm) {
+struct bStream * s;
+
+       if (readPtr == NULL) return NULL;
+       s = (struct bStream *) bstr__alloc (sizeof (struct bStream));
+       if (s == NULL) return NULL;
+       s->parm = parm;
+       s->buff = bfromcstr ("");
+       s->readFnPtr = readPtr;
+       s->maxBuffSz = BS_BUFF_SZ;
+       s->isEOF = 0;
+       return s;
+}
+
+/*  int bsbufflength (struct bStream * s, int sz)
+ *
+ *  Set the length of the buffer used by the bStream.  If sz is zero, the 
+ *  length is not set.  This function returns with the previous length.
+ */
+int bsbufflength (struct bStream * s, int sz) {
+int oldSz;
+       if (s == NULL || sz < 0) return BSTR_ERR;
+       oldSz = s->maxBuffSz;
+       if (sz > 0) s->maxBuffSz = sz;
+       return oldSz;
+}
+
+int bseof (const struct bStream * s) {
+       if (s == NULL || s->readFnPtr == NULL) return BSTR_ERR;
+       return s->isEOF && (s->buff->slen == 0);
+}
+
+/*  void * bsclose (struct bStream * s)
+ *
+ *  Close the bStream, and return the handle to the stream that was originally
+ *  used to open the given stream.
+ */
+void * bsclose (struct bStream * s) {
+void * parm;
+       if (s == NULL) return NULL;
+       s->readFnPtr = NULL;
+       if (s->buff) bdestroy (s->buff);
+       s->buff = NULL;
+       parm = s->parm;
+       s->parm = NULL;
+       s->isEOF = 1;
+       bstr__free (s);
+       return parm;
+}
+
+/*  int bsreadlna (bstring r, struct bStream * s, char terminator)
+ *
+ *  Read a bstring terminated by the terminator character or the end of the
+ *  stream from the bStream (s) and return it into the parameter r.  This 
+ *  function may read additional characters from the core stream that are not 
+ *  returned, but will be retained for subsequent read operations.
+ */
+int bsreadlna (bstring r, struct bStream * s, char terminator) {
+int i, l, ret, rlo;
+char * b;
+struct tagbstring x;
+
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 ||
+           r->slen < 0 || r->mlen < r->slen) return BSTR_ERR;
+       l = s->buff->slen;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       b = (char *) s->buff->data;
+       x.data = (unsigned char *) b;
+
+       /* First check if the current buffer holds the terminator */
+       b[l] = terminator; /* Set sentinel */
+       for (i=0; b[i] != terminator; i++) ;
+       if (i < l) {
+               x.slen = i + 1;
+               ret = bconcat (r, &x);
+               s->buff->slen = l;
+               if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
+               return BSTR_OK;
+       }
+
+       rlo = r->slen;
+
+       /* If not then just concatenate the entire buffer to the output */
+       x.slen = l;
+       if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
+
+       /* Perform direct in-place reads into the destination to allow for
+          the minimum of data-copies */
+       for (;;) {
+               if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
+               b = (char *) (r->data + r->slen);
+               l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
+               if (l <= 0) {
+                       r->data[r->slen] = (unsigned char) '\0';
+                       s->buff->slen = 0;
+                       s->isEOF = 1;
+                       /* If nothing was read return with an error message */
+                       return BSTR_ERR & -(r->slen == rlo);
+               }
+               b[l] = terminator; /* Set sentinel */
+               for (i=0; b[i] != terminator; i++) ;
+               if (i < l) break;
+               r->slen += l;
+       }
+
+       /* Terminator found, push over-read back to buffer */
+       i++;
+       r->slen += i;
+       s->buff->slen = l - i;
+       bstr__memcpy (s->buff->data, b + i, l - i);
+       r->data[r->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bsreadlnsa (bstring r, struct bStream * s, bstring term)
+ *
+ *  Read a bstring terminated by any character in the term string or the end 
+ *  of the stream from the bStream (s) and return it into the parameter r.  
+ *  This function may read additional characters from the core stream that 
+ *  are not returned, but will be retained for subsequent read operations.
+ */
+int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) {
+int i, l, ret, rlo;
+unsigned char * b;
+struct tagbstring x;
+struct charField cf;
+
+       if (s == NULL || s->buff == NULL || r == NULL || term == NULL ||
+           term->data == NULL || r->mlen <= 0 || r->slen < 0 ||
+           r->mlen < r->slen) return BSTR_ERR;
+       if (term->slen == 1) return bsreadlna (r, s, term->data[0]);
+       if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR;
+
+       l = s->buff->slen;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       b = (unsigned char *) s->buff->data;
+       x.data = b;
+
+       /* First check if the current buffer holds the terminator */
+       b[l] = term->data[0]; /* Set sentinel */
+       for (i=0; !testInCharField (&cf, b[i]); i++) ;
+       if (i < l) {
+               x.slen = i + 1;
+               ret = bconcat (r, &x);
+               s->buff->slen = l;
+               if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
+               return BSTR_OK;
+       }
+
+       rlo = r->slen;
+
+       /* If not then just concatenate the entire buffer to the output */
+       x.slen = l;
+       if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
+
+       /* Perform direct in-place reads into the destination to allow for
+          the minimum of data-copies */
+       for (;;) {
+               if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
+               b = (unsigned char *) (r->data + r->slen);
+               l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
+               if (l <= 0) {
+                       r->data[r->slen] = (unsigned char) '\0';
+                       s->buff->slen = 0;
+                       s->isEOF = 1;
+                       /* If nothing was read return with an error message */
+                       return BSTR_ERR & -(r->slen == rlo);
+               }
+
+               b[l] = term->data[0]; /* Set sentinel */
+               for (i=0; !testInCharField (&cf, b[i]); i++) ;
+               if (i < l) break;
+               r->slen += l;
+       }
+
+       /* Terminator found, push over-read back to buffer */
+       i++;
+       r->slen += i;
+       s->buff->slen = l - i;
+       bstr__memcpy (s->buff->data, b + i, l - i);
+       r->data[r->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bsreada (bstring r, struct bStream * s, int n)
+ *
+ *  Read a bstring of length n (or, if it is fewer, as many bytes as is 
+ *  remaining) from the bStream.  This function may read additional 
+ *  characters from the core stream that are not returned, but will be 
+ *  retained for subsequent read operations.  This function will not read
+ *  additional characters from the core stream beyond virtual stream pointer.
+ */
+int bsreada (bstring r, struct bStream * s, int n) {
+int l, ret, orslen;
+char * b;
+struct tagbstring x;
+
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
+        || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR;
+
+       n += r->slen;
+       if (n <= 0) return BSTR_ERR;
+
+       l = s->buff->slen;
+
+       orslen = r->slen;
+
+       if (0 == l) {
+               if (s->isEOF) return BSTR_ERR;
+               if (r->mlen > n) {
+                       l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm);
+                       if (0 >= l || l > n - r->slen) {
+                               s->isEOF = 1;
+                               return BSTR_ERR;
+                       }
+                       r->slen += l;
+                       r->data[r->slen] = (unsigned char) '\0';
+                       return 0;
+               }
+       }
+
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       b = (char *) s->buff->data;
+       x.data = (unsigned char *) b;
+
+       do {
+               if (l + r->slen >= n) {
+                       x.slen = n - r->slen;
+                       ret = bconcat (r, &x);
+                       s->buff->slen = l;
+                       if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen);
+                       return BSTR_ERR & -(r->slen == orslen);
+               }
+
+               x.slen = l;
+               if (BSTR_OK != bconcat (r, &x)) break;
+
+               l = n - r->slen;
+               if (l > s->maxBuffSz) l = s->maxBuffSz;
+
+               l = (int) s->readFnPtr (b, 1, l, s->parm);
+
+       } while (l > 0);
+       if (l < 0) l = 0;
+       if (l == 0) s->isEOF = 1;
+       s->buff->slen = l;
+       return BSTR_ERR & -(r->slen == orslen);
+}
+
+/*  int bsreadln (bstring r, struct bStream * s, char terminator)
+ *
+ *  Read a bstring terminated by the terminator character or the end of the
+ *  stream from the bStream (s) and return it into the parameter r.  This 
+ *  function may read additional characters from the core stream that are not 
+ *  returned, but will be retained for subsequent read operations.
+ */
+int bsreadln (bstring r, struct bStream * s, char terminator) {
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0)
+               return BSTR_ERR;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       r->slen = 0;
+       return bsreadlna (r, s, terminator);
+}
+
+/*  int bsreadlns (bstring r, struct bStream * s, bstring term)
+ *
+ *  Read a bstring terminated by any character in the term string or the end 
+ *  of the stream from the bStream (s) and return it into the parameter r.  
+ *  This function may read additional characters from the core stream that 
+ *  are not returned, but will be retained for subsequent read operations.
+ */
+int bsreadlns (bstring r, struct bStream * s, const_bstring term) {
+       if (s == NULL || s->buff == NULL || r == NULL || term == NULL 
+        || term->data == NULL || r->mlen <= 0) return BSTR_ERR;
+       if (term->slen == 1) return bsreadln (r, s, term->data[0]);
+       if (term->slen < 1) return BSTR_ERR;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       r->slen = 0;
+       return bsreadlnsa (r, s, term);
+}
+
+/*  int bsread (bstring r, struct bStream * s, int n)
+ *
+ *  Read a bstring of length n (or, if it is fewer, as many bytes as is 
+ *  remaining) from the bStream.  This function may read additional 
+ *  characters from the core stream that are not returned, but will be 
+ *  retained for subsequent read operations.  This function will not read
+ *  additional characters from the core stream beyond virtual stream pointer.
+ */
+int bsread (bstring r, struct bStream * s, int n) {
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
+        || n <= 0) return BSTR_ERR;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       r->slen = 0;
+       return bsreada (r, s, n);
+}
+
+/*  int bsunread (struct bStream * s, const_bstring b)
+ *
+ *  Insert a bstring into the bStream at the current position.  These 
+ *  characters will be read prior to those that actually come from the core 
+ *  stream.
+ */
+int bsunread (struct bStream * s, const_bstring b) {
+       if (s == NULL || s->buff == NULL) return BSTR_ERR;
+       return binsert (s->buff, 0, b, (unsigned char) '?');
+}
+
+/*  int bspeek (bstring r, const struct bStream * s)
+ *
+ *  Return the currently buffered characters from the bStream that will be 
+ *  read prior to reads from the core stream.
+ */
+int bspeek (bstring r, const struct bStream * s) {
+       if (s == NULL || s->buff == NULL) return BSTR_ERR;
+       return bassign (r, s->buff);
+}
+
+/*  bstring bjoin (const struct bstrList * bl, const_bstring sep);
+ *
+ *  Join the entries of a bstrList into one bstring by sequentially 
+ *  concatenating them with the sep string in between.  If there is an error 
+ *  NULL is returned, otherwise a bstring with the correct result is returned.
+ */
+bstring bjoin (const struct bstrList * bl, const_bstring sep) {
+bstring b;
+int i, c, v;
+
+       if (bl == NULL || bl->qty < 0) return NULL;
+       if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL;
+
+       for (i = 0, c = 1; i < bl->qty; i++) {
+               v = bl->entry[i]->slen;
+               if (v < 0) return NULL; /* Invalid input */
+               c += v;
+               if (c < 0) return NULL; /* Wrap around ?? */
+       }
+
+       if (sep != NULL) c += (bl->qty - 1) * sep->slen;
+
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (NULL == b) return NULL; /* Out of memory */
+       b->data = (unsigned char *) bstr__alloc (c);
+       if (b->data == NULL) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       b->mlen = c;
+       b->slen = c-1;
+
+       for (i = 0, c = 0; i < bl->qty; i++) {
+               if (i > 0 && sep != NULL) {
+                       bstr__memcpy (b->data + c, sep->data, sep->slen);
+                       c += sep->slen;
+               }
+               v = bl->entry[i]->slen;
+               bstr__memcpy (b->data + c, bl->entry[i]->data, v);
+               c += v;
+       }
+       b->data[c] = (unsigned char) '\0';
+       return b;
+}
+
+#define BSSSC_BUFF_LEN (256)
+
+/*  int bssplitscb (struct bStream * s, const_bstring splitStr, 
+ *     int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings read from a stream 
+ *  divided by any of the characters in splitStr.  An empty splitStr causes 
+ *  the whole stream to be iterated once.
+ *
+ *  Note: At the point of calling the cb function, the bStream pointer is 
+ *  pointed exactly at the position right after having read the split 
+ *  character.  The cb function can act on the stream by causing the bStream
+ *  pointer to move, and bssplitscb will continue by starting the next split
+ *  at the position of the pointer after the return from cb.
+ *
+ *  However, if the cb causes the bStream s to be destroyed then the cb must
+ *  return with a negative value, otherwise bssplitscb will continue in an 
+ *  undefined manner.
+ */
+int bssplitscb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
+struct charField chrs;
+bstring buff;
+int i, p, ret;
+
+       if (cb == NULL || s == NULL || s->readFnPtr == NULL 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+       if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
+
+       if (splitStr->slen == 0) {
+               while (bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ;
+               if ((ret = cb (parm, 0, buff)) > 0) 
+                       ret = 0;
+       } else {
+               buildCharField (&chrs, splitStr);
+               ret = p = i = 0;
+               for (;;) {
+                       if (i >= buff->slen) {
+                               bsreada (buff, s, BSSSC_BUFF_LEN);
+                               if (i >= buff->slen) {
+                                       if (0 < (ret = cb (parm, p, buff))) ret = 0;
+                                       break;
+                               }
+                       }
+                       if (testInCharField (&chrs, buff->data[i])) {
+                               struct tagbstring t;
+                               unsigned char c;
+
+                               blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1));
+                               if ((ret = bsunread (s, &t)) < 0) break;
+                               buff->slen = i;
+                               c = buff->data[i];
+                               buff->data[i] = (unsigned char) '\0';
+                               if ((ret = cb (parm, p, buff)) < 0) break;
+                               buff->data[i] = c;
+                               buff->slen = 0;
+                               p += i + 1;
+                               i = -1;
+                       }
+                       i++;
+               }
+       }
+
+       bdestroy (buff);
+       return ret;
+}
+
+/*  int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+ *     int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings read from a stream 
+ *  divided by the entire substring splitStr.  An empty splitStr causes 
+ *  each character of the stream to be iterated.
+ *
+ *  Note: At the point of calling the cb function, the bStream pointer is 
+ *  pointed exactly at the position right after having read the split 
+ *  character.  The cb function can act on the stream by causing the bStream
+ *  pointer to move, and bssplitscb will continue by starting the next split
+ *  at the position of the pointer after the return from cb.
+ *
+ *  However, if the cb causes the bStream s to be destroyed then the cb must
+ *  return with a negative value, otherwise bssplitscb will continue in an 
+ *  undefined manner.
+ */
+int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
+bstring buff;
+int i, p, ret;
+
+       if (cb == NULL || s == NULL || s->readFnPtr == NULL 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+       if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm);
+
+       if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
+
+       if (splitStr->slen == 0) {
+               for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) {
+                       if ((ret = cb (parm, 0, buff)) < 0) {
+                               bdestroy (buff);
+                               return ret;
+                       }
+                       buff->slen = 0;
+               }
+               return BSTR_OK;
+       } else {
+               ret = p = i = 0;
+               for (i=p=0;;) {
+                       if ((ret = binstr (buff, 0, splitStr)) >= 0) {
+                               struct tagbstring t;
+                               blk2tbstr (t, buff->data, ret);
+                               i = ret + splitStr->slen;
+                               if ((ret = cb (parm, p, &t)) < 0) break;
+                               p += i;
+                               bdelete (buff, 0, i);
+                       } else {
+                               bsreada (buff, s, BSSSC_BUFF_LEN);
+                               if (bseof (s)) {
+                                       if ((ret = cb (parm, p, buff)) > 0) ret = 0;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       bdestroy (buff);
+       return ret;
+}
+
+/*  int bstrListCreate (void)
+ *
+ *  Create a bstrList.
+ */
+struct bstrList * bstrListCreate (void) {
+struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (sl) {
+               sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring));
+               if (!sl->entry) {
+                       bstr__free (sl);
+                       sl = NULL;
+               } else {
+                       sl->qty = 0;
+                       sl->mlen = 1;
+               }
+       }
+       return sl;
+}
+
+/*  int bstrListDestroy (struct bstrList * sl)
+ *
+ *  Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate.
+ */
+int bstrListDestroy (struct bstrList * sl) {
+int i;
+       if (sl == NULL || sl->qty < 0) return BSTR_ERR;
+       for (i=0; i < sl->qty; i++) {
+               if (sl->entry[i]) {
+                       bdestroy (sl->entry[i]);
+                       sl->entry[i] = NULL;
+               }
+       }
+       sl->qty  = -1;
+       sl->mlen = -1;
+       bstr__free (sl->entry);
+       sl->entry = NULL;
+       bstr__free (sl);
+       return BSTR_OK;
+}
+
+/*  int bstrListAlloc (struct bstrList * sl, int msz)
+ *
+ *  Ensure that there is memory for at least msz number of entries for the
+ *  list.
+ */
+int bstrListAlloc (struct bstrList * sl, int msz) {
+bstring * l;
+int smsz;
+size_t nsz;
+       if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+       if (sl->mlen >= msz) return BSTR_OK;
+       smsz = snapUpSize (msz);
+       nsz = ((size_t) smsz) * sizeof (bstring);
+       if (nsz < (size_t) smsz) return BSTR_ERR;
+       l = (bstring *) bstr__realloc (sl->entry, nsz);
+       if (!l) {
+               smsz = msz;
+               nsz = ((size_t) smsz) * sizeof (bstring);
+               l = (bstring *) bstr__realloc (sl->entry, nsz);
+               if (!l) return BSTR_ERR;
+       }
+       sl->mlen = smsz;
+       sl->entry = l;
+       return BSTR_OK;
+}
+
+/*  int bstrListAllocMin (struct bstrList * sl, int msz)
+ *
+ *  Try to allocate the minimum amount of memory for the list to include at
+ *  least msz entries or sl->qty whichever is greater.
+ */
+int bstrListAllocMin (struct bstrList * sl, int msz) {
+bstring * l;
+size_t nsz;
+       if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+       if (msz < sl->qty) msz = sl->qty;
+       if (sl->mlen == msz) return BSTR_OK;
+       nsz = ((size_t) msz) * sizeof (bstring);
+       if (nsz < (size_t) msz) return BSTR_ERR;
+       l = (bstring *) bstr__realloc (sl->entry, nsz);
+       if (!l) return BSTR_ERR;
+       sl->mlen = msz;
+       sl->entry = l;
+       return BSTR_OK;
+}
+
+/*  int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+ *     int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings over str divided by the
+ *  character in splitChar.
+ *
+ *  Note: Non-destructive modification of str from within the cb function 
+ *  while performing this split is not undefined.  bsplitcb behaves in 
+ *  sequential lock step with calls to cb.  I.e., after returning from a cb 
+ *  that return a non-negative integer, bsplitcb continues from the position 
+ *  1 character after the last detected split character and it will halt 
+ *  immediately if the length of str falls below this point.  However, if the 
+ *  cb function destroys str, then it *must* return with a negative value, 
+ *  otherwise bsplitcb will continue in an undefined manner.
+ */
+int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm) {
+int i, p, ret;
+
+       if (cb == NULL || str == NULL || pos < 0 || pos > str->slen) 
+               return BSTR_ERR;
+
+       p = pos;
+       do {
+               for (i=p; i < str->slen; i++) {
+                       if (str->data[i] == splitChar) break;
+               }
+               if ((ret = cb (parm, p, i - p)) < 0) return ret;
+               p = i + 1;
+       } while (p <= str->slen);
+       return BSTR_OK;
+}
+
+/*  int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+ *     int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings over str divided by any 
+ *  of the characters in splitStr.  An empty splitStr causes the whole str to
+ *  be iterated once.
+ *
+ *  Note: Non-destructive modification of str from within the cb function 
+ *  while performing this split is not undefined.  bsplitscb behaves in 
+ *  sequential lock step with calls to cb.  I.e., after returning from a cb 
+ *  that return a non-negative integer, bsplitscb continues from the position 
+ *  1 character after the last detected split character and it will halt 
+ *  immediately if the length of str falls below this point.  However, if the 
+ *  cb function destroys str, then it *must* return with a negative value, 
+ *  otherwise bsplitscb will continue in an undefined manner.
+ */
+int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm) {
+struct charField chrs;
+int i, p, ret;
+
+       if (cb == NULL || str == NULL || pos < 0 || pos > str->slen 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+       if (splitStr->slen == 0) {
+               if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0;
+               return ret;
+       }
+
+       if (splitStr->slen == 1) 
+               return bsplitcb (str, splitStr->data[0], pos, cb, parm);
+
+       buildCharField (&chrs, splitStr);
+
+       p = pos;
+       do {
+               for (i=p; i < str->slen; i++) {
+                       if (testInCharField (&chrs, str->data[i])) break;
+               }
+               if ((ret = cb (parm, p, i - p)) < 0) return ret;
+               p = i + 1;
+       } while (p <= str->slen);
+       return BSTR_OK;
+}
+
+/*  int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+ *     int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings over str divided by the 
+ *  substring splitStr.  An empty splitStr causes the whole str to be 
+ *  iterated once.
+ *
+ *  Note: Non-destructive modification of str from within the cb function 
+ *  while performing this split is not undefined.  bsplitstrcb behaves in 
+ *  sequential lock step with calls to cb.  I.e., after returning from a cb 
+ *  that return a non-negative integer, bsplitscb continues from the position 
+ *  1 character after the last detected split character and it will halt 
+ *  immediately if the length of str falls below this point.  However, if the 
+ *  cb function destroys str, then it *must* return with a negative value, 
+ *  otherwise bsplitscb will continue in an undefined manner.
+ */
+int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm) {
+int i, p, ret;
+
+       if (cb == NULL || str == NULL || pos < 0 || pos > str->slen 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+       if (0 == splitStr->slen) {
+               for (i=pos; i < str->slen; i++) {
+                       if ((ret = cb (parm, i, 1)) < 0) return ret;
+               }
+               return BSTR_OK;
+       }
+
+       if (splitStr->slen == 1) 
+               return bsplitcb (str, splitStr->data[0], pos, cb, parm);
+
+       for (i=p=pos; i <= str->slen - splitStr->slen; i++) {
+               if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) {
+                       if ((ret = cb (parm, p, i - p)) < 0) return ret;
+                       i += splitStr->slen;
+                       p = i;
+               }
+       }
+       if ((ret = cb (parm, p, str->slen - p)) < 0) return ret;
+       return BSTR_OK;
+}
+
+struct genBstrList {
+       bstring b;
+       struct bstrList * bl;
+};
+
+static int bscb (void * parm, int ofs, int len) {
+struct genBstrList * g = (struct genBstrList *) parm;
+       if (g->bl->qty >= g->bl->mlen) {
+               int mlen = g->bl->mlen * 2;
+               bstring * tbl;
+
+               while (g->bl->qty >= mlen) {
+                       if (mlen < g->bl->mlen) return BSTR_ERR;
+                       mlen += mlen;
+               }
+
+               tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen);
+               if (tbl == NULL) return BSTR_ERR;
+
+               g->bl->entry = tbl;
+               g->bl->mlen = mlen;
+       }
+
+       g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len);
+       g->bl->qty++;
+       return BSTR_OK;
+}
+
+/*  struct bstrList * bsplit (const_bstring str, unsigned char splitChar)
+ *
+ *  Create an array of sequential substrings from str divided by the character
+ *  splitChar.  
+ */
+struct bstrList * bsplit (const_bstring str, unsigned char splitChar) {
+struct genBstrList g;
+
+       if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
+
+       g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (g.bl == NULL) return NULL;
+       g.bl->mlen = 4;
+       g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+       if (NULL == g.bl->entry) {
+               bstr__free (g.bl);
+               return NULL;
+       }
+
+       g.b = (bstring) str;
+       g.bl->qty = 0;
+       if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) {
+               bstrListDestroy (g.bl);
+               return NULL;
+       }
+       return g.bl;
+}
+
+/*  struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr)
+ *
+ *  Create an array of sequential substrings from str divided by the entire
+ *  substring splitStr.
+ */
+struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) {
+struct genBstrList g;
+
+       if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
+
+       g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (g.bl == NULL) return NULL;
+       g.bl->mlen = 4;
+       g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+       if (NULL == g.bl->entry) {
+               bstr__free (g.bl);
+               return NULL;
+       }
+
+       g.b = (bstring) str;
+       g.bl->qty = 0;
+       if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) {
+               bstrListDestroy (g.bl);
+               return NULL;
+       }
+       return g.bl;
+}
+
+/*  struct bstrList * bsplits (const_bstring str, bstring splitStr)
+ *
+ *  Create an array of sequential substrings from str divided by any of the 
+ *  characters in splitStr.  An empty splitStr causes a single entry bstrList
+ *  containing a copy of str to be returned.
+ */
+struct bstrList * bsplits (const_bstring str, const_bstring splitStr) {
+struct genBstrList g;
+
+       if (     str == NULL ||      str->slen < 0 ||      str->data == NULL ||
+           splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL)
+               return NULL;
+
+       g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (g.bl == NULL) return NULL;
+       g.bl->mlen = 4;
+       g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+       if (NULL == g.bl->entry) {
+               bstr__free (g.bl);
+               return NULL;
+       }
+       g.b = (bstring) str;
+       g.bl->qty = 0;
+
+       if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) {
+               bstrListDestroy (g.bl);
+               return NULL;
+       }
+       return g.bl;
+}
+
+#if defined (__TURBOC__) && !defined (__BORLANDC__)
+# ifndef BSTRLIB_NOVSNP
+#  define BSTRLIB_NOVSNP
+# endif
+#endif
+
+/* Give WATCOM C/C++, MSVC some latitude for their non-support of vsnprintf */
+#if defined(__WATCOMC__) || defined(_MSC_VER)
+#define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);}
+#else
+#ifdef BSTRLIB_NOVSNP
+/* This is just a hack.  If you are using a system without a vsnprintf, it is 
+   not recommended that bformat be used at all. */
+#define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;}
+#define START_VSNBUFF (256)
+#else
+
+#ifdef __GNUC__
+/* Something is making gcc complain about this prototype not being here, so 
+   I've just gone ahead and put it in. */
+extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg);
+#endif
+
+#define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);}
+#endif
+#endif
+
+#if !defined (BSTRLIB_NOVSNP)
+
+#ifndef START_VSNBUFF
+#define START_VSNBUFF (16)
+#endif
+
+/* On IRIX vsnprintf returns n-1 when the operation would overflow the target 
+   buffer, WATCOM and MSVC both return -1, while C99 requires that the 
+   returned value be exactly what the length would be if the buffer would be
+   large enough.  This leads to the idea that if the return value is larger 
+   than n, then changing n to the return value will reduce the number of
+   iterations required. */
+
+/*  int bformata (bstring b, const char * fmt, ...)
+ *
+ *  After the first parameter, it takes the same parameters as printf (), but 
+ *  rather than outputting results to stdio, it appends the results to 
+ *  a bstring which contains what would have been output. Note that if there 
+ *  is an early generation of a '\0' character, the bstring will be truncated 
+ *  to this end point.
+ */
+int bformata (bstring b, const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+       if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 
+        || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+       /* Since the length is not determinable beforehand, a search is
+          performed using the truncating "vsnprintf" call (to avoid buffer
+          overflows) on increasing potential sizes for the output result. */
+
+       if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+       if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+               n = 1;
+               if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+       }
+
+       for (;;) {
+               va_start (arglist, fmt);
+               exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+               va_end (arglist);
+
+               buff->data[n] = (unsigned char) '\0';
+               buff->slen = (int) (strlen) ((char *) buff->data);
+
+               if (buff->slen < n) break;
+
+               if (r > n) n = r; else n += n;
+
+               if (BSTR_OK != balloc (buff, n + 2)) {
+                       bdestroy (buff);
+                       return BSTR_ERR;
+               }
+       }
+
+       r = bconcat (b, buff);
+       bdestroy (buff);
+       return r;
+}
+
+/*  int bassignformat (bstring b, const char * fmt, ...)
+ *
+ *  After the first parameter, it takes the same parameters as printf (), but 
+ *  rather than outputting results to stdio, it outputs the results to 
+ *  the bstring parameter b. Note that if there is an early generation of a 
+ *  '\0' character, the bstring will be truncated to this end point.
+ */
+int bassignformat (bstring b, const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+       if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 
+        || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+       /* Since the length is not determinable beforehand, a search is
+          performed using the truncating "vsnprintf" call (to avoid buffer
+          overflows) on increasing potential sizes for the output result. */
+
+       if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+       if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+               n = 1;
+               if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+       }
+
+       for (;;) {
+               va_start (arglist, fmt);
+               exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+               va_end (arglist);
+
+               buff->data[n] = (unsigned char) '\0';
+               buff->slen = (int) (strlen) ((char *) buff->data);
+
+               if (buff->slen < n) break;
+
+               if (r > n) n = r; else n += n;
+
+               if (BSTR_OK != balloc (buff, n + 2)) {
+                       bdestroy (buff);
+                       return BSTR_ERR;
+               }
+       }
+
+       r = bassign (b, buff);
+       bdestroy (buff);
+       return r;
+}
+
+/*  bstring bformat (const char * fmt, ...)
+ *
+ *  Takes the same parameters as printf (), but rather than outputting results
+ *  to stdio, it forms a bstring which contains what would have been output.
+ *  Note that if there is an early generation of a '\0' character, the 
+ *  bstring will be truncated to this end point.
+ */
+bstring bformat (const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+       if (fmt == NULL) return NULL;
+
+       /* Since the length is not determinable beforehand, a search is
+          performed using the truncating "vsnprintf" call (to avoid buffer
+          overflows) on increasing potential sizes for the output result. */
+
+       if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+       if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+               n = 1;
+               if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL;
+       }
+
+       for (;;) {
+               va_start (arglist, fmt);
+               exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+               va_end (arglist);
+
+               buff->data[n] = (unsigned char) '\0';
+               buff->slen = (int) (strlen) ((char *) buff->data);
+
+               if (buff->slen < n) break;
+
+               if (r > n) n = r; else n += n;
+
+               if (BSTR_OK != balloc (buff, n + 2)) {
+                       bdestroy (buff);
+                       return NULL;
+               }
+       }
+
+       return buff;
+}
+
+/*  int bvcformata (bstring b, int count, const char * fmt, va_list arglist)
+ *
+ *  The bvcformata function formats data under control of the format control 
+ *  string fmt and attempts to append the result to b.  The fmt parameter is 
+ *  the same as that of the printf function.  The variable argument list is 
+ *  replaced with arglist, which has been initialized by the va_start macro.
+ *  The size of the output is upper bounded by count.  If the required output
+ *  exceeds count, the string b is not augmented with any contents and a value
+ *  below BSTR_ERR is returned.  If a value below -count is returned then it
+ *  is recommended that the negative of this value be used as an update to the
+ *  count in a subsequent pass.  On other errors, such as running out of 
+ *  memory, parameter errors or numeric wrap around BSTR_ERR is returned.  
+ *  BSTR_OK is returned when the output is successfully generated and 
+ *  appended to b.
+ *
+ *  Note: There is no sanity checking of arglist, and this function is
+ *  destructive of the contents of b from the b->slen point onward.  If there 
+ *  is an early generation of a '\0' character, the bstring will be truncated 
+ *  to this end point.
+ */
+int bvcformata (bstring b, int count, const char * fmt, va_list arg) {
+int n, r, l;
+
+       if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL
+        || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+       if (count > (n = b->slen + count) + 2) return BSTR_ERR;
+       if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR;
+
+       exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg);
+
+       /* Did the operation complete successfully within bounds? */
+
+       if (n >= (l = b->slen + (int) (strlen) ((const char *) b->data + b->slen))) {
+               b->slen = l;
+               return BSTR_OK;
+       }
+
+       /* Abort, since the buffer was not large enough.  The return value 
+          tries to help set what the retry length should be. */
+
+       b->data[b->slen] = '\0';
+       if (r > count+1) l = r; else {
+               l = count+count;
+               if (count > l) l = INT_MAX;
+       }
+       n = -l;
+       if (n > BSTR_ERR-1) n = BSTR_ERR-1;
+       return n;
+}
+
+#endif
index 75b41c112984dcd5bf827acc9189c724d1dc5f7e..cd3ca24fda0587571a63c6498934b8f12f1ce153 100644 (file)
@@ -11,7 +11,7 @@ libcnid_cdb_la_SOURCES = cnid_cdb_add.c \
                         cnid_cdb_rebuild_add.c \
                         cnid_cdb.h
 libcnid_cdb_la_CFLAGS = @BDB_CFLAGS@
-libcnid_cdb_la_LIBADD = @BDB_LIBS@
+libcnid_cdb_la_LIBADD = @BDB_LIBS@ @PTHREAD_LIBS@
 
 if USE_CDB_BACKEND
 noinst_LTLIBRARIES = libcnid_cdb.la
index 9231cf5508f72648af2dd114da9143879cb0b978..263f24145063bac89aa7b0593a2a6d095f1de0cb 100644 (file)
@@ -166,7 +166,7 @@ struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int fla
 static void block_signal( u_int32_t flags)
 {
     if ((flags & CNID_FLAG_BLOCK)) {
-        sigprocmask(SIG_BLOCK, &sigblockset, NULL);
+        pthread_sigmask(SIG_BLOCK, &sigblockset, NULL);
     }
 }
 
@@ -174,7 +174,7 @@ static void block_signal( u_int32_t flags)
 static void unblock_signal(u_int32_t flags)
 {
     if ((flags & CNID_FLAG_BLOCK)) {
-        sigprocmask(SIG_UNBLOCK, &sigblockset, NULL);
+        pthread_sigmask(SIG_UNBLOCK, &sigblockset, NULL);
     }
 }
 
index dfeb97cecfa6d3f4c0a3a4b2684bdcde1b8089ad..c98adafcd152f872d24904bf0a69c97112afb9e0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: cnid_dbd.c,v 1.17 2010-03-31 09:47:32 franklahm Exp $
+ * $Id: cnid_dbd.c,v 1.17 2010/03/31 09:47:32 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
@@ -143,8 +143,6 @@ static int write_vec(int fd, struct iovec *iov, size_t towrite)
     ssize_t len;
     size_t len1;
 
-    LOG(log_maxdebug, logtype_cnid, "write_vec: request to write %d bytes", towrite);
-
     len1 =  iov[1].iov_len;
     while (towrite > 0) {
         if (((len = writev(fd, iov, 2)) == -1 && errno == EINTR) || !len)
@@ -213,8 +211,6 @@ static int send_packet(CNID_private *db, struct cnid_dbd_rqst *rqst)
     struct iovec iov[2];
     size_t towrite;
 
-    LOG(log_maxdebug, logtype_cnid, "send_packet: BEGIN");
-
     if (!rqst->namelen) {
         if (write(db->fd, rqst, sizeof(struct cnid_dbd_rqst)) != sizeof(struct cnid_dbd_rqst)) {
             LOG(log_warning, logtype_cnid, "send_packet: Error/short write rqst (db_dir %s): %s",
@@ -239,7 +235,7 @@ static int send_packet(CNID_private *db, struct cnid_dbd_rqst *rqst)
         return -1;
     }
     
-    LOG(log_maxdebug, logtype_cnid, "send_packet: OK");
+    LOG(log_maxdebug, logtype_cnid, "send_packet: {done}");
     return 0;
 }
 
@@ -299,8 +295,6 @@ static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd
     char *nametmp;
     size_t len;
 
-    LOG(log_maxdebug, logtype_cnid, "dbd_rpc: BEGIN");
-
     if (send_packet(db, rqst) < 0) {
         return -1;
     }
@@ -328,7 +322,7 @@ static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd
         return -1;
     }
 
-    LOG(log_maxdebug, logtype_cnid, "dbd_rpc: END");
+    LOG(log_maxdebug, logtype_cnid, "dbd_rpc: {done}");
 
     return 0;
 }
@@ -339,8 +333,6 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
     time_t orig, t;
     int clean = 1; /* no errors so far - to prevent sleep on first try */
 
-    LOG(log_debug7, logtype_cnid, "transmit: BEGIN");
-
     if (db->changed) {
         /* volume and db don't have the same timestamp
          */
@@ -352,7 +344,7 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
             struct cnid_dbd_rply rply_stamp;
             char  stamp[ADEDLEN_PRIVSYN];
 
-            LOG(log_debug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
+            LOG(log_maxdebug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
             if ((db->fd = init_tsock(db)) < 0) {
                 goto transmit_fail;
             }
@@ -381,11 +373,11 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
                     memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
                 memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
             }
-            LOG(log_debug, logtype_cnid, "transmit: succesfully attached to cnid_dbd for volume '%s' with stamp '%08lx'.", 
+            LOG(log_debug, logtype_cnid, "transmit: attached to '%s', stamp: '%08lx'.", 
                 db->db_dir, *(uint64_t *)stamp);
         }
         if (!dbd_rpc(db, rqst, rply)) {
-            LOG(log_debug7, logtype_cnid, "transmit: END OK");
+            LOG(log_maxdebug, logtype_cnid, "transmit: {done}");
             return 0;
         }
     transmit_fail:
index 608996b28aa3565a94952f2dc5bba680bf0a29d6..0f887e45ec40b5d3d513f249a02002e219b68a43 100644 (file)
@@ -63,7 +63,7 @@ pselect(int count, fd_set * restrict rfds, fd_set * restrict wfds,
         tvp = 0;
 
     if (mask != 0) {
-        rv = sigprocmask(SIG_SETMASK, mask, &omask);
+        rv = pthread_sigmask(SIG_SETMASK, mask, &omask);
         if (rv != 0)
             return rv;
     }
@@ -71,7 +71,7 @@ pselect(int count, fd_set * restrict rfds, fd_set * restrict wfds,
     rv = select(count, rfds, wfds, efds, tvp);
     if (mask != 0) {
         sverrno = errno;
-        sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
+        pthread_sigmask(SIG_SETMASK, &omask, (sigset_t *)0);
         errno = sverrno;
     }
 
index 1bb81b4cfe2c0372c029033e83085f5bade37a05..9c44116a8494944fa15a96346c4b31e02ab9d6de 100644 (file)
@@ -218,7 +218,7 @@ static int dsi_tcp_open(DSI *dsi)
 #define IFF_SLAVE 0
 #endif
 
-static void guess_interface(DSI *dsi, const char *hostname)
+static void guess_interface(DSI *dsi, const char *hostname, const char *port)
 {
     int fd;
     char **start, **list;
@@ -250,11 +250,11 @@ static void guess_interface(DSI *dsi, const char *hostname)
 
         memset(&dsi->server, 0, sizeof(struct sockaddr_storage));
         sa->sin_family = AF_INET;
-        sa->sin_port = htons(548);
+        sa->sin_port = htons(atoi(port));
         sa->sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
 
-        LOG(log_info, logtype_dsi, "dsi_tcp: '%s' on interface '%s' will be used instead.",
-                  getip_string((struct sockaddr *)&dsi->server), ifr.ifr_name);
+        LOG(log_info, logtype_dsi, "dsi_tcp: '%s:%s' on interface '%s' will be used instead.",
+            getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name);
         goto iflist_done;
     }
     LOG(log_info, logtype_dsi, "dsi_tcp (Chooser will not select afp/tcp) "
@@ -408,7 +408,7 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
     freeaddrinfo(servinfo);
 
 interfaces:
-    guess_interface(dsi, hostname);
+    guess_interface(dsi, hostname, port ? port : "548");
     return 1;
 }
 
index f8eeb6b42c9c6e8506d8f6fcd95e713887c96368..52597e9c971ac64af20f01e89ee7d2879f6304e8 100644 (file)
@@ -14,6 +14,7 @@ libutil_la_SOURCES = \
        locking.c   \
        logger.c        \
        module.c        \
+       queue.c     \
        server_child.c  \
        server_ipc.c    \
        server_lock.c   \
index 7ef4fb8a5143cac21ddc8c6bc604d60ca82822a2..c9bce38c39272a66091a6d23ca0bb0093ce4cba8 100644 (file)
@@ -22,8 +22,6 @@
 #include "config.h"
 #endif
 
-#ifdef DEBUG1
-
 #include <sys/types.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -84,65 +82,28 @@ static void (*CatchSignal(int signum,void (*handler)(int )))(int)
  Something really nasty happened - panic !
 ********************************************************************/
 
-static void smb_panic(const char *why)
+void netatalk_panic(const char *why)
 {
-#if 0
-       char *cmd;
-       int result;
-#endif
 #ifdef HAVE_BACKTRACE_SYMBOLS
        void *backtrace_stack[BACKTRACE_STACK_SIZE];
        size_t backtrace_size;
        char **backtrace_strings;
-#endif
-
-#ifdef DEVELOPER
-       {
-               extern char *global_clobber_region_function;
-               extern unsigned int global_clobber_region_line;
-
-               if (global_clobber_region_function) {
-                       DEBUG(0,("smb_panic: clobber_region() last called from [%s(%u)]",
-                                        global_clobber_region_function,
-                                        global_clobber_region_line));
-               } 
-       }
-#endif
 
-#if 0
-       cmd = lp_panic_action();
-       if (cmd && *cmd) {
-               DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmd));
-               result = system(cmd);
-
-               if (result == -1)
-                       DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n",
-                                         strerror(errno)));
-               else
-                       DEBUG(0, ("smb_panic(): action returned status %d\n",
-                                         WEXITSTATUS(result)));
-       }
-       DEBUG(0,("PANIC: %s\n", why));
-#endif
-
-#ifdef HAVE_BACKTRACE_SYMBOLS
        /* get the backtrace (stack frames) */
        backtrace_size = backtrace(backtrace_stack,BACKTRACE_STACK_SIZE);
        backtrace_strings = backtrace_symbols(backtrace_stack, backtrace_size);
 
-       LOG(log_error, logtype_default, "BACKTRACE: %d stack frames:\n", backtrace_size);
+       LOG(log_severe, logtype_default, "BACKTRACE: %d stack frames:", backtrace_size);
        
        if (backtrace_strings) {
                size_t i;
 
                for (i = 0; i < backtrace_size; i++)
-                       LOG(log_error, logtype_default, " #%u %s", i, backtrace_strings[i]);
+                       LOG(log_severe, logtype_default, " #%u %s", i, backtrace_strings[i]);
 
                SAFE_FREE(backtrace_strings);
        }
-
 #endif
-
 }
 
 
@@ -157,11 +118,11 @@ static void fault_report(int sig)
 
        counter++;
 
-       LOG(log_error, logtype_default, "===============================================================");
-       LOG(log_error, logtype_default, "INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION);
-       LOG(log_error, logtype_default, "===============================================================");
+       LOG(log_severe, logtype_default, "===============================================================");
+       LOG(log_severe, logtype_default, "INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION);
+       LOG(log_severe, logtype_default, "===============================================================");
   
-       smb_panic("internal error");
+       netatalk_panic("internal error");
 
        if (cont_fn) {
                cont_fn(NULL);
@@ -199,4 +160,3 @@ void fault_setup(void (*fn)(void *))
 #endif
 }
 
-#endif
diff --git a/libatalk/util/queue.c b/libatalk/util/queue.c
new file mode 100644 (file)
index 0000000..2e7ff5e
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+  $Id: queue.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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
+ * Netatalk utility functions: queue
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <atalk/queue.h>
+
+static qnode_t *alloc_init_node(void *data)
+{
+    qnode_t *node;
+    if ((node = malloc(sizeof(qnode_t))) == NULL)
+        return NULL;
+    node->data = data;
+
+    return node;
+}
+
+/********************************************************************************
+ * Interface
+ *******************************************************************************/
+
+q_t *queue_init(void)
+{
+    q_t *queue;
+
+    if ((queue = alloc_init_node(NULL)) == NULL)
+        return NULL;
+
+    queue->prev = queue->next = queue;
+    return queue;
+}
+
+qnode_t *enqueue(q_t *q, void *data)
+{
+    qnode_t *node;
+
+    if ((node = alloc_init_node(data)) == NULL)
+        return NULL;
+
+    /* insert at tail */
+    node->next = q;
+    node->prev = q->prev;
+    q->prev->next = node;
+    q->prev = node;
+
+    return node;
+}
+
+void *dequeue(q_t *q)
+{
+    qnode_t *node;
+    void *data;
+
+    if (q == NULL || q->next == q)
+        return NULL;
+
+    /* take first node from head */
+    node = q->next;
+    data = node->data;
+    q->next = node->next;
+    node->next->prev = node->prev;
+    free(node);
+
+    return data;    
+}
+
+void queue_destroy(q_t *q, void (*callback)(void *))
+{
+    void *p;
+
+    while ((p = dequeue(q)) != NULL)
+        callback(p);
+
+    free(q);
+    q = NULL;
+}
+
index 85c150c4c37cd9290f0aae8a53a99ab606d8660f..b21bae35f08fe427b4f1f8eed027f0420cb2dd57 100644 (file)
@@ -141,12 +141,12 @@ int server_child_add(server_child *children, const int forkid,
    * chance to add the child in. */
   sigemptyset(&sig);
   sigaddset(&sig, SIGCHLD);
-  sigprocmask(SIG_BLOCK, &sig, &oldsig);
+  pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
 
   /* it's possible that the child could have already died before the
-   * sigprocmask. we need to check for this. */
+   * pthread_sigmask. we need to check for this. */
   if (kill(pid, 0) < 0) {
-    sigprocmask(SIG_SETMASK, &oldsig, NULL);
+    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
     return 1;
   }
 
@@ -154,13 +154,13 @@ int server_child_add(server_child *children, const int forkid,
 
   /* if we already have an entry. just return. */
   if (resolve_child(fork->table, pid)) {
-    sigprocmask(SIG_SETMASK, &oldsig, NULL);
+    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
     return 0;
   }
 
   if ((child = (struct server_child_data *) 
        calloc(1, sizeof(struct server_child_data))) == NULL) {
-    sigprocmask(SIG_SETMASK, &oldsig, NULL);
+    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
     return -1;
   }
 
@@ -169,7 +169,7 @@ int server_child_add(server_child *children, const int forkid,
   child->killed = 0;
   hash_child(fork->table, child);
   children->count++;
-  sigprocmask(SIG_SETMASK, &oldsig, NULL);
+  pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
 
   return 0;
 }
@@ -427,6 +427,6 @@ void server_reset_signal(void)
     sigaddset(&sigs, SIGHUP);
     sigaddset(&sigs, SIGUSR1);
     sigaddset(&sigs, SIGCHLD);
-    sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
         
 }
index d48caa315961ace34097d50d2118518a848b3f23..9b8d14374c8e4fbb6aeaa86716ce7aff2af73c09 100644 (file)
@@ -1,10 +1,9 @@
-
-# Makefile.am for libatalk/adouble/
+# Makefile.am for libatalk/vfs/
 
 noinst_LTLIBRARIES = libvfs.la
 
 libvfs_la_SOURCES = vfs.c unix.c ea.c sys_ea.c ea_sys.c
 
-if USE_NFSv4_ACLS
+if HAVE_ACLS
 libvfs_la_SOURCES += acl.c
 endif
index b9940508dff6a071766b7b871b70c8bfc68bddaa..2b8b4ebfd16acfa10684f1d7f3e694b53354daa0 100644 (file)
@@ -1,5 +1,6 @@
 /*
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   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
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
+#endif
 
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/logger.h>
 
+#ifdef HAVE_SOLARIS_ACLS
+
 /* Get ACL. Allocates storage as needed. Caller must free.
  * Returns no of ACEs or -1 on error.  */
 int get_nfsv4_acl(const char *name, ace_t **retAces)
@@ -53,14 +59,14 @@ int get_nfsv4_acl(const char *name, ace_t **retAces)
 
     aces = malloc(ace_count * sizeof(ace_t));
     if (aces == NULL) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
-       return -1;
+        LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
+        return -1;
     }
 
     if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
-       free(aces);
-       return -1;
+        LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
+        free(aces);
+        return -1;
     }
 
     LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
@@ -70,7 +76,7 @@ int get_nfsv4_acl(const char *name, ace_t **retAces)
 }
 
 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-int remove_acl(const char *name)
+int remove_acl_vfs(const char *name)
 {
     int ret,i, ace_count, trivial_aces, new_aces_count;
     ace_t *old_aces = NULL;
@@ -122,3 +128,12 @@ exit:
     LOG(log_debug9, logtype_afpd, "remove_acl: END");
     return ret;
 }
+
+#endif  /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+int remove_acl_vfs(const char *name)
+{
+    return AFP_OK;
+}
+#endif /* HAVE_POSIX_ACLS */
index dadedc5bbee20c21ac384b0ee7d19f435656c069..6b2d5ab27f0f1f2d0f5823e98765fe3bf332fc42 100644 (file)
@@ -320,7 +320,7 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE)
        return 0;
 }
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
 static int RF_solaris_acl(VFS_FUNC_ARGS_ACL)
 {
     static char buf[ MAXPATHLEN + 1];
@@ -358,14 +358,14 @@ static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
        if (len < 0 || len >=  MAXPATHLEN)
            return AFPERR_MISC;
        /* remove ACL from .AppleDouble/.Parent first */
-       if ((ret = remove_acl(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
+       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
            return ret;
        /* now remove from .AppleDouble dir */
-       if ((ret = remove_acl(buf)) != AFP_OK)
+       if ((ret = remove_acl_vfs(buf)) != AFP_OK)
            return ret;
     } else
        /* remove ACL from ressource fork */
-       if ((ret = remove_acl(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
+       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
            return ret;
 
     return AFP_OK;
@@ -977,7 +977,7 @@ static struct vfs_ops netatalk_ea_sys = {
  * Tertiary VFS modules for ACLs
  */
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
 static struct vfs_ops netatalk_solaris_acl_adouble = {
     /* validupath:        */ NULL,
     /* rf_chown:          */ NULL,
@@ -1027,7 +1027,7 @@ void initvol_vfs(struct vol *vol)
     }
 
     /* ACLs */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
     vol->vfs_modules[2] = &netatalk_solaris_acl_adouble;
 #endif
 }
index 78ffb3eae7f3a7b12f052550c89e5065795a9a98..7673d4822589523e6eafb14e3d2050e02bf15b0c 100644 (file)
@@ -16,7 +16,6 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
                AC_MSG_RESULT([         Large file support (>2GB) for AFP3: $wx_largefile])
        fi
        AC_MSG_RESULT([         Extended Attributes: $neta_cv_eas])
-       AC_MSG_RESULT([         DDP enabled: $netatalk_cv_ddp_enabled])
        AC_MSG_RESULT([    CNID:])
        AC_MSG_RESULT([         backends: $compiled_backends])
        AC_MSG_RESULT([    UAMS:])
@@ -48,20 +47,22 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
        AC_MSG_RESULT([         passwd  ($uams_using_options)])
        AC_MSG_RESULT([         guest])
        AC_MSG_RESULT([    Options:])
-       AC_MSG_RESULT([         CUPS support:           $netatalk_cv_use_cups])
-       AC_MSG_RESULT([         SLP support:            $netatalk_cv_srvloc])
-       AC_MSG_RESULT([         tcp wrapper support:    $netatalk_cv_tcpwrap])
+       AC_MSG_RESULT([         DDP (AppleTalk) support: $netatalk_cv_ddp_enabled])
+       AC_MSG_RESULT([         CUPS support:            $netatalk_cv_use_cups])
+       AC_MSG_RESULT([         SLP support:             $netatalk_cv_srvloc])
+       AC_MSG_RESULT([         Zeroconf support:        $netatalk_cv_zeroconf])
+       AC_MSG_RESULT([         tcp wrapper support:     $netatalk_cv_tcpwrap])
 dnl    if test x"$netatalk_cv_linux_sendfile" != x; then
-dnl            AC_MSG_RESULT([         Linux sendfile support: $netatalk_cv_linux_sendfile])
+dnl            AC_MSG_RESULT([         Linux sendfile support:  $netatalk_cv_linux_sendfile])
 dnl    fi
-       AC_MSG_RESULT([         quota support:          $netatalk_cv_quotasupport])
-       AC_MSG_RESULT([         admin group support:    $netatalk_cv_admin_group])
-       AC_MSG_RESULT([         valid shell check:      $netatalk_cv_use_shellcheck])
-       AC_MSG_RESULT([         cracklib support:       $netatalk_cv_with_cracklib])
-       AC_MSG_RESULT([         dropbox kludge:         $netatalk_cv_dropkludge])
-       AC_MSG_RESULT([         force volume uid/gid:   $netatalk_cv_force_uidgid])
-       AC_MSG_RESULT([         Apple 2 boot support:   $compile_a2boot])
-       AC_MSG_RESULT([         ACL support:            $neta_cv_nfsv4acl])
+       AC_MSG_RESULT([         quota support:           $netatalk_cv_quotasupport])
+       AC_MSG_RESULT([         admin group support:     $netatalk_cv_admin_group])
+       AC_MSG_RESULT([         valid shell check:       $netatalk_cv_use_shellcheck])
+       AC_MSG_RESULT([         cracklib support:        $netatalk_cv_with_cracklib])
+       AC_MSG_RESULT([         dropbox kludge:          $netatalk_cv_dropkludge])
+       AC_MSG_RESULT([         force volume uid/gid:    $netatalk_cv_force_uidgid])
+       AC_MSG_RESULT([         Apple 2 boot support:    $compile_a2boot])
+       AC_MSG_RESULT([         ACL support:             $with_acl_support])
        if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
                AC_MSG_RESULT([])
                AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])
diff --git a/macros/zeroconf.m4 b/macros/zeroconf.m4
new file mode 100644 (file)
index 0000000..788a193
--- /dev/null
@@ -0,0 +1,71 @@
+dnl Check for optional Zeroconf support
+
+AC_DEFUN([NETATALK_ZEROCONF], [
+       ZEROCONF_LIBS=""
+       ZEROCONF_CFLAGS=""
+       found_zeroconf=no
+       zeroconf_dir=""
+
+       AC_ARG_ENABLE(zeroconf,
+               [  --enable-zeroconf[[=DIR]]   enable Zeroconf support [[auto]]],
+               [zeroconf=$enableval],
+               [zeroconf=try]
+       )
+
+    dnl make sure atalk_libname is defined beforehand
+    [[ -n "$atalk_libname" ]] || AC_MSG_ERROR([internal error, atalk_libname undefined])
+
+       if test "x$zeroconf" != "xno"; then
+               savedcppflags="$CPPFLAGS"
+               savedldflags="$LDFLAGS"
+
+               if test "x$zeroconf" = "xyes" -o "x$zeroconf" = "xtry"; then
+                       zeroconf_dir="/usr"
+               else
+                       zeroconf_dir="$zeroconf"
+               fi
+
+    # mDNS support using Avahi
+    AC_CHECK_HEADER(
+        avahi-client/client.h,
+        AC_CHECK_LIB(
+           avahi-client,
+           avahi_client_new,
+           AC_DEFINE(USE_ZEROCONF, 1, [Use DNS-SD registration]))
+    )
+
+    case "$ac_cv_lib_avahi_client_avahi_client_new" in
+      yes)
+      PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6 ])
+      PKG_CHECK_MODULES(AVAHI_TPOLL, [ avahi-client >= 0.6.4 ],
+        [AC_DEFINE(HAVE_AVAHI_THREADED_POLL, 1, [Uses Avahis threaded poll implementation])],
+        [AC_MSG_WARN(This Avahi implementation is not supporting threaded poll objects. Maybe this is not what you want.)])
+      ZEROCONF_LIBS="$AVAHI_LIBS"
+      ZEROCONF_CFLAGS="$AVAHI_CFLAGS"
+      AC_DEFINE(HAVE_AVAHI, 1, [Use Avahi/DNS-SD registration])
+      found_zeroconf=yes
+      ;;
+    esac
+
+               CPPFLAGS="$savedcppflags"
+               LDFLAGS="$savedldflags"
+       fi
+       
+       netatalk_cv_zeroconf=no
+       AC_MSG_CHECKING([whether to enable Zerconf support])
+       if test "x$found_zeroconf" = "xyes"; then
+               AC_MSG_RESULT([yes])
+               AC_DEFINE(USE_ZEROCONF, 1, [Define to enable Zeroconf support])
+               netatalk_cv_zeroconf=yes
+       else
+               AC_MSG_RESULT([no])
+               if test "x$zeroconf" != "xno" -a "x$zeroconf" != "xtry"; then
+                       AC_MSG_ERROR([Zeroconf installation not found])
+               fi
+       fi
+
+       LIB_REMOVE_USR_LIB(ZEROCONF_LIBS)
+       CFLAGS_REMOVE_USR_INCLUDE(ZEROCONF_CFLAGS)
+       AC_SUBST(ZEROCONF_LIBS)
+       AC_SUBST(ZEROCONF_CFLAGS)
+])
index a9e39c1529d58bf570aeaad927bf3709c5631fe5..9ccb204f0f4ff4392860edee546e5249f0893649 100644 (file)
@@ -16,27 +16,30 @@ GENERATED_MANS      = apple_cp.1 apple_mv.1 apple_rm.1 uniconv.1 asip-status.pl.1
 TEMPLATE_FILES = apple_cp.1.tmpl apple_mv.1.tmpl apple_rm.1.tmpl uniconv.1.tmpl asip-status.pl.1.tmpl
 NONGENERATED_MANS      =       achfile.1  \
                                ad.1 \
-                               aecho.1 \
                                afile.1 \
                                afppasswd.1 \
                                apple_dump.1 \
                                dbd.1 \
-                               getzones.1 \
                                hqx2bin.1 \
                                macbinary.1 \
                                megatron.1 \
+                               netatalk-config.1 \
+                               single2bin.1 \
+                               unbin.1 \
+                               unhex.1 \
+                               unsingle.1
+
+if USE_APPLETALK
+NONGENERATED_MANS += aecho.1 \
+                               getzones.1 \
                                nbp.1 \
                                nbplkup.1 \
                                nbprgstr.1 \
                                nbpunrgstr.1 \
-                               netatalk-config.1 \
                                pap.1 \
                                papstatus.1 \
-                               psorder.1 \
-                               single2bin.1 \
-                               unbin.1 \
-                               unhex.1 \
-                               unsingle.1
+                               psorder.1
+endif
 
 man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
 
index 1569cfd7296d6eb254f24544583996bdda7d86d4..4212f267b4d0f98c15108cd0ca0b9574602b4b4e 100644 (file)
@@ -1,5 +1,7 @@
 # Makefile.am for man/man3
 
+if USE_APPLETALK
 man_MANS = atalk_aton.3 nbp_name.3
+endif
 
 EXTRA_DIST = $(man_MANS)
index 944c3602ab2cfc1dad0e2b09bb738e9117da0e09..73e1610e39f1da61c58c32c5b6cf459ad59aa7e9 100644 (file)
@@ -1,5 +1,7 @@
 # Makefile.am for man/man4/
 
+if USE_APPLETALK
 man_MANS = atalk.4
+endif
 
 EXTRA_DIST = $(man_MANS)
index a1e28886df45aa60cca4ce65529e79ca207da133..3265d4be56f2f0a438decb50407fcbc6d64f6610 100644 (file)
@@ -13,13 +13,22 @@ SUFFIXES = .tmpl .
            -e "s@:COMPILED_BACKENDS:@${compiled_backends}@g" \
            <$< >$@
 
-GENERATED_MANS = AppleVolumes.default.5 afpd.conf.5 \
-       atalkd.conf.5 netatalk.conf.5 papd.conf.5 \
-       afp_ldap.conf.5 afp_signature.conf.5
-
-TEMPLATE_FILES = AppleVolumes.default.5.tmpl afpd.conf.5.tmpl \
-       atalkd.conf.5.tmpl netatalk.conf.5.tmpl papd.conf.5.tmpl \
-       afp_ldap.conf.5.tmpl afp_signature.conf.5.tmpl
+GENERATED_MANS = AppleVolumes.default.5 \
+       afpd.conf.5 \
+       netatalk.conf.5 \
+       afp_ldap.conf.5 \
+       afp_signature.conf.5
+
+TEMPLATE_FILES = AppleVolumes.default.5.tmpl \
+       afpd.conf.5.tmpl \
+       netatalk.conf.5.tmpl \
+       afp_ldap.conf.5.tmpl \
+       afp_signature.conf.5.tmpl
+
+if USE_APPLETALK
+GENERATED_MANS += atalkd.conf.5 papd.conf.5
+TEMPLATE_FILES += atalkd.conf.5.tmpl papd.conf.5.tmpl
+endif
 
 NONGENERATED_MANS = AppleVolumes.5 AppleVolumes.system.5
 
index 99d41e391b953aa99736dd2a5640640c4da69031..241be9c68b2ff9518a39fb0bdb5b52616ea3cdd2 100644 (file)
@@ -14,9 +14,13 @@ SUFFIXES = .tmpl .
            <$< >$@
 
 NONGENERATED_MANS = timelord.8
-GENERATED_MANS    = afp_acls.8 afpd.8 atalkd.8 cnid_dbd.8 cnid_metad.8 papd.8 papstatus.8 psf.8
-TEMPLATE_FILES    = afp_acls.8.tmpl afpd.8.tmpl atalkd.8.tmpl cnid_dbd.8.tmpl \
-       cnid_metad.8.tmpl papd.8.tmpl papstatus.8.tmpl psf.8.tmpl
+GENERATED_MANS    = afpd.8 cnid_dbd.8 cnid_metad.8
+TEMPLATE_FILES    = afpd.8.tmpl cnid_dbd.8.tmpl cnid_metad.8.tmpl
+
+if USE_APPLETALK
+GENERATED_MANS += atalkd.8 papd.8 papstatus.8 psf.8
+TEMPLATE_FILES += atalkd.8.tmpl papd.8.tmpl papstatus.8.tmpl psf.8.tmpl
+endif
 
 man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
 
diff --git a/man/man8/afp_acls.8.tmpl b/man/man8/afp_acls.8.tmpl
deleted file mode 100644 (file)
index c09f03b..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-'\" t
-.\"     Title: afp_acls
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 02 Feb 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
-.\"  Language: English
-.\"
-.TH "AFP_ACLS" "8" "02 Feb 2009" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp_acls \- Setup and Usage Howto for ACLs with Netatalk
-.SH "DESCRIPTION"
-.PP
-ACL support for AFP is implemented with NFSv4 ACLs\&. Few filesystems and fewer OSes support these\&. At the time of implementation its only provided with ZFS on Solaris, Opensolaris and derived distributions\&.
-.SH "CONFIGURATION"
-.PP
-In order to be able to support ACLs, the following things have to be configured:
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 1.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  1." 4.2
-.\}
-ZFS Volumes
-.sp
-You MUST configure two ACL parameters for any volume you want to use with Netatalk:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-aclinherit = passthrough
-aclmode = passthrough
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-For an explanation of what these parameters mean and how to apply them see, your hosts ZFS documentation (e\&.g\&. man zfs)\&.
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 2.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  2." 4.2
-.\}
-Authentication Domain
-.sp
-Your server and the clients must be part of a security association where identity data is coming from a common source\&. ACLs in Darwin are based on UUIDs and so is the ACL specification in AFP 3\&.2\&. Therefor your source of identity data has to provide an attribute for every user and group where a UUID is stored as a ASCII string\&.
-.sp
-In other words:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-you need an Open Directory Server or an LDAP server where you store UUIDs in some attribute
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-your clients must be configured to use this server
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-your server should be configured to use this server via nsswitch and PAM\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBTip\fR
-.ps -1
-.br
-This however is not a strict requirement: if you create duplicates of every LDAP/OD user and group with identic attributes (name, uid, gid) in your local data store (/etc/[passwd|group]) ACLs will work
-\fIas long as user/group names/ids in the filesystem are equal to their counterparts in the LDAP/OD datastore\fR\&.
-.sp .5v
-.RE
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-configure Netatalk via afp_ldap\&.conf so that Netatalk is able to retrieve the UUID for users and groups via LDAP search queries
-.RE
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 3.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  3." 4.2
-.\}
-Netatalk Volumes
-.sp
-Finally you can add
-\fBoptions:acls\fR
-to your volume defintion to add ACL support\&. In case your volume basedir doesn\'t grant read permissions via mode (like:
-\fB0700 root:adm\fR) but only via ACLs, you MUST add the
-\fBnostat\fR
-option to the volume defintion\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafp_ldap.conf\fR(5),
-\fBAppleVolumes.default\fR(5)
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644 (file)
index 0000000..282522d
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644 (file)
index 0000000..bfd9288
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = afpd
diff --git a/test/afpd/.gitignore b/test/afpd/.gitignore
new file mode 100644 (file)
index 0000000..4d6beea
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+.deps
+.libs
+test
+test.conf
+test.default
\ No newline at end of file
diff --git a/test/afpd/Makefile.am b/test/afpd/Makefile.am
new file mode 100644 (file)
index 0000000..2bb6b3e
--- /dev/null
@@ -0,0 +1,65 @@
+# Makefile.am for test/afpd/
+
+pkgconfdir = @PKGCONFDIR@
+
+TESTS = test.sh test
+
+check_PROGRAMS = test
+noinst_HEADERS = test.h subtests.h afpfunc_helpers.h
+EXTRA_DIST = test.sh
+CLEANFILES = test.default test.conf
+
+test_SOURCES =  test.c subtests.c afpfunc_helpers.c \
+                               $(top_builddir)/etc/afpd/afp_avahi.c \
+                               $(top_builddir)/etc/afpd/afp_config.c \
+                               $(top_builddir)/etc/afpd/afp_dsi.c \
+                               $(top_builddir)/etc/afpd/afp_options.c \
+                               $(top_builddir)/etc/afpd/afp_util.c \
+                               $(top_builddir)/etc/afpd/afprun.c \
+                               $(top_builddir)/etc/afpd/appl.c \
+                               $(top_builddir)/etc/afpd/auth.c \
+                               $(top_builddir)/etc/afpd/afp_zeroconf.c \
+                               $(top_builddir)/etc/afpd/catsearch.c \
+                               $(top_builddir)/etc/afpd/desktop.c \
+                               $(top_builddir)/etc/afpd/dircache.c \
+                               $(top_builddir)/etc/afpd/directory.c \
+                               $(top_builddir)/etc/afpd/enumerate.c \
+                               $(top_builddir)/etc/afpd/extattrs.c \
+                               $(top_builddir)/etc/afpd/file.c \
+                               $(top_builddir)/etc/afpd/filedir.c \
+                               $(top_builddir)/etc/afpd/fork.c \
+                               $(top_builddir)/etc/afpd/gettok.c \
+                               $(top_builddir)/etc/afpd/hash.c \
+                               $(top_builddir)/etc/afpd/mangle.c \
+                               $(top_builddir)/etc/afpd/messages.c \
+                               $(top_builddir)/etc/afpd/nfsquota.c \
+                               $(top_builddir)/etc/afpd/ofork.c \
+                               $(top_builddir)/etc/afpd/quota.c \
+                               $(top_builddir)/etc/afpd/status.c \
+                               $(top_builddir)/etc/afpd/switch.c \
+                               $(top_builddir)/etc/afpd/uam.c \
+                               $(top_builddir)/etc/afpd/unix.c \
+                               $(top_builddir)/etc/afpd/volume.c
+
+if HAVE_ACLS
+test_SOURCES += $(top_builddir)/etc/afpd/acls.c
+endif
+
+test_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/etc/afpd \
+        @SLP_CFLAGS@ @ZEROCONF_CFLAGS@ \
+        -DAPPLCNAME \
+        -DSERVERTEXT=\"$(SERVERTEXT)/\" \
+        -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
+        -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
+        -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
+        -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
+        -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
+        -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
+        -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
+        -D_PATH_AFPDUUIDCONF=\"$(pkgconfdir)/afp_voluuid.conf\"
+
+test_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la \
+       $(top_builddir)/libatalk/libatalk.la \
+       @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@
+
+test_LDFLAGS = -export-dynamic
diff --git a/test/afpd/afpfunc_helpers.c b/test/afpd/afpfunc_helpers.c
new file mode 100644 (file)
index 0000000..b5b8165
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+  $Id: afpfunc_helpers.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+
+#define rbufsize 128000
+static char rbuf[rbufsize];
+static size_t rbuflen;
+
+#define ADD(a, b, c) (a) += (c); \
+                         (b) += (c)
+
+#define PUSHBUF(p, val, size, len) \
+    memcpy((p), (val), (size));    \
+    (p) += (size);                 \
+    (len) += (size)
+
+#define PUSHVAL(p, type, val, len)         \
+    { \
+        type type = val;                          \
+        memcpy(p, &type, sizeof(type));           \
+        (p) += sizeof(type);                      \
+        (len) += sizeof(type);                    \
+    }
+
+static int push_path(char **bufp, const char *name)
+{
+    int len = 0;
+    int slen = strlen(name);
+    char *p = *bufp;
+
+    PUSHVAL(p, uint8_t, 3, len); /* path type */
+    PUSHVAL(p, uint32_t, kTextEncodingUTF8, len); /* text encoding hint */
+    PUSHVAL(p, uint16_t, htons(slen), len);
+    if (slen) {
+        for (int i = 0; i < slen; i++) {
+            if (name[i] == '/')
+                p[i] = 0;
+            else
+                p[i] = name[i];
+        }
+        len += slen;
+    }
+
+    *bufp += len;
+    return len;
+}
+
+/***********************************************************************************
+ * Interface
+ ***********************************************************************************/
+
+char **cnamewrap(const char *name)
+{
+    static char buf[256];
+    static char *p = buf;
+    int len = 0;
+
+    PUSHVAL(p, uint8_t, 3, len); /* path type */
+    PUSHVAL(p, uint32_t, kTextEncodingUTF8, len); /* text encoding hint */
+    PUSHVAL(p, uint16_t, ntohs(strlen(name)), len);
+    strcpy(p, name);
+
+    p = buf;
+    return &p;
+}
+
+int getfiledirparms(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    ADD(p, len , 2);
+
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    PUSHVAL(p, uint16_t, htons(FILPBIT_FNUM | FILPBIT_PDINFO), len);
+    PUSHVAL(p, uint16_t, htons(DIRPBIT_DID | DIRPBIT_PDINFO), len);
+
+    len += push_path(&p, name);
+
+    return afp_getfildirparams(obj, buf, len, rbuf, &rbuflen);
+}
+
+int createdir(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    ADD(p, len , 2);
+
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    len += push_path(&p, name);
+
+    return afp_createdir(obj, buf, len, rbuf, &rbuflen);
+}
+
+int createfile(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    PUSHVAL(p, uint16_t, htons(128), len); /* hard create */
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    len += push_path(&p, name);
+
+    return afp_createfile(obj, buf, len, rbuf, &rbuflen);
+}
+
+int delete(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    PUSHVAL(p, uint16_t, htons(128), len); /* hard create */
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    len += push_path(&p, name);
+
+    return afp_delete(obj, buf, len, rbuf, &rbuflen);
+}
+
+int enumerate(AFPObj *obj, uint16_t vid, cnid_t did)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    ADD(p, len , 2);
+
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    PUSHVAL(p, uint16_t, htons(FILPBIT_PDID | FILPBIT_FNUM | FILPBIT_PDINFO), len);
+    PUSHVAL(p, uint16_t, htons(DIRPBIT_PDID | DIRPBIT_DID | DIRPBIT_PDINFO), len);
+    PUSHVAL(p, uint16_t, htons(20), len);       /* reqcount */
+    PUSHVAL(p, uint32_t, htonl(1), len);        /* startindex */
+    PUSHVAL(p, uint32_t, htonl(rbufsize), len); /* max replysize */
+
+    len += push_path(&p, "");
+
+    return afp_enumerate_ext2(obj, buf, len, rbuf, &rbuflen);
+}
+
+uint16_t openvol(AFPObj *obj, const char *name)
+{
+    int ret;
+    uint16_t bitmap;
+    uint16_t vid;
+    const int bufsize = 32;
+    char buf[bufsize];
+    char *p = buf;
+    char len = strlen(name);
+
+    memset(p, 0, bufsize);
+    p += 2;
+
+    /* bitmap */
+    bitmap = htons(1<<VOLPBIT_VID);
+    memcpy(p, &bitmap, 2);
+    p += 2;
+
+    /* name */
+    *p = len;
+    p++;
+    memcpy(p, name, len);
+    p += len;
+
+    len += 2 + 2 + 1; /* (command+pad) + bitmap + len */
+    if (len & 1)
+        len++;
+
+    rbuflen = 0;
+    if ((ret = afp_openvol(obj, buf, len, rbuf, &rbuflen)) != AFP_OK)
+        return 0;
+
+    p = rbuf;
+    memcpy(&bitmap, p, 2);
+    p += 2;
+    bitmap = ntohs(bitmap);
+    if ( ! (bitmap & 1<<VOLPBIT_VID))
+        return 0;
+
+    memcpy(&vid, p, 2);
+    return vid;
+}
+
diff --git a/test/afpd/afpfunc_helpers.h b/test/afpd/afpfunc_helpers.h
new file mode 100644 (file)
index 0000000..3fda7df
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  $Id: afpfunc_helpers.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifndef AFPFUNC_HELPERS
+#define AFPFUNC_HELPERS
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+extern char **cnamewrap(const char *name);
+
+extern int getfiledirparms(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int createdir(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int createfile(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int delete(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int enumerate(AFPObj *obj, uint16_t vid, cnid_t did);
+extern uint16_t openvol(AFPObj *obj, const char *name);
+
+#endif  /* AFPFUNC_HELPERS */
diff --git a/test/afpd/subtests.c b/test/afpd/subtests.c
new file mode 100644 (file)
index 0000000..d606be8
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+  $Id: subtests.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+static int reti;                /* for the TEST_int macro */
+
+int test001_add_x_dirs(const struct vol *vol, cnid_t start, cnid_t end)
+{
+    struct dir *dir;
+    char dirname[20];
+    while (start++ < end) {
+        sprintf(dirname, "dir%04u", start);
+        dir = dir_new(dirname, dirname, vol, DIRDID_ROOT, htonl(start), bfromcstr(vol->v_path));
+        if (dir == NULL)
+            return -1;
+        if (dircache_add(dir) != 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+int test002_rem_x_dirs(const struct vol *vol, cnid_t start, cnid_t end)
+{
+    struct dir *dir;
+    while (start++ < end) {
+        if ((dir = dircache_search_by_did(vol, htonl(start))))
+            if (dir_remove(vol, dir) != 0)
+                return -1;
+    }
+
+    return 0;
+}
diff --git a/test/afpd/subtests.h b/test/afpd/subtests.h
new file mode 100644 (file)
index 0000000..1ce10a0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  $Id: subtests.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifndef SUBTESTS_H
+#define SUBTESTS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+extern int test001_add_x_dirs(const struct vol *vol, cnid_t start, cnid_t end);
+extern int test002_rem_x_dirs(const struct vol *vol, cnid_t start, cnid_t end);
+#endif  /* SUBTESTS_H */
diff --git a/test/afpd/test.c b/test/afpd/test.c
new file mode 100644 (file)
index 0000000..4ebc2df
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+  $Id: test.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+#include "afpfunc_helpers.h"
+
+/* Stuff from main.c which of cource can't be added as source to testbin */
+unsigned char nologin = 0;
+struct afp_options default_options;
+static AFPConfig *configs;
+
+/* Static variables */
+
+int main(int argc, char **argv)
+{
+    #define ARGNUM 7
+    char *args[ARGNUM] = {"test", "-F", "test.conf", "-f", "test.default", "-s" ,"test.system"};
+    int reti;
+    uint16_t vid;
+    struct vol *vol;
+    struct dir *dir;
+    struct dir *retdir;
+    struct path *path;
+
+    /* initialize */
+    afp_version = 32;
+    printf("Initializing\n============\n");
+    TEST(setuplog("default log_note /dev/tty"));
+    TEST(afp_options_init(&default_options));
+    TEST_int(afp_options_parse( ARGNUM, args, &default_options), 1);
+    TEST_expr(configs = configinit(&default_options), configs != NULL);
+    TEST(cnid_init());
+    TEST(load_volumes(&configs->obj));
+    TEST_int(dircache_init(8192), 0);
+    printf("\n");
+
+    /* now run tests */
+    printf("Running tests\n=============\n");
+
+    TEST_expr(vid = openvol(&configs->obj, "test"), vid != 0);
+    TEST_expr(vol = getvolbyvid(vid), vol != NULL);
+
+    /* test dircache.c stuff*/
+    TEST_expr(dir = dir_new("dir", "dir", vol, DIRDID_ROOT, htonl(20), bfromcstr(vol->v_path)),
+              dir != NULL);
+    TEST_int(dircache_add(dir), 0);
+    TEST_expr(retdir = dircache_search_by_did(vol, dir->d_did ),
+              retdir != NULL && retdir == dir && bstrcmp(retdir->d_u_name, dir->d_u_name) == 0);
+    TEST_expr(retdir = dircache_search_by_name(vol, vol->v_root, "dir", strlen("dir")),
+              retdir != NULL && retdir == dir && bstrcmp(retdir->d_u_name, dir->d_u_name) == 0);
+    TEST_int(dir_remove(vol, dir), 0);
+    TEST_int(test001_add_x_dirs(vol, 100, 100000), 0);
+    TEST_int(test002_rem_x_dirs(vol, 100, 100000), 0);
+
+    /* test directory.c stuff */
+    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT_PARENT), retdir != NULL);
+    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
+    TEST_expr(path = cname(vol, retdir, cnamewrap("Network Trash Folder")), path != NULL);
+
+    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT_PARENT, "test"), 0);
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, ""), 0);
+
+    TEST_expr(reti = createdir(&configs->obj, vid, DIRDID_ROOT, "dir1"),
+              reti == 0 || reti == AFPERR_EXIST);
+
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "dir1"), 0);
+/*
+  FIXME: this doesn't work although it should. "//" get translated to \000 \000 at means ".."
+  ie this should getfiledirparms for DIRDID_ROOT_PARENT -- at least afair!
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "//"), 0);
+*/
+    TEST_int(createfile(&configs->obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
+    TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
+    TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "dir1"), 0);
+
+    TEST_int(createfile(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+    TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+
+
+    /* test enumerate.c stuff */
+    TEST_int(enumerate(&configs->obj, vid, DIRDID_ROOT), 0);
+}
diff --git a/test/afpd/test.h b/test/afpd/test.h
new file mode 100644 (file)
index 0000000..802120a
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  $Id: test.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifndef TEST_H
+#define TEST_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+#include "subtests.h"
+
+static inline void alignok(int len)
+{
+    int i = 1;
+    if (len < 80)
+        i = 80 - len;
+    while (i--)
+        printf(" ");
+}
+
+#define TEST(a) \
+    printf("Testing: %s ... ", (#a) ); \
+    alignok(strlen(#a));               \
+    a;                                 \
+    printf("[ok]\n");
+
+#define TEST_int(a, b) \
+    printf("Testing: %s ... ", (#a) );            \
+    alignok(strlen(#a));                          \
+    if ((reti = (a)) != b) {                      \
+        printf("[error]\n");                      \
+        exit(1);                                  \
+    } else { printf("[ok]\n"); }
+
+#define TEST_expr(a, b)                              \
+    printf("Testing: %s ... ", (#a) );               \
+    alignok(strlen(#a));                             \
+    a;                                               \
+    if (b) {                                         \
+        printf("[ok]\n");                            \
+    } else {                                         \
+        printf("[error]\n");                         \
+        exit(1);                                     \
+    }
+#endif  /* TEST_H */
diff --git a/test/afpd/test.sh b/test/afpd/test.sh
new file mode 100755 (executable)
index 0000000..80066b6
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+if [ ! -d /tmp/AFPtestvolume ] ; then
+    mkdir -p /tmp/AFPtestvolume
+    if [ $? -ne 0 ] ; then
+        echo Error creating AFP test volume /tmp/AFPtestvolume
+        exit 1
+    fi
+fi
+
+if [ ! -f test.conf ] ; then
+    echo -n "Creating configuration template ... "
+    cat > test.conf <<EOF
+test -noddp -port 10548
+EOF
+    echo [ok]
+fi
+
+if [ ! -f test.default ] ; then
+    echo -n "Creating volume config template ... "
+    cat > test.default <<EOF
+/tmp/AFPtestvolume "test" ea:none
+EOF
+    echo [ok]
+fi