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  }