[27223] in Source-Commits

home help back first fref pref prev next nref lref last post

/svn/athena r26030 - in trunk/athena/lib/locker: . debian man src

daemon@ATHENA.MIT.EDU (Alexander Chernyakhovsky)
Sun Jun 2 00:57:35 2013

Date: Sun, 2 Jun 2013 00:57:28 -0400
From: Alexander Chernyakhovsky <achernya@MIT.EDU>
Message-Id: <201306020457.r524vS6T005736@drugstore.mit.edu>
To: source-commits@MIT.EDU
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Author: achernya
Date: 2013-06-02 00:57:27 -0400 (Sun, 02 Jun 2013)
New Revision: 26030

Added:
   trunk/athena/lib/locker/Makefile.am
   trunk/athena/lib/locker/debian/debathena-liblocker.install
   trunk/athena/lib/locker/m4/
   trunk/athena/lib/locker/man/Makefile.am
   trunk/athena/lib/locker/src/
   trunk/athena/lib/locker/src/Makefile.am
   trunk/athena/lib/locker/src/afs.c
   trunk/athena/lib/locker/src/attach.c
   trunk/athena/lib/locker/src/attachtab.c
   trunk/athena/lib/locker/src/conf.c
   trunk/athena/lib/locker/src/detach.c
   trunk/athena/lib/locker/src/liblocker.pc.in
   trunk/athena/lib/locker/src/locker.h
   trunk/athena/lib/locker/src/locker_private.h
   trunk/athena/lib/locker/src/misc.c
   trunk/athena/lib/locker/src/miscfs.c
   trunk/athena/lib/locker/src/mount.c
   trunk/athena/lib/locker/src/mountpoint.c
   trunk/athena/lib/locker/src/mul.c
   trunk/athena/lib/locker/src/nfs.c
   trunk/athena/lib/locker/src/util.c
   trunk/athena/lib/locker/src/zephyr.c
Removed:
   trunk/athena/lib/locker/Makefile.in
   trunk/athena/lib/locker/afs.c
   trunk/athena/lib/locker/attach.c
   trunk/athena/lib/locker/attachtab.c
   trunk/athena/lib/locker/conf.c
   trunk/athena/lib/locker/detach.c
   trunk/athena/lib/locker/locker.h
   trunk/athena/lib/locker/locker_private.h
   trunk/athena/lib/locker/misc.c
   trunk/athena/lib/locker/miscfs.c
   trunk/athena/lib/locker/mount.c
   trunk/athena/lib/locker/mountpoint.c
   trunk/athena/lib/locker/mul.c
   trunk/athena/lib/locker/nfs.c
   trunk/athena/lib/locker/util.c
   trunk/athena/lib/locker/zephyr.c
Modified:
   trunk/athena/lib/locker/configure.ac
   trunk/athena/lib/locker/debian/changelog
   trunk/athena/lib/locker/debian/control
   trunk/athena/lib/locker/debian/rules
Log:
In locker:
  * Convert to dh7
  * Update build system to use automake
  * Provide a pkg-config file
  * Switch from pioctl to k_pioctl
  * Don't build for squeeze and lucid, OpenAFS 1.4 is too old


Added: trunk/athena/lib/locker/Makefile.am
===================================================================
--- trunk/athena/lib/locker/Makefile.am	                        (rev 0)
+++ trunk/athena/lib/locker/Makefile.am	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,2 @@
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = src man

Modified: trunk/athena/lib/locker/configure.ac
===================================================================
--- trunk/athena/lib/locker/configure.ac	2013-06-02 04:23:37 UTC (rev 26029)
+++ trunk/athena/lib/locker/configure.ac	2013-06-02 04:57:27 UTC (rev 26030)
@@ -1,39 +1,53 @@
-dnl Process this file with autoconf to produce a configure script.
-AC_INIT([liblocker], [10.0.2])
-AC_CONFIG_SRCDIR([locker.h])
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
 
+AC_PREREQ([2.65])
+AC_INIT([liblocker], [10.1], [debathena@mit.edu])
+AM_INIT_AUTOMAKE([foreign -Wall])
+AM_MAINTAINER_MODE([enable])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_SRCDIR([src/afs.c])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
 AC_PROG_CC
-AC_PROG_INSTALL
-AC_PROG_RANLIB
-AC_PROG_MKDIR_P
+# automake 1.12 seems to require this, but automake 1.11 doesn't recognize it
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+LT_INIT
 
-AC_ARG_ENABLE([nfs],
-	[AS_HELP_STRING([--enable-nfs], [support for kNFS lockers (deprecated)])])
+# Checks for libraries.
+RRA_LIB_KRB5
+RRA_LIB_KAFS
 
-if test "$enable_nfs" = "yes"; then
-    AC_DEFINE([ENABLE_NFS],[1],[Support for kNFS lockers (deprecated).])
+# Checks for header files.
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stddef.h stdlib.h string.h sys/mount.h sys/param.h sys/socket.h unistd.h])
 
-    AC_PATH_PROG(MOUNT_CMD, mount, , ${PATH}:/sbin:/usr/sbin)
-    AC_PATH_PROG(UMOUNT_CMD, umount, , ${PATH}:/sbin:/usr/sbin)
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
 
-    AC_MSG_CHECKING(if rpcsvc/mount.h needs sys/fs/nfs.h)
-    AC_TRY_COMPILE([
-#include <rpc/rpc.h>
-#include <rpcsvc/mount.h>
-    ], , AC_MSG_RESULT(no), AC_TRY_COMPILE([
-#include <rpc/rpc.h>
-#include <sys/fs/nfs.h>
-#include <rpcsvc/mount.h>
-    ], , AC_MSG_RESULT(yes)
-    AC_DEFINE(NEED_SYS_FS_NFS_H),
-    AC_MSG_ERROR(couldn't include rpcsvc/mount.h)))
+# Checks for library functions.
+AC_FUNC_FORK
+AC_FUNC_GETGROUPS
+AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([alarm dup2 ftruncate getcwd gethostbyaddr gethostbyname inet_ntoa memmove memset mkdir regcomp rmdir strchr strcspn strdup strerror strncasecmp strrchr strtol strtoul uname])
 
-    ATHENA_KRB4_REQUIRED
-fi
+# More stuff
+PKG_CHECK_MODULES([HESIOD], [hesiod])
+AC_SUBST([HESIOD_CFLAGS])
+AC_SUBST([HESIOD_LIBS])
+PKG_CHECK_MODULES([ZEPHYR], [zephyr])
+AC_SUBST([ZEPHYR_CFLAGS])
+AC_SUBST([ZEPHYR_LIBS])
 
-ATHENA_KRB5_REQUIRED
-ATHENA_AFS_REQUIRED
-ATHENA_HESIOD_REQUIRED
-
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile
+                 src/Makefile
+		 src/liblocker.pc
+		 man/Makefile
+])
 AC_OUTPUT

Modified: trunk/athena/lib/locker/debian/changelog
===================================================================
--- trunk/athena/lib/locker/debian/changelog	2013-06-02 04:23:37 UTC (rev 26029)
+++ trunk/athena/lib/locker/debian/changelog	2013-06-02 04:57:27 UTC (rev 26030)
@@ -1,9 +1,17 @@
-debathena-liblocker (10.0.2-0debathena2) UNRELEASED; urgency=low
+debathena-liblocker (10.1-0debathena1) UNRELEASED; urgency=low
 
+  [ Jonathan Reed ]
   * Switch from control.in to control (Trac: #561)
   * Bump Standards-Version to 3.9.3
   * Bump compat level to 7
 
+  [ Alexander Chernyakhovsky ]
+  * Convert to dh7
+  * Update build system to use automake
+  * Provide a pkg-config file
+  * Switch from pioctl to k_pioctl
+  * Don't build for squeeze and lucid, OpenAFS 1.4 is too old
+
  -- Jonathan Reed <jdreed@mit.edu>  Wed, 03 Apr 2013 14:01:56 -0400
 
 debathena-liblocker (10.0.2-0debathena1) unstable; urgency=low

Modified: trunk/athena/lib/locker/debian/control
===================================================================
--- trunk/athena/lib/locker/debian/control	2013-06-02 04:23:37 UTC (rev 26029)
+++ trunk/athena/lib/locker/debian/control	2013-06-02 04:57:27 UTC (rev 26030)
@@ -2,8 +2,9 @@
 Section: debathena/libs
 Priority: extra
 Maintainer: Debathena Project <debathena@mit.edu>
-Build-Depends: cdbs, debhelper, dh-buildinfo, automake1.9, autoconf, autotools-dev, debathena-aclocal, libopenafs-dev, libkrb5-dev, libhesiod-dev, libzephyr-dev
+Build-Depends: debhelper (>= 7.0.50~), dh-autoreconf, debathena-aclocal (>= 1.1.3), libopenafs-dev, libkrb5-dev, libhesiod-dev, libzephyr-dev
 Standards-Version: 3.9.3
+X-Debathena-No-Build: squeeze lucid
 
 Package: debathena-liblocker
 Architecture: all

Added: trunk/athena/lib/locker/debian/debathena-liblocker.install
===================================================================
--- trunk/athena/lib/locker/debian/debathena-liblocker.install	                        (rev 0)
+++ trunk/athena/lib/locker/debian/debathena-liblocker.install	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1 @@
+debian/attach.conf etc/athena/

Modified: trunk/athena/lib/locker/debian/rules
===================================================================
--- trunk/athena/lib/locker/debian/rules	2013-06-02 04:23:37 UTC (rev 26029)
+++ trunk/athena/lib/locker/debian/rules	2013-06-02 04:57:27 UTC (rev 26030)
@@ -1,29 +1,6 @@
 #!/usr/bin/make -f
+%:
+	dh $@ --with autoreconf
 
-DEB_AUTO_UPDATE_AUTOCONF = 2.50
-DEB_AUTO_UPDATE_ACLOCAL = 1.9
-
-include /usr/share/cdbs/1/rules/debhelper.mk
-include /usr/share/cdbs/1/class/autotools.mk
-
-debian/stamp-autotools-files: aclocal.m4 config.guess config.sub install-sh missing
-aclocal.m4:
-	touch $@
-config.guess config.sub:
-	cp /usr/share/misc/$@ $@
-install-sh missing:
-	-automake-1.9 --foreign --add-missing
-
-DEB_CONFIGURE_EXTRA_FLAGS += --with-afs=/usr --disable-nfs --with-krb5 --with-hesiod
-DEB_DH_INSTALL_SOURCEDIR = $(DEB_DESTDIR)
-DEB_UPDATE_RCD_PARAMS = "start 65 S ."
-
-ifeq ($(DEB_BUILD_ARCH),amd64)
-    CFLAGS += -fPIC
-endif
-
-common-install-indep::
-	dh_install -pdebathena-liblocker debian/attach.conf etc/athena/
-
-clean::
-	rm -f configure aclocal.m4 install-sh missing config.guess config.sub
+override_dh_installinit:
+	dh_installinit -- start 65 S .

Added: trunk/athena/lib/locker/man/Makefile.am
===================================================================
--- trunk/athena/lib/locker/man/Makefile.am	                        (rev 0)
+++ trunk/athena/lib/locker/man/Makefile.am	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,23 @@
+dist_man3_MANS = \
+	locker_attach.3 \
+	locker_attach_attachent.3 \
+	locker_attach_explicit.3 \
+	locker_auth.3 \
+	locker_auth_to_cell.3 \
+	locker_auth_to_host.3 \
+	locker_check_host.3 \
+	locker_check_owner.3 \
+	locker_detach.3 \
+	locker_detach_attachent.3 \
+	locker_detach_explicit.3 \
+	locker_do_zsubs.3 \
+	locker_end.3 \
+	locker_free_attachent.3 \
+	locker_init.3 \
+	locker_iterate_attachtab.3 \
+	locker_read_attachent.3 \
+	locker_zsubs.3
+
+dist_man5_MANS = \
+	attach.conf.5 \
+	attachtab.5

Added: trunk/athena/lib/locker/src/Makefile.am
===================================================================
--- trunk/athena/lib/locker/src/Makefile.am	                        (rev 0)
+++ trunk/athena/lib/locker/src/Makefile.am	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,27 @@
+# Disable deprecated kNFS lockers
+AM_CFLAGS=-DMOUNT_CMD=\"/bin/false\" -DUMOUNT_CMD=\"/bin/false\"
+
+include_HEADERS = locker.h
+
+lib_LTLIBRARIES = liblocker.la
+liblocker_la_SOURCES = \
+	afs.c \
+	attach.c \
+	attachtab.c \
+	conf.c \
+	detach.c \
+	locker.h \
+	locker_private.h \
+	misc.c \
+	miscfs.c \
+	mount.c \
+	mountpoint.c \
+	mul.c \
+	nfs.c \
+	util.c \
+	zephyr.c
+
+liblocker_la_LIBADD = @KRB5_LIBS@ @HESIOD_LIBS@ @ZEPHYR_LIBS@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = liblocker.pc

Copied: trunk/athena/lib/locker/src/afs.c (from rev 25923, trunk/athena/lib/locker/afs.c)
===================================================================
--- trunk/athena/lib/locker/src/afs.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/afs.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,537 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements AFS lockers. */
+
+static const char rcsid[] = "$Id: afs.c,v 1.16 2006-08-08 21:50:09 ghudson Exp $";
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <afs/stds.h>
+#include <afs/param.h>
+#include <afs/auth.h>
+#include <afs/cellconfig.h>
+#include <afs/ptserver.h>
+#include <afs/ptuser.h>
+#include <afs/venus.h>
+#include <rx/rxkad.h>
+
+/* This is defined in <afs/volume.h>, but it doesn't seem possible to
+ * include that without dragging in most of the rest of the afs
+ * includes as dependencies.
+ */
+#define VNAMESIZE 32
+
+#include <com_err.h>
+#include <krb5.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+/* Cheesy test for determining AFS 3.5. */
+#ifndef AFSCONF_CLIENTNAME
+#define AFS35
+#endif
+
+#ifdef AFS35
+#include <afs/dirpath.h>
+#else
+#define AFSDIR_CLIENT_ETC_DIRPATH AFSCONF_CLIENTNAME
+#endif
+
+/*
+ * Why doesn't AFS provide this prototype?
+ */
+/*extern int k_pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32); */
+
+static int afs_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **at);
+static int afs_attach(locker_context context, locker_attachent *at,
+		      char *mountoptions);
+static int afs_detach(locker_context context, locker_attachent *at);
+static int afs_auth(locker_context context, locker_attachent *at,
+		    int mode, int op);
+static int afs_zsubs(locker_context context, locker_attachent *at);
+
+struct locker_ops locker__afs_ops = {
+  "AFS",
+  0,
+  afs_parse,
+  afs_attach,
+  afs_detach,
+  afs_auth,
+  afs_zsubs
+};
+
+static int afs_get_cred(krb5_context context, char *name, char *inst, char *realm,
+			krb5_creds **creds);
+static int afs_maybe_auth_to_cell(locker_context context, char *name,
+				  char *cell, int op, int force);
+
+static int afs_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp)
+{
+  locker_attachent *at;
+  char *p, *dup = NULL, *lasts = NULL;
+  int status;
+
+  at = locker__new_attachent(context, &locker__afs_ops);
+  if (!at)
+    return LOCKER_ENOMEM;
+
+  if (!name)
+    {
+      /* This is an explicit description. */
+
+      if (strncmp(desc, "/afs/", 5))
+	{
+	  locker__error(context, "%s: Path is not in AFS.\n", desc);
+	  status = LOCKER_EPARSE;
+	  goto cleanup;
+	}
+
+      at->name = strdup(desc);
+      at->hostdir = strdup(desc);
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	{
+	  p = strrchr(desc, '/') + 1;
+	  at->mountpoint = malloc(strlen(context->afs_mount_dir) +
+				  strlen(p) + 2);
+	  if (at->mountpoint)
+	    sprintf(at->mountpoint, "%s/%s", context->afs_mount_dir, p);
+	}
+      if (!at->name || !at->hostdir || !at->mountpoint)
+	goto mem_error;
+
+      at->mode = LOCKER_AUTH_READWRITE;
+    }
+  else
+    {
+      /* A Hesiod AFS description looks like:
+       * AFS /afs/dev.mit.edu/source/src-current w /mit/source
+       */
+
+      at->name = strdup(name);
+      if (!at->name)
+	goto mem_error;
+
+      dup = strdup(desc);
+      if (!dup)
+	goto mem_error;
+
+      /* Skip "AFS". */
+      if (!strtok_r(dup, " ", &lasts))
+	goto parse_error;
+
+      /* Hostdir */
+      at->hostdir = strtok_r(NULL, " ", &lasts);
+      if (!at->hostdir)
+	goto parse_error;
+      if (strncmp(at->hostdir, "/afs/", 5))
+	{
+	  locker__error(context, "%s: Path \"%s\" is not in AFS.\n", name,
+			at->hostdir);
+	  status = LOCKER_EPARSE;
+	  goto cleanup;
+	}
+      at->hostdir = strdup(at->hostdir);
+      if (!at->hostdir)
+	goto mem_error;
+
+      /* Auth mode */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p || *(p + 1))
+	goto parse_error;
+
+      switch (*p)
+	{
+	case 'r':
+	  at->mode = LOCKER_AUTH_READONLY;
+	  break;
+	case 'w':
+	  at->mode = LOCKER_AUTH_READWRITE;
+	  break;
+	case 'n':
+	  at->mode = LOCKER_AUTH_NONE;
+	  break;
+	default:
+	  locker__error(context, "%s: Unrecognized auth mode '%c' in "
+			"description:\n%s\n", name, *p, desc);
+	  status = LOCKER_EPARSE;
+	  goto cleanup;
+	}
+
+      /* Mountpoint */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p)
+	goto parse_error;
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	at->mountpoint = strdup(p);
+      if (!at->mountpoint)
+	goto mem_error;
+
+      free(dup);
+      dup = NULL;
+    }
+
+  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_MOST,
+				     &(at->mountpoint), &(at->buildfrom));
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+
+  *atp = at;
+  return LOCKER_SUCCESS;
+
+mem_error:
+  locker__error(context, "Out of memory parsing locker description.\n");
+  status = LOCKER_ENOMEM;
+  goto cleanup;
+
+parse_error:
+  locker__error(context, "Could not parse locker description "
+		"\"%s\".\n", desc);
+  status = LOCKER_EPARSE;
+
+cleanup:
+  free(dup);
+  locker_free_attachent(context, at);
+  return status;
+}
+
+static int afs_attach(locker_context context, locker_attachent *at,
+		      char *mountoptions)
+{
+  struct stat st1, st2;
+  struct ViceIoctl vio;
+  afs_int32 hosts[8]; /* AFS docs say VIOCWHEREIS won't return more than 8. */
+  uid_t uid = geteuid();
+  int status;
+
+  /* Make sure user can read the destination, and it's a directory. */
+  if (uid != context->user)
+    seteuid(context->user);
+  status = lstat(at->hostdir, &st1);
+  if (uid != context->user)
+    seteuid(uid);
+
+  if (status == -1)
+    {
+     if (errno == ETIMEDOUT)
+	{
+	  locker__error(context, "%s: Connection timed out while trying to "
+			"attach locker.\nThis probably indicates a temporary "
+			"problem with the file server containing\n"
+			"this locker. Try again later.\n", at->name);
+	}
+      else
+	{
+	  locker__error(context, "%s: Could not attach locker:\n%s for %s\n",
+			at->name, strerror(errno), at->hostdir);
+	}
+      return LOCKER_EATTACH;
+    }
+  if (!S_ISDIR(st1.st_mode) && !S_ISLNK(st1.st_mode))
+    {
+      locker__error(context, "%s: Could not attach locker:\n"
+		    "%s is not a directory.\n", at->name, at->hostdir);
+      return LOCKER_EATTACH;
+    }
+
+  /* Make sure nothing is already mounted on our mountpoint. */
+  status = stat(at->mountpoint, &st2);
+  if (!status)
+    {
+      /* Assume the automounter took care of it */
+    }
+  else
+    {
+    status = symlink(at->hostdir, at->mountpoint);
+    if (status < 0)
+      {
+        locker__error(context, "%s: Could not attach locker:\n%s while "
+		    "symlinking %s to %s\n", at->name, strerror(errno),
+		    at->hostdir, at->mountpoint);
+        return LOCKER_EATTACH;
+      }
+    }
+
+  /* Find host that the locker is on, and update the attachent. */
+  memset(hosts, 0, sizeof(hosts));
+  vio.in_size = 0;
+  vio.out = (caddr_t)hosts;
+  vio.out_size = sizeof(hosts);
+  if (k_pioctl(at->hostdir, VIOCWHEREIS, &vio, 1) == 0)
+    {
+      /* Only record the hostaddr if the locker is on a single host.
+       * (We assume that if it's on multiple hosts, it can't fail,
+       * so we don't need to know what those hosts are.)
+       */
+      if (!hosts[1])
+	at->hostaddr.s_addr = hosts[0];
+    }
+
+  return LOCKER_SUCCESS;
+}
+
+static int afs_detach(locker_context context, locker_attachent *at)
+{
+  int status;
+
+  /* Let the automounter manage the symlink */
+  /* status = unlink(at->mountpoint);
+  if (status < 0)
+    {
+      if (errno == ENOENT)
+	{
+	  locker__error(context, "%s: Locker is not attached.\n", at->name);
+	  return LOCKER_ENOTATTACHED;
+	}
+      else
+	{
+	  locker__error(context, "%s: Could not detach locker:\n%s while "
+			"trying to unlink %s.\n", at->name, strerror(errno),
+			at->mountpoint);
+	  return LOCKER_EDETACH;
+	}
+    }
+  */
+  return LOCKER_SUCCESS;
+}
+
+static int afs_auth(locker_context context, locker_attachent *at,
+		    int mode, int op)
+{
+  char *cell, *p;
+  int status;
+
+  if (op != LOCKER_AUTH_AUTHENTICATE)
+    return LOCKER_SUCCESS;
+
+  /* We know (from afs_parse) that at->hostdir starts with "/afs/". */
+  cell = at->hostdir + 5;
+  /* Skip initial "." in the cell name (if this is a path to a rw volume). */
+  if (*cell == '.')
+    cell++;
+
+  p = strchr(cell, '/');
+  if (p)
+    *p = '\0';
+  status = afs_maybe_auth_to_cell(context, at->name, cell, op, 0);
+  if (p)
+    *p = '/';
+  return status;
+}
+
+int locker_auth_to_cell(locker_context context, char *name, char *cell, int op)
+{
+  return afs_maybe_auth_to_cell(context, name, cell, op, 1);
+}
+
+static int afs_maybe_auth_to_cell(locker_context context, char *name,
+				  char *cell, int op, int force)
+{
+  uid_t uid = geteuid(), ruid = getuid();
+  pid_t pid;
+  int stat_loc;
+
+  if (op != LOCKER_AUTH_AUTHENTICATE)
+    return LOCKER_SUCCESS;
+
+  pid = fork();
+  if (pid == -1)
+    {
+      locker__error(context, "%s: Unable to fork: %s\n",
+		    name, error_message(errno));
+      return LOCKER_EAUTH;
+    }
+  else if (pid == 0)
+    {
+      if (uid != ruid)
+	seteuid(ruid);
+
+      close(0);
+      close(1);
+      close(2);
+      open("/dev/null", O_RDWR);
+      dup2(0, 1);
+      dup2(0, 2);
+
+      if (-1 == execlp("aklog", "aklog", "-c", cell, NULL))
+	exit(errno);
+    }
+  else
+    {
+      waitpid(pid, &stat_loc, 0);
+
+      if (0 != WEXITSTATUS(stat_loc))
+	{
+	  locker__error(context, "%s: Error acquiring tokens: %s\n",
+			name, error_message(WEXITSTATUS(stat_loc)));
+	  return LOCKER_EAUTH;
+	}
+    }
+
+  return LOCKER_SUCCESS;
+}
+
+static int afs_get_cred(krb5_context context, char *name, char *inst, char *realm,
+			krb5_creds **creds)
+{
+  krb5_creds increds;
+  krb5_principal client_principal = NULL;
+  krb5_ccache krb425_ccache = NULL;
+  krb5_error_code retval = 0;
+
+  memset(&increds, 0, sizeof(increds));
+  /* ANL - instance may be ptr to a null string. Pass null then */
+  retval = krb5_build_principal(context, &increds.server, strlen(realm),
+				realm, name,
+				(inst && strlen(inst)) ? inst : NULL,
+				NULL);
+  if (retval)
+    goto fail;
+
+  retval = krb5_cc_default(context, &krb425_ccache);
+  if (retval)
+    goto fail;
+
+  retval = krb5_cc_get_principal(context, krb425_ccache, &client_principal);
+  if (retval)
+    goto fail;
+
+  increds.client = client_principal;
+  increds.times.endtime = 0;
+  /* Ask for DES since that is what V4 understands */
+  increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
+
+  retval = krb5_get_credentials(context, 0, krb425_ccache, &increds, creds);
+
+fail:
+  krb5_free_cred_contents(context, &increds);
+  if (krb425_ccache)
+    krb5_cc_close(context, krb425_ccache);
+  return(retval);
+}
+
+static int afs_zsubs(locker_context context, locker_attachent *at)
+{
+  struct ViceIoctl vio;
+  char *path, *last_component, *p, *subs[3];
+  char cell[MAXCELLCHARS + 1], vol[VNAMESIZE + 1];
+  char cellvol[MAXCELLCHARS + VNAMESIZE + 2];
+  afs_int32 hosts[8];
+  int status = 0, pstatus;
+  struct hostent *h;
+
+  subs[0] = cell;
+  subs[1] = cellvol;
+
+  path = strdup(at->hostdir);
+  if (!path)
+    {
+      locker__error(context, "Out of memory getting zephyr subscriptions.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  /* Walk down the path. At each level, add subscriptions for the cell,
+   * host, and volume where that path component lives.
+   */
+  p = path;
+  do
+    {
+      /* Move trailing NUL over one pathname component. */
+      *p = '/';
+      p = strchr(p + 1, '/');
+      if (p)
+	*p = '\0';
+
+      /* Get cell */
+      vio.in_size = 0;
+      vio.out = cell;
+      vio.out_size = sizeof(cell);
+      if (k_pioctl(path, VIOC_FILE_CELL_NAME, &vio, 1) != 0)
+	continue;
+
+      /* Get mountpoint name and generate cell:mountpoint. */
+      last_component = strrchr(path, '/');
+      if (last_component)
+	{
+	  *last_component++ = '\0';
+	  vio.in = last_component;
+	}
+      else
+	vio.in = "/";
+      vio.in_size = strlen(vio.in) + 1;
+      vio.out = vol;
+      vio.out_size = sizeof(vol);
+      pstatus = k_pioctl(path, VIOC_AFS_STAT_MT_PT, &vio, 1);
+      if (last_component)
+	*(last_component - 1) = '/';
+
+      if (pstatus != 0)
+	continue;
+
+      /* Get cell:volumname into cellvol, ignoring initial '#' or '%'. */
+      if (strchr(vol, ':'))
+	strcpy(cellvol, vol + 1);
+      else
+	sprintf(cellvol, "%s:%s", cell, vol + 1);
+
+      /* If there's only one site for this volume, add the hostname
+       * of the server to the subs list.
+       */
+      memset(hosts, 0, 2 * sizeof(*hosts));
+      vio.out = (caddr_t)hosts;
+      vio.out_size = sizeof(hosts);
+      if (k_pioctl(path, VIOCWHEREIS, &vio, 1) != 0)
+	continue;
+      if (!hosts[1])
+	{
+	  h = gethostbyaddr((char *)&hosts[0], 4, AF_INET);
+	  if (!h)
+	    continue;
+	  subs[2] = h->h_name;
+	  status = locker__add_zsubs(context, subs, 3);
+	}
+      else
+	status = locker__add_zsubs(context, subs, 2);
+    }
+  while (status == LOCKER_SUCCESS && strlen(path) != strlen(at->hostdir));
+
+  free(path);
+  return status;
+}
+
+/* librxkad depends on this symbol in Transarc's des library, which we
+ * can't link with because of conflicts with our krb4 library. It never
+ * gets called though.
+ */
+void des_pcbc_init(void)
+{
+  abort();
+}

Copied: trunk/athena/lib/locker/src/attach.c (from rev 25923, trunk/athena/lib/locker/attach.c)
===================================================================
--- trunk/athena/lib/locker/src/attach.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/attach.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,517 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements attaching lockers. */
+
+static const char rcsid[] = "$Id: attach.c,v 1.13 2006-07-25 23:29:09 ghudson Exp $";
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/param.h>
+
+#include <hesiod.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+static int attach_attachent(locker_context context, locker_attachent *at,
+			    int authmode, int options, char *mountoptions);
+static int try_local_copy(locker_context context, locker_attachent *at);
+static int check_mountoptions(locker_context context, locker_attachent *at,
+			      int authmode, int options, char **mountoptions);
+static int add_owner(locker_context context, locker_attachent *at,
+		     uid_t user);
+
+int locker_attach(locker_context context, char *filesystem, char *mountpoint,
+		  int authmode, int options, char *mountoptions,
+		  locker_attachent **atp)
+{
+  int status, astatus = LOCKER_EATTACH, order;
+  locker_attachent *at;
+
+  if (!context->trusted)
+    {
+      if (mountpoint && !context->exp_mountpoint)
+	{
+	  locker__error(context, "%s: You are not allowed to specify "
+			"an explicit mountpoint.\n", filesystem);
+	  return LOCKER_EPERM;
+	}
+    }
+
+  /* Try each Hesiod entry for the named filesystem until we run out
+   * or succeed in attaching one.
+   */
+  for (order = 1; ; order++)
+    {
+      status = locker__lookup_attachent(context, filesystem,
+					mountpoint, order, &at);
+      if (LOCKER_LOOKUP_FAILURE(status))
+	break;
+      if (status)
+	continue;
+
+      astatus = status = locker_attach_attachent(context, at, authmode,
+						 options, mountoptions);
+      if (LOCKER_ATTACH_SUCCESS(status))
+	{
+	  if (atp)
+	    *atp = at;
+	  else
+	    locker_free_attachent(context, at);
+	  return status;
+	}
+
+      locker_free_attachent(context, at);
+    }
+
+  return status == LOCKER_ENOENT ? astatus : status;
+}
+
+int locker_attach_explicit(locker_context context, char *type,
+			   char *desc, char *mountpoint, int authmode,
+			   int options, char *mountoptions,
+			   locker_attachent **atp)
+{
+  int status;
+  locker_attachent *at;
+
+  if (!context->trusted)
+    {
+      if (!context->exp_desc)
+	{
+	  locker__error(context, "%s: You are not allowed to specify "
+			"an explicit locker description.\n", desc);
+	  return LOCKER_EPERM;
+	}
+
+      if (mountpoint && !context->exp_mountpoint)
+	{
+	  locker__error(context, "%s: You are not allowed to specify "
+			"an explicit mountpoint.\n", desc);
+	  return LOCKER_EPERM;
+	}
+    }
+
+  status = locker__lookup_attachent_explicit(context, type, desc,
+					     mountpoint, 1, &at);
+  if (status)
+    return status;
+
+  status = locker_attach_attachent(context, at, authmode,
+				   options, mountoptions);
+
+  if (LOCKER_ATTACH_SUCCESS(status) && atp)
+    *atp = at;
+  else
+    locker_free_attachent(context, at);
+
+  return status;
+}
+
+int locker_attach_attachent(locker_context context, locker_attachent *at,
+			    int authmode, int options, char *mountoptions)
+{
+  int status = LOCKER_SUCCESS;
+  locker_attachent *ai;
+
+  if (!context->trusted)
+    {
+      if (options & LOCKER_ATTACH_OPT_OVERRIDE)
+	{
+	  locker__error(context, "%s: You are not authorized to use the "
+			"override option.\n", at->name);
+	  return LOCKER_EPERM;
+	}
+      if (options & LOCKER_ATTACH_OPT_LOCK)
+	{
+	  locker__error(context, "%s: You are not authorized to use the "
+			"lock option.\n", at->name);
+	  return LOCKER_EPERM;
+	}
+      if (options & LOCKER_ATTACH_OPT_ALLOW_SETUID)
+	{
+	  locker__error(context, "%s: You are not authorized to use the "
+			"setuid option.\n", at->name);
+	  return LOCKER_EPERM;
+	}
+    }
+
+  /* If this is a MUL locker, attach sublockers first. */
+  for (ai = at->next; ai && status == LOCKER_SUCCESS; ai = ai->next)
+    status = attach_attachent(context, ai, authmode, options, mountoptions);
+
+  if (!LOCKER_ATTACH_SUCCESS(status))
+    {
+      locker__error(context, "%s: MUL attach failed.\n", at->name);
+      return status;
+    }
+
+  return attach_attachent(context, at, authmode, options, mountoptions);
+}
+
+/* This is the routine that does all of the work of attaching a single
+ * filesystem.
+ */
+static int attach_attachent(locker_context context, locker_attachent *at,
+			    int authmode, int options, char *mountoptions)
+{
+  int status;
+  char *origmountoptions = mountoptions;
+
+  if (!(options & LOCKER_ATTACH_OPT_OVERRIDE))
+    {
+      /* Make sure this locker is allowed. */
+      if (!locker__fs_ok(context, context->allow, at->fs, at->name))
+	{
+	  locker__error(context, "%s: You are not allowed to attach this "
+			"locker.\n", at->name);
+	  return LOCKER_EPERM;
+	}
+
+      /* Make sure this mountpoint is allowed. */
+      if (strcmp(at->fs->name, "MUL") &&
+	  !locker__fs_ok(context, context->mountpoint, at->fs, at->mountpoint))
+	{
+	  locker__error(context, "%s: You are not allowed to attach a "
+			"locker on %s\n", at->name, at->mountpoint);
+	  return LOCKER_EPERM;
+	}
+    }
+
+  /* Authenticate. */
+  if (authmode == LOCKER_AUTH_DEFAULT)
+    authmode = at->mode;
+  if ((authmode != LOCKER_AUTH_NONE) &&
+      (!at->attached || (options & LOCKER_ATTACH_OPT_REAUTH)))
+    {
+      status = at->fs->auth(context, at, authmode, LOCKER_AUTH_AUTHENTICATE);
+      if (status != LOCKER_SUCCESS && authmode != LOCKER_AUTH_MAYBE_READWRITE)
+	return status;
+    }
+
+  if (!at->attached)
+    {
+      /* Check and update the mountoptions. */
+      if (locker__fs_ok(context, context->setuid, at->fs, at->name))
+	{
+	  options |= LOCKER_ATTACH_OPT_ALLOW_SETUID;
+	  at->flags &= ~LOCKER_FLAG_NOSUID;
+	}
+      status = check_mountoptions(context, at, authmode, options,
+				  &mountoptions);
+      if (status != LOCKER_SUCCESS)
+	return status;
+
+      /* Build the mountpoint if all of the directories don't exist. */
+      status = locker__build_mountpoint(context, at);
+      if (status != LOCKER_SUCCESS)
+	{
+	  free(mountoptions);
+	  return status;
+	}
+
+      /* Attach the locker.  Look for a local copy first. */
+      if (!(options & LOCKER_ATTACH_OPT_MASTER) && try_local_copy(context, at))
+	status = LOCKER_SUCCESS;
+      else
+	status = at->fs->attach(context, at, mountoptions);
+      free(mountoptions);
+      if (status == LOCKER_EALREADY)
+	{
+	  if (context->keep_mount)
+	    at->flags |= LOCKER_FLAG_KEEP;
+	}
+      else if (status != LOCKER_SUCCESS)
+	{
+	  locker__remove_mountpoint(context, at);
+	  return status;
+	} 
+
+      /* Release the lock on the directory we have just created. */
+      locker__put_mountpoint(context, at);
+    }
+  else
+      status = LOCKER_EALREADY;
+
+  if (origmountoptions && status == LOCKER_EALREADY)
+    {
+      locker__error(context, "Warning: %s is already attached. "
+		    "Ignoring mountoptions '%s'\n", at->name,
+		    origmountoptions);
+    }
+
+  /* Update attachent on disk. */
+  at->attached = 1;
+  add_owner(context, at, context->user);
+  if (options & LOCKER_ATTACH_OPT_LOCK)
+    at->flags |= LOCKER_FLAG_LOCKED;
+  if (!(options & LOCKER_ATTACH_OPT_ALLOW_SETUID))
+    at->flags |= LOCKER_FLAG_NOSUID;
+  locker__update_attachent(context, at);
+
+  /* Record zephyr subscriptions. */
+  if (options & LOCKER_ATTACH_OPT_ZEPHYR)
+    at->fs->zsubs(context, at);
+
+  return status;
+}
+
+/* Look for a symlink at {context->local_dir}/{at->name}.  If it
+ * exists, it should point to a local copy of the locker which has
+ * been created with athlsync(8) via /etc/athena/local-lockers.  Copy
+ * this symlink instead of actually attaching the locker.
+ */
+static int try_local_copy(locker_context context, locker_attachent *at)
+{
+  char *path, linkbuf[MAXPATHLEN + 1];
+  int len;
+
+  /* Read the symlink, if it exists. */
+  path = malloc(strlen(context->local_dir) + 1 + strlen(at->name) + 1);
+  if (path == NULL)
+    return 0;
+  sprintf(path, "%s/%s", context->local_dir, at->name);
+  len = readlink(path, linkbuf, sizeof(linkbuf) - 1);
+  free(path);
+  if (len == -1)
+      return 0;
+  linkbuf[len] = '\0';
+
+  path = strdup(linkbuf);
+  if (path == NULL)
+    return 0;
+  if (symlink(path, at->mountpoint) == -1)
+    {
+      free(path);
+      return 0;
+    }
+  free(at->hostdir);
+  at->hostdir = path;
+  at->fs = locker__get_fstype(context, "LOC");
+  return 1;
+}
+
+/* This is a helper function used by check_mountoptions to determine
+ * if two strings affect the same option (either identically or
+ * oppositely). o1 and o2 are the option names, l1 and l2 are their
+ * lengths.
+ */
+static int same_option(char *o1, int l1, char *o2, int l2)
+{
+  int cmp;
+
+  /* If one is an explicit negation of the other, they're "the same". */
+  if ((l1 == l2 + 2) && !strncmp(o1, "no", 2) && !strncmp(o1 + 2, o2, l2))
+    return 1;
+  else if ((l2 == l1 + 2) && !strncmp(o2, "no", 2) && !strncmp(o1, o2 + 2, l1))
+    return 1;
+
+  cmp = strncmp(o1, o2, l1 < l2 ? l1 : l2);
+  if (cmp == 0)
+    cmp = l1 - l2;
+  if (cmp == 0)
+    return 1;
+
+  /* Make sure o1 comes first alphabetically to cut down on the number
+   * of comparisons.
+   */
+  if (cmp > 0)
+    {
+      char *tmp = o1;
+      int ltmp = l1;
+      o1 = o2;
+      l1 = l2;
+      o2 = tmp;
+      l2 = ltmp;
+    }
+
+  if (!strncmp(o1, "bg", l1) && !strncmp(o2, "fg", l2))
+    return 1;
+  else if (!strncmp(o1, "ro", l1) && !strncmp(o2, "rw", l2))
+    return 1;
+  else if (!strncmp(o1, "hard", l1) && !strncmp(o2, "soft", l2))
+    return 1;
+
+  /* They're different. */
+  return 0;
+}
+
+/* This function constructs a complete string of mount options for the
+ * filesystem based on the authmode, options, mountoptions, and
+ * attach.conf.
+ */
+static int check_mountoptions(locker_context context, locker_attachent *at,
+			      int authmode, int options, char **mountoptions)
+{
+  char *mo, *req, *def, *allow, *p, *q;
+  int len;
+
+  req = locker__fs_data(context, context->reqopts, at->fs, at->name);
+  def = locker__fs_data(context, context->defopts, at->fs, at->name);
+  allow = locker__fs_data(context, context->allowopts, at->fs, at->name);
+
+  /* Malloc a string large enough to hold all necessary mount options. */
+  len = 1;
+  if (*mountoptions)
+    len += strlen(*mountoptions) + 1;
+  if (req)
+    len += strlen(req) + 1;
+  if (def)
+    len += strlen(def) + 1;
+  if (!(options & LOCKER_ATTACH_OPT_ALLOW_SETUID))
+    len += 7;
+  if (authmode != LOCKER_AUTH_NONE)
+    len += 3;
+  mo = malloc(len);
+  if (!mo)
+    {
+      locker__error(context, "Out of memory checking mountoptions.\n");
+      return LOCKER_ENOMEM;
+    }
+  *mo = '\0';
+
+  /* Later mountoptions will override earlier ones. So we copy them in
+   * in the order: defaults, authmode, mountoptions, setuid (from
+   * options), and finally the mountoptions specified by attach.conf.
+   * This means that an explicitly specified "ro" or "rw" will
+   * override the default locker mode, an explicitly specified "suid"
+   * will NOT override an enforced "nosuid", and nothing will override
+   * the attach.conf-mandated options. In addition to this, we have to
+   * verify that each option specified in mountoptions is allowed at
+   * all.
+   */
+
+  if (def)
+    sprintf(mo, "%s,", def);
+  if (authmode == LOCKER_AUTH_READONLY)
+    strcat(mo, "ro,");
+  else if (authmode != LOCKER_AUTH_NONE)
+    strcat(mo, "rw,");
+  p = mo + strlen(mo);
+  if (*mountoptions)
+    sprintf(p, "%s,", *mountoptions);
+
+  /* Interlude: check user-specified mountoptions */
+  while (p && *p)
+    {
+      /* Get the length of the option pointed to by p. */
+      len = strcspn(p, "=,");
+
+      /* See if this option appears in the allowed options list. */
+      q = allow;
+      while (q)
+	{
+	  if (!strncmp(p, q, len))
+	    break;
+
+	  q = strchr(q, ',');
+	  if (q)
+	    q++;
+	}
+
+      if (!q)
+	{
+	  /* Option not allowed. Silently discard it. */
+	  q = strchr(p, ',');
+	  if (q)
+	    memmove(p, q + 1, strlen(q + 1) + 1);
+	  else
+	    *p = '\0';
+	}
+      else
+	{
+	  p = strchr(p, ',');
+	  if (p)
+	    p++;
+	}
+    }
+
+  /* Now continue appending options. */
+  if (!(options & LOCKER_ATTACH_OPT_ALLOW_SETUID))
+    strcat(mo, "nosuid,");
+  if (req)
+    strcat(mo, req);
+
+  /* If mo ends with a comma, remove it. */
+  len = strlen(mo);
+  if (mo[len - 1] == ',')
+    mo[len - 1] = '\0';
+
+  /* Finally, go back through and remove duplicates and contradictions. */
+  p = mo;
+  while (p)
+    {
+      q = strchr(p, ',');
+      if (q)
+	{
+	  /* Read through options, looking for a match for p. If p
+	   * starts with "no", also look for a match without the "no".
+	   * If p doesn't start with "no", look for a match with "no".
+	   */
+	  while (q)
+	    {
+	      q++;
+	      if (same_option(p, strcspn(p, "=,"), q, strcspn(q, "=,")))
+		break;
+	      q = strchr(q, ',');
+	    }
+
+	  /* If we found a match, delete the first occurrence. */
+	  if (q)
+	    {
+	      q = strchr(p, ','); /* We know this won't return NULL here. */
+	      memmove(p, q + 1, strlen(q + 1) + 1);
+	    }
+	  else
+	    {
+	      p = strchr(p, ',');
+	      if (p)
+		p++;
+	    }
+	}
+      else
+	break;
+    }
+
+  *mountoptions = mo;
+  return LOCKER_SUCCESS;
+}
+
+static int add_owner(locker_context context, locker_attachent *at, uid_t user)
+{
+  int i;
+  uid_t *new;
+
+  for (i = 0; i < at->nowners; i++)
+    {
+      if (at->owners[i] == user)
+	return LOCKER_SUCCESS;
+    }
+
+  new = realloc(at->owners, (at->nowners + 1) * sizeof(uid_t));
+  if (!new)
+    {
+      locker__error(context, "Out of memory adding new owner to "
+		    "attachtab entry.\n");
+      return LOCKER_ENOMEM;
+    }
+  at->owners = new;
+  at->owners[at->nowners++] = user;
+  return LOCKER_SUCCESS;
+}

Copied: trunk/athena/lib/locker/src/attachtab.c (from rev 25923, trunk/athena/lib/locker/attachtab.c)
===================================================================
--- trunk/athena/lib/locker/src/attachtab.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/attachtab.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,1266 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements the attachtab directory,
+ * which contains files corresponding to attached (or formerly attached)
+ * lockers.
+ */
+
+static const char rcsid[] = "$Id: attachtab.c,v 1.11 2002-02-18 15:50:26 ghudson Exp $";
+
+#include "locker.h"
+#include "locker_private.h"
+
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Used internally by locker_iterate_attachtab */
+struct locker__dirent {
+  char *name;
+  time_t ctime;
+};
+
+static int delete_attachent(locker_context context, locker_attachent *at);
+static int get_attachent(locker_context context, char *name,
+			 char *mountpoint, int create,
+			 locker_attachent **atp);
+static int read_attachent(locker_context context, int kind,
+			  char *name, locker_attachent **atp);
+static int lock_attachtab(locker_context context, int *lock);
+static void unlock_attachtab(locker_context context, int *lock);
+static int compare_locker__dirents(const void *a, const void *b);
+
+/* Public interface to locker__lookup_attachent. */
+int locker_read_attachent(locker_context context, char *name,
+			  locker_attachent **atp)
+{
+  return locker__lookup_attachent(context, name, NULL, 0, atp);
+}
+
+/* Look up a locker by filesys name and return an attachent for it. If
+ * "which" is 0, only return an already-attached locker. Otherwise,
+ * "which" is the index of the Hesiod filesys entry for "name" to look
+ * up. (The caller can keep calling with successively higher values of
+ * "which" until it succeeds in attaching one, or
+ * locker__lookup_attachent returns a LOCKER_LOOKUP_FAILURE() status.
+ *
+ * name can be NULL iff mountpoint is non-NULL and which is 0.
+ */
+int locker__lookup_attachent(locker_context context, char *name,
+			     char *mountpoint, int which,
+			     locker_attachent **atp)
+{
+  int status, i, lock;
+  locker_attachent *at;
+  char **descs, *desc, *p;
+  void *cleanup;
+  struct locker_ops *fs;
+
+  status = lock_attachtab(context, &lock);
+  if (status != LOCKER_SUCCESS)
+    return status;
+
+  if (which < 2)
+    {
+      /* Look for a pre-attached locker. */
+      at = NULL;
+      status = get_attachent(context, name, mountpoint, 0, &at);
+      if (status != LOCKER_ENOTATTACHED)
+	{
+	  unlock_attachtab(context, &lock);
+	  if (status == LOCKER_SUCCESS)
+	    *atp = at;
+	  return status;
+	}
+      else if (!which)
+	{
+	  unlock_attachtab(context, &lock);
+	  locker__error(context, "%s: Not attached.\n",
+			name ? name : mountpoint);
+	  return status;
+	}
+    }
+
+  /* It's not attached. Look up the locker description. */
+  status = locker_lookup_filsys(context, name, &descs, &cleanup);
+  if (status)
+    {
+      unlock_attachtab(context, &lock);
+      return status;
+    }
+
+  if (!descs[1])
+    {
+      /* If only one description, it won't have an order field. */
+      if (which == 1)
+	desc = descs[0];
+      else
+	desc = NULL;
+    }
+  else
+    {
+      /* Check each filesystem description until we find the one we want. */
+      for (i = 0, desc = NULL; descs[i]; i++)
+	{
+	  p = strrchr(descs[i], ' ');
+	  if (!p)
+	    {
+	      unlock_attachtab(context, &lock);
+	      locker_free_filesys(context, descs, cleanup);
+	      locker__error(context, "%s: Could not parse locker "
+			    "description \"%s\".\n", name, descs[i]);
+	      return LOCKER_EPARSE;
+	    }
+
+	  if (atoi(p + 1) == which)
+	    {
+	      desc = descs[i];
+	      break;
+	    }
+	}
+    }
+
+  if (!desc)
+    {
+      unlock_attachtab(context, &lock);
+      locker_free_filesys(context, descs, cleanup);
+      return LOCKER_ENOENT;
+    }
+
+  fs = locker__get_fstype(context, desc);
+  if (!fs)
+    {
+      unlock_attachtab(context, &lock);
+      locker__error(context, "%s: Unknown locker type in description "
+		    "\"%s\".\n", name, desc);
+      locker_free_filesys(context, descs, cleanup);
+      return LOCKER_EPARSE;
+    }
+
+  status = fs->parse(context, name, desc, mountpoint, &at);
+  locker_free_filesys(context, descs, cleanup);
+  if (status)
+    {
+      unlock_attachtab(context, &lock);
+      return status;
+    }
+
+  if (mountpoint)
+    status = get_attachent(context, NULL, at->mountpoint, which != 0, &at);
+  else
+    status = get_attachent(context, name, at->mountpoint, which != 0, &at);
+  unlock_attachtab(context, &lock);
+  if (status == LOCKER_SUCCESS)
+    *atp = at;
+  else
+    locker_free_attachent(context, at);
+
+  return status;
+}
+
+/* Look up a locker by explicit description and return an attachent
+ * for it. If "create" is 0, only return an already-attached locker.
+ */
+int locker__lookup_attachent_explicit(locker_context context, char *type,
+				      char *desc, char *mountpoint,
+				      int create, locker_attachent **atp)
+{
+  struct locker_ops *fs;
+  locker_attachent *at;
+  int status, lock;
+
+  fs = locker__get_fstype(context, type);
+  if (!fs)
+    {
+      locker__error(context, "%s: Unknown locker type \"%s\".\n", desc, type);
+      return LOCKER_EPARSE;
+    }
+
+  status = fs->parse(context, NULL, desc, mountpoint, &at);
+  if (status)
+    return status;
+
+  status = lock_attachtab(context, &lock);
+  if (status != LOCKER_SUCCESS)
+    {
+      locker_free_attachent(context, at);
+      return status;
+    }
+  status = get_attachent(context, NULL, at->mountpoint, create, &at);
+  unlock_attachtab(context, &lock);
+  if (status == LOCKER_SUCCESS)
+    *atp = at;
+  else
+    locker_free_attachent(context, at);
+
+  return status;
+}
+
+/* Delete an attachent file and its corresponding symlink, if any. */
+static int delete_attachent(locker_context context, locker_attachent *at)
+{
+  int status, lock;
+  char *path;
+
+  status = lock_attachtab(context, &lock);
+  if (status != LOCKER_SUCCESS)
+    return status;
+
+  if (at->flags & LOCKER_FLAG_NAMEFILE)
+    {
+      path = locker__attachtab_pathname(context, LOCKER_NAME, at->name);
+      if (!path)
+	{
+	  locker__error(context, "Out of memory deleting attachent.\n");
+	  status = LOCKER_ENOMEM;
+	  goto cleanup;
+	}
+      if (unlink(path) == -1 && errno != ENOENT)
+	{
+	  locker__error(context, "Could not delete file %s:\n%s.\n",
+			path, strerror(errno));
+	  status = LOCKER_EATTACHTAB;
+	  goto cleanup;
+	}
+      free(path);
+    }
+
+  path = locker__attachtab_pathname(context, LOCKER_MOUNTPOINT,
+				    at->mountpoint);
+  if (!path)
+    {
+      locker__error(context, "Out of memory deleting attachent.\n");
+      status = LOCKER_ENOMEM;
+      goto cleanup;
+    }
+  if (unlink(path) == -1)
+    {
+      locker__error(context, "Could not delete file %s:\n%s.\n",
+		    path, strerror(errno));
+      status = LOCKER_EATTACHTAB;
+      goto cleanup;
+    }
+  free(path);
+
+  unlock_attachtab(context, &lock);
+  return LOCKER_SUCCESS;
+
+cleanup:
+  free(path);
+  unlock_attachtab(context, &lock);
+  return status;
+}
+
+/* Free a locker_attachent and unlock any locks associated with it. */
+void locker_free_attachent(locker_context context, locker_attachent *at)
+{
+  if (at->mountpoint_file)
+    {
+      fclose(at->mountpoint_file);
+      if (!at->attached && at->mountpoint)
+	delete_attachent(context, at);
+    }
+  if (at->dirlockfd)
+    close(at->dirlockfd);
+  free(at->name);
+  free(at->mountpoint);
+  free(at->buildfrom);
+  free(at->hostdir);
+  free(at->owners);
+
+  if (at->next)
+    locker_free_attachent(context, at->next);
+
+  free(at);
+}
+
+
+/* Iterate through all attachent entries, calling the "test" function
+ * on each one, and then calling the "act" function on all entries for
+ * which the "test" function returns true. Holds an exclusive lock on
+ * the attachtab lock file through the duration of the call. Only
+ * returns an error if it was unable to begin iterating through the
+ * entries.
+ */
+int locker_iterate_attachtab(locker_context context,
+			     locker_callback test, void *testarg,
+			     locker_callback act, void *actarg)
+{
+  int status, lock;
+  char *path;
+  DIR *dir;
+  struct dirent *entry;
+  locker_attachent *at;
+  struct locker__dirent *files = NULL;
+  int nfiles = 0, filessize = 0, i;
+  struct stat st;
+
+  status = lock_attachtab(context, &lock);
+  if (status)
+    return status;
+
+  path = locker__attachtab_pathname(context, LOCKER_MOUNTPOINT, "");
+  if (!path)
+    {
+      unlock_attachtab(context, &lock);
+      locker__error(context, "Out of memory reading attachtab.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  dir = opendir(path);
+  if (!dir)
+    {
+      unlock_attachtab(context, &lock);
+      locker__error(context, "Could not read attachtab:\n%s opening "
+		    "directory %s\n", strerror(errno), path);
+      free(path);
+      return LOCKER_EATTACHTAB;
+    }
+
+  while ((entry = readdir(dir)))
+    {
+      if (entry->d_name[0] == '.')
+	continue;
+
+      if (nfiles >= filessize)
+	{
+	  struct locker__dirent *newfiles;
+	  int newfilessize;
+
+	  newfilessize = filessize ? 2 * filessize : 10;
+	  newfiles = realloc(files, (newfilessize) * sizeof(*files));
+	  if (!newfiles)
+	    {
+	      status = LOCKER_ENOMEM;
+	      break;
+	    }
+	  files = newfiles;
+	  filessize = newfilessize;
+	}
+
+      files[nfiles].name = malloc(strlen(path) + strlen(entry->d_name) + 1);
+      if (!files[nfiles].name)
+	{
+	  status = LOCKER_ENOMEM;
+	  break;
+	}
+      sprintf(files[nfiles].name, "%s%s", path, entry->d_name);
+      
+      if (stat(files[nfiles].name, &st) != 0)
+	{
+	  locker__error(context, "%s while reading attachtab entry\n\"%s\".\n",
+			strerror(errno), files[nfiles].name);
+	  status = LOCKER_EATTACHTAB;
+	  break;
+	}
+
+      files[nfiles++].ctime = st.st_ctime;
+    }
+  closedir(dir);
+  free(path);
+
+  if (status != LOCKER_SUCCESS)
+    {
+      if (status == LOCKER_ENOMEM)
+	locker__error(context, "Out of memory reading attachtab.\n");
+
+      unlock_attachtab(context, &lock);
+      goto cleanup;
+    }
+
+  /* Sort the entries. */
+  qsort(files, nfiles, sizeof(*files), compare_locker__dirents);
+
+  /* Unlock the attachtab directory, since callbacks might take a while. */
+  unlock_attachtab(context, &lock);
+
+  /* Now run through the callbacks. */
+  for (i = 0; i < nfiles; i++)
+    {
+      status = read_attachent(context, LOCKER_FULL_PATH, files[i].name, &at);
+      if (status == LOCKER_SUCCESS)
+	{
+	  if (!test || test(context, at, testarg))
+	    act(context, at, actarg);
+	  locker_free_attachent(context, at);
+	}
+    }
+  status = LOCKER_SUCCESS;
+
+cleanup:
+  for (i = 0; i < nfiles; i++)
+    free(files[i].name);
+  free(files);
+
+  return status;
+}
+
+static int compare_locker__dirents(const void *a, const void *b)
+{
+  const struct locker__dirent *da = a, *db = b;
+
+  /* MUL lockers (which have spaces in their "mountpoint" names) are
+   * sorted before all other lockers. Other than that, lockers are
+   * sorted by attachent creation time.
+   */
+
+  if (strchr(da->name, ' '))
+    {
+      if (!strchr(db->name, ' '))
+	return -1;
+    }
+  else if (strchr(db->name, ' '))
+    return 1;
+
+  return (da->ctime < db->ctime) ? -1 : (da->ctime > db->ctime) ? 1 : 0;
+}
+
+
+/* Fetch the desired attachtab entry into a struct attachtab. name is
+ * the locker name and mountpoint is its mountpoint. If create is non-0
+ * an empty attachtab entry will be created if none exists.
+ *
+ * name may be NULL. mountpoint may be NULL if name is not. *atp should
+ * be a partially-filled in attachent if create is true. Otherwise it
+ * can be partially-filled in, or NULL.
+ */
+static int get_attachent(locker_context context, char *name,
+			 char *mountpoint, int create,
+			 locker_attachent **atp)
+{
+  locker_attachent *at = NULL;
+  int fd = 0;
+  FILE *fp = NULL;
+  int status;
+  char *path = NULL;
+  struct flock fl;
+  mode_t omask;
+
+  /* Try opening an existing file */
+  if (mountpoint)
+    status = read_attachent(context, LOCKER_MOUNTPOINT, mountpoint, &at);
+  else
+    status = read_attachent(context, LOCKER_NAME, name, &at);
+
+  if (status == LOCKER_SUCCESS)
+    {
+      /* If caller passed in both mountpoint and name, make sure they
+       * both matched.
+       */
+      if (mountpoint && name && strcmp(name, at->name))
+	{
+	  locker__error(context, "%s: %s is already attached on %s.\n",
+			name, at->name, mountpoint);
+	  locker_free_attachent(context, at);
+	  return LOCKER_EMOUNTPOINTBUSY;
+	}
+
+      /* Otherwise, if no attachent was passed in, we've won. */
+      if (!*atp)
+	{
+	  *atp = at;
+	  return status;
+	}
+
+      /* Make sure the found attachent matches the passed-in attachent. */
+      if (strcmp(at->name, (*atp)->name) ||
+	  strcmp(at->mountpoint, (*atp)->mountpoint) ||
+	  strcmp(at->hostdir, (*atp)->hostdir) ||
+	  at->fs != (*atp)->fs)
+	{
+	  locker__error(context, "%s: %s is already attached on %s.\n",
+			(*atp)->name, at->name, at->mountpoint);
+	  locker_free_attachent(context, at);
+	  return LOCKER_EMOUNTPOINTBUSY;
+	}
+
+      /* Free the passed-in attachent and return the found one. */
+      locker_free_attachent(context, *atp);
+      *atp = at;
+      return LOCKER_SUCCESS;
+    }
+  else if (status != LOCKER_ENOTATTACHED)
+    return status;
+
+  /* If we were looking for something already attached, we lose. (But
+   * don't print an error: this might not be fatal.)
+   */
+  if (!create)
+    return LOCKER_ENOTATTACHED;
+
+  path = locker__attachtab_pathname(context, LOCKER_MOUNTPOINT, mountpoint);
+  if (!path)
+    return LOCKER_ENOMEM;
+
+  omask = umask(0);
+  fd = open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+  umask(omask);
+  if (fd == -1)
+    {
+      locker__error(context, "Could not create attachtab file %s:\n%s.\n",
+		    path, strerror(errno));
+      status = LOCKER_EATTACHTAB;
+      goto cleanup;
+    }
+
+  fp = fdopen(fd, "r+");
+  if (!fp)
+    {
+      locker__error(context, "Could not create attachtab file %s:\n%s.\n",
+		    path, strerror(errno));
+      status = LOCKER_EATTACHTAB;
+      goto cleanup;
+    }
+
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_SET;
+  fl.l_start = fl.l_len = 0;
+  status = fcntl(fileno(fp), F_SETLKW, &fl);
+  if (status < 0)
+    {
+      locker__error(context, "Could not lock attachtab file %s:\n%s.\n",
+		    path, strerror(errno));
+      status = LOCKER_EATTACHTAB;
+      goto cleanup;
+    }
+
+  if (name)
+    {
+      char *npath;
+      npath = locker__attachtab_pathname(context, LOCKER_NAME, name);
+      if (!npath)
+	{
+	  status = LOCKER_ENOMEM;
+	  goto cleanup;
+	}
+      unlink(npath);
+      if (symlink(path, npath) < 0)
+	{
+	  locker__error(context, "Could not symlink %s\n to %s: %s.\n",
+			path, npath, strerror(errno));
+	  free(npath);
+	  status = LOCKER_EATTACHTAB;
+	  goto cleanup;
+	}
+      free(npath);
+      (*atp)->flags |= LOCKER_FLAG_NAMEFILE;
+    }
+
+  free(path);
+
+  (*atp)->mountpoint_file = fp;
+  return LOCKER_SUCCESS;
+
+cleanup:
+  if (fp || fd > 0)
+    {
+      unlink(path);
+      if (fp)
+	fclose(fp);
+      else
+	close(fd);
+    }
+  if (path)
+    free(path);
+  if (at)
+    free(at);
+  return status;
+}
+
+/* The format of a mountpoint file is:
+ *
+ * version (currently 1)
+ * hesiod or explicit name
+ * mountpoint
+ * fstype
+ * IP addr of host (or 0.0.0.0)
+ * path to locker
+ * auth mode
+ * flags
+ * # of owners:owner uid:owner uid:...
+ *
+ */
+
+/* Copy data from a locker_attachent into the corresponding file. */
+void locker__update_attachent(locker_context context, locker_attachent *at)
+{
+  int i;
+
+  if (at->attached)
+    {
+      sigset_t omask, mask;
+
+      sigemptyset(&mask);
+      sigaddset(&mask, SIGHUP);
+      sigaddset(&mask, SIGINT);
+      sigaddset(&mask, SIGQUIT);
+      sigaddset(&mask, SIGTERM);
+      sigprocmask(SIG_BLOCK, &mask, &omask);
+
+      rewind(at->mountpoint_file);
+      fprintf(at->mountpoint_file, "1\n%s\n%s\n%s\n%s\n%s\n%c\n%d\n",
+	      at->name, at->mountpoint, at->fs->name, inet_ntoa(at->hostaddr),
+	      at->hostdir, at->mode, at->flags);
+
+      fprintf(at->mountpoint_file, "%d", at->nowners);
+      for (i = 0; i < at->nowners; i++)
+	fprintf(at->mountpoint_file, ":%ld", (long)at->owners[i]);
+      fprintf(at->mountpoint_file, "\n");
+      fflush(at->mountpoint_file);
+
+      sigprocmask(SIG_SETMASK, &omask, NULL);
+    }
+  else
+    {
+      ftruncate(fileno(at->mountpoint_file), 0);
+      rewind(at->mountpoint_file);
+    }
+}
+
+/* Read the named attachtab file into a locker_attachent. */
+static int read_attachent(locker_context context, int kind, char *name,
+			  locker_attachent **atp)
+{
+  FILE *fp;
+  char *path, *pmem = NULL, *buf = NULL, *p, *q;
+  int bufsize, status, i;
+  struct flock fl;
+  locker_attachent *at;
+  struct stat st1, st2;
+
+  if (kind != LOCKER_FULL_PATH)
+    {
+      path = pmem = locker__attachtab_pathname(context, kind, name);
+      if (!path)
+	return LOCKER_ENOMEM;
+    }
+  else
+    path = name;
+
+  /* Need to open it read/write so we can get an F_WRLCK. */
+  fp = fopen(path, "r+");
+  if (!fp)
+    {
+      if (errno == ENOENT)
+	{
+	  free(pmem);
+	  return LOCKER_ENOTATTACHED;
+	}
+
+      locker__error(context, "Could not open attachtab file %s:\n%s.\n",
+		    path, strerror(errno));
+      free(pmem);
+      return LOCKER_EATTACHTAB;
+    }
+
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_SET;
+  fl.l_start = fl.l_len = 0;
+  status = fcntl(fileno(fp), F_SETLKW, &fl);
+  if (status < 0)
+    {
+      fclose(fp);
+      locker__error(context, "Could not lock attachtab file %s:\n%s.\n",
+		    path, strerror(errno));
+      free(pmem);
+      return LOCKER_EATTACHTAB;
+    }
+
+  /* The file might have been deleted while we were waiting on the lock.
+   * Worse yet, someone else might have created a new one after the one
+   * we're waiting for was deleted, but before the deleter gave up its
+   * lock. So check first that it's there, and second that it's still
+   * the right file.
+   */
+  if (stat(path, &st1) == -1)
+    {
+      fclose(fp);
+      if (errno == ENOENT)
+	{
+	  free(pmem);
+	  return LOCKER_ENOTATTACHED;
+	}
+      else
+	{
+	  locker__error(context, "Could not stat attachent file %s:\n%s.\n",
+			path, strerror(errno));
+	  free(pmem);
+	  return LOCKER_EATTACHTAB;
+	}
+    }
+  if (fstat(fileno(fp), &st2) == -1)
+    {
+      fclose(fp);
+      locker__error(context, "Could not fstat attachent file %s:\n%s.\n",
+		    path, strerror(errno));
+      free(pmem);
+      return LOCKER_EATTACHTAB;
+    }
+  if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+    {
+      fclose(fp);
+      free(pmem);
+      return LOCKER_ENOTATTACHED;
+    }
+  if (st1.st_size == 0)
+    {
+      /* "This can't happen." (Ha ha) */
+      unlink(path);
+      fclose(fp);
+      free(pmem);
+      return LOCKER_ENOTATTACHED;
+    }
+  free(pmem);
+
+  /* Start building the attachent. */
+  at = locker__new_attachent(context, NULL);
+  if (!at)
+    {
+      fclose(fp);
+      return LOCKER_ENOMEM;
+    }
+
+  at->mountpoint_file = fp;
+  at->attached = 1;
+
+  /* Read version number. */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  if (atoi(buf) != 1)
+    {
+      locker__error(context, "Could not parse attachtab file %s:\n"
+		    "unknown version number \"%s\".\n", name, buf);
+      status = LOCKER_EATTACHTAB;
+      goto cleanup2;
+    }
+
+  /* Read locker name. */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->name = strdup(buf);
+  if (!at->name)
+    {
+      status = LOCKER_ENOMEM;
+      goto cleanup;
+    }
+
+  /* Read mountpoint. */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->mountpoint = strdup(buf);
+  if (!at->mountpoint)
+    {
+      status = LOCKER_ENOMEM;
+      goto cleanup;
+    }
+
+  /* Read fstype. */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->fs = locker__get_fstype(context, buf);
+  if (!at->fs)
+    {
+      status = LOCKER_ENOMEM;
+      goto cleanup;
+    }
+
+  /* Read hostaddr. */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->hostaddr.s_addr = inet_addr(buf);
+
+  /* Read hostdir. */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->hostdir = strdup(buf);
+  if (!at->hostdir)
+    {
+      status = LOCKER_ENOMEM;
+      goto cleanup;
+    }
+
+  /* Read mode */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->mode = buf[0];
+
+  /* Read flags */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->flags = atoi(buf);
+
+  /* Read list of owners. */
+  status = locker__read_line(fp, &buf, &bufsize);
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+  at->nowners = strtol(buf, &p, 10);
+  if (p == buf || (*p && *p != ':'))
+    {
+      status = LOCKER_EATTACHTAB;
+      goto cleanup;
+    }
+  at->owners = malloc(at->nowners * sizeof(uid_t));
+  if (!at->owners)
+    {
+      status = LOCKER_ENOMEM;
+      goto cleanup;
+    }
+
+  for (i = 0, p++; i < at->nowners && p; i++, p = q + 1)
+    {
+      at->owners[i] = strtol(p, &q, 10);
+      if (p == q || (*q && *q != ':'))
+	{
+	  status = LOCKER_EATTACHTAB;
+	  goto cleanup;
+	}
+    }
+  if (i < at->nowners)
+    {
+      status = LOCKER_EATTACHTAB;
+      goto cleanup;
+    }
+
+  free(buf);
+
+  /* If this is a MUL locker, use mul_parse to read in the other
+   * attachents.
+   */
+  if (!strcmp(at->fs->name, "MUL"))
+    {
+      locker_attachent *atm;
+
+      status = at->fs->parse(context, at->name, at->mountpoint, NULL, &atm);
+      if (status)
+	goto cleanup;
+
+      /* Swap the parsed attachent's MUL chain into the one we read off
+       * disk, and then dispose of the parsed one.
+       */
+      at->next = atm->next;
+      atm->next = NULL;
+      locker_free_attachent(context, atm);
+    }
+
+  *atp = at;
+  return LOCKER_SUCCESS;
+
+cleanup:
+  if (status == LOCKER_EATTACHTAB)
+    {
+      locker__error(context, "Invalid line in attachtab file %s:%s\n",
+		    name, buf);
+    }
+  else if (status == LOCKER_EMOUNTPOINTBUSY)
+    locker__error(context, "%s: Mountpoint busy.\n", at->mountpoint);
+  else if (status == LOCKER_ENOMEM)
+    locker__error(context, "Out of memory reading attachtab file.\n");
+  else
+    {
+      locker__error(context, "Unexpected %s while reading attachtab "
+		    "file %s.\n", status == LOCKER_EOF ? "end of file" :
+		    "file error", name);
+      status = LOCKER_EATTACHTAB;
+    }
+cleanup2:
+  free(buf);
+  locker_free_attachent(context, at);
+  return status;
+}
+
+/* Lock the attachtab directory. This must be done in order to:
+ *  - create an attachent
+ *  - delete an attachent
+ *  - create a symlink from mountpoints/ to lockers/
+ *  - prevent other processes from doing the above, to guarantee
+ *    a consistent view of the attachtab.
+ */
+static int lock_attachtab(locker_context context, int *lock)
+{
+  int status;
+  char *path;
+  struct flock fl;
+  mode_t o_umask;
+
+  /* If the attachtab is already locked (because we're reading a
+   * subcomponent of a MUL locker, for instance), just record an
+   * additional lock and return success.
+   */
+  if (context->locks)
+    {
+      context->locks++;
+      return LOCKER_SUCCESS;
+    }
+
+  path = locker__attachtab_pathname(context, LOCKER_LOCK, ".lock");
+  if (!path)
+    {
+      locker__error(context, "Out of memory in lock_attachtab.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  o_umask = umask(0);
+  *lock = open(path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+  free (path);
+  umask(o_umask);
+  if (*lock < 0)
+    {
+      locker__error(context, "Could not open attachtab lock file: %s.\n",
+		    strerror(errno));
+      return LOCKER_EATTACHTAB;
+    }
+
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_SET;
+  fl.l_start = fl.l_len = 0;
+  status = fcntl(*lock, F_SETLKW, &fl);
+  if (status < 0)
+    {
+      close(*lock);
+      locker__error(context, "Could not lock attachtab: %s.\n",
+		    strerror(errno));
+      return LOCKER_EATTACHTAB;
+    }
+
+  context->locks++;
+  return LOCKER_SUCCESS;
+}
+
+static void unlock_attachtab(locker_context context, int *lock)
+{
+  if (--context->locks == 0)
+    close(*lock);
+}
+
+static char *kpath[] = {
+  ".", /* LOCK */
+  "locker", /* NAME */
+  "mountpoint", /* MOUNTPOINT */
+  "directory", /* DIRECTORY */
+};
+
+char *locker__attachtab_pathname(locker_context context,
+				 int kind, char *name)
+{
+  int len;
+  char *path, *s, *d;
+
+  len = strlen(context->attachtab) + strlen(kpath[kind]) + strlen(name) + 3;
+  for (path = name; *path; path++)
+    {
+      if (*path == '/' || *path == '_')
+	len++;
+    }
+
+  path = malloc(len);
+  if (!path)
+    {
+      locker__error(context, "Out of memory reading attachent.\n");
+      return NULL;
+    }
+
+  sprintf(path, "%s/%s/", context->attachtab, kpath[kind]);
+  d = path + strlen(path);
+
+  for (s = name; *s; s++)
+    {
+      switch (*s)
+	{
+	case '/':
+	  *d++ = '_';
+	  *d++ = '=';
+	  break;
+
+	case '_':
+	  *d++ = '_';
+	  /* fall through */
+
+	default:
+	  *d++ = *s;
+	}
+    }
+  *d = '\0';
+
+  return path;
+}
+
+locker_attachent *locker__new_attachent(locker_context context,
+					struct locker_ops *type)
+{
+  locker_attachent *at;
+
+  at = malloc(sizeof(locker_attachent));
+  if (!at)
+    {
+      locker__error(context, "Out of memory creating attachent.\n");
+      return NULL;
+    }
+
+  memset(at, 0, sizeof(locker_attachent));
+  at->name = at->mountpoint = at->hostdir = at->buildfrom = NULL;
+  at->owners = NULL;
+  at->mountpoint_file = NULL;
+  at->next = NULL;
+
+  at->fs = type;
+  return at;
+}
+
+
+/* Convert an old attachtab file into a new attachtab directory */
+int locker_convert_attachtab(locker_context context, char *oattachtab)
+{
+  FILE *fp;
+  char *buf = NULL;
+  int bufsize, status;
+  char *p, *lasts = NULL, *host, *name;
+  int rmdir;
+  locker_attachent *at;
+  struct locker_ops *fs;
+
+  fp = fopen(oattachtab, "r");
+  if (!fp)
+    {
+      locker__error(context, "Could not open %s: %s.\n", oattachtab,
+		    strerror(errno));
+      return LOCKER_EATTACHTAB;
+    }
+
+  while ((status = locker__read_line(fp, &buf, &bufsize)) == LOCKER_SUCCESS)
+    {
+      /* Check attachtab version. */
+      if (strncmp(buf, "A1 ", 3))
+	{
+	  locker__error(context, "Ignoring unrecognized attachtab line: %s\n",
+			buf);
+	  continue;
+	}
+
+      /* Check if it's attached, attaching, or detaching. */
+      if (buf[4] != '+')
+	{
+	  locker__error(context, "Ignoring not-fully-attached locker: %s\n",
+			buf);
+	  continue;
+	}
+
+      /* Get fs type. */
+      p = strtok_r(buf + 5, " ", &lasts);
+      fs = locker__get_fstype(context, p);
+      if (!fs)
+	{
+	  locker__error(context, "Ignoring unknown locker type \"%s\"\n", p);
+	  continue;
+	}
+      at = locker__new_attachent(context, fs);
+      if (!at)
+	{
+	  locker__error(context, "Out of memory.\n");
+	  status = LOCKER_ENOMEM;
+	  break;
+	}
+
+      /* Now start parsing. First name. */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p)
+	at->name = strdup(p);
+
+      /* Host */
+      host = strtok_r(NULL, " ", &lasts);
+
+      /* Directory */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p)
+	{
+	  if (!strcmp(at->fs->name, "MUL"))
+	    {
+	      /* Old attachtab stores MUL components as hostdir. We store
+	       * it as mountpoint.
+	       */
+
+	      char *q;
+	      /* Convert commas to spaces */
+	      for (q = p; *q; q++)
+		{
+		  if (*q == ',')
+		    *q = ' ';
+		}
+
+	      at->mountpoint = strdup(p);
+	      at->hostdir = strdup("");
+	    }
+	  else if (host && strcmp(host, "localhost"))
+	    {
+	      at->hostdir = malloc(strlen(host) + strlen(p) + 2);
+	      if (at->hostdir)
+		sprintf(at->hostdir, "%s:%s", host, p);
+	    }
+	  else
+	    at->hostdir = strdup(p);
+	}
+
+      /* IP addr */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p)
+	at->hostaddr.s_addr = inet_addr(p);
+
+      /* Number of directories to rm on detach.
+       * Needs special conversion later.
+       */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p)
+	rmdir = atoi(p);
+
+      /* Mountpoint */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p && strcmp(at->fs->name, "MUL"))
+	at->mountpoint = strdup(p);
+
+      /* Convert old flags to new flags */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p)
+	{
+	  int oflags = atoi(p);
+	  if (oflags & 0x1)
+	    at->flags |= LOCKER_FLAG_NOSUID;
+	  if (oflags & 0x2)
+	    at->flags |= LOCKER_FLAG_LOCKED;
+	  if (oflags & 0x8)
+	    at->flags |= LOCKER_FLAG_KEEP;
+	}
+
+      /* Comma-separated list of owners */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p)
+	{
+	  char *q = p;
+
+	  do
+	    {
+	      at->nowners++;
+	      q++;
+	    }
+	  while ((q = strchr(q, ',')));
+
+	  at->owners = malloc(at->nowners * sizeof(uid_t));
+	  if (at->owners)
+	    {
+	      int i;
+	      for (i = 0; i < at->nowners; i++)
+		{
+		  at->owners[i] = strtoul(p, &p, 10);
+		  p++;
+		}
+	    }
+	}
+
+      /* RVD drivenum -- Ignored */
+      p = strtok_r(NULL, " ", &lasts);
+
+      /* And mount mode */
+      p = strtok_r(NULL, " ", &lasts);
+      if (p)
+	at->mode = *p;
+
+      /* Make sure we got all that */
+      if (!p || !at->name || !at->hostdir || !at->mountpoint || !at->owners)
+	{
+	  locker__error(context, "Parse error or out of memory.\n");
+	  status = LOCKER_EATTACHTAB;
+	  break;
+	}
+
+      at->attached = 1;
+      if (buf[3] == '0')
+	{
+	  at->flags |= LOCKER_FLAG_NAMEFILE;
+	  name = at->name;
+	}
+      else
+	name = NULL;
+
+      status = get_attachent(context, name, at->mountpoint, 1, &at);
+      if (status != LOCKER_SUCCESS)
+	{
+	  locker__error(context, "Could not create attachent for %s.\n",
+			at->name);
+	  locker_free_attachent(context, at);
+	  continue;
+	}
+
+      locker__update_attachent(context, at);
+
+      /* Due to a bug in the old attach, rmdir will be "1" instead of "0"
+       * if you attach an AFS locker in an immediate subdir of "/".
+       */
+      if (rmdir == 1 && !strchr(at->mountpoint + 1, '/'))
+	rmdir--;
+
+      /* Now record the directories to delete on detach. */
+      while (rmdir--)
+	{
+	  int fd;
+	  char *file = locker__attachtab_pathname(context, LOCKER_DIRECTORY,
+						  at->mountpoint);
+	  if (!file)
+	    {
+	      locker__error(context, "Out of memory.\n");
+	      status = LOCKER_ENOMEM;
+	      goto cleanup;
+	    }
+
+	  fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+	  if (fd == -1 && errno != EEXIST)
+	    {
+	      locker__error(context, "Could not create directory data file "
+			    "%s:\n%s.\n", file, strerror(errno));
+	      free(file);
+	      continue;
+	    }
+	  free(file);
+	  close(fd);
+
+	  p = strrchr(at->mountpoint, '/');
+	  if (!p || p == at->mountpoint)
+	    break;
+	  *p = '\0';
+	}
+
+      locker_free_attachent(context, at);
+    }
+
+cleanup:
+  fclose(fp);
+  free(buf);
+
+  return status;
+}

Copied: trunk/athena/lib/locker/src/conf.c (from rev 25923, trunk/athena/lib/locker/conf.c)
===================================================================
--- trunk/athena/lib/locker/src/conf.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/conf.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,572 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements reading attach.conf. */
+
+static const char rcsid[] = "$Id: conf.c,v 1.10 2006-08-08 21:50:09 ghudson Exp $";
+
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <com_err.h>
+#include <hesiod.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+extern struct locker_ops locker__err_ops, locker__mul_ops, locker__afs_ops;
+extern struct locker_ops locker__nfs_ops, locker__ufs_ops, locker__loc_ops;
+
+#define LOCKER_ALL_FSTYPES (-1L)
+
+static void free_regexp_list(struct locker__regexp_list lst, int strings);
+
+static int parse_bool(locker_context context, char *line, void *val);
+static int parse_string(locker_context context, char *line, void *val);
+static int parse_trusted_list(locker_context context, char *line, void *val);
+static int parse_regex_list_yes(locker_context context, char *line, void *val);
+static int parse_regex_list_no(locker_context context, char *line, void *val);
+static int parse_regex_list(locker_context context, char *line,
+			    struct locker__regexp_list *val, int flag);
+static int parse_fs_list(locker_context context, char *line, void *val);
+static int add_regexp(locker_context context, struct locker__regexp_list *val,
+		      char *regexp);
+static int parse_obsolete(locker_context context, char *line, void *val);
+
+static struct opt_def {
+  char *name;
+  int (*parse)(locker_context, char *, void *);
+  size_t offset;
+} conf_options[] = {
+  /* Boolean options */
+  { "explicit", parse_bool, offsetof(struct locker_context, exp_desc) },
+  { "explicit-mntpt", parse_bool, offsetof(struct locker_context, exp_mountpoint) },
+  { "keep-mount", parse_bool, offsetof(struct locker_context, keep_mount) },
+  { "nfs-root-hack", parse_bool, offsetof(struct locker_context, nfs_root_hack) },
+  { "ownercheck", parse_bool, offsetof(struct locker_context, ownercheck) },
+  { "use-krb4", parse_bool, offsetof(struct locker_context, use_krb4) },
+
+  /* String options */
+  { "afs-mount-dir", parse_string, offsetof(struct locker_context, afs_mount_dir) },
+  { "attachtab", parse_string, offsetof(struct locker_context, attachtab) },
+  { "local-dir", parse_string, offsetof(struct locker_context, local_dir) },
+  { "nfs-mount-dir", parse_string, offsetof(struct locker_context, nfs_mount_dir) },
+
+  /* Trusted user list */
+  { "trusted", parse_trusted_list, offsetof(struct locker_context, trusted) },
+
+  /* Regexp list options */
+  { "allow", parse_regex_list_yes, offsetof(struct locker_context, allow) },
+  { "noallow", parse_regex_list_no, offsetof(struct locker_context, allow) },
+  { "mountpoint", parse_regex_list_yes, offsetof(struct locker_context, mountpoint) },
+  { "nomountpoint", parse_regex_list_no, offsetof(struct locker_context, mountpoint) },
+  { "setuid", parse_regex_list_yes, offsetof(struct locker_context, setuid) },
+  { "suid", parse_regex_list_yes, offsetof(struct locker_context, setuid) },
+  { "nosetuid", parse_regex_list_no, offsetof(struct locker_context, setuid) },
+  { "nosuid", parse_regex_list_no, offsetof(struct locker_context, setuid) },
+
+  /* Filesystem regexp and argument options */
+  { "allowoptions", parse_fs_list, offsetof(struct locker_context, allowopts) },
+  { "defoptions", parse_fs_list, offsetof(struct locker_context, defopts) },
+  { "filesystem", parse_fs_list, offsetof(struct locker_context, filesystem) },
+  { "options", parse_fs_list, offsetof(struct locker_context, reqopts) },
+
+  /* Obsolete options */
+  { "aklog", parse_obsolete, 0 },
+  { "debug", parse_obsolete, 0 },
+  { "fsck", parse_obsolete, 0 },
+  { "mtab", parse_obsolete, 0 },
+  { "ownerlist", parse_obsolete, 0 },
+  { "verbose", parse_obsolete, 0 },
+};
+
+static int noptions = sizeof(conf_options) / sizeof(struct opt_def);
+
+int locker_init(locker_context *contextp, uid_t user,
+		locker_error_fun errfun, void *errdata)
+{
+  locker_context context;
+  FILE *fp;
+  char *buf = NULL, *p, *q;
+  int i, size, status;
+
+  context = *contextp = malloc(sizeof(struct locker_context));
+  if (!context)
+    {
+      locker__error(context, "Out of memory reading attach.conf.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  /* Set defaults. */
+  memset(context, 0, sizeof(struct locker_context));
+  if (errfun)
+    {
+      context->errfun = errfun;
+      context->errdata = errdata;
+    }
+  else
+    context->errfun = NULL;
+  context->nfs_root_hack = context->exp_desc = context->exp_mountpoint = 1;
+  context->afs_mount_dir = strdup(LOCKER_AFS_MOUNT_DIR);
+  context->attachtab = strdup(LOCKER_PATH_ATTACHTAB);
+  context->local_dir = strdup(LOCKER_PATH_LOCAL);
+  if (!context->afs_mount_dir || !context->attachtab)
+    {
+      locker__error(context, "Out of memory reading attach.conf.\n");
+      locker_end(context);
+      return LOCKER_ENOMEM;
+    }
+  context->nfs_mount_dir = NULL;
+  context->user = user;
+  if (context->user == 0)
+    context->trusted = 1;
+  context->zsubs = NULL;
+
+  context->setuid.tab = NULL;
+  context->setuid.defflag = 1;
+  context->allow.tab = NULL;
+  context->allow.defflag = 1;
+  context->mountpoint.tab = NULL;
+  context->mountpoint.defflag = 1;
+  context->filesystem.tab = NULL;
+  context->reqopts.tab = NULL;
+  context->defopts.tab = NULL;
+  context->allowopts.tab = NULL;
+
+  /* Filesystem defaults. */
+  context->nfstypes = 6;
+  context->fstype = malloc(context->nfstypes * sizeof(struct locker_ops *));
+  if (!context->fstype)
+    {
+      locker__error(context, "Out of memory reading attach.conf.\n");
+      locker_end(context);
+      return LOCKER_ENOMEM;
+    }
+  context->fstype[0] = &locker__err_ops;
+  context->fstype[1] = &locker__mul_ops;
+  context->fstype[2] = &locker__afs_ops;
+  context->fstype[3] = &locker__nfs_ops;
+  context->fstype[4] = &locker__ufs_ops;
+  context->fstype[5] = &locker__loc_ops;
+  for (i = 0; i < context->nfstypes; i++)
+    context->fstype[i]->id = 1 << i;
+
+  fp = fopen(LOCKER_PATH_ATTACH_CONF, "r");
+  if (!fp)
+    {
+      locker__error(context, "%s while trying to open attach.conf.\n",
+		    strerror(errno));
+      locker_end(context);
+      return LOCKER_EATTACHCONF;
+    }
+
+  while ((status = locker__read_line(fp, &buf, &size)) == 0)
+    {
+      if (!buf[0] || buf[0] == '#')
+	continue;
+
+      p = buf;
+      while (isspace((unsigned char)*p))
+	p++;
+      q = p;
+      while (*q && !isspace((unsigned char)*q))
+	q++;
+
+      for (i = 0; i < noptions; i++)
+	{
+	  if (!strncmp(conf_options[i].name, p, q - p) &&
+	      !conf_options[i].name[q - p])
+	    break;
+	}
+      if (i == noptions)
+	{
+	  locker__error(context, "Unrecognized attach.conf line:\n%s\n", buf);
+	  locker_end(context);
+	  free(buf);
+	  fclose(fp);
+	  return LOCKER_EATTACHCONF;
+	}
+      else
+	{
+	  while (*q && isspace((unsigned char)*q))
+	    q++;
+	  status = conf_options[i].parse(context, q,
+					 ((char *)context +
+					  conf_options[i].offset));
+	}
+
+      if (status)
+	break;
+    }
+
+  free(buf);
+  fclose(fp);
+
+  if (status && status != LOCKER_EOF)
+    {
+      if (status == LOCKER_ENOMEM)
+	locker__error(context, "Out of memory reading attach.conf.\n");
+      else if (status == LOCKER_EFILE)
+	{
+	  locker__error(context, "Error reading attach.conf:\n%s\n",
+			strerror(errno));
+	  status = LOCKER_EATTACHCONF;
+	}
+      else
+	locker__error(context, "Bad attach.conf line: \"%s\"\n", buf);
+
+      locker_end(context);
+      return status;
+    }
+
+  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_NONE,
+				     &context->attachtab, NULL);
+  if (status)
+    {
+      locker_end(context);
+      return status;
+    }
+
+  /* Initialize Hesiod. */
+  if (hesiod_init(&context->hes_context) != 0)
+    {
+      locker__error(context, "Could not create locker context:\n"
+		    "%s while initializing Hesiod.\n", strerror(errno));
+      locker_end(context);
+      return LOCKER_EHESIOD;
+    }
+
+  return LOCKER_SUCCESS;
+}
+
+void locker_end(locker_context context)
+{
+  free(context->afs_mount_dir);
+  free(context->attachtab);
+  free(context->nfs_mount_dir);
+  free(context->local_dir);
+  free(context->fstype);
+  free_regexp_list(context->setuid, 0);
+  free_regexp_list(context->allow, 0);
+  free_regexp_list(context->mountpoint, 0);
+  free_regexp_list(context->filesystem, 1);
+  free_regexp_list(context->reqopts, 1);
+  free_regexp_list(context->defopts, 1);
+  free_regexp_list(context->allowopts, 1);
+
+  if (context->hes_context)
+    hesiod_end(context->hes_context);
+  locker__free_zsubs(context);
+
+  free(context);
+}
+
+static void free_regexp_list(struct locker__regexp_list lst, int strings)
+{
+  int i;
+
+  for (i = 0; i < lst.num; i++)
+    {
+      regfree(&(lst.tab[i].pattern));
+      if (strings)
+	free(lst.tab[i].data.string);
+    }
+  free(lst.tab);
+}
+
+static int parse_bool(locker_context context, char *line, void *val)
+{
+  int *boolval = val;
+
+  /* Nothing = yes */
+  if (!*line)
+    {
+      *boolval = 1;
+      return LOCKER_SUCCESS;
+    }
+
+  if (!strcmp(line, "on"))
+    *boolval = 1;
+  else if (!strcmp(line, "off"))
+    *boolval = 0;
+  else
+    {
+      locker__error(context, "Unrecognized boolean value \"%s\".\n", line);
+      return LOCKER_EATTACHCONF;
+    }
+  return LOCKER_SUCCESS;
+}
+
+static int parse_string(locker_context context, char *line, void *val)
+{
+  char **strval = val;
+
+  free(*strval);
+  *strval = strdup(line);
+
+  if (!*strval)
+    return LOCKER_ENOMEM;
+  return LOCKER_SUCCESS;
+}
+
+static int parse_trusted_list(locker_context context, char *line, void *val)
+{
+  int *trusted = val;
+  char *lasts = NULL;
+
+  for (line = strtok_r(line, " \t", &lasts); line;
+       line = strtok_r(NULL, " \t", &lasts))
+    {
+      if (isdigit((unsigned char)*line) && context->user == atoi(line))
+	*trusted = 1;
+      else
+	{
+	  struct passwd *pw;
+
+	  pw = getpwnam(line);
+	  if (!pw)
+	    continue;
+	  if (pw->pw_uid == context->user)
+	    *trusted = 1;
+	}
+    }
+  return LOCKER_SUCCESS;
+}
+
+static int parse_regex_list_yes(locker_context context, char *line, void *val)
+{
+  return parse_regex_list(context, line, val, 1);
+}
+
+static int parse_regex_list_no(locker_context context, char *line, void *val)
+{
+  return parse_regex_list(context, line, val, 0);
+}
+
+static int parse_regex_list(locker_context context, char *line,
+			    struct locker__regexp_list *val, int flag)
+{
+  int status = LOCKER_SUCCESS;
+  char *data, *dup = strdup(line), *lasts = NULL;
+
+  if (!dup)
+    return LOCKER_ENOMEM;
+  for (data = strtok_r(dup, " \t", &lasts); data && !status;
+       data = strtok_r(NULL, " \t", &lasts))
+    {
+      status = add_regexp(context, val, data);
+      if (status == LOCKER_SUCCESS)
+	val->tab[val->num++].data.flag = flag;
+    }
+  free(dup);
+  return status;
+}
+
+static int parse_fs_list(locker_context context, char *line, void *val)
+{
+  int status;
+  char *data = strdup(line), *lasts = NULL;
+  struct locker__regexp_list *lst = val;
+
+  if (!data)
+    return LOCKER_ENOMEM;
+
+  line = strtok_r(data, " \t", &lasts);
+  if (!line || !lasts)
+    return LOCKER_EATTACHCONF;
+  for (line = lasts; isspace((unsigned char)*line); line++)
+    ;
+
+  status = add_regexp(context, lst, data);
+  if (status == LOCKER_SUCCESS)
+    {
+      lst->tab[lst->num].data.string = strdup(line);
+      if (!lst->tab[lst->num++].data.string)
+	status = LOCKER_ENOMEM;
+    }
+  free(data);
+  return status;
+}
+
+static int add_regexp(locker_context context, struct locker__regexp_list *lst,
+		      char *regexp)
+{
+  int i, len, status;
+
+  /* Extend the array if we ran out of slots. */
+  if (lst->num >= lst->size - 1)
+    {
+      struct locker__regexp *new;
+      int nsize;
+
+      if (lst->size)
+	nsize = 2 * lst->size;
+      else
+	nsize = 5;
+      new = realloc(lst->tab, nsize * sizeof(struct locker__regexp));
+      if (!new)
+	return LOCKER_ENOMEM;
+      /* Zero out the newly-allocated parts, but not the old parts. */
+      memset(new + lst->size, 0,
+	     (nsize - lst->size) * sizeof(struct locker__regexp));
+      lst->size = nsize;
+      lst->tab = new;
+    }
+
+  /* Check for {[-+]?^?fstype,fstype,...}: prefix. */
+  if (*regexp == '{')
+    {
+      int invert = 0;
+
+      if (*++regexp == '+')
+	{
+	  regexp++;
+	  lst->tab[lst->num].explicit = LOCKER_EXPLICIT;
+	}
+      else if (*regexp == '-')
+	{
+	  regexp++;
+	  lst->tab[lst->num].explicit = LOCKER_NOEXPLICIT;
+	}
+      else if (*regexp == '^')
+	{
+	  regexp++;
+	  invert = 1;
+	}
+
+      while (*regexp != '}')
+	{
+	  for (i = 0; i < context->nfstypes; i++)
+	    {
+	      len = strlen(context->fstype[i]->name);
+	      if (!strncasecmp(regexp, context->fstype[i]->name, len) &&
+		  (regexp[len] == ',' || regexp[len] == '}'))
+		break;
+	    }
+	  if (i == context->nfstypes)
+	    {
+	      len = strcspn(regexp, " ,}");
+	      locker__error(context, "Unrecognized filesystem type "
+			    "\"%.*s\".\n", len, regexp);
+	      return LOCKER_EATTACHCONF;
+	    }
+
+	  lst->tab[lst->num].fstypes |= context->fstype[i]->id;
+	  regexp += len;
+	}
+      if (invert)
+	lst->tab[lst->num].fstypes ^= LOCKER_ALL_FSTYPES;
+
+      /* Skip "}" or "}:". */
+      regexp++;
+      if (*regexp == ':')
+	regexp++;
+    }
+  else
+    lst->tab[lst->num].fstypes = LOCKER_ALL_FSTYPES;
+
+  /* Now compile the normal regexp part. */
+  status = regcomp(&(lst->tab[lst->num].pattern), regexp, REG_NOSUB);
+  if (status)
+    {
+      char *errbuf;
+      int size = regerror(status, &(lst->tab[lst->num].pattern), NULL, 0);
+      errbuf = malloc(size);
+      if (!errbuf)
+	return LOCKER_ENOMEM;
+      else
+	{
+	  regerror(status, &(lst->tab[lst->num].pattern), errbuf, size);
+	  locker__error(context, "Could not parse regular expression "
+			"\"%s\":%s.\n", regexp, errbuf);
+	  return LOCKER_EATTACHCONF;
+	}
+    }
+
+  return LOCKER_SUCCESS;
+}
+
+static int parse_obsolete(locker_context context, char *line, void *val)
+{
+  locker__error(context, "Ignoring obsolete attach.conf option: %s\n",
+		line);
+  return LOCKER_SUCCESS;
+}
+
+void locker__error(locker_context context, char *fmt, ...)
+{
+  va_list ap;
+
+  va_start(ap, fmt);
+  if (context && context->errfun)
+    context->errfun(context->errdata, fmt, ap);
+  else
+    vfprintf(stderr, fmt, ap);
+  va_end(ap);
+}
+
+int locker__fs_ok(locker_context context, struct locker__regexp_list list,
+		  struct locker_ops *fs, char *filesystem)
+{
+  int i;
+
+  for (i = 0; i < list.num; i++)
+    {
+      if (fs && !(list.tab[i].fstypes & fs->id))
+	continue;
+
+      if (regexec(&(list.tab[i].pattern), filesystem, 0, NULL, 0) == 0)
+	return list.tab[i].data.flag;
+    }
+
+  return list.defflag;
+}
+
+char *locker__fs_data(locker_context context, struct locker__regexp_list list,
+		      struct locker_ops *fs, char *filesystem)
+{
+  int i;
+
+  for (i = 0; i < list.num; i++)
+    {
+      if (fs && !(list.tab[i].fstypes & fs->id))
+	continue;
+
+      if (regexec(&(list.tab[i].pattern), filesystem, 0, NULL, 0) == 0)
+	return list.tab[i].data.string;
+    }
+
+  return NULL;
+}
+
+struct locker_ops *locker__get_fstype(locker_context context, char *type)
+{
+  int i, len;
+
+  for (i = 0; i < context->nfstypes; i++)
+    {
+      len = strlen(context->fstype[i]->name);
+      if (!strncasecmp(type, context->fstype[i]->name, len) &&
+	  (!type[len] || isspace((unsigned char)type[len])))
+	return context->fstype[i];
+    }
+  return NULL;
+}

Copied: trunk/athena/lib/locker/src/detach.c (from rev 25923, trunk/athena/lib/locker/detach.c)
===================================================================
--- trunk/athena/lib/locker/src/detach.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/detach.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,225 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements detaching lockers. */
+
+static const char rcsid[] = "$Id: detach.c,v 1.3 1999-12-27 18:13:16 danw Exp $";
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hesiod.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+static int detach_attachent(locker_context context,
+			    locker_attachent *at, int options);
+static void del_owner(locker_context context, locker_attachent *at,
+		      uid_t user);
+static void clean_owners(locker_context context, locker_attachent *at);
+
+
+int locker_detach(locker_context context, char *filesystem, char *mountpoint,
+		  int options, locker_attachent **atp)
+{
+  int status;
+  locker_attachent *at;
+
+  status = locker__lookup_attachent(context, filesystem, mountpoint, 0, &at);
+  if (status)
+    return status;
+
+  status = locker_detach_attachent(context, at, options);
+  if (LOCKER_DETACH_SUCCESS(status) && atp)
+    *atp = at;
+  else
+    locker_free_attachent(context, at);
+  return status;
+}
+
+int locker_detach_explicit(locker_context context, char *type,
+			   char *desc, char *mountpoint, int options,
+			   locker_attachent **atp)
+{
+  int status;
+  locker_attachent *at;
+
+  status = locker__lookup_attachent_explicit(context, type, desc,
+					     mountpoint, 1, &at);
+  if (status)
+    return status;
+
+  status = locker_detach_attachent(context, at, options);
+  if (LOCKER_DETACH_SUCCESS(status) && atp)
+    *atp = at;
+  else
+    locker_free_attachent(context, at);
+  return status;
+}
+
+int locker_detach_attachent(locker_context context,
+			    locker_attachent *at, int options)
+{
+  int status = LOCKER_SUCCESS;
+  locker_attachent *ai;
+
+  if (!context->trusted)
+    {
+      if (options & LOCKER_DETACH_OPT_UNLOCK)
+	{
+	  locker__error(context, "%s: You are not authorized to use the "
+			"unlock option.\n", at->name);
+	  return LOCKER_EPERM;
+	}
+
+      if (options & LOCKER_DETACH_OPT_OVERRIDE)
+	{
+	  locker__error(context, "%s: You are not authorized to use the "
+			"override option.\n", at->name);
+	  return LOCKER_EPERM;
+	}
+    }
+
+  /* If this is a MUL locker, detach sublockers first. */
+  for (ai = at->next; ai && status == LOCKER_SUCCESS; ai = ai->next)
+    status = detach_attachent(context, ai, options);
+
+  if (!LOCKER_DETACH_SUCCESS(status))
+    {
+      locker__error(context, "%s: MUL detach failed.\n", at->name);
+      return status;
+    }
+
+  status = detach_attachent(context, at, options);
+
+  return status;
+}
+
+/* This is the routine that does all of the work of attaching a single
+ * filesystem.
+ */
+static int detach_attachent(locker_context context,
+			    locker_attachent *at, int options)
+{
+  int status, dstatus;
+
+  if (!context->trusted)
+    {
+      /* Don't allow a user to detach something he couldn't attach. */
+      if (!locker__fs_ok(context, context->allow, at->fs, at->name))
+	{
+	  locker__error(context, "%s: You are not allowed to detach this "
+			"locker.\n", at->name);
+	  return LOCKER_EPERM;
+	}
+      if (strcmp(at->fs->name, "MUL") &&
+	  !locker__fs_ok(context, context->mountpoint, at->fs, at->mountpoint))
+	{
+	  locker__error(context, "%s: You are not allowed to detach a "
+			"locker from %s.\n", at->name, at->mountpoint);
+	  return LOCKER_EPERM;
+	}
+    }
+
+  /* Check locking. */
+  if (at->flags & LOCKER_FLAG_LOCKED &&
+      !(options & LOCKER_DETACH_OPT_UNLOCK))
+    {
+      locker__error(context, "%s: filesystem is locked.\n", at->name);
+      return LOCKER_EPERM;
+    }
+
+  if (options & LOCKER_DETACH_OPT_CLEAN)
+    {
+      clean_owners(context, at);
+      if (at->nowners)
+	{
+	  locker__update_attachent(context, at);
+	  return LOCKER_SUCCESS;
+	}
+    }
+  else
+    del_owner(context, at, context->user);
+
+  if (at->nowners && (context->ownercheck ||
+		      (options & LOCKER_DETACH_OPT_OWNERCHECK)) &&
+      !(options & LOCKER_DETACH_OPT_OVERRIDE))
+    {
+      locker__error(context, "%s: Locker is wanted by others. Not detached.\n",
+		    at->name);
+      dstatus = LOCKER_EINUSE;
+    }
+  else
+    {
+      if (!(at->flags & LOCKER_FLAG_KEEP))
+	{
+	  /* Detach the locker. */
+	  dstatus = at->fs->detach(context, at);
+	  if (!LOCKER_DETACH_SUCCESS(dstatus))
+	    return dstatus;
+
+	  /* Remove any mountpoint components we created. */
+	  status = locker__remove_mountpoint(context, at);
+	  if (status != LOCKER_SUCCESS && status != LOCKER_EMOUNTPOINTBUSY)
+	    return status;
+	}
+      else
+	dstatus = LOCKER_SUCCESS;
+
+      /* Mark the locker detached. */
+      at->attached = 0;
+    }
+  locker__update_attachent(context, at);
+
+  /* Unauthenticate. */
+  if (options & LOCKER_DETACH_OPT_UNAUTH)
+    at->fs->auth(context, at, LOCKER_AUTH_DEFAULT, LOCKER_AUTH_UNAUTHENTICATE);
+
+  /* Record zephyr unsubscriptions. */
+  if (options & LOCKER_DETACH_OPT_UNZEPHYR)
+    at->fs->zsubs(context, at);
+
+  return dstatus;
+}
+
+static void del_owner(locker_context context, locker_attachent *at,
+		      uid_t user)
+{
+  int i;
+
+  for (i = 0; i < at->nowners; i++)
+    {
+      if (at->owners[i] == user)
+	{
+	  at->owners[i] = at->owners[--at->nowners];
+	  return;
+	}
+    }
+}
+
+static void clean_owners(locker_context context, locker_attachent *at)
+{
+  int i;
+
+  for (i = 0; i < at->nowners; )
+    {
+      if (!getpwuid(at->owners[i]))
+	at->owners[i] = at->owners[--at->nowners];
+      else
+	i++;
+    }
+}

Added: trunk/athena/lib/locker/src/liblocker.pc.in
===================================================================
--- trunk/athena/lib/locker/src/liblocker.pc.in	                        (rev 0)
+++ trunk/athena/lib/locker/src/liblocker.pc.in	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @PACKAGE@
+Description: Project Athena's lockers library
+Version: @PACKAGE_VERSION@
+Requires: hesiod zephyr
+Libs: -L${libdir} -l@PACKAGE_NAME@
+Libs.private: @KRB5_LIBS@ @HESIOD_LIBS@ @ZEPHYR_LIBS@
+Cflags: -I${includedir}

Copied: trunk/athena/lib/locker/src/locker.h (from rev 25923, trunk/athena/lib/locker/locker.h)
===================================================================
--- trunk/athena/lib/locker/src/locker.h	                        (rev 0)
+++ trunk/athena/lib/locker/src/locker.h	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,212 @@
+/* $Id: locker.h,v 1.5 2002-10-17 05:20:07 ghudson Exp $ */
+
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#define LOCKER_SUCCESS		0	/* Success */
+
+/* Strictly internal errors */
+#define LOCKER_EFILE		-1	/* from locker__read_line */
+#define LOCKER_EOF		1	/* from locker__read_line */
+#define LOCKER_ENOENT		2	/* No such file or directory. */
+
+/* Exported errors */
+#define LOCKER_EATTACHTAB	3	/* Error reading attachtab. */
+#define LOCKER_EHESIOD		4	/* Unexpected Hesiod error. */
+#define LOCKER_ENOMEM		5	/* Out of memory. */
+#define LOCKER_EPARSE		6	/* Could not parse fs description. */
+#define LOCKER_EPERM		7	/* Permission denied. */	
+#define LOCKER_EUNKNOWN		8	/* Unknown locker. */
+
+#define LOCKER_EALREADY		9	/* Locker is already attached. */
+#define LOCKER_ENOTATTACHED	10	/* Locker is not attached. */
+
+#define LOCKER_EATTACH		11	/* Could not attach locker. */
+#define LOCKER_EATTACHCONF	12	/* Error reading attach.conf. */
+#define LOCKER_EAUTH		13	/* Could not authenticate. */
+#define LOCKER_EBADPATH		14	/* Unsafe path for mountpoint. */
+#define LOCKER_EDETACH		15	/* Could not detach locker. */
+#define LOCKER_EINUSE		16	/* Locker in use: not detached. */
+#define LOCKER_EMOUNTPOINT	17	/* Couldn't build mountpoint. */
+#define LOCKER_EMOUNTPOINTBUSY	18	/* Another locker is mounted there. */
+#define LOCKER_EZEPHYR		19	/* Zephyr-related error. */
+
+#define LOCKER_ATTACH_SUCCESS(stat) (stat == LOCKER_SUCCESS || stat == LOCKER_EALREADY)
+#define LOCKER_DETACH_SUCCESS(stat) (stat == LOCKER_SUCCESS || stat == LOCKER_ENOTATTACHED)
+#define LOCKER_LOOKUP_FAILURE(stat) (stat >= LOCKER_ENOENT && stat <= LOCKER_EUNKNOWN)
+
+/* Global context */
+typedef struct locker_context *locker_context;
+typedef int (*locker_error_fun)(void *, char *, va_list);
+
+struct locker_ops;
+
+/* The attachtab directory and entries */
+
+typedef struct locker_attachent {
+  /* Data from Hesiod (or other source) */
+  char *name, *mountpoint;
+  struct locker_ops *fs;
+  struct in_addr hostaddr;
+  char *hostdir;
+  int mode;
+
+  /* Additional data kept in the attachtab file for attached lockers */
+  int flags;
+  int nowners;
+  uid_t *owners;
+
+  /* Is the locker attached? */
+  int attached;
+  /* If the mountpoint doesn't exist, where do we start building it from? */
+  char *buildfrom;
+
+  /* Filesystem state */
+  FILE *mountpoint_file;
+  int dirlockfd;
+
+  /* Chaining for MUL lockers */
+  struct locker_attachent *next;
+} locker_attachent;
+
+/* struct locker_ops contains the pointers to filesystem-specific code
+ * and data. */
+struct locker_ops {
+  char *name;
+  int flags;
+  int (*parse)(locker_context context, char *name, char *desc,
+	       char *mountpoint, locker_attachent **atp);
+  int (*attach)(locker_context context, locker_attachent *at,
+		char *mountoptions);
+  int (*detach)(locker_context context, locker_attachent *at);
+  int (*auth)(locker_context context, locker_attachent *at,
+	      int mode, int op);
+  int (*zsubs)(locker_context context, locker_attachent *at);
+
+  /* Set by locker_init. */
+  long id;
+};
+
+/* locker_ops flags */
+#define LOCKER_FS_NEEDS_MOUNTDIR	(1 << 0)
+
+/* Attachent flags */
+#define LOCKER_FLAG_LOCKED		(1 << 0)
+#define LOCKER_FLAG_KEEP		(1 << 1)
+#define LOCKER_FLAG_NOSUID		(1 << 2)
+#define LOCKER_FLAG_NAMEFILE		(1 << 3)
+
+/* Attach / Detach options */
+#define LOCKER_ATTACH_OPT_OVERRIDE		(1 << 0)
+#define LOCKER_ATTACH_OPT_LOCK			(1 << 1)
+#define LOCKER_ATTACH_OPT_ALLOW_SETUID		(1 << 2)
+#define LOCKER_ATTACH_OPT_ZEPHYR		(1 << 3)
+#define LOCKER_ATTACH_OPT_REAUTH		(1 << 4)
+#define LOCKER_ATTACH_OPT_MASTER		(1 << 5)
+
+#define LOCKER_ATTACH_DEFAULT_OPTIONS ( LOCKER_ATTACH_OPT_REAUTH | LOCKER_ATTACH_OPT_ZEPHYR )
+
+#define LOCKER_DETACH_OPT_OVERRIDE		(1 << 0)
+#define LOCKER_DETACH_OPT_UNLOCK		(1 << 1)
+#define LOCKER_DETACH_OPT_UNZEPHYR		(1 << 2)
+#define LOCKER_DETACH_OPT_UNAUTH		(1 << 3)
+#define LOCKER_DETACH_OPT_OWNERCHECK		(1 << 4)
+#define LOCKER_DETACH_OPT_CLEAN			(1 << 5)
+
+#define LOCKER_DETACH_DEFAULT_OPTIONS ( LOCKER_DETACH_OPT_UNAUTH | LOCKER_DETACH_OPT_UNZEPHYR )
+
+/* Authentication modes */
+#define LOCKER_AUTH_DEFAULT 0
+#define LOCKER_AUTH_NONE 'n'
+#define LOCKER_AUTH_READONLY 'r'
+#define LOCKER_AUTH_READWRITE 'w'
+#define LOCKER_AUTH_MAYBE_READWRITE 'm'
+
+/* Authentication ops. These numbers cannot be changed: they
+ * correspond to the corresponding RPC mount call procedure numbers.
+ */
+enum { LOCKER_AUTH_AUTHENTICATE = 7, LOCKER_AUTH_UNAUTHENTICATE = 8,
+       LOCKER_AUTH_PURGE = 9, LOCKER_AUTH_PURGEUSER = 10 };
+
+/* Zephyr ops */
+enum { LOCKER_ZEPHYR_SUBSCRIBE, LOCKER_ZEPHYR_UNSUBSCRIBE };
+
+
+/* Callback function */
+typedef int (*locker_callback)(locker_context, locker_attachent *, void *);
+
+
+/* Context operations */
+int locker_init(locker_context *context, uid_t user,
+		locker_error_fun errfun, void *errdata);
+void locker_end(locker_context context);
+
+/* Attachtab operations */
+int locker_read_attachent(locker_context context, char *name,
+			  locker_attachent **atp);
+int locker_iterate_attachtab(locker_context context,
+			     locker_callback test, void *testarg,
+			     locker_callback act, void *actarg);
+void locker_free_attachent(locker_context context, locker_attachent *at);
+
+int locker_check_owner(locker_context context, locker_attachent *at,
+		       void *ownerp);
+int locker_check_host(locker_context context, locker_attachent *at,
+		       void *addrp);
+int locker_convert_attachtab(locker_context context, char *oattachtab);
+
+/* Attaching lockers */
+int locker_attach(locker_context context, char *filesystem,
+		  char *mountpoint, int auth, int options,
+		  char *mountoptions, locker_attachent **atp);
+int locker_attach_explicit(locker_context context, char *type,
+			   char *desc, char *mountpoint, int auth, int options,
+			   char *mountoptions, locker_attachent **atp);
+
+int locker_attach_attachent(locker_context context, locker_attachent *at,
+			    int auth, int options, char *mountoptions);
+
+/* Detaching lockers */
+int locker_detach(locker_context context, char *filesystem,
+		  char *mountpoint, int options, locker_attachent **atp);
+int locker_detach_explicit(locker_context context, char *type,
+			   char *desc, char *mountpoint, int options,
+			   locker_attachent **atp);
+
+int locker_detach_attachent(locker_context context, locker_attachent *at,
+			    int options);
+
+/* Other locker ops */
+int locker_auth(locker_context context, char *filesystem, int op);
+int locker_auth_to_cell(locker_context context, char *name, char *cell,
+			int op);
+int locker_auth_to_host(locker_context context, char *name, char *host,
+			int op);
+
+int locker_zsubs(locker_context context, char *filesystem);
+
+/* Lookup */
+int locker_lookup_filsys(locker_context context, char *name, char ***descs,
+			  void **cleanup);
+void locker_free_filesys(locker_context context, char **descs, void *cleanup);
+
+/* Zephyr */
+int locker_do_zsubs(locker_context context, int op);

Copied: trunk/athena/lib/locker/src/locker_private.h (from rev 25923, trunk/athena/lib/locker/locker_private.h)
===================================================================
--- trunk/athena/lib/locker/src/locker_private.h	                        (rev 0)
+++ trunk/athena/lib/locker/src/locker_private.h	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,140 @@
+/* $Id: locker_private.h,v 1.8 2006-08-08 21:50:10 ghudson Exp $ */
+
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+#include <stdarg.h>
+
+#define LOCKER_PATH_ATTACHTAB "/var/run/attachtab"
+#define LOCKER_PATH_ATTACH_CONF "/etc/athena/attach.conf"
+#define LOCKER_PATH_LOCAL "/var/athena/local-validated"
+
+#define LOCKER_AFS_MOUNT_DIR "/mit"
+#define LOCKER_UFS_MOUNT_DIR "/mnt"
+#define LOCKER_LOC_MOUNT_DIR "/mit"
+
+#define LOCKER_NFS_KSERVICE "rvdsrv"
+
+#define LOCKER_DEFAULT_GID 101
+
+#define LOCKER_MOUNT_TIMEOUT 20
+
+#define LOCKER_ZEPHYR_CLASS "filsrv"
+
+enum explstate { LOCKER_DONT_CARE, LOCKER_EXPLICIT, LOCKER_NOEXPLICIT };
+
+union locker__datum {
+  char *string;
+  int flag;
+};
+
+struct locker__regexp {
+  int fstypes;
+  enum explstate explicit;
+  regex_t pattern;
+  union locker__datum data;
+};
+
+struct locker__regexp_list {
+  struct locker__regexp *tab;
+  int size, num;
+  int defflag;
+};
+
+struct locker_context {
+  /* attach.conf variables */
+  int exp_desc, exp_mountpoint, keep_mount, nfs_root_hack, ownercheck;
+  int use_krb4;
+  char *afs_mount_dir, *attachtab, *nfs_mount_dir, *local_dir;
+  struct locker__regexp_list allow, setuid, mountpoint;
+  struct locker__regexp_list allowopts, defopts, filesystem, reqopts;
+
+  /* Defined fs types */
+  struct locker_ops **fstype;
+  int nfstypes;
+
+  /* error reporting function and its cookie */
+  locker_error_fun errfun;
+  void *errdata;
+
+  /* user we're acting on behalf of, and whether or not s/he is trusted */
+  uid_t user;
+  int trusted;
+
+  /* Hesiod context */
+  void *hes_context;
+
+  /* Queued zephyr subs */
+  char **zsubs;
+  int nzsubs;
+
+  /* Number of locks held on attachtab */
+  int locks;
+};
+
+/* "kind"s for locker__attachtab_pathname */
+#define LOCKER_LOCK 0
+#define LOCKER_NAME 1
+#define LOCKER_MOUNTPOINT 2
+#define LOCKER_DIRECTORY 3
+/* Already a full path: don't touch it. */
+#define LOCKER_FULL_PATH 4
+
+/* options for locker__canonicalize_path */
+#define LOCKER_CANON_CHECK_NONE 0
+#define LOCKER_CANON_CHECK_MOST 1
+#define LOCKER_CANON_CHECK_ALL 2
+
+/* Prototypes from attachtab.c */
+locker_attachent *locker__new_attachent(locker_context context,
+					struct locker_ops *type);
+int locker__lookup_attachent(locker_context context, char *name,
+			     char *mountpoint, int create,
+			     locker_attachent **atp);
+int locker__lookup_attachent_explicit(locker_context context, char *type,
+				      char *desc, char *mountpoint,
+				      int create, locker_attachent **atp);
+void locker__update_attachent(locker_context context, locker_attachent *at);
+char *locker__attachtab_pathname(locker_context context,
+				 int kind, char *name);
+
+/* Prototypes from conf.c */
+int locker__fs_ok(locker_context context, struct locker__regexp_list list,
+		  struct locker_ops *fs, char *filesystem);
+char *locker__fs_data(locker_context context, struct locker__regexp_list list,
+		      struct locker_ops *fs, char *filesystem);
+struct locker_ops *locker__get_fstype(locker_context context, char *fstype);
+void locker__error(locker_context context, char *fmt, ...);
+
+/* Prototypes from mount.c */
+int locker__mount(locker_context context, locker_attachent *at,
+		  char *mountoptions);
+int locker__unmount(locker_context context, locker_attachent *at);
+
+/* Prototypes from mountpoint.c */
+int locker__canonicalize_path(locker_context context, int check,
+			      char **pathp, char **extp);
+int locker__build_mountpoint(locker_context context, locker_attachent *at);
+int locker__remove_mountpoint(locker_context context, locker_attachent *at);
+void locker__put_mountpoint(locker_context context, locker_attachent *at);
+
+/* Prototypes from util.c */
+int locker__read_line(FILE *fp, char **buf, int *bufsize);
+
+/* Prototypes from zephyr.c */
+int locker__add_zsubs(locker_context context, char **subs, int nsubs);
+void locker__free_zsubs(locker_context context);

Copied: trunk/athena/lib/locker/src/misc.c (from rev 25923, trunk/athena/lib/locker/misc.c)
===================================================================
--- trunk/athena/lib/locker/src/misc.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/misc.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,143 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It deals with miscellaneous
+ * public locker operations besides attaching and detaching.
+ */
+
+static const char rcsid[] = "$Id: misc.c,v 1.3 1999-10-30 19:32:51 danw Exp $";
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hesiod.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+int locker_auth(locker_context context, char *filesystem, int op)
+{
+  int status;
+  locker_attachent *at;
+
+  status = locker_read_attachent(context, filesystem, &at);
+  if (status)
+    return status;
+
+  status = at->fs->auth(context, at, LOCKER_AUTH_DEFAULT, op);
+
+  locker_free_attachent(context, at);
+  return status;
+}
+
+int locker_zsubs(locker_context context, char *filesystem)
+{
+  int status;
+  locker_attachent *at;
+
+  status = locker_read_attachent(context, filesystem, &at);
+  if (status)
+    return status;
+
+  status = at->fs->zsubs(context, at);
+
+  locker_free_attachent(context, at);
+  return status;
+}
+
+/* See if the owner list for the locker contains the uid pointed to by
+ * ownerp. (For use with locker_iterate_attachtab.)
+ */
+int locker_check_owner(locker_context context, locker_attachent *at,
+		       void *ownerp)
+{
+  uid_t owner;
+  int i;
+
+  owner = *(uid_t *)ownerp;
+
+  for (i = 0; i < at->nowners; i++)
+    {
+      if (at->owners[i] == owner)
+	return 1;
+    }
+  return 0;
+}
+
+/* See if the locker is on the host pointed to by addrp. If *addrp is
+ * 0.0.0.0, always returns true. (For use with
+ * locker_iterate_attachtab.)
+ */
+int locker_check_host(locker_context context, locker_attachent *at,
+		      void *addrp)
+{
+  struct in_addr *addr = addrp;
+  return !addr->s_addr || at->hostaddr.s_addr == addr->s_addr;
+}
+
+
+/* Look up a locker description (in Hesiod or attach.conf). */
+int locker_lookup_filsys(locker_context context, char *name, char ***descs,
+			  void **cleanup)
+{
+  char *conffs;
+
+  /* Look for a locker defined in attach.conf. */
+  conffs = locker__fs_data(context, context->filesystem, NULL, name);
+  if (conffs)
+    {
+      *descs = malloc(2 * sizeof(char *));
+      if (!*descs)
+	{
+	  locker__error(context, "Out of memory in looking up filesystem.\n");
+	  return LOCKER_ENOMEM;
+	}
+      (*descs)[0] = conffs;
+      (*descs)[1] = NULL;
+      *cleanup = NULL;
+
+      return LOCKER_SUCCESS;
+    }
+  else
+    {
+      /* Otherwise, look for a locker in Hesiod--which might be an fsgroup. */
+      *descs = hesiod_resolve(context->hes_context, name, "filsys");
+      if (!*descs && errno == ENOENT)
+	{
+	  locker__error(context, "%s: Locker unknown.\n", name);
+	  return LOCKER_EUNKNOWN;
+	}
+      if (!*descs || !**descs)
+	{
+	  if (*descs)
+	    hesiod_free_list(context->hes_context, *descs);
+	  locker__error(context, "%s: Could not look up Hesiod entry: %s.\n",
+			name, strerror(errno));
+	  return LOCKER_EHESIOD;
+	}
+
+      *cleanup = context->hes_context;
+      return LOCKER_SUCCESS;
+    }
+}
+
+void locker_free_filesys(locker_context context, char **descs, void *cleanup)
+{
+  if (cleanup)
+    hesiod_free_list(cleanup, descs);
+  else
+    free(descs);
+}

Copied: trunk/athena/lib/locker/src/miscfs.c (from rev 25923, trunk/athena/lib/locker/miscfs.c)
===================================================================
--- trunk/athena/lib/locker/src/miscfs.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/miscfs.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,407 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements UFS and ERR lockers. */
+
+static const char rcsid[] = "$Id: miscfs.c,v 1.5 2006-07-25 23:29:09 ghudson Exp $";
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+static int ufs_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp);
+static int ufs_auth(locker_context context, locker_attachent *at,
+		    int mode, int op);
+static int ufs_zsubs(locker_context context, locker_attachent *at);
+
+struct locker_ops locker__ufs_ops = {
+  "UFS",
+  LOCKER_FS_NEEDS_MOUNTDIR,
+  ufs_parse,
+  locker__mount,
+  locker__unmount,
+  ufs_auth,
+  ufs_zsubs
+};
+
+static int loc_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp);
+static int loc_attach(locker_context context, locker_attachent *at,
+		      char *mountoptions);
+static int loc_detach(locker_context context, locker_attachent *at);
+static int loc_auth(locker_context context, locker_attachent *at,
+		    int mode, int op);
+static int loc_zsubs(locker_context context, locker_attachent *at);
+
+struct locker_ops locker__loc_ops = {
+  "LOC",
+  0,
+  loc_parse,
+  loc_attach,
+  loc_detach,
+  loc_auth,
+  loc_zsubs
+};
+
+static int err_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **at);
+
+struct locker_ops locker__err_ops = {
+  "ERR",
+  0,
+  err_parse,
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+
+static int ufs_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp)
+{
+  locker_attachent *at;
+  char *p, *dup = NULL, *lasts = NULL;
+  int status;
+
+  at = locker__new_attachent(context, &locker__ufs_ops);
+  if (!at)
+    return LOCKER_ENOMEM;
+
+  if (!name)
+    {
+      at->name = strdup(desc);
+      at->hostdir = strdup(desc);
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	at->mountpoint = strdup(LOCKER_UFS_MOUNT_DIR);
+      at->mode = LOCKER_AUTH_READWRITE;
+
+      if (!at->name || !at->hostdir || !at->mountpoint)
+	goto mem_error;
+    }
+  else
+    {
+      /* A Hesiod UFS description (if we had any) would look like:
+       * UFS /dev/dsk/c0t0d0s2 w /u1
+       */
+
+      at->name = strdup(name);
+      if (!at->name)
+	goto mem_error;
+
+      dup = strdup(desc);
+      if (!dup)
+	goto mem_error;
+
+      /* Skip "UFS". */
+      if (!strtok_r(dup, " ", &lasts))
+	goto parse_error;
+
+      /* Hostdir */
+      at->hostdir = strtok_r(NULL, " ", &lasts);
+      if (!at->hostdir)
+	goto parse_error;
+      at->hostdir = strdup(at->hostdir);
+      if (!at->hostdir)
+	goto mem_error;
+
+      /* Mount mode */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p || *(p + 1))
+	goto parse_error;
+
+      switch (*p)
+	{
+	case 'r':
+	  at->mode = LOCKER_AUTH_READONLY;
+	  break;
+	case 'w':
+	  at->mode = LOCKER_AUTH_READWRITE;
+	  break;
+	case 'n':
+	  at->mode = LOCKER_AUTH_READWRITE;
+	  break;
+	default:
+	  locker__error(context, "%s: Unrecognized mount option '%c' in "
+			"description:\n%s\n", name, *p, desc);
+	  status = LOCKER_EPARSE;
+	  goto cleanup;
+	}
+
+      /* Mountpoint */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p)
+	goto parse_error;
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	at->mountpoint = strdup(at->mountpoint);
+      if (!at->mountpoint)
+	goto mem_error;
+
+      free(dup);
+      dup = NULL;
+    }
+
+  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_ALL,
+				     &(at->mountpoint), &(at->buildfrom));
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+
+  *atp = at;
+  return LOCKER_SUCCESS;
+
+mem_error:
+  locker__error(context, "Out of memory parsing locker description.\n");
+  status = LOCKER_ENOMEM;
+  goto cleanup;
+
+parse_error:
+  locker__error(context, "Could not parse locker description "
+		"\"%s\".\n", desc);
+  status = LOCKER_EPARSE;
+
+cleanup:
+  free(dup);
+  locker_free_attachent(context, at);
+  return status;
+}
+
+static int ufs_auth(locker_context context, locker_attachent *at,
+		    int mode, int op)
+{
+  return LOCKER_SUCCESS;
+}
+
+static int ufs_zsubs(locker_context context, locker_attachent *at)
+{
+  return LOCKER_SUCCESS;
+}
+
+
+static int loc_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp)
+{
+  locker_attachent *at;
+  char *p, *dup = NULL, *lasts = NULL;
+  int status;
+
+  at = locker__new_attachent(context, &locker__loc_ops);
+  if (!at)
+    return LOCKER_ENOMEM;
+
+  if (!name)
+    {
+      at->name = strdup(desc);
+      at->hostdir = strdup(desc);
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	at->mountpoint = strdup(LOCKER_LOC_MOUNT_DIR);
+      at->mode = LOCKER_AUTH_READWRITE;
+
+      if (!at->name || !at->hostdir || !at->mountpoint)
+	goto mem_error;
+    }
+  else
+    {
+      /* A Hesiod LOC description (if we had any) would look like:
+       * LOC /u1/lockers/sipb w /mit/sipb
+       */
+
+      at->name = strdup(name);
+      if (!at->name)
+	goto mem_error;
+
+      dup = strdup(desc);
+      if (!dup)
+	goto mem_error;
+
+      /* Skip "LOC". */
+      if (!strtok_r(dup, " ", &lasts))
+	goto parse_error;
+
+      /* Hostdir */
+      at->hostdir = strtok_r(NULL, " ", &lasts);
+      if (!at->hostdir)
+	goto parse_error;
+      at->hostdir = strdup(at->hostdir);
+      if (!at->hostdir)
+	goto mem_error;
+
+      /* Mount mode */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p || *(p + 1))
+	goto parse_error;
+
+      switch (*p)
+	{
+	case 'r':
+	case 'w':
+	case 'n':
+	  at->mode = LOCKER_AUTH_NONE;
+	  break;
+	default:
+	  locker__error(context, "%s: Unrecognized mount option '%c' in "
+			"description:\n%s\n", name, *p, desc);
+	  status = LOCKER_EPARSE;
+	  goto cleanup;
+	}
+
+      /* Mountpoint */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p)
+	goto parse_error;
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	at->mountpoint = strdup(p);
+      if (!at->mountpoint)
+	goto mem_error;
+
+      free(dup);
+      dup = NULL;
+    }
+
+  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_ALL,
+				     &(at->mountpoint), &(at->buildfrom));
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+
+  *atp = at;
+  return LOCKER_SUCCESS;
+
+mem_error:
+  locker__error(context, "Out of memory parsing locker description.\n");
+  status = LOCKER_ENOMEM;
+  goto cleanup;
+
+parse_error:
+  locker__error(context, "Could not parse locker description "
+		"\"%s\".\n", desc);
+  status = LOCKER_EPARSE;
+
+cleanup:
+  free(dup);
+  locker_free_attachent(context, at);
+  return status;
+}
+
+static int loc_attach(locker_context context, locker_attachent *at,
+		      char *mountoptions)
+{
+  struct stat st1, st2;
+  uid_t uid = geteuid();
+  int status;
+
+  /* Make sure user can read the destination, and it's a directory. */
+  if (uid != context->user)
+    seteuid(context->user);
+  status = lstat(at->hostdir, &st1);
+  if (uid != context->user)
+    seteuid(uid);
+
+  if (status == -1)
+    {
+      locker__error(context, "%s: Could not attach locker:\n%s for %s\n",
+			at->name, strerror(errno), at->hostdir);
+      return LOCKER_EATTACH;
+    }
+  if (!S_ISDIR(st1.st_mode) && !S_ISLNK(st1.st_mode))
+    {
+      locker__error(context, "%s: Could not attach locker:\n"
+		    "%s is not a directory.\n", at->name, at->hostdir);
+      return LOCKER_EATTACH;
+    }
+
+  /* Make sure nothing is already mounted on our mountpoint. */
+  status = stat(at->mountpoint, &st2);
+  if (!status && st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
+    {
+      locker__error(context, "%s: Locker is already attached.\n",
+		    at->name);
+      return LOCKER_EALREADY;
+    }
+  else if (!status || errno != ENOENT)
+    {
+      locker__error(context, "%s: Could not attach locker:\n"
+		    "Mountpoint %s is busy.\n", at->name, at->mountpoint);
+      return LOCKER_EMOUNTPOINTBUSY;
+    }
+
+  status = symlink(at->hostdir, at->mountpoint);
+  if (status < 0)
+    {
+      locker__error(context, "%s: Could not attach locker:\n%s while "
+		    "symlinking %s to %s\n", at->name, strerror(errno),
+		    at->hostdir, at->mountpoint);
+      return LOCKER_EATTACH;
+    }
+
+  return LOCKER_SUCCESS;
+}
+
+static int loc_detach(locker_context context, locker_attachent *at)
+{
+  int status;
+
+  status = unlink(at->mountpoint);
+  if (status < 0)
+    {
+      if (errno == ENOENT)
+	{
+	  locker__error(context, "%s: Locker is not attached.\n", at->name);
+	  return LOCKER_ENOTATTACHED;
+	}
+      else
+	{
+	  locker__error(context, "%s: Could not detach locker:\n%s while "
+			"trying to unlink %s.\n", at->name, strerror(errno),
+			at->mountpoint);
+	  return LOCKER_EDETACH;
+	}
+    }
+  return LOCKER_SUCCESS;
+}
+
+static int loc_auth(locker_context context, locker_attachent *at,
+		    int mode, int op)
+{
+  return LOCKER_SUCCESS;
+}
+
+static int loc_zsubs(locker_context context, locker_attachent *at)
+{
+  return LOCKER_SUCCESS;
+}
+
+
+static int err_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp)
+{
+  if (name)
+    locker__error(context, "%s: %s\n", name, desc + 4);
+  else
+    locker__error(context, "%s\n", desc);
+  return LOCKER_EATTACH;
+}

Copied: trunk/athena/lib/locker/src/mount.c (from rev 25923, trunk/athena/lib/locker/mount.c)
===================================================================
--- trunk/athena/lib/locker/src/mount.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/mount.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,260 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements routines to
+ * mount and unmount system-recognized filesystems.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <sys/wait.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+static const char rcsid[] = "$Id: mount.c,v 1.3 1999-05-19 14:19:49 danw Exp $";
+
+static int domount(locker_context context, int mount,
+		   locker_attachent *at, char **argv);
+static void cancel_mount(int sig);
+static volatile int cancel;
+
+
+int locker__mount(locker_context context, locker_attachent *at,
+		  char *mountoptions)
+{
+  char *argv[6];
+
+  argv[0] = MOUNT_CMD;
+  argv[1] = "-o";
+  argv[2] = mountoptions;
+  argv[3] = at->hostdir;
+  argv[4] = at->mountpoint;
+  argv[5] = NULL;
+  return domount(context, 1, at, argv);
+}
+
+int locker__unmount(locker_context context, locker_attachent *at)
+{
+  char *argv[3];
+  int status;
+
+  argv[0] = UMOUNT_CMD;
+  argv[1] = at->mountpoint;
+  argv[2] = NULL;
+  status = domount(context, 0, at, argv);
+
+  if (status == LOCKER_EDETACH)
+    {
+      /* Check if the detach failed because the filesystem was already
+       * unmounted. (By seeing if the mountpoint directory isn't
+       * there, or if its st_dev matches its parent's st_dev.)
+       */
+      struct stat st1, st2;
+      char *parent, *p;
+
+      if (stat(at->mountpoint, &st1) == -1 && errno == ENOENT)
+	return LOCKER_SUCCESS;
+      parent = strdup(at->mountpoint);
+      if (!parent)
+	return LOCKER_EDETACH;
+
+      p =  strrchr(parent, '/');
+      if (!p)
+	{
+	  free(parent);
+	  return LOCKER_EDETACH;
+	}
+      if (p == parent)
+	p++;
+      *p = '\0';
+      status = stat(parent, &st2);
+      free(parent);
+      if (status == -1)
+	return LOCKER_EDETACH;
+
+      if (st1.st_dev == st2.st_dev)
+	return LOCKER_SUCCESS;
+      else
+	return LOCKER_EDETACH;
+    }
+  else
+    return status;
+}
+
+static int domount(locker_context context, int mount,
+		   locker_attachent *at, char **argv)
+{
+  pid_t pid;
+  int status, pstat, pipefds[2], bufsiz, nread, new, oldalarm;
+  char *buf = NULL;
+  sigset_t omask, mask;
+  struct sigaction sa, sa_chld, sa_int, sa_alrm;
+
+  if (pipe(pipefds) == -1)
+    {
+      locker__error(context, "%s: Could not open pipe for mount: %s.\n",
+		    at->name, strerror(errno));
+      return mount ? LOCKER_EATTACH : LOCKER_EDETACH;
+    }
+
+  /* If the liblocker caller has set SIGCHLD's handler to SIG_IGN,
+   * waitpid won't work. So we set its handler to SIG_DFL after
+   * blocking it. This makes waitpid work but still prevents the
+   * caller from seeing SIGCHLDs.
+   */
+  sigemptyset(&mask);
+  sigaddset(&mask, SIGCHLD);
+  sigprocmask(SIG_BLOCK, &mask, &omask);
+
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sa.sa_handler = SIG_DFL;
+  sigaction(SIGCHLD, &sa, &sa_chld);
+
+  /* Set SIGINT and SIGALRM to interrupt us nicely and then unblock
+   * them.
+   */
+  cancel = 0;
+  sa.sa_handler = cancel_mount;
+  sigaction(SIGINT, &sa, &sa_int);
+  sigaction(SIGALRM, &sa, &sa_alrm);
+  oldalarm = alarm(LOCKER_MOUNT_TIMEOUT);
+
+  sigdelset(&mask, SIGALRM);
+  sigdelset(&mask, SIGINT);
+  sigprocmask(SIG_SETMASK, &mask, NULL);
+
+  switch (pid = fork())
+    {
+    case -1:
+      close(pipefds[0]);
+      close(pipefds[1]);
+      if (errno == ENOMEM)
+	{
+	  locker__error(context, "Not enough memory to fork.\n");
+	  status = LOCKER_ENOMEM;
+	  goto cleanup;
+	}
+      else
+	{
+	  locker__error(context, "%s: Could not fork to exec %s: %s.\n",
+			at->name, argv[0], strerror(errno));
+	  status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
+	  goto cleanup;
+	}
+
+    case 0:
+      setuid(0);
+      close(pipefds[0]);
+      close(STDOUT_FILENO);
+      close(STDERR_FILENO);
+      dup2(pipefds[1], STDOUT_FILENO);
+      dup2(pipefds[1], STDERR_FILENO);
+
+      execv(argv[0], argv);
+      exit(1);
+
+    default:
+      close(pipefds[1]);
+    }
+
+  /* Need to read the data out of the pipe first, in case mount blocks
+   * writing to it before it can exit.
+   */
+  bufsiz = nread = 0;
+
+  new = -1;
+  while (new != 0 && !cancel)
+    {
+      char *nbuf;
+
+      bufsiz += 1024;
+      nbuf = realloc(buf, bufsiz);
+      if (nbuf)
+	{
+	  buf = nbuf;
+	  new = read(pipefds[0], buf + nread, 1023);
+	}
+
+      if (!nbuf || new == -1)
+	{
+	  /* We can't finish reading the buffer, but we need to wait for
+	   * the child to exit.
+	   */
+	  free(buf);
+	  buf = NULL;
+	  break;
+	}
+      else
+	nread += new;
+    }
+  close(pipefds[0]);
+  if (buf)
+    buf[nread] = '\0';
+
+  do
+    {
+      if (cancel)
+	kill(pid, SIGKILL);
+    }
+  while (waitpid(pid, &pstat, 0) < 0 && errno == EINTR);
+
+  if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
+    status = LOCKER_SUCCESS;
+  else if (cancel == SIGALRM)
+    {
+      locker__error(context, "%s: %s timed out.\n", at->name,
+		    mount ? "Mount" : "Unmount");
+      status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
+    }
+  else if (cancel == SIGINT)
+    {
+      locker__error(context, "%s: %s interrupted by user.\n", at->name,
+		    mount ? "Mount" : "Unmount");
+      status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
+    }
+  else
+    {
+      locker__error(context, "%s: %s failed:\n%s", at->name,
+		    mount ? "Mount" : "Unmount",
+		    buf ? buf : "Unknown error.\n");
+      status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
+    }
+
+cleanup:
+  free(buf);
+  sigaction(SIGCHLD, &sa_chld, NULL);
+  sigaction(SIGINT, &sa_int, NULL);
+  alarm(0);
+  sigaction(SIGALRM, &sa_alrm, NULL);
+  sigprocmask(SIG_SETMASK, &omask, NULL);
+  alarm(oldalarm);
+
+  return status;
+}
+
+static void cancel_mount(int sig)
+{
+  cancel = sig;
+}

Copied: trunk/athena/lib/locker/src/mountpoint.c (from rev 25923, trunk/athena/lib/locker/mountpoint.c)
===================================================================
--- trunk/athena/lib/locker/src/mountpoint.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/mountpoint.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,613 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It deals with canonicalizing and
+ * creating mountpoints, and the associated security issues.
+ */
+
+static const char rcsid[] = "$Id: mountpoint.c,v 1.11 2001-08-16 14:27:52 ghudson Exp $";
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+/* Include <sys/param.h> for MAXPATHLEN (and MAXSYMLINKS). We assume
+ * that if some future OS doesn't have a maximum path length then
+ * they'll also provide replacements for getcwd() and readlink() that
+ * don't require the caller to guess how large a buffer to provide.
+ */
+#include <sys/param.h>
+
+#define MODE_MASK (S_IRWXU | S_IRWXG | S_IRWXO)
+#define LOCKER_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+#define BAD_MODE_BITS (S_IWGRP | S_IWOTH)
+
+static int mountpoint_mkdir(locker_context context, locker_attachent *at,
+			    char *path);
+static int mountpoint_rmdir(locker_context context, locker_attachent *at,
+			    char *path);
+
+static int get_dirlock(locker_context context, locker_attachent *at,
+		       int type);
+static void release_dirlock(locker_context context, locker_attachent *at);
+
+/* Canonicalize a path and optionally make sure that it doesn't pass
+ * through any mountpoints or user-writable directories.  *pathp must
+ * contain an allocated string, which may be freed and replaced with
+ * another allocated string containing the canonicalized path.  If
+ * buildfromp is not NULL, *buildfromp is set to an allocated string
+ * containing the first ancestor directory of the canonicalized path
+ * which doesn't exist, or to NULL if the canonicalized path already
+ * exists.
+ */
+int locker__canonicalize_path(locker_context context, int check,
+			      char **pathp, char **buildfromp)
+{
+  char *path, *buildfrom = NULL, *cur, *end, *p;
+  struct stat st;
+  int nlinks = 0, status = 0;
+  dev_t last_dev = 0;
+  uid_t uid = geteuid();
+
+  path = *pathp;
+
+  /* If it doesn't start with '/', prepend cwd. */
+  if (path[0] != '/')
+    {
+      char cwdbuf[MAXPATHLEN], *newpath;
+
+      if (!getcwd(cwdbuf, sizeof(cwdbuf)))
+	{
+	  locker__error(context, "Could not retrieve current working "
+			"directory:\n%s.\n", strerror(errno));
+	  free(path);
+	  *pathp = NULL;
+	  return LOCKER_EBADPATH;
+	}
+      if (!strcmp(cwdbuf, "/"))
+	*cwdbuf = '\0';		/* Avoid generating "//foo". */
+
+      newpath = malloc(strlen(cwdbuf) + strlen(path) + 2);
+      if (!newpath)
+	{
+	  free(path);
+	  *pathp = NULL;
+	  locker__error(context, "Out of memory canonicalizing mountpoint.\n");
+	  return LOCKER_ENOMEM;
+	}
+
+      sprintf(newpath, "%s/%s", cwdbuf, path);
+      free(path);
+      path = newpath;
+
+      /* Don't need to canonicalize cwd part, so skip that. */
+      cur = path + strlen(cwdbuf) + 1;
+    }
+  else
+    cur = path + 1;
+
+  /* Remove trailing slash. */
+  p = strrchr(path, '/');
+  if (p && !*(p + 1) && p > path)
+    *p = '\0';
+
+  if (uid != context->user)
+    seteuid(context->user);
+
+  /* Expand symlinks. */
+  do
+    {
+      end = strchr(cur, '/');
+      if (end)
+	*end = '\0';
+
+      /* cur points to the character after a '/'. Everything before cur is
+       * canonicalized. If cur points to the final component, end is NULL.
+       * Otherwise, end points to a '\0' that is covering the next '/'
+       * after cur.
+       */
+
+      /* Handle special cases. */
+      if (!*cur || !strcmp(cur, "."))
+	{
+	  /* "." can't be final path component (since we might not have
+	   * wanted to resolve the final path component, and now it's
+	   * too late.
+	   */
+	  if (!end)
+	    {
+	      locker__error(context, "Path cannot end with \".\" (%s).\n",
+			    path);
+	      status = LOCKER_EBADPATH;
+	      goto cleanup;
+	    }
+
+	  /* Copy the next component over this one. */
+	  strcpy(cur, end + 1);
+	  continue;
+	}
+      else if (!strcmp(cur, ".."))
+	{
+	  /* ".." can't be final path component for similar reasons
+	   * to above.
+	   */
+	  if (!end)
+	    {
+	      locker__error(context, "%s: Path cannot end with \"..\" (%s).\n",
+			    path);
+	      status = LOCKER_EBADPATH;
+	      goto cleanup;
+	    }
+
+	  /* Remove this component and the previous one. */
+	  cur -= 2;
+	  while (cur > path && *cur != '/')
+	    cur--;
+
+	  if (cur < path)
+	    cur++;		/* "/.." == "/" */
+
+	  cur++;                /* Leave a trailing "/", because end just
+				 * got its leading "/" stripped off
+				 */
+
+	  /* Copy the next component over the previous one. */
+	  strcpy(cur, end + 1);
+	  continue;
+	}
+
+      /* Don't resolve the final component unless we need to check it. */
+      if (!end && (check != LOCKER_CANON_CHECK_ALL))
+	break;
+
+      /* Check if current component is a symlink. */
+      if (lstat(path, &st) < 0)
+	{
+	  if (errno == ENOENT)
+	    {
+	      /* The rest of the path doesn't exist. */
+	      buildfrom = strdup(path);
+	      if (!buildfrom)
+		{
+		  locker__error(context, "Out of memory canonicalizing "
+				"mountpoint.\n");
+		  status = LOCKER_ENOMEM;
+		  goto cleanup;
+		}
+	      if (end)
+		*end = '/';
+	      break;
+	    }
+	  else
+	    {
+	      locker__error(context, "Could not canonicalize path:\n"
+			    "%s for %s\n", strerror(errno), path);
+	      status = LOCKER_EBADPATH;
+	      goto cleanup;
+	    }
+	}
+      else if (!end && !S_ISDIR(st.st_mode))
+	{
+	  locker__error(context, "Final path component is not a directory "
+			"in \"%s\".\n", path);
+	  status = LOCKER_EBADPATH;
+	  goto cleanup;
+	}
+      else if (S_ISLNK(st.st_mode))
+	{
+	  char linkbuf[MAXPATHLEN], *newpath;
+	  int len;
+
+	  if (++nlinks > MAXSYMLINKS)
+	    {
+	      locker__error(context, "Too many levels of symlinks "
+			    "while canonicalizing path \"%s\".\n",
+			    path);
+	      status = LOCKER_EBADPATH;
+	      goto cleanup;
+	    }
+
+	  len = readlink(path, linkbuf, sizeof(linkbuf));
+	  if (len == -1)
+	    {
+	      locker__error(context, "Could not canonicalize mountpoint:\n"
+			    "%s while reading symlink %s\n", strerror(errno),
+			    path);
+	      status = LOCKER_EBADPATH;
+	      goto cleanup;
+	    }
+
+	  if (linkbuf[0] == '/')
+	    {
+	      /* It's absolute, so replace existing path with it. */
+	      newpath = malloc(len + strlen(end + 1) + 2);
+	      if (!newpath)
+		{
+		  locker__error(context, "Out of memory canonicalizing "
+				"mountpoint.\n");
+		  status = LOCKER_ENOMEM;
+		  goto cleanup;
+		}
+	      sprintf(newpath, "%.*s/%s", len, linkbuf, end + 1);
+	      free(path);
+	      path = newpath;
+	      /* And start over from the top. */
+	      cur = path + 1;
+	    }
+	  else
+	    {
+	      /* Add this in to existing path. */
+	      *cur = '\0';
+	      newpath = malloc(strlen(path) + len + strlen(end + 1) + 3);
+	      if (!newpath)
+		{
+		  locker__error(context, "Out of memory canonicalizing "
+				"mountpoint.\n");
+		  status = LOCKER_ENOMEM;
+		  goto cleanup;
+		}
+	      sprintf(newpath, "%s%.*s/%s", path, len, linkbuf, end + 1);
+	      /* cur is effectively unchanged. */
+	      cur = newpath + (cur - path);
+	      free(path);
+	      path = newpath;
+	    }
+	}
+      else
+	{
+	  if (check != LOCKER_CANON_CHECK_NONE)
+	    {
+	      /* Check that we can build in this directory. */
+	      if (last_dev && st.st_dev != last_dev)
+		{
+		  locker__error(context, "Cannot attach locker on %s:\n"
+				"directory %s is not on root filesystem.\n",
+				*pathp, path);
+		  status = LOCKER_EBADPATH;
+		  goto cleanup;
+		}
+	      else if (st.st_uid != 0)
+		{
+		  locker__error(context, "Cannot attach locker on %s:\n"
+				"directory %s is not owned by root.\n",
+				*pathp, path);
+		  status = LOCKER_EBADPATH;
+		  goto cleanup;
+		}
+	      else if (st.st_mode & BAD_MODE_BITS)
+		{
+		  locker__error(context, "Cannot attach locker on %s:\n"
+				"directory %s is group/other writable.\n",
+				*pathp, path);
+		  status = LOCKER_EBADPATH;
+		  goto cleanup;
+		}
+
+	      last_dev = st.st_dev;
+	    }
+
+	  /* Replace *end, update cur. */
+	  if (end)
+	    {
+	      *end = '/';
+	      cur = end + 1;
+	    }
+	}
+    }
+  while (end);
+
+  if (check != LOCKER_CANON_CHECK_NONE)
+    {
+      int len = strlen(context->attachtab);
+      if (!strncmp(context->attachtab, path, len) &&
+	  (path[len] == '\0' || path[len] == '/'))
+	{
+	  locker__error(context, "Cannot attach locker on %s:\n"
+			"directory passes through attachtab directory.\n",
+			*pathp, path);
+	  status = LOCKER_EBADPATH;
+	  goto cleanup;
+	}
+    }
+
+  /* Remove trailing slash. */
+  p = strrchr(path, '/');
+  if (p && !*(p + 1) && p > path)
+    *p = '\0';
+
+  *pathp = path;
+  if (buildfromp)
+    *buildfromp = buildfrom;
+  else
+    free(buildfrom);
+
+cleanup:
+  if (uid != context->user)
+    seteuid(uid);
+  if (status)
+    {
+      free(path);
+      *pathp = NULL;
+      free(buildfrom);
+    }
+  return status;
+}
+
+/* Create any directories needed to attach something to the named
+ * mountpoint. Record the fact that we created the directory so that
+ * we know to delete it later.
+ */
+int locker__build_mountpoint(locker_context context, locker_attachent *at)
+{
+  char *q;
+  int status;
+
+  status = get_dirlock(context, at, F_RDLCK);
+  if (status)
+    return status;
+
+  if (at->buildfrom)
+    {
+      /* Create any remaining directories. */
+      q = at->mountpoint + (strrchr(at->buildfrom, '/') - at->buildfrom);
+      while (!status)
+	{
+	  q = strchr(q + 1, '/');
+	  if (q)
+	    *q = '\0';
+	  else if (!(at->fs->flags & LOCKER_FS_NEEDS_MOUNTDIR))
+	    break;
+	  status = mountpoint_mkdir(context, at, at->mountpoint);
+	  if (q)
+	    *q = '/';
+	  else
+	    break;
+	}
+    }
+
+  /* We do not release the dirlock now, to guarantee that no one
+   * else deletes our directories before we're done mounting
+   * the filesystem. It will be released by locker_free_attachent().
+   */
+
+  return status;
+}
+
+/* Remove any directories we created as part of attaching a locker */
+int locker__remove_mountpoint(locker_context context, locker_attachent *at)
+{
+  char *p, *q;
+  int status;
+
+  p = strrchr(at->mountpoint, '/');
+
+  /* If no '/', it must be a MUL locker. */
+  if (!p)
+    return LOCKER_SUCCESS;
+
+  status = get_dirlock(context, at, F_WRLCK);
+  if (status)
+    return status;
+
+  status = mountpoint_rmdir(context, at, at->mountpoint);
+  if (status == LOCKER_ENOENT)
+    status = LOCKER_SUCCESS;
+  while (!status && (q = p) && (q != at->mountpoint))
+    {
+      *q = '\0';
+      p = strrchr(at->mountpoint, '/');
+      status = mountpoint_rmdir(context, at, at->mountpoint);
+      *q = '/';
+    }
+
+  release_dirlock(context, at);
+
+  if (status == LOCKER_ENOENT || status == LOCKER_EMOUNTPOINTBUSY)
+    return LOCKER_SUCCESS;
+  else
+    return status;
+}
+
+/* Unlock the mountpoint created by locker__build_mountpoint(). */
+void locker__put_mountpoint(locker_context context, locker_attachent *at)
+{
+  release_dirlock(context, at);
+}
+
+/* Lock the dirlock file. type should be F_RDLCK if you are creating
+ * directories and F_WRLCK if you are deleting them. A process that creates
+ * directories should hold onto the lock until it has actually finished
+ * trying to attach the locker, so that some other detach procedure doesn't
+ * remove those directories out from under it.
+ */
+static int get_dirlock(locker_context context, locker_attachent *at, int type)
+{
+  int status;
+  char *lock;
+  struct flock fl;
+  mode_t o_umask;
+
+  lock = locker__attachtab_pathname(context, LOCKER_LOCK, ".dirlock");
+  if (!lock)
+    return LOCKER_ENOMEM;
+
+  o_umask = umask(0);
+  at->dirlockfd = open(lock, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+  free(lock);
+  umask(o_umask);
+  if (at->dirlockfd < 0)
+    {
+      at->dirlockfd = 0;
+      locker__error(context, "%s: Could not %s mountpoint:\n"
+		    "%s while opening directory lock file.\n", at->name,
+		    at->attached ? "remove" : "create", strerror(errno));
+      return LOCKER_EATTACHTAB;
+    }
+  fl.l_type = type;
+  fl.l_whence = SEEK_SET;
+  fl.l_start = fl.l_len = 0;
+  status = fcntl(at->dirlockfd, F_SETLKW, &fl);
+  if (status < 0)
+    {
+      close(at->dirlockfd);
+      at->dirlockfd = 0;
+      locker__error(context, "%s: Could not %s mountpoint:\n"
+		    "%s while locking directory lock file.\n", at->name,
+		    at->attached ? "remove" : "create", strerror(errno));
+      return LOCKER_EATTACHTAB;
+    }
+
+  return LOCKER_SUCCESS;
+}
+
+static void release_dirlock(locker_context context, locker_attachent *at)
+{
+  close(at->dirlockfd);
+  at->dirlockfd = 0;
+}
+
+static int mountpoint_mkdir(locker_context context, locker_attachent *at,
+			    char *path)
+{
+  char *file;
+  struct stat st;
+  mode_t omask;
+  int status;
+
+  file = locker__attachtab_pathname(context, LOCKER_DIRECTORY, path);
+  if (!file)
+    {
+      locker__error(context, "Out of memory building mountpoint.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  if (access(file, F_OK) != 0 || lstat(path, &st) != 0)
+    {
+      int fd;
+
+      /* Need to create the dirfile and the directory. If we get killed
+       * in between the two steps, we'd rather have a dirfile that
+       * doesn't correspond to a directory than a directory that we
+       * don't know is our fault. So do the dirfile first.
+       */
+
+      fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+      if (fd == -1)
+	{
+	  free(file);
+	  locker__error(context, "Could not create directory data file "
+			"%s:\n%s.\n", file, strerror(errno));
+	  return LOCKER_EATTACHTAB;
+	}
+      close(fd);
+
+      /* Make directory. */
+      omask = umask(0);
+      status = mkdir(path, LOCKER_DIR_MODE);
+      umask(omask);
+      if (status == -1)
+	{
+	  if (errno == EEXIST)
+	    {
+	      locker__error(context, "%s: Directory should not exist: %s\n",
+			    at->name, path);
+	    }
+	  else
+	    {
+	      locker__error(context, "%s: Could not create directory %s:\n"
+			    "%s.\n", at->name, path, strerror(errno));
+	    }
+	  unlink(file);
+	  free(file);
+	  return LOCKER_EMOUNTPOINT;
+	}
+    }
+  else
+    {
+      if (!S_ISDIR(st.st_mode) || st.st_uid != 0 ||
+	  (st.st_mode & MODE_MASK) != LOCKER_DIR_MODE)
+	{
+	  locker__error(context, "%s: Directory \"%s\" has changed.\n",
+			at->name, path);
+	  unlink(file);
+	  free(file);
+	  return LOCKER_EMOUNTPOINT;
+	}
+    }
+
+  free(file);
+  return LOCKER_SUCCESS;
+}
+
+static int mountpoint_rmdir(locker_context context, locker_attachent *at,
+			    char *path)
+{
+  char *file;
+  struct stat st;
+
+  file = locker__attachtab_pathname(context, LOCKER_DIRECTORY, path);
+  if (!file)
+    {
+      locker__error(context, "Out of memory removing mountpoint.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  if (access(file, F_OK) != 0)
+    {
+      free(file);
+      return LOCKER_ENOENT;
+    }
+
+  /* Make sure directory exists and looks like we built it. */
+  if (lstat(path, &st) == -1)
+    {
+      locker__error(context, "%s: Could not check directory %s:\n%s.\n",
+		    at->name, path, strerror(errno));
+      unlink(file);
+      free(file);
+      return LOCKER_EMOUNTPOINT;
+    }
+
+  if (!S_ISDIR(st.st_mode) || st.st_uid != 0 ||
+      (st.st_mode & MODE_MASK) != LOCKER_DIR_MODE)
+    {
+      locker__error(context, "%s: Directory \"%s\" has changed.\n",
+		    at->name, path);
+      unlink(file);
+      free(file);
+      return LOCKER_EMOUNTPOINT;
+    }
+
+  if (rmdir(path) == -1)
+    {
+      free(file);
+      if (errno == ENOTEMPTY || errno == EEXIST)
+	return LOCKER_EMOUNTPOINTBUSY;
+      locker__error(context, "%s: Could not remove mountpoint component "
+		    "%s:\n%s.\n", at->name, path, strerror(errno));
+      return LOCKER_EMOUNTPOINT;
+    }
+
+  unlink(file);
+  free(file);
+  return LOCKER_SUCCESS;
+}

Copied: trunk/athena/lib/locker/src/mul.c (from rev 25923, trunk/athena/lib/locker/mul.c)
===================================================================
--- trunk/athena/lib/locker/src/mul.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/mul.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,157 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements some of the code for
+ * MUL lockers. (Most of the code for MUL lockers is special-cased
+ * into attachtab.c, attach.c, and detach.c.)
+ */
+
+static const char rcsid[] = "$Id: mul.c,v 1.3 1999-09-22 22:25:08 danw Exp $";
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+static int mul_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **at);
+static int mul_attach(locker_context context, locker_attachent *at,
+		      char *mountoptions);
+static int mul_detach(locker_context context, locker_attachent *at);
+static int mul_auth(locker_context context, locker_attachent *at,
+		    int mode, int op);
+static int mul_zsubs(locker_context context, locker_attachent *at);
+
+struct locker_ops locker__mul_ops = {
+  "MUL",
+  0,
+  mul_parse,
+  mul_attach,
+  mul_detach,
+  mul_auth,
+  mul_zsubs
+};
+
+static int mul_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp)
+{
+  int status;
+  char *p, *dup, *lasts = NULL;
+  locker_attachent *at, *attmp;
+
+  if (!name)
+    {
+      locker__error(context, "Cannot explicitly specify MUL locker.\n");
+      return LOCKER_EPARSE;
+    }
+
+  if (mountpoint)
+    {
+      locker__error(context, "%s: Explicit mountpoint is meaningless for "
+		    "MUL locker.\n", name);
+      return LOCKER_EPARSE;
+    }
+
+  at = locker__new_attachent(context, &locker__mul_ops);
+  if (!at)
+    return LOCKER_ENOMEM;
+
+  /* Skip "MUL". (But if this is called from read_attachent, the "MUL"
+   * won't be there.)
+   */
+  if (!strncmp(desc, "MUL ", 4))
+    {
+      p = desc + 4;
+      while (isspace((unsigned char)*p))
+	p++;
+    }
+  else
+    p = desc;
+
+  at->name = strdup(name);
+  at->mountpoint = strdup(p);
+  at->hostdir = strdup("");
+  at->mode = '-';
+  dup = strdup(p);
+  if (!at->name || !at->mountpoint || !at->hostdir || !dup)
+    {
+      locker_free_attachent(context, at);
+      locker__error(context, "Out of memory parsing locker description.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  /* Read list of locker names. */
+  for (p = strtok_r(dup, " ", &lasts); p; p = strtok_r(NULL, " ", &lasts))
+    {
+      status = locker__lookup_attachent(context, p, NULL, 1, &attmp);
+      if (status)
+	break;
+
+      if (attmp->fs == at->fs)
+	{
+	  locker__error(context, "%s: Cannot have MUL locker \"%s\" as a "
+			"component of a MUL locker.\n", at->name,
+			attmp->name);
+	  locker_free_attachent(context, attmp);
+	  status = LOCKER_EPARSE;
+	  break;
+	}
+
+      attmp->next = at->next;
+      at->next = attmp;
+    }
+
+  free(dup);
+
+  if (status)
+    {
+      for (; at; at = attmp)
+	{
+	  attmp = at->next;
+	  locker_free_attachent(context, at);
+	}
+    }
+  else
+    *atp = at;
+
+  return status;
+}
+
+static int mul_attach(locker_context context, locker_attachent *at,
+		      char *mountoptions)
+{
+  return LOCKER_SUCCESS;
+}
+
+static int mul_detach(locker_context context, locker_attachent *at)
+{
+  return LOCKER_SUCCESS;
+}
+
+static int mul_auth(locker_context context, locker_attachent *at,
+		    int mode, int op)
+{
+  return LOCKER_SUCCESS;
+}
+
+static int mul_zsubs(locker_context context, locker_attachent *at)
+{
+  return LOCKER_SUCCESS;
+}

Copied: trunk/athena/lib/locker/src/nfs.c (from rev 25923, trunk/athena/lib/locker/nfs.c)
===================================================================
--- trunk/athena/lib/locker/src/nfs.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/nfs.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,438 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements NFS lockers. */
+
+static const char rcsid[] = "$Id: nfs.c,v 1.5 2006-07-25 23:29:09 ghudson Exp $";
+
+#ifdef ENABLE_NFS
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#ifdef NEED_SYS_FS_NFS_H
+#include <sys/fs/nfs.h>
+#endif
+#include <rpcsvc/mount.h>
+
+#include <krb.h>
+
+#endif /* ENABLE_NFS */
+
+#include "locker.h"
+#include "locker_private.h"
+
+#ifdef ENABLE_NFS
+static bool_t xdr_krbtkt(XDR *xdrs, KTEXT authp);
+#endif /* ENABLE_NFS */
+
+static int nfs_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **at);
+static int nfs_auth(locker_context context, locker_attachent *at,
+		    int mode, int op);
+static int nfs_zsubs(locker_context context, locker_attachent *at);
+
+struct locker_ops locker__nfs_ops = {
+  "NFS",
+  LOCKER_FS_NEEDS_MOUNTDIR,
+  nfs_parse,
+  locker__mount,
+  locker__unmount,
+  nfs_auth,
+  nfs_zsubs
+};
+
+static int nfs_parse(locker_context context, char *name, char *desc,
+		     char *mountpoint, locker_attachent **atp)
+{
+#ifdef ENABLE_NFS
+  locker_attachent *at;
+  struct hostent *h;
+  char *dup;
+  char *p, *host, *dir, *lasts = NULL;
+  int status;
+
+  at = locker__new_attachent(context, &locker__nfs_ops);
+  if (!at)
+    return LOCKER_ENOMEM;
+
+  dup = strdup(desc);
+  if (!dup)
+    goto mem_error;
+
+  if (!name)
+    {
+      /* It's an explicit description: host:path. */
+
+      at->name = strdup(desc);
+      if (!at->name)
+	goto mem_error;
+
+      if (!(p = strchr(dup, ':')))
+	{
+	  locker__error(context, "%s: No \":\" in description.\n", desc);
+	  status = LOCKER_EPARSE;
+	  goto cleanup;
+	}
+
+      *p = '\0';
+      host = dup;
+      dir = p + 1;
+      at->mode = LOCKER_AUTH_READWRITE;
+    }
+  else
+    {
+      /* A hesiod NFS description looks like:
+       * NFS /u1/bitbucket JASON.MIT.EDU w /mit/bitbucket
+       */
+
+      at->name = strdup(name);
+      if (!at->name)
+	goto mem_error;
+
+      /* Skip "NFS". */
+      if (!strtok_r(dup, " ", &lasts))
+	goto parse_error;
+
+      /* Directory on remote host */
+      dir = strtok_r(NULL, " ", &lasts);
+      if (!dir)
+	goto parse_error;
+
+      /* Remote host */
+      host = strtok_r(NULL, " ", &lasts);
+      if (!host)
+	goto parse_error;
+
+      /* Mount mode */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p || *(p + 1))
+	goto parse_error;
+      switch (*p)
+	{
+	case 'r':
+	  at->mode = LOCKER_AUTH_READONLY;
+	  break;
+	case 'm':
+	  at->mode = LOCKER_AUTH_MAYBE_READWRITE;
+	  break;
+	case 'w':
+	  at->mode = LOCKER_AUTH_READWRITE;
+	  break;
+	case 'n':
+	  at->mode = LOCKER_AUTH_NONE;
+	  break;
+	default:
+	  locker__error(context, "%s: Unrecognized mount option '%c' in\n"
+			"\"%s\".\n", name, *p, desc);
+	  return LOCKER_EPARSE;
+	}
+
+      /* Mountpoint */
+      p = strtok_r(NULL, " ", &lasts);
+      if (!p)
+	goto parse_error;
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	at->mountpoint = strdup(p);
+      if (!at->mountpoint)
+	goto mem_error;
+    }
+
+  /* Canonicalize hostname and build hostdir. */
+  h = gethostbyname(host);
+  if (!h)
+    {
+      locker__error(context, "%s: Could not resolve hostname %s.\n",
+		    name ? name : desc, host);
+      status = LOCKER_EPARSE;
+      goto cleanup;
+    }
+  memcpy(&at->hostaddr, h->h_addr, sizeof(at->hostaddr));
+
+  at->hostdir = malloc(strlen(h->h_name) + strlen(dir) + 2);
+  if (!at->hostdir)
+    goto mem_error;
+  sprintf(at->hostdir, "%s:%s", h->h_name, dir);
+
+  if (!at->mountpoint)
+    {
+      if (mountpoint)
+	at->mountpoint = strdup(mountpoint);
+      else
+	{
+	  /* Generate a default mountpoint. This defaults to
+	   * /hostname/hostdir where "hostname" is the canonical
+	   * hostname, in lowercase, up to the first dot, and hostdir
+	   * is the path to the NFS locker on that host. BUT, if
+	   * nfs_mount_dir is set, its value is prepended, and if
+	   * nfs_root_hack is set, "root" is appended if hostdir is
+	   * "/".
+	   */
+	  char *mount_dir = context->nfs_mount_dir ?
+	    context->nfs_mount_dir : "";
+	  int root_hack = context->nfs_root_hack && !strcmp(dir, "/");
+	  int hostlen = strcspn(host, ".");
+
+	  at->mountpoint = malloc(strlen(mount_dir) + 1 + hostlen +
+			      strlen(dir) + (root_hack ? 5 : 1));
+	  if (at->mountpoint)
+	    {
+	      sprintf(at->mountpoint, "%s/%.*s%s%s", mount_dir, hostlen,
+		      host, dir, root_hack ? "root" : "");
+	    }
+
+	  for (p = at->mountpoint + strlen(mount_dir) + 1; *p != '/'; p++)
+	    *p = tolower(*p);
+	}
+      if (!at->mountpoint)
+	goto mem_error;
+    }
+
+  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_ALL,
+				     &(at->mountpoint), &(at->buildfrom));
+  if (status != LOCKER_SUCCESS)
+    goto cleanup;
+
+  free(dup);
+  *atp = at;
+  return LOCKER_SUCCESS;
+
+mem_error:
+  locker__error(context, "Out of memory parsing locker description.\n");
+  status = LOCKER_ENOMEM;
+  goto cleanup;
+
+parse_error:
+  locker__error(context, "Could not parse locker description "
+		"\"%s\".\n", desc);
+  status = LOCKER_EPARSE;
+
+cleanup:
+  free(dup);
+  locker_free_attachent(context, at);
+  return status;
+#else /* ENABLE_NFS */
+  return LOCKER_EPARSE;
+#endif /* ENABLE_NFS */
+}
+
+static int nfs_auth(locker_context context, locker_attachent *at,
+		    int mode, int op)
+{
+#ifdef ENABLE_NFS
+  int status, len;
+  char *host;
+
+  len = strcspn(at->hostdir, ":");
+  host = malloc(len + 1);
+  if (!host)
+    {
+      locker__error(context, "Out of memory authenticating to host.\n");
+      return LOCKER_ENOMEM;
+    }
+  memcpy(host, at->hostdir, len);
+  host[len] = '\0';
+
+  status = locker_auth_to_host(context, at->name, host, op);
+  free(host);
+  return status;
+#else /* ENABLE_NFS */
+  return LOCKER_EAUTH;
+#endif /* ENABLE_NFS */
+}
+
+int locker_auth_to_host(locker_context context, char *name, char *host,
+			int op)
+{
+#ifdef ENABLE_NFS
+  int status, len;
+  struct timeval timeout;
+  CLIENT *cl;
+  struct utsname uts;
+  struct passwd *pw;
+  gid_t gids[NGRPS];
+  KTEXT_ST authent;
+  enum clnt_stat rpc_stat;
+
+  if (op == LOCKER_AUTH_PURGE && !context->trusted)
+    {
+      locker__error(context, "%s: You are not allowed to use the "
+		    "'purge all host mappings' option.\n", name);
+      return LOCKER_EPERM;
+    }
+
+  timeout.tv_usec = 0;
+  timeout.tv_sec = 20;
+
+  /* Get an RPC handle. */
+  cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "udp");
+  if (!cl)
+    {
+      locker__error(context, "%s: server %s not responding.\n", name, host);
+      return LOCKER_EATTACH;
+    }
+  auth_destroy(cl->cl_auth);
+
+  /* Create authunix authentication. */
+  uname(&uts);
+  pw = getpwuid(context->user);
+  len = getgroups(NGRPS, gids);
+  cl->cl_auth = authunix_create(uts.nodename, context->user,
+				pw ? pw->pw_gid : LOCKER_DEFAULT_GID,
+				len, gids);
+
+  /* Mapping and user purging are the only Kerberos-authenticated
+   * functions.
+   */
+  if (op == LOCKER_AUTH_AUTHENTICATE || op == LOCKER_AUTH_PURGEUSER)
+    {
+      char *realm, *instance, *src, *dst;
+
+      realm = (char *) krb_realmofhost(host);
+      instance = malloc(strcspn(host, ".") + 1);
+
+      for (src = host, dst = instance; *src && *src != '.'; )
+	*dst++ = tolower(*src++);
+      *dst = '\0';
+
+      status = krb_mk_req(&authent, LOCKER_NFS_KSERVICE, instance, realm, 0);
+      free(instance);
+      if (status != KSUCCESS)
+	{
+	  auth_destroy(cl->cl_auth);
+	  clnt_destroy(cl);
+	  if (status == KDC_PR_UNKNOWN)
+	    {
+	      locker__error(context, "%s: (warning) Host %s isn't registered "
+			    "with Kerberos.%s\n", name, host,
+			    op == LOCKER_AUTH_AUTHENTICATE ?
+			    " Mapping failed." : "");
+	      return LOCKER_SUCCESS;
+	    }
+	  else
+	    {
+	      locker__error(context, "%s: Could not get Kerberos ticket for "
+			    "NFS authentication:\n%s.\n", name,
+			    krb_err_txt[status]);
+	      return LOCKER_EAUTH;
+	    }
+	}
+
+      rpc_stat = clnt_call(cl, op, (xdrproc_t)xdr_krbtkt, (caddr_t)&authent,
+			   (xdrproc_t)xdr_void, 0, timeout);
+    }
+  else
+    rpc_stat = clnt_call(cl, op, (xdrproc_t)xdr_void, 0, (xdrproc_t)xdr_void,
+			 0, timeout);
+
+  auth_destroy(cl->cl_auth);
+  clnt_destroy(cl);
+
+  if (rpc_stat != RPC_SUCCESS)
+    {
+      switch (rpc_stat)
+	{
+	case RPC_TIMEDOUT:
+	  locker__error(context, "%s: Timeout while contacting mount "
+			"daemon on %s.\n", name, host);
+	  return LOCKER_EATTACH;
+
+	case RPC_PMAPFAILURE:
+	case RPC_PROGUNAVAIL:
+	case RPC_PROGNOTREGISTERED:
+	  locker__error(context, "%s: No mount daemon on %s.\n", name, host);
+	  return LOCKER_EATTACH;
+
+	case RPC_AUTHERROR:
+	  locker__error(context, "%s: Authentication failed to host %s.\n",
+		  name, host);
+	  return LOCKER_EAUTH;
+
+	case RPC_PROCUNAVAIL:
+	  locker__error(context, "%s: (warning) Mount daemon on %s doesn't "
+			"understand UID maps.\n", name, host);
+	  return LOCKER_SUCCESS;
+
+	default:
+	  locker__error(context, "%s: System error contacting server %s.\n",
+			name, host);
+	  return LOCKER_EAUTH;
+	}
+    }
+
+  return LOCKER_SUCCESS;
+#else /* ENABLE_NFS */
+  return LOCKER_EAUTH;
+#endif /* ENABLE_NFS */
+}
+
+/* XDR for sending a Kerberos ticket - sends the whole KTEXT block,
+ * but this is very old lossage, and nothing that can really be fixed
+ * now.
+ */
+
+#ifdef ENABLE_NFS
+
+static bool_t xdr_krbtkt(XDR *xdrs, KTEXT authp)
+{
+  KTEXT_ST auth;
+
+  auth = *authp;
+  auth.length = htonl(authp->length);
+  return xdr_opaque(xdrs, (caddr_t)&auth, sizeof(KTEXT_ST));
+}
+
+#endif /* ENABLE_NFS */
+
+static int nfs_zsubs(locker_context context, locker_attachent *at)
+{
+#ifdef ENABLE_NFS
+  int len, status;
+  char *subs[2];
+
+  subs[0] = at->hostdir;
+
+  len = strchr(at->hostdir, ':') - at->hostdir;
+  subs[1] = malloc(len + 1);
+  if (!subs[1])
+    {
+      free(subs[0]);
+      free(subs);
+      locker__error(context, "Out of memory getting Zephyr subscriptions.\n");
+      return LOCKER_ENOMEM;
+    }
+  memcpy(subs[1], at->hostdir, len);
+  subs[1][len] = '\0';
+
+  status = locker__add_zsubs(context, subs, 2);
+  free(subs[1]);
+  return status;
+#else /* ENABLE_NFS */
+  return LOCKER_EPARSE;
+#endif
+}

Copied: trunk/athena/lib/locker/src/util.c (from rev 25923, trunk/athena/lib/locker/util.c)
===================================================================
--- trunk/athena/lib/locker/src/util.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/util.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,71 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It implements various utility
+   routines. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+static const char rcsid[] = "$Id: util.c,v 1.1 1999-02-26 19:04:54 danw Exp $";
+
+/* This is an internal function.  Its contract is to read a line from a
+ * file into a dynamically allocated buffer, zeroing the trailing newline
+ * if there is one.  The calling routine may call locker__read_line multiple
+ * times with the same buf and bufsize pointers; *buf will be reallocated
+ * and *bufsize adjusted as appropriate.  The initial value of *buf
+ * should be NULL.  After the calling routine is done reading lines, it
+ * should free *buf.  This function returns 0 if a line was successfully
+ * read, 1 if the file ended, and -1 if there was an I/O error or if it
+ * ran out of memory.
+ */
+
+int locker__read_line(FILE *fp, char **buf, int *bufsize)
+{
+  char *newbuf;
+  int offset = 0, len;
+
+  if (*buf == NULL)
+    {
+      *buf = malloc(128);
+      if (!*buf)
+	return -1;
+      *bufsize = 128;
+    }
+
+  while (1)
+    {
+      if (!fgets(*buf + offset, *bufsize - offset, fp))
+	return (offset != 0) ? 0 : (ferror(fp)) ? -1 : 1;
+      len = offset + strlen(*buf + offset);
+      if ((*buf)[len - 1] == '\n')
+	{
+	  (*buf)[len - 1] = 0;
+	  return 0;
+	}
+      offset = len;
+
+      /* Allocate more space. */
+      newbuf = realloc(*buf, *bufsize * 2);
+      if (!newbuf)
+	return -1;
+      *buf = newbuf;
+      *bufsize *= 2;
+    }
+}

Copied: trunk/athena/lib/locker/src/zephyr.c (from rev 25923, trunk/athena/lib/locker/zephyr.c)
===================================================================
--- trunk/athena/lib/locker/src/zephyr.c	                        (rev 0)
+++ trunk/athena/lib/locker/src/zephyr.c	2013-06-02 04:57:27 UTC (rev 26030)
@@ -0,0 +1,139 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/* This file is part of liblocker. It deals with zephyr subscriptions
+ * pertaining to lockers.
+ */
+
+static const char rcsid[] = "$Id: zephyr.c,v 1.7 2003-11-04 19:21:12 ghudson Exp $";
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <com_err.h>
+#include <zephyr/zephyr.h>
+
+#include "locker.h"
+#include "locker_private.h"
+
+/* Magic number taken from zctl sources. */
+#define ZEPHYR_MAXONEPACKET 7
+
+int locker_do_zsubs(locker_context context, int op)
+{
+  int wgport;
+  ZSubscription_t zsubs[ZEPHYR_MAXONEPACKET];
+  int i, j, status, retval;
+  uid_t uid = geteuid();
+
+  if (!context->zsubs)
+    return LOCKER_SUCCESS;
+
+  /* Initialize Zephyr. (This can fail.) */
+  status = ZInitialize();
+  if (status)
+    {
+      locker__error(context, "Could not initialize Zephyr library: %s.\n",
+		    error_message(status));
+      return LOCKER_EZEPHYR;
+    }
+
+  wgport = ZGetWGPort();
+  if (wgport == -1)
+    {
+      locker__free_zsubs(context);
+      return LOCKER_EZEPHYR;
+    }
+
+  for (j = 0; j < ZEPHYR_MAXONEPACKET; j++)
+    {
+      zsubs[j].zsub_recipient = "*";
+      zsubs[j].zsub_class = LOCKER_ZEPHYR_CLASS;
+    }
+
+  /* seteuid since Zephyr ops may touch ticket file. */
+  if (uid != context->user)
+    seteuid(context->user);
+
+  retval = LOCKER_SUCCESS;
+  for (i = 0; i < context->nzsubs; i += j)
+    {
+      for (j = 0; j < ZEPHYR_MAXONEPACKET && i + j < context->nzsubs; j++)
+	zsubs[j].zsub_classinst = context->zsubs[i + j];
+
+      if (op == LOCKER_ZEPHYR_SUBSCRIBE)
+	status = ZSubscribeToSansDefaults(zsubs, j, wgport);
+      else
+	status = ZUnsubscribeTo(zsubs, j, wgport);
+      if (status)
+	{
+	  locker__error(context, "Error while %ssubscribing:\n%s.\n",
+			op == LOCKER_ZEPHYR_SUBSCRIBE ? "" : "un",
+			error_message(status));
+	  retval = LOCKER_EZEPHYR;
+	  break;
+	}
+    }
+
+  if (uid != context->user)
+    seteuid(uid);
+
+  locker__free_zsubs(context);
+  ZClosePort();
+  return retval;
+}
+
+int locker__add_zsubs(locker_context context, char **subs, int nsubs)
+{
+  int newnzsubs = context->nzsubs + nsubs, i;
+  char **newzsubs;
+
+  newzsubs = realloc(context->zsubs, newnzsubs * sizeof(char *));
+  if (!newzsubs)
+    {
+      locker__error(context, "Out of memory recording Zephyr subscriptions.\n");
+      return LOCKER_ENOMEM;
+    }
+
+  context->zsubs = newzsubs;
+  for (i = 0; i < nsubs; i++)
+    {
+      context->zsubs[context->nzsubs + i] = strdup(subs[i]);
+      if (!context->zsubs[context->nzsubs + i])
+	{
+	  locker__error(context, "Out of memory recording Zephyr "
+			"subscriptions.\n");
+	  while (i--)
+	    free(context->zsubs[context->nzsubs + i]);
+	  return LOCKER_ENOMEM;
+	}
+    }
+  context->nzsubs += nsubs;
+
+  return LOCKER_SUCCESS;
+}
+
+void locker__free_zsubs(locker_context context)
+{
+  int i;
+
+  for (i = 0; i < context->nzsubs; i++)
+    free(context->zsubs[i]);
+  free(context->zsubs);
+
+  context->nzsubs = 0;
+  context->zsubs = NULL;
+}


home help back first fref pref prev next nref lref last post