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