2
2
mirror of https://github.com/octoleo/restic.git synced 2024-06-01 16:40:50 +00:00

Refactor device ID checking

This commit is contained in:
Alexander Neumann 2020-11-21 21:45:54 +01:00
parent 9a97095a4c
commit 037f0a4c91

View File

@ -199,12 +199,17 @@ func isDirExcludedByFile(dir, tagFilename, header string) bool {
return true return true
} }
// gatherDevices returns the set of unique device ids of the files and/or // DeviceMap is used to track allowed source devices for backup. This is used to
// directory paths listed in "items". // check for crossing mount points during backup (for --one-file-system). It
func gatherDevices(items []string) (deviceMap map[string]uint64, err error) { // maps the name of a source path to its device ID.
deviceMap = make(map[string]uint64) type DeviceMap map[string]uint64
for _, item := range items {
item, err = filepath.Abs(filepath.Clean(item)) // NewDeviceMap creates a new device map from the list of source paths.
func NewDeviceMap(allowedSourcePaths []string) (DeviceMap, error) {
deviceMap := make(map[string]uint64)
for _, item := range allowedSourcePaths {
item, err := filepath.Abs(filepath.Clean(item))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -213,30 +218,63 @@ func gatherDevices(items []string) (deviceMap map[string]uint64, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
id, err := fs.DeviceID(fi) id, err := fs.DeviceID(fi)
if err != nil { if err != nil {
return nil, err return nil, err
} }
deviceMap[item] = id deviceMap[item] = id
} }
if len(deviceMap) == 0 { if len(deviceMap) == 0 {
return nil, errors.New("zero allowed devices") return nil, errors.New("zero allowed devices")
} }
return deviceMap, nil return deviceMap, nil
} }
// IsAllowed returns true if the path is located on an allowed device.
func (m DeviceMap) IsAllowed(item string, deviceID uint64) (bool, error) {
for dir := item; ; dir = filepath.Dir(dir) {
debug.Log("item %v, test dir %v", item, dir)
// find a parent directory that is on an allowed device (otherwise
// we would not traverse the directory at all)
allowedID, ok := m[dir]
if !ok {
if dir == filepath.Dir(dir) {
// arrived at root, no allowed device found. this should not happen.
break
}
continue
}
// if the item has a different device ID than the parent directory,
// we crossed a file system boundary
if allowedID != deviceID {
debug.Log("item %v (dir %v) on disallowed device %d", item, dir, deviceID)
return false, nil
}
// item is on allowed device, accept it
debug.Log("item %v allowed", item)
return true, nil
}
return false, fmt.Errorf("item %v (device ID %v) not found, deviceMap: %v", item, deviceID, m)
}
// rejectByDevice returns a RejectFunc that rejects files which are on a // rejectByDevice returns a RejectFunc that rejects files which are on a
// different file systems than the files/dirs in samples. // different file systems than the files/dirs in samples.
func rejectByDevice(samples []string) (RejectFunc, error) { func rejectByDevice(samples []string) (RejectFunc, error) {
allowed, err := gatherDevices(samples) deviceMap, err := NewDeviceMap(samples)
if err != nil { if err != nil {
return nil, err return nil, err
} }
debug.Log("allowed devices: %v\n", allowed) debug.Log("allowed devices: %v\n", deviceMap)
return func(item string, fi os.FileInfo) bool { return func(item string, fi os.FileInfo) bool {
item = filepath.Clean(item)
id, err := fs.DeviceID(fi) id, err := fs.DeviceID(fi)
if err != nil { if err != nil {
// This should never happen because gatherDevices() would have // This should never happen because gatherDevices() would have
@ -244,26 +282,13 @@ func rejectByDevice(samples []string) (RejectFunc, error) {
panic(err) panic(err)
} }
for dir := item; ; dir = filepath.Dir(dir) { allowed, err := deviceMap.IsAllowed(filepath.Clean(item), id)
debug.Log("item %v, test dir %v", item, dir) if err != nil {
// this should not happen
allowedID, ok := allowed[dir] panic(fmt.Sprintf("error checking device ID of %v: %v", item, err))
if !ok {
if dir == filepath.Dir(dir) {
break
}
continue
}
if allowedID != id {
debug.Log("path %q on disallowed device %d", item, id)
return true
}
return false
} }
panic(fmt.Sprintf("item %v, device id %v not found, allowedDevs: %v", item, id, allowed)) return !allowed
}, nil }, nil
} }