parent.go

 1package fsext
 2
 3import (
 4	"errors"
 5	"os"
 6	"path/filepath"
 7)
 8
 9// SearchParent searches for a target file or directory starting from dir
10// and walking up the directory tree until found or root or home is reached.
11// It also checks the ownership of directories to ensure that the search does
12// not cross ownership boundaries.
13// Returns the full path to the target if found, empty string and false otherwise.
14// The search includes the starting directory itself.
15func SearchParent(dir, target string) (string, bool) {
16	absDir, err := filepath.Abs(dir)
17	if err != nil {
18		return "", false
19	}
20
21	path := filepath.Join(absDir, target)
22	if _, err := os.Stat(path); err == nil {
23		return path, true
24	} else if !errors.Is(err, os.ErrNotExist) {
25		return "", false
26	}
27
28	previousParent := absDir
29	previousOwner, err := Owner(previousParent)
30	if err != nil {
31		return "", false
32	}
33
34	for {
35		parent := filepath.Dir(previousParent)
36		if parent == previousParent || parent == HomeDir() {
37			return "", false
38		}
39
40		parentOwner, err := Owner(parent)
41		if err != nil {
42			return "", false
43		}
44		if parentOwner != previousOwner {
45			return "", false
46		}
47
48		path := filepath.Join(parent, target)
49		if _, err := os.Stat(path); err == nil {
50			return path, true
51		} else if !errors.Is(err, os.ErrNotExist) {
52			return "", false
53		}
54
55		previousParent = parent
56		previousOwner = parentOwner
57	}
58}