github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/partition/partition.go (about) 1 /* 2 Copyright 2020 The OpenEBS Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package partition 18 19 import ( 20 "fmt" 21 22 "github.com/diskfs/go-diskfs" 23 "github.com/diskfs/go-diskfs/disk" 24 "github.com/diskfs/go-diskfs/partition/gpt" 25 "github.com/openebs/node-disk-manager/pkg/blkid" 26 27 "k8s.io/klog/v2" 28 ) 29 30 // TODO this package needs to be upstreamed to diskfs/go-diskfs 31 32 const ( 33 // BytesRequiredForGPTPartitionEntries is the total bytes required to store the GPT partition 34 // entries. 128 bytes are required per partition, total no of partition supported by GPT = 128 35 // Therefore, total bytes = 128*128 36 BytesRequiredForGPTPartitionEntries = 16384 37 38 // GPTPartitionStartByte is the byte on the disk at which the first partition starts. 39 // Normally partition starts at 1MiB, (as done by fdisk utility). This is done to 40 // align the partition start to physical block sizes on the disk. 41 GPTPartitionStartByte = 1048576 42 43 // NoOfLogicalBlocksForGPTHeader is the no. of logical blocks for the GPT header. 44 NoOfLogicalBlocksForGPTHeader = 1 45 46 // OpenEBSNDMPartitionName is the name meta info for openEBS created partitions. 47 OpenEBSNDMPartitionName = "OpenEBS_NDM" 48 ) 49 50 // Disk struct represents a disk which needs to be partitioned 51 type Disk struct { 52 // DevPath is the /dev/sdX entry of the disk 53 DevPath string 54 // DiskSize is size of disk in bytes 55 DiskSize uint64 56 // LogicalBlockSize is the block size of the disk normally 512 or 4k 57 LogicalBlockSize uint64 58 59 table *gpt.Table 60 61 disk *disk.Disk 62 } 63 64 // applyPartitionTable applies the partition table to the given disk. 65 func (d *Disk) applyPartitionTable() error { 66 if len(d.table.Partitions) == 0 { 67 return fmt.Errorf("no partitions specified in partition table") 68 } 69 70 err := d.disk.Partition(d.table) 71 if err != nil { 72 return fmt.Errorf("unable to create/write partition table. %v", err) 73 } 74 return nil 75 } 76 77 // createPartitionTable creates gpt partition table structure with protective MBR turned on 78 func (d *Disk) createPartitionTable() error { 79 if d.DiskSize == 0 { 80 klog.Errorf("disk %s has size zero", d.DevPath) 81 return fmt.Errorf("disk size is zero, unable to initialize partition table") 82 } 83 if d.LogicalBlockSize == 0 { 84 klog.Warningf("logical block size of %s not set, falling back to 512 bytes", d.DevPath) 85 klog.Warning("partitioning may fail.") 86 d.LogicalBlockSize = 512 87 } 88 // set protective MBR to true. 89 // https://en.wikipedia.org/wiki/GUID_Partition_Table#Protective_MBR_(LBA_0) 90 d.table = &gpt.Table{ 91 LogicalSectorSize: int(d.LogicalBlockSize), 92 ProtectiveMBR: true, 93 } 94 return nil 95 } 96 97 // addPartition is used to add a partition to the partition table. 98 // Currently only a single partition can be created i.e, The method can be called only once for a disk. 99 // TODO: @akhilerm, add method to create partition with given size 100 func (d *Disk) addPartition() error { 101 var startSector, endSector uint64 102 if len(d.table.Partitions) == 0 { 103 // First sector of partition is aligned at 1MiB 104 startSector = (GPTPartitionStartByte) / d.LogicalBlockSize 105 } 106 107 PrimaryPartitionTableSize := BytesRequiredForGPTPartitionEntries/d.LogicalBlockSize + NoOfLogicalBlocksForGPTHeader 108 109 // last sector for the partition. Since GPT scheme contains a backup partition table at 110 // the last blocks of the disk. 111 endSector = (d.DiskSize / d.LogicalBlockSize) - PrimaryPartitionTableSize - 1 112 113 partition := &gpt.Partition{ 114 Start: startSector, 115 End: endSector, 116 Type: gpt.LinuxFilesystem, 117 Name: OpenEBSNDMPartitionName, 118 } 119 d.table.Partitions = append(d.table.Partitions, partition) 120 return nil 121 } 122 123 // CreateSinglePartition creates a single GPT partition on the disk 124 // that spans the entire disk 125 func (d *Disk) CreateSinglePartition() error { 126 fd, err := diskfs.Open(d.DevPath) 127 if err != nil { 128 return fmt.Errorf("error opening disk fd for disk %s: %v", d.DevPath, err) 129 } 130 d.disk = fd 131 132 // check for any existing partition table on the disk 133 if _, err := d.disk.GetPartitionTable(); err == nil { 134 klog.Errorf("aborting partition creation, disk %s already contains a known partition table", d.DevPath) 135 return fmt.Errorf("disk %s contains a partition table, cannot create a single partition", d.DevPath) 136 } 137 138 // check for any existing filesystem on the disk 139 deviceIdentifier := blkid.DeviceIdentifier{ 140 DevPath: d.DevPath, 141 } 142 if fs := deviceIdentifier.GetOnDiskFileSystem(); len(fs) != 0 { 143 klog.Errorf("aborting partition creation, disk %s contains a known filesystem: %s", d.DevPath, fs) 144 return fmt.Errorf("disk %s contains a known filesyste: %s, cannot create a single partition", d.DevPath, fs) 145 } 146 147 err = d.createPartitionTable() 148 if err != nil { 149 klog.Error("partition table initialization failed") 150 return err 151 } 152 153 err = d.addPartition() 154 if err != nil { 155 klog.Error("could not add a partition to partition table") 156 return err 157 } 158 159 err = d.applyPartitionTable() 160 if err != nil { 161 klog.Error("writing partition table to disk failed") 162 return err 163 } 164 klog.Infof("created a single partition on disk %s", d.DevPath) 165 return nil 166 } 167 168 // CreatePartitionTable create a GPT header on the disk 169 func (d *Disk) CreatePartitionTable() error { 170 fd, err := diskfs.Open(d.DevPath) 171 if err != nil { 172 return fmt.Errorf("error opening disk fd for disk %s: %v", d.DevPath, err) 173 } 174 d.disk = fd 175 176 // check for any existing partition table on the disk 177 if _, err := d.disk.GetPartitionTable(); err == nil { 178 klog.Errorf("aborting partition creation, disk %s already contains a known partition table", d.DevPath) 179 return fmt.Errorf("disk %s contains a partition table, cannot create a new partition table", d.DevPath) 180 } 181 182 // check for any existing filesystem on the disk 183 deviceIdentifier := blkid.DeviceIdentifier{ 184 DevPath: d.DevPath, 185 } 186 if fs := deviceIdentifier.GetOnDiskFileSystem(); len(fs) != 0 { 187 klog.Errorf("aborting partition creation, disk %s contains a known filesystem: %s", d.DevPath, fs) 188 return fmt.Errorf("disk %s contains a known filesyste: %s, cannot create a partition table", d.DevPath, fs) 189 } 190 191 err = d.createPartitionTable() 192 if err != nil { 193 klog.Error("partition table initialization failed") 194 return err 195 } 196 197 err = d.disk.Partition(d.table) 198 if err != nil { 199 return fmt.Errorf("unable to create/write partition table. %v", err) 200 } 201 klog.Infof("created partition table on disk %s", d.DevPath) 202 return nil 203 }