github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/filesystems.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fs
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/sync"
    24  )
    25  
    26  // FilesystemFlags matches include/linux/fs.h:file_system_type.fs_flags.
    27  type FilesystemFlags int
    28  
    29  const (
    30  	// FilesystemRequiresDev indicates that the file system requires a device name
    31  	// on mount. It is used to construct the output of /proc/filesystems.
    32  	FilesystemRequiresDev FilesystemFlags = 1
    33  
    34  	// Currently other flags are not used, but can be pulled in from
    35  	// include/linux/fs.h:file_system_type as needed.
    36  )
    37  
    38  // Filesystem is a mountable file system.
    39  type Filesystem interface {
    40  	// Name is the unique identifier of the file system. It corresponds to the
    41  	// filesystemtype argument of sys_mount and will appear in the output of
    42  	// /proc/filesystems.
    43  	Name() string
    44  
    45  	// Flags indicate common properties of the file system.
    46  	Flags() FilesystemFlags
    47  
    48  	// Mount generates a mountable Inode backed by device and configured
    49  	// using file system independent flags and file system dependent
    50  	// data options.
    51  	//
    52  	// Mount may return arbitrary errors. They do not need syserr translations.
    53  	Mount(ctx context.Context, device string, flags MountSourceFlags, data string, dataObj interface{}) (*Inode, error)
    54  
    55  	// AllowUserMount determines whether mount(2) is allowed to mount a
    56  	// file system of this type.
    57  	AllowUserMount() bool
    58  
    59  	// AllowUserList determines whether this filesystem is listed in
    60  	// /proc/filesystems
    61  	AllowUserList() bool
    62  }
    63  
    64  // filesystems is the global set of registered file systems. It does not need
    65  // to be saved. Packages registering and unregistering file systems must do so
    66  // before calling save/restore methods.
    67  var filesystems = struct {
    68  	// mu protects registered below.
    69  	mu sync.Mutex
    70  
    71  	// registered is a set of registered Filesystems.
    72  	registered map[string]Filesystem
    73  }{
    74  	registered: make(map[string]Filesystem),
    75  }
    76  
    77  // RegisterFilesystem registers a new file system that is visible to mount and
    78  // the /proc/filesystems list. Packages implementing Filesystem should call
    79  // RegisterFilesystem in init().
    80  func RegisterFilesystem(f Filesystem) {
    81  	filesystems.mu.Lock()
    82  	defer filesystems.mu.Unlock()
    83  
    84  	if _, ok := filesystems.registered[f.Name()]; ok {
    85  		panic(fmt.Sprintf("filesystem already registered at %q", f.Name()))
    86  	}
    87  	filesystems.registered[f.Name()] = f
    88  }
    89  
    90  // FindFilesystem returns a Filesystem registered at name or (nil, false) if name
    91  // is not a file system type that can be found in /proc/filesystems.
    92  func FindFilesystem(name string) (Filesystem, bool) {
    93  	filesystems.mu.Lock()
    94  	defer filesystems.mu.Unlock()
    95  
    96  	f, ok := filesystems.registered[name]
    97  	return f, ok
    98  }
    99  
   100  // GetFilesystems returns the set of registered filesystems in a consistent order.
   101  func GetFilesystems() []Filesystem {
   102  	filesystems.mu.Lock()
   103  	defer filesystems.mu.Unlock()
   104  
   105  	var ss []Filesystem
   106  	for _, s := range filesystems.registered {
   107  		ss = append(ss, s)
   108  	}
   109  	sort.Slice(ss, func(i, j int) bool { return ss[i].Name() < ss[j].Name() })
   110  	return ss
   111  }
   112  
   113  // MountSourceFlags represents all mount option flags as a struct.
   114  //
   115  // +stateify savable
   116  type MountSourceFlags struct {
   117  	// ReadOnly corresponds to mount(2)'s "MS_RDONLY" and indicates that
   118  	// the filesystem should be mounted read-only.
   119  	ReadOnly bool
   120  
   121  	// NoAtime corresponds to mount(2)'s "MS_NOATIME" and indicates that
   122  	// the filesystem should not update access time in-place.
   123  	NoAtime bool
   124  
   125  	// ForcePageCache causes all filesystem I/O operations to use the page
   126  	// cache, even when the platform supports direct mapped I/O. This
   127  	// doesn't correspond to any Linux mount options.
   128  	ForcePageCache bool
   129  
   130  	// NoExec corresponds to mount(2)'s "MS_NOEXEC" and indicates that
   131  	// binaries from this file system can't be executed.
   132  	NoExec bool
   133  }
   134  
   135  // GenericMountSourceOptions splits a string containing comma separated tokens of the
   136  // format 'key=value' or 'key' into a map of keys and values. For example:
   137  //
   138  // data = "key0=value0,key1,key2=value2" -> map{'key0':'value0','key1':'','key2':'value2'}
   139  //
   140  // If data contains duplicate keys, then the last token wins.
   141  func GenericMountSourceOptions(data string) map[string]string {
   142  	options := make(map[string]string)
   143  	if len(data) == 0 {
   144  		// Don't return a nil map, callers might not be expecting that.
   145  		return options
   146  	}
   147  
   148  	// Parse options and skip empty ones.
   149  	for _, opt := range strings.Split(data, ",") {
   150  		if len(opt) > 0 {
   151  			res := strings.SplitN(opt, "=", 2)
   152  			if len(res) == 2 {
   153  				options[res[0]] = res[1]
   154  			} else {
   155  				options[opt] = ""
   156  			}
   157  		}
   158  	}
   159  	return options
   160  }