Description

A NameSpace is a file system made up of other file systems mounted at specific locations in the name space.

The representation is a map from mount point locations to the list of file systems mounted at that location. A traditional Unix mount table would use a single file system per mount point, but we want to be able to mount multiple file systems on a single mount point and have the system behave as if the union of those file systems were present at the mount point. For example, if the OS file system has a Go installation in c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then this name space creates the view we want for the godoc server:

NameSpace{
	"/": {
		{old: "/", fs: OS(`c:\Go`), new: "/"},
	},
	"/src/pkg": {
		{old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
		{old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
		{old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
	},
}

This is created by executing:

ns := NameSpace{}
ns.Bind("/", OS(`c:\Go`), "/", BindReplace)
ns.Bind("/src/pkg", OS(`d:\Work1`), "/src", BindAfter)
ns.Bind("/src/pkg", OS(`d:\Work2`), "/src", BindAfter)

A particular mount point entry is a triple (old, fs, new), meaning that to operate on a path beginning with old, replace that prefix (old) with new and then pass that path to the FileSystem implementation fs.

If you do not explicitly mount a FileSystem at the root mountpoint "/" of the NameSpace like above, Stat("/") will return a "not found" error which could break typical directory traversal routines. In such cases, use NewNameSpace() to get a NameSpace pre-initialized with an emulated empty directory at root.

Given this name space, a ReadDir of /src/pkg/code will check each prefix of the path for a mount point (first /src/pkg/code, then /src/pkg, then /src, then /), stopping when it finds one. For the above example, /src/pkg/code will find the mount point at /src/pkg:

{old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
{old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
{old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},

ReadDir will when execute these three calls and merge the results:

OS(`c:\Go`).ReadDir("/src/pkg/code")
OS(`d:\Work1').ReadDir("/src/code")
OS(`d:\Work2').ReadDir("/src/code")

Note that the "/src/pkg" in "/src/pkg/code" has been replaced by just "/src" in the final two calls.

OS is itself an implementation of a file system: it implements OS(`c:\Go`).ReadDir("/src/pkg/code") as ioutil.ReadDir(`c:\Go\src\pkg\code`).

Because the new path is evaluated by fs (here OS(root)), another way to read the mount table is to mentally combine fs+new, so that this table:

{old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
{old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
{old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},

reads as:

"/src/pkg" -> c:\Go\src\pkg
"/src/pkg" -> d:\Work1\src
"/src/pkg" -> d:\Work2\src

An invariant (a redundancy) of the name space representation is that ns[mtpt][i].old is always equal to mtpt (in the example, ns["/src/pkg"]'s mount table entries always have old == "/src/pkg"). The 'old' field is useful to callers, because they receive just a []mountedFS and not any other indication of which mount point was found.

NameSpace is referenced in 15 repositories

github.com/golang/tools github.com/golang-china/golangdoc github.com/Go-zh/godoc-i18n

...