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 }