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