github.com/Cloud-Foundations/Dominator@v0.3.4/lib/fsutil/api.go (about) 1 package fsutil 2 3 import ( 4 "errors" 5 "hash" 6 "io" 7 "os" 8 "time" 9 10 "github.com/Cloud-Foundations/Dominator/lib/log" 11 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 12 ) 13 14 const ( 15 DirPerms = wsyscall.S_IRWXU | wsyscall.S_IRGRP | wsyscall.S_IXGRP | 16 wsyscall.S_IROTH | wsyscall.S_IXOTH 17 PrivateDirPerms = wsyscall.S_IRWXU 18 PrivateFilePerms = wsyscall.S_IRUSR | wsyscall.S_IWUSR 19 PublicFilePerms = PrivateFilePerms | wsyscall.S_IRGRP | wsyscall.S_IROTH 20 ) 21 22 var ( 23 ErrorChecksumMismatch = errors.New("checksum mismatch") 24 ) 25 26 // CompareFile will read and compare the content of a file and buffer and will 27 // return true if the contents are the same else false. 28 func CompareFile(buffer []byte, filename string) (bool, error) { 29 return compareFile(buffer, filename) 30 } 31 32 // CompareFiles will read and compare the content of two files and return true 33 // if they are the same else false. 34 func CompareFiles(leftFilename, rightFilename string) (bool, error) { 35 return compareFiles(leftFilename, rightFilename) 36 } 37 38 func CopyFile(destFilename, sourceFilename string, mode os.FileMode) error { 39 return copyFile(destFilename, sourceFilename, mode) 40 } 41 42 // CopyToFile will create a new file, write length bytes from reader to the 43 // file and then atomically renames the file to destFilename. If length is zero 44 // all remaining bytes from reader are written. If there are any errors, then 45 // destFilename is unchanged. 46 func CopyToFile(destFilename string, perm os.FileMode, reader io.Reader, 47 length uint64) error { 48 return copyToFile(destFilename, perm, reader, length) 49 } 50 51 // CopyTree will copy a directory tree. 52 func CopyTree(destDir, sourceDir string) error { 53 return copyTree(destDir, sourceDir, copyFile) 54 } 55 56 // CopyTreeWithCopyFunc is similar to CopyTree except it uses a specified copy 57 // function for copying regular files. 58 func CopyTreeWithCopyFunc(destDir, sourceDir string, 59 copyFunc func(destFilename, sourceFilename string, 60 mode os.FileMode) error) error { 61 return copyTree(destDir, sourceDir, copyFunc) 62 } 63 64 // Fallocate will allocate blocks for the file named filename, up to size 65 // specified in bytes. 66 func Fallocate(filename string, size uint64) error { 67 return fallocate(filename, size) 68 } 69 70 // ForceLink creates newname as a hard link to the oldname file. It first 71 // attempts to link using os.Link. If the first attempt fails due to 72 // a permission error, it blindly calls MakeMutable and then retries. If the 73 // first attempt fails due to newname existing, it blindly removes it and then 74 // retries. 75 func ForceLink(oldname, newname string) error { 76 return forceLink(oldname, newname) 77 } 78 79 // ForceRemove removes the named file or directory. It first attempts to remove 80 // using os.Remove and that fails, it blindly calls MakeMutable and then 81 // retries. 82 func ForceRemove(name string) error { 83 return forceRemove(name) 84 } 85 86 // ForceRemoveAll removes path and any children it contains. It first attempts 87 // to remove using os.RemoveAll and that fails, it blindly calls MakeMutable and 88 // then retries. 89 func ForceRemoveAll(path string) error { 90 return forceRemoveAll(path) 91 } 92 93 // ForceRename renames (moves) a file. It first attempts to rename using 94 // os.Rename and if that fails due to a permission error, it blindly calls 95 // MakeMutable and then retries. If it fails because newpath is a directory, it 96 // calls ForceRemoveAll(newpath) and tries again. 97 func ForceRename(oldpath, newpath string) error { 98 return forceRename(oldpath, newpath) 99 } 100 101 // FsyncFile will call file.Sync if it has not been called recently. This 102 // attempts to reduce the performance problems of fsync(2) by potentially 103 // sacrificing some file-system consistency. 104 func FsyncFile(file *os.File) error { return fsyncFile(file) } 105 106 // GetTreeSize will walk a directory tree and count the size of the files. 107 func GetTreeSize(dirname string) (uint64, error) { 108 return getTreeSize(dirname) 109 } 110 111 // LoadLines will open a file and read lines from it. Comment lines (i.e. lines 112 // beginning with '#') are skipped. 113 func LoadLines(filename string) ([]string, error) { 114 return loadLines(filename) 115 } 116 117 // LoopbackDelete will disassociate (delete) a loopback block device from its 118 // backing file. 119 func LoopbackDelete(loopDevice string) error { 120 return loopbackDelete(loopDevice, true) 121 } 122 123 // LoopbackDeleteAndWaitForPartition will disassociate (delete) a loopback block 124 // device from its backing file and wait for specified partition inode to 125 // disappear. 126 func LoopbackDeleteAndWaitForPartition(loopDevice, partition string, 127 timeout time.Duration, logger log.DebugLogger) error { 128 return loopbackDeleteAndWaitForPartition(loopDevice, partition, timeout, 129 logger) 130 } 131 132 // LoopbackSetup will associate a loopback block device with a regular file 133 // named filename. The name of the loop block device is returned. 134 func LoopbackSetup(filename string) (string, error) { 135 return loopbackSetup(filename, true) 136 } 137 138 // LoopbackSetupAndWaitForPartition will associate a loopback block device with 139 // a regular file named filename and wait for the specified partition block 140 // device node to become available. The timeout is limited to one hour. 141 // The name of the loop block device (excluding the partition) is returned. 142 func LoopbackSetupAndWaitForPartition(filename, partition string, 143 timeout time.Duration, logger log.DebugLogger) (string, error) { 144 return loopbackSetupAndWaitForPartition(filename, partition, timeout, 145 logger) 146 } 147 148 // MakeMutable attempts to remove the "immutable" and "append-only" ext2 149 // file-system attributes for one or more files. It is equivalent to calling the 150 // command-line programme "chattr -ai pathname...". 151 func MakeMutable(pathname ...string) error { 152 return makeMutable(pathname...) 153 } 154 155 // ReadDirnames will open the directory named dirname and will read the entries 156 // in that directory. If ignoreMissing is true, no error is returned if the 157 // directory does not exist. 158 func ReadDirnames(dirname string, ignoreMissing bool) ([]string, error) { 159 return readDirnames(dirname, ignoreMissing) 160 } 161 162 // ReadFileTree will traverse the specified directory tree rooted at topdir and 163 // reads the content of each file. The data are returned in a map with the keys 164 // being the filename (excluding the topdir and the specified prefix) and the 165 // data being the corresponding file contents. 166 func ReadFileTree(topdir, prefix string) (map[string][]byte, error) { 167 return readFileTree(topdir, prefix) 168 } 169 170 // ReadLines will read lines from a reader. Comment lines (i.e. lines beginning 171 // with '#') are skipped. 172 func ReadLines(reader io.Reader) ([]string, error) { 173 return readLines(reader) 174 } 175 176 // UpdateFile will read and compare the contents of a file and buffer and will 177 // update the file if different. It returns true if the contents were updated. 178 func UpdateFile(buffer []byte, filename string) (bool, error) { 179 return updateFile(buffer, filename) 180 } 181 182 // WaitFile waits for the file given by pathname to become available to read and 183 // yields a io.ReadCloser when available, or an error if the timeout is 184 // exceeded or an error (other than file not existing) is encountered. A 185 // negative timeout indicates to wait forever. The io.ReadCloser must be closed 186 // after use. 187 func WaitFile(pathname string, timeout time.Duration) (io.ReadCloser, error) { 188 return waitFile(pathname, timeout) 189 } 190 191 // WatchFile watches the file given by pathname and yields a new io.ReadCloser 192 // when a new inode is found and it is a regular file. The io.ReadCloser must 193 // be closed after use. 194 // Any errors are logged to the logger if it is not nil. 195 func WatchFile(pathname string, logger log.Logger) <-chan io.ReadCloser { 196 return watchFile(pathname, logger) 197 } 198 199 // WatchFileStop stops all file watching and cleans up resources that would 200 // otherwise persist across syscall.Exec. 201 func WatchFileStop() { 202 watchFileStop() 203 } 204 205 type ChecksumReader struct { 206 checksummer hash.Hash 207 reader io.Reader 208 } 209 210 func NewChecksumReader(reader io.Reader) *ChecksumReader { 211 return newChecksumReader(reader) 212 } 213 214 func (r *ChecksumReader) GetChecksum() []byte { 215 return r.getChecksum() 216 } 217 218 func (r *ChecksumReader) Read(p []byte) (int, error) { 219 return r.read(p) 220 } 221 222 func (r *ChecksumReader) ReadByte() (byte, error) { 223 return r.readByte() 224 } 225 226 func (r *ChecksumReader) VerifyChecksum() error { 227 return r.verifyChecksum() 228 } 229 230 type ChecksumWriter struct { 231 checksummer hash.Hash 232 writer io.Writer 233 } 234 235 func NewChecksumWriter(writer io.Writer) *ChecksumWriter { 236 return newChecksumWriter(writer) 237 } 238 239 func (w *ChecksumWriter) Write(p []byte) (int, error) { 240 return w.write(p) 241 } 242 243 func (w *ChecksumWriter) WriteChecksum() error { 244 return w.writeChecksum() 245 } 246 247 // RenamingWriter is similar to a writable os.File, except that it attempts to 248 // ensure data integrity. A temporary file is used for writing, which is 249 // renamed during the Close method and an fsync(2) is attempted. 250 type RenamingWriter struct { 251 *os.File 252 filename string 253 abort bool 254 } 255 256 // CreateRenamingWriter will create a temporary file for writing and will rename 257 // the temporary file to filename in the Close method if there are no write 258 // errors. 259 func CreateRenamingWriter(filename string, perm os.FileMode) ( 260 *RenamingWriter, error) { 261 return createRenamingWriter(filename, perm) 262 } 263 264 // Abort will prevent file renaming during a subsequent Close method call. 265 func (w *RenamingWriter) Abort() { 266 w.abort = true 267 } 268 269 // Close may attempt to fsync(2) the contents of the temporary file (if fsync(2) 270 // has not been called recently) and will then close and rename the temporary 271 // file if there were no Write errors or a call to the Abort method. 272 func (w *RenamingWriter) Close() error { 273 if err := recover(); err != nil { 274 w.abort = true 275 w.close() 276 panic(err) 277 } 278 return w.close() 279 } 280 281 func (w *RenamingWriter) Write(p []byte) (int, error) { 282 return w.write(p) 283 }