github.com/sercand/please@v13.4.0+incompatible/src/fs/fs.go (about)

     1  // Package fs provides various filesystem helpers.
     2  package fs
     3  
     4  import (
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path"
    10  	"syscall"
    11  
    12  	"gopkg.in/op/go-logging.v1"
    13  )
    14  
    15  var log = logging.MustGetLogger("fs")
    16  
    17  // DirPermissions are the default permission bits we apply to directories.
    18  const DirPermissions = os.ModeDir | 0775
    19  
    20  // EnsureDir ensures that the directory of the given file has been created.
    21  func EnsureDir(filename string) error {
    22  	dir := path.Dir(filename)
    23  	err := os.MkdirAll(dir, DirPermissions)
    24  	if err != nil && FileExists(dir) {
    25  		// It looks like this is a file and not a directory. Attempt to remove it; this can
    26  		// happen in some cases if you change a rule from outputting a file to a directory.
    27  		log.Warning("Attempting to remove file %s; a subdirectory is required", dir)
    28  		if err2 := os.Remove(dir); err2 == nil {
    29  			err = os.MkdirAll(dir, DirPermissions)
    30  		} else {
    31  			log.Error("%s", err2)
    32  		}
    33  	}
    34  	return err
    35  }
    36  
    37  // PathExists returns true if the given path exists, as a file or a directory.
    38  func PathExists(filename string) bool {
    39  	_, err := os.Lstat(filename)
    40  	return err == nil
    41  }
    42  
    43  // FileExists returns true if the given path exists and is a file.
    44  func FileExists(filename string) bool {
    45  	info, err := os.Lstat(filename)
    46  	return err == nil && !info.IsDir()
    47  }
    48  
    49  // IsSymlink returns true if the given path exists and is a symlink.
    50  func IsSymlink(filename string) bool {
    51  	info, err := os.Lstat(filename)
    52  	return err == nil && (info.Mode()&os.ModeSymlink) != 0
    53  }
    54  
    55  // IsSameFile returns true if two filenames describe the same underlying file (i.e. inode)
    56  func IsSameFile(a, b string) bool {
    57  	i1, err1 := getInode(a)
    58  	i2, err2 := getInode(b)
    59  	return err1 == nil && err2 == nil && i1 == i2
    60  }
    61  
    62  // getInode returns the inode of a file.
    63  func getInode(filename string) (uint64, error) {
    64  	fi, err := os.Stat(filename)
    65  	if err != nil {
    66  		return 0, err
    67  	}
    68  	s, ok := fi.Sys().(*syscall.Stat_t)
    69  	if !ok {
    70  		return 0, fmt.Errorf("Not a syscall.Stat_t")
    71  	}
    72  	return uint64(s.Ino), nil
    73  }
    74  
    75  // CopyFile copies a file from 'from' to 'to', with an attempt to perform a copy & rename
    76  // to avoid chaos if anything goes wrong partway.
    77  func CopyFile(from string, to string, mode os.FileMode) error {
    78  	fromFile, err := os.Open(from)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	defer fromFile.Close()
    83  	return WriteFile(fromFile, to, mode)
    84  }
    85  
    86  // WriteFile writes data from a reader to the file named 'to', with an attempt to perform
    87  // a copy & rename to avoid chaos if anything goes wrong partway.
    88  func WriteFile(fromFile io.Reader, to string, mode os.FileMode) error {
    89  	if err := os.RemoveAll(to); err != nil {
    90  		return err
    91  	}
    92  	dir, file := path.Split(to)
    93  	if err := os.MkdirAll(dir, DirPermissions); err != nil {
    94  		return err
    95  	}
    96  	tempFile, err := ioutil.TempFile(dir, file)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	if _, err := io.Copy(tempFile, fromFile); err != nil {
   101  		return err
   102  	}
   103  	if err := tempFile.Close(); err != nil {
   104  		return err
   105  	}
   106  	// OK, now file is written; adjust permissions appropriately.
   107  	if mode == 0 {
   108  		mode = 0664
   109  	}
   110  	if err := os.Chmod(tempFile.Name(), mode); err != nil {
   111  		return err
   112  	}
   113  	// And move it to its final destination.
   114  	return os.Rename(tempFile.Name(), to)
   115  }
   116  
   117  // IsDirectory checks if a given path is a directory
   118  func IsDirectory(path string) bool {
   119  	info, err := os.Stat(path)
   120  	return err == nil && info.IsDir()
   121  }