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 }