gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/vfs/device.go (about)

     1  // Copyright 2019 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 vfs
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"gvisor.dev/gvisor/pkg/context"
    21  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    22  )
    23  
    24  // DeviceKind indicates whether a device is a block or character device.
    25  //
    26  // +stateify savable
    27  type DeviceKind uint32
    28  
    29  const (
    30  	// BlockDevice indicates a block device.
    31  	BlockDevice DeviceKind = iota
    32  
    33  	// CharDevice indicates a character device.
    34  	CharDevice
    35  )
    36  
    37  // String implements fmt.Stringer.String.
    38  func (kind DeviceKind) String() string {
    39  	switch kind {
    40  	case BlockDevice:
    41  		return "block"
    42  	case CharDevice:
    43  		return "character"
    44  	default:
    45  		return fmt.Sprintf("invalid device kind %d", kind)
    46  	}
    47  }
    48  
    49  // +stateify savable
    50  type devTuple struct {
    51  	kind  DeviceKind
    52  	major uint32
    53  	minor uint32
    54  }
    55  
    56  // A Device backs device special files.
    57  type Device interface {
    58  	// Open returns a FileDescription representing this device.
    59  	Open(ctx context.Context, mnt *Mount, d *Dentry, opts OpenOptions) (*FileDescription, error)
    60  }
    61  
    62  // +stateify savable
    63  type registeredDevice struct {
    64  	dev  Device
    65  	opts RegisterDeviceOptions
    66  }
    67  
    68  // RegisterDeviceOptions contains options to
    69  // VirtualFilesystem.RegisterDevice().
    70  //
    71  // +stateify savable
    72  type RegisterDeviceOptions struct {
    73  	// GroupName is the name shown for this device registration in
    74  	// /proc/devices. If GroupName is empty, this registration will not be
    75  	// shown in /proc/devices.
    76  	GroupName string
    77  	// Pathname is the name for the device file of this device in /dev directory.
    78  	// If Pathname is empty, then no device file is created.
    79  	Pathname string
    80  	// FilePerms are the permission bits to create the device file with. Only
    81  	// used if Pathname is provided.
    82  	FilePerms uint16
    83  }
    84  
    85  // RegisterDevice registers the given Device in vfs with the given major and
    86  // minor device numbers.
    87  func (vfs *VirtualFilesystem) RegisterDevice(kind DeviceKind, major, minor uint32, dev Device, opts *RegisterDeviceOptions) error {
    88  	tup := devTuple{kind, major, minor}
    89  	vfs.devicesMu.Lock()
    90  	defer vfs.devicesMu.Unlock()
    91  	if existing, ok := vfs.devices[tup]; ok {
    92  		return fmt.Errorf("%s device number (%d, %d) is already registered to device type %T", kind, major, minor, existing.dev)
    93  	}
    94  	vfs.devices[tup] = &registeredDevice{
    95  		dev:  dev,
    96  		opts: *opts,
    97  	}
    98  	return nil
    99  }
   100  
   101  // ForEachDevice calls the given callback for each registered device.
   102  func (vfs *VirtualFilesystem) ForEachDevice(cb func(pathname string, kind DeviceKind, major, minor uint32, perms uint16) error) error {
   103  	vfs.devicesMu.Lock()
   104  	defer vfs.devicesMu.Unlock()
   105  	for tup, dev := range vfs.devices {
   106  		if err := cb(dev.opts.Pathname, tup.kind, tup.major, tup.minor, dev.opts.FilePerms); err != nil {
   107  			return err
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  // OpenDeviceSpecialFile returns a FileDescription representing the given
   114  // device.
   115  func (vfs *VirtualFilesystem) OpenDeviceSpecialFile(ctx context.Context, mnt *Mount, d *Dentry, kind DeviceKind, major, minor uint32, opts *OpenOptions) (*FileDescription, error) {
   116  	tup := devTuple{kind, major, minor}
   117  	vfs.devicesMu.RLock()
   118  	defer vfs.devicesMu.RUnlock()
   119  	rd, ok := vfs.devices[tup]
   120  	if !ok {
   121  		return nil, linuxerr.ENXIO
   122  	}
   123  	return rd.dev.Open(ctx, mnt, d, *opts)
   124  }
   125  
   126  // GetDynamicCharDevMajor allocates and returns an unused major device number
   127  // for a character device or set of character devices.
   128  func (vfs *VirtualFilesystem) GetDynamicCharDevMajor() (uint32, error) {
   129  	vfs.dynCharDevMajorMu.Lock()
   130  	defer vfs.dynCharDevMajorMu.Unlock()
   131  	// Compare Linux's fs/char_dev.c:find_dynamic_major().
   132  	for major := uint32(254); major >= 234; major-- {
   133  		if _, ok := vfs.dynCharDevMajorUsed[major]; !ok {
   134  			vfs.dynCharDevMajorUsed[major] = struct{}{}
   135  			return major, nil
   136  		}
   137  	}
   138  	for major := uint32(511); major >= 384; major-- {
   139  		if _, ok := vfs.dynCharDevMajorUsed[major]; !ok {
   140  			vfs.dynCharDevMajorUsed[major] = struct{}{}
   141  			return major, nil
   142  		}
   143  	}
   144  	return 0, linuxerr.EBUSY
   145  }
   146  
   147  // PutDynamicCharDevMajor deallocates a major device number returned by a
   148  // previous call to GetDynamicCharDevMajor.
   149  func (vfs *VirtualFilesystem) PutDynamicCharDevMajor(major uint32) {
   150  	vfs.dynCharDevMajorMu.Lock()
   151  	defer vfs.dynCharDevMajorMu.Unlock()
   152  	delete(vfs.dynCharDevMajorUsed, major)
   153  }
   154  
   155  // GetAnonBlockDevMinor allocates and returns an unused minor device number for
   156  // an "anonymous" block device with major number UNNAMED_MAJOR.
   157  func (vfs *VirtualFilesystem) GetAnonBlockDevMinor() (uint32, error) {
   158  	vfs.anonBlockDevMinorMu.Lock()
   159  	defer vfs.anonBlockDevMinorMu.Unlock()
   160  	minor := vfs.anonBlockDevMinorNext
   161  	const maxDevMinor = (1 << 20) - 1
   162  	for minor < maxDevMinor {
   163  		if _, ok := vfs.anonBlockDevMinor[minor]; !ok {
   164  			vfs.anonBlockDevMinor[minor] = struct{}{}
   165  			vfs.anonBlockDevMinorNext = minor + 1
   166  			return minor, nil
   167  		}
   168  		minor++
   169  	}
   170  	return 0, linuxerr.EMFILE
   171  }
   172  
   173  // PutAnonBlockDevMinor deallocates a minor device number returned by a
   174  // previous call to GetAnonBlockDevMinor.
   175  func (vfs *VirtualFilesystem) PutAnonBlockDevMinor(minor uint32) {
   176  	vfs.anonBlockDevMinorMu.Lock()
   177  	defer vfs.anonBlockDevMinorMu.Unlock()
   178  	delete(vfs.anonBlockDevMinor, minor)
   179  	if minor < vfs.anonBlockDevMinorNext {
   180  		vfs.anonBlockDevMinorNext = minor
   181  	}
   182  }