From 0fcb6601ca66b9e4c2349392663fe9baa30dbbc7 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 31 Aug 2017 14:05:29 -0700 Subject: [PATCH] filesystem: libblkid -> search /dev/disk/by-uuid --- .travis.yml | 1 - Makefile | 8 +-- README.md | 12 ++-- filesystem/filesystem.go | 4 +- filesystem/mountpoint.go | 130 +++++++++++++++++++-------------------- 5 files changed, 71 insertions(+), 84 deletions(-) diff --git a/.travis.yml b/.travis.yml index f530e1e1..54215e97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ addons: - sourceline: 'deb http://en.archive.ubuntu.com/ubuntu/ artful main universe' packages: - libargon2-0-dev - - libblkid-dev - libpam0g-dev - e2fsprogs - protobuf-compiler diff --git a/Makefile b/Makefile index 1ec009e9..9f8327e3 100644 --- a/Makefile +++ b/Makefile @@ -59,9 +59,9 @@ endif # builds the C code with high optimizations, and C warnings fail. # LDFLAGS # Change the flags passed to the C linker. Empty by default. -# For example: -# make fscrypt "LDFLAGS = -static -luuid -ldl -laudit -lpthread" -# will build a static binary. +# For example (on my system with additional dev packages): +# make fscrypt "LDFLAGS = -static -ldl -laudit -lcap-ng" +# will build a static fscrypt binary. # GO_FLAGS # Change the flags passed to "go build". Empty by default. # For example: @@ -179,8 +179,6 @@ test-setup: sudo mkdir -p $(MOUNT) sudo mount -o rw,loop $(IMAGE) $(MOUNT) sudo chmod +777 $(MOUNT) - # Add UUID to BLKID cache - sudo blkid $$(df $(MOUNT) --output=source | grep /dev/) test-teardown: sudo umount $(MOUNT) diff --git a/README.md b/README.md index 59eac5d9..3c0eedbb 100644 --- a/README.md +++ b/README.md @@ -119,11 +119,10 @@ fscrypt has the following build dependencies: >>>>> make >>>>> sudo make install ``` -* Headers for `libblkid` and `libpam`. These can be installed with the - appropriate package manager. - - `sudo apt-get install libblkid-dev libpam0g-dev` - - `sudo yum install libblkid-devel pam-devel` - - `pam` and `util-liux` packages for Arch +* Headers for `libpam`. Install them with the appropriate package manager. + - `sudo apt-get install libpam0g-dev` + - `sudo yum install pam-devel` + - `pam` package for Arch (part of the `base` group) Once all the dependencies are installed, you can get the repository by running: ```shell @@ -149,8 +148,7 @@ fscrypt has the following runtime dependencies: * Kernel support for filesystem encryption (this will depend on your kernel configuration and specific filesystem) * `libargon2.so` (see the above installation instructions for Argon2) -* `libblkid.so` and `libpam.so`. These libraries are almost certainly already - on your system. +* `libpam.so` (almost certainly already on your system) The dynamic libraries are not needed if you built a static executable. diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index b5fedf9c..86c168af 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -84,9 +84,7 @@ var ( // There is also the ability to reference another filesystem's metadata. This is // used when a Policy on filesystem A is protected with Protector on filesystem // B. In this scenario, we store a "link file" in the protectors directory whose -// contents look like "UUID=3a6d9a76-47f0-4f13-81bf-3332fbe984fb". These -// contents can be anything parsable by libblkid (i.e. anything that could be in -// the Device column of /etc/fstab). +// contents look like "UUID=3a6d9a76-47f0-4f13-81bf-3332fbe984fb". type Mount struct { Path string Filesystem string diff --git a/filesystem/mountpoint.go b/filesystem/mountpoint.go index eab75922..12016dd4 100644 --- a/filesystem/mountpoint.go +++ b/filesystem/mountpoint.go @@ -21,20 +21,17 @@ package filesystem +import ( + "io/ioutil" + "os" +) + /* -#cgo LDFLAGS: -lblkid -#include // blkid functions -#include // free() #include // setmntent, getmntent, endmntent // The file containing mountpoints info and how we should read it const char* mountpoints_filename = "/proc/mounts"; const char* read_mode = "r"; - -// Helper function for freeing strings -void string_free(char* str) { free(str); } - -// Helper function to lookup tokens */ import "C" @@ -53,12 +50,14 @@ var ( // These maps hold data about the state of the system's mountpoints. mountsByPath map[string]*Mount mountsByDevice map[string][]*Mount - // Cache for information about the devices - cache C.blkid_cache // Used to make the mount functions thread safe mountMutex sync.Mutex // True if the maps have been successfully initialized. mountsInitialized bool + // Supported tokens for filesystem links + uuidToken = "UUID" + // Location to perform UUID lookup + uuidDirectory = "/dev/disk/by-uuid" ) // getMountInfo populates the Mount mappings by parsing the filesystem @@ -81,14 +80,6 @@ func getMountInfo() error { } defer C.endmntent(fileHandle) - // Load the device information from the default blkid cache - if cache != nil { - C.blkid_put_cache(cache) - } - if C.blkid_get_cache(&cache, nil) != 0 { - return errors.Wrap(ErrGlobalMountInfo, "could not read blkid cache") - } - for { entry := C.getmntent(fileHandle) // When getmntent returns nil, we have read all of the entries. @@ -214,72 +205,75 @@ func GetMount(mountpoint string) (*Mount, error) { } // getMountsFromLink returns the Mount objects which match the provided link. -// This link can be an unparsed tag (e.g. =) or path (e.g. -// /dev/dm-0). The matching rules are determined by libblkid. These are the same -// matching rules for things like UUID=3a6d9a76-47f0-4f13-81bf-3332fbe984fb in -// "/etc/fstab". Note that this can match multiple Mounts. An error is returned -// if the link is invalid or we cannot load the required mount data. If a -// filesystem has been updated since the last call to one of the mount -// functions, run UpdateMountInfo to see the change. +// This link if formatted as a tag (e.g. =) similar to how they +// apprear in "/etc/fstab". Currently, only "UUID" tokens are supported. Note +// that this can match multiple Mounts (due to the existance of bind mounts). An +// error is returned if the link is invalid or we cannot load the required mount +// data. If a filesystem has been updated since the last call to one of the +// mount functions, run UpdateMountInfo to see the change. func getMountsFromLink(link string) ([]*Mount, error) { - // Use blkid_evaluate_spec to get the device name. - cLink := C.CString(link) - defer C.string_free(cLink) - - cDeviceName := C.blkid_evaluate_spec(cLink, &cache) - defer C.string_free(cDeviceName) - deviceName := C.GoString(cDeviceName) - - log.Printf("blkid_evaluate_spec(%q, ) = %q", link, deviceName) + // Parse the link + linkComponents := strings.Split(link, "=") + if len(linkComponents) != 2 { + return nil, errors.Wrapf(ErrFollowLink, "link %q format in invalid", link) + } + token := linkComponents[0] + value := linkComponents[1] + if token != uuidToken { + return nil, errors.Wrapf(ErrFollowLink, "token type %q not supported", token) + } - if deviceName == "" { - return nil, errors.Wrapf(ErrFollowLink, "link %q is invalid", link) + // See if UUID points to an existing device + searchPath := filepath.Join(uuidDirectory, value) + if filepath.Base(searchPath) != value { + return nil, errors.Wrapf(ErrFollowLink, "value %q is not a UUID", value) } - deviceName, err := cannonicalizePath(deviceName) + devicePath, err := cannonicalizePath(searchPath) if err != nil { - return nil, err + return nil, errors.Wrapf(ErrFollowLink, "no device with UUID %q", value) } + // Lookup mountpoints for device in global store mountMutex.Lock() defer mountMutex.Unlock() if err := getMountInfo(); err != nil { return nil, err } - - if mnts, ok := mountsByDevice[deviceName]; ok { - return mnts, nil + mnts, ok := mountsByDevice[devicePath] + if !ok { + return nil, errors.Wrapf(ErrFollowLink, "no mounts for device %q", devicePath) } - - return nil, errors.Wrapf(ErrFollowLink, "device %q is invalid", deviceName) + return mnts, nil } // makeLink returns a link of the form = where value is the tag -// value for the Mount's device according to libblkid. An error is returned if -// the device/token pair has no value. +// value for the Mount's device. Currently, only "UUID" tokens are supported. An +// error is returned if the mount has no device, or no UUID. func makeLink(mnt *Mount, token string) (string, error) { - // The blkid cache may not always hold the canonical device path. To - // solve this we first use blkid_evaluate_spec to find the right entry - // in the cache. Then that name is used to get the token value. - cDevice := C.CString(mnt.Device) - defer C.string_free(cDevice) - - cDeviceEntry := C.blkid_evaluate_spec(cDevice, &cache) - defer C.string_free(cDeviceEntry) - deviceEntry := C.GoString(cDeviceEntry) - - log.Printf("blkid_evaluate_spec(%q, ) = %q", mnt.Device, deviceEntry) - - cToken := C.CString(token) - defer C.string_free(cToken) - - cValue := C.blkid_get_tag_value(cache, cToken, cDeviceEntry) - defer C.string_free(cValue) - value := C.GoString(cValue) - - log.Printf("blkid_get_tag_value(, %s, %s) = %s", token, deviceEntry, value) + if token != uuidToken { + return "", errors.Wrapf(ErrMakeLink, "token type %q not supported", token) + } + if mnt.Device == "" { + return "", errors.Wrapf(ErrMakeLink, "no device for mount %q", mnt.Path) + } - if value == "" { - return "", errors.Wrapf(ErrMakeLink, "no %s", token) + dirContents, err := ioutil.ReadDir(uuidDirectory) + if err != nil { + return "", errors.Wrap(ErrMakeLink, err.Error()) + } + for _, fileInfo := range dirContents { + if fileInfo.Mode()&os.ModeSymlink == 0 { + continue // Only interested in UUID symlinks + } + uuid := fileInfo.Name() + devicePath, err := cannonicalizePath(filepath.Join(uuidDirectory, uuid)) + if err != nil { + log.Print(err) + continue + } + if mnt.Device == devicePath { + return fmt.Sprintf("%s=%s", uuidToken, uuid), nil + } } - return fmt.Sprintf("%s=%s", token, C.GoString(cValue)), nil + return "", errors.Wrapf(ErrMakeLink, "device %q has no UUID", mnt.Device) }