profiles/internal/ipc: Pass more errors to the client

The change makes more Glob errors visible to the client.

MultiPart: 1/2
Change-Id: I127f1983a4ef7a8e81720085ec9b2aa3037fdf4d
diff --git a/profiles/internal/ipc/glob_test.go b/profiles/internal/ipc/glob_test.go
index a829c68..28c88b3 100644
--- a/profiles/internal/ipc/glob_test.go
+++ b/profiles/internal/ipc/glob_test.go
@@ -11,6 +11,7 @@
 	"v.io/v23/context"
 	"v.io/v23/i18n"
 	"v.io/v23/ipc"
+	"v.io/v23/ipc/reserved"
 	"v.io/v23/naming"
 	"v.io/v23/security"
 	"v.io/v23/verror"
@@ -59,6 +60,12 @@
 	}
 	defer stop()
 
+	var (
+		noExist        = verror.New(verror.ErrNoExist, ctx, "")
+		notImplemented = ipc.NewErrGlobNotImplemented(ctx, "")
+		maxRecursion   = reserved.NewErrGlobMaxRecursionReached(ctx)
+	)
+
 	testcases := []struct {
 		name, pattern string
 		expected      []string
@@ -78,7 +85,7 @@
 			"a/x/y",
 			"a/x/y/z",
 			"leaf",
-		}, []naming.GlobError{{Name: "leaf", Error: ipc.NewErrGlobNotImplemented(ctx, "leaf")}}},
+		}, []naming.GlobError{{Name: "leaf", Error: notImplemented}}},
 		{"a", "...", []string{
 			"",
 			"b",
@@ -122,7 +129,7 @@
 			"",
 		}, nil},
 		{"", "", []string{""}, nil},
-		{"", "*", []string{"a", "leaf"}, []naming.GlobError{{Name: "leaf", Error: ipc.NewErrGlobNotImplemented(ctx, "leaf")}}},
+		{"", "*", []string{"a", "leaf"}, []naming.GlobError{{Name: "leaf", Error: notImplemented}}},
 		{"a", "", []string{""}, nil},
 		{"a", "*", []string{"b", "x"}, nil},
 		{"a/b", "", []string{""}, nil},
@@ -135,13 +142,16 @@
 		{"a", "*/*", []string{"b/c1", "b/c2", "x/y"}, nil},
 		{"a", "*/*/*", []string{"b/c1/d1", "b/c1/d2", "b/c2/d1", "b/c2/d2", "x/y/z"}, nil},
 		{"a/x", "*/*", []string{"y/z"}, nil},
-		{"bad", "", []string{}, nil},
-		{"a/bad", "", []string{}, nil},
-		{"a/b/bad", "", []string{}, nil},
-		{"a/b/c1/bad", "", []string{}, nil},
-		{"a/x/bad", "", []string{}, nil},
-		{"a/x/y/bad", "", []string{}, nil},
-		{"leaf", "", []string{""}, []naming.GlobError{{Name: "", Error: ipc.NewErrGlobNotImplemented(ctx, "")}}},
+		{"bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
+		{"bad/foo", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
+		{"a/bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
+		{"a/b/bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
+		{"a/b/c1/bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
+		{"a/x/bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
+		{"a/x/y/bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
+		{"leaf", "", []string{""}, []naming.GlobError{{Name: "", Error: notImplemented}}},
+		{"leaf", "*", []string{}, []naming.GlobError{{Name: "", Error: notImplemented}}},
+		{"leaf/foo", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
 		// muah is an infinite space to test rescursion limit.
 		{"muah", "*", []string{"ha"}, nil},
 		{"muah", "*/*", []string{"ha/ha"}, nil},
@@ -158,7 +168,7 @@
 			"ha/ha/ha/ha/ha/ha/ha/ha",
 			"ha/ha/ha/ha/ha/ha/ha/ha/ha",
 			"ha/ha/ha/ha/ha/ha/ha/ha/ha/ha",
-		}, nil},
+		}, []naming.GlobError{{Name: "ha/ha/ha/ha/ha/ha/ha/ha/ha/ha/ha", Error: maxRecursion}}},
 	}
 	for _, tc := range testcases {
 		name := naming.JoinAddressName(ep, tc.name)
@@ -171,11 +181,15 @@
 			t.Errorf("unexpected result for (%q, %q). Got %q, want %q", tc.name, tc.pattern, results, tc.expected)
 		}
 		if len(globErrors) != len(tc.errors) {
-			t.Errorf("unexpected number of glob errors for (%q, %q): %v", tc.name, tc.pattern, globErrors)
+			t.Errorf("unexpected number of glob errors for (%q, %q): got %#v, expected %#v", tc.name, tc.pattern, globErrors, tc.errors)
 		}
 		for i, e := range globErrors {
+			if i >= len(tc.errors) {
+				t.Errorf("unexpected glob error for (%q, %q): %#v", tc.name, tc.pattern, e.Error)
+				continue
+			}
 			if e.Name != tc.errors[i].Name {
-				t.Errorf("unexpected glob errors for (%q, %q): %v", tc.name, tc.pattern, e)
+				t.Errorf("unexpected glob error for (%q, %q): %v", tc.name, tc.pattern, e)
 			}
 			if got, expected := verror.ErrorID(e.Error), verror.ErrorID(tc.errors[i].Error); got != expected {
 				t.Errorf("unexpected error ID for (%q, %q): Got %v, expected %v", tc.name, tc.pattern, got, expected)
@@ -261,7 +275,7 @@
 		return ipc.ChildrenGlobberInvoker("ha"), auth, nil
 
 	}
-	if len(elems) != 0 && elems[0] == "leaf" {
+	if len(elems) != 0 && elems[len(elems)-1] == "leaf" {
 		return leafObject{}, auth, nil
 	}
 	if len(elems) < 2 || (elems[0] == "a" && elems[1] == "x") {
@@ -288,7 +302,7 @@
 	}
 	n := o.n.find(o.suffix, false)
 	if n == nil {
-		return nil, nil
+		return nil, verror.New(verror.ErrNoExist, call.Context(), o.suffix)
 	}
 	ch := make(chan naming.VDLGlobReply)
 	go func() {
@@ -317,10 +331,10 @@
 	suffix []string
 }
 
-func (o *vChildrenObject) GlobChildren__(ipc.ServerCall) (<-chan string, error) {
+func (o *vChildrenObject) GlobChildren__(call ipc.ServerCall) (<-chan string, error) {
 	n := o.n.find(o.suffix, false)
 	if n == nil {
-		return nil, fmt.Errorf("object does not exist")
+		return nil, verror.New(verror.ErrNoExist, call.Context(), o.suffix)
 	}
 	ch := make(chan string, len(n.children))
 	for child, _ := range n.children {
diff --git a/profiles/internal/ipc/reserved.go b/profiles/internal/ipc/reserved.go
index 61f1e7b..aa9cbdb 100644
--- a/profiles/internal/ipc/reserved.go
+++ b/profiles/internal/ipc/reserved.go
@@ -6,11 +6,13 @@
 
 	"v.io/v23/context"
 	"v.io/v23/ipc"
+	"v.io/v23/ipc/reserved"
 	"v.io/v23/naming"
 	"v.io/v23/security"
 	"v.io/v23/services/security/access"
 	"v.io/v23/vdl"
 	"v.io/v23/vdlroot/signature"
+	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 
 	"v.io/x/ref/lib/glob"
@@ -225,15 +227,24 @@
 		call.M.Suffix = naming.Join(i.receiver, state.name)
 		if state.depth > maxRecursiveGlobDepth {
 			vlog.Errorf("ipc Glob: exceeded recursion limit (%d): %q", maxRecursiveGlobDepth, call.Suffix())
+			call.Send(naming.VDLGlobReplyError{
+				naming.GlobError{Name: state.name, Error: reserved.NewErrGlobMaxRecursionReached(call.Context())},
+			})
 			continue
 		}
 		obj, auth, err := disp.Lookup(call.Suffix())
 		if err != nil {
 			vlog.VI(3).Infof("ipc Glob: Lookup failed for %q: %v", call.Suffix(), err)
+			call.Send(naming.VDLGlobReplyError{
+				naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrNoExist, call.Context(), err)},
+			})
 			continue
 		}
 		if obj == nil {
 			vlog.VI(3).Infof("ipc Glob: object not found for %q", call.Suffix())
+			call.Send(naming.VDLGlobReplyError{
+				naming.GlobError{Name: state.name, Error: verror.New(verror.ErrNoExist, call.Context(), "nil object")},
+			})
 			continue
 		}
 
@@ -249,6 +260,9 @@
 		invoker, err := objectToInvoker(obj)
 		if err != nil {
 			vlog.VI(3).Infof("ipc Glob: object for %q cannot be converted to invoker: %v", call.Suffix(), err)
+			call.Send(naming.VDLGlobReplyError{
+				naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, call.Context(), err)},
+			})
 			continue
 		}
 		gs := invoker.Globber()
@@ -266,6 +280,7 @@
 			ch, err := gs.AllGlobber.Glob__(call, state.glob.String())
 			if err != nil {
 				vlog.VI(3).Infof("ipc Glob: %q.Glob(%q) failed: %v", call.Suffix(), state.glob, err)
+				call.Send(naming.VDLGlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, call.Context(), err)}})
 				continue
 			}
 			if ch == nil {
@@ -287,6 +302,7 @@
 		children, err := gs.ChildrenGlobber.GlobChildren__(call)
 		// The requested object doesn't exist.
 		if err != nil {
+			call.Send(naming.VDLGlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, call.Context(), err)}})
 			continue
 		}
 		// The glob pattern matches the current object.