github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/disk/disk_linux.go (about) 1 //go:build linux 2 3 package disk 4 5 import ( 6 "bufio" 7 "bytes" 8 "context" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15 16 "github.com/isyscore/isc-gobase/system/common" 17 "golang.org/x/sys/unix" 18 ) 19 20 const ( 21 SectorSize = 512 22 ) 23 const ( 24 // man statfs 25 ADFS_SUPER_MAGIC = 0xadf5 26 AFFS_SUPER_MAGIC = 0xADFF 27 BDEVFS_MAGIC = 0x62646576 28 BEFS_SUPER_MAGIC = 0x42465331 29 BFS_MAGIC = 0x1BADFACE 30 BINFMTFS_MAGIC = 0x42494e4d 31 BTRFS_SUPER_MAGIC = 0x9123683E 32 CGROUP_SUPER_MAGIC = 0x27e0eb 33 CIFS_MAGIC_NUMBER = 0xFF534D42 34 CODA_SUPER_MAGIC = 0x73757245 35 COH_SUPER_MAGIC = 0x012FF7B7 36 CRAMFS_MAGIC = 0x28cd3d45 37 DEBUGFS_MAGIC = 0x64626720 38 DEVFS_SUPER_MAGIC = 0x1373 39 DEVPTS_SUPER_MAGIC = 0x1cd1 40 EFIVARFS_MAGIC = 0xde5e81e4 41 EFS_SUPER_MAGIC = 0x00414A53 42 EXT_SUPER_MAGIC = 0x137D 43 EXT2_OLD_SUPER_MAGIC = 0xEF51 44 EXT2_SUPER_MAGIC = 0xEF53 45 EXT3_SUPER_MAGIC = 0xEF53 46 EXT4_SUPER_MAGIC = 0xEF53 47 FUSE_SUPER_MAGIC = 0x65735546 48 FUTEXFS_SUPER_MAGIC = 0xBAD1DEA 49 HFS_SUPER_MAGIC = 0x4244 50 HFSPLUS_SUPER_MAGIC = 0x482b 51 HOSTFS_SUPER_MAGIC = 0x00c0ffee 52 HPFS_SUPER_MAGIC = 0xF995E849 53 HUGETLBFS_MAGIC = 0x958458f6 54 ISOFS_SUPER_MAGIC = 0x9660 55 JFFS2_SUPER_MAGIC = 0x72b6 56 JFS_SUPER_MAGIC = 0x3153464a 57 MINIX_SUPER_MAGIC = 0x137F /* orig. minix */ 58 MINIX_SUPER_MAGIC2 = 0x138F /* 30 char minix */ 59 MINIX2_SUPER_MAGIC = 0x2468 /* minix V2 */ 60 MINIX2_SUPER_MAGIC2 = 0x2478 /* minix V2, 30 char names */ 61 MINIX3_SUPER_MAGIC = 0x4d5a /* minix V3 fs, 60 char names */ 62 MQUEUE_MAGIC = 0x19800202 63 MSDOS_SUPER_MAGIC = 0x4d44 64 NCP_SUPER_MAGIC = 0x564c 65 NFS_SUPER_MAGIC = 0x6969 66 NILFS_SUPER_MAGIC = 0x3434 67 NTFS_SB_MAGIC = 0x5346544e 68 OCFS2_SUPER_MAGIC = 0x7461636f 69 OPENPROM_SUPER_MAGIC = 0x9fa1 70 PIPEFS_MAGIC = 0x50495045 71 PROC_SUPER_MAGIC = 0x9fa0 72 PSTOREFS_MAGIC = 0x6165676C 73 QNX4_SUPER_MAGIC = 0x002f 74 QNX6_SUPER_MAGIC = 0x68191122 75 RAMFS_MAGIC = 0x858458f6 76 REISERFS_SUPER_MAGIC = 0x52654973 77 ROMFS_MAGIC = 0x7275 78 SELINUX_MAGIC = 0xf97cff8c 79 SMACK_MAGIC = 0x43415d53 80 SMB_SUPER_MAGIC = 0x517B 81 SOCKFS_MAGIC = 0x534F434B 82 SQUASHFS_MAGIC = 0x73717368 83 SYSFS_MAGIC = 0x62656572 84 SYSV2_SUPER_MAGIC = 0x012FF7B6 85 SYSV4_SUPER_MAGIC = 0x012FF7B5 86 TMPFS_MAGIC = 0x01021994 87 UDF_SUPER_MAGIC = 0x15013346 88 UFS_MAGIC = 0x00011954 89 USBDEVICE_SUPER_MAGIC = 0x9fa2 90 V9FS_MAGIC = 0x01021997 91 VXFS_SUPER_MAGIC = 0xa501FCF5 92 XENFS_SUPER_MAGIC = 0xabba1974 93 XENIX_SUPER_MAGIC = 0x012FF7B4 94 XFS_SUPER_MAGIC = 0x58465342 95 _XIAFS_SUPER_MAGIC = 0x012FD16D 96 97 AFS_SUPER_MAGIC = 0x5346414F 98 AUFS_SUPER_MAGIC = 0x61756673 99 ANON_INODE_FS_SUPER_MAGIC = 0x09041934 100 CEPH_SUPER_MAGIC = 0x00C36400 101 ECRYPTFS_SUPER_MAGIC = 0xF15F 102 FAT_SUPER_MAGIC = 0x4006 103 FHGFS_SUPER_MAGIC = 0x19830326 104 FUSEBLK_SUPER_MAGIC = 0x65735546 105 FUSECTL_SUPER_MAGIC = 0x65735543 106 GFS_SUPER_MAGIC = 0x1161970 107 GPFS_SUPER_MAGIC = 0x47504653 108 MTD_INODE_FS_SUPER_MAGIC = 0x11307854 109 INOTIFYFS_SUPER_MAGIC = 0x2BAD1DEA 110 ISOFS_R_WIN_SUPER_MAGIC = 0x4004 111 ISOFS_WIN_SUPER_MAGIC = 0x4000 112 JFFS_SUPER_MAGIC = 0x07C0 113 KAFS_SUPER_MAGIC = 0x6B414653 114 LUSTRE_SUPER_MAGIC = 0x0BD00BD0 115 NFSD_SUPER_MAGIC = 0x6E667364 116 PANFS_SUPER_MAGIC = 0xAAD7AAEA 117 RPC_PIPEFS_SUPER_MAGIC = 0x67596969 118 SECURITYFS_SUPER_MAGIC = 0x73636673 119 UFS_BYTESWAPPED_SUPER_MAGIC = 0x54190100 120 VMHGFS_SUPER_MAGIC = 0xBACBACBC 121 VZFS_SUPER_MAGIC = 0x565A4653 122 ZFS_SUPER_MAGIC = 0x2FC12FC1 123 ) 124 125 // coreutils/src/stat.c 126 var fsTypeMap = map[int64]string{ 127 ADFS_SUPER_MAGIC: "adfs", /* 0xADF5 local */ 128 AFFS_SUPER_MAGIC: "affs", /* 0xADFF local */ 129 AFS_SUPER_MAGIC: "afs", /* 0x5346414F remote */ 130 ANON_INODE_FS_SUPER_MAGIC: "anon-inode FS", /* 0x09041934 local */ 131 AUFS_SUPER_MAGIC: "aufs", /* 0x61756673 remote */ 132 // AUTOFS_SUPER_MAGIC: "autofs", /* 0x0187 local */ 133 BEFS_SUPER_MAGIC: "befs", /* 0x42465331 local */ 134 BDEVFS_MAGIC: "bdevfs", /* 0x62646576 local */ 135 BFS_MAGIC: "bfs", /* 0x1BADFACE local */ 136 BINFMTFS_MAGIC: "binfmt_misc", /* 0x42494E4D local */ 137 BTRFS_SUPER_MAGIC: "btrfs", /* 0x9123683E local */ 138 CEPH_SUPER_MAGIC: "ceph", /* 0x00C36400 remote */ 139 CGROUP_SUPER_MAGIC: "cgroupfs", /* 0x0027E0EB local */ 140 CIFS_MAGIC_NUMBER: "cifs", /* 0xFF534D42 remote */ 141 CODA_SUPER_MAGIC: "coda", /* 0x73757245 remote */ 142 COH_SUPER_MAGIC: "coh", /* 0x012FF7B7 local */ 143 CRAMFS_MAGIC: "cramfs", /* 0x28CD3D45 local */ 144 DEBUGFS_MAGIC: "debugfs", /* 0x64626720 local */ 145 DEVFS_SUPER_MAGIC: "devfs", /* 0x1373 local */ 146 DEVPTS_SUPER_MAGIC: "devpts", /* 0x1CD1 local */ 147 ECRYPTFS_SUPER_MAGIC: "ecryptfs", /* 0xF15F local */ 148 EFS_SUPER_MAGIC: "efs", /* 0x00414A53 local */ 149 EXT_SUPER_MAGIC: "ext", /* 0x137D local */ 150 EXT2_SUPER_MAGIC: "ext2/ext3", /* 0xEF53 local */ 151 EXT2_OLD_SUPER_MAGIC: "ext2", /* 0xEF51 local */ 152 FAT_SUPER_MAGIC: "fat", /* 0x4006 local */ 153 FHGFS_SUPER_MAGIC: "fhgfs", /* 0x19830326 remote */ 154 FUSEBLK_SUPER_MAGIC: "fuseblk", /* 0x65735546 remote */ 155 FUSECTL_SUPER_MAGIC: "fusectl", /* 0x65735543 remote */ 156 FUTEXFS_SUPER_MAGIC: "futexfs", /* 0x0BAD1DEA local */ 157 GFS_SUPER_MAGIC: "gfs/gfs2", /* 0x1161970 remote */ 158 GPFS_SUPER_MAGIC: "gpfs", /* 0x47504653 remote */ 159 HFS_SUPER_MAGIC: "hfs", /* 0x4244 local */ 160 HFSPLUS_SUPER_MAGIC: "hfsplus", /* 0x482b local */ 161 HPFS_SUPER_MAGIC: "hpfs", /* 0xF995E849 local */ 162 HUGETLBFS_MAGIC: "hugetlbfs", /* 0x958458F6 local */ 163 MTD_INODE_FS_SUPER_MAGIC: "inodefs", /* 0x11307854 local */ 164 INOTIFYFS_SUPER_MAGIC: "inotifyfs", /* 0x2BAD1DEA local */ 165 ISOFS_SUPER_MAGIC: "isofs", /* 0x9660 local */ 166 ISOFS_R_WIN_SUPER_MAGIC: "isofs", /* 0x4004 local */ 167 ISOFS_WIN_SUPER_MAGIC: "isofs", /* 0x4000 local */ 168 JFFS_SUPER_MAGIC: "jffs", /* 0x07C0 local */ 169 JFFS2_SUPER_MAGIC: "jffs2", /* 0x72B6 local */ 170 JFS_SUPER_MAGIC: "jfs", /* 0x3153464A local */ 171 KAFS_SUPER_MAGIC: "k-afs", /* 0x6B414653 remote */ 172 LUSTRE_SUPER_MAGIC: "lustre", /* 0x0BD00BD0 remote */ 173 MINIX_SUPER_MAGIC: "minix", /* 0x137F local */ 174 MINIX_SUPER_MAGIC2: "minix (30 char.)", /* 0x138F local */ 175 MINIX2_SUPER_MAGIC: "minix v2", /* 0x2468 local */ 176 MINIX2_SUPER_MAGIC2: "minix v2 (30 char.)", /* 0x2478 local */ 177 MINIX3_SUPER_MAGIC: "minix3", /* 0x4D5A local */ 178 MQUEUE_MAGIC: "mqueue", /* 0x19800202 local */ 179 MSDOS_SUPER_MAGIC: "msdos", /* 0x4D44 local */ 180 NCP_SUPER_MAGIC: "novell", /* 0x564C remote */ 181 NFS_SUPER_MAGIC: "nfs", /* 0x6969 remote */ 182 NFSD_SUPER_MAGIC: "nfsd", /* 0x6E667364 remote */ 183 NILFS_SUPER_MAGIC: "nilfs", /* 0x3434 local */ 184 NTFS_SB_MAGIC: "ntfs", /* 0x5346544E local */ 185 OPENPROM_SUPER_MAGIC: "openprom", /* 0x9FA1 local */ 186 OCFS2_SUPER_MAGIC: "ocfs2", /* 0x7461636f remote */ 187 PANFS_SUPER_MAGIC: "panfs", /* 0xAAD7AAEA remote */ 188 PIPEFS_MAGIC: "pipefs", /* 0x50495045 remote */ 189 PROC_SUPER_MAGIC: "proc", /* 0x9FA0 local */ 190 PSTOREFS_MAGIC: "pstorefs", /* 0x6165676C local */ 191 QNX4_SUPER_MAGIC: "qnx4", /* 0x002F local */ 192 QNX6_SUPER_MAGIC: "qnx6", /* 0x68191122 local */ 193 RAMFS_MAGIC: "ramfs", /* 0x858458F6 local */ 194 REISERFS_SUPER_MAGIC: "reiserfs", /* 0x52654973 local */ 195 ROMFS_MAGIC: "romfs", /* 0x7275 local */ 196 RPC_PIPEFS_SUPER_MAGIC: "rpc_pipefs", /* 0x67596969 local */ 197 SECURITYFS_SUPER_MAGIC: "securityfs", /* 0x73636673 local */ 198 SELINUX_MAGIC: "selinux", /* 0xF97CFF8C local */ 199 SMB_SUPER_MAGIC: "smb", /* 0x517B remote */ 200 SOCKFS_MAGIC: "sockfs", /* 0x534F434B local */ 201 SQUASHFS_MAGIC: "squashfs", /* 0x73717368 local */ 202 SYSFS_MAGIC: "sysfs", /* 0x62656572 local */ 203 SYSV2_SUPER_MAGIC: "sysv2", /* 0x012FF7B6 local */ 204 SYSV4_SUPER_MAGIC: "sysv4", /* 0x012FF7B5 local */ 205 TMPFS_MAGIC: "tmpfs", /* 0x01021994 local */ 206 UDF_SUPER_MAGIC: "udf", /* 0x15013346 local */ 207 UFS_MAGIC: "ufs", /* 0x00011954 local */ 208 UFS_BYTESWAPPED_SUPER_MAGIC: "ufs", /* 0x54190100 local */ 209 USBDEVICE_SUPER_MAGIC: "usbdevfs", /* 0x9FA2 local */ 210 V9FS_MAGIC: "v9fs", /* 0x01021997 local */ 211 VMHGFS_SUPER_MAGIC: "vmhgfs", /* 0xBACBACBC remote */ 212 VXFS_SUPER_MAGIC: "vxfs", /* 0xA501FCF5 local */ 213 VZFS_SUPER_MAGIC: "vzfs", /* 0x565A4653 local */ 214 XENFS_SUPER_MAGIC: "xenfs", /* 0xABBA1974 local */ 215 XENIX_SUPER_MAGIC: "xenix", /* 0x012FF7B4 local */ 216 XFS_SUPER_MAGIC: "xfs", /* 0x58465342 local */ 217 _XIAFS_SUPER_MAGIC: "xia", /* 0x012FD16D local */ 218 ZFS_SUPER_MAGIC: "zfs", /* 0x2FC12FC1 local */ 219 } 220 221 func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) { 222 useMounts := false 223 224 filename := common.HostProc("1/mountinfo") 225 lines, err := common.ReadLines(filename) 226 if err != nil { 227 if err != err.(*os.PathError) { 228 return nil, err 229 } 230 // if kernel does not support 1/mountinfo, fallback to 1/mounts (<2.6.26) 231 useMounts = true 232 filename = common.HostProc("1/mounts") 233 lines, err = common.ReadLines(filename) 234 if err != nil { 235 return nil, err 236 } 237 } 238 239 fs, err := getFileSystems() 240 if err != nil && !all { 241 return nil, err 242 } 243 244 ret := make([]PartitionStat, 0, len(lines)) 245 246 for _, line := range lines { 247 var d PartitionStat 248 if useMounts { 249 fields := strings.Fields(line) 250 251 d = PartitionStat{ 252 Device: fields[0], 253 Mountpoint: unescapeFstab(fields[1]), 254 Fstype: fields[2], 255 Opts: fields[3], 256 } 257 258 if !all { 259 if d.Device == "none" || !common.StringsHas(fs, d.Fstype) { 260 continue 261 } 262 } 263 } else { 264 // a line of 1/mountinfo has the following structure: 265 // 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue 266 // (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) 267 268 // split the mountinfo line by the separator hyphen 269 parts := strings.Split(line, " - ") 270 if len(parts) != 2 { 271 return nil, fmt.Errorf("found invalid mountinfo line in file %s: %s ", filename, line) 272 } 273 274 fields := strings.Fields(parts[0]) 275 blockDeviceID := fields[2] 276 mountPoint := fields[4] 277 mountOpts := fields[5] 278 279 if rootDir := fields[3]; rootDir != "" && rootDir != "/" { 280 if len(mountOpts) == 0 { 281 mountOpts = "bind" 282 } else { 283 mountOpts = "bind," + mountOpts 284 } 285 } 286 287 fields = strings.Fields(parts[1]) 288 fstype := fields[0] 289 device := fields[1] 290 291 d = PartitionStat{ 292 Device: device, 293 Mountpoint: unescapeFstab(mountPoint), 294 Fstype: fstype, 295 Opts: mountOpts, 296 } 297 298 if !all { 299 if d.Device == "none" || !common.StringsHas(fs, d.Fstype) { 300 continue 301 } 302 } 303 304 if strings.HasPrefix(d.Device, "/dev/mapper/") { 305 devpath, err := filepath.EvalSymlinks(common.HostDev(strings.Replace(d.Device, "/dev", "", -1))) 306 if err == nil { 307 d.Device = devpath 308 } 309 } 310 311 // /dev/root is not the real device name 312 // so we get the real device name from its major/minor number 313 if d.Device == "/dev/root" { 314 devpath, err := os.Readlink(common.HostSys("/dev/block/" + blockDeviceID)) 315 if err != nil { 316 return nil, err 317 } 318 d.Device = strings.Replace(d.Device, "root", filepath.Base(devpath), 1) 319 } 320 } 321 ret = append(ret, d) 322 } 323 324 return ret, nil 325 } 326 327 // getFileSystems returns supported filesystems from /proc/filesystems 328 func getFileSystems() ([]string, error) { 329 filename := common.HostProc("filesystems") 330 lines, err := common.ReadLines(filename) 331 if err != nil { 332 return nil, err 333 } 334 var ret []string 335 for _, line := range lines { 336 if !strings.HasPrefix(line, "nodev") { 337 ret = append(ret, strings.TrimSpace(line)) 338 continue 339 } 340 t := strings.Split(line, "\t") 341 if len(t) != 2 || t[1] != "zfs" { 342 continue 343 } 344 ret = append(ret, strings.TrimSpace(t[1])) 345 } 346 347 return ret, nil 348 } 349 350 func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) { 351 filename := common.HostProc("diskstats") 352 lines, err := common.ReadLines(filename) 353 if err != nil { 354 return nil, err 355 } 356 ret := make(map[string]IOCountersStat, 0) 357 empty := IOCountersStat{} 358 359 // use only basename such as "/dev/sda1" to "sda1" 360 for i, name := range names { 361 names[i] = filepath.Base(name) 362 } 363 364 for _, line := range lines { 365 fields := strings.Fields(line) 366 if len(fields) < 14 { 367 // malformed line in /proc/diskstats, avoid panic by ignoring. 368 continue 369 } 370 name := fields[2] 371 372 if len(names) > 0 && !common.StringsHas(names, name) { 373 continue 374 } 375 376 reads, err := strconv.ParseUint((fields[3]), 10, 64) 377 if err != nil { 378 return ret, err 379 } 380 mergedReads, err := strconv.ParseUint((fields[4]), 10, 64) 381 if err != nil { 382 return ret, err 383 } 384 rbytes, err := strconv.ParseUint((fields[5]), 10, 64) 385 if err != nil { 386 return ret, err 387 } 388 rtime, err := strconv.ParseUint((fields[6]), 10, 64) 389 if err != nil { 390 return ret, err 391 } 392 writes, err := strconv.ParseUint((fields[7]), 10, 64) 393 if err != nil { 394 return ret, err 395 } 396 mergedWrites, err := strconv.ParseUint((fields[8]), 10, 64) 397 if err != nil { 398 return ret, err 399 } 400 wbytes, err := strconv.ParseUint((fields[9]), 10, 64) 401 if err != nil { 402 return ret, err 403 } 404 wtime, err := strconv.ParseUint((fields[10]), 10, 64) 405 if err != nil { 406 return ret, err 407 } 408 iopsInProgress, err := strconv.ParseUint((fields[11]), 10, 64) 409 if err != nil { 410 return ret, err 411 } 412 iotime, err := strconv.ParseUint((fields[12]), 10, 64) 413 if err != nil { 414 return ret, err 415 } 416 weightedIO, err := strconv.ParseUint((fields[13]), 10, 64) 417 if err != nil { 418 return ret, err 419 } 420 d := IOCountersStat{ 421 ReadBytes: rbytes * SectorSize, 422 WriteBytes: wbytes * SectorSize, 423 ReadCount: reads, 424 WriteCount: writes, 425 MergedReadCount: mergedReads, 426 MergedWriteCount: mergedWrites, 427 ReadTime: rtime, 428 WriteTime: wtime, 429 IopsInProgress: iopsInProgress, 430 IoTime: iotime, 431 WeightedIO: weightedIO, 432 } 433 if d == empty { 434 continue 435 } 436 d.Name = name 437 438 d.SerialNumber = GetDiskSerialNumber(name) 439 d.Label = GetLabel(name) 440 441 ret[name] = d 442 } 443 return ret, nil 444 } 445 446 // GetDiskSerialNumber returns Serial Number of given device or empty string 447 // on error. Name of device is expected, eg. /dev/sda 448 func GetDiskSerialNumber(name string) string { 449 return GetDiskSerialNumberWithContext(context.Background(), name) 450 } 451 452 func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { 453 var stat unix.Stat_t 454 err := unix.Stat(name, &stat) 455 if err != nil { 456 return "" 457 } 458 major := unix.Major(uint64(stat.Rdev)) 459 minor := unix.Minor(uint64(stat.Rdev)) 460 461 // Try to get the serial from udev data 462 udevDataPath := common.HostRun(fmt.Sprintf("udev/data/b%d:%d", major, minor)) 463 if udevdata, err := ioutil.ReadFile(udevDataPath); err == nil { 464 scanner := bufio.NewScanner(bytes.NewReader(udevdata)) 465 for scanner.Scan() { 466 values := strings.Split(scanner.Text(), "=") 467 if len(values) == 2 && values[0] == "E:ID_SERIAL" { 468 return values[1] 469 } 470 } 471 } 472 473 // Try to get the serial from sysfs, look at the disk device (minor 0) directly 474 // because if it is a partition it is not going to contain any device information 475 devicePath := common.HostSys(fmt.Sprintf("dev/block/%d:0/device", major)) 476 model, _ := ioutil.ReadFile(filepath.Join(devicePath, "model")) 477 serial, _ := ioutil.ReadFile(filepath.Join(devicePath, "serial")) 478 if len(model) > 0 && len(serial) > 0 { 479 return fmt.Sprintf("%s_%s", string(model), string(serial)) 480 } 481 return "" 482 } 483 484 // GetLabel returns label of given device or empty string on error. 485 // Name of device is expected, eg. /dev/sda 486 // Supports label based on devicemapper name 487 // See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm 488 func GetLabel(name string) string { 489 // Try label based on devicemapper name 490 dmname_filename := common.HostSys(fmt.Sprintf("block/%s/dm/name", name)) 491 492 if !common.PathExists(dmname_filename) { 493 return "" 494 } 495 496 dmname, err := ioutil.ReadFile(dmname_filename) 497 if err != nil { 498 return "" 499 } else { 500 return strings.TrimSpace(string(dmname)) 501 } 502 } 503 504 func getFsType(stat unix.Statfs_t) string { 505 t := int64(stat.Type) 506 ret, ok := fsTypeMap[t] 507 if !ok { 508 return "" 509 } 510 return ret 511 }