]> arthur.barton.de Git - netatalk.git/commitdiff
Merge dircache-rewrite
authorFrank Lahm <franklahm@googlemail.com>
Thu, 29 Apr 2010 09:56:36 +0000 (11:56 +0200)
committerFrank Lahm <franklahm@googlemail.com>
Thu, 29 Apr 2010 09:56:36 +0000 (11:56 +0200)
17 files changed:
1  2 
configure.in
etc/afpd/Makefile.am
etc/afpd/afp_dsi.c
etc/afpd/afp_options.c
etc/afpd/catsearch.c
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/ofork.c
etc/afpd/volume.c
include/atalk/util.h
include/atalk/volume.h
libatalk/cnid/dbd/cnid_dbd.c

diff --combined configure.in
index b90d14cea1284bb08224360b5937d4224c4417e4,a68111bfe3f336d45caeabf164a2573e238d3dc3..016ba88a9de4b1b75bee67a6f59869298dd8ac9c
@@@ -1,4 -1,4 +1,4 @@@
- dnl $Id: configure.in,v 1.244 2010-04-13 08:05:06 franklahm Exp $
 -dnl $Id: configure.in,v 1.237.2.1 2010-02-01 10:56:08 franklahm Exp $
++dnl $Id: configure.in,v 1.241 2010/04/03 07:11:33 franklahm Exp $
  dnl configure.in for netatalk
  
  AC_INIT(etc/afpd/main.c)
@@@ -135,9 -135,9 +135,9 @@@ AC_TYPE_SIGNA
  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)
 +AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo strlcpy strlcat setlinebuf dirfd pselect)
  AC_CHECK_FUNCS(waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64)
 -AC_CHECK_FUNCS(strlcpy strlcat setlinebuf gethostid dirfd)
 +AC_CHECK_FUNC(renameat, AC_DEFINE([_ATFILE_SOURCE], 1, AT file source)) 
  AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include <time.h>])
  AC_CACHE_SAVE
  
@@@ -278,9 -278,11 +278,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])
        ]
  )
  
@@@ -597,8 -599,6 +599,8 @@@ dnl       /usr/include or similar place
  LIBS="$LIBS -L\$(top_srcdir)/libatalk"
  CFLAGS="-I\$(top_srcdir)/include $CFLAGS -I\$(top_srcdir)/sys"
  
 +AC_DEFINE(OPEN_NOFOLLOW_ERRNO, ELOOP, errno returned by open with O_NOFOLLOW)
 +
  dnl --------------------------------------------------------------------------
  dnl specific configuration comes in here:
  dnl --------------------------------------------------------------------------
@@@ -652,7 -652,6 +654,7 @@@ if test x"$this_os" = "xfreebsd"; the
        AC_DEFINE(BSD4_4, 1, [BSD compatiblity macro])
        AC_DEFINE(FREEBSD, 1, [Define if OS is FreeBSD])
        AC_DEFINE(SENDFILE_FLAVOR_BSD, 1, [Define if the sendfile() function uses BSD semantics])
 +    AC_DEFINE(OPEN_NOFOLLOW_ERRNO, EMLINK, errno returned by open with O_NOFOLLOW)
  fi
  
  dnl ----- HP-UX 11 specific -----
@@@ -798,10 -797,8 +800,10 @@@ dnl ----- NetBSD specific ----
  if test x"$this_os" = "xnetbsd"; then 
        AC_MSG_RESULT([ * NetBSD specific configuration])
        AC_DEFINE(BSD4_4, 1, [BSD compatiblity macro])
 -      AC_DEFINE(NETBSD, 1, [Define if OS is FreeBSD])
 -      CFLAGS="-I\$(top_srcdir)/sys/netbsd -I/usr/include/kerberosIV $CFLAGS"
 +      AC_DEFINE(NETBSD, 1, [Define if OS is NetBSD])
 +    AC_DEFINE(OPEN_NOFOLLOW_ERRNO, EFTYPE, errno returned by open with O_NOFOLLOW)
 +
 +      CFLAGS="-I\$(top_srcdir)/sys/netbsd $CFLAGS"
        need_dash_r=yes 
  
        dnl ----- NetBSD does not have crypt.h, uses unistd.h -----
  AC_SUBST(LIBATALK_ACLS)
  
  dnl --------------------- check for Extended Attributes support
 -neta_cv_eas="files"
 +neta_cv_eas="ad"
  neta_cv_eas_sys_found=no
  neta_cv_eas_sys_not_found=no
  
  
  if test "x$neta_cv_eas_sys_found" = "xyes" ; then
     if test "x$neta_cv_eas_sys_not_found" != "xyes" ; then
 -      neta_cv_eas="$neta_cv_eas | native"
 +      neta_cv_eas="$neta_cv_eas | sys"
     fi
  fi
  
 +dnl --------------------- Check if realpath() takes NULL
 +AC_CACHE_CHECK([if the realpath function allows a NULL argument],
 +    neta_cv_REALPATH_TAKES_NULL, [
 +        AC_TRY_RUN([
 +            #include <stdio.h>
 +            #include <limits.h>
 +            #include <signal.h>
 +
 +            void exit_on_core(int ignored) {
 +                 exit(1);
 +            }
 +
 +            main() {
 +                char *newpath;
 +                signal(SIGSEGV, exit_on_core);
 +                newpath = realpath("/tmp", NULL);
 +                exit((newpath != NULL) ? 0 : 1);
 +            }],
 +            neta_cv_REALPATH_TAKES_NULL=yes,
 +            neta_cv_REALPATH_TAKES_NULL=no,
 +            neta_cv_REALPATH_TAKES_NULL=cross
 +        )
 +    ]
 +)
 +
 +if test x"$neta_cv_REALPATH_TAKES_NULL" = x"yes"; then
 +    AC_DEFINE(REALPATH_TAKES_NULL,1,[Whether the realpath function allows NULL])
 +fi
 +
  dnl --------------------- Netatalk Webmin
  NETATALK_WEBMIN
  
@@@ -1275,6 -1243,7 +1277,7 @@@ AC_OUTPUT([Makefil
        libatalk/adouble/Makefile
        libatalk/asp/Makefile
        libatalk/atp/Makefile
+       libatalk/bstring/Makefile
        libatalk/cnid/Makefile
        libatalk/cnid/cdb/Makefile
        libatalk/cnid/last/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_*]
  )
diff --combined etc/afpd/Makefile.am
index 54ae84fee31e9661e5a88c92f5ec8f8e5d151488,ae937ecc3976b78b5456c515a63ad876c38c8866..f4d04f1a907d6dc2506c5bc40c5af1a77ae41d88
@@@ -9,7 -9,7 +9,7 @@@ afpd_SOURCES = unix.c ofork.c main.c sw
         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
+       catsearch.c afprun.c hash.c extattrs.c dircache.c
  
  if USE_NFSv4_ACLS
  afpd_SOURCES += acls.c
@@@ -23,7 -23,6 +23,7 @@@ afpd_CFLAGS = -I$(top_srcdir)/include -
         -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 \
@@@ -31,7 -30,8 +31,8 @@@
  
  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
  
  hash_SOURCES = hash.c
  hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
diff --combined etc/afpd/afp_dsi.c
index a3d40ded6ec3d8969d21d9706df1b9b29d0a6c09,efe1d241825a4123aeb3dd3886f40f2b93a07b65..1b10187a42c0e3e9d63449911491f078733f1550
@@@ -1,5 -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.49.2.2 2010-02-04 14:34:31 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 +37,7 @@@
  #include "switch.h"
  #include "auth.h"
  #include "fork.h"
+ #include "dircache.h"
  
  #ifdef FORCE_UIDGID
  #warning UIDGID
  #define CHILD_SLEEPING    (1 << 2)
  #define CHILD_DATA        (1 << 3)
  
 +/* 
 + * We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
 + * available everywhere. Unfortunately some funcs (eg acltoownermode) need acces to it
 + * but are deeply nested in the function chain with the caller already without acces to it.
 + * Changing this would require adding a reference to the caller which itself might be
 + * called in many places (eg acltoownermode is called from accessmode).
 + * The only sane way out is providing a copy of it here:
 + */
 +AFPObj *AFPobj = NULL;
 +
  static struct {
      AFPObj *obj;
      unsigned char flags;
@@@ -180,16 -171,6 +181,16 @@@ static void afp_dsi_reload(int sig _U_
      reload_request = 1;
  }
  
 +/* ---------------------------------
 + * SIGINT: enable max_debug LOGging
 + */
 +static volatile sig_atomic_t debug_request = 0;
 +
 +static void afp_dsi_debug(int sig _U_)
 +{
 +    debug_request = 1;
 +}
 +
  /* ---------------------- */
  #ifdef SERVERTEXT
  static void afp_dsi_getmesg (int sig _U_)
@@@ -270,7 -251,6 +271,7 @@@ void afp_over_dsi(AFPObj *obj
      u_int8_t function;
      struct sigaction action;
  
 +    AFPobj = obj;
      obj->exit = afp_dsi_die;
      obj->reply = (int (*)()) dsi_cmdreply;
      obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
      sigaddset(&action.sa_mask, SIGALRM);
      sigaddset(&action.sa_mask, SIGTERM);
      sigaddset(&action.sa_mask, SIGUSR1);
 +    sigaddset(&action.sa_mask, SIGINT);
  #ifdef SERVERTEXT
      sigaddset(&action.sa_mask, SIGUSR2);
  #endif    
      sigaddset(&action.sa_mask, SIGALRM);
      sigaddset(&action.sa_mask, SIGHUP);
      sigaddset(&action.sa_mask, SIGUSR1);
 +    sigaddset(&action.sa_mask, SIGINT);
  #ifdef SERVERTEXT
      sigaddset(&action.sa_mask, SIGUSR2);
  #endif    
      sigaddset(&action.sa_mask, SIGTERM);
      sigaddset(&action.sa_mask, SIGUSR1);
      sigaddset(&action.sa_mask, SIGHUP);
 +    sigaddset(&action.sa_mask, SIGINT);
      action.sa_flags = SA_RESTART;
      if ( sigaction( SIGUSR2, &action, NULL) < 0 ) {
          LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
      sigaddset(&action.sa_mask, SIGALRM);
      sigaddset(&action.sa_mask, SIGHUP);
      sigaddset(&action.sa_mask, SIGTERM);
 +    sigaddset(&action.sa_mask, SIGINT);
  #ifdef SERVERTEXT
      sigaddset(&action.sa_mask, SIGUSR2);
  #endif    
          afp_dsi_die(EXITERR_SYS);
      }
  
 +    /*  SIGINT - enable max_debug LOGging to /tmp/afpd.PID.XXXXXX */
 +    action.sa_handler = afp_dsi_debug;
 +    sigfillset( &action.sa_mask );
 +    action.sa_flags = SA_RESTART;
 +    if ( sigaction( SIGINT, &action, NULL) < 0 ) {
 +        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
 +        afp_dsi_die(EXITERR_SYS);
 +    }
 +
  #ifndef DEBUGGING
      /* tickle handler */
      action.sa_handler = alarm_handler;
      sigaddset(&action.sa_mask, SIGHUP);
      sigaddset(&action.sa_mask, SIGTERM);
      sigaddset(&action.sa_mask, SIGUSR1);
 +    sigaddset(&action.sa_mask, SIGINT);
  #ifdef SERVERTEXT
      sigaddset(&action.sa_mask, SIGUSR2);
  #endif    
      }
  #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;
          child.flags &= ~CHILD_SLEEPING;
          dsi_sleep(dsi, 0); /* wake up */
 +
          if (reload_request) {
              reload_request = 0;
              load_volumes(child.obj);
+             dircache_dump();
          }
  
 +        if (debug_request) {
 +            char logstr[50];
 +            debug_request = 0;
 +
 +            /* The first SIGINT enables debugging, the second one kills us */
 +            action.sa_handler = afp_dsi_die;
 +            sigfillset( &action.sa_mask );
 +            action.sa_flags = SA_RESTART;
 +            if ( sigaction( SIGINT, &action, NULL ) < 0 ) {
 +                LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
 +                afp_dsi_die(EXITERR_SYS);
 +            }
 +
 +            sprintf(logstr, "default log_maxdebug /tmp/afpd.%u.XXXXXX", getpid());
 +            setuplog(logstr);
 +        }
 +
          if (cmd == DSIFUNC_TICKLE) {
              /* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
              if ((child.flags & CHILD_DIE))
              if ( afp_switch[ function ] != NULL ) {
                  dsi->datalen = DSI_DATASIZ;
                  child.flags |= CHILD_RUNNING;
 +
 +                LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 +
                  err = (*afp_switch[function])(obj,
                                                (char *)&dsi->commands, dsi->cmdlen,
                                                (char *)&dsi->data, &dsi->datalen);
 +
 +                LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
 +                    AfpNum2name(function), AfpErr2name(err));
 +
                  child.flags &= ~CHILD_RUNNING;
  #ifdef FORCE_UIDGID
                /* bring everything back to old euid, egid */
diff --combined etc/afpd/afp_options.c
index aa728512e0ba722be8133d4d5f23f1ea25b699fc,f23c370b3319842ea7f00b03464d29500583abb3..0761724f292760d73b7c14e1a785af8e869e32c9
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: afp_options.c,v 1.54 2010-04-02 16:17:22 hat001 Exp $
 - * $Id: afp_options.c,v 1.51.4.2 2010-02-05 10:27:58 franklahm Exp $
++ * $Id: afp_options.c,v 1.54 2010/04/02 16:17:22 hat001 Exp $
   *
   * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
   * Copyright (c) 1990,1993 Regents of The University of Michigan.
@@@ -49,6 -49,7 +49,7 @@@ char *strchr (), *strrchr ()
  #include "globals.h"
  #include "status.h"
  #include "auth.h"
+ #include "dircache.h"
  
  #include <atalk/compat.h>
  
@@@ -165,7 -166,6 +166,7 @@@ void afp_options_init(struct afp_option
      options->defaultvol.name = _PATH_AFPDDEFVOL;
      options->systemvol.name = _PATH_AFPDSYSVOL;
      options->configfile = _PATH_AFPDCONF;
 +    options->sigconffile = _PATH_AFPDSIGCONF;
      options->uampath = _PATH_AFPDUAMPATH;
      options->uamlist = "uams_dhx.so,uams_dhx2.so";
      options->guest = "nobody";
      options->sleep = 10* 120; /* 10 h in 30 seconds tick */
      options->server_notif = 1;
      options->authprintdir = NULL;
 -    options->signature = "host";
      options->umask = 0;
  #ifdef ADMIN_GRP
      options->admingid = 0;
      /* 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
@@@ -398,6 -400,8 +400,6 @@@ int afp_options_parseline(char *buf, st
          options->port = strdup(c);
      if ((c = getoption(buf, "-ddpaddr")))
          atalk_aton(c, &options->ddpaddr);
 -    if ((c = getoption(buf, "-signature")) && (opt = strdup(c)))
 -        options->signature = opt;
  
      /* do a little checking for the domain name. */
      if ((c = getoption(buf, "-fqdn"))) {
  
      if ((c = getoption(buf, "-ntseparator")) && (opt = strdup(c)))
         options->ntseparator = opt;
+     if ((c = getoption(buf, "-dircachesize")))
+         options->dircachesize = atoi(c);
       
 +    if ((c = getoption(buf, "-signature")) && (opt = strdup(c))) {
 +        set_signature(opt, options);
 +    }
 +    else {
 +        set_signature("auto", options);
 +    }
 +
      return 1;
  }
  
@@@ -586,21 -586,19 +591,21 @@@ static void show_version_extended(void 
  static void show_paths( void )
  {
        printf( "             afpd.conf:\t%s\n", _PATH_AFPDCONF );
 +      printf( "    afp_signature.conf:\t%s\n", _PATH_AFPDSIGCONF );
        printf( "   AppleVolumes.system:\t%s\n", _PATH_AFPDSYSVOL );
        printf( "  AppleVolumes.default:\t%s\n", _PATH_AFPDDEFVOL );
        printf( "       UAM search path:\t%s\n", _PATH_AFPDUAMPATH );
 +    printf( "  Server messages path:\t%s\n", SERVERTEXT);
  }
  
  /*
 - * Display usage information about adpd.
 + * Display usage information about afpd.
   */
  static void show_usage( char *name )
  {
 -      fprintf( stderr, "Usage:\t%s [-dDIptTu] [-c maxconnections] [-f defaultvolumes] [-F config]\n", name );
 -      fprintf( stderr, "\t     [-g guest] [-L message] [-m umask][-n nbpname] [-P pidfile]\n" );
 -      fprintf( stderr, "\t     [-s systemvolumes] [-S port] [-U uams]\n" );
 +      fprintf( stderr, "Usage:\t%s [-duptDTI] [-f defaultvolumes] [-s systemvolumes] [-n nbpname]\n", name );
 +      fprintf( stderr, "\t     [-c maxconnections] [-g guest] [-P pidfile] [-S port] [-L message]\n" );
 +      fprintf( stderr, "\t     [-F configfile] [-U uams] [-m umask]\n" );
        fprintf( stderr, "\t%s -h|-v|-V\n", name );
  }
  
diff --combined etc/afpd/catsearch.c
index a1e96dfc50ab74649f67c09569f6ea8c99818e37,4a1d229f59e13c1036d9b5d974edb8a58ab249af..b266348ca0ca6a15073c1dfb003b70d90a426901
  #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"
@@@ -119,6 -120,8 +122,6 @@@ struct dsitem 
   
  
  #define DS_BSIZE 128
 -static u_int32_t cur_pos = 0;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
 -static DIR *dirpos = NULL; /* UNIX structure describing currently opened directory. */
  static int save_cidx = -1; /* Saved index of currently scanned directory. */
  
  static struct dsitem *dstack = NULL; /* Directory stack data... */
@@@ -144,6 -147,7 +147,7 @@@ static int addstack(char *uname, struc
        /* 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 +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 +194,7 @@@ static void clearstack(void
        save_cidx = -1;
        while (dsidx > 0) {
                dsidx--;
+         dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK;
                free(dstack[dsidx].path);
        }
  } 
@@@ -237,13 -243,13 +243,13 @@@ static struct finderinfo *unpack_buffer
  
  /* -------------------- */
  static struct finderinfo *
 -unpack_finderinfo(struct vol *vol, struct path *path, struct adouble **adp, struct finderinfo *finfo)
 +unpack_finderinfo(struct vol *vol, struct path *path, struct adouble **adp, struct finderinfo *finfo, int islnk)
  {
        packed_finder  buf;
        void           *ptr;
        
      *adp = adl_lkup(vol, path, *adp);
 -      ptr = get_finderinfo(vol, path->u_name, *adp, &buf);
 +      ptr = get_finderinfo(vol, path->u_name, *adp, &buf,islnk);
        return unpack_buffer(finfo, ptr);
  }
  
@@@ -265,8 -271,6 +271,8 @@@ static int crit_check(struct vol *vol, 
        u_int32_t ac_date, ab_date;
        static char convbuf[514]; /* for convert_charset dest_len parameter +2 */
        size_t len;
 +    int islnk;
 +    islnk=S_ISLNK(path->st.st_mode);
  
        if (S_ISDIR(path->st.st_mode)) {
                if (!c1.dbitmap)
  
          /* Check file type ID */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.f_type != 0) {
 -          finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
 +          finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                if (finfo->f_type != c1.finfo.f_type)
                        goto crit_check_ret;
        }
        /* Check creator ID */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.creator != 0) {
                if (!finfo) {
 -                      finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
 +                      finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                }
                if (finfo->creator != c1.finfo.creator)
                        goto crit_check_ret;
        /* Check finder info attributes */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.attrs != 0) {
                if (!finfo) {
 -                      finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
 +                      finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                }
  
                if ((finfo->attrs & c2.finfo.attrs) != c1.finfo.attrs)
        /* Check label */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.label != 0) {
                if (!finfo) {
 -                      finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
 +                      finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                }
                if ((finfo->label & c2.finfo.label) != c1.finfo.label)
                        goto crit_check_ret;
@@@ -492,8 -496,6 +498,8 @@@ static int rslt_add ( struct vol *vol, 
  static int catsearch(struct vol *vol, struct dir *dir,  
                     int rmatches, u_int32_t *pos, char *rbuf, u_int32_t *nrecs, int *rsize, int ext)
  {
 +    static u_int32_t cur_pos;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
 +    static DIR *dirpos;                /* UNIX structure describing currently opened directory. */
        int cidx, r;
        struct dirent *entry;
        int result = AFP_OK;
        char *rrbuf = rbuf;
      time_t start_time;
      int num_rounds = NUM_ROUNDS;
-     int cached;
      int cwd = -1;
 +    int error;
          
        if (*pos != 0 && *pos != cur_pos) {
                result = AFPERR_CATCHNG;
  
        /* FIXME: Category "offspring count ! */
  
 -      /* So we are beginning... */
 -    start_time = time(NULL);
  
        /* We need to initialize all mandatory structures/variables and change working directory appropriate... */
        if (*pos == 0) {
          goto catsearch_end;
      }
        
 +      /* So we are beginning... */
 +    start_time = time(NULL);
 +
        while ((cidx = reducestack()) != -1) {
-               cached = 1;
 -              if (dirpos == NULL) {
 +              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 (dirpos == NULL) {
++
 +              if (error || dirpos == NULL) {
                        switch (errno) {
                        case EACCES:
                                dstack[cidx].checked = 1;
                        } /* switch (errno) */
                        goto catsearch_end;
                }
 -
 -              if (chdir(dstack[cidx].path) != 0) {
 -                              result = AFPERR_NFILE;
 -                goto catsearch_end;
 -        }
                
 -
                while ((entry=readdir(dirpos)) != NULL) {
                        (*pos)++;
  
                                   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 -867,7 +872,7 @@@ static int catsearch_afp(AFPObj *obj _U
      
      /* 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);
  
diff --combined etc/afpd/directory.c
index 6480ca7f65c83158d770194d2a5f7a3bdea973bb,e86bd10db80f88389e97f71abbfed7563509b21d..467bc62d89e936c2cf69988db42f66b27ea3fe36
@@@ -1,42 -1,24 +1,24 @@@
  /*
-  * $Id: directory.c,v 1.140 2010-03-12 15:16:49 franklahm Exp $
 - * $Id: directory.c,v 1.131.2.14 2010-02-11 14:13:06 franklahm Exp $
++ * $Id: directory.c,v 1.140 2010/03/12 15:16:49 franklahm Exp $
   *
   * 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>
  #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"
  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
-  */
- struct dir  *curdir;
- int         afp_errno;
- #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;
-     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.
+  * 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 *
- dirsearch(const struct vol *vol, u_int32_t did)
- {
-     struct dir  *dir;
-     /* 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 );
-     }
-     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;
-     }
-     return NULL;
- }
  
- /* ------------------- */
- int get_afp_errno(const int param)
- {
-     if (afp_errno != AFPERR_DID1)
-         return afp_errno;
-     return param;
- }
- /* ------------------- */
- struct dir *
- dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
- {
-     struct dir *dir = NULL;
-     if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
-         struct dir key;
-         hnode_t *hn;
-         key.d_parent = cdir;
-         key.d_u_name = name;
-         hn = hash_lookup(vol->v_hash, &key);
-         if (hn) {
-             dir = hnode_get(hn);
-         }
-     }
-     return dir;
- }
- /* -----------------------------------------
-  * 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)
- {
-     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;
-     ret = dirsearch(vol, did);
-     if (ret != NULL || afp_errno == AFPERR_PARAM)
-         return ret;
-     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;
-     }
-     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;
-         }
-         strcpy(ptr - len, mpath);
-         ptr -= len;
-     }
-     /* fill the cache, another place where we know about the path type */
-     if (utf8) {
-         u_int16_t temp16;
-         u_int32_t temp;
-         ptr -= 2;
-         temp16 = htons(pathlen);
-         memcpy(ptr, &temp16, sizeof(temp16));
-         temp = htonl(kTextEncodingUTF8);
-         ptr -= 4;
-         memcpy(ptr, &temp, sizeof(temp));
-         ptr--;
-         *ptr = 3;
-     }
-     else {
-         ptr--;
-         *ptr = (unsigned char)pathlen;
-         ptr--;
-         *ptr = 2;
-     }
-     /* cname is not efficient */
-     if (cname( vol, ret, &ptr ) == NULL )
-         return NULL;
-     return dirsearch(vol, did);
- }
- /* child addition/removal */
- static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
- {
-     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);
-     }
- }
- static void dirchildremove(struct dir *a,struct dir *b)
- {
-     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;
- }
- /* --------------------------- */
- /* rotate the tree to the left */
- static void dir_leftrotate(struct vol *vol, struct dir *dir)
- {
-     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;
-     }
-     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 */
-     /* re-insert dir on the left tree */
-     if (dir != SENTINEL)
-         dir->d_back = right;
- }
- /* rotate the tree to the right */
- static void dir_rightrotate(struct vol *vol, struct dir *dir)
- {
-     struct dir *left = dir->d_left;
-     /* 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;
-     if (left != SENTINEL) {
-         left->d_back = dir->d_back;
-         left->d_right = dir;
-     }
-     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 */
-     /* re-insert dir on the right tree */
-     if (dir != SENTINEL)
-         dir->d_back = left;
- }
- #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;
-             }
-             /* 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;
-             }
-             /* 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;
-             }
-         }
-     }
-     dir->d_color = DIRTREE_COLOR_BLACK;
-     return dir;
- }
- #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);
-     }
- }
- /* 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. */
- static void dir_remove( struct vol *vol, struct dir *dir)
- {
- #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;
-     }
  
-     /* get that child */
-     leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
+ /*******************************************************************************************
+  * Globals
+  ******************************************************************************************/
  
-     /* 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;
-     }
-     /* 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;
-         memcpy(&save, dir, sizeof(save));
-         memcpy(dir, node, sizeof(struct dir));
-         /* 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;
-         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;
-         }
-         /* fix up children. */
-         tmp = dir->d_child;
-         while (tmp) {
-             tmp->d_parent = dir;
-             tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
-         }
-         if (node == curdir) /* another pointer to fixup */
-             curdir = dir;
-         /* 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;
-             }
-         }
-         /* 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);
-     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);
-     }
-     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);
-         }
-     }
-     /* 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;
-         }
-     }
-     return pdir;
- }
- #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)
- {
-     DIR               *dp;
-     struct dirent     *de;
-     int               ret;
-     static u_int32_t  did = 0;
-     static char       cname[MAXPATHLEN];
-     static char       lname[MAXPATHLEN];
-     ucs2_t        u2_path[MAXPATHLEN];
-     ucs2_t        u2_dename[MAXPATHLEN];
-     char          *tmp, *savepath;
-     if (!(vol->v_flags & AFPVOL_CASEINSEN))
-         return -1;
-     if (veto_file(ENUMVETO, path->u_name))
-         return -1;
-     savepath = path->u_name;
-     /* very simple cache */
-     if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
-         path->u_name = cname;
-         path->d_dir = NULL;
-         if (of_stat( path ) == 0 ) {
-             return 0;
-         }
-         /* something changed, we cannot stat ... */
-         did = 0;
-     }
-     if (NULL == ( dp = opendir( "." )) ) {
-         LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
-         return -1;
-     }
-     /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
-     if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, -1, u2_path, sizeof(u2_path)) )
-         LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
-     /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
-     ret = -1;
-     for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
-         if (NULL == check_dirent(vol, de->d_name))
-             continue;
-         if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, -1, u2_dename, sizeof(u2_dename)) )
-             continue;
-         if (strcasecmp_w( u2_path, u2_dename) == 0) {
-             tmp = path->u_name;
-             strlcpy(cname, de->d_name, sizeof(cname));
-             path->u_name = cname;
-             path->d_dir = NULL;
-             if (of_stat( path ) == 0 ) {
-                 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
-                 strlcpy(lname, tmp, sizeof(lname));
-                 did = dir->d_did;
-                 ret = 0;
-                 break;
-             }
-             else
-                 path->u_name = tmp;
-         }
-     }
-     closedir(dp);
-     if (ret) {
-         /* invalidate cache */
-         cname[0] = 0;
-         did = 0;
-         path->u_name = savepath;
-     }
-     /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
-     return ret;
- }
- /*
-  * 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(struct vol *vol, struct dir *dir, struct path *path)
- {
-     path->d_dir = NULL;
-     if ( path->u_name == NULL) {
-         afp_errno = AFPERR_PARAM;
-         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;
-         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 );
-     }
+ 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 */
++    NULL,/* struct dir * */
+     0,   /* stat is not set */
++    0,   /* errno */
++    {0} /* struct stat */
+ };
  
-     /* 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 );
-     }
+ /*******************************************************************************************
+  * Locals
+  ******************************************************************************************/
  
-     return( dir );
- }
  
  /* -------------------------
     appledouble mkdir afp error code.
@@@ -787,7 -107,7 +109,7 @@@ static int netatalk_mkdir(const char *n
  }
  
  /* ------------------- */
 -static int deletedir(char *dir)
 +static int deletedir(int dirfd, char *dir)
  {
      char path[MAXPATHLEN + 1];
      DIR *dp;
          return AFPERR_PARAM;
  
      /* already gone */
 -    if ((dp = opendir(dir)) == NULL)
 +    if ((dp = opendirat(dirfd, dir)) == NULL)
          return AFP_OK;
  
      strcpy(path, dir);
              break;
          }
          strcpy(path + len, de->d_name);
 -        if (stat(path, &st)) {
 +        if (lstatat(dirfd, path, &st)) {
              continue;
          }
          if (S_ISDIR(st.st_mode)) {
 -            err = deletedir(path);
 +            err = deletedir(dirfd, path);
          } else {
 -            err = netatalk_unlink(path);
 +            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(dir);
 +        err = netatalk_rmdir(dirfd, dir);
      }
      return err;
  }
  
  /* do a recursive copy. */
 -static int copydir(const struct vol *vol, char *src, char *dst)
 +static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
  {
      char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
      DIR *dp;
      /* doesn't exist or the path is too long. */
      if (((slen = strlen(src)) > sizeof(spath) - 2) ||
          ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
 -        ((dp = opendir(src)) == NULL))
 +        ((dp = opendirat(dirfd, src)) == NULL))
          return AFPERR_PARAM;
  
      /* try to create the destination directory */
          }
          strcpy(spath + slen, de->d_name);
  
 -        if (stat(spath, &st) == 0) {
 +        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, spath, dpath)))
 +                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
                      goto copydir_done;
 -            } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
 +            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
                  goto copydir_done;
  
              } else {
      }
  
      /* keep the same time stamp. */
 -    if (stat(src, &st) == 0) {
 +    if (lstatat(dirfd, src, &st) == 0) {
          ut.actime = ut.modtime = st.st_mtime;
          utime(dst, &ut);
      }
@@@ -916,418 -236,772 +238,772 @@@ copydir_done
      return err;
  }
  
+ /* ---------------------
+  * is our cached offspring count valid?
+  */
+ static int diroffcnt(struct dir *dir, struct stat *st)
+ {
+     return st->st_ctime == dir->ctime;
+ }
+ /* --------------------- */
+ static int invisible_dots(const struct vol *vol, const char *name)
+ {
+     return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
+ }
  
- /* --- public functions follow --- */
+ /* ------------------ */
+ 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;
+ }
  
- /* NOTE: we start off with at least one node (the root directory). */
- static struct dir *dirinsert(struct vol *vol, struct dir *dir)
+ /*!
+  * @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)
  {
-     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. */
+     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 {
-                 if (dir == dir->d_back->d_right) {
-                     dir = dir->d_back;
-                     dir_leftrotate(vol, dir);
+                 /* toUTF8 */
+                 if (mtoUTF8(vol, ret->m_name, strlen(ret->m_name), temp, MAXPATHLEN) == (size_t)-1) {
+                     afp_errno = AFPERR_PARAM;
+                     return -1;
                  }
-                 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);
+                 strcpy(ret->m_name, temp);
              }
-         } 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);
+         }
+         /* 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;
+         }
      }
  
-     vol->v_root->d_color = DIRTREE_COLOR_BLACK;
-     return NULL;
+     return 0;
  }
  
- /* ---------------------------- */
- struct dir *
- adddir(struct vol *vol, struct dir *dir, struct path *path)
+ /*!
+  * @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  *cdir, *edir;
-     int     upathlen;
-     char        *name;
-     char        *upath;
-     struct stat *st;
-     int         deleted;
-     struct adouble  ad;
-     struct adouble *adp = NULL;
-     cnid_t      id;
+     if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+         return NULL;
  
-     upath = path->u_name;
-     st    = &path->st;
-     upathlen = strlen(upath);
+     switch (afp_errno) {
  
-     /* 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)
-         adp = &ad;
+     case AFPERR_ACCESS:
+         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); /* 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);
+         }
+         ret->d_dir = dir;
  
-     id = get_id(vol, adp, st, dir->d_did, upath, upathlen);
+         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);
  
-     if (adp)
-         ad_close_metadata(adp);
+         return ret;
+     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;
+     }
+     /* DEADC0DE: never get here */
+     return NULL;
+ }
+ /*********************************************************************************************
+  * Interface
+  ********************************************************************************************/
+ int get_afp_errno(const int param)
+ {
+     if (afp_errno != AFPERR_DID1)
+         return afp_errno;
+     return param;
+ }
+ /*!
+  * @brief Resolve a DID
+  *
+  * Resolve a DID, allocate a struct dir for it
+  * 1. Check for special CNIDs 0 (invalid), 1 and 2.
+  * 2. Check if the DID is in the cache.
+  * 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)
+ {
+     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;
  
-     if (id == 0) {
+     LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {start}", ntohl(did));
+     /* 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;
+     }
+     /* Search the cache */
+     if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2 */
+         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;
+     }
+     utf8 = utf8_encoding();
+     maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
+     /* 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;
+     /*
+      * 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;
+     }
+     /* build the fullpath */
+     if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
+         || bconchar(fullpath, '/') != BSTR_OK
+         || bcatcstr(fullpath, upath) != BSTR_OK) {
+         err = 1;
+         goto exit;
      }
-     if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
+     /* stat it and check if it's a dir */
+     LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstring(fullpath));
+     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;
+         }
+     }
+     /* Get macname from unix name */
+     if ( (mpath = utompath(vol, upath, did, utf8)) == NULL ) {
+         afp_errno = AFPERR_NOOBJ;
+         err = 1;
+         goto exit;
+     }
+     /* 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;
+     }
+     /* Add it to the cache only if it's a dir */
+     if (dircache_add(ret) != 0) { /* 7 */
+         err = 1;
+         goto exit;
+     }
+     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 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)
+ {
+     DIR               *dp;
+     struct dirent     *de;
+     int               ret;
+     static u_int32_t  did = 0;
+     static char       cname[MAXPATHLEN];
+     static char       lname[MAXPATHLEN];
+     ucs2_t        u2_path[MAXPATHLEN];
+     ucs2_t        u2_dename[MAXPATHLEN];
+     char          *tmp, *savepath;
+     if (!(vol->v_flags & AFPVOL_CASEINSEN))
+         return -1;
+     if (veto_file(ENUMVETO, path->u_name))
+         return -1;
+     savepath = path->u_name;
+     /* very simple cache */
+     if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
+         path->u_name = cname;
+         path->d_dir = NULL;
+         if (of_stat( path ) == 0 ) {
+             return 0;
+         }
+         /* something changed, we cannot stat ... */
+         did = 0;
+     }
+     if (NULL == ( dp = opendir( "." )) ) {
+         LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
+         return -1;
+     }
+     /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
+     if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, -1, u2_path, sizeof(u2_path)) )
+         LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
+     /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
+     ret = -1;
+     for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
+         if (NULL == check_dirent(vol, de->d_name))
+             continue;
+         if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, -1, u2_dename, sizeof(u2_dename)) )
+             continue;
+         if (strcasecmp_w( u2_path, u2_dename) == 0) {
+             tmp = path->u_name;
+             strlcpy(cname, de->d_name, sizeof(cname));
+             path->u_name = cname;
+             path->d_dir = NULL;
+             if (of_stat( path ) == 0 ) {
+                 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
+                 strlcpy(lname, tmp, sizeof(lname));
+                 did = dir->d_did;
+                 ret = 0;
+                 break;
+             }
+             else
+                 path->u_name = tmp;
+         }
+     }
+     closedir(dp);
+     if (ret) {
+         /* invalidate cache */
+         cname[0] = 0;
+         did = 0;
+         path->u_name = savepath;
+     }
+     /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
+     return ret;
+ }
+ /*!
+  * @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
+  *
+  * @returns pointer to new struct dir or NULL on error
+  *
+  * @note Most of the time mac name and unix name are the same.
+  */
+ struct dir *dir_new(const char *m_name,
+                     const char *u_name,
+                     const struct vol *vol,
+                     cnid_t pdid,
+                     cnid_t did,
+                     bstring path)
+ {
+     struct dir *dir;
+     dir = (struct dir *) calloc(1, sizeof( struct dir ));
+     if (!dir)
+         return NULL;
+     if ((dir->d_m_name = bfromcstr(m_name)) == NULL) {
+         free(dir);
          return NULL;
      }
-     name  = path->m_name;
-     if ((cdir = dirnew(name, upath)) == NULL) {
-         LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
+     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;
+     }
+     if (m_name == u_name || !strcmp(m_name, u_name)) {
+         dir->d_u_name = dir->d_m_name;
+     }
+     else if ((dir->d_u_name = bfromcstr(u_name)) == NULL) {
+         bdestroy(dir->d_m_name);
+         free(dir);
          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;
-         }
-         /* 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 );
+     dir->d_did = did;
+     dir->d_pdid = pdid;
+     dir->d_vid = vol->v_vid;
+     dir->d_fullpath = path;
+     return dir;
  }
  
- /* --- 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)
+ /*!
+  * @brief Free a struct dir and all its members
+  *
+  * @param (rw) pointer to struct dir
+  */
+ void dir_free(struct dir *dir)
  {
      if (dir->d_u_name != dir->d_m_name) {
-         free(dir->d_u_name);
+         bdestroy(dir->d_u_name);
      }
      if (dir->d_m_name_ucs2)
          free(dir->d_m_name_ucs2);
-     free(dir->d_m_name);
+     bdestroy(dir->d_m_name);
+     bdestroy(dir->d_fullpath);
+     free(dir);
  }
  
- void dirfree(struct dir *dir)
+ /*!
+  * @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)
  {
-     if (!dir || (dir == SENTINEL))
-         return;
-     if ( dir->d_left != SENTINEL ) {
-         dirfree( dir->d_left );
-     }
-     if ( dir->d_right != SENTINEL ) {
-         dirfree( dir->d_right );
+     int err = 0;
+     struct dir  *cdir = NULL;
+     cnid_t      id;
+     struct adouble  ad;
+     struct adouble *adp = NULL;
+     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();
+             exit(EXITERR_SYS);
+         }
      }
  
-     if (dir != SENTINEL) {
-         dirfreename(dir);
-         free( dir );
+     /* get_id needs adp for reading CNID from adouble file */
+     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+     if ((ad_open_metadata(path->u_name, ADFLAGS_DIR, 0, &ad)) == 0) /* 1 */
+         adp = &ad;
+     /* Get CNID */
+     if ((id = get_id(vol, adp, &path->st, dir->d_did, path->u_name, len)) == 0) { /* 2 */
+         err = 1;
+         goto exit;
      }
- }
  
- /* --------------------------------------------
-  * most of the time mac name and unix name are the same
-  */
- struct dir *dirnew(const char *m_name, const char *u_name)
- {
-     struct dir *dir;
+     if (adp)
+         ad_close_metadata(adp);
  
-     dir = (struct dir *) calloc(1, sizeof( struct dir ));
-     if (!dir)
-         return NULL;
+     /* 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;
+         }
+     }
  
-     if ((dir->d_m_name = strdup(m_name)) == NULL) {
-         free(dir);
-         return NULL;
+     /* 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 (m_name == u_name || !strcmp(m_name, u_name)) {
-         dir->d_u_name = dir->d_m_name;
+     /* 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;
      }
-     else if ((dir->d_u_name = strdup(u_name)) == NULL) {
-         free(dir->d_m_name);
-         free(dir);
-         return NULL;
+     if ((dircache_add(cdir)) != 0) { /* 4 */
+         LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstring(fullpath));
+         exit(EXITERR_SYS);
      }
  
-     dir->d_m_name_ucs2 = NULL;
-     dir->d_left = dir->d_right = SENTINEL;
-     dir->d_next = dir->d_prev = dir;
-     return 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);
  }
  
- #if 0
- /* ------------------ */
- static hash_val_t hash_fun_dir(const void *key)
+ /*!
+  * @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
+  */
+ int dir_remove(const struct vol *vol, struct dir *dir)
  {
-     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
+     AFP_ASSERT(vol);
+     AFP_ASSERT(dir);
  
- #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 (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+         return 0;
  
- #if !defined (get16bits)
- #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)    \
-                       +(uint32_t)(((const uint8_t *)(d))[0]) )
- #endif
+     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;
+     }
  
- static hash_val_t hash_fun2_dir(const void *key)
- {
-     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;
+     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);
+         }
      }
  
-     /* Force "avalanching" of final 127 bits */
-     hash ^= hash << 3;
-     hash += hash >> 5;
-     hash ^= hash << 4;
-     hash += hash >> 17;
-     hash ^= hash << 25;
-     hash += hash >> 6;
+     LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}",
+         ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
  
-     return hash;
+     dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 3 */
+     dir_free(dir);              /* 4 */
+     return 0;
  }
  
- /* ---------------- */
- static int hash_comp_dir(const void *key1, const void *key2)
+ /*!
+  * @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 *k1 = key1;
-     const struct dir *k2 = key2;
+     int ret = 0;
  
-     return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
- }
+     /* Remove it from the cache */
+     dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
  
- /* ---------------- */
- hash_t *
- dirhash(void)
- {
-     return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir);
- }
+     if (pdid)
+         dir->d_pdid = pdid;
+     if (did)
+         dir->d_did = did;
  
- /* ------------------ */
- 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;
+     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);
+         if (new_uname == NULL)
+             new_uname = new_mname;
+         /* 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();
+         exit(EXITERR_SYS);
+     }
+     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;
              len = ntohs(len16);
              data += 2;
              size = 7;
-             sep = 0; /* '/';*/
              break;
          }
          /* else it's an error */
          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;
 -    AFP_ASSERT(vol);
++    int ret;
 -    if (dir == NULL)
 -        return -1;
++    AFP_ASSERT(vol);
++    AFP_ASSERT(dir);
  
-     if ( dir == curdir ) {
+     LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')",
+         cfrombstring(curdir->d_fullpath), getcwdpath());
+     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 ( chdir(cfrombstring(dir->d_fullpath)) < 0 ) {
 -        LOG(log_debug, logtype_afpd, "movecwd('%s'): %s", cfrombstring(dir->d_fullpath), strerror(errno));
++    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:
              break;
          default:
              afp_errno = AFPERR_NOOBJ;
          }
          return( -1 );
      }
-     vol->v_curdir = curdir = dir;
+     curdir = dir;
      return( 0 );
  }
  
@@@ -1658,30 -1282,15 +1297,15 @@@ void setdiroffcnt(struct dir *dir, stru
      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
@@@ -1720,20 -1329,14 +1344,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 ) {
          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;
          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 :
                  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));
                  }
          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 :
      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 );
@@@ -2001,33 -1608,7 +1623,7 @@@ int afp_setdirparams(AFPObj *obj, char 
   *
   * 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;
           * 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));
          }
      }
  
@@@ -2336,8 -1917,8 +1932,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);
  
      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;
@@@ -2441,7 -2022,7 +2037,7 @@@ int afp_syncdir(AFPObj *obj _U_, char *
  
          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);
      }
  
@@@ -2497,8 -2078,10 +2093,10 @@@ int afp_createdir(AFPObj *obj, char *ib
      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;
      }
  
@@@ -2534,23 -2117,17 +2132,21 @@@ createdir_done
   * dst       new unix filename (not a pathname)
   * newname   new mac name
   * newparent curdir
 + * dirfd     -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
   */
 -int renamedir(const struct vol *vol, char *src, char *dst,
 +int renamedir(const struct vol *vol,
 +              int dirfd,
 +              char *src,
 +              char *dst,
                struct dir *dir,
                struct dir *newparent,
                char *newname)
  {
      struct adouble  ad;
-     struct dir      *parent;
-     char                *buf;
-     int         len, err;
+     int             err;
  
      /* existence check moved to afp_moveandrename */
 -    if ( unix_rename( src, dst ) < 0 ) {
 +    if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
          switch ( errno ) {
          case ENOENT :
              return( AFPERR_NOOBJ );
          case EXDEV:
              /* this needs to copy and delete. bleah. that means we have
               * to deal with entire directory hierarchies. */
 -            if ((err = copydir(vol, src, dst)) < 0) {
 -                deletedir(dst);
 +            if ((err = copydir(vol, dirfd, src, dst)) < 0) {
 +                deletedir(-1, dst);
                  return err;
              }
 -            if ((err = deletedir(src)) < 0)
 +            if ((err = deletedir(dirfd, src)) < 0)
                  return err;
              break;
          default :
          }
      }
  
 -    vol->vfs->vfs_renamedir(vol, src, dst);
 +    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)) {
          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 );
  }
  
@@@ -2649,7 -2182,7 +2201,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 );
      }
  
      if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
  
          ad_getattr(&ad, &ashort);
 -        ad_close( &ad, ADFLAGS_HF );
 +        ad_close_metadata(&ad);
          if ((ashort & htons(ATTRBIT_NODELETE))) {
              return  AFPERR_OLOCK;
          }
          }
      }
  
-     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(cfrombstring(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 );
      }
diff --combined etc/afpd/directory.h
index 5334be5d5610817c7e117f2efd3390c4420f2aff,852d5343ddd06c4e8acd6eb2df381ac063c36e8f..0fe65e7000af0f02fe5a7e3e7049e013d4c96e16
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: directory.h,v 1.34 2010-03-12 15:16:49 franklahm Exp $
 - * $Id: directory.h,v 1.33.4.2 2010-02-05 10:27:59 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;
  #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 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_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         setdirparams (struct vol *, struct path *, u_int16_t, char *);
 -extern int         renamedir (const struct vol *, 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 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);
diff --combined etc/afpd/enumerate.c
index c06ef20300f3f296e4e2b0d41b2b40404a4538b0,3f754a26d19ba07be8ae24d4a06b017c9cd89614..daa83a693a2557e82c106839f5d2dcc278046f2e
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: enumerate.c,v 1.49 2010-02-10 14:05:37 franklahm Exp $
 - * $Id: enumerate.c,v 1.48.2.2 2010-02-04 14:34:31 franklahm Exp $
++ * $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 -270,10 +270,10 @@@ static int enumerate(AFPObj *obj _U_, c
          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 */
      if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
          sd.sd_last = sd.sd_buf;
          /* if dir was in the cache we don't have the inode */
 -        if (( !o_path->st_valid && stat( ".", &o_path->st ) < 0 ) ||
 +        if (( !o_path->st_valid && lstat( ".", &o_path->st ) < 0 ) ||
                (ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0) 
          {
              switch (errno) {
              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 ) {
diff --combined etc/afpd/file.c
index aa692842ed4a5c8d919080336292528d33dbcbd2,30e077d0a4ba2a3a0b705919f560788061fd4365..93b1a169612ab5c4f25e6fd69fc4fb01c0e8baa2
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: file.c,v 1.141 2010-03-12 15:16:49 franklahm Exp $
 - * $Id: file.c,v 1.131.2.3 2010-02-04 14:34:31 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.
@@@ -89,7 -89,7 +89,7 @@@ static int default_type(void *finder
  }
  
  /* FIXME path : unix or mac name ? (for now it's unix name ) */
 -void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data)
 +void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
  {
      struct extmap     *em;
      void                *ad_finder = NULL;
              memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
          }
      }
 +
 +    if (islink){
 +        u_int16_t linkflag;
 +        memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
 +        linkflag |= htons(FINDERINFO_ISALIAS);
 +        memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
 +        memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4); 
 +        memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); 
 +        chk_ext = 0;
 +    }
 +
      /** Only enter if no appledouble information and no finder information found. */
      if (chk_ext && (em = getextmap( upath ))) {
          memcpy(data, em->em_type, sizeof( em->em_type ));
@@@ -202,7 -191,7 +202,7 @@@ char *set_name(const struct vol *vol, c
                                  (1 << FILPBIT_UNIXPR)))
  
  /* -------------------------- */
- u_int32_t get_id(struct vol *vol, struct adouble *adp,  const struct stat *st,
+ u_int32_t get_id(const struct vol *vol, struct adouble *adp,  const struct stat *st,
                   const cnid_t did, char *upath, const int len) 
  {
      u_int32_t adcnid;
@@@ -259,9 -248,6 +259,6 @@@ 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;
  #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 :
              break;
  
          case FILPBIT_FINFO :
 -          get_finderinfo(vol, upath, adp, (char *)data);
 +              get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
              data += ADEDLEN_FINDERI;
              break;
  
          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 :
@@@ -524,10 -516,6 +527,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;
  
      if ( adp ) {
          ad_close_metadata( adp);
      }
- #ifdef DEBUG
-     LOG(log_debug9, logtype_afpd, "end getfilparams:");
- #endif /* DEBUG */
  
      return( rc );
  }
@@@ -806,28 -791,6 +802,28 @@@ int setfilparams(struct vol *vol
          case FILPBIT_FINFO :
              change_mdate = 1;
              memcpy(finder_buf, buf, 32 );
 +            if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
 +            // SLFINFO
 +                int fp;
 +                ssize_t len;
 +                int erc=1;
 +                char buf[PATH_MAX+1];
 +                if ((fp=open(path->u_name,O_RDONLY))>=0){
 +                    if ((len=read(fp,buf,PATH_MAX+1))){
 +                        if (unlink(path->u_name)==0){
 +                            buf[len]=0;
 +                            erc = symlink(buf, path->u_name);
 +                            if (!erc)
 +                                of_stat(path);
 +                        }
 +                    }
 +                    close(fp);
 +                }
 +                if (erc!=0){
 +                    err=AFPERR_BITMAP;
 +                    goto setfilparam_done;
 +                }
 +            }
              buf += 32;
              break;
          case FILPBIT_UNIXPR :
           * - change of modification date
           * - UNIX privs (Bug-ID #2863424)
           */
 -        if ( (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
 +        if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
              LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
 -            return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
 +            return AFPERR_ACCESS;
          }
          LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
          isad = 0;
@@@ -1003,19 -966,21 +999,19 @@@ setfilparam_done
   * renamefile and copyfile take the old and new unix pathnames
   * and the new mac name.
   *
 + * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
 + *             passing -1 means this is not used, src path is a full path
   * src         the source path 
   * dst         the dest filename in current dir
   * newname     the dest mac name
   * adp         adouble struct of src file, if open, or & zeroed one
   *
   */
 -int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp)
 +int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
  {
      int               rc;
  
 -#ifdef DEBUG
 -    LOG(log_debug9, logtype_afpd, "begin renamefile:");
 -#endif /* DEBUG */
 -
 -    if ( unix_rename( src, dst ) < 0 ) {
 +    if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
          switch ( errno ) {
          case ENOENT :
              return( AFPERR_NOOBJ );
                /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
                return AFPERR_OLOCK; /* little lie */
            }
 -            if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
 +            if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
                  /* on error copyfile delete dest */
                  return( rc );
              }
 -            return deletefile(vol, src, 0);
 +            return deletefile(vol, sdir_fd, src, 0);
          default :
              return( AFPERR_PARAM );
          }
      }
  
 -    if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) {
 +    if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
          int err;
          
          err = errno;        
         * we know we are on the same device 
        */
        if (err) {
 -          unix_rename( dst, src ); 
 +        unix_rename(-1, dst, sdir_fd, src ); 
            /* return the first error */
            switch ( err) {
              case ENOENT :
          ad_flush( adp );
          ad_close( adp, ADFLAGS_HF );
      }
 -#ifdef DEBUG
 -    LOG(log_debug9, logtype_afpd, "end renamefile:");
 -#endif /* DEBUG */
  
      return( AFP_OK );
  }
@@@ -1202,10 -1170,9 +1198,10 @@@ int afp_copyfile(AFPObj *obj, char *ibu
      }
      denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
                    getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
 -    ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
 +
      if (denyreadset) {
 -        return AFPERR_DENYCONF;
 +        retvalue = AFPERR_DENYCONF;
 +        goto copy_exit;
      }
  
      newname = obj->newtmp;
  
      p = ctoupath( s_vol, curdir, newname );
      if (!p) {
 -        return AFPERR_PARAM;
 -    
 +        retvalue = AFPERR_PARAM;
 +        goto copy_exit;
      }
 +
  #ifdef FORCE_UIDGID
      /* FIXME svid != dvid && dvid's user can't read svid */
  #endif
      if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
 -        return( AFPERR_PARAM );
 +        retvalue = AFPERR_PARAM;
 +        goto copy_exit;
      }
  
 -    if (d_vol->v_flags & AFPVOL_RO)
 -        return AFPERR_VLOCK;
 +    if (d_vol->v_flags & AFPVOL_RO) {
 +        retvalue = AFPERR_VLOCK;
 +        goto copy_exit;
 +    }
  
      if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
 -        return afp_errno;
 +        retvalue = afp_errno;
 +        goto copy_exit;
      }
  
      if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
 -        return get_afp_errno(AFPERR_NOOBJ); 
 +        retvalue = get_afp_errno(AFPERR_NOOBJ);
 +        goto copy_exit;
      }
 +    
      if ( *s_path->m_name != '\0' ) {
 -      path_error(s_path, AFPERR_PARAM);
 +      retvalue =path_error(s_path, AFPERR_NOOBJ);
 +        goto copy_exit;
      }
  
      /* one of the handful of places that knows about the path type */
      if (copy_path_name(d_vol, newname, ibuf) < 0) {
 -        return( AFPERR_PARAM );
 +        retvalue = AFPERR_PARAM;
 +        goto copy_exit;
      }
      /* newname is always only a filename so curdir *is* its
       * parent folder
      */
      if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
 -        return( AFPERR_PARAM );
 +        retvalue =AFPERR_PARAM;
 +        goto copy_exit;
      }
 -    if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
 -        return err;
 +
 +    if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
 +        retvalue = err;
 +        goto copy_exit;
      }
      curdir->offcnt++;
  
  
      setvoltime(obj, d_vol );
  
 +copy_exit:
 +    ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
      return( retvalue );
  }
  
@@@ -1380,13 -1333,8 +1376,13 @@@ static int copy_fork(int eid, struct ad
   * because we are doing it elsewhere.
   * currently if newname is NULL then adp is NULL. 
   */
 -int copyfile(const struct vol *s_vol, const struct vol*d_vol, 
 -    char *src, char *dst, char *newname, struct adouble *adp)
 +int copyfile(const struct vol *s_vol,
 +             const struct vol *d_vol, 
 +             int sfd,
 +             char *src,
 +             char *dst,
 +             char *newname,
 +             struct adouble *adp)
  {
      struct adouble    ads, add;
      int                       err = 0;
      int                 stat_result;
      struct stat         st;
      
 -#ifdef DEBUG
 -    LOG(log_debug9, logtype_afpd, "begin copyfile:");
 -#endif /* DEBUG */
 +    LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
 +        sfd, src, dst, newname);
  
      if (adp == NULL) {
          ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
          adp = &ads;
      }
 -    ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
 +
      adflags = ADFLAGS_DF;
      if (newname) {
          adflags |= ADFLAGS_HF;
      }
  
 -    if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
 +    if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
          ret_err = errno;
          goto done;
      }
        st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
      }
  
 +    ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
      if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
          ret_err = errno;
          ad_close( adp, adflags );
          if (EEXIST != ret_err) {
 -            deletefile(d_vol, dst, 0);
 +            deletefile(d_vol, -1, dst, 0);
              goto done;
          }
          return AFPERR_EXIST;
      if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
          /* copy the data fork */
          if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
 -            err = d_vol->vfs->vfs_copyfile(d_vol, src, dst);
 +            err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
          }
      }
  
      } 
  
      if (ret_err) {
 -        deletefile(d_vol, dst, 0);
 +        deletefile(d_vol, -1, dst, 0);
      }
      else if (stat_result == 0) {
          /* set dest modification date to src date */
        */
      }
  
 -#ifdef DEBUG
 -    LOG(log_debug9, logtype_afpd, "end copyfile:");
 -#endif /* DEBUG */
 -
  done:
      switch ( ret_err ) {
      case 0:
@@@ -1522,57 -1474,59 +1518,57 @@@ u_int16_t   bshort = 0
        }
        return 0;
  }
 -
 -int deletefile(const struct vol *vol, char *file, int checkAttrib)
 +/* 
 + * dirfd can be used for unlinkat semantics
 + */
 +int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
  {
      struct adouble    ad;
 -    struct adouble      *adp = &ad;
 +    struct adouble      *adp = NULL;
      int                       adflags, err = AFP_OK;
 +    int                       meta = 0;
  
 -#ifdef DEBUG
 -    LOG(log_debug9, logtype_afpd, "begin deletefile:");
 -#endif /* DEBUG */
 +    LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
  
 -    /* try to open both forks at once */
 -    adflags = ADFLAGS_DF|ADFLAGS_HF;
 +    ad_init(&ad, vol->v_adouble, vol->v_ad_options);
      if (checkAttrib) {
          /* was EACCESS error try to get only metadata */
 -        ad_init(&ad, vol->v_adouble, vol->v_ad_options);
          /* we never want to create a resource fork here, we are going to delete it 
           * moreover sometimes deletefile is called with a no existent file and 
           * ad_open would create a 0 byte resource fork
          */
 -        if ( ad_metadata( file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
 -            ad_close( &ad, adflags );
 +        if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
              if ((err = check_attrib(&ad))) {
 +               ad_close_metadata(&ad);
                 return err;
              }
 +            meta = 1;
          }
      }
   
 -    while(1) {
 -      ad_init(&ad, vol->v_adouble, vol->v_ad_options);  /* OK */
 -        if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
 -            switch (errno) {
 -            case ENOENT:
 -                if (adflags == ADFLAGS_DF)
 -                    return AFPERR_NOOBJ;
 -                   
 -                /* that failed. now try to open just the data fork */
 -                adflags = ADFLAGS_DF;
 -                continue;
 -
 -            case EACCES:
 -                adp = NULL; /* maybe it's a file with no write mode for us */
 -                break;      /* was return AFPERR_ACCESS;*/
 -            case EROFS:
 -                return AFPERR_VLOCK;
 -            default:
 -                return( AFPERR_PARAM );
 -            }
 +    /* try to open both forks at once */
 +    adflags = ADFLAGS_DF;
 +    if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
 +        switch (errno) {
 +        case ENOENT:
 +            err = AFPERR_NOOBJ;
 +            goto end;
 +        case EACCES: /* maybe it's a file with no write mode for us */
 +            break;   /* was return AFPERR_ACCESS;*/
 +        case EROFS:
 +            err = AFPERR_VLOCK;
 +            goto end;
 +        default:
 +            err = AFPERR_PARAM;
 +            goto end;
          }
 -        break;        /* from the while */
 +    }
 +    else {
 +        adp = &ad;
      }
  
 -    if (adp && (adflags & ADFLAGS_HF) ) {
 +    if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
 +        adflags |= ADFLAGS_HF;
          /* FIXME we have a pb here because we want to know if a file is open 
           * there's a 'priority inversion' if you can't open the ressource fork RW
           * you can delete it if it's open because you can't get a write lock.
           * FIXME it doesn't work for RFORK open read only and fork open without deny mode
           */
          if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
 -            ad_close( &ad, adflags );
 -            return( AFPERR_BUSY );
 +            err = AFPERR_BUSY;
 +            goto end;
          }
      }
  
      if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
          err = AFPERR_BUSY;
 -    }
 -    else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
 +    } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
          cnid_t id;
 -        if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
 -        {
 +        if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
              cnid_delete(vol->v_cdb, id);
          }
      }
 +
 +end:
 +    if (meta)
 +        ad_close_metadata(&ad);
 +
      if (adp)
          ad_close( &ad, adflags );  /* ad_close removes locks if any */
  
 -#ifdef DEBUG
 -    LOG(log_debug9, logtype_afpd, "end deletefile:");
 -#endif /* DEBUG */
 -
      return err;
  }
  
@@@ -1695,7 -1650,7 +1691,7 @@@ static int reenumerate_loop(struct dire
      cnid_t        did  = param->did;
      cnid_t      aint;
      
 -    if ( stat(de->d_name, &path.st)<0 )
 +    if ( lstat(de->d_name, &path.st)<0 )
          return 0;
      
      /* update or add to cnid */
@@@ -1740,7 -1695,7 +1736,7 @@@ reenumerate_id(struct vol *vol, char *n
      }
      
      /* FIXME use of_statdir ? */
 -    if (stat(name, &st)) {
 +    if (lstat(name, &st)) {
        return -1;
      }
  
@@@ -1906,11 -1861,15 +1902,15 @@@ int afp_deleteid(AFPObj *obj _U_, char 
      }
  
      if (NULL == ( dir = dirlookup( vol, id )) ) {
+         if (afp_errno == AFPERR_NOOBJ) {
+             err = AFPERR_NOOBJ;
+             goto delete;
+         }
          return( AFPERR_PARAM );
      }
  
      err = AFP_OK;
 -    if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
 +    if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
          switch (errno) {
          case EACCES:
          case EPERM:
      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:
@@@ -2132,17 -2092,17 +2133,17 @@@ int afp_exchangefiles(AFPObj *obj, cha
      }
  
      /* now, quickly rename the file. we error if we can't. */
 -    if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
 +    if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
          goto err_exchangefile;
      of_rename(vol, s_of, sdir, spath, curdir, temp);
  
      /* rename destination to source */
 -    if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
 +    if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
          goto err_src_to_tmp;
      of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
  
      /* rename temp to destination */
 -    if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
 +    if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
          goto err_dest_to_src;
      of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
  
      if (did) {
        cnid_delete(vol->v_cdb, did);
      }
 -    if ((did && ( (crossdev && stat( upath, &srcst) < 0) || 
 +    if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
                  cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
         ||
 -       (sid && ( (crossdev && stat(p, &destst) < 0) ||
 +       (sid && ( (crossdev && lstat(p, &destst) < 0) ||
                  cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
      ) {
          switch (errno) {
       * properly. */
  err_temp_to_dest:
      /* rename dest to temp */
 -    renamefile(vol, upath, temp, temp, adsp);
 +    renamefile(vol, -1, upath, temp, temp, adsp);
      of_rename(vol, s_of, curdir, upath, curdir, temp);
  
  err_dest_to_src:
      /* rename source back to dest */
 -    renamefile(vol, p, upath, path->m_name, addp);
 +    renamefile(vol, -1, p, upath, path->m_name, addp);
      of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
  
  err_src_to_tmp:
      /* rename temp back to source */
 -    renamefile(vol, temp, p, spath, adsp);
 +    renamefile(vol, -1, temp, p, spath, adsp);
      of_rename(vol, s_of, curdir, temp, sdir, spath);
  
  err_exchangefile:
diff --combined etc/afpd/file.h
index 6cf4ae22f8184d15319dddb26d6d25b75bf5c832,ad4e312a5c7502a72869b6eb6263d7a95e7bde40..58a574c6c1aa44180868b1fa4464602c4d04b43e
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: file.h,v 1.26 2010-03-12 15:16:49 franklahm Exp $
 - * $Id: file.h,v 1.24.4.1 2010-02-01 10:56:08 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.
@@@ -115,19 -115,19 +115,19 @@@ extern int getfilparams (struct vol *, 
                                   struct dir *, char *buf, size_t *);
  
  extern int setfilparams (struct vol *, struct path *, u_int16_t, char *);
 -extern int renamefile   (const struct vol *, char *, char *, char *, struct adouble *);
 -extern int copyfile     (const struct vol *, const struct vol *, char *, char *, char *, struct adouble *);
 -extern int deletefile   (const struct vol *, char *, int);
 +extern int renamefile   (const struct vol *, int, char *, char *, char *, struct adouble *);
 +extern int copyfile     (const struct vol *, const struct vol *, int, char *, char *, char *, struct adouble *);
 +extern int deletefile   (const struct vol *, int, char *, int);
  
  extern int getmetadata  (struct vol *vol, u_int16_t bitmap, struct path *path, 
                           struct dir *dir, char *buf, size_t *buflen, struct adouble *adp);
  
 -extern void *get_finderinfo (const struct vol *, const char *, struct adouble *, void *);
 +extern void *get_finderinfo (const struct vol *, const char *, struct adouble *, void *, int);
  
  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 *,
+ extern u_int32_t get_id  (const struct vol *, struct adouble *, const struct stat *,
                                  const cnid_t , char *, const int );
  
  /* FP functions */
diff --combined etc/afpd/filedir.c
index 3e5cb2f5ee8048597a75d0552c971f7c769821f9,b79e1251c65d17add04d1c6541eafc8032ea33b4..fd7cc8899e46283f3c90cf8c3972dbb1f0cf711e
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: filedir.c,v 1.73 2010-03-12 15:16:49 franklahm Exp $
 - * $Id: filedir.c,v 1.69.2.3 2010-02-04 14:34:31 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,6 -39,8 +39,8 @@@ 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 "desktop.h"
  #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));
                      upath, strerror(errno));
                  ret = AFPERR_ACCESS;
              }
 -            else if (chmod(upath,(st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0)
 +            else if ((!S_ISLNK(st->st_mode)) && (chmod(upath,(st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0))
              {
                  LOG(log_error, logtype_afpd,
                      "matchfile2dirperms(%s): Error adding file read permissions: %s",
                      adpath, strerror(errno));
                  ret = AFPERR_ACCESS;
              }
-             seteuid(uid); 
+             seteuid(uid);
          }
      } /* end else if stat success */
  
  
  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;
      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 );
      }
  
      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) {
          of_statdir(vol, s_path);
      }
      if ( s_path->st_errno != 0 ) {
-         return( AFPERR_NOOBJ );
+         if (afp_errno != AFPERR_ACCESS) {
+             return( AFPERR_NOOBJ );
+         }
      }
  
  
      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 */
  
  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;
      ibuf += sizeof( did);
  
      if (NULL == ( dir = dirlookup( vol, did )) ) {
-       return afp_errno;    
+         return afp_errno;
      }
  
      memcpy( &bitmap, ibuf, sizeof( bitmap ));
      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;
      }
  
      if ( path->st_errno != 0 ) {
-         return( AFPERR_NOOBJ );
+         if (afp_errno != AFPERR_ACCESS)
+             return( AFPERR_NOOBJ );
      }
      /*
       * If ibuf is odd, make it even.
      return( rc );
  }
  
- /* -------------------------------------------- 
+ /* --------------------------------------------
     Factorise some checks on a pathname
  */
  int check_name(const struct vol *vol, char *name)
      return 0;
  }
  
 -/* -------------------------
 -   move and rename sdir:oldname to curdir:newname in volume vol
 -
 -   special care is needed for lock
 +/* ------------------------- 
 +    move and rename sdir:oldname to curdir:newname in volume vol
 +    special care is needed for lock   
  */
 -static int moveandrename(const struct vol *vol, struct dir *sdir, char *oldname, char *newname, int isdir)
 +static int moveandrename(const struct vol *vol,
 +                         struct dir *sdir,
 +                         int sdir_fd,
 +                         char *oldname,
 +                         char *newname,
 +                         int isdir)
  {
      char            *p;
      char            *upath;
      int             rc;
      struct stat     *st, nst;
      int             adflags;
 -    struct adouble  ad;
 -    struct adouble  *adp;
 -    struct ofork    *opened = NULL;
 -    struct path         path;
 -    cnid_t      id;
 +    struct adouble    ad;
 +    struct adouble    *adp;
 +    struct ofork      *opened = NULL;
 +    struct path     path;
 +    cnid_t          id;
 +    int             cwd_fd;
  
      ad_init(&ad, vol->v_adouble, vol->v_ad_options);
      adp = &ad;
      adflags = 0;
  
      if (!isdir) {
 -        p = mtoupath(vol, oldname, sdir->d_did, utf8_encoding());
 -        if (!p) {
 +        if ((p = mtoupath(vol, oldname, sdir->d_did, utf8_encoding())) == NULL)
              return AFPERR_PARAM; /* can't convert */
 -        }
 +
 +#ifndef HAVE_RENAMEAT
 +        /* Need full path */
          id = cnid_get(vol->v_cdb, sdir->d_did, p, strlen(p));
          p = ctoupath( vol, sdir, oldname );
 -        if (!p) {
 +        if (!p)
              return AFPERR_PARAM; /* pathname too long */
 -        }
 +#endif /* HAVE_RENAMEAT */
 +
          path.st_valid = 0;
          path.u_name = p;
 -        if ((opened = of_findname(&path))) {
 +#ifdef HAVE_RENAMEAT
 +        opened = of_findnameat(sdir_fd, &path);
 +#else
 +        opened = of_findname(&path);
 +#endif /* HAVE_RENAMEAT */
 +        if (opened) {
              /* reuse struct adouble so it won't break locks */
              adp = opened->of_ad;
          }
 -    }
 -    else {
 +    } 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 the full pathname of the source fs object.
 -     *
 -     * we are in the dest folder so we need to use p for ad_open
 +     * p now points to either
 +     *   a) full pathname of the source fs object (if renameat is not available)
 +     *   b) the oldname (renameat is available)
 +     * we are in the dest folder so we need to use 
 +     *   a) p for ad_open
 +     *   b) fchdir sdir_fd before eg ad_open or use *at functions where appropiate
       */
  
 +    if (sdir_fd != -1) {
 +        if ((cwd_fd = open(".", O_RDONLY)) == -1)
 +            return AFPERR_MISC;
 +        if (fchdir(sdir_fd) != 0)
 +            return AFPERR_MISC;
 +    }
      if (!ad_metadata(p, adflags, adp)) {
          u_int16_t bshort;
  
          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) {
 +        if (fchdir(cwd_fd) != 0) {
 +            LOG(log_error, logtype_afpd, "moveandrename: %s", strerror(errno) );
 +            return AFPERR_MISC;
 +        }
 +    }
  
-     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;
  
          if (of_findname(&path)) {
              rc = AFPERR_EXIST; /* was AFPERR_BUSY; */
          } else {
 -            rc = renamefile(vol, p, upath, newname, adp );
 +            rc = renamefile(vol, sdir_fd, p, upath, newname, adp );
              if (rc == AFP_OK)
                  of_rename(vol, opened, sdir, oldname, curdir, newname);
          }
      } else {
 -        rc = renamedir(vol, p, upath, sdir, curdir, newname);
 +        rc = renamedir(vol, sdir_fd, p, upath, sdir, curdir, newname);
      }
      if ( rc == AFP_OK && id ) {
          /* renaming may have moved the file/dir across a filesystem */
  /* -------------------------------------------- */
  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;
  
      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;
          }
      }
      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 */
      if (!plen) {
          return AFP_OK; /* newname == oldname same dir */
      }
 -
 -    rc = moveandrename(vol, sdir, oldname, newname, isdir);
 -
 +    
 +    rc = moveandrename(vol, sdir, -1, oldname, newname, isdir);
      if ( rc == AFP_OK ) {
          setvoltime(obj, vol );
      }
  /* ------------------------------- */
  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;
  
      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 {
              rc = AFPERR_NOOBJ;
          }
          else {
 -            rc = deletefile(vol, upath, 1);
 +            rc = deletefile(vol, -1, upath, 1);
          }
      }
      if ( rc == AFP_OK ) {
-       curdir->offcnt--;
+         curdir->offcnt--;
          setvoltime(obj, vol );
      }
  
  /* ------------------------ */
  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;
 +
  
      *rbuflen = 0;
      ibuf += 2;
  
      /* 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) {
          }
          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
 +    if ((sdir_fd = open(".", O_RDONLY)) == -1)
 +        return AFPERR_MISC;
 +#endif
 +
      /* get the destination directory */
      if (NULL == ( ddir = dirlookup( vol, did )) ) {
 -        return afp_errno; /*  was AFPERR_PARAM */
 +        rc = afp_errno; /*  was AFPERR_PARAM */
 +        goto exit;
      }
      if (NULL == ( path = cname( vol, ddir, &ibuf ))) {
 -        return( AFPERR_NOOBJ );
 +        rc = AFPERR_NOOBJ;
 +        goto exit;
      }
      pdid = curdir->d_did;
      if ( *path->m_name != '\0' ) {
 -        return path_error(path, AFPERR_NOOBJ);
 +        rc = path_error(path, AFPERR_NOOBJ);
 +        goto exit;
      }
  
      /* one more place where we know about path type */
      if ((plen = copy_path_name(vol, newname, ibuf)) < 0) {
 -        return( AFPERR_PARAM );
 +        rc = AFPERR_PARAM;
 +        goto exit;
      }
  
      if (!plen) {
          strcpy(newname, oldname);
      }
  
 -    LOG(log_debug, logtype_afpd, "afp_move: {oldname:'%s', newname:'%s', isdir:%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, 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) {
 -            return AFPERR_PARAM;
 +            rc = AFPERR_PARAM;
 +            goto exit;
          }
          curdir->offcnt++;
          sdir->offcnt--;
          if (vol->v_flags & AFPVOL_DROPBOX) {
              /* FIXME did is not always the source id */
              if ((retvalue=matchfile2dirperms (upath, vol, did)) != AFP_OK) {
 -                return retvalue;
 +                rc = retvalue;
 +                goto exit;
              }
          }
          else
          setvoltime(obj, vol );
      }
  
 +exit:
 +#ifdef HAVE_RENAMEAT
 +    if (sdir_fd != -1)
 +        close(sdir_fd);
 +#endif
 +
      return( rc );
  }
  
@@@ -772,8 -717,8 +763,8 @@@ int veto_file(const char*veto_str, cons
   * 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;
          } else {
              if (veto_str[i] != path[j]) {
                  while ((veto_str[i] != '/')
-                         && (veto_str[i] != '\0'))
+                        && (veto_str[i] != '\0'))
                      i++;
                  j = 0;
                  continue;
diff --combined etc/afpd/globals.h
index e096044a2603d63bfcce5860aafd19606b66e306,e6989e39bada6eb6a779633ef92d08c73ecb06c2..31f7b4a8b720d91397e8d162114f3af7c69b699b
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: globals.h,v 1.33 2010-03-29 15:22:57 franklahm Exp $
 - * $Id: globals.h,v 1.31.2.2 2010-02-04 14:34:31 franklahm Exp $
++ * $Id: globals.h,v 1.33 2010/03/29 15:22:57 franklahm Exp $
   *
   * Copyright (c) 1990,1993 Regents of The University of Michigan.
   * All Rights Reserved.  See COPYRIGHT.
@@@ -56,21 -56,20 +56,21 @@@ 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;
      struct at_addr ddpaddr;
      char *uampath, *fqdn;
      char *pidfile;
 +    char *sigconffile;
      struct afp_volume_name defaultvol, systemvol, uservol;
      int  closevol;
  
      char *guest, *loginmesg, *keyfile, *passwdfile;
      char *uamlist;
      char *authprintdir;
 -    char *signature;
 +    unsigned char signature[16];
      char *k5service, *k5realm, *k5keytab;
      char *unixcodepage,*maccodepage;
      charset_t maccharset, unixcharset; 
@@@ -113,9 -112,6 +113,9 @@@ typedef struct _AFPObj 
  /* typedef for AFP functions handlers */
  typedef int (*AFPCmd)(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
  
 +/* afp_dsi.c */
 +extern AFPObj *AFPobj;
 +
  extern int            afp_version;
  extern int            afp_errno;
  extern unsigned char  nologin;
@@@ -143,6 -139,9 +143,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 */
diff --combined etc/afpd/ofork.c
index 49c014ccef9befec7be7f15068e5c7039e757139,687809e3e5ad7262c5d615d519adcead56dc2be2..a6359ad29329373e5db2fdb7b673cfbf3fee6241
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: ofork.c,v 1.32 2010-03-12 15:16:49 franklahm Exp $
 - * $Id: ofork.c,v 1.30.4.4 2010-02-09 14:56:30 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 +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 +47,7 @@@ static unsigned long hashfn(const struc
      while (*name) {
          i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
      }
- #endif    
+ #endif
      return key->inode & (OFORK_HASHSIZE - 1);
  }
  
@@@ -72,7 -74,7 +74,7 @@@ static void of_unhash(struct ofork *of
  #ifdef DEBUG1
  void of_pforkdesc( FILE *f)
  {
-     int       ofrefnum;
+     int ofrefnum;
  
      if (!oforks)
          return;
  
  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) );
          }
      }
      return( 0 );
  }
  
 -int of_rename(
 -    const struct vol *vol,
 -    struct ofork *s_of,
 -    struct dir *olddir, const char *oldpath _U_,
 -    struct dir *newdir, const char *newpath)
 +int of_rename(const struct vol *vol,
 +              struct ofork *s_of,
 +              struct dir *olddir, const char *oldpath _U_,
 +              struct dir *newdir, const char *newpath)
  {
      struct ofork *of, *next, *d_ofork;
      int done = 0;
  
      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;
      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)
          }
      }
      /* 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 ) {
  
      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,40 -282,31 +281,49 @@@ struct ofork *of_find(const u_int16_t o
  }
  
  /* -------------------------- */
- 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 = stat(path->u_name, &path->st)) < 0) {
 -        LOG(log_debug, logtype_afpd, "of_stat: {'%s/%s': %s}",
++
++    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;
 +      path->st_errno = errno;
-    return ret;
+     }
++
+     return ret;
  }
  
 -/* --------------------------
++
 +#ifdef HAVE_RENAMEAT
 +int of_fstatat(int dirfd, struct path *path)
 +{
 +    int ret;
 +
 +    path->st_errno = 0;
 +    path->st_valid = 1;
 +
 +    if ((ret = fstatat(dirfd, path->u_name, &path->st, AT_SYMLINK_NOFOLLOW)) < 0)
 +      path->st_errno = errno;
 +
 +   return ret;
 +}
 +#endif /* HAVE_RENAMEAT */
 +
 +/* -------------------------- 
     stat the current directory.
     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 */
      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 = stat(pathname, &path->st)))
 +    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 = stat(cfrombstring(path->d_dir->d_u_name), &path->st)) < 0)
 -            path->st_errno = errno;
++       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 -344,11 +362,11 @@@ struct ofork *of_findname(struct path *
  {
      struct ofork *of;
      struct file_key key;
-     
      if (!path->st_valid) {
-        of_stat(path);
+         of_stat(path);
      }
-       
      if (path->st_errno)
          return NULL;
  
      return NULL;
  }
  
 +/*!
 + * @brief Search for open fork by dirfd/name
 + *
 + * Function call of_fstatat with dirfd and path and uses dev and ino
 + * to search the open fork table.
 + *
 + * @param dirfd     (r) directory fd
 + * @param path      (rw) pointer to struct path
 + */
 +#ifdef HAVE_RENAMEAT
 +struct ofork *of_findnameat(int dirfd, struct path *path)
 +{
 +    struct ofork *of;
 +    struct file_key key;
 +    
 +    if ( ! path->st_valid) {
 +        of_fstatat(dirfd, path);
 +    }
 +      
 +    if (path->st_errno)
 +        return NULL;
 +
 +    key.dev = path->st.st_dev;
 +    key.inode = path->st.st_ino;
 +
 +    for (of = ofork_table[hashfn(&key)]; of; of = of->next) {
 +        if (key.dev == of->key.dev && key.inode == of->key.inode ) {
 +            return of;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +#endif
 +
  void of_dealloc( struct ofork *of)
  {
      if (!oforks)
  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;
              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 );
              }
          }
      }
      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;
      return adp;
  }
  
- /* ---------------------- 
+ /* ----------------------
     close all forks for a volume
  */
  void of_closevol(const struct vol *vol)
  {
-     int       refnum;
+     int refnum;
  
      if (!oforks)
          return;
diff --combined etc/afpd/volume.c
index f43d65c48b68b0fa28f5c6d446c5f5a61942964e,368dd50c0c8c355c2d987318533e957796765d01..5d61d69408cdc0513858c3d4ac7140b90ada8adf
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: volume.c,v 1.127 2010-04-16 18:28:45 franklahm Exp $
 - * $Id: volume.c,v 1.115.2.2 2010-02-09 14:56:30 franklahm Exp $
++ * $Id$
   *
   * Copyright (c) 1990,1993 Regents of The University of Michigan.
   * All Rights Reserved.  See COPYRIGHT.
@@@ -92,38 -92,45 +92,38 @@@ static void             free_extmap(voi
  #define VOLOPT_CASEFOLD   5  /* character case mangling */
  #define VOLOPT_FLAGS      6  /* various flags */
  #define VOLOPT_DBPATH     7  /* path to database */
 -#define VOLOPT_MAPCHARS   8  /* does mtou and utom mappings. syntax:
 -                                m and u can be double-byte hex
 -                                strings if necessary.
 -                                m=u -> map both ways
 -                                m>u -> map m to u
 -                                m<u -> map u to m
 -                                !u  -> make u illegal always
 -                                ~u  -> make u illegal only as the first
 -                                part of a double-byte character.
 -                             */
 +#define VOLOPT_LIMITSIZE  8  /* Limit the size of the volume */
 +/* Usable slot: 9 */
  #define VOLOPT_VETO          10  /* list of veto filespec */
  #define VOLOPT_PREEXEC       11  /* preexec command */
  #define VOLOPT_ROOTPREEXEC   12  /* root preexec command */
 -
  #define VOLOPT_POSTEXEC      13  /* postexec command */
  #define VOLOPT_ROOTPOSTEXEC  14  /* root postexec command */
 -
  #define VOLOPT_ENCODING      15  /* mac encoding (pre OSX)*/
  #define VOLOPT_MACCHARSET    16
  #define VOLOPT_CNIDSCHEME    17
  #define VOLOPT_ADOUBLE       18  /* adouble version */
 +
  #ifdef FORCE_UIDGID
  #warning UIDGID
  #include "uid.h"
  
 -#define VOLOPT_FORCEUID  19  /* force uid for username x */
 -#define VOLOPT_FORCEGID  20  /* force gid for group x */
 +#define VOLOPT_FORCEUID      19  /* force uid for username x */
 +#define VOLOPT_FORCEGID      20  /* force gid for group x */
  #endif /* FORCE_UIDGID */
  
 -#define VOLOPT_UMASK     21
 +#define VOLOPT_UMASK         21
  #define VOLOPT_ALLOWED_HOSTS 22
  #define VOLOPT_DENIED_HOSTS  23
 -#define VOLOPT_DPERM     24  /* dperm default directories perms */
 -#define VOLOPT_FPERM     25  /* fperm default files perms */
 -#define VOLOPT_DFLTPERM  26  /* perm */
 -#define VOLOPT_EA_VFS    27  /* Extended Attributes vfs indirection */
 +#define VOLOPT_DPERM         24  /* dperm default directories perms */
 +#define VOLOPT_FPERM         25  /* fperm default files perms */
 +#define VOLOPT_DFLTPERM      26  /* perm */
 +#define VOLOPT_EA_VFS        27  /* Extended Attributes vfs indirection */
 +#define VOLOPT_CNIDSERVER    28  /* CNID Server ip address*/
 +#define VOLOPT_CNIDPORT      30  /* CNID server tcp port */
  
 -#define VOLOPT_MAX       28  /* <== IMPORTANT !!!!!! */
 -#define VOLOPT_NUM       (VOLOPT_MAX + 1)
 +#define VOLOPT_MAX           31  /* <== IMPORTANT !!!!!! */
 +#define VOLOPT_NUM           (VOLOPT_MAX + 1)
  
  #define VOLPASSLEN  8
  #define VOLOPT_DEFAULT     ":DEFAULT:"
@@@ -459,22 -466,12 +459,26 @@@ static void volset(struct vol_option *o
                  options[VOLOPT_FLAGS].i_value &= ~AFPVOL_CACHE;
              else if (strcasecmp(p, "tm") == 0)
                  options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
 -            else if (strcasecmp(p, "eject") == 0)
 -                options[VOLOPT_FLAGS].i_value |= AFPVOL_EJECT | AFPVOL_RO;
--
++/* 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, ",");
          }
  
 +    } else if (optionok(tmp, "cnidserver:", val)) {
 +        setoption(options, save, VOLOPT_CNIDSERVER, val);
 +
 +        char *p = strrchr(val + 1, ':');
 +        if (p) {
 +            *p = 0;
 +            setoption(options, save, VOLOPT_CNIDPORT, p);
 +        }
 +
 +        LOG(log_debug, logtype_afpd, "CNID Server for volume '%s': %s:%s",
 +            volname, val + 1, p ? p + 1 : Cnid_port);
 +
      } else if (optionok(tmp, "dbpath:", val)) {
          setoption(options, save, VOLOPT_DBPATH, val);
  
          options[VOLOPT_FPERM].i_value = (int)strtol(val+1, NULL, 8);
      } else if (optionok(tmp, "perm:", val)) {
          options[VOLOPT_DFLTPERM].i_value = (int)strtol(val+1, NULL, 8);
 -    } else if (optionok(tmp, "mapchars:",val)) {
 -        setoption(options, save, VOLOPT_MAPCHARS, val);
 -
      } else if (optionok(tmp, "password:", val)) {
          setoption(options, save, VOLOPT_PASSWORD, val);
  
          else if (strcasecmp(val + 1, "none") == 0)
              options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_NONE;
  
 +    } else if (optionok(tmp, "volsizelimit:", val)) {
 +        options[VOLOPT_LIMITSIZE].i_value = (uint32_t)strtoul(val + 1, NULL, 10);
 +
      } else {
          /* ignore unknown options */
          LOG(log_debug, logtype_afpd, "ignoring unknown volume option: %s", tmp);
@@@ -722,12 -719,6 +726,12 @@@ static int creatvol(AFPObj *obj, struc
          if (options[VOLOPT_CNIDSCHEME].c_value)
              volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value);
  
 +        if (options[VOLOPT_CNIDSERVER].c_value)
 +            volume->v_cnidserver = strdup(options[VOLOPT_CNIDSERVER].c_value);
 +
 +        if (options[VOLOPT_CNIDPORT].c_value)
 +            volume->v_cnidport = strdup(options[VOLOPT_CNIDPORT].c_value);
 +
          if (options[VOLOPT_UMASK].i_value)
              volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value;
  
          else
              volume->v_adouble = AD_VERSION;
  
 +        if (options[VOLOPT_LIMITSIZE].i_value)
 +            volume->v_limitsize = options[VOLOPT_LIMITSIZE].i_value;
 +
          /* Mac to Unix conversion flags*/
          volume->v_mtou_flags = 0;
          if (!(volume->v_flags & AFPVOL_NOHEX))
@@@ -996,12 -984,12 +1000,12 @@@ static void setextmap(char *ext, char *
      }
  
      if ( *type == '\0' ) {
 -        memcpy(em->em_type, "????", sizeof( em->em_type ));
 +        memcpy(em->em_type, "\0\0\0\0", sizeof( em->em_type ));
      } else {
          memcpy(em->em_type, type, sizeof( em->em_type ));
      }
      if ( *creator == '\0' ) {
 -        memcpy(em->em_creator, "UNIX", sizeof( em->em_creator ));
 +        memcpy(em->em_creator, "\0\0\0\0", sizeof( em->em_creator ));
      } else {
          memcpy(em->em_creator, creator, sizeof( em->em_creator ));
      }
@@@ -1359,7 -1347,8 +1363,7 @@@ static int getvolspace(struct vol *vol
      }
  #endif
  
 -    if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal,
 -                                    bsize)) != AFP_OK ) {
 +    if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal, bsize)) != AFP_OK ) {
          return( rc );
      }
  
      vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
  
  getvolspace_done:
 +    if (vol->v_limitsize) {
 +        /* FIXME: Free could be limit minus (total minus used), */
 +        /* which will confuse the client less ? */
 +        *xbfree = min(*xbfree, (vol->v_limitsize * 1024 * 1024));
 +        *xbtotal = min(*xbtotal, (vol->v_limitsize * 1024 * 1024));
 +    }
 +
      *bfree = min( *xbfree, maxsize);
      *btotal = min( *xbtotal, maxsize);
      return( AFP_OK );
@@@ -1839,21 -1821,34 +1843,45 @@@ static int volume_openDB(struct vol *vo
          LOG(log_info, logtype_afpd, "Volume %s use CNID scheme %s.", volume->v_path, volume->v_cnidscheme);
      }
  
 -    if (volume->v_flags & AFPVOL_EJECT) {
 +    LOG(log_info, logtype_afpd, "%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,
 +                              flags,
 +                              volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
 +                              volume->v_cnidport ? volume->v_cnidport : Cnid_port);
  
-     if (!volume->v_cdb) {
 -    if (volume->v_dbpath)
 -        volume->v_cdb = cnid_open (volume->v_dbpath, volume->v_umask, volume->v_cnidscheme, flags);
 -    else
 -        volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, volume->v_cnidscheme, flags);
+     if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) {
+         /* The first attempt failed and it wasn't yet an attempt to open in-memory */
          flags |= CNID_FLAG_MEMORY;
          LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.", volume->v_path);
 -        volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags);
 +        volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
  #ifdef SERVERTEXT
          /* kill ourself with SIGUSR2 aka msg pending */
          if (volume->v_cdb) {
@@@ -2020,27 -2015,6 +2048,27 @@@ int afp_openvol(AFPObj *obj, char *ibuf
          return AFPERR_MISC;
      }
  
 +    /* Normalize volume path */
 +#ifdef REALPATH_TAKES_NULL
 +    if ((volume->v_path = realpath(path, NULL)) == NULL)
 +        return AFPERR_MISC;
 +#else
 +    if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL)
 +        return AFPERR_MISC;
 +    if (realpath(path, volume->v_path) == NULL) {
 +        free(volume->v_path);
 +        return AFPERR_MISC;
 +    }
 +    /* Safe some memory */
 +    char *tmp;
 +    if ((tmp = strdup(volume->v_path)) == NULL) {
 +        free(volume->v_path);
 +        return AFPERR_MISC;
 +    } 
 +    free(volume->v_path);
 +    volume->v_path = tmp;
 +#endif
 +
      if (volume_codepage(obj, volume) < 0) {
          ret = AFPERR_MISC;
          goto openvol_err;
          volume->max_filename = MACFILELEN;
      }
  
-     volume->v_dir = volume->v_root = NULL;
-     volume->v_hash = NULL;
      volume->v_flags |= AFPVOL_OPEN;
      volume->v_cdb = NULL;
  
      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;
      }
      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);
  
          if (!(volume->v_flags & AFPVOL_RO)) {
              handle_special_folders( volume );
 -            savevolinfo(volume, Cnid_srv, Cnid_port);
 +            savevolinfo(volume,
 +                        volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
 +                        volume->v_cnidport   ? volume->v_cnidport   : Cnid_port);
          }
  
          /*
          }
          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;
@@@ -2158,9 -2127,8 +2183,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;
@@@ -2201,7 -2169,7 +2225,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;
          }
      }
  
@@@ -2502,13 -2470,18 +2526,13 @@@ static int create_special_folder (cons
      if ( !ret && folder->hide) {
          /* Hide it */
          ad_init(&ad, vol->v_adouble, vol->v_ad_options);
 -        if (ad_open( p, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR|O_CREAT, 0666, &ad) < 0) {
 +        if (ad_open_metadata( p, ADFLAGS_DIR, O_CREAT, &ad) < 0) {
              free (p);
              free(q);
              return (-1);
          }
 -        if ((ad_get_HF_flags( &ad ) & O_CREAT) ) {
 -            if (ad_getentryoff(&ad, ADEID_NAME)) {
 -                ad_setentrylen( &ad, ADEID_NAME, strlen(folder->name));
 -                memcpy(ad_entry( &ad, ADEID_NAME ), folder->name,
 -                       ad_getentrylen( &ad, ADEID_NAME ));
 -            }
 -        }
 +        
 +        ad_setname(&ad, folder->name);
  
          ad_getattr(&ad, &attr);
          attr |= htons( ntohs( attr ) | ATTRBIT_INVISIBLE );
          }
  
          ad_flush( &ad );
 -        ad_close( &ad, ADFLAGS_HF );
 +        ad_close_metadata( &ad);
      }
      free(p);
      free(q);
diff --combined include/atalk/util.h
index 0d1f59ae8f1c502811bc4db2edd02ab4a9d2af18,3e1885c57a3de632af1fbc43f872a7e9c5bb1a24..080b13408a216f153d18120bf3f3a075f7d7c9f3
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: util.h,v 1.21 2010-02-28 22:29:16 didg Exp $
 - * $Id: util.h,v 1.18.2.1 2010-02-11 13:06:55 franklahm 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 */
  
+ /* LOG assert errors */
+ #ifndef NDEBUG
+ #define AFP_ASSERT(b) \
+     do {                                                                \
+         if (!(b)) {                                                     \
+             LOG(log_error, logtype_default, "PANIC, assert failed: %s", #b); \
+             abort();                                                    \
+         } \
+     } 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);
@@@ -121,6 -133,5 +133,6 @@@ extern int compare_ip(const struct sock
   *****************************************************************/
  
  extern const char *getcwdpath(void);
 +extern int lchdir(const char *dir);
  
  #endif  /* _ATALK_UTIL_H */
diff --combined include/atalk/volume.h
index f1b2084b0c9fb6ebf47e01103fc898d20f110741,c0516a41495f4bcc8c8cfc60f688c2079a605a7e..de1134f2ede26930a144a0aaf99784a4df75b1c8
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * $Id: volume.h,v 1.17 2010-04-10 08:24:54 franklahm Exp $
 - * $Id: volume.h,v 1.11.2.2 2010-02-09 14:56:30 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.
  #define AFPVOL_U8MNAMELEN   255 /* AFP3 sepc */
  #define AFPVOL_MACNAMELEN    27 /* AFP2 spec */
  
 +typedef u_int64_t VolSpace;
 +
  struct vol {
      struct vol      *v_next;
      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;
@@@ -45,7 -41,6 +43,7 @@@
      char            *(*ad_path)(const char *, int);
      struct _cnid_db *v_cdb;
      char            v_stamp[ADEDLEN_PRIVSYN];
 +    VolSpace        v_limitsize; /* Size limit, if any, in MiB */
      mode_t          v_umask;
      mode_t          v_perm;  /* default permission value OR with requested perm*/
      mode_t          v_dperm; /* default directories permission value OR with requested perm*/
@@@ -76,8 -71,6 +74,8 @@@
      char            *v_password;
      char            *v_cnidscheme;
      char            *v_dbpath;
 +    char            *v_cnidserver;
 +    char            *v_cnidport;
      int             v_hide;       /* new volume wait until old volume is closed */
      int             v_new;        /* volume deleted but there's a new one with the same name */
      int             v_deleted;    /* volume open but deleted in new config file */
  #endif /*__svr4__*/
  };
  
 -#ifdef NO_LARGE_VOL_SUPPORT
 -typedef u_int32_t VolSpace;
 -#else /* NO_LARGE_VOL_SUPPORT */
 -typedef u_int64_t VolSpace;
 -#endif /* NO_LARGE_VOL_SUPPORT */
 -
  #define AFPVOL_OPEN (1<<0)
  
  /* flags  for AFS and quota 0xxx0 */
  
  /*
    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 */
  #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_EJECT     (1 << 25)   /* Ejectable media eg CD -> in memory CNID db */
++#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 13d5a6ebe4e0b6f9e86de995337569b523aba8a7,b915c65baea448c86c5ef985df7e458d06fd858b..570da3d9342e4d9d20021b0b6776244e284fa470
@@@ -1,5 -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.16.2.1 2010-02-04 14:34:31 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.
@@@ -52,6 -52,9 +52,6 @@@ static void RQST_RESET(struct cnid_dbd_
  }
  
  /* ----------- */
 -extern char *Cnid_srv;
 -extern char *Cnid_port;
 -
  #define MAX_DELAY 40
  
  /* *MUST* be < afp tickle or it's never triggered (got EINTR first) */
@@@ -143,8 -146,6 +143,6 @@@ static int write_vec(int fd, struct iov
      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)
@@@ -182,10 -183,7 +180,10 @@@ static int init_tsock(CNID_private *db
      int len;
      struct iovec iov[2];
  
 -    if ((fd = tsock_getfd(Cnid_srv, Cnid_port)) < 0)
 +    LOG(log_debug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s", 
 +        db->db_dir, db->cnidserver, db->cnidport);
 +
 +    if ((fd = tsock_getfd(db->cnidserver, db->cnidport)) < 0)
          return -1;
  
      len = strlen(db->db_dir);
          return -1;
      }
  
 -    LOG(log_maxdebug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s",
 -        db->db_dir, Cnid_srv, Cnid_port);
 +    LOG(log_debug, logtype_cnid, "init_tsock: ok");
  
      return fd;
  }
@@@ -213,8 -212,6 +211,6 @@@ static int send_packet(CNID_private *db
      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",
          return -1;
      }
      
-     LOG(log_maxdebug, logtype_cnid, "send_packet: OK");
+     LOG(log_maxdebug, logtype_cnid, "send_packet: {done}");
      return 0;
  }
  
@@@ -299,8 -296,6 +295,6 @@@ static int dbd_rpc(CNID_private *db, st
      char *nametmp;
      size_t len;
  
-     LOG(log_maxdebug, logtype_cnid, "dbd_rpc: BEGIN");
      if (send_packet(db, rqst) < 0) {
          return -1;
      }
          return -1;
      }
  
-     LOG(log_maxdebug, logtype_cnid, "dbd_rpc: END");
+     LOG(log_maxdebug, logtype_cnid, "dbd_rpc: {done}");
  
      return 0;
  }
@@@ -339,8 -334,6 +333,6 @@@ static int transmit(CNID_private *db, s
      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
           */
              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;
              }
                      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:
@@@ -440,16 -433,16 +432,16 @@@ static struct _cnid_db *cnid_dbd_new(co
  }
  
  /* ---------------------- */
 -struct _cnid_db *cnid_dbd_open(const char *dir, mode_t mask _U_, u_int32_t flags _U_)
 +struct _cnid_db *cnid_dbd_open(struct cnid_open_args *args)
  {
      CNID_private *db = NULL;
      struct _cnid_db *cdb = NULL;
  
 -    if (!dir) {
 +    if (!args->dir) {
          return NULL;
      }
  
 -    if ((cdb = cnid_dbd_new(dir)) == NULL) {
 +    if ((cdb = cnid_dbd_new(args->dir)) == NULL) {
          LOG(log_error, logtype_cnid, "cnid_open: Unable to allocate memory for database");
          return NULL;
      }
  
      /* We keep a copy of the directory in the db structure so that we can
         transparently reconnect later. */
 -    strcpy(db->db_dir, dir);
 +    strcpy(db->db_dir, args->dir);
      db->magic = CNID_DB_MAGIC;
      db->fd = -1;
 +    db->cnidserver = strdup(args->cnidserver);
 +    db->cnidport = strdup(args->cnidport);
  
      LOG(log_debug, logtype_cnid, "cnid_dbd_open: Finished initializing cnid dbd module for volume '%s'", db->db_dir);