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  }