diff options
| author | Eric Biggers <ebiggers@google.com> | 2019-10-29 00:04:39 -0700 |
|---|---|---|
| committer | Eric Biggers <ebiggers@google.com> | 2019-10-30 09:11:29 -0700 |
| commit | d9d2b32f9fa9e39b154b71b2abc9eda43d5aaa3c (patch) | |
| tree | 74a26e4fc5219e98c990579248e76d7a6ccc6369 | |
| parent | c7b963bfa76b9541e01493d21fef3e3596ef9904 (diff) | |
filesystem: add device number utilities
Add a utility type and functions for handling device numbers.
| -rw-r--r-- | filesystem/path.go | 26 | ||||
| -rw-r--r-- | filesystem/path_test.go | 54 |
2 files changed, 80 insertions, 0 deletions
diff --git a/filesystem/path.go b/filesystem/path.go index cfc3dc0..a99b743 100644 --- a/filesystem/path.go +++ b/filesystem/path.go @@ -20,6 +20,7 @@ package filesystem import ( + "fmt" "log" "os" "path/filepath" @@ -99,3 +100,28 @@ func isRegularFile(path string) bool { info, err := loggedStat(path) return err == nil && info.Mode().IsRegular() } + +// DeviceNumber represents a combined major:minor device number. +type DeviceNumber uint64 + +func (num DeviceNumber) String() string { + return fmt.Sprintf("%d:%d", unix.Major(uint64(num)), unix.Minor(uint64(num))) +} + +func newDeviceNumberFromString(str string) (DeviceNumber, error) { + var major, minor uint32 + if count, _ := fmt.Sscanf(str, "%d:%d", &major, &minor); count != 2 { + return 0, errors.Errorf("invalid device number string %q", str) + } + return DeviceNumber(unix.Mkdev(major, minor)), nil +} + +// getDeviceNumber returns the device number of the device node at the given +// path. If there is a symlink at the path, it is dereferenced. +func getDeviceNumber(path string) (DeviceNumber, error) { + var stat unix.Stat_t + if err := unix.Stat(path, &stat); err != nil { + return 0, err + } + return DeviceNumber(stat.Rdev), nil +} diff --git a/filesystem/path_test.go b/filesystem/path_test.go new file mode 100644 index 0000000..eef5ce3 --- /dev/null +++ b/filesystem/path_test.go @@ -0,0 +1,54 @@ +/* + * path_test.go - Tests for path utilities. + * + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package filesystem + +import ( + "fmt" + "testing" +) + +func TestDeviceNumber(t *testing.T) { + num, err := getDeviceNumber("/NONEXISTENT") + if num != 0 || err == nil { + t.Error("Should have failed to get device number of nonexistent file") + } + // /dev/null is always device 1:3 on Linux. + num, err = getDeviceNumber("/dev/null") + if err != nil { + t.Fatal(err) + } + if str := num.String(); str != "1:3" { + t.Errorf("Wrong device number string: %q", str) + } + if str := fmt.Sprintf("%v", num); str != "1:3" { + t.Errorf("Wrong device number string: %q", str) + } + var num2 DeviceNumber + num2, err = newDeviceNumberFromString("1:3") + if err != nil { + t.Error("Failed to parse device number") + } + if num2 != num { + t.Errorf("Wrong device number: %d", num2) + } + num2, err = newDeviceNumberFromString("foo") + if num2 != 0 || err == nil { + t.Error("Should have failed to parse invalid device number") + } +} |