github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/tmpfs/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 tmpfs
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    25  )
    26  
    27  const (
    28  	// Set initial permissions for the root directory.
    29  	modeKey = "mode"
    30  
    31  	// UID for the root directory.
    32  	rootUIDKey = "uid"
    33  
    34  	// GID for the root directory.
    35  	rootGIDKey = "gid"
    36  
    37  	// cacheKey sets the caching policy for the mount.
    38  	cacheKey = "cache"
    39  
    40  	// cacheAll uses the virtual file system cache for everything (default).
    41  	cacheAll = "cache"
    42  
    43  	// cacheRevalidate allows dirents to be cached, but revalidates them on each
    44  	// lookup.
    45  	cacheRevalidate = "revalidate"
    46  
    47  	// Permissions that exceed modeMask will be rejected.
    48  	modeMask = 01777
    49  
    50  	// Default permissions are read/write/execute.
    51  	defaultMode = 0777
    52  )
    53  
    54  // Filesystem is a tmpfs.
    55  //
    56  // +stateify savable
    57  type Filesystem struct{}
    58  
    59  var _ fs.Filesystem = (*Filesystem)(nil)
    60  
    61  func init() {
    62  	fs.RegisterFilesystem(&Filesystem{})
    63  }
    64  
    65  // FilesystemName is the name under which the filesystem is registered.
    66  // Name matches mm/shmem.c:shmem_fs_type.name.
    67  const FilesystemName = "tmpfs"
    68  
    69  // Name is the name of the file system.
    70  func (*Filesystem) Name() string {
    71  	return FilesystemName
    72  }
    73  
    74  // AllowUserMount allows users to mount(2) this file system.
    75  func (*Filesystem) AllowUserMount() bool {
    76  	return true
    77  }
    78  
    79  // AllowUserList allows this filesystem to be listed in /proc/filesystems.
    80  func (*Filesystem) AllowUserList() bool {
    81  	return true
    82  }
    83  
    84  // Flags returns that there is nothing special about this file system.
    85  //
    86  // In Linux, tmpfs returns FS_USERNS_MOUNT, see mm/shmem.c.
    87  func (*Filesystem) Flags() fs.FilesystemFlags {
    88  	return 0
    89  }
    90  
    91  // Mount returns a tmpfs root that can be positioned in the vfs.
    92  func (f *Filesystem) Mount(ctx context.Context, device string, flags fs.MountSourceFlags, data string, _ interface{}) (*fs.Inode, error) {
    93  	// device is always ignored.
    94  
    95  	// Parse generic comma-separated key=value options, this file system expects them.
    96  	options := fs.GenericMountSourceOptions(data)
    97  
    98  	// Parse the root directory permissions.
    99  	perms := fs.FilePermsFromMode(defaultMode)
   100  	if m, ok := options[modeKey]; ok {
   101  		i, err := strconv.ParseUint(m, 8, 32)
   102  		if err != nil {
   103  			return nil, fmt.Errorf("mode value not parsable 'mode=%s': %v", m, err)
   104  		}
   105  		if i&^modeMask != 0 {
   106  			return nil, fmt.Errorf("invalid mode %q: must be less than %o", m, modeMask)
   107  		}
   108  		perms = fs.FilePermsFromMode(linux.FileMode(i))
   109  		delete(options, modeKey)
   110  	}
   111  
   112  	creds := auth.CredentialsFromContext(ctx)
   113  	owner := fs.FileOwnerFromContext(ctx)
   114  	if uidstr, ok := options[rootUIDKey]; ok {
   115  		uid, err := strconv.ParseInt(uidstr, 10, 32)
   116  		if err != nil {
   117  			return nil, fmt.Errorf("uid value not parsable 'uid=%d': %v", uid, err)
   118  		}
   119  		owner.UID = creds.UserNamespace.MapToKUID(auth.UID(uid))
   120  		delete(options, rootUIDKey)
   121  	}
   122  
   123  	if gidstr, ok := options[rootGIDKey]; ok {
   124  		gid, err := strconv.ParseInt(gidstr, 10, 32)
   125  		if err != nil {
   126  			return nil, fmt.Errorf("gid value not parsable 'gid=%d': %v", gid, err)
   127  		}
   128  		owner.GID = creds.UserNamespace.MapToKGID(auth.GID(gid))
   129  		delete(options, rootGIDKey)
   130  	}
   131  
   132  	// Construct a mount which will follow the cache options provided.
   133  	//
   134  	// TODO(github.com/SagerNet/issue/179): There should be no reason to disable
   135  	// caching once bind mounts are properly supported.
   136  	var msrc *fs.MountSource
   137  	switch options[cacheKey] {
   138  	case "", cacheAll:
   139  		msrc = fs.NewCachingMountSource(ctx, f, flags)
   140  	case cacheRevalidate:
   141  		msrc = fs.NewRevalidatingMountSource(ctx, f, flags)
   142  	default:
   143  		return nil, fmt.Errorf("invalid cache policy option %q", options[cacheKey])
   144  	}
   145  	delete(options, cacheKey)
   146  
   147  	// Fail if the caller passed us more options than we can parse. They may be
   148  	// expecting us to set something we can't set.
   149  	if len(options) > 0 {
   150  		return nil, fmt.Errorf("unsupported mount options: %v", options)
   151  	}
   152  
   153  	// Construct the tmpfs root.
   154  	return NewDir(ctx, nil, owner, perms, msrc, nil /* parent */)
   155  }