Merge "More "go vet" fixes."
diff --git a/lib/vdl/codegen/java/file_client_interface.go b/lib/vdl/codegen/java/file_client_interface.go
index db4366f..ffc17c8 100644
--- a/lib/vdl/codegen/java/file_client_interface.go
+++ b/lib/vdl/codegen/java/file_client_interface.go
@@ -23,6 +23,7 @@
 {{ range $method := .Methods }}
     {{/* If this method has multiple return arguments, generate the class. */}}
     {{ if $method.IsMultipleRet }}
+    @io.v.v23.vdl.MultiReturn
     public static class {{ $method.UppercaseMethodName }}Out {
         {{ range $retArg := $method.RetArgs }}
         public {{ $retArg.Type }} {{ $retArg.Name }};
@@ -47,8 +48,8 @@
 	AccessModifier      string
 	Args                string
 	Doc                 string
-	IsMultipleRet       bool
 	Name                string
+	IsMultipleRet       bool
 	RetArgs             []clientInterfaceArg
 	RetType             string
 	UppercaseMethodName string
@@ -66,8 +67,8 @@
 	}
 }
 
-func clientInterfaceOutArg(iface *compile.Interface, method *compile.Method, isService bool, env *compile.Env) string {
-	if isStreamingMethod(method) && !isService {
+func clientInterfaceOutArg(iface *compile.Interface, method *compile.Method, env *compile.Env) string {
+	if isStreamingMethod(method) {
 		return fmt.Sprintf("io.v.v23.vdl.ClientStream<%s, %s, %s>", javaType(method.InStream, true, env), javaType(method.OutStream, true, env), clientInterfaceNonStreamingOutArg(iface, method, true, env))
 	}
 	return clientInterfaceNonStreamingOutArg(iface, method, false, env)
@@ -87,10 +88,10 @@
 		AccessModifier:      accessModifierForName(method.Name),
 		Args:                javaDeclarationArgStr(method.InArgs, env, true),
 		Doc:                 method.Doc,
-		IsMultipleRet:       len(retArgs) > 1,
 		Name:                vdlutil.FirstRuneToLower(method.Name),
+		IsMultipleRet:       len(retArgs) > 1,
 		RetArgs:             retArgs,
-		RetType:             clientInterfaceOutArg(iface, method, false, env),
+		RetType:             clientInterfaceOutArg(iface, method, env),
 		UppercaseMethodName: method.Name,
 	}
 }
diff --git a/lib/vdl/codegen/java/file_client_stub.go b/lib/vdl/codegen/java/file_client_stub.go
index 4f8c3dc..fd28e07 100644
--- a/lib/vdl/codegen/java/file_client_stub.go
+++ b/lib/vdl/codegen/java/file_client_stub.go
@@ -43,24 +43,6 @@
 
     }
 
-    // Methods from interface UniversalServiceMethods.
-    @Override
-    public io.v.v23.vdlroot.signature.Interface getSignature(io.v.v23.context.VContext context) throws io.v.v23.verror.VException {
-        return getSignature(context, null);
-    }
-    @Override
-    public io.v.v23.vdlroot.signature.Interface getSignature(io.v.v23.context.VContext context, io.v.v23.Options vOpts) throws io.v.v23.verror.VException {
-        // Start the call.
-        final io.v.v23.rpc.Client.Call _call = getClient(context).startCall(context, this.vName, "signature", new java.lang.Object[0], new java.lang.reflect.Type[0], vOpts);
-
-        // Finish the call.
-        final java.lang.reflect.Type[] _resultTypes = new java.lang.reflect.Type[]{
-            new com.google.common.reflect.TypeToken<io.v.v23.vdlroot.signature.Interface>() {}.getType(),
-        };
-        final java.lang.Object[] _results = _call.finish(_resultTypes);
-        return (io.v.v23.vdlroot.signature.Interface)_results[0];
-    }
-
     // Methods from interface {{ .ServiceName }}Client.
 {{/* Iterate over methods defined directly in the body of this service */}}
 {{ range $method := .Methods }}
@@ -218,7 +200,7 @@
 		NotStreaming:            !isStreamingMethod(method),
 		OutArgs:                 outArgs,
 		RecvType:                javaType(method.OutStream, true, env),
-		RetType:                 clientInterfaceOutArg(iface, method, false, env),
+		RetType:                 clientInterfaceOutArg(iface, method, env),
 		Returns:                 len(method.OutArgs) >= 1 || isStreamingMethod(method),
 		SendType:                javaType(method.InStream, true, env),
 		ServiceName:             vdlutil.FirstRuneToUpper(iface.Name),
@@ -232,7 +214,7 @@
 		DeclarationArgs:         javaDeclarationArgStr(embedMethod.InArgs, env, true),
 		LocalStubVarName:        vdlutil.FirstRuneToLower(iface.Name) + "ClientStub",
 		Name:                    vdlutil.FirstRuneToLower(embedMethod.Name),
-		RetType:                 clientInterfaceOutArg(iface, embedMethod, false, env),
+		RetType:                 clientInterfaceOutArg(iface, embedMethod, env),
 		Returns:                 len(embedMethod.OutArgs) >= 1 || isStreamingMethod(embedMethod),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_server_interface.go b/lib/vdl/codegen/java/file_server_interface.go
index 947fa66..d3d8891 100644
--- a/lib/vdl/codegen/java/file_server_interface.go
+++ b/lib/vdl/codegen/java/file_server_interface.go
@@ -24,6 +24,16 @@
 )
 {{ .AccessModifier }} interface {{ .ServiceName }}Server {{ .Extends }} {
 {{ range $method := .Methods }}
+    {{/* If this method has multiple return arguments, generate the class. */}}
+    {{ if $method.IsMultipleRet }}
+    @io.v.v23.vdl.MultiReturn
+    public static class {{ $method.UppercaseMethodName }}Out {
+        {{ range $retArg := $method.RetArgs }}
+        public {{ $retArg.Type }} {{ $retArg.Name }};
+        {{ end }}
+    }
+    {{ end }}
+
     {{/* Generate the method signature. */}}
     {{ $method.Doc }}
     {{ $method.AccessModifier }} {{ $method.RetType }} {{ $method.Name }}(final io.v.v23.context.VContext ctx, final io.v.v23.rpc.ServerCall call{{ $method.Args }}) throws io.v.v23.verror.VException;
@@ -31,23 +41,31 @@
 }
 `
 
-func serverInterfaceOutArg(method *compile.Method, iface *compile.Interface, env *compile.Env) string {
+func serverInterfaceOutArg(iface *compile.Interface, method *compile.Method, env *compile.Env) string {
 	switch len(method.OutArgs) {
 	case 0:
 		return "void"
 	case 1:
 		return javaType(method.OutArgs[0].Type, false, env)
 	default:
-		return javaPath(path.Join(interfaceFullyQualifiedName(iface)+"Client", method.Name+"Out"))
+		return javaPath(path.Join(interfaceFullyQualifiedName(iface)+"Server", method.Name+"Out"))
 	}
 }
 
+type serverInterfaceArg struct {
+	Type string
+	Name string
+}
+
 type serverInterfaceMethod struct {
-	AccessModifier string
-	Args           string
-	Doc            string
-	Name           string
-	RetType        string
+	AccessModifier      string
+	Args                string
+	Doc                 string
+	Name                string
+	IsMultipleRet       bool
+	RetArgs             []serverInterfaceArg
+	RetType             string
+	UppercaseMethodName string
 }
 
 func processServerInterfaceMethod(method *compile.Method, iface *compile.Interface, env *compile.Env) serverInterfaceMethod {
@@ -55,12 +73,25 @@
 	if isStreamingMethod(method) {
 		args += fmt.Sprintf(", io.v.v23.vdl.Stream<%s, %s> stream", javaType(method.OutStream, true, env), javaType(method.InStream, true, env))
 	}
+	retArgs := make([]serverInterfaceArg, len(method.OutArgs))
+	for i := 0; i < len(method.OutArgs); i++ {
+		if method.OutArgs[i].Name != "" {
+			retArgs[i].Name = vdlutil.FirstRuneToLower(method.OutArgs[i].Name)
+		} else {
+			retArgs[i].Name = fmt.Sprintf("ret%d", i+1)
+		}
+		retArgs[i].Type = javaType(method.OutArgs[i].Type, false, env)
+	}
+
 	return serverInterfaceMethod{
-		AccessModifier: accessModifierForName(method.Name),
-		Args:           args,
-		Doc:            method.Doc,
-		Name:           vdlutil.FirstRuneToLower(method.Name),
-		RetType:        serverInterfaceOutArg(method, iface, env),
+		AccessModifier:      accessModifierForName(method.Name),
+		Args:                args,
+		Doc:                 method.Doc,
+		Name:                vdlutil.FirstRuneToLower(method.Name),
+		IsMultipleRet:       len(retArgs) > 1,
+		RetArgs:             retArgs,
+		RetType:             serverInterfaceOutArg(iface, method, env),
+		UppercaseMethodName: method.Name,
 	}
 }
 
diff --git a/lib/vdl/codegen/java/file_server_wrapper.go b/lib/vdl/codegen/java/file_server_wrapper.go
index d7b69f9..5043d6d 100644
--- a/lib/vdl/codegen/java/file_server_wrapper.go
+++ b/lib/vdl/codegen/java/file_server_wrapper.go
@@ -39,7 +39,7 @@
     /**
      * Returns a description of this server.
      */
-    public io.v.v23.vdlroot.signature.Interface signature(final io.v.v23.context.VContext ctx, final io.v.v23.rpc.ServerCall call) throws io.v.v23.verror.VException {
+    public io.v.v23.vdlroot.signature.Interface signature() {
         java.util.List<io.v.v23.vdlroot.signature.Embed> embeds = new java.util.ArrayList<io.v.v23.vdlroot.signature.Embed>();
         java.util.List<io.v.v23.vdlroot.signature.Method> methods = new java.util.ArrayList<io.v.v23.vdlroot.signature.Method>();
         {{ range $method := .Methods }}
@@ -75,7 +75,7 @@
      * by this server.
      */
     @SuppressWarnings("unused")
-    public io.v.v23.vdl.VdlValue[] getMethodTags(final io.v.v23.rpc.StreamServerCall call, final java.lang.String method) throws io.v.v23.verror.VException {
+    public io.v.v23.vdl.VdlValue[] getMethodTags(final java.lang.String method) throws io.v.v23.verror.VException {
         {{ range $methodName, $tags := .MethodTags }}
         if ("{{ $methodName }}".equals(method)) {
             try {
@@ -89,7 +89,7 @@
         {{ end }}
         {{ range $embed := .Embeds }}
         {
-            final io.v.v23.vdl.VdlValue[] tags = this.{{ $embed.LocalWrapperVarName }}.getMethodTags(call, method);
+            final io.v.v23.vdl.VdlValue[] tags = this.{{ $embed.LocalWrapperVarName }}.getMethodTags(method);
             if (tags != null) {
                 return tags;
             }
@@ -195,7 +195,7 @@
 		IsStreaming:     isStreamingMethod(method),
 		Name:            vdlutil.FirstRuneToLower(method.Name),
 		RecvType:        javaType(method.InStream, true, env),
-		RetType:         clientInterfaceOutArg(iface, method, true, env),
+		RetType:         serverInterfaceOutArg(iface, method, env),
 		RetJavaTypes:    retArgTypes,
 		Returns:         len(method.OutArgs) >= 1,
 		SendType:        javaType(method.OutStream, true, env),
@@ -211,7 +211,7 @@
 		DeclarationArgs:     javaDeclarationArgStr(embedMethod.InArgs, env, true),
 		LocalWrapperVarName: vdlutil.FirstRuneToLower(iface.Name) + "Wrapper",
 		Name:                vdlutil.FirstRuneToLower(embedMethod.Name),
-		RetType:             clientInterfaceOutArg(iface, embedMethod, true, env),
+		RetType:             serverInterfaceOutArg(iface, embedMethod, env),
 		Returns:             len(embedMethod.OutArgs) >= 1,
 	}
 }
@@ -227,9 +227,6 @@
 		})
 	}
 	methodTags := make(map[string][]methodTag)
-	// Add generated methods to the tag map:
-	methodTags["signature"] = []methodTag{}
-	methodTags["getMethodTags"] = []methodTag{}
 	// Copy method tags off of the interface.
 	methods := make([]serverWrapperMethod, len(iface.Methods))
 	for i, method := range iface.Methods {
diff --git a/lib/vdl/codegen/java/util_interface.go b/lib/vdl/codegen/java/util_interface.go
index 73bc317..9a35327 100644
--- a/lib/vdl/codegen/java/util_interface.go
+++ b/lib/vdl/codegen/java/util_interface.go
@@ -38,14 +38,18 @@
 // javaClientExtendsStr creates an extends clause for a client interface
 // e.g. "extends com.a.B, com.d.E"
 func javaClientExtendsStr(embeds []*compile.Interface) string {
+	if len(embeds) == 0 {
+		return ""
+	}
 	var buf bytes.Buffer
 	buf.WriteString("extends ")
-	for _, embed := range embeds {
+	for i, embed := range embeds {
+		if i > 0 {
+			buf.WriteString(", ")
+		}
 		buf.WriteString(javaPath(interfaceFullyQualifiedName(embed)))
 		buf.WriteString("Client")
-		buf.WriteString(", ")
 	}
-	buf.WriteString("io.v.v23.rpc.UniversalServiceMethods")
 	return buf.String()
 }
 
diff --git a/profiles/internal/naming/namespace/all_test.go b/profiles/internal/naming/namespace/all_test.go
index 236fb6a..07f540a 100644
--- a/profiles/internal/naming/namespace/all_test.go
+++ b/profiles/internal/naming/namespace/all_test.go
@@ -61,7 +61,7 @@
 
 // N squared but who cares, this is a little test.
 // Ignores dups.
-func contains(container, contained []string) bool {
+func contains(container, contained []string) (string, bool) {
 L:
 	for _, d := range contained {
 		for _, r := range container {
@@ -69,21 +69,31 @@
 				continue L
 			}
 		}
-		return false
+		return d, false
 	}
-	return true
+	return "", true
 }
 
 func compare(t *testing.T, caller, name string, got, want []string) {
 	// Compare ignoring dups.
-	if !contains(got, want) || !contains(want, got) {
-		boom(t, "%s: %q: got %v, want %v", caller, name, got, want)
+	a, foundA := contains(got, want)
+	b, foundB := contains(want, got)
+	if !foundA {
+		vlog.Infof("%s: %q: failed to find %q: got %v, want %v", caller, name, a, got, want)
+		boom(t, "%s: %q: failed to find %q: got %v, want %v", caller, name, a, got, want)
+	}
+	if !foundB {
+		vlog.Infof("%s: %q: failed to find %q: got %v, want %v", caller, name, a, got, want)
+		boom(t, "%s: %q: failed to find %q: got %v, want %v", caller, name, b, got, want)
 	}
 }
 
 func doGlob(t *testing.T, ctx *context.T, ns namespace.T, pattern string, limit int) []string {
 	var replies []string
-	rc, err := ns.Glob(ctx, pattern)
+
+	sctx, done := context.WithTimeout(ctx, 2*time.Minute)
+	defer done()
+	rc, err := ns.Glob(sctx, pattern)
 	if err != nil {
 		boom(t, "Glob(%s): %s", pattern, err)
 	}
@@ -94,6 +104,8 @@
 			if limit > 0 && len(replies) > limit {
 				boom(t, "Glob returns too many results, perhaps not limiting recursion")
 			}
+		case *naming.GlobReplyError:
+			boom(t, "Glob failed at %q: %v", v.Value.Name, v.Value.Error)
 		}
 	}
 	return replies
@@ -141,11 +153,23 @@
 }
 
 func doResolveTest(t *testing.T, fname string, f func(*context.T, string, ...naming.NamespaceOpt) (*naming.MountEntry, error), ctx *context.T, name string, want []string, opts ...naming.NamespaceOpt) {
-	me, err := f(ctx, name, opts...)
-	if err != nil {
-		boom(t, "Failed to %s %s: %s", fname, name, err)
+	maxretries := 5
+	var lastErr error
+	for i := 0; i < maxretries; i++ {
+		me, err := f(ctx, name, opts...)
+		if err == nil {
+			if i > 0 {
+				t.Logf("doResolveTest: retried %d times", i)
+			}
+			compare(t, fname, name, me.Names(), want)
+			return
+		}
+		if err != nil && verror.Action(err).RetryAction() != 0 {
+			boom(t, "Failed to %s %s: %s, attempt %d", fname, name, err, i)
+		}
+		lastErr = err
 	}
-	compare(t, fname, name, me.Names(), want)
+	boom(t, "Failed to %s %s: %s after %d attempts", fname, name, lastErr, maxretries)
 }
 
 func testResolveToMountTable(t *testing.T, ctx *context.T, ns namespace.T, name string, want ...string) {
@@ -197,6 +221,7 @@
 	if err := s.ServeDispatcher(mountPoint, disp); err != nil {
 		boom(t, "Failed to serve mount table at %s: %s", mountPoint, err)
 	}
+	t.Logf("server %q -> %s", eps[0].Name(), mountPoint)
 	return &serverEntry{mountPoint: mountPoint, server: s, endpoint: eps[0], name: eps[0].Name()}
 }
 
@@ -267,7 +292,9 @@
 	// We directly mount baz into the mt4/foo mount table.
 	globalMP := naming.JoinAddressName(mts["mt4/foo"].name, "baz")
 	mts["baz"] = runMT(t, ctx, "baz")
-	if err := ns.Mount(ctx, globalMP, mts["baz"].name, ttl); err != nil {
+	sctx, done := context.WithTimeout(ctx, 2*time.Minute)
+	defer done()
+	if err := ns.Mount(sctx, globalMP, mts["baz"].name, ttl); err != nil {
 		boom(t, "Failed to Mount %s: %s", globalMP, err)
 	}
 }
@@ -645,6 +672,9 @@
 
 	// Intermediate mounttables should be authenticated.
 	mt := runMT(t, mtCtx, "mt")
+	defer func() {
+		mt.server.Stop()
+	}()
 	// Mount a server on "mt".
 	if err := mount("mt/server", serverEndpoint, time.Minute, naming.ReplaceMount(true)); err != nil {
 		t.Error(err)
@@ -736,6 +766,8 @@
 	_, ctx, cleanup := createContexts(t)
 	defer cleanup()
 	root := runMT(t, ctx, "")
+	defer func() { root.server.Stop() }()
+
 	ns := v23.GetNamespace(ctx)
 	ns.SetRoots(root.name)
 
@@ -750,6 +782,7 @@
 	if err := server.Serve("leaf", &leafObject{}, nil); err != nil {
 		boom(t, "server.Serve failed: %s", err)
 	}
+	defer server.Stop()
 
 	mountEntry, err := ns.Resolve(ctx, "leaf")
 	if err != nil {
diff --git a/profiles/internal/naming/namespace/glob.go b/profiles/internal/naming/namespace/glob.go
index 267330a..732b48c 100644
--- a/profiles/internal/naming/namespace/glob.go
+++ b/profiles/internal/naming/namespace/glob.go
@@ -9,14 +9,15 @@
 	"strings"
 	"sync"
 
-	"v.io/x/ref/lib/glob"
+	"v.io/x/lib/vlog"
 
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
+
+	"v.io/x/ref/lib/glob"
 )
 
 type tracks struct {
diff --git a/profiles/internal/naming/namespace/perms_test.go b/profiles/internal/naming/namespace/perms_test.go
index 93a30e5..0719656 100644
--- a/profiles/internal/naming/namespace/perms_test.go
+++ b/profiles/internal/naming/namespace/perms_test.go
@@ -239,6 +239,7 @@
 	if err != nil {
 		t.Fatalf("v23.NewServer failed: %v", err)
 	}
+	defer server.Stop()
 	if _, err := server.Listen(v23.GetListenSpec(rootCtx)); err != nil {
 		t.Fatalf("Failed to Listen: %s", err)
 	}
diff --git a/profiles/internal/rpc/client.go b/profiles/internal/rpc/client.go
index 4e61ff9..a032018 100644
--- a/profiles/internal/rpc/client.go
+++ b/profiles/internal/rpc/client.go
@@ -565,7 +565,7 @@
 				go func() {
 					select {
 					case <-doneChan:
-						vtrace.GetSpan(fc.ctx).Annotate("Cancelled")
+						vtrace.GetSpan(fc.ctx).Annotate("Canceled")
 						fc.flow.Cancel()
 					case <-fc.flow.Closed():
 					}
@@ -898,8 +898,6 @@
 	defer vlog.LogCallf("itemptr=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	switch {
 	case fc.response.Error != nil:
-		// TODO(cnicolaou): this will become a verror.E when we convert the
-		// server.
 		return verror.New(verror.ErrBadProtocol, fc.ctx, fc.response.Error)
 	case fc.response.EndStreamResults:
 		return io.EOF
@@ -912,8 +910,6 @@
 		return fc.close(berr)
 	}
 	if fc.response.Error != nil {
-		// TODO(cnicolaou): this will become a verror.E when we convert the
-		// server.
 		return verror.New(verror.ErrBadProtocol, fc.ctx, fc.response.Error)
 	}
 	if fc.response.EndStreamResults {
diff --git a/services/internal/pathperms/permsaccess.go b/services/internal/pathperms/permsaccess.go
index 6e463ac..81c6b76 100644
--- a/services/internal/pathperms/permsaccess.go
+++ b/services/internal/pathperms/permsaccess.go
@@ -49,7 +49,7 @@
 }
 
 // Get returns the Permissions from the data file in dir.
-func (store PathStore) Get(dir string) (access.Permissions, string, error) {
+func (store *PathStore) Get(dir string) (access.Permissions, string, error) {
 	permspath := filepath.Join(dir, permsName)
 	sigpath := filepath.Join(dir, sigName)
 	defer store.lockPath(dir)()
@@ -57,7 +57,7 @@
 }
 
 // TODO(rjkroege): Improve lock handling.
-func (store PathStore) lockPath(dir string) func() {
+func (store *PathStore) lockPath(dir string) func() {
 	store.lk.Lock()
 	lck, contains := store.pthlks[dir]
 	if !contains {
@@ -107,7 +107,7 @@
 
 // Set writes the specified Permissions to the provided directory with
 // enforcement of version synchronization mechanism and locking.
-func (store PathStore) Set(dir string, perms access.Permissions, version string) error {
+func (store *PathStore) Set(dir string, perms access.Permissions, version string) error {
 	return store.SetShareable(dir, perms, version, false)
 }
 
@@ -115,7 +115,7 @@
 // directory with enforcement of version synchronization mechanism and
 // locking with file modes that will give the application read-only
 // access to the permissions file.
-func (store PathStore) SetShareable(dir string, perms access.Permissions, version string, shareable bool) error {
+func (store *PathStore) SetShareable(dir string, perms access.Permissions, version string, shareable bool) error {
 	permspath := filepath.Join(dir, permsName)
 	sigpath := filepath.Join(dir, sigName)
 	defer store.lockPath(dir)()
@@ -187,7 +187,7 @@
 	return nil
 }
 
-func (store PathStore) PermsForPath(path string) (access.Permissions, bool, error) {
+func (store *PathStore) PermsForPath(path string) (access.Permissions, bool, error) {
 	perms, _, err := store.Get(path)
 	if os.IsNotExist(err) {
 		return nil, true, nil
diff --git a/services/mounttable/mounttablelib/mounttable.go b/services/mounttable/mounttablelib/mounttable.go
index 3b87098..4fb98d6 100644
--- a/services/mounttable/mounttablelib/mounttable.go
+++ b/services/mounttable/mounttablelib/mounttable.go
@@ -28,6 +28,7 @@
 )
 
 const pkgPath = "v.io/x/ref/services/mounttable/mounttablelib"
+const defaultMaxNodesPerUser = 1000
 
 var (
 	errMalformedAddress = verror.Register(pkgPath+".errMalformedAddress", verror.NoRetry, "{1:}{2:} malformed address {3} for mounted server {4}{:_}")
@@ -36,6 +37,8 @@
 	errCantDeleteRoot   = verror.Register(pkgPath+".errCantDeleteRoot", verror.NoRetry, "{1:}{2:} cannot delete root node{:_}")
 	errNotEmpty         = verror.Register(pkgPath+".errNotEmpty", verror.NoRetry, "{1:}{2:} cannot delete {3}: has children{:_}")
 	errNamingLoop       = verror.Register(pkgPath+".errNamingLoop", verror.NoRetry, "{1:}{2:} Loop in namespace{:_}")
+	errTooManyNodes     = verror.Register(pkgPath+".errTooManyNodes", verror.NoRetry, "{1:}{2:} User has exceeded his node limit {:_}")
+	errNoSharedRoot     = verror.Register(pkgPath+".errNoSharedRoot", verror.NoRetry, "{1:}{2:} Server and User share no blessing root {:_}")
 )
 
 var (
@@ -51,19 +54,23 @@
 )
 
 type persistence interface {
-	persistPerms(name string, perm *VersionedPermissions) error
+	persistPerms(name, creator string, perm *VersionedPermissions) error
 	persistDelete(name string) error
 	close()
 }
 
 // mountTable represents a namespace.  One exists per server instance.
 type mountTable struct {
-	root          *node
-	superUsers    access.AccessList
-	persisting    bool
-	persist       persistence
-	nodeCounter   *stats.Integer
-	serverCounter *stats.Integer
+	sync.Mutex
+	root               *node
+	superUsers         access.AccessList
+	persisting         bool
+	persist            persistence
+	nodeCounter        *stats.Integer
+	serverCounter      *stats.Integer
+	perUserNodeCounter *stats.Map
+	maxNodesPerUser    int64
+	userPrefixes       []string
 }
 
 var _ rpc.Dispatcher = (*mountTable)(nil)
@@ -93,6 +100,7 @@
 	vPerms              *VersionedPermissions
 	permsTemplate       access.Permissions
 	explicitPermissions bool
+	creator             string
 }
 
 const templateVar = "%%"
@@ -109,9 +117,11 @@
 // statsPrefix is the prefix for for exported statistics objects.
 func NewMountTableDispatcher(permsFile, persistDir, statsPrefix string) (rpc.Dispatcher, error) {
 	mt := &mountTable{
-		root:          new(node),
-		nodeCounter:   stats.NewInteger(naming.Join(statsPrefix, "num-nodes")),
-		serverCounter: stats.NewInteger(naming.Join(statsPrefix, "num-mounted-servers")),
+		root:               new(node),
+		nodeCounter:        stats.NewInteger(naming.Join(statsPrefix, "num-nodes")),
+		serverCounter:      stats.NewInteger(naming.Join(statsPrefix, "num-mounted-servers")),
+		perUserNodeCounter: stats.NewMap(naming.Join(statsPrefix, "num-nodes-per-user")),
+		maxNodesPerUser:    defaultMaxNodesPerUser,
 	}
 	mt.root.parent = mt.newNode() // just for its lock
 	if persistDir != "" {
@@ -140,6 +150,7 @@
 	if n == nil {
 		return
 	}
+	mt.credit(n)
 	nodeCount := int64(0)
 	serverCount := int64(0)
 	queue := []*node{n}
@@ -151,6 +162,7 @@
 		for _, ch := range n.children {
 			ch.Lock() // Keep locked until it is deleted.
 			queue = append(queue, ch)
+			mt.credit(ch)
 		}
 	}
 
@@ -315,8 +327,14 @@
 				return nil, nil, err
 			}
 		}
+		// Obey account limits.
+		creator, err := mt.debit(ctx, call)
+		if err != nil {
+			return nil, nil, err
+		}
 		// At this point cur is still locked, OK to use and change it.
 		next := mt.newNode()
+		next.creator = creator
 		next.parent = cur
 		if cur.permsTemplate != nil {
 			next.vPerms = createVersionedPermissionsFromTemplate(cur.permsTemplate, e)
@@ -805,7 +823,7 @@
 	n.vPerms, err = n.vPerms.Set(ctx, version, perms)
 	if err == nil {
 		if mt.persisting {
-			mt.persist.persistPerms(ms.name, n.vPerms)
+			mt.persist.persistPerms(ms.name, n.creator, n.vPerms)
 		}
 		n.explicitPermissions = true
 	}
@@ -829,3 +847,23 @@
 	version, perms := n.vPerms.Get()
 	return perms, version, nil
 }
+
+// credit user for node deletion.
+func (mt *mountTable) credit(n *node) {
+	mt.perUserNodeCounter.Incr(n.creator, -1)
+}
+
+// debit user for node creation.
+func (mt *mountTable) debit(ctx *context.T, call security.Call) (string, error) {
+	creator := mt.pickCreator(ctx, call)
+	count, ok := mt.perUserNodeCounter.Incr(creator, 1).(int64)
+	if !ok {
+		return "", verror.New(errTooManyNodes, ctx)
+	}
+	// If we have no prefixes defining users, don't bother with checking per user limits.
+	if len(mt.userPrefixes) != 0 && count > mt.maxNodesPerUser {
+		mt.perUserNodeCounter.Incr(creator, -1)
+		return "", verror.New(errTooManyNodes, ctx)
+	}
+	return creator, nil
+}
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index 5f0da6e..6d27b94 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -5,6 +5,7 @@
 package mounttablelib
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -25,6 +26,7 @@
 	"v.io/v23/vdl"
 	"v.io/x/lib/vlog"
 
+	libstats "v.io/x/ref/lib/stats"
 	"v.io/x/ref/services/debug/debuglib"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/testutil"
@@ -177,7 +179,7 @@
 	}
 }
 
-func newMT(t *testing.T, permsFile, persistDir string, rootCtx *context.T) (rpc.Server, string) {
+func newMT(t *testing.T, permsFile, persistDir, statsDir string, rootCtx *context.T) (rpc.Server, string) {
 	reservedDisp := debuglib.NewDispatcher(vlog.Log.LogDir, nil)
 	ctx := v23.WithReservedNameDispatcher(rootCtx, reservedDisp)
 	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
@@ -185,7 +187,7 @@
 		boom(t, "r.NewServer: %s", err)
 	}
 	// Add mount table service.
-	mt, err := NewMountTableDispatcher(permsFile, persistDir, "mounttable")
+	mt, err := NewMountTableDispatcher(permsFile, persistDir, statsDir)
 	if err != nil {
 		boom(t, "NewMountTableDispatcher: %v", err)
 	}
@@ -227,7 +229,7 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	mt, mtAddr := newMT(t, "testdata/test.perms", "", rootCtx)
+	mt, mtAddr := newMT(t, "testdata/test.perms", "", "testMountTable", rootCtx)
 	defer mt.Stop()
 	collection, collectionAddr := newCollection(t, rootCtx)
 	defer collection.Stop()
@@ -397,7 +399,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testGlob", rootCtx)
 	defer server.Stop()
 
 	// set up a mount space
@@ -444,7 +446,7 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", rootCtx)
+	server, estr := newMT(t, "testdata/test.perms", "", "testAccessListTemplate", rootCtx)
 	defer server.Stop()
 	fakeServer := naming.JoinAddressName(estr, "quux")
 
@@ -457,13 +459,67 @@
 	doMount(t, aliceCtx, estr, "users/alice", fakeServer, true)
 	doMount(t, bobCtx, estr, "users/bob", fakeServer, true)
 	doMount(t, rootCtx, estr, "users/root", fakeServer, true)
+
+	// Make sure the counter works.
+	doUnmount(t, aliceCtx, estr, "users/alice", "", true)
+	doUnmount(t, bobCtx, estr, "users/bob", "", true)
+	doUnmount(t, rootCtx, estr, "users/root", "", true)
+	perms := access.Permissions{"Admin": access.AccessList{In: []security.BlessingPattern{security.AllPrincipals}}}
+	doSetPermissions(t, aliceCtx, estr, "users/alice/a/b/c/d", perms, "", true)
+	doSetPermissions(t, aliceCtx, estr, "users/alice/a/b/c/d", perms, "", true)
+
+	// Do we obey limits?
+	for i := 0; i < defaultMaxNodesPerUser-5; i++ {
+		node := fmt.Sprintf("users/alice/a/b/c/d/%d", i)
+		doSetPermissions(t, aliceCtx, estr, node, perms, "", true)
+	}
+	doSetPermissions(t, aliceCtx, estr, "users/alice/a/b/c/d/straw", perms, "", false)
+
+	// See if the stats numbers are correct.
+	testcases := []struct {
+		key      string
+		expected interface{}
+	}{
+		{"alice", int64(defaultMaxNodesPerUser)},
+		{"bob", int64(0)},
+		{"root", int64(0)},
+		{localUser, int64(3)},
+	}
+	for _, tc := range testcases {
+		name := "testAccessListTemplate/num-nodes-per-user/" + tc.key
+		got, err := libstats.Value(name)
+		if err != nil {
+			t.Errorf("unexpected error getting map entry for %s: %s", name, err)
+		}
+		if got != tc.expected {
+			t.Errorf("unexpected getting map entry for %s. Got %v, want %v", name, got, tc.expected)
+		}
+	}
+}
+
+func getUserNodeCounts(t *testing.T) (counts map[string]int32) {
+	s, err := libstats.Value("mounttable/num-nodes-per-user")
+	if err != nil {
+		boom(t, "Can't get mounttable statistics")
+	}
+	// This string is a json encoded map.  Decode.
+	switch v := s.(type) {
+	default:
+		boom(t, "Wrong type for mounttable statistics")
+	case string:
+		err = json.Unmarshal([]byte(v), &counts)
+		if err != nil {
+			boom(t, "Can't unmarshal mounttable statistics")
+		}
+	}
+	return
 }
 
 func TestGlobAccessLists(t *testing.T) {
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", rootCtx)
+	server, estr := newMT(t, "testdata/test.perms", "", "testGlobAccessLists", rootCtx)
 	defer server.Stop()
 
 	// set up a mount space
@@ -496,7 +552,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testCleanup", rootCtx)
 	defer server.Stop()
 
 	// Set up one mount.
@@ -524,7 +580,7 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", rootCtx)
+	server, estr := newMT(t, "testdata/test.perms", "", "testDelete", rootCtx)
 	defer server.Stop()
 
 	// set up a mount space
@@ -551,7 +607,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testerverFormat", rootCtx)
 	defer server.Stop()
 
 	doMount(t, rootCtx, estr, "endpoint", naming.JoinAddressName(estr, "life/on/the/mississippi"), true)
@@ -565,7 +621,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testExpiry", rootCtx)
 	defer server.Stop()
 	collection, collectionAddr := newCollection(t, rootCtx)
 	defer collection.Stop()
@@ -633,7 +689,7 @@
 	ft := NewFakeTimeClock()
 	setServerListClock(ft)
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "mounttable", rootCtx)
 	defer server.Stop()
 
 	// Test flat tree
diff --git a/services/mounttable/mounttablelib/persist_test.go b/services/mounttable/mounttablelib/persist_test.go
index a6b936b..c6b7aca 100644
--- a/services/mounttable/mounttablelib/persist_test.go
+++ b/services/mounttable/mounttablelib/persist_test.go
@@ -25,7 +25,7 @@
 	}
 	defer os.RemoveAll(td)
 	fmt.Printf("temp persist dir %s\n", td)
-	mt, mtAddr := newMT(t, "", td, rootCtx)
+	mt, mtAddr := newMT(t, "", td, "testPersistence", rootCtx)
 
 	perms1 := access.Permissions{
 		"Read":    access.AccessList{In: []security.BlessingPattern{security.AllPrincipals}},
@@ -53,7 +53,7 @@
 	mt.Stop()
 
 	// Restart with the persisted data.
-	mt, mtAddr = newMT(t, "", td, rootCtx)
+	mt, mtAddr = newMT(t, "", td, "testPersistence", rootCtx)
 
 	// Add root as Admin to each of the perms since the mounttable itself will.
 	perms1["Admin"] = access.AccessList{In: []security.BlessingPattern{"root"}}
diff --git a/services/mounttable/mounttablelib/persistentstore.go b/services/mounttable/mounttablelib/persistentstore.go
index 1dfd2f8..0cb614d 100644
--- a/services/mounttable/mounttablelib/persistentstore.go
+++ b/services/mounttable/mounttablelib/persistentstore.go
@@ -26,7 +26,8 @@
 type storeElement struct {
 	N string // Name of affected node
 	V VersionedPermissions
-	D bool // True if the subtree at N has been deleted
+	D bool   // True if the subtree at N has been deleted
+	C string // Creator
 }
 
 // newPersistentStore will read the permissions log from the directory and apply them to the
@@ -132,6 +133,7 @@
 		elems := strings.Split(e.N, "/")
 		n, err := mt.findNode(nil, nil, elems, true, nil)
 		if n != nil || err == nil {
+			n.creator = e.C
 			if e.D {
 				mt.deleteNode(n.parent, elems[len(elems)-1])
 				vlog.VI(2).Infof("deleted %s", e.N)
@@ -152,7 +154,7 @@
 // any duplicate or deleted entries disappear.
 func (s *store) depthFirstPersist(n *node, name string) {
 	if n.explicitPermissions {
-		s.persistPerms(name, n.vPerms)
+		s.persistPerms(name, n.creator, n.vPerms)
 	}
 	for nodeName, c := range n.children {
 		s.depthFirstPersist(c, path.Join(name, nodeName))
@@ -160,10 +162,10 @@
 }
 
 // persistPerms appends a changed permission to the log.
-func (s *store) persistPerms(name string, vPerms *VersionedPermissions) error {
+func (s *store) persistPerms(name, creator string, vPerms *VersionedPermissions) error {
 	s.l.Lock()
 	defer s.l.Unlock()
-	e := storeElement{N: name, V: *vPerms}
+	e := storeElement{N: name, V: *vPerms, C: creator}
 	return s.enc.Encode(&e)
 }
 
diff --git a/services/mounttable/mounttablelib/versionedpermissions.go b/services/mounttable/mounttablelib/versionedpermissions.go
index f68401f..867be02 100644
--- a/services/mounttable/mounttablelib/versionedpermissions.go
+++ b/services/mounttable/mounttablelib/versionedpermissions.go
@@ -8,6 +8,7 @@
 	"encoding/json"
 	"io"
 	"os"
+	"reflect"
 	"strconv"
 	"strings"
 
@@ -19,6 +20,12 @@
 	"v.io/x/lib/vlog"
 )
 
+// Blessings can't include a comma so we use them in made up user ids.  The following distinctions are
+// made so that we can account for them differently.
+const localUser = ",LOCAL,"     // a client that has our public key but no blessing from which we can extract a user name
+const blessedUser = ",BLESSED," // a client with blessings we trust but from which we can't extract a user name
+const unknownUser = ",UNKNOWN," // a client which presents no blessing we trust
+
 // VersionedPermissions associates a Version with a Permissions
 type VersionedPermissions struct {
 	V int32
@@ -45,8 +52,8 @@
 		}
 	}
 	b.P = perm
+	// Increment with possible wrap.
 	b.V++
-	// Protect against wrap.
 	if b.V < 0 {
 		b.V = 0
 	}
@@ -132,6 +139,12 @@
 				vlog.VI(2).Infof("added perms %v to %s", perms, name)
 				if isPattern {
 					n.permsTemplate = perms
+					// Save the pattern prefix as a prefix describing a user.
+					prefix := strings.Join(elems[:len(elems)-1], "/")
+					if prefix != "" {
+						prefix += "/"
+					}
+					mt.userPrefixes = append(mt.userPrefixes, prefix)
 				} else {
 					n.vPerms, _ = n.vPerms.Set(nil, "", perms)
 					n.explicitPermissions = true
@@ -143,3 +156,38 @@
 	}
 	return nil
 }
+
+// pickCreator returns a string matching the blessing of the user performing the creation.  We do this using
+// the user prefixes found when parsing the config.  Eventually we may need a better way to define user
+// prefixes.
+//
+// TODO(p): readdress this argument after we have some experience with real users.
+func (mt *mountTable) pickCreator(ctx *context.T, call security.Call) string {
+	// For each blessing, look for one that has a matching user prefix.  The creator is the perfix
+	// plus one more element.
+	//
+	// The prefixes themselves come from the templates in the config that constrain node names to
+	// match the user.
+	blessings, _ := security.RemoteBlessingNames(ctx, call)
+	for _, b := range blessings {
+		for _, p := range mt.userPrefixes {
+			sb := string(b)
+			if !strings.HasPrefix(sb, p) {
+				continue
+			}
+			suffix := strings.TrimPrefix(sb, p)
+			elems := strings.Split(suffix, "/")
+			return p + elems[0]
+		}
+	}
+	if ctx == nil || call == nil {
+		return localUser
+	}
+	if l, r := call.LocalBlessings().PublicKey(), call.RemoteBlessings().PublicKey(); l != nil && reflect.DeepEqual(l, r) {
+		return localUser
+	}
+	if len(blessings) > 0 {
+		return blessedUser
+	}
+	return unknownUser
+}