github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/namespace/namespace.go (about)

     1  // Copyright 2020 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package namespace
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  )
    12  
    13  //go:generate stringer -type mountflag
    14  type mountflag int
    15  
    16  //go:generate stringer -type syzcall
    17  type syzcall byte
    18  
    19  // File are a collection of namespace modifiers
    20  type File []Modifier
    21  
    22  // Namespace is a plan9 namespace. It implmenets the
    23  // http://man.cat-v.org/plan_9/2/bind calls.
    24  //
    25  // Bind and mount modify the file name space of the current
    26  // process and other processes in its name space group
    27  // (see http://man.cat-v.org/plan_9/2/fork).
    28  // For both calls, old is the name of an existing file or directory
    29  // in the current name space where the modification is to be made.
    30  // The name old is evaluated as described in
    31  // http://man.cat-v.org/plan_9/2/intro,
    32  // except that no translation of the final path element is done.
    33  type Namespace interface {
    34  	// Bind binds new on old.
    35  	Bind(new, old string, flag mountflag) error
    36  	// Mount mounts servename on old.
    37  	Mount(servername, old, spec string, flag mountflag) error
    38  	// Unmount unmounts new from old, or everything mounted on old if new is missing.
    39  	Unmount(new, old string) error
    40  	// Clear clears the name space with rfork(RFCNAMEG).
    41  	Clear() error
    42  	// Chdir changes the working directory to dir.
    43  	Chdir(dir string) error
    44  	// Import imports a name space from a remote system
    45  	Import(host, remotepath, mountpoint string, flag mountflag) error
    46  }
    47  
    48  // Modifier repesents an individual command that can be applied
    49  // to a plan9 name space which will modify the name space of the process or process group.
    50  type Modifier interface {
    51  	// Modify modifies the namespace
    52  	Modify(ns Namespace, b *Builder) error
    53  	String() string
    54  }
    55  
    56  // NewNS builds a name space for user.
    57  // It opens the file nsfile (/lib/namespace is used if nsfile is ""),
    58  // copies the old environment, erases the current name space,
    59  // sets the environment variables user and home, and interprets the commands in nsfile.
    60  // The format of nsfile is described in namespace(6).
    61  func NewNS(nsfile string, user string) error { return buildNS(nil, nsfile, user, true) }
    62  
    63  // AddNS also interprets and executes the commands in nsfile.
    64  // Unlike newns it applies the command to the current name
    65  // space rather than starting from scratch.
    66  func AddNS(nsfile string, user string) error { return buildNS(nil, nsfile, user, false) }
    67  
    68  func buildNS(ns Namespace, nsfile, user string, new bool) error {
    69  	if err := os.Setenv("user", user); err != nil {
    70  		return err
    71  	}
    72  	if ns == nil {
    73  		ns = DefaultNamespace
    74  	}
    75  	if new {
    76  		ns.Clear()
    77  	}
    78  	r, err := NewBuilder()
    79  	if err != nil {
    80  		return err
    81  	}
    82  	if err := r.Parse(nsfile); err != nil {
    83  		return err
    84  	}
    85  	return r.buildNS(ns)
    86  }
    87  
    88  // OpenFunc opens files for the include or . commands in name space files.
    89  // while the default open function is just os.Open for the file://
    90  // one could extend this to other protocols. Potentially we could
    91  // even import files from the web.eg
    92  //
    93  //	. https://harvey-os.org/lib/namespace@sha256:deadbeef
    94  //
    95  // could be interesting.
    96  type OpenFunc func(path string) (io.Reader, error)
    97  
    98  // Builder helps building plan9 name spaces. Builder keeps track of directory changes
    99  // when another name space file is included, another builder will be created for it's
   100  // modifications, and it's final working directory will be set to be the parents working
   101  // directroy after it's modifications are complete.
   102  type Builder struct {
   103  	dir  string
   104  	file File
   105  
   106  	open OpenFunc
   107  }
   108  
   109  func open1(path string) (io.Reader, error) { return os.Open(path) }
   110  
   111  // NewBuilder returns a builder with defaults
   112  func NewBuilder() (*Builder, error) {
   113  	wd, err := os.Getwd()
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	return &Builder{
   118  		dir:  wd,
   119  		open: open1,
   120  	}, nil
   121  }
   122  
   123  func newBuilder(wd string, b OpenFunc) (*Builder, error) {
   124  	return &Builder{
   125  		dir:  wd,
   126  		open: b,
   127  	}, nil
   128  }
   129  
   130  // Parse takes a path and parses the namespace file
   131  func (b *Builder) Parse(file string) error {
   132  	f, err := b.open(file)
   133  	if err != nil {
   134  		return err
   135  	}
   136  	b.file, err = Parse(f)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	return nil
   141  }
   142  
   143  // Run takes a namespace and runs commands defined in the namespace file
   144  func (b *Builder) buildNS(ns Namespace) error {
   145  	for _, c := range b.file {
   146  		if err := c.Modify(ns, b); err != nil {
   147  			return fmt.Errorf("newns failed to perform %s failed: %v", c, err)
   148  		}
   149  	}
   150  	return nil
   151  }