github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/dev/dev.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 dev provides a filesystem with simple devices.
    16  package dev
    17  
    18  import (
    19  	"fmt"
    20  	"math"
    21  
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/hostarch"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/fs/ramfs"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/fs/tmpfs"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/inet"
    28  	"github.com/SagerNet/gvisor/pkg/usermem"
    29  )
    30  
    31  // Memory device numbers are from Linux's drivers/char/mem.c
    32  const (
    33  	// Mem device major.
    34  	memDevMajor uint16 = 1
    35  
    36  	// Mem device minors.
    37  	nullDevMinor    uint32 = 3
    38  	zeroDevMinor    uint32 = 5
    39  	fullDevMinor    uint32 = 7
    40  	randomDevMinor  uint32 = 8
    41  	urandomDevMinor uint32 = 9
    42  )
    43  
    44  // TTY major device number comes from include/uapi/linux/major.h.
    45  const (
    46  	ttyDevMinor = 0
    47  	ttyDevMajor = 5
    48  )
    49  
    50  func newCharacterDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, major uint16, minor uint32) *fs.Inode {
    51  	return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
    52  		DeviceID:        devDevice.DeviceID(),
    53  		InodeID:         devDevice.NextIno(),
    54  		BlockSize:       hostarch.PageSize,
    55  		Type:            fs.CharacterDevice,
    56  		DeviceFileMajor: major,
    57  		DeviceFileMinor: minor,
    58  	})
    59  }
    60  
    61  func newMemDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, minor uint32) *fs.Inode {
    62  	return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
    63  		DeviceID:        devDevice.DeviceID(),
    64  		InodeID:         devDevice.NextIno(),
    65  		BlockSize:       hostarch.PageSize,
    66  		Type:            fs.CharacterDevice,
    67  		DeviceFileMajor: memDevMajor,
    68  		DeviceFileMinor: minor,
    69  	})
    70  }
    71  
    72  func newDirectory(ctx context.Context, contents map[string]*fs.Inode, msrc *fs.MountSource) *fs.Inode {
    73  	iops := ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555))
    74  	return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
    75  		DeviceID:  devDevice.DeviceID(),
    76  		InodeID:   devDevice.NextIno(),
    77  		BlockSize: hostarch.PageSize,
    78  		Type:      fs.Directory,
    79  	})
    80  }
    81  
    82  func newSymlink(ctx context.Context, target string, msrc *fs.MountSource) *fs.Inode {
    83  	iops := ramfs.NewSymlink(ctx, fs.RootOwner, target)
    84  	return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
    85  		DeviceID:  devDevice.DeviceID(),
    86  		InodeID:   devDevice.NextIno(),
    87  		BlockSize: hostarch.PageSize,
    88  		Type:      fs.Symlink,
    89  	})
    90  }
    91  
    92  // New returns the root node of a device filesystem.
    93  func New(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
    94  	shm, err := tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, nil /* parent */)
    95  	if err != nil {
    96  		panic(fmt.Sprintf("tmpfs.NewDir failed: %v", err))
    97  	}
    98  
    99  	contents := map[string]*fs.Inode{
   100  		"fd":     newSymlink(ctx, "/proc/self/fd", msrc),
   101  		"stdin":  newSymlink(ctx, "/proc/self/fd/0", msrc),
   102  		"stdout": newSymlink(ctx, "/proc/self/fd/1", msrc),
   103  		"stderr": newSymlink(ctx, "/proc/self/fd/2", msrc),
   104  
   105  		"null": newMemDevice(ctx, newNullDevice(ctx, fs.RootOwner, 0666), msrc, nullDevMinor),
   106  		"zero": newMemDevice(ctx, newZeroDevice(ctx, fs.RootOwner, 0666), msrc, zeroDevMinor),
   107  		"full": newMemDevice(ctx, newFullDevice(ctx, fs.RootOwner, 0666), msrc, fullDevMinor),
   108  
   109  		// This is not as good as /dev/random in linux because go
   110  		// runtime uses sys_random and /dev/urandom internally.
   111  		// According to 'man 4 random', this will be sufficient unless
   112  		// application uses this to generate long-lived GPG/SSL/SSH
   113  		// keys.
   114  		"random":  newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, randomDevMinor),
   115  		"urandom": newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, urandomDevMinor),
   116  
   117  		"shm": shm,
   118  
   119  		// A devpts is typically mounted at /dev/pts to provide
   120  		// pseudoterminal support. Place an empty directory there for
   121  		// the devpts to be mounted over.
   122  		"pts": newDirectory(ctx, nil, msrc),
   123  		// Similarly, applications expect a ptmx device at /dev/ptmx
   124  		// connected to the terminals provided by /dev/pts/. Rather
   125  		// than creating a device directly (which requires a hairy
   126  		// lookup on open to determine if a devpts exists), just create
   127  		// a symlink to the ptmx provided by devpts. (The Linux devpts
   128  		// documentation recommends this).
   129  		//
   130  		// If no devpts is mounted, this will simply be a dangling
   131  		// symlink, which is fine.
   132  		"ptmx": newSymlink(ctx, "pts/ptmx", msrc),
   133  
   134  		"tty": newCharacterDevice(ctx, newTTYDevice(ctx, fs.RootOwner, 0666), msrc, ttyDevMajor, ttyDevMinor),
   135  	}
   136  
   137  	if isNetTunSupported(inet.StackFromContext(ctx)) {
   138  		contents["net"] = newDirectory(ctx, map[string]*fs.Inode{
   139  			"tun": newCharacterDevice(ctx, newNetTunDevice(ctx, fs.RootOwner, 0666), msrc, netTunDevMajor, netTunDevMinor),
   140  		}, msrc)
   141  	}
   142  
   143  	iops := ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555))
   144  	return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
   145  		DeviceID:  devDevice.DeviceID(),
   146  		InodeID:   devDevice.NextIno(),
   147  		BlockSize: hostarch.PageSize,
   148  		Type:      fs.Directory,
   149  	})
   150  }
   151  
   152  // readZeros implements fs.FileOperations.Read with infinite null bytes.
   153  type readZeros struct{}
   154  
   155  // Read implements fs.FileOperations.Read.
   156  func (*readZeros) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
   157  	return dst.ZeroOut(ctx, math.MaxInt64)
   158  }