github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfs/util.go (about) 1 // Copyright 2019 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libfs 6 7 import ( 8 "context" 9 "fmt" 10 "os" 11 "path" 12 "regexp" 13 "strings" 14 15 billy "gopkg.in/src-d/go-billy.v4" 16 ) 17 18 // RecursiveDelete deletes the given entry from the given filesystem. 19 // If it's a directory, first all the items in the directory are 20 // deleted recursively. 21 func RecursiveDelete( 22 ctx context.Context, fs billy.Filesystem, fi os.FileInfo) error { 23 select { 24 case <-ctx.Done(): 25 return ctx.Err() 26 default: 27 } 28 29 if !fi.IsDir() { 30 // Delete regular files and symlinks directly. 31 return fs.Remove(fi.Name()) 32 } 33 34 subdirFS, err := fs.Chroot(fi.Name()) 35 if err != nil { 36 return err 37 } 38 39 children, err := subdirFS.ReadDir("/") 40 if err != nil { 41 return err 42 } 43 for _, childFI := range children { 44 if childFI.Name() == "." { 45 continue 46 } 47 err := RecursiveDelete(ctx, subdirFS, childFI) 48 if err != nil { 49 return err 50 } 51 } 52 53 return fs.Remove(fi.Name()) 54 } 55 56 var obsConflictRegexp = regexp.MustCompile(`-([[:digit:]]+)(\.|$)`) 57 58 func stripObfuscatedConflictSuffix(s string) string { 59 replace := "" 60 if strings.Contains(s, ".") { 61 replace = "." 62 } 63 return obsConflictRegexp.ReplaceAllString(s, replace) 64 } 65 66 func deobfuscate( 67 ctx context.Context, fs *FS, pathParts []string) (res []string, err error) { 68 if len(pathParts) == 0 { 69 return nil, nil 70 } 71 72 fis, err := fs.ReadDir("") 73 if err != nil { 74 return nil, err 75 } 76 77 elem := stripObfuscatedConflictSuffix(pathParts[0]) 78 for _, fi := range fis { 79 name := fi.Name() 80 obsName := stripObfuscatedConflictSuffix(fs.PathForLogging(name)) 81 if obsName == elem { 82 if len(pathParts) == 1 { 83 res = append(res, name) 84 } else { 85 childFS, err := fs.ChrootAsLibFS(name) 86 if err != nil { 87 return nil, err 88 } 89 90 children, err := deobfuscate(ctx, childFS, pathParts[1:]) 91 if err != nil { 92 return nil, err 93 } 94 for _, c := range children { 95 res = append(res, path.Join(name, c)) 96 } 97 } 98 } 99 100 if fi.Mode()&os.ModeSymlink > 0 { 101 link, err := fs.Readlink(name) 102 if err != nil { 103 return nil, err 104 } 105 obsName := fs.RootNode().ChildName(link).String() 106 if obsName == elem { 107 res = append(res, fmt.Sprintf("%s (%s)", name, link)) 108 } 109 } 110 } 111 return res, nil 112 } 113 114 // Deobfuscate returns a set of possible plaintext paths, given an 115 // obfuscated path as input. The set is ambiguous because of possible 116 // conflicts in the obfuscated name. If the last element of the 117 // obfuscated path matches the obfuscated version of a symlink target 118 // within the target directory, the returned string includes the 119 // symlink itself, followed by the target name in parentheses like 120 // `/keybase/private/me/link (/etc/passwd)`. 121 func Deobfuscate( 122 ctx context.Context, fs *FS, obfuscatedPath string) ([]string, error) { 123 s := strings.Split(obfuscatedPath, "/") 124 return deobfuscate(ctx, fs, s) 125 }