github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/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  	"github.com/MerlinKodo/gvisor/pkg/context"
    21  	"github.com/MerlinKodo/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  }
    78  
    79  // RegisterDevice registers the given Device in vfs with the given major and
    80  // minor device numbers.
    81  func (vfs *VirtualFilesystem) RegisterDevice(kind DeviceKind, major, minor uint32, dev Device, opts *RegisterDeviceOptions) error {
    82  	tup := devTuple{kind, major, minor}
    83  	vfs.devicesMu.Lock()
    84  	defer vfs.devicesMu.Unlock()
    85  	if existing, ok := vfs.devices[tup]; ok {
    86  		return fmt.Errorf("%s device number (%d, %d) is already registered to device type %T", kind, major, minor, existing.dev)
    87  	}
    88  	vfs.devices[tup] = &registeredDevice{
    89  		dev:  dev,
    90  		opts: *opts,
    91  	}
    92  	return nil
    93  }
    94  
    95  // OpenDeviceSpecialFile returns a FileDescription representing the given
    96  // device.
    97  func (vfs *VirtualFilesystem) OpenDeviceSpecialFile(ctx context.Context, mnt *Mount, d *Dentry, kind DeviceKind, major, minor uint32, opts *OpenOptions) (*FileDescription, error) {
    98  	tup := devTuple{kind, major, minor}
    99  	vfs.devicesMu.RLock()
   100  	defer vfs.devicesMu.RUnlock()
   101  	rd, ok := vfs.devices[tup]
   102  	if !ok {
   103  		return nil, linuxerr.ENXIO
   104  	}
   105  	return rd.dev.Open(ctx, mnt, d, *opts)
   106  }
   107  
   108  // GetDynamicCharDevMajor allocates and returns an unused major device number
   109  // for a character device or set of character devices.
   110  func (vfs *VirtualFilesystem) GetDynamicCharDevMajor() (uint32, error) {
   111  	vfs.dynCharDevMajorMu.Lock()
   112  	defer vfs.dynCharDevMajorMu.Unlock()
   113  	// Compare Linux's fs/char_dev.c:find_dynamic_major().
   114  	for major := uint32(254); major >= 234; major-- {
   115  		if _, ok := vfs.dynCharDevMajorUsed[major]; !ok {
   116  			vfs.dynCharDevMajorUsed[major] = struct{}{}
   117  			return major, nil
   118  		}
   119  	}
   120  	for major := uint32(511); major >= 384; major-- {
   121  		if _, ok := vfs.dynCharDevMajorUsed[major]; !ok {
   122  			vfs.dynCharDevMajorUsed[major] = struct{}{}
   123  			return major, nil
   124  		}
   125  	}
   126  	return 0, linuxerr.EBUSY
   127  }
   128  
   129  // PutDynamicCharDevMajor deallocates a major device number returned by a
   130  // previous call to GetDynamicCharDevMajor.
   131  func (vfs *VirtualFilesystem) PutDynamicCharDevMajor(major uint32) {
   132  	vfs.dynCharDevMajorMu.Lock()
   133  	defer vfs.dynCharDevMajorMu.Unlock()
   134  	delete(vfs.dynCharDevMajorUsed, major)
   135  }
   136  
   137  // GetAnonBlockDevMinor allocates and returns an unused minor device number for
   138  // an "anonymous" block device with major number UNNAMED_MAJOR.
   139  func (vfs *VirtualFilesystem) GetAnonBlockDevMinor() (uint32, error) {
   140  	vfs.anonBlockDevMinorMu.Lock()
   141  	defer vfs.anonBlockDevMinorMu.Unlock()
   142  	minor := vfs.anonBlockDevMinorNext
   143  	const maxDevMinor = (1 << 20) - 1
   144  	for minor < maxDevMinor {
   145  		if _, ok := vfs.anonBlockDevMinor[minor]; !ok {
   146  			vfs.anonBlockDevMinor[minor] = struct{}{}
   147  			vfs.anonBlockDevMinorNext = minor + 1
   148  			return minor, nil
   149  		}
   150  		minor++
   151  	}
   152  	return 0, linuxerr.EMFILE
   153  }
   154  
   155  // PutAnonBlockDevMinor deallocates a minor device number returned by a
   156  // previous call to GetAnonBlockDevMinor.
   157  func (vfs *VirtualFilesystem) PutAnonBlockDevMinor(minor uint32) {
   158  	vfs.anonBlockDevMinorMu.Lock()
   159  	defer vfs.anonBlockDevMinorMu.Unlock()
   160  	delete(vfs.anonBlockDevMinor, minor)
   161  	if minor < vfs.anonBlockDevMinorNext {
   162  		vfs.anonBlockDevMinorNext = minor
   163  	}
   164  }