gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/builtin/raw_volume.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  	"strings"
    26  
    27  	"gitee.com/mysnapcore/mysnapd/interfaces"
    28  	"gitee.com/mysnapcore/mysnapd/interfaces/apparmor"
    29  	"gitee.com/mysnapcore/mysnapd/interfaces/udev"
    30  	"gitee.com/mysnapcore/mysnapd/snap"
    31  )
    32  
    33  const rawVolumeSummary = `allows read/write access to specific disk partition`
    34  
    35  // raw-volume grants full access to a particular disk partition. Since the
    36  // volume is device-specific, it is desirable to limit the plugging snap's
    37  // connection (eg to avoid situations of intending to grant access to a 'data'
    38  // disk on one device but granting access to a 'system' disk on another).
    39  // Therefore, require a snap declaration for connecting the interface at all.
    40  const rawVolumeBaseDeclarationSlots = `
    41    raw-volume:
    42      allow-installation:
    43        slot-snap-type:
    44          - core
    45          - gadget
    46      deny-connection: true
    47      deny-auto-connection: true
    48  `
    49  
    50  // Only allow disk device partitions; not loop, ram, CDROM, generic SCSI,
    51  // network, tape, raid, etc devices
    52  const rawVolumeConnectedPlugAppArmorPath = `
    53  # Description: can access disk partition read/write
    54  %s rw,
    55  
    56  # needed for write access
    57  capability sys_admin,
    58  
    59  # allow read access to sysfs and udev for block devices
    60  @{PROC}/devices r,
    61  /run/udev/data/b[0-9]*:[0-9]* r,
    62  /sys/block/ r,
    63  /sys/devices/**/block/** r,
    64  `
    65  
    66  // The type for this interface
    67  type rawVolumeInterface struct{}
    68  
    69  // Getter for the name of this interface
    70  func (iface *rawVolumeInterface) Name() string {
    71  	return "raw-volume"
    72  }
    73  
    74  func (iface *rawVolumeInterface) StaticInfo() interfaces.StaticInfo {
    75  	return interfaces.StaticInfo{
    76  		Summary:              rawVolumeSummary,
    77  		BaseDeclarationSlots: rawVolumeBaseDeclarationSlots,
    78  	}
    79  }
    80  
    81  func (iface *rawVolumeInterface) String() string {
    82  	return iface.Name()
    83  }
    84  
    85  // https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
    86  //
    87  // For now, only list common devices and skip the following:
    88  // - Acorn MFM mfma-mfmb
    89  // - ACSI ada-adp
    90  // - Parallel port IDE pda-pdd
    91  // - Parallel port ATAPI pf0-3
    92  // - USB block device uba-ubz
    93  //
    94  // The '0' partition number (eg, hda0) is omitted since it refers to the whole
    95  // disk.
    96  
    97  // IDE, MFM, RLL hda-hdt, 1-63 partitions:
    98  const hdPat = `hd[a-t]([1-9]|[1-5][0-9]|6[0-3])`
    99  
   100  // SCSI sda-sdiv, 1-15 partitions:
   101  const sdPat = `sd([a-z]|[a-h][a-z]|i[a-v])([1-9]|1[0-5])`
   102  
   103  // I2O i2o/hda-hddx, 1-15 partitions:
   104  const i2oPat = `i2o/hd([a-z]|[a-c][a-z]|d[a-x])([1-9]|1[0-5])`
   105  
   106  // MMC mmcblk0-999, 1-63 partitions (number of partitions is kernel cmdline
   107  // configurable. Ubuntu uses 32, so use 64 for headroom):
   108  const mmcPat = `mmcblk([0-9]|[1-9][0-9]{1,2})p([1-9]|[1-5][0-9]|6[0-3])`
   109  
   110  // NVMe nvme0-99, 1-63 partitions with 1-63 optional namespaces:
   111  const nvmePat = `nvme([0-9]|[1-9][0-9])(n([1-9]|[1-5][0-9]|6[0-3])){0,1}p([1-9]|[1-5][0-9]|6[0-3])`
   112  
   113  // virtio vda-vdz, 1-63 partitions:
   114  const vdPat = `vd[a-z]([1-9]|[1-5][0-9]|6[0-3])`
   115  
   116  var rawVolumePartitionPattern = regexp.MustCompile(fmt.Sprintf("^/dev/(%s|%s|%s|%s|%s|%s)$", hdPat, sdPat, i2oPat, mmcPat, nvmePat, vdPat))
   117  
   118  const invalidDeviceNodeSlotPathErrFmt = "slot %q path attribute must be a valid device node"
   119  
   120  // Check validity of the defined slot
   121  func (iface *rawVolumeInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
   122  	_, err := verifySlotPathAttribute(&interfaces.SlotRef{Snap: slot.Snap.InstanceName(), Name: slot.Name}, slot, rawVolumePartitionPattern, invalidDeviceNodeSlotPathErrFmt)
   123  	return err
   124  }
   125  
   126  func (iface *rawVolumeInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   127  	cleanedPath, err := verifySlotPathAttribute(slot.Ref(), slot, rawVolumePartitionPattern, invalidDeviceNodeSlotPathErrFmt)
   128  	if err != nil {
   129  		return nil
   130  	}
   131  
   132  	spec.AddSnippet(fmt.Sprintf(rawVolumeConnectedPlugAppArmorPath, cleanedPath))
   133  
   134  	return nil
   135  }
   136  
   137  func (iface *rawVolumeInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   138  	cleanedPath, err := verifySlotPathAttribute(slot.Ref(), slot, rawVolumePartitionPattern, invalidDeviceNodeSlotPathErrFmt)
   139  	if err != nil {
   140  		return nil
   141  	}
   142  
   143  	spec.TagDevice(fmt.Sprintf(`KERNEL=="%s"`, strings.TrimPrefix(cleanedPath, "/dev/")))
   144  
   145  	return nil
   146  }
   147  
   148  func (iface *rawVolumeInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool {
   149  	// Allow what is allowed in the declarations
   150  	return true
   151  }
   152  
   153  func init() {
   154  	registerIface(&rawVolumeInterface{})
   155  }