Merge "mounttablelib: use v23/conventions to pick up the user id from the set of blessings.  This id is used for resource accounting."
diff --git a/services/mounttable/mounttablelib/mounttable.go b/services/mounttable/mounttablelib/mounttable.go
index c63a53c..2885228 100644
--- a/services/mounttable/mounttablelib/mounttable.go
+++ b/services/mounttable/mounttablelib/mounttable.go
@@ -359,7 +359,7 @@
 // findNode finds a node in the table and optionally creates a path to it.
 //
 // If a node is found, on return it and its parent are locked.
-func (mt *mountTable) findNode(ctx *context.T, call security.Call, elems []string, create bool, tags []mounttable.Tag) (*node, error) {
+func (mt *mountTable) findNode(ctx *context.T, call security.Call, elems []string, create bool, tags, ptags []mounttable.Tag) (*node, error) {
 	n, nelems, err := mt.traverse(ctx, call, elems, create)
 	if err != nil {
 		return nil, err
@@ -372,10 +372,18 @@
 		n.Unlock()
 		return nil, nil
 	}
+	// Either the node has to satisfy tags or the parent has to satisfy ptags.
 	if err := n.satisfies(mt, ctx, call, tags); err != nil {
-		n.parent.Unlock()
-		n.Unlock()
-		return nil, err
+		if ptags == nil {
+			n.parent.Unlock()
+			n.Unlock()
+			return nil, err
+		}
+		if err := n.parent.satisfies(mt, ctx, call, ptags); err != nil {
+			n.parent.Unlock()
+			n.Unlock()
+			return nil, err
+		}
 	}
 	return n, nil
 }
@@ -485,7 +493,7 @@
 	}
 
 	// Find/create node in namespace and add the mount.
-	n, werr := mt.findNode(ctx, call.Security(), ms.elems, true, mountTags)
+	n, werr := mt.findNode(ctx, call.Security(), ms.elems, true, mountTags, nil)
 	if werr != nil {
 		return werr
 	}
@@ -578,7 +586,7 @@
 func (ms *mountContext) Unmount(ctx *context.T, call rpc.ServerCall, server string) error {
 	vlog.VI(2).Infof("*********************Unmount %q, %s", ms.name, server)
 	mt := ms.mt
-	n, err := mt.findNode(ctx, call.Security(), ms.elems, false, mountTags)
+	n, err := mt.findNode(ctx, call.Security(), ms.elems, false, mountTags, nil)
 	if err != nil {
 		return err
 	}
@@ -611,8 +619,9 @@
 		return verror.New(errCantDeleteRoot, ctx)
 	}
 	mt := ms.mt
-	// Find and lock the parent node.
-	n, err := mt.findNode(ctx, call.Security(), ms.elems, false, removeTags)
+	// Find and lock the parent node and parent node.  Either the node or its parent has
+	// to satisfy removeTags.
+	n, err := mt.findNode(ctx, call.Security(), ms.elems, false, removeTags, removeTags)
 	if err != nil {
 		return err
 	}
@@ -757,7 +766,7 @@
 	go func() {
 		defer close(ch)
 		// If there was an access error, just ignore the entry, i.e., make it invisible.
-		n, err := mt.findNode(ctx, scall, ms.elems, false, nil)
+		n, err := mt.findNode(ctx, scall, ms.elems, false, nil, nil)
 		if err != nil {
 			return
 		}
@@ -792,7 +801,7 @@
 	mt := ms.mt
 
 	// Find/create node in namespace and add the mount.
-	n, err := mt.findNode(ctx, call.Security(), ms.elems, true, setTags)
+	n, err := mt.findNode(ctx, call.Security(), ms.elems, true, setTags, nil)
 	if err != nil {
 		return err
 	}
@@ -838,7 +847,7 @@
 	mt := ms.mt
 
 	// Find node in namespace and add the mount.
-	n, err := mt.findNode(ctx, call.Security(), ms.elems, false, getTags)
+	n, err := mt.findNode(ctx, call.Security(), ms.elems, false, getTags, nil)
 	if err != nil {
 		return nil, "", err
 	}
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index ebd628c..21a2f29 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -602,6 +602,12 @@
 	checkExists(t, rootCtx, estr, "one/bright/day", false)
 	doDeleteNode(t, bobCtx, estr, "one/bright", true)
 	checkExists(t, rootCtx, estr, "one/bright", false)
+
+	// Make sure directory admin can delete directory children.
+	perms := access.Permissions{"Admin": access.AccessList{In: []security.BlessingPattern{"bob"}}}
+	doSetPermissions(t, bobCtx, estr, "hoohaa", perms, "", false)
+	doDeleteNode(t, rootCtx, estr, "hoohaa", true)
+	checkExists(t, rootCtx, estr, "hoohaa", false)
 }
 
 func TestServerFormat(t *testing.T) {
diff --git a/services/mounttable/mounttablelib/persistentstore.go b/services/mounttable/mounttablelib/persistentstore.go
index 0cb614d..a96eb19 100644
--- a/services/mounttable/mounttablelib/persistentstore.go
+++ b/services/mounttable/mounttablelib/persistentstore.go
@@ -131,7 +131,7 @@
 		}
 
 		elems := strings.Split(e.N, "/")
-		n, err := mt.findNode(nil, nil, elems, true, nil)
+		n, err := mt.findNode(nil, nil, elems, true, nil, nil)
 		if n != nil || err == nil {
 			n.creator = e.C
 			if e.D {
diff --git a/services/mounttable/mounttablelib/versionedpermissions.go b/services/mounttable/mounttablelib/versionedpermissions.go
index 44336cd..d4da750 100644
--- a/services/mounttable/mounttablelib/versionedpermissions.go
+++ b/services/mounttable/mounttablelib/versionedpermissions.go
@@ -143,7 +143,7 @@
 			}
 
 			// Create name and add the Permissions map to it.
-			n, err := mt.findNode(nil, nil, elems, true, nil)
+			n, err := mt.findNode(nil, nil, elems, true, nil, nil)
 			if n != nil || err == nil {
 				vlog.VI(2).Infof("added perms %v to %s", perms, name)
 				if isPattern {