github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/specutils/fs.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 specutils
    16  
    17  import (
    18  	"fmt"
    19  	"math/bits"
    20  	"path"
    21  	"strings"
    22  
    23  	specs "github.com/opencontainers/runtime-spec/specs-go"
    24  	"golang.org/x/sys/unix"
    25  )
    26  
    27  type mapping struct {
    28  	set bool
    29  	val uint32
    30  }
    31  
    32  // optionsMap maps mount propagation-related OCI filesystem options to mount(2)
    33  // syscall flags.
    34  var optionsMap = map[string]mapping{
    35  	"acl":           {set: true, val: unix.MS_POSIXACL},
    36  	"async":         {set: false, val: unix.MS_SYNCHRONOUS},
    37  	"atime":         {set: false, val: unix.MS_NOATIME},
    38  	"bind":          {set: true, val: unix.MS_BIND},
    39  	"defaults":      {set: true, val: 0},
    40  	"dev":           {set: false, val: unix.MS_NODEV},
    41  	"diratime":      {set: false, val: unix.MS_NODIRATIME},
    42  	"dirsync":       {set: true, val: unix.MS_DIRSYNC},
    43  	"exec":          {set: false, val: unix.MS_NOEXEC},
    44  	"noexec":        {set: true, val: unix.MS_NOEXEC},
    45  	"iversion":      {set: true, val: unix.MS_I_VERSION},
    46  	"loud":          {set: false, val: unix.MS_SILENT},
    47  	"mand":          {set: true, val: unix.MS_MANDLOCK},
    48  	"noacl":         {set: false, val: unix.MS_POSIXACL},
    49  	"noatime":       {set: true, val: unix.MS_NOATIME},
    50  	"nodev":         {set: true, val: unix.MS_NODEV},
    51  	"nodiratime":    {set: true, val: unix.MS_NODIRATIME},
    52  	"noiversion":    {set: false, val: unix.MS_I_VERSION},
    53  	"nomand":        {set: false, val: unix.MS_MANDLOCK},
    54  	"norelatime":    {set: false, val: unix.MS_RELATIME},
    55  	"nostrictatime": {set: false, val: unix.MS_STRICTATIME},
    56  	"nosuid":        {set: true, val: unix.MS_NOSUID},
    57  	"rbind":         {set: true, val: unix.MS_BIND | unix.MS_REC},
    58  	"relatime":      {set: true, val: unix.MS_RELATIME},
    59  	"remount":       {set: true, val: unix.MS_REMOUNT},
    60  	"ro":            {set: true, val: unix.MS_RDONLY},
    61  	"rw":            {set: false, val: unix.MS_RDONLY},
    62  	"silent":        {set: true, val: unix.MS_SILENT},
    63  	"strictatime":   {set: true, val: unix.MS_STRICTATIME},
    64  	"suid":          {set: false, val: unix.MS_NOSUID},
    65  	"sync":          {set: true, val: unix.MS_SYNCHRONOUS},
    66  }
    67  
    68  // verityMountOptions is the set of valid verity mount option keys.
    69  var verityMountOptions = map[string]struct{}{
    70  	"verity.roothash": struct{}{},
    71  	"verity.action":   struct{}{},
    72  }
    73  
    74  // propOptionsMap is similar to optionsMap, but it lists propagation options
    75  // that cannot be used together with other flags.
    76  var propOptionsMap = map[string]mapping{
    77  	"private":     {set: true, val: unix.MS_PRIVATE},
    78  	"rprivate":    {set: true, val: unix.MS_PRIVATE | unix.MS_REC},
    79  	"slave":       {set: true, val: unix.MS_SLAVE},
    80  	"rslave":      {set: true, val: unix.MS_SLAVE | unix.MS_REC},
    81  	"unbindable":  {set: true, val: unix.MS_UNBINDABLE},
    82  	"runbindable": {set: true, val: unix.MS_UNBINDABLE | unix.MS_REC},
    83  }
    84  
    85  // invalidOptions list options not allowed.
    86  //   - shared: sandbox must be isolated from the host. Propagating mount changes
    87  //     from the sandbox to the host breaks the isolation.
    88  var invalidOptions = []string{"shared", "rshared"}
    89  
    90  // OptionsToFlags converts mount options to syscall flags.
    91  func OptionsToFlags(opts []string) uint32 {
    92  	return optionsToFlags(opts, optionsMap)
    93  }
    94  
    95  // PropOptionsToFlags converts propagation mount options to syscall flags.
    96  // Propagation options cannot be set other with other options and must be
    97  // handled separately.
    98  func PropOptionsToFlags(opts []string) uint32 {
    99  	return optionsToFlags(opts, propOptionsMap)
   100  }
   101  
   102  func optionsToFlags(opts []string, source map[string]mapping) uint32 {
   103  	var rv uint32
   104  	for _, opt := range opts {
   105  		if m, ok := source[opt]; ok {
   106  			if m.set {
   107  				rv |= m.val
   108  			} else {
   109  				rv ^= m.val
   110  			}
   111  		}
   112  	}
   113  	return rv
   114  }
   115  
   116  // validateMount validates that spec mounts are correct.
   117  func validateMount(mnt *specs.Mount) error {
   118  	if !path.IsAbs(mnt.Destination) {
   119  		return fmt.Errorf("Mount.Destination must be an absolute path: %v", mnt)
   120  	}
   121  	if mnt.Type == "bind" {
   122  		return ValidateMountOptions(mnt.Options)
   123  	}
   124  	return nil
   125  }
   126  
   127  func moptKey(opt string) string {
   128  	if len(opt) == 0 {
   129  		return opt
   130  	}
   131  	// Guaranteed to have at least one token, since opt is not empty.
   132  	return strings.SplitN(opt, "=", 2)[0]
   133  }
   134  
   135  // ValidateMountOptions validates that mount options are correct.
   136  func ValidateMountOptions(opts []string) error {
   137  	for _, o := range opts {
   138  		if ContainsStr(invalidOptions, o) {
   139  			return fmt.Errorf("mount option %q is not supported", o)
   140  		}
   141  		_, ok1 := optionsMap[o]
   142  		_, ok2 := propOptionsMap[o]
   143  		_, ok3 := verityMountOptions[moptKey(o)]
   144  		if !ok1 && !ok2 && !ok3 {
   145  			return fmt.Errorf("unknown mount option %q", o)
   146  		}
   147  		if err := validatePropagation(o); err != nil {
   148  			return err
   149  		}
   150  	}
   151  	return nil
   152  }
   153  
   154  // ValidateRootfsPropagation validates that rootfs propagation options are
   155  // correct.
   156  func validateRootfsPropagation(opt string) error {
   157  	flags := PropOptionsToFlags([]string{opt})
   158  	if flags&(unix.MS_SLAVE|unix.MS_PRIVATE) == 0 {
   159  		return fmt.Errorf("root mount propagation option must specify private or slave: %q", opt)
   160  	}
   161  	return validatePropagation(opt)
   162  }
   163  
   164  func validatePropagation(opt string) error {
   165  	flags := PropOptionsToFlags([]string{opt})
   166  	exclusive := flags & (unix.MS_SLAVE | unix.MS_PRIVATE | unix.MS_SHARED | unix.MS_UNBINDABLE)
   167  	if bits.OnesCount32(exclusive) > 1 {
   168  		return fmt.Errorf("mount propagation options are mutually exclusive: %q", opt)
   169  	}
   170  	return nil
   171  }