github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/upath/symlink.go (about)

     1  // Copyright 2014-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package upath contains utilities for dealing with symlinked paths.
     6  package upath
     7  
     8  import (
     9  	"log"
    10  	"os"
    11  	"path/filepath"
    12  )
    13  
    14  // AbsSymlink returns an absolute path for the link from a file to a target.
    15  func AbsSymlink(originalFile, target string) string {
    16  	if !filepath.IsAbs(originalFile) {
    17  		var err error
    18  		originalFile, err = filepath.Abs(originalFile)
    19  		if err != nil {
    20  			// This should not happen on Unix systems, or you're
    21  			// already royally screwed.
    22  			log.Fatalf("could not determine absolute path for %v: %v", originalFile, err)
    23  		}
    24  	}
    25  	// Relative symlinks are resolved relative to the original file's
    26  	// parent directory.
    27  	//
    28  	// E.g. /bin/defaultsh -> ../bbin/elvish
    29  	if !filepath.IsAbs(target) {
    30  		return filepath.Join(filepath.Dir(originalFile), target)
    31  	}
    32  	return target
    33  }
    34  
    35  // IsTargetSymlink returns true if a target of a symlink is also a symlink.
    36  func IsTargetSymlink(originalFile, target string) bool {
    37  	s, err := os.Lstat(AbsSymlink(originalFile, target))
    38  	if err != nil {
    39  		return false
    40  	}
    41  	return (s.Mode() & os.ModeSymlink) == os.ModeSymlink
    42  }
    43  
    44  // ResolveUntilLastSymlink resolves until the last symlink.
    45  //
    46  // This is needed when we have a chain of symlinks and want the last
    47  // symlink, not the file pointed to (which is why we don't use
    48  // filepath.EvalSymlinks)
    49  //
    50  // I.e.
    51  //
    52  // /foo/bar -> ../baz/foo
    53  // /baz/foo -> bla
    54  //
    55  // ResolveUntilLastSymlink(/foo/bar) returns /baz/foo.
    56  func ResolveUntilLastSymlink(p string) string {
    57  	for target, err := os.Readlink(p); err == nil && IsTargetSymlink(p, target); target, err = os.Readlink(p) {
    58  		p = AbsSymlink(p, target)
    59  	}
    60  	return p
    61  }