Resolve candidate paths in traversal order
This commit is contained in:
@@ -106,34 +106,36 @@ func (tree *CandidateTree) ResolveDestination(ctx context.Context, sourcePath, d
|
||||
if strings.HasPrefix(destination, "/") {
|
||||
return Destination{Kind: DestinationOutsideCheckout, Path: path.Clean(destination)}, nil
|
||||
}
|
||||
resolved := path.Clean(path.Join(path.Dir(sourcePath), destination))
|
||||
return tree.resolveDestination(ctx, resolved, make(map[string]bool))
|
||||
return tree.resolveDestination(ctx, path.Dir(sourcePath), strings.Split(destination, "/"), make(map[string]bool))
|
||||
}
|
||||
|
||||
func (tree *CandidateTree) resolveDestination(ctx context.Context, resolved string, active map[string]bool) (Destination, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return Destination{}, err
|
||||
}
|
||||
if outsideCheckout(resolved) {
|
||||
return Destination{Kind: DestinationOutsideCheckout, Path: resolved}, nil
|
||||
}
|
||||
components := strings.Split(resolved, "/")
|
||||
partial := ""
|
||||
func (tree *CandidateTree) resolveDestination(ctx context.Context, directory string, components []string, active map[string]bool) (Destination, error) {
|
||||
for index, component := range components {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return Destination{}, err
|
||||
}
|
||||
partial = path.Join(partial, component)
|
||||
remaining := strings.Join(components[index+1:], "/")
|
||||
remaining := components[index+1:]
|
||||
switch component {
|
||||
case "", ".":
|
||||
continue
|
||||
case "..":
|
||||
if directory == "." {
|
||||
return Destination{Kind: DestinationOutsideCheckout, Path: unresolvedPath("..", remaining)}, nil
|
||||
}
|
||||
directory = path.Dir(directory)
|
||||
continue
|
||||
}
|
||||
partial := path.Join(directory, component)
|
||||
entry, exists := tree.Entry(partial)
|
||||
if !exists {
|
||||
if _, directory := tree.directories[partial]; !directory {
|
||||
return Destination{Kind: DestinationMissing, Path: resolved}, nil
|
||||
if _, isDirectory := tree.directories[partial]; !isDirectory {
|
||||
return Destination{Kind: DestinationMissing, Path: unresolvedPath(partial, remaining)}, nil
|
||||
}
|
||||
directory = partial
|
||||
continue
|
||||
}
|
||||
if entry.Mode == "160000" {
|
||||
return Destination{Kind: DestinationSubmodule, Path: path.Join(partial, remaining)}, nil
|
||||
return Destination{Kind: DestinationSubmodule, Path: unresolvedPath(partial, remaining)}, nil
|
||||
}
|
||||
if entry.Mode == "120000" {
|
||||
if active[partial] {
|
||||
@@ -145,30 +147,38 @@ func (tree *CandidateTree) resolveDestination(ctx context.Context, resolved stri
|
||||
}
|
||||
target := string(linkText)
|
||||
if strings.HasPrefix(target, "/") {
|
||||
return Destination{Kind: DestinationOutsideCheckout, Path: target}, nil
|
||||
return Destination{Kind: DestinationOutsideCheckout, Path: unresolvedPath(target, remaining)}, nil
|
||||
}
|
||||
active[partial] = true
|
||||
destination, err := tree.resolveDestination(ctx, path.Join(path.Dir(partial), target), active)
|
||||
destination, err := tree.resolveDestination(ctx, directory, strings.Split(target, "/"), active)
|
||||
delete(active, partial)
|
||||
if err != nil || remaining == "" || destination.Kind != DestinationDirectory {
|
||||
if err == nil && remaining != "" && destination.Kind == DestinationEntry {
|
||||
return Destination{Kind: DestinationMissing, Path: path.Join(destination.Path, remaining)}, nil
|
||||
}
|
||||
if err != nil || len(remaining) == 0 {
|
||||
return destination, err
|
||||
}
|
||||
return tree.resolveDestination(ctx, path.Join(destination.Path, remaining), active)
|
||||
switch destination.Kind {
|
||||
case DestinationDirectory:
|
||||
return tree.resolveDestination(ctx, destination.Path, remaining, active)
|
||||
case DestinationEntry:
|
||||
destination.Kind = DestinationMissing
|
||||
case DestinationSymlinkCycle:
|
||||
return destination, nil
|
||||
}
|
||||
destination.Path = unresolvedPath(destination.Path, remaining)
|
||||
return destination, nil
|
||||
}
|
||||
if remaining != "" {
|
||||
return Destination{Kind: DestinationMissing, Path: resolved}, nil
|
||||
if len(remaining) != 0 {
|
||||
return Destination{Kind: DestinationMissing, Path: unresolvedPath(partial, remaining)}, nil
|
||||
}
|
||||
return Destination{Kind: DestinationEntry, Path: partial}, nil
|
||||
}
|
||||
if _, directory := tree.directories[resolved]; directory {
|
||||
return Destination{Kind: DestinationDirectory, Path: resolved}, nil
|
||||
}
|
||||
return Destination{Kind: DestinationMissing, Path: resolved}, nil
|
||||
return Destination{Kind: DestinationDirectory, Path: directory}, nil
|
||||
}
|
||||
|
||||
func outsideCheckout(name string) bool {
|
||||
return name == ".." || strings.HasPrefix(name, "../")
|
||||
// unresolvedPath preserves components that could not be traversed. Cleaning
|
||||
// them would hide missing entries, non-directories, or checkout boundaries.
|
||||
func unresolvedPath(base string, remaining []string) string {
|
||||
if len(remaining) == 0 {
|
||||
return base
|
||||
}
|
||||
return base + "/" + strings.Join(remaining, "/")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user