* scripts/amd.conf.5, doc/am-utils.texi (normalize_slashes
authorErez Zadok <ezk@cs.sunysb.edu>
Sun, 17 Apr 2005 03:05:54 +0000 (03:05 +0000)
committerErez Zadok <ezk@cs.sunysb.edu>
Sun, 17 Apr 2005 03:05:54 +0000 (03:05 +0000)
Parameter), scripts/amd.conf-sample: document new
normalize_slashes global configuration parameter.

* amd/opts.c (deslashify, normalize_slash): don't touch trailing
slashes, even if multiples of them, if user said
normalize_slashes=no in amd.conf.

* amd/conf.c (gopt_normalize_slashes): new function to record if
to normalize slashes or not.

* amd/amd.h (CFM_NORMALIZE_SLASHES): new flag to decide if to
normalize double-slashes or not ("yes" by default).

* amd/autil.c (am_mounted): pass TRUE when calling mf_mounted.
This is the parent mntfs which does the mf->mf_fo
(am_opts type), and we're passing TRUE here to tell mf_mounted to
actually free the am_opts.

* amd/autil.c (mf_mounted): Be careful when calling free_ops and
XFREE here.  Some pseudo file systems like nfsx call this
function, even though it would be called by the lower-level amd
file system functions.  nfsx needs to call this function because
of the other actions it takes.  So we pass a boolean from the
caller (yes, not so clean workaround) to determine if we should
free or not.  If we're not freeing (often because we're called
from a callback function), then just to be sure, we'll zero out
the am_opts structure and set the pointer to NULL.  The parent
mntfs node owns this memory and is going to free it with a call to
mf_mounted(mntfs,TRUE).

* amd/amd.h: pass flag to mf_mounted, to free or not to free the
am_opts.

* amd/amfs_nfsx.c (amfs_nfsx_cont): call mf_mounted with FALSE to
tell it not to free the am_opts, to avoid double free.

* include/am_defs.h: include limits.h if found.

* configure.in: check for limits.h.  Check for certain Linux
headers such as auto_fs.h after checking for limits.h, and include
the latter if it exists, because some Linux headers depend on
limits.h.  This prevents warnings during configure time.

* amd/amfs_toplvl.c (amfs_toplvl_mount): do NOT set retrans/timeo
values from default global UDP settings, because it can cause
unexpected timeouts in Amd on slow systems.  The default that each
OS provides for these toplvl NFS mounts should be OK, or else you
can use the map_options entry.

13 files changed:
ChangeLog
NEWS
amd/amd.h
amd/amfs_nfsx.c
amd/amfs_toplvl.c
amd/autil.c
amd/conf.c
amd/opts.c
configure.in
doc/am-utils.texi
include/am_defs.h
scripts/amd.conf-sample
scripts/amd.conf.5

index e65784b5a855180fe5d1a25c8dae96c768fc5b52..03a1c849f8ef22f23549f72a17853bdb8384ed83 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,57 @@
+2005-04-16  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * scripts/amd.conf.5, doc/am-utils.texi (normalize_slashes
+       Parameter), scripts/amd.conf-sample: document new
+       normalize_slashes global configuration parameter.
+
+       * amd/opts.c (deslashify, normalize_slash): don't touch trailing
+       slashes, even if multiples of them, if user said
+       normalize_slashes=no in amd.conf.
+
+       * amd/conf.c (gopt_normalize_slashes): new function to record if
+       to normalize slashes or not.
+
+       * amd/amd.h (CFM_NORMALIZE_SLASHES): new flag to decide if to
+       normalize double-slashes or not ("yes" by default).
+
+       * amd/autil.c (am_mounted): pass TRUE when calling mf_mounted.
+       This is the parent mntfs which does the mf->mf_fo
+       (am_opts type), and we're passing TRUE here to tell mf_mounted to
+       actually free the am_opts.
+
+       * amd/autil.c (mf_mounted): Be careful when calling free_ops and
+       XFREE here.  Some pseudo file systems like nfsx call this
+       function, even though it would be called by the lower-level amd
+       file system functions.  nfsx needs to call this function because
+       of the other actions it takes.  So we pass a boolean from the
+       caller (yes, not so clean workaround) to determine if we should
+       free or not.  If we're not freeing (often because we're called
+       from a callback function), then just to be sure, we'll zero out
+       the am_opts structure and set the pointer to NULL.  The parent
+       mntfs node owns this memory and is going to free it with a call to
+       mf_mounted(mntfs,TRUE).
+
+       * amd/amd.h: pass flag to mf_mounted, to free or not to free the
+       am_opts.
+
+       * amd/amfs_nfsx.c (amfs_nfsx_cont): call mf_mounted with FALSE to
+       tell it not to free the am_opts, to avoid double free.
+
+       * include/am_defs.h: include limits.h if found.
+
+       * configure.in: check for limits.h.  Check for certain Linux
+       headers such as auto_fs.h after checking for limits.h, and include
+       the latter if it exists, because some Linux headers depend on
+       limits.h.  This prevents warnings during configure time.
+
+2005-04-12  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * amd/amfs_toplvl.c (amfs_toplvl_mount): do NOT set retrans/timeo
+       values from default global UDP settings, because it can cause
+       unexpected timeouts in Amd on slow systems.  The default that each
+       OS provides for these toplvl NFS mounts should be OK, or else you
+       can use the map_options entry.
+
 2005-04-09  Daniel P. Ottavio  <dottavio@ic.sunysb.edu>
 
        * amd/nfs_subr.c (mp_to_fh): Replace xstrlcpy with memcpy because the
@@ -22,7 +76,7 @@
        * libamu/util.c (xstrlcpy): Return immediately if len is 0 to
        avoid unnecessary work.  Log an error and return if len is less
        than 0.
-       
+
 2005-04-07  Erez Zadok  <ezk@cs.sunysb.edu>
 
        * include/am_utils.h (XFREE): XFREE() should nullify the pointer
diff --git a/NEWS b/NEWS
index 5cd20be00068b7610803d7ee931b36483a69045b..22a21f5c9ad9634999c2154c37c0739888968a5f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,11 +1,17 @@
 *** Notes specific to am-utils version 6.1-rc2
 
+New amd.conf global parameter: normalize_slashes (default to "yes").  If set
+to "no," then Amd will not condense repeated slashes or remove trailing ones
+from strings representing pathnames.  This is sometimes useful with SMB
+mounts, which often require multiple slash characters in pathnames.
+
 Using a custom version of strlcpy instead of strncpy (but only where
 it makes sense), to minimize string overlow changes.  Audited all use
 of strncpy/strlcpy to ensure safety.
 
 - bugs fixed:
        * pawd handles all file systems
+       * fix double-free in type:=nfsx
 
 *** Notes specific to am-utils version 6.1-rc1
 
index 2a71a0ad21830fd2d78dfbd0df058ce602fba4e2..dc076427bd7061bfd24c04458203304694daa09d 100644 (file)
--- a/amd/amd.h
+++ b/amd/amd.h
@@ -37,7 +37,7 @@
  * SUCH DAMAGE.
  *
  *
- * $Id: amd.h,v 1.63 2005/03/08 06:05:33 ezk Exp $
+ * $Id: amd.h,v 1.64 2005/04/17 03:05:54 ezk Exp $
  *
  */
 
@@ -73,8 +73,9 @@
 #define CFM_AUTOFS_USE_LOFS            0x1000
 #define CFM_NFS_INSECURE_PORT          0x2000
 #define CFM_DOMAIN_STRIP               0x4000
+#define CFM_NORMALIZE_SLASHES          0x8000 /* normalize slashes? */
 /* defaults global flags: plock, tcpwrappers, and autofs/lofs */
-#define CFM_DEFAULT_FLAGS      (CFM_PROCESS_LOCK|CFM_USE_TCPWRAPPERS|CFM_AUTOFS_USE_LOFS|CFM_DOMAIN_STRIP)
+#define CFM_DEFAULT_FLAGS      (CFM_PROCESS_LOCK|CFM_USE_TCPWRAPPERS|CFM_AUTOFS_USE_LOFS|CFM_DOMAIN_STRIP|CFM_NORMALIZE_SLASHES)
 
 /*
  * macro definitions for automounter vfs/vnode operations.
@@ -547,7 +548,7 @@ extern void assign_error_mntfs(am_node *mp);
 extern am_node *next_nonerror_node(am_node *xp);
 extern void flush_srvr_nfs_cache(void);
 extern void am_mounted(am_node *);
-extern void mf_mounted(mntfs *mf);
+extern void mf_mounted(mntfs *mf, bool_t call_free_opts);
 extern void am_unmounted(am_node *);
 extern am_node *get_exported_ap(int index);
 extern am_node *get_first_exported_ap(int *index);
index b7338a77c79e8e6baafb38d900efadb4fb87423a..3cbca6576c7018e55bef0a06374a554ebb137e0c 100644 (file)
@@ -37,7 +37,7 @@
  * SUCH DAMAGE.
  *
  *
- * $Id: amfs_nfsx.c,v 1.22 2005/01/03 20:56:45 ezk Exp $
+ * $Id: amfs_nfsx.c,v 1.23 2005/04/17 03:05:54 ezk Exp $
  *
  */
 
@@ -339,7 +339,7 @@ amfs_nfsx_cont(int rc, int term, opaque_t arg)
     /*
      * The mount worked.
      */
-    mf_mounted(n->n_mnt);
+    mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */
     n->n_error = 0;
   }
 
@@ -386,7 +386,7 @@ amfs_nfsx_remount(am_node *am, mntfs *mf, int fg)
       break;
 
     if (m->mf_flags & MFF_MOUNTED) {
-      mf_mounted(m);
+      mf_mounted(m, FALSE);    /* FALSE => don't free the m->am_opts */
       n->n_error = glob_error = 0;
       continue;
     }
index 0fe7201777f90cf4f16b34c8391f203bb11eedbe..56a5b194c3dc6bd7ad478daa29d9bc7b5656dda7 100644 (file)
@@ -37,7 +37,7 @@
  * SUCH DAMAGE.
  *
  *
- * $Id: amfs_toplvl.c,v 1.37 2005/02/17 21:32:05 ezk Exp $
+ * $Id: amfs_toplvl.c,v 1.38 2005/04/17 03:05:54 ezk Exp $
  *
  */
 
@@ -178,13 +178,19 @@ amfs_toplvl_mount(am_node *mp, mntfs *mf)
     strcat(preopts, MNTTAB_OPT_IGNORE);
     strcat(preopts, ",");
 #endif /* MNTTAB_OPT_IGNORE */
+#ifdef WANT_TIMEO_AND_RETRANS_ON_TOPLVL
     sprintf(opts, "%s%s,%s=%d,%s=%d,%s=%d,%s,map=%s",
+#else /* WANT_TIMEO_AND_RETRANS_ON_TOPLVL */
+    sprintf(opts, "%s%s,%s=%d,%s,map=%s",
+#endif /* WANT_TIMEO_AND_RETRANS_ON_TOPLVL */
            preopts,
            MNTTAB_OPT_RW,
            MNTTAB_OPT_PORT, nfs_port,
+#ifdef WANT_TIMEO_AND_RETRANS_ON_TOPLVL
            /* note: TIMEO+RETRANS for toplvl are only "udp" currently */
            MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[AMU_TYPE_UDP],
            MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[AMU_TYPE_UDP],
+#endif /* WANT_TIMEO_AND_RETRANS_ON_TOPLVL */
            mf->mf_ops->fs_type, mf->mf_info);
 #ifdef MNTTAB_OPT_NOAC
     if (gopt.auto_attrcache == 0) {
index bbe163608aeda0d375ae52db84bf35371e4de60b..e57c6dadb43378bafb34415f2d14f3a953236b74 100644 (file)
@@ -37,7 +37,7 @@
  * SUCH DAMAGE.
  *
  *
- * $Id: autil.c,v 1.50 2005/03/05 07:09:17 ezk Exp $
+ * $Id: autil.c,v 1.51 2005/04/17 03:05:54 ezk Exp $
  *
  */
 
@@ -251,7 +251,7 @@ forcibly_timeout_mp(am_node *mp)
 
 
 void
-mf_mounted(mntfs *mf)
+mf_mounted(mntfs *mf, bool_t call_free_opts)
 {
   int quoted;
   int wasmounted = mf->mf_flags & MFF_MOUNTED;
@@ -271,8 +271,25 @@ mf_mounted(mntfs *mf)
     if (mf->mf_ops->mounted)
       mf->mf_ops->mounted(mf);
 
-    free_opts(mf->mf_fo);
-    XFREE(mf->mf_fo);
+    /*
+     * Be careful when calling free_ops and XFREE here.  Some pseudo file
+     * systems like nfsx call this function (mf_mounted), even though it
+     * would be called by the lower-level amd file system functions.  nfsx
+     * needs to call this function because of the other actions it takes.
+     * So we pass a boolean from the caller (yes, not so clean workaround)
+     * to determine if we should free or not.  If we're not freeing (often
+     * because we're called from a callback function), then just to be sure,
+     * we'll zero out the am_opts structure and set the pointer to NULL.
+     * The parent mntfs node owns this memory and is going to free it with a
+     * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code).
+     */
+    if (call_free_opts) {
+      free_opts(mf->mf_fo);    /* this free is needed to prevent leaks */
+      XFREE(mf->mf_fo);                /* (also this one) */
+    } else {
+      memset(mf->mf_fo, 0, sizeof(am_opts));
+      mf->mf_fo = NULL;
+    }
   }
 
   if (mf->mf_flags & MFF_RESTART) {
@@ -299,7 +316,12 @@ am_mounted(am_node *mp)
   int notimeout = 0;           /* assume normal timeouts initially */
   mntfs *mf = mp->am_mnt;
 
-  mf_mounted(mf);
+  /*
+   * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
+   * we're passing TRUE here to tell mf_mounted to actually free the
+   * am_opts.  See a related comment in mf_mounted().
+   */
+  mf_mounted(mf, TRUE);
 
 #ifdef HAVE_FS_AUTOFS
   if (mf->mf_flags & MFF_IS_AUTOFS)
index 6c152dd92b9718a945ed1f1ae80875de3a4c9fed..4a9faf0e414ccd64a0aeba0a16a963771f9ed691 100644 (file)
@@ -37,7 +37,7 @@
  * SUCH DAMAGE.
  *
  *
- * $Id: conf.c,v 1.31 2005/03/08 06:05:33 ezk Exp $
+ * $Id: conf.c,v 1.32 2005/04/17 03:05:54 ezk Exp $
  *
  */
 
@@ -118,6 +118,7 @@ static int gopt_nfs_retry_interval_tcp(const char *val);
 static int gopt_nfs_vers(const char *val);
 static int gopt_nis_domain(const char *val);
 static int gopt_normalize_hostnames(const char *val);
+static int gopt_normalize_slashes(const char *val);
 static int gopt_os(const char *val);
 static int gopt_osver(const char *val);
 static int gopt_plock(const char *val);
@@ -194,6 +195,7 @@ static struct _func_map glob_functable[] = {
   {"nfs_vers",                 gopt_nfs_vers},
   {"nis_domain",               gopt_nis_domain},
   {"normalize_hostnames",      gopt_normalize_hostnames},
+  {"normalize_slashes",                gopt_normalize_slashes},
   {"os",                       gopt_os},
   {"osver",                    gopt_osver},
   {"plock",                    gopt_plock},
@@ -898,6 +900,22 @@ gopt_normalize_hostnames(const char *val)
 }
 
 
+static int
+gopt_normalize_slashes(const char *val)
+{
+  if (STREQ(val, "yes")) {
+    gopt.flags |= CFM_NORMALIZE_SLASHES;
+    return 0;
+  } else if (STREQ(val, "no")) {
+    gopt.flags &= ~CFM_NORMALIZE_SLASHES;
+    return 0;
+  }
+
+  fprintf(stderr, "conf: unknown value to normalize_slashes \"%s\"\n", val);
+  return 1;                    /* unknown value */
+}
+
+
 static int
 gopt_os(const char *val)
 {
index 38f04ff2b2fbf4b6d7530ebfe4b809501e758350..9911f2e50df968cf0362e675086a85b42e047fcd 100644 (file)
@@ -37,7 +37,7 @@
  * SUCH DAMAGE.
  *
  *
- * $Id: opts.c,v 1.38 2005/04/09 18:15:35 ottavio Exp $
+ * $Id: opts.c,v 1.39 2005/04/17 03:05:54 ezk Exp $
  *
  */
 
@@ -962,9 +962,12 @@ free_op(opt_apply *p, int b)
 void
 normalize_slash(char *p)
 {
-  char *f = strchr(p, '/');
-  char *f0 = f;
+  char *f, *f0;
 
+  if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
+    return;
+
+  f0 = f = strchr(p, '/');
   if (f) {
     char *t = f;
     do {
@@ -1375,10 +1378,14 @@ expand_options(char *key)
  * Remove trailing /'s from a string
  * unless the string is a single / (Steven Glassman)
  * or unless it is two slashes // (Kevin D. Bond)
+ * or unless amd.conf says not to touch slashes.
  */
 void
 deslashify(char *s)
 {
+  if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
+    return;
+
   if (s && *s) {
     char *sl = s + strlen(s);
 
index feb6dc58d90c3376d5453c12bfb8b27e88eb550b..368816d02f152972f5a8815ad93742742d54ba0d 100644 (file)
@@ -53,7 +53,7 @@ AH_BOTTOM([
 dnl
 dnl AC_CONFIG_AUX_DIR(m4)
 AC_PREREQ(2.52)
-AC_REVISION($Revision: 1.83 $)
+AC_REVISION($Revision: 1.84 $)
 AC_COPYRIGHT([Copyright (c) 1997-2005 Erez Zadok])
 dnl find out system type
 AC_MSG_NOTICE(*** SYSTEM TYPES ***)
@@ -417,14 +417,11 @@ AC_CHECK_HEADERS(                 \
        gdbm/ndbm.h                     \
        hsfs/hsfs.h                     \
        isofs/cd9660/cd9660_mount.h     \
-       linux/auto_fs.h                 \
-       linux/auto_fs4.h                \
+       limits.h                        \
        linux/fs.h                      \
        linux/kdev_t.h                  \
        linux/list.h                    \
-       linux/loop.h                    \
        linux/nfs.h                     \
-       linux/nfs_mount.h               \
        linux/posix_types.h             \
        machine/endian.h                \
        msdosfs/msdosfsmount.h          \
@@ -531,6 +528,7 @@ dnl dirent.h                        \
        lber.h                          \
        ldap.h                          \
        libgen.h                        \
+       limits.h                        \
        malloc.h                        \
        memory.h                        \
        mntent.h                        \
@@ -561,7 +559,27 @@ dnl        dirent.h                        \
        varargs.h                       \
        unistd.h                        \
        )
-dnl ======================================================================
+dnl headers which depend on others, else you get an configure error
+AC_CHECK_HEADERS([                     \
+       linux/auto_fs.h                 \
+       linux/auto_fs4.h                \
+       linux/loop.h                    \
+       linux/nfs_mount.h               \
+], [], [],
+[
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif /* HAVE_LIMITS_H */
+#ifdef HAVE_LINUX_POSIX_TYPES_H
+# include <linux/posix_types.h>
+#endif /* HAVE_LINUX_POSIX_TYPES_H */
+/* next dev_t lines needed due to changes in kernel code */
+#undef dev_t
+#define dev_t unsigned short   /* compatible with Red Hat and SuSE */
+])
 
 dnl ======================================================================
 dnl db/ndbm/gdbm: This is serious autoconf-fu...
index c53b6c6f2171f69cb4c1470e7f50ed50fab67013..ebc1c0ff0f41928c1d2151c43f4ef5d4c55c0613 100644 (file)
@@ -38,7 +38,7 @@
 @c
 @c      %W% (Berkeley) %G%
 @c
-@c $Id: am-utils.texi,v 1.100 2005/03/08 06:05:33 ezk Exp $
+@c $Id: am-utils.texi,v 1.101 2005/04/17 03:05:54 ezk Exp $
 @c
 @setfilename am-utils.info
 
@@ -4302,6 +4302,7 @@ The following parameters are applicable to the @samp{[global]} section only.
 * nfs_vers Parameter::
 * nis_domain Parameter::
 * normalize_hostnames Parameter::
+* normalize_slashes Parameter::
 * os Parameter::
 * osver Parameter::
 * pid_file Parameter::
@@ -4760,7 +4761,7 @@ which to fetch the NIS maps.  The default is the system domain name.
 This option is ignored if NIS support is not available.
 
 @c ----------------------------------------------------------------
-@node normalize_hostnames Parameter, os Parameter, nis_domain Parameter, Global Parameters
+@node normalize_hostnames Parameter, normalize_slashes Parameter, nis_domain Parameter, Global Parameters
 @comment  node-name,  next,  previous,  up
 @subsection @t{normalize_hostnames} Parameter
 @cindex normalize_hostnames Parameter
@@ -4771,7 +4772,20 @@ relative to the host database before being used.  The effect is to
 translate aliases into ``official'' names.
 
 @c ----------------------------------------------------------------
-@node os Parameter, osver Parameter, normalize_hostnames Parameter, Global Parameters
+@node normalize_slashes Parameter, os Parameter, normalize_hostnames Parameter, Global Parameters
+@comment  node-name,  next,  previous,  up
+@subsection @t{normalize_slashes} Parameter
+@cindex normalize_slashes Parameter
+
+(type=boolean, default=@samp{yes}).  If @samp{yes} then amd will
+condense all multiple @code{/} (slash) characters into one and remove
+all trailing slashes.  If @samp{no}, then amd will not touch strings
+that may contain repeated or trailing slashes.  The latter is
+sometimes useful with SMB mounts, which often require multiple slash
+characters in pathnames.
+
+@c ----------------------------------------------------------------
+@node os Parameter, osver Parameter, normalize_slashes Parameter, Global Parameters
 @comment  node-name,  next,  previous,  up
 @subsection @t{os} Parameter
 @cindex os Parameter
index 7bc15d3f8d050100158be89575db0813009a400b..50bbea54cb72ba520df479df991d871e9b14adf9 100644 (file)
@@ -37,7 +37,7 @@
  * SUCH DAMAGE.
  *
  *
- * $Id: am_defs.h,v 1.55 2005/04/07 03:50:41 ezk Exp $
+ * $Id: am_defs.h,v 1.56 2005/04/17 03:05:54 ezk Exp $
  *
  */
 
@@ -165,6 +165,13 @@ struct sigevent;
 # include <sys/types.h>
 #endif /* HAVE_SYS_TYPES_H */
 
+/*
+ * Actions to take if HAVE_LIMITS_H is defined.
+ */
+#if HAVE_LIMITS_H_H
+# include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
 /*
  * Actions to take if HAVE_UNISTD_H is defined.
  */
index cf5a1568f7b3a02b6bb50b6251e343de845547a3..a5a60dda9db093ca03e5f61db585919ade5d4bba 100644 (file)
@@ -104,6 +104,8 @@ nfs_allow_insecure_port =   yes | no
 localhost_address =            foo.example.com | 192.168.1.2
 # number of seconds to timeout before map returns output
 exec_map_timeout =             10
+# normalize multiple/trailing slashes or not?
+normalize_slashes =            yes | no
 
 ##############################################################################
 # DEFINE AN AMD MOUNT POINT
index 18972a0d71ea7aa49023cb463d9b7aecf7e8d4fa..e0654f7f9806dd2f4231c81d0af1da1de62a02c3 100644 (file)
@@ -38,7 +38,7 @@
 .\"
 .\"    %W% (Berkeley) %G%
 .\"
-.\" $Id: amd.conf.5,v 1.36 2005/03/08 06:05:33 ezk Exp $
+.\" $Id: amd.conf.5,v 1.37 2005/04/17 03:05:54 ezk Exp $
 .\"
 .TH AMD.CONF 5 "7 August 1997"
 .SH NAME
@@ -503,6 +503,15 @@ option to amd.  If "yes," then the name refereed to by ${rhost} is
 normalized relative to the host database before being used.  The effect is
 to translate aliases into ``official'' names.
 
+.TP
+.BR normalize_slashes " (boolean, default=yes)"
+
+If "yes," then amd will condense all multiple ``/'' (slash) characters into
+one and remove all trailing slashes.  If "no," then amd will not touch
+strings that may contain repeated or trailing slashes.  The latter is
+sometimes useful with SMB mounts, which often require multiple slash
+characters in pathnames.
+
 .TP
 .BR os " (string, default to compiled in value)"
 Same as the