github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/mount/block/vm_test.go (about) 1 // Copyright 2017-2020 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !race 6 // +build !race 7 8 package block 9 10 import ( 11 "fmt" 12 "log" 13 "os" 14 "reflect" 15 "syscall" 16 "testing" 17 18 "github.com/rekby/gpt" 19 "github.com/mvdan/u-root-coreutils/pkg/mount" 20 "github.com/mvdan/u-root-coreutils/pkg/pci" 21 "github.com/mvdan/u-root-coreutils/pkg/qemu" 22 "github.com/mvdan/u-root-coreutils/pkg/vmtest" 23 ) 24 25 // VM setup: 26 // 27 // /dev/sda is ./testdata/mbrdisk 28 // /dev/sda1 is ext4 29 // /dev/sda2 is vfat 30 // /dev/sda3 is fat32 31 // /dev/sda4 is xfs 32 // 33 // /dev/sdb is ../testdata/12Kzeros 34 // /dev/sdb1 exists, but is not formatted. 35 // 36 // /dev/sdc and /dev/nvme0n1 are ../testdata/gptdisk 37 // /dev/sdc1 and /dev/nvme0n1p1 exist (EFI system partition), but is not formatted 38 // /dev/sdc2 and /dev/nvme0n1p2 exist (Linux), but is not formatted 39 // 40 // /dev/sdd is ./testdata/gptdisk_label 41 // /dev/sdd1 is ext4 with no GPT partition label 42 // /dev/sdd2 is ext4 with GPT partition label "TEST_LABEL" 43 // 44 // ARM tests will load drives as virtio-blk devices (/dev/vd*) 45 46 func TestVM(t *testing.T) { 47 o := &vmtest.Options{ 48 QEMUOpts: qemu.Options{ 49 Devices: []qemu.Device{ 50 // CONFIG_ATA_PIIX is required for this option to work. 51 qemu.ArbitraryArgs{"-hda", "testdata/mbrdisk"}, 52 qemu.ArbitraryArgs{"-hdb", "../testdata/12Kzeros"}, 53 qemu.ArbitraryArgs{"-hdc", "../testdata/gptdisk"}, 54 qemu.ArbitraryArgs{"-hdd", "testdata/gptdisk_label"}, 55 qemu.ArbitraryArgs{"-drive", "file=../testdata/gptdisk2,if=none,id=NVME1"}, 56 // use-intel-id uses the vendor=0x8086 and device=0x5845 ids for NVME 57 qemu.ArbitraryArgs{"-device", "nvme,drive=NVME1,serial=nvme-1,use-intel-id"}, 58 59 // With NVMe devices enabled, kernel crashes when not using q35 machine model. 60 qemu.ArbitraryArgs{"-machine", "q35"}, 61 }, 62 }, 63 } 64 vmtest.GolangTest(t, []string{"github.com/mvdan/u-root-coreutils/pkg/mount/block"}, o) 65 } 66 67 func TestBlockDevMount(t *testing.T) { 68 if os.Getuid() != 0 { 69 t.Skip("Skipping since we are not root") 70 } 71 72 prefix := getDevicePrefix() 73 devs := testDevs(t) 74 devs = devs.FilterName("sdd1") 75 want := BlockDevices{ 76 &BlockDev{Name: prefix + "d1", FsUUID: "02175989-d49f-4e8e-836e-99300af66fc1"}, 77 } 78 if !reflect.DeepEqual(devs, want) { 79 t.Fatalf("Test block devices: \n\t%v \nwant: \n\t%v", devs, want) 80 } 81 82 mountPath := t.TempDir() 83 84 // Only testing for the right calls to the mount pkg here. 85 // Mounting itself is out of scope here and covered in pkg mount. 86 87 dev := devs[0] // FSType unset 88 mp, err := dev.Mount(mountPath, mount.ReadOnly) 89 if err != nil { 90 t.Errorf("%s.Mount() = _,%v \nunexpected error", dev.Name, err) 91 } 92 if err := mp.Unmount(0); err != nil { 93 t.Fatal(err) 94 } 95 96 dev.FSType = "ext4" // FSType set 97 mp, err = dev.Mount(mountPath, mount.ReadOnly) 98 if err != nil { 99 t.Errorf("%s.Mount() = _,%v \nunexpected error", dev.Name, err) 100 } 101 if err := mp.Unmount(0); err != nil { 102 t.Fatal(err) 103 } 104 } 105 106 func TestBlockDevGPTTable(t *testing.T) { 107 if os.Getuid() != 0 { 108 t.Skip("Skipping since we are not root") 109 } 110 111 devpath := fmt.Sprintf("/dev/%sc", getDevicePrefix()) 112 113 sdcDisk, err := Device(devpath) 114 if err != nil { 115 t.Fatal(err) 116 } 117 118 parts, err := sdcDisk.GPTTable() 119 if err != nil { 120 t.Fatal(err) 121 } 122 wantParts := []gpt.Partition{ 123 {FirstLBA: 34, LastLBA: 200}, 124 {FirstLBA: 201, LastLBA: 366}, 125 } 126 for i, p := range parts.Partitions { 127 if !p.IsEmpty() { 128 want := wantParts[i] 129 if p.FirstLBA != want.FirstLBA { 130 t.Errorf("partition %d: got FirstLBA %d want %d", i, p.FirstLBA, want.FirstLBA) 131 } 132 if p.LastLBA != want.LastLBA { 133 t.Errorf("partition %d: got LastLBA %d want %d", i, p.LastLBA, want.LastLBA) 134 } 135 136 t.Logf("partition: %v", p) 137 } 138 } 139 } 140 141 func TestBlockDevSizes(t *testing.T) { 142 if os.Getuid() != 0 { 143 t.Skip("Skipping since we are not root") 144 } 145 146 prefix := getDevicePrefix() 147 devs := testDevs(t) 148 149 sizes := map[string]uint64{ 150 "nvme0n1": 50 * 4096, 151 "nvme0n1p1": 167 * 512, 152 "nvme0n1p2": 166 * 512, 153 prefix + "a": 36864 * 512, 154 prefix + "a1": 1024 * 512, 155 prefix + "a2": 1023 * 512, 156 prefix + "a3": 1024 * 512, 157 prefix + "a4": 32768 * 512, 158 prefix + "b": 24 * 512, 159 prefix + "b1": 23 * 512, 160 prefix + "c": 50 * 4096, 161 prefix + "c1": 167 * 512, 162 prefix + "c2": 166 * 512, 163 prefix + "d": 512 * 4096, 164 prefix + "d1": 1025 * 512, 165 prefix + "d2": 990 * 512, 166 } 167 for _, dev := range devs { 168 size, err := dev.Size() 169 if err != nil { 170 t.Errorf("Size(%s) error: %v", dev, err) 171 } 172 if size != sizes[dev.Name] { 173 t.Errorf("Size(%s) = %v, want %v", dev, size, sizes[dev.Name]) 174 } 175 } 176 177 wantBlkSize := 512 178 for _, dev := range devs { 179 size, err := dev.BlockSize() 180 if err != nil { 181 t.Errorf("BlockSize(%s) = %v", dev, err) 182 } 183 if size != wantBlkSize { 184 t.Errorf("BlockSize(%s) = %d, want %d", dev, size, wantBlkSize) 185 } 186 187 pSize, err := dev.PhysicalBlockSize() 188 if err != nil { 189 t.Errorf("PhysicalBlockSize(%s) = %v", dev, err) 190 } 191 if pSize != wantBlkSize { 192 t.Errorf("PhysicalBlockSize(%s) = %d, want %d", dev, pSize, wantBlkSize) 193 } 194 195 kSize, err := dev.KernelBlockSize() 196 if err != nil { 197 t.Errorf("KernelBlockSize(%s) = %v", dev, err) 198 } 199 if pSize != wantBlkSize { 200 t.Errorf("KernelBlockSize(%s) = %d, want %d", dev, kSize, wantBlkSize) 201 } 202 } 203 } 204 205 func TestBlockDevReadPartitionTable(t *testing.T) { 206 if os.Getuid() != 0 { 207 t.Skip("Skipping since we are not root") 208 } 209 210 prefix := getDevicePrefix() 211 devs := testDevs(t) 212 213 wantRR := map[string]error{ 214 "nvme0n1": nil, 215 "nvme0n1p1": syscall.EINVAL, 216 "nvme0n1p2": syscall.EINVAL, 217 prefix + "a": nil, 218 prefix + "a1": syscall.EINVAL, 219 prefix + "a2": syscall.EINVAL, 220 prefix + "a3": syscall.EINVAL, 221 prefix + "a4": syscall.EINVAL, 222 prefix + "b": nil, 223 prefix + "b1": syscall.EINVAL, 224 prefix + "c": nil, 225 prefix + "c1": syscall.EINVAL, 226 prefix + "c2": syscall.EINVAL, 227 prefix + "d": nil, 228 prefix + "d1": syscall.EINVAL, 229 prefix + "d2": syscall.EINVAL, 230 } 231 for _, dev := range devs { 232 if err := dev.ReadPartitionTable(); err != wantRR[dev.Name] { 233 t.Errorf("ReadPartitionTable(%s) : \n\t%v \nwant: \n\t%v", dev, err, wantRR[dev.Name]) 234 } 235 } 236 } 237 238 func TestBlockDevicesFilterHavingPartitions(t *testing.T) { 239 if os.Getuid() != 0 { 240 t.Skip("Skipping since we are not root") 241 } 242 243 prefix := getDevicePrefix() 244 devs := testDevs(t) 245 246 devs = devs.FilterHavingPartitions([]int{1, 2}) 247 248 want := BlockDevices{ 249 &BlockDev{Name: "nvme0n1"}, 250 &BlockDev{Name: prefix + "a"}, 251 &BlockDev{Name: prefix + "c"}, 252 &BlockDev{Name: prefix + "d"}, 253 } 254 if !reflect.DeepEqual(devs, want) { 255 t.Fatalf("Filtered block devices: \n\t%v \nwant: \n\t%v", devs, want) 256 } 257 } 258 259 func TestBlockDevicesFilterPartID(t *testing.T) { 260 if os.Getuid() != 0 { 261 t.Skip("Skipping since we are not root") 262 } 263 264 devname := fmt.Sprintf("%sc2", getDevicePrefix()) 265 devs := testDevs(t) 266 267 for _, tt := range []struct { 268 guid string 269 want BlockDevices 270 }{ 271 { 272 guid: "C9865081-266C-4A23-A948-C03DAB506198", 273 want: BlockDevices{ 274 &BlockDev{Name: "nvme0n1p2"}, 275 &BlockDev{Name: devname}, 276 }, 277 }, 278 { 279 guid: "c9865081-266c-4a23-a948-c03dab506198", 280 want: BlockDevices{ 281 &BlockDev{Name: "nvme0n1p2"}, 282 &BlockDev{Name: devname}, 283 }, 284 }, 285 { 286 guid: "", 287 want: nil, 288 }, 289 } { 290 parts := devs.FilterPartID(tt.guid) 291 if !reflect.DeepEqual(parts, tt.want) { 292 t.Errorf("FilterPartID(%s) : \n\t%v \nwant: \n\t%v", tt.guid, parts, tt.want) 293 } 294 } 295 } 296 297 func TestBlockDevicesFilterPartType(t *testing.T) { 298 if os.Getuid() != 0 { 299 t.Skip("Skipping since we are not root") 300 } 301 302 prefix := getDevicePrefix() 303 devs := testDevs(t) 304 305 for _, tt := range []struct { 306 guid string 307 want BlockDevices 308 }{ 309 { 310 // EFI system partition. 311 guid: "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", 312 want: BlockDevices{ 313 &BlockDev{Name: "nvme0n1p1"}, 314 &BlockDev{Name: prefix + "c1"}, 315 }, 316 }, 317 { 318 // EFI system partition. mixed case. 319 guid: "c12a7328-f81F-11D2-BA4B-00A0C93ec93B", 320 want: BlockDevices{ 321 &BlockDev{Name: "nvme0n1p1"}, 322 &BlockDev{Name: prefix + "c1"}, 323 }, 324 }, 325 { 326 // This is some random Linux GUID. 327 guid: "0FC63DAF-8483-4772-8E79-3D69D8477DE4", 328 want: BlockDevices{ 329 &BlockDev{Name: "nvme0n1p2"}, 330 &BlockDev{Name: prefix + "c2"}, 331 &BlockDev{Name: prefix + "d1", FsUUID: "02175989-d49f-4e8e-836e-99300af66fc1"}, 332 &BlockDev{Name: prefix + "d2", FsUUID: "f3323a7f-a90a-4342-9508-d042afed287d"}, 333 }, 334 }, 335 } { 336 parts := devs.FilterPartType(tt.guid) 337 if !reflect.DeepEqual(parts, tt.want) { 338 t.Errorf("FilterPartType(%s) : \n\t%v \nwant: \n\t%v", tt.guid, parts, tt.want) 339 } 340 } 341 } 342 343 func TestBlockDevicesFilterPartLabel(t *testing.T) { 344 if os.Getuid() != 0 { 345 t.Skip("Skipping since we are not root") 346 } 347 348 prefix := getDevicePrefix() 349 devs := testDevs(t) 350 351 label := "TEST_LABEL" 352 want := BlockDevices{ 353 &BlockDev{Name: prefix + "d2", FsUUID: "f3323a7f-a90a-4342-9508-d042afed287d"}, 354 } 355 356 parts := devs.FilterPartLabel(label) 357 if !reflect.DeepEqual(parts, want) { 358 t.Errorf("FilterPartLabel(%s) : \n\t%v \nwant: \n\t%v", label, parts, want) 359 } 360 } 361 362 func TestBlockDevicesFilterBlockPCIString(t *testing.T) { 363 if os.Getuid() != 0 { 364 t.Skip("Skipping since we are not root") 365 } 366 367 prefix := getDevicePrefix() 368 devs := testDevs(t) 369 370 Debug = log.Printf 371 devs, err := devs.FilterBlockPCIString("0x8086:0x5845") 372 if err != nil { 373 t.Fatal(err) 374 } 375 376 want := BlockDevices{ 377 &BlockDev{Name: prefix + "a"}, 378 &BlockDev{Name: prefix + "a1", FsUUID: "2183ead8-a510-4b3d-9777-19c7090f66d9"}, 379 &BlockDev{Name: prefix + "a2", FsUUID: "ace5-5144"}, 380 &BlockDev{Name: prefix + "a3", FsUUID: "a896-d7b8"}, 381 &BlockDev{Name: prefix + "a4", FsUUID: "dca5f234-726b-47e2-b16e-07d3dbde7d8c"}, 382 &BlockDev{Name: prefix + "b"}, 383 &BlockDev{Name: prefix + "b1"}, 384 &BlockDev{Name: prefix + "c"}, 385 &BlockDev{Name: prefix + "c1"}, 386 &BlockDev{Name: prefix + "c2"}, 387 &BlockDev{Name: prefix + "d"}, 388 &BlockDev{Name: prefix + "d1", FsUUID: "02175989-d49f-4e8e-836e-99300af66fc1"}, 389 &BlockDev{Name: prefix + "d2", FsUUID: "f3323a7f-a90a-4342-9508-d042afed287d"}, 390 } 391 if !reflect.DeepEqual(devs, want) { 392 t.Fatalf("Filtered block devices: \n\t%v \nwant: \n\t%v", devs, want) 393 } 394 } 395 396 func TestBlockDevicesFilterBlockPCI(t *testing.T) { 397 if os.Getuid() != 0 { 398 t.Skip("Skipping since we are not root") 399 } 400 401 prefix := getDevicePrefix() 402 devs := testDevs(t) 403 404 p := &pci.PCI{Vendor: 0x8086, Device: 0x5845} 405 pl := pci.Devices{p} 406 407 Debug = log.Printf 408 devs = devs.FilterBlockPCI(pl) 409 410 want := BlockDevices{ 411 &BlockDev{Name: prefix + "a"}, 412 &BlockDev{Name: prefix + "a1", FsUUID: "2183ead8-a510-4b3d-9777-19c7090f66d9"}, 413 &BlockDev{Name: prefix + "a2", FsUUID: "ace5-5144"}, 414 &BlockDev{Name: prefix + "a3", FsUUID: "a896-d7b8"}, 415 &BlockDev{Name: prefix + "a4", FsUUID: "dca5f234-726b-47e2-b16e-07d3dbde7d8c"}, 416 &BlockDev{Name: prefix + "b"}, 417 &BlockDev{Name: prefix + "b1"}, 418 &BlockDev{Name: prefix + "c"}, 419 &BlockDev{Name: prefix + "c1"}, 420 &BlockDev{Name: prefix + "c2"}, 421 &BlockDev{Name: prefix + "d"}, 422 &BlockDev{Name: prefix + "d1", FsUUID: "02175989-d49f-4e8e-836e-99300af66fc1"}, 423 &BlockDev{Name: prefix + "d2", FsUUID: "f3323a7f-a90a-4342-9508-d042afed287d"}, 424 } 425 if !reflect.DeepEqual(devs, want) { 426 t.Fatalf("Filtered block devices: \n\t%v \nwant: \n\t%v", devs, want) 427 } 428 } 429 430 func getDevicePrefix() string { 431 if _, err := os.Stat("/dev/sdc"); err != nil { 432 return "vd" 433 } 434 return "sd" 435 } 436 437 func testDevs(t *testing.T) BlockDevices { 438 t.Helper() 439 440 prefix := getDevicePrefix() 441 442 devs, err := GetBlockDevices() 443 if err != nil { 444 t.Fatal(err) 445 } 446 devs = devs.FilterZeroSize() 447 448 want := BlockDevices{ 449 &BlockDev{Name: "nvme0n1"}, 450 &BlockDev{Name: "nvme0n1p1"}, 451 &BlockDev{Name: "nvme0n1p2"}, 452 &BlockDev{Name: prefix + "a"}, 453 &BlockDev{Name: prefix + "a1", FsUUID: "2183ead8-a510-4b3d-9777-19c7090f66d9"}, 454 &BlockDev{Name: prefix + "a2", FsUUID: "ace5-5144"}, 455 &BlockDev{Name: prefix + "a3", FsUUID: "a896-d7b8"}, 456 &BlockDev{Name: prefix + "a4", FsUUID: "dca5f234-726b-47e2-b16e-07d3dbde7d8c"}, 457 &BlockDev{Name: prefix + "b"}, 458 &BlockDev{Name: prefix + "b1"}, 459 &BlockDev{Name: prefix + "c"}, 460 &BlockDev{Name: prefix + "c1"}, 461 &BlockDev{Name: prefix + "c2"}, 462 &BlockDev{Name: prefix + "d"}, 463 &BlockDev{Name: prefix + "d1", FsUUID: "02175989-d49f-4e8e-836e-99300af66fc1"}, 464 &BlockDev{Name: prefix + "d2", FsUUID: "f3323a7f-a90a-4342-9508-d042afed287d"}, 465 } 466 if !reflect.DeepEqual(devs, want) { 467 t.Fatalf("Test block devices: \n\t%v \nwant: \n\t%v", devs, want) 468 } 469 470 return devs 471 }