Merge "veyron/services/mgmt/suidhelper/impl: chown, setuid"
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
index 9326030..b0104d5 100644
--- a/lib/modules/core/mounttable.go
+++ b/lib/modules/core/mounttable.go
@@ -89,7 +89,7 @@
 				output += fmt.Sprintf("R%d=%s[", entry, n.Name)
 				t := ""
 				for _, s := range n.Servers {
-					t += fmt.Sprintf("%s:%s, ", s.Server, s.TTL)
+					t += fmt.Sprintf("%s:%s, ", s.Server, s.Expires)
 				}
 				t = strings.TrimSuffix(t, ", ")
 				output += fmt.Sprintf("%s]\n", t)
diff --git a/profiles/internal/gce/gce_linux_android.go b/profiles/internal/gce/gce_linux_android.go
index eafa31c..24288f1 100644
--- a/profiles/internal/gce/gce_linux_android.go
+++ b/profiles/internal/gce/gce_linux_android.go
@@ -9,7 +9,7 @@
 )
 
 func RunningOnGCE() bool {
-	panic("The GCE profile was unexpectedly used with android.")
+	return false
 }
 
 func ExternalIPAddress() (net.IP, error) {
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index dba781d..39e0649 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -318,9 +318,6 @@
 		ln.Close()
 		return nil, err
 	}
-	if ipaddr == nil {
-		vlog.VI(2).Infof("the address %q requested for listening contained a fixed IP address which disables roaming, use :0 instead", address)
-	}
 
 	s.Lock()
 	if s.stopped {
@@ -330,9 +327,14 @@
 		return nil, errServerStopped
 	}
 
-	h, _, _ := net.SplitHostPort(address)
+	var ip net.IP
+	if ipaddr != nil {
+		ip = net.ParseIP(ipaddr.String())
+	} else {
+		vlog.VI(2).Infof("the address %q requested for listening contained a fixed IP address which disables roaming, use :0 instead", address)
+	}
 	publisher := listenSpec.StreamPublisher
-	if ip := net.ParseIP(h); ip != nil && ip.IsLoopback() && publisher != nil {
+	if ip != nil && !ip.IsLoopback() && publisher != nil {
 		streamName := listenSpec.StreamName
 		ch := make(chan config.Setting)
 		_, err := publisher.ForkStream(streamName, ch)
diff --git a/runtimes/google/lib/publisher/publisher.go b/runtimes/google/lib/publisher/publisher.go
index 83468e2..f7e474a 100644
--- a/runtimes/google/lib/publisher/publisher.go
+++ b/runtimes/google/lib/publisher/publisher.go
@@ -313,17 +313,17 @@
 func (ps *pubState) published() []string {
 	var ret []string
 	for _, name := range ps.names {
-		mtServers, err := ps.ns.ResolveToMountTable(ps.ctx, name)
+		e, err := ps.ns.ResolveToMountTableX(ps.ctx, name)
 		if err != nil {
 			vlog.Errorf("ipc pub: couldn't resolve %v to mount table: %v", name, err)
 			continue
 		}
-		if len(mtServers) == 0 {
+		if len(e.Servers) == 0 {
 			vlog.Errorf("ipc pub: no mount table found for %v", name)
 			continue
 		}
-		for _, s := range mtServers {
-			ret = append(ret, naming.MakeResolvable(s))
+		for _, s := range e.Servers {
+			ret = append(ret, naming.JoinAddressName(s.Server, e.Name))
 		}
 	}
 	return ret
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index ad6a9b0..9c29b25 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -143,7 +143,7 @@
 	if err != nil {
 		boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
 	}
-	compare(t, "ResolveToMoutTable", name, servers, want)
+	compare(t, "ResolveToMountTable", name, servers, want)
 }
 
 func testResolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
diff --git a/runtimes/google/naming/namespace/cache.go b/runtimes/google/naming/namespace/cache.go
index 4686928..01b704e 100644
--- a/runtimes/google/naming/namespace/cache.go
+++ b/runtimes/google/naming/namespace/cache.go
@@ -19,26 +19,25 @@
 
 // cache is a generic interface to the resolution cache.
 type cache interface {
-	remember(prefix string, servers []mountedServer)
+	remember(prefix string, entry *naming.MountEntry)
 	forget(names []string)
-	lookup(name string) ([]mountedServer, string)
+	lookup(name string) (naming.MountEntry, error)
 }
 
 // ttlCache is an instance of cache that obeys ttl from the mount points.
 type ttlCache struct {
 	sync.Mutex
-	epochStart time.Time
-	entries    map[string][]mountedServer
+	entries map[string]naming.MountEntry
 }
 
 // newTTLCache creates an empty ttlCache.
 func newTTLCache() cache {
-	return &ttlCache{epochStart: time.Now(), entries: make(map[string][]mountedServer)}
+	return &ttlCache{entries: make(map[string]naming.MountEntry)}
 }
 
-func isStale(now uint32, servers []mountedServer) bool {
-	for _, s := range servers {
-		if s.TTL <= now {
+func isStale(now time.Time, e naming.MountEntry) bool {
+	for _, s := range e.Servers {
+		if s.Expires.Before(now) {
 			return true
 		}
 	}
@@ -54,11 +53,6 @@
 	return strings.TrimSuffix(name, "/")
 }
 
-// esecs returns seconds since start of this cache's epoch.
-func (c *ttlCache) esecs() uint32 {
-	return uint32(time.Since(c.epochStart).Seconds())
-}
-
 // randomDrop randomly removes one cache entry.  Assumes we've already locked the cache.
 func (c *ttlCache) randomDrop() {
 	n := rand.Intn(len(c.entries))
@@ -74,7 +68,7 @@
 // cleaner reduces the number of entries.  Assumes we've already locked the cache.
 func (c *ttlCache) cleaner() {
 	// First dump any stale entries.
-	now := c.esecs()
+	now := time.Now()
 	for k, v := range c.entries {
 		if len(c.entries) < cacheHisteresisSize {
 			return
@@ -91,12 +85,19 @@
 }
 
 // remember the servers associated with name with suffix removed.
-func (c *ttlCache) remember(prefix string, servers []mountedServer) {
+func (c *ttlCache) remember(prefix string, entry *naming.MountEntry) {
+	// Remove suffix.  We only care about the name that gets us
+	// to the mounttable from the last mounttable.
 	prefix = normalize(prefix)
-	for i := range servers {
-		// Remember when this cached entry times out relative to our epoch.
-		servers[i].TTL += c.esecs()
+	prefix = naming.TrimSuffix(prefix, entry.Name)
+	// Copy the entry.
+	var ce naming.MountEntry
+	for _, s := range entry.Servers {
+		ce.Servers = append(ce.Servers, s)
 	}
+	ce.MT = entry.MT
+	// All keys must be terminal.
+	prefix = naming.MakeTerminal(prefix)
 	c.Lock()
 	// Enforce an upper limit on the cache size.
 	if len(c.entries) >= maxCacheEntries {
@@ -104,7 +105,7 @@
 			c.cleaner()
 		}
 	}
-	c.entries[prefix] = servers
+	c.entries[prefix] = ce
 	c.Unlock()
 }
 
@@ -125,25 +126,26 @@
 }
 
 // lookup searches the cache for a maximal prefix of name and returns the associated servers,
-// prefix, and suffix.  If any of the associated servers is past its TTL, don't return anything
+// prefix, and suffix.  If any of the associated servers is expired, don't return anything
 // since that would reduce availability.
-func (c *ttlCache) lookup(name string) ([]mountedServer, string) {
+func (c *ttlCache) lookup(name string) (naming.MountEntry, error) {
 	name = normalize(name)
 	c.Lock()
 	defer c.Unlock()
-	now := c.esecs()
+	now := time.Now()
 	for prefix, suffix := name, ""; len(prefix) > 0; prefix, suffix = backup(prefix, suffix) {
-		servers, ok := c.entries[prefix]
+		e, ok := c.entries[prefix]
 		if !ok {
 			continue
 		}
-		if isStale(now, servers) {
-			return nil, ""
+		if isStale(now, e) {
+			return e, naming.ErrNoSuchName
 		}
-		vlog.VI(2).Infof("namespace cache %s -> %v %s", name, servers, suffix)
-		return servers, suffix
+		vlog.VI(2).Infof("namespace cache %s -> %v %s", name, e.Servers, e.Name)
+		e.Name = suffix
+		return e, nil
 	}
-	return nil, ""
+	return naming.MountEntry{}, naming.ErrNoSuchName
 }
 
 // backup moves the last element of the prefix to the suffix.  "//" is preserved.  Thus
@@ -170,7 +172,7 @@
 // nullCache is an instance of cache that does nothing.
 type nullCache int
 
-func newNullCache() cache                                         { return nullCache(1) }
-func (nullCache) remember(prefix string, servers []mountedServer) {}
-func (nullCache) forget(names []string)                           {}
-func (nullCache) lookup(name string) ([]mountedServer, string)    { return nil, "" }
+func newNullCache() cache                                             { return nullCache(1) }
+func (nullCache) remember(prefix string, entry *naming.MountEntry)    {}
+func (nullCache) forget(names []string)                               {}
+func (nullCache) lookup(name string) (e naming.MountEntry, err error) { return e, naming.ErrNoSuchName }
diff --git a/runtimes/google/naming/namespace/cache_test.go b/runtimes/google/naming/namespace/cache_test.go
index e3908a5..d707509 100644
--- a/runtimes/google/naming/namespace/cache_test.go
+++ b/runtimes/google/naming/namespace/cache_test.go
@@ -3,17 +3,22 @@
 import (
 	"fmt"
 	"testing"
+	"time"
 
 	"veyron.io/veyron/veyron2/naming"
 )
 
-func compatible(server string, servers []mountedServer) bool {
+func compatible(server string, servers []naming.MountedServer) bool {
 	if len(servers) == 0 {
 		return server == ""
 	}
 	return servers[0].Server == server
 }
 
+func future(secs uint32) time.Time {
+	return time.Now().Add(time.Duration(secs) * time.Second)
+}
+
 // TestCache tests the cache directly rather than via the namespace methods.
 func TestCache(t *testing.T) {
 	preload := []struct {
@@ -27,63 +32,69 @@
 	}
 	c := newTTLCache()
 	for _, p := range preload {
-		c.remember(naming.TrimSuffix(p.name, p.suffix), []mountedServer{mountedServer{Server: p.server, TTL: 30}})
+		e := &naming.MountEntry{Name: p.suffix, Servers: []naming.MountedServer{naming.MountedServer{Server: p.server, Expires: future(30)}}}
+		c.remember(p.name, e)
 	}
 
 	tests := []struct {
-		name   string
-		suffix string
-		server string
+		name    string
+		suffix  string
+		server  string
+		succeed bool
 	}{
-		{"/h1//a/b/c/d", "c/d", "/h2"},
-		{"/h2//c/d", "d", "/h3"},
-		{"/h3//d", "", "/h4:1234"},
-		{"/notintcache", "", ""},
-		{"/h1//a/b/f//g", "f//g", "/h2"},
-		{"/h3//d//e", "//e", "/h4:1234"},
+		{"/h1//a/b/c/d", "c/d", "/h2", true},
+		{"/h2//c/d", "d", "/h3", true},
+		{"/h3//d", "", "/h4:1234", true},
+		{"/notintcache", "", "", false},
+		{"/h1//a/b/f//g", "f//g", "/h2", true},
+		{"/h3//d//e", "//e", "/h4:1234", true},
 	}
 	for _, p := range tests {
-		servers, suffix := c.lookup(p.name)
-		if suffix != p.suffix || !compatible(p.server, servers) {
-			t.Errorf("%s: unexpected depth: got %v, %s not %s, %s", p.name, servers, suffix, p.server, p.suffix)
+		e, err := c.lookup(p.name)
+		if (err == nil) != p.succeed {
+			t.Errorf("%s: lookup failed", p.name)
+		}
+		if e.Name != p.suffix || !compatible(p.server, e.Servers) {
+			t.Errorf("%s: got %v, %s not %s, %s", p.name, e.Name, e.Servers, p.suffix, p.server)
 		}
 	}
 }
 
 func TestCacheLimit(t *testing.T) {
 	c := newTTLCache().(*ttlCache)
-	servers := []mountedServer{mountedServer{Server: "the rain in spain", TTL: 3000}}
+	e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: "the rain in spain", Expires: future(3000)}}}
 	for i := 0; i < maxCacheEntries; i++ {
-		c.remember(fmt.Sprintf("%d", i), servers)
+		c.remember(fmt.Sprintf("%d", i), e)
 		if len(c.entries) > maxCacheEntries {
 			t.Errorf("unexpected cache size: got %d not %d", len(c.entries), maxCacheEntries)
 		}
 	}
 	// Adding one more element should reduce us to 3/4 full.
-	c.remember(fmt.Sprintf("%d", maxCacheEntries), servers)
+	c.remember(fmt.Sprintf("%d", maxCacheEntries), e)
 	if len(c.entries) != cacheHisteresisSize {
 		t.Errorf("cache shrunk wrong amount: got %d not %d", len(c.entries), cacheHisteresisSize)
 	}
 }
 
 func TestCacheTTL(t *testing.T) {
+	before := time.Now()
 	c := newTTLCache().(*ttlCache)
 	// Fill cache.
-	servers := []mountedServer{mountedServer{Server: "the rain in spain", TTL: 3000}}
+	e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: "the rain in spain", Expires: future(3000)}}}
 	for i := 0; i < maxCacheEntries; i++ {
-		c.remember(fmt.Sprintf("%d", i), servers)
+		c.remember(fmt.Sprintf("%d", i), e)
 	}
 	// Time out half the entries.
 	i := len(c.entries) / 2
 	for k := range c.entries {
-		c.entries[k][0].TTL = 0
+		c.entries[k].Servers[0].Expires = before
 		if i == 0 {
 			break
 		}
 		i--
 	}
 	// Add an entry and make sure we now have room.
-	c.remember(fmt.Sprintf("%d", maxCacheEntries+2), servers)
+	c.remember(fmt.Sprintf("%d", maxCacheEntries+2), e)
 	if len(c.entries) > cacheHisteresisSize {
 		t.Errorf("entries did not timeout: got %d not %d", len(c.entries), cacheHisteresisSize)
 	}
@@ -101,7 +112,8 @@
 	ns, _ := New(nil)
 	c := ns.resolutionCache.(*ttlCache)
 	for _, p := range preload {
-		c.remember(p.name, []mountedServer{mountedServer{Server: p.server, TTL: 3000}})
+		e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: "p.server", Expires: future(3000)}}}
+		c.remember(p.name, e)
 	}
 	toflush := "/h1/xyzzy"
 	if ns.FlushCacheEntry(toflush) {
@@ -112,17 +124,17 @@
 		t.Errorf("%s should have caused something to flush", toflush)
 	}
 	name := preload[2].name
-	if _, ok := c.entries[name]; ok {
-		t.Errorf("%s should have been flushed", name)
+	if _, ok := c.entries[name]; !ok {
+		t.Errorf("%s should not have been flushed", name)
 	}
 	if len(c.entries) != 2 {
 		t.Errorf("%s flushed too many entries", toflush)
 	}
+	toflush = preload[1].name
 	if !ns.FlushCacheEntry(toflush) {
 		t.Errorf("%s should have caused something to flush", toflush)
 	}
-	name = preload[1].name
-	if _, ok := c.entries[name]; ok {
+	if _, ok := c.entries[toflush]; ok {
 		t.Errorf("%s should have been flushed", name)
 	}
 	if len(c.entries) != 1 {
@@ -146,8 +158,9 @@
 	name := "/h1//a"
 	serverName := "/h2//"
 	c := ns.resolutionCache.(*ttlCache)
-	c.remember(name, []mountedServer{mountedServer{Server: serverName, TTL: 3000}})
-	if servers, _ := c.lookup(name); servers == nil || servers[0].Server != serverName {
+	e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: serverName, Expires: future(3000)}}}
+	c.remember(name, e)
+	if ne, err := c.lookup(name); err != nil || ne.Servers[0].Server != serverName {
 		t.Errorf("should have found the server in the cache")
 	}
 
@@ -157,8 +170,8 @@
 		t.Errorf("caching not disabled")
 	}
 	nc := ns.resolutionCache.(nullCache)
-	nc.remember(name, []mountedServer{mountedServer{Server: serverName, TTL: 3000}})
-	if servers, _ := nc.lookup(name); servers != nil {
+	nc.remember(name, e)
+	if _, err := nc.lookup(name); err == nil {
 		t.Errorf("should not have found the server in the cache")
 	}
 
@@ -168,8 +181,8 @@
 		t.Errorf("caching disabled")
 	}
 	c = ns.resolutionCache.(*ttlCache)
-	c.remember(name, []mountedServer{mountedServer{Server: serverName, TTL: 3000}})
-	if servers, _ := c.lookup(name); servers == nil || servers[0].Server != serverName {
+	c.remember(name, e)
+	if ne, err := c.lookup(name); err != nil || ne.Servers[0].Server != serverName {
 		t.Errorf("should have found the server in the cache")
 	}
 }
diff --git a/runtimes/google/naming/namespace/glob.go b/runtimes/google/naming/namespace/glob.go
index 0992eaa..89fdfb1 100644
--- a/runtimes/google/naming/namespace/glob.go
+++ b/runtimes/google/naming/namespace/glob.go
@@ -4,12 +4,12 @@
 	"container/list"
 	"io"
 	"strings"
-	"time"
 
 	"veyron.io/veyron/veyron/lib/glob"
 
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/services/mounttable/types"
 	"veyron.io/veyron/veyron2/vlog"
 )
 
@@ -57,7 +57,7 @@
 
 		// At this point we're commited to a server since it answered tha call.
 		for {
-			var e mountEntry
+			var e types.MountEntry
 			err := call.Recv(&e)
 			if err == io.EOF {
 				break
@@ -127,24 +127,6 @@
 	return reply, nil
 }
 
-func convertStringsToServers(servers []string) (ret []naming.MountedServer) {
-	for _, s := range servers {
-		ret = append(ret, naming.MountedServer{Server: s})
-	}
-	return
-}
-
-// TODO(p):  I may just give up and assume that these two will always be the same.  For
-// now this lets me make the RPC interface and the model's MountTable structs be arbitrarily
-// different.
-func convertServers(servers []mountedServer) []naming.MountedServer {
-	var reply []naming.MountedServer
-	for _, s := range servers {
-		reply = append(reply, naming.MountedServer{Server: s.Server, TTL: time.Duration(s.TTL) * time.Second})
-	}
-	return reply
-}
-
 // depth returns the directory depth of a given name.
 func depth(name string) int {
 	name = strings.Trim(name, "/")
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index 35d5c01..8339a8b 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -6,6 +6,7 @@
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/options"
 	"veyron.io/veyron/veyron2/services/mounttable/types"
 	"veyron.io/veyron/veyron2/vlog"
 )
@@ -13,7 +14,7 @@
 // mountIntoMountTable mounts a single server into a single mount table.
 func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration, flags types.MountFlag) error {
 	ctx, _ = ctx.WithTimeout(callTimeout)
-	call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags})
+	call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags}, options.NoResolve(true))
 	if err != nil {
 		return err
 	}
@@ -26,7 +27,7 @@
 // unmountFromMountTable removes a single mounted server from a single mount table.
 func unmountFromMountTable(ctx context.T, client ipc.Client, name, server string) error {
 	ctx, _ = ctx.WithTimeout(callTimeout)
-	call, err := client.StartCall(ctx, name, "Unmount", []interface{}{server})
+	call, err := client.StartCall(ctx, name, "Unmount", []interface{}{server}, options.NoResolve(true))
 	if err != nil {
 		return err
 	}
diff --git a/runtimes/google/naming/namespace/namespace.go b/runtimes/google/naming/namespace/namespace.go
index 88855c0..5c2ceca 100644
--- a/runtimes/google/naming/namespace/namespace.go
+++ b/runtimes/google/naming/namespace/namespace.go
@@ -107,6 +107,32 @@
 	return []string{name}
 }
 
+// rootMountEntry 'roots' a name creating a mount entry for the name.
+func (ns *namespace) rootMountEntry(name string) *naming.MountEntry {
+	e := new(naming.MountEntry)
+	expiration := time.Now().Add(time.Hour) // plenty of time for a call
+	address, suffix := naming.SplitAddressName(name)
+	if len(address) == 0 {
+		e.MT = true
+		e.Name = name
+		ns.RLock()
+		defer ns.RUnlock()
+		for _, r := range ns.roots {
+			e.Servers = append(e.Servers, naming.MountedServer{Server: r, Expires: expiration})
+		}
+		return e
+	}
+	// TODO(p): right now I assume any address handed to me to be resolved is a mount table.
+	// Eventually we should do something like the following:
+	// if ep, err := ns.rt.NewEndpoint(address); err == nil && ep.ServesMountTable() {
+	//	e.MT = true
+	// }
+	e.MT = true
+	e.Name = suffix
+	e.Servers = append(e.Servers, naming.MountedServer{Server: address, Expires: expiration})
+	return e
+}
+
 // notAnMT returns true if the error indicates this isn't a mounttable server.
 func notAnMT(err error) bool {
 	switch verror.ErrorID(err) {
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index f246c06..b6a8315 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -7,40 +7,32 @@
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/options"
+	"veyron.io/veyron/veyron2/services/mounttable/types"
 	"veyron.io/veyron/veyron2/verror"
 	"veyron.io/veyron/veyron2/vlog"
 )
 
-func convertServersToStrings(servers []mountedServer, suffix string) (ret []string) {
-	for _, s := range servers {
-		ret = append(ret, naming.Join(s.Server, suffix))
-	}
-	return
-}
-
-func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, names []string) ([]string, error) {
+func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry) (*naming.MountEntry, error) {
 	// Try each server till one answers.
 	finalErr := errors.New("no servers to resolve query")
-	for _, name := range names {
-		// We want to resolve the name against the MountTable specified in its
-		// address, without recursing through ourselves. To this we force
-		// the entire name component to be terminal.
-		name = naming.MakeTerminal(name)
+	for _, s := range e.Servers {
+		name := naming.JoinAddressName(s.Server, e.Name)
 		// First check the cache.
-		if servers, suffix := ns.resolutionCache.lookup(name); len(servers) > 0 {
-			return convertServersToStrings(servers, suffix), nil
+		if ne, err := ns.resolutionCache.lookup(name); err == nil {
+			vlog.VI(2).Infof("resolveAMT %s from cache -> %v", name, convertServersToStrings(ne.Servers, ne.Name))
+			return &ne, nil
 		}
 		// Not in cache, call the real server.
 		callCtx, _ := ctx.WithTimeout(callTimeout)
-		call, err := client.StartCall(callCtx, name, "ResolveStep", nil)
+		call, err := client.StartCall(callCtx, name, "ResolveStepX", nil, options.NoResolve(true))
 		if err != nil {
 			finalErr = err
 			vlog.VI(2).Infof("ResolveStep.StartCall %s failed: %s", name, err)
 			continue
 		}
-		servers := []mountedServer{}
-		var suffix string
-		ierr := call.Finish(&servers, &suffix, &err)
+		var entry types.MountEntry
+		ierr := call.Finish(&entry, &err)
 		if ierr != nil {
 			// Internal/system error.
 			finalErr = ierr
@@ -57,15 +49,17 @@
 			continue
 		}
 		// Add result to cache.
-		ns.resolutionCache.remember(naming.TrimSuffix(name, suffix), servers)
-		return convertServersToStrings(servers, suffix), nil
+		ne := convertMountEntry(&entry)
+		ns.resolutionCache.remember(name, ne)
+		vlog.VI(2).Infof("resolveAMT %s -> %v", name, *ne)
+		return ne, nil
 	}
 	return nil, finalErr
 }
 
-func terminal(names []string) bool {
-	for _, name := range names {
-		if !naming.Terminal(name) {
+func terminal(e *naming.MountEntry) bool {
+	for _, s := range e.Servers {
+		if !naming.Terminal(naming.JoinAddressName(s.Server, e.Name)) {
 			return false
 		}
 	}
@@ -79,79 +73,86 @@
 	return
 }
 
-// Resolve implements veyron2/naming.Namespace.
-func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
+// ResolveX implements veyron2/naming.Namespace.
+func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
 	defer vlog.LogCall()()
-	names := ns.rootName(name)
+	e := ns.rootMountEntry(name)
 	if vlog.V(2) {
 		_, file, line, _ := runtime.Caller(1)
-		vlog.Infof("Resolve(%s) called from %s:%d", name, file, line)
-		vlog.Infof("Resolve(%s) -> rootNames %s", name, names)
+		vlog.Infof("ResolveX(%s) called from %s:%d", name, file, line)
+		vlog.Infof("ResolveX(%s) -> rootMountEntry %v", name, *e)
 	}
-	if len(names) == 0 {
+	if len(e.Servers) == 0 {
 		return nil, naming.ErrNoMountTable
 	}
 	// Iterate walking through mount table servers.
 	for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
-		vlog.VI(2).Infof("Resolve(%s) loop %s", name, names)
-		if terminal(names) {
-			vlog.VI(1).Infof("Resolve(%s) -> %s", name, names)
-			return names, nil
+		vlog.VI(2).Infof("ResolveX(%s) loop %v", name, *e)
+		if !e.MT || terminal(e) {
+			vlog.VI(1).Infof("ResolveX(%s) -> %v", name, *e)
+			return e, nil
 		}
 		var err error
-		curr := names
-		if names, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
+		curr := e
+		if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr); err != nil {
 			// If the name could not be found in the mount table, return an error.
 			if verror.Equal(naming.ErrNoSuchNameRoot, err) {
 				err = naming.ErrNoSuchName
 			}
 			if verror.Equal(naming.ErrNoSuchName, err) {
-				vlog.VI(1).Infof("Resolve(%s) -> (NoSuchName: %v)", name, curr)
+				vlog.VI(1).Infof("ResolveX(%s) -> (NoSuchName: %v)", name, curr)
 				return nil, err
 			}
 			// Any other failure (server not found, no ResolveStep
 			// method, etc.) are a sign that iterative resolution can
 			// stop.
-			t := makeTerminal(curr)
-			vlog.VI(1).Infof("Resolve(%s) -> %s", name, t)
-			return t, nil
+			vlog.VI(1).Infof("ResolveX(%s) -> %v", name, curr)
+			return curr, nil
 		}
 	}
 	return nil, naming.ErrResolutionDepthExceeded
 }
 
-// ResolveToMountTable implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+// Resolve implements veyron2/naming.Namespace.
+func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
 	defer vlog.LogCall()()
-	names := ns.rootName(name)
+	e, err := ns.ResolveX(ctx, name)
+	if err != nil {
+		return nil, err
+	}
+	return naming.ToStringSlice(e), nil
+}
+
+// ResolveToMountTableX implements veyron2/naming.Namespace.
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
+	defer vlog.LogCall()()
+	e := ns.rootMountEntry(name)
 	if vlog.V(2) {
 		_, file, line, _ := runtime.Caller(1)
-		vlog.Infof("ResolveToMountTable(%s) called from %s:%d", name, file, line)
-		vlog.Infof("ResolveToMountTable(%s) -> rootNames %s", name, names)
+		vlog.Infof("ResolveToMountTableX(%s) called from %s:%d", name, file, line)
+		vlog.Infof("ResolveToMountTableX(%s) -> rootNames %v", name, e)
 	}
-	if len(names) == 0 {
+	if len(e.Servers) == 0 {
 		return nil, naming.ErrNoMountTable
 	}
-	last := names
+	last := e
 	for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
-		vlog.VI(2).Infof("ResolveToMountTable(%s) loop %s", name, names)
+		vlog.VI(2).Infof("ResolveToMountTable(%s) loop %v", name, e)
 		var err error
-		curr := names
-		if terminal(curr) {
-			t := makeTerminal(last)
-			vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
-			return t, nil
+		curr := e
+		// If the next name to resolve doesn't point to a mount table, we're done.
+		if !e.MT || terminal(e) {
+			vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
+			return last, nil
 		}
-		if names, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
+		if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e); err != nil {
 			if verror.Equal(naming.ErrNoSuchNameRoot, err) {
-				t := makeTerminal(last)
-				vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchRoot: %v)", name, t, curr)
-				return t, nil
+				vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchRoot: %v)", name, last, curr)
+				return last, nil
 			}
 			if verror.Equal(naming.ErrNoSuchName, err) {
-				t := makeTerminal(curr)
-				vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchName: %v)", name, t, curr)
-				return t, nil
+				vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchName: %v)", name, curr, curr)
+				return curr, nil
 			}
 			// Lots of reasons why another error can happen.  We are trying
 			// to single out "this isn't a mount table".
@@ -159,22 +160,30 @@
 			// that means "we are up but don't implement what you are
 			// asking for".
 			if notAnMT(err) {
-				t := makeTerminal(last)
-				vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
-				return t, nil
+				vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
+				return last, nil
 			}
 			// TODO(caprita): If the server is unreachable for
 			// example, we may still want to return its parent
 			// mounttable rather than an error.
-			vlog.VI(1).Infof("ResolveToMountTable(%s) -> %v", name, err)
+			vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, err)
 			return nil, err
 		}
-
 		last = curr
 	}
 	return nil, naming.ErrResolutionDepthExceeded
 }
 
+// ResolveToMountTable implements veyron2/naming.Namespace.
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+	defer vlog.LogCall()()
+	e, err := ns.ResolveToMountTableX(ctx, name)
+	if err != nil {
+		return nil, err
+	}
+	return naming.ToStringSlice(e), nil
+}
+
 func finishUnresolve(call ipc.Call) ([]string, error) {
 	var newNames []string
 	var unresolveErr error
@@ -245,14 +254,14 @@
 		// all branches since we want to flush all entries at which we might end up whereas in a resolution,
 		// we stop with the first branch that works.
 		n := naming.MakeTerminal(n)
-		if mts, suffix := ns.resolutionCache.lookup(n); mts != nil {
+		if e, err := ns.resolutionCache.lookup(n); err == nil {
 			// Recurse.
-			for _, server := range mts {
-				flushed = flushed || ns.FlushCacheEntry(naming.Join(server.Server, suffix))
+			for _, s := range e.Servers {
+				flushed = flushed || ns.FlushCacheEntry(naming.Join(s.Server, e.Name))
 			}
 			if !flushed {
 				// Forget the entry we just used.
-				ns.resolutionCache.forget([]string{naming.TrimSuffix(n, suffix)})
+				ns.resolutionCache.forget([]string{naming.TrimSuffix(n, e.Name)})
 				flushed = true
 			}
 		}
diff --git a/runtimes/google/naming/namespace/stub.go b/runtimes/google/naming/namespace/stub.go
index 9661cae..5e1699c 100644
--- a/runtimes/google/naming/namespace/stub.go
+++ b/runtimes/google/naming/namespace/stub.go
@@ -1,21 +1,38 @@
 package namespace
 
-// This file defines data types that are also defined in the idl for the
-// mounttable. We live with the duplication here to avoid having to depend
-// on stubs which in turn depend on the runtime etc.
+import (
+	"time"
 
-// mountedServer mirrors mounttable.MountedServer
-type mountedServer struct {
-	// Server is the OA that's mounted.
-	Server string
-	// TTL is the remaining time (in seconds) before the mount entry expires.
-	TTL uint32
+	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/services/mounttable/types"
+)
+
+func convertServersToStrings(servers []naming.MountedServer, suffix string) (ret []string) {
+	for _, s := range servers {
+		ret = append(ret, naming.Join(s.Server, suffix))
+	}
+	return
 }
 
-// mountEntry mirrors mounttable.MountEntry
-type mountEntry struct {
-	// Name is the mounted name.
-	Name string
-	// Servers (if present) specifies the mounted names (Link is empty).
-	Servers []mountedServer
+func convertStringsToServers(servers []string) (ret []naming.MountedServer) {
+	for _, s := range servers {
+		ret = append(ret, naming.MountedServer{Server: s})
+	}
+	return
+}
+
+func convertServers(servers []types.MountedServer) []naming.MountedServer {
+	var reply []naming.MountedServer
+	for _, s := range servers {
+		if s.TTL == 0 {
+			s.TTL = 32000000 // > 1 year
+		}
+		expires := time.Now().Add(time.Duration(s.TTL) * time.Second)
+		reply = append(reply, naming.MountedServer{Server: s.Server, Expires: expires})
+	}
+	return reply
+}
+
+func convertMountEntry(e *types.MountEntry) *naming.MountEntry {
+	return &naming.MountEntry{Name: e.Name, MT: e.MT, Servers: convertServers(e.Servers)}
 }
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index 58c914c..bff47f6 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -77,6 +77,34 @@
 	return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
 }
 
+func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
+	defer vlog.LogCall()()
+	e := new(naming.MountEntry)
+	if address, _ := naming.SplitAddressName(name); len(address) > 0 {
+		e.Servers = []naming.MountedServer{naming.MountedServer{Server: name, Expires: time.Now().Add(1000 * time.Hour)}}
+		return e, nil
+	}
+	ns.Lock()
+	defer ns.Unlock()
+	for prefix, servers := range ns.mounts {
+		if strings.HasPrefix(name, prefix) {
+			e.Name = strings.TrimLeft(strings.TrimPrefix(name, prefix), "/")
+			for _, s := range servers {
+				e.Servers = append(e.Servers, naming.MountedServer{Server: s, Expires: time.Now().Add(1000 * time.Hour)})
+			}
+			return e, nil
+		}
+	}
+	return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
+}
+
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
+	defer vlog.LogCall()()
+	// TODO(mattr): Implement this method for tests that might need it.
+	panic("ResolveToMountTable not implemented")
+	return nil, nil
+}
+
 func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
 	defer vlog.LogCall()()
 	// TODO(mattr): Implement this method for tests that might need it.
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index ce6e6f3..b052b1b 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -275,7 +275,8 @@
 
 	// Make sure the server name is reasonable.
 	epString, _ := naming.SplitAddressName(server)
-	if _, err := rt.R().NewEndpoint(epString); err != nil {
+	ep, err := rt.R().NewEndpoint(epString)
+	if err != nil {
 		return fmt.Errorf("malformed address %q for mounted server %q", epString, server)
 	}
 
@@ -289,10 +290,11 @@
 	if hasReplaceFlag(flags) {
 		n.mount = nil
 	}
+	wantMT := hasMTFlag(flags) || ep.ServesMountTable()
 	if n.mount == nil {
-		n.mount = &mount{servers: NewServerList(), mt: hasMTFlag(flags)}
+		n.mount = &mount{servers: NewServerList(), mt: wantMT}
 	} else {
-		if hasMTFlag(flags) != n.mount.mt {
+		if wantMT != n.mount.mt {
 			return fmt.Errorf("MT doesn't match")
 		}
 	}
diff --git a/tools/debug/impl.go b/tools/debug/impl.go
index 4448abe..3b4ba10 100644
--- a/tools/debug/impl.go
+++ b/tools/debug/impl.go
@@ -91,7 +91,7 @@
 			}
 			fmt.Fprint(cmd.Stdout(), me.Name)
 			for _, s := range me.Servers {
-				fmt.Fprintf(cmd.Stdout(), " %s (TTL %s)", s.Server, s.TTL)
+				fmt.Fprintf(cmd.Stdout(), " %s (Expires %s)", s.Server, s.Expires)
 			}
 			fmt.Fprintln(cmd.Stdout())
 		}
diff --git a/tools/mounttable/impl.go b/tools/mounttable/impl.go
index 1ad5896..1c88cc7 100644
--- a/tools/mounttable/impl.go
+++ b/tools/mounttable/impl.go
@@ -7,20 +7,27 @@
 	"veyron.io/veyron/veyron/lib/cmdline"
 
 	"veyron.io/veyron/veyron2/context"
+	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/options"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/services/mounttable"
+	"veyron.io/veyron/veyron2/services/mounttable/types"
 )
 
 func bindMT(ctx context.T, name string) (mounttable.MountTable, error) {
-	mts, err := rt.R().Namespace().ResolveToMountTable(ctx, name)
+	e, err := rt.R().Namespace().ResolveToMountTableX(ctx, name)
 	if err != nil {
 		return nil, err
 	}
-	if len(mts) == 0 {
+	if len(e.Servers) == 0 {
 		return nil, fmt.Errorf("Failed to find any mount tables at %q", name)
 	}
-	fmt.Println(mts)
-	return mounttable.BindMountTable(mts[0])
+	var servers []string
+	for _, s := range e.Servers {
+		servers = append(servers, naming.JoinAddressName(s.Server, e.Name))
+	}
+	fmt.Println(servers)
+	return mounttable.BindMountTable(servers[0])
 }
 
 var cmdGlob = &cmdline.Command{
@@ -86,23 +93,38 @@
 }
 
 func runMount(cmd *cmdline.Command, args []string) error {
-	if expected, got := 3, len(args); expected != got {
-		return cmd.UsageErrorf("mount: incorrect number of arguments, expected %d, got %d", expected, got)
+	got := len(args)
+	if got < 2 || got > 4 {
+		return cmd.UsageErrorf("mount: incorrect number of arguments, expected 2, 3, or 4, got %d", got)
+	}
+	var flags types.MountFlag
+	var seconds uint32
+	if got >= 3 {
+		ttl, err := time.ParseDuration(args[2])
+		if err != nil {
+			return fmt.Errorf("TTL parse error: %v", err)
+		}
+		seconds = uint32(ttl.Seconds())
+	}
+	if got >= 4 {
+		for _, c := range args[3] {
+			switch c {
+			case 'M':
+				flags |= types.MountFlag(types.MT)
+			case 'R':
+				flags |= types.MountFlag(types.Replace)
+			}
+		}
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	c, err := bindMT(ctx, args[0])
-	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
-	}
-	ttl, err := time.ParseDuration(args[2])
-	if err != nil {
-		return fmt.Errorf("TTL parse error: %v", err)
-	}
-	err = c.Mount(ctx, args[1], uint32(ttl.Seconds()), 0)
+	call, err := rt.R().Client().StartCall(ctx, args[0], "Mount", []interface{}{args[1], seconds, 0}, options.NoResolve(true))
 	if err != nil {
 		return err
 	}
+	if ierr := call.Finish(&err); ierr != nil {
+		return ierr
+	}
 
 	fmt.Fprintln(cmd.Stdout(), "Name mounted successfully.")
 	return nil
@@ -126,14 +148,13 @@
 	}
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	c, err := bindMT(ctx, args[0])
-	if err != nil {
-		return fmt.Errorf("bind error: %v", err)
-	}
-	err = c.Unmount(ctx, args[1])
+	call, err := rt.R().Client().StartCall(ctx, args[0], "Unmount", []interface{}{args[1]}, options.NoResolve(true))
 	if err != nil {
 		return err
 	}
+	if ierr := call.Finish(&err); ierr != nil {
+		return ierr
+	}
 
 	fmt.Fprintln(cmd.Stdout(), "Name unmounted successfully.")
 	return nil
diff --git a/tools/namespace/impl.go b/tools/namespace/impl.go
index 0980229..5b00ce6 100644
--- a/tools/namespace/impl.go
+++ b/tools/namespace/impl.go
@@ -5,7 +5,7 @@
 	"time"
 
 	"veyron.io/veyron/veyron/lib/cmdline"
-
+	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/vlog"
 )
@@ -38,7 +38,7 @@
 	for res := range c {
 		fmt.Fprint(cmd.Stdout(), res.Name)
 		for _, s := range res.Servers {
-			fmt.Fprintf(cmd.Stdout(), " %s (TTL %s)", s.Server, s.TTL)
+			fmt.Fprintf(cmd.Stdout(), " %s (Expires %s)", s.Server, s.Expires)
 		}
 		fmt.Fprintln(cmd.Stdout())
 	}
@@ -156,13 +156,13 @@
 	ns := rt.R().Namespace()
 	ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	servers, err := ns.ResolveToMountTable(ctx, name)
+	e, err := ns.ResolveToMountTableX(ctx, name)
 	if err != nil {
-		vlog.Infof("ns.ResolveToMountTable(%q) failed: %v", name, err)
+		vlog.Infof("ns.ResolveToMountTableX(%q) failed: %v", name, err)
 		return err
 	}
-	for _, s := range servers {
-		fmt.Fprintln(cmd.Stdout(), s)
+	for _, s := range e.Servers {
+		fmt.Fprintln(cmd.Stdout(), naming.JoinAddressName(s.Server, e.Name))
 	}
 	return nil
 }
diff --git a/tools/naming/simulator/mt_complex.scr b/tools/naming/simulator/mt_complex.scr
index a5c85e2..027ddff 100644
--- a/tools/naming/simulator/mt_complex.scr
+++ b/tools/naming/simulator/mt_complex.scr
@@ -238,7 +238,7 @@
 
 # Now, use mount directly to create a 'symlink'
 set symlink_target=some/deep/name/that/is/a/mount
-mount tl/b/symlink /$mt_b_addr/$symlink_target 1h
+mount tl/b/symlink /$mt_b_addr/$symlink_target 1h M
 wait $_
 
 ls -l tl/b/symlink