device_test.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package main
  2. import (
  3. "testing"
  4. "golang.org/x/sys/unix"
  5. )
  6. const sampleMountinfo = `22 1 252:0 / / rw,relatime shared:1 - ext4 /dev/mapper/vg-root rw
  7. 30 22 0:5 / /dev rw,nosuid shared:2 - devtmpfs udev rw
  8. 95 22 8:1 / /media/u/Data rw,relatime shared:50 - ext4 /dev/sda1 rw
  9. 96 22 8:1 /photos /mnt/photos\040bind rw,relatime shared:50 - ext4 /dev/sda1 rw
  10. `
  11. func TestParseMountinfo(t *testing.T) {
  12. es := parseMountinfo(sampleMountinfo)
  13. if len(es) != 4 {
  14. t.Fatalf("应解析出 4 条:%d", len(es))
  15. }
  16. if e := es[3]; e.Point != "/mnt/photos bind" || e.Root != "/photos" || e.Major != 8 || e.Minor != 1 || e.FSType != "ext4" || e.Source != "/dev/sda1" {
  17. t.Errorf("解析不正确:%+v", e)
  18. }
  19. }
  20. // fakeSystem 模拟挂载表、UUID 与设备号
  21. type fakeSystem struct {
  22. info string
  23. uuid map[uint64]string
  24. devs map[string]uint64 // 路径前缀 → 设备号(最长匹配)
  25. stats []string
  26. }
  27. func (f *fakeSystem) mountinfo() []mountEntry { return parseMountinfo(f.info) }
  28. func (f *fakeSystem) uuids() map[uint64]string { return f.uuid }
  29. func (f *fakeSystem) devOf(p string) (uint64, error) {
  30. f.stats = append(f.stats, p)
  31. best, dev := "", uint64(0)
  32. for prefix, d := range f.devs {
  33. if within(p, prefix) && len(prefix) >= len(best) {
  34. best, dev = prefix, d
  35. }
  36. }
  37. if best == "" {
  38. return 0, unix.ENOENT
  39. }
  40. return dev, nil
  41. }
  42. func TestIdentifyAndLocate(t *testing.T) {
  43. sda := unix.Mkdev(8, 1)
  44. root := unix.Mkdev(252, 0)
  45. sys := &fakeSystem{
  46. info: sampleMountinfo,
  47. uuid: map[uint64]string{sda: "aaaa-1111", root: "rootuuid"},
  48. devs: map[string]uint64{"/": root, "/media/u/Data": sda, "/mnt/photos bind": sda},
  49. }
  50. // 系统盘:只记路径
  51. if id := identify(sys, "/home/u/docs"); !id.SystemDisk || id.UUID != "" {
  52. t.Errorf("系统盘上的目录:%+v", id)
  53. }
  54. // 外接盘:UUID + 设备内路径
  55. id := identify(sys, "/media/u/Data/资料/合同")
  56. if id.UUID != "aaaa-1111" || id.RelPath != "/资料/合同" || id.SystemDisk || id.MountPoint != "/media/u/Data" {
  57. t.Fatalf("外接盘上的目录:%+v", id)
  58. }
  59. src := localSource{Path: id.Path, UUID: id.UUID, RelPath: id.RelPath}
  60. if p, err := locate(sys, src); err != nil || p != "/media/u/Data/资料/合同" {
  61. t.Errorf("设备在原位置:%q %v", p, err)
  62. }
  63. // bind mount 的子目录
  64. if p, err := locate(sys, localSource{Path: "/mnt/photos bind/2024", UUID: "aaaa-1111", RelPath: "/photos/2024"}); err != nil || p != "/mnt/photos bind/2024" {
  65. t.Errorf("bind mount:%q %v", p, err)
  66. }
  67. // 设备拔出:UUID 不在了
  68. sys.uuid = map[uint64]string{root: "rootuuid"}
  69. if _, err := locate(sys, src); err == nil || err.State != "device_missing" {
  70. t.Errorf("设备拔出应为 device_missing:%v", err)
  71. }
  72. // 设备插回,但挂到了别的位置(sdb)
  73. sdb := unix.Mkdev(8, 17)
  74. sys.uuid = map[uint64]string{sdb: "aaaa-1111", root: "rootuuid"}
  75. sys.info = "22 1 252:0 / / rw - ext4 /dev/mapper/vg-root rw\n97 22 8:17 / /media/u/Data1 rw - ext4 /dev/sdb1 rw\n"
  76. if p, err := locate(sys, src); err != nil || p != "/media/u/Data1/资料/合同" {
  77. t.Errorf("挂载位置变化后应能找到:%q %v", p, err)
  78. }
  79. // 另一块盘挂在原路径上:UUID 不同,不会被误认
  80. sys.uuid = map[uint64]string{sdb: "bbbb-2222", root: "rootuuid"}
  81. sys.info = "22 1 252:0 / / rw - ext4 /dev/mapper/vg-root rw\n97 22 8:17 / /media/u/Data rw - ext4 /dev/sdb1 rw\n"
  82. if _, err := locate(sys, src); err == nil || err.State != "device_missing" {
  83. t.Errorf("换了一块盘应为 device_missing:%v", err)
  84. }
  85. // 设备已连接但没有挂载(例如自动挂载):会访问原路径触发挂载
  86. sys.uuid = map[uint64]string{sda: "aaaa-1111", root: "rootuuid"}
  87. sys.info = "22 1 252:0 / / rw - ext4 /dev/mapper/vg-root rw\n"
  88. sys.stats = nil
  89. if _, err := locate(sys, src); err == nil || err.State != "device_missing" || !contains(err.Message, "没有挂载") {
  90. t.Errorf("已连接未挂载:%v", err)
  91. }
  92. if len(sys.stats) == 0 || sys.stats[0] != src.Path {
  93. t.Errorf("应访问原路径以触发自动挂载:%v", sys.stats)
  94. }
  95. // 只记路径的目录
  96. if p, err := locate(sys, localSource{Path: "/home/u/docs"}); err != nil || p != "/home/u/docs" {
  97. t.Errorf("系统盘:%q %v", p, err)
  98. }
  99. }