FindEntry search a TreeEntry in this tree or any subtree. The lookup path is validated against pathutil.ValidTreePath to prevent attacker-controlled tree contents from leaking past this boundary as `.git`-shaped or path-traversal-shaped names. Callers that legitimately need to look up unsafe paths
(path string)
| 145 | // that legitimately need to look up unsafe paths should walk the |
| 146 | // tree manually. |
| 147 | func (t *Tree) FindEntry(path string) (*TreeEntry, error) { |
| 148 | if err := pathutil.ValidTreePath(path); err != nil { |
| 149 | return nil, err |
| 150 | } |
| 151 | if t.t == nil { |
| 152 | t.t = make(map[string]*Tree) |
| 153 | } |
| 154 | |
| 155 | pathParts := strings.Split(path, "/") |
| 156 | startingTree := t |
| 157 | pathCurrent := "" |
| 158 | |
| 159 | // search for the longest path in the tree path cache |
| 160 | for i := len(pathParts) - 1; i > 1; i-- { |
| 161 | path := filepath.Join(pathParts[:i]...) |
| 162 | |
| 163 | tree, ok := t.t[path] |
| 164 | if ok { |
| 165 | startingTree = tree |
| 166 | pathParts = pathParts[i:] |
| 167 | pathCurrent = path |
| 168 | |
| 169 | break |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | var tree *Tree |
| 174 | var err error |
| 175 | for tree = startingTree; len(pathParts) > 1; pathParts = pathParts[1:] { |
| 176 | if tree, err = tree.dir(pathParts[0]); err != nil { |
| 177 | return nil, err |
| 178 | } |
| 179 | |
| 180 | pathCurrent = filepath.Join(pathCurrent, pathParts[0]) |
| 181 | t.t[pathCurrent] = tree |
| 182 | } |
| 183 | |
| 184 | return tree.entry(pathParts[0]) |
| 185 | } |
| 186 | |
| 187 | func (t *Tree) dir(baseName string) (*Tree, error) { |
| 188 | entry, err := t.entry(baseName) |