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}