github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/fileutil/fileutil.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package fileutil 8 9 import ( 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 15 "github.com/pkg/errors" 16 ) 17 18 // CreateAndSyncFileAtomically writes the content to the tmpFile, fsyncs the tmpFile, 19 // renames the tmpFile to the finalFile. In other words, in the event of a crash, either 20 // the final file will not be visible or it will have the full contents. 21 // The tmpFile should not be existing. The finalFile, if exists, will be overwritten (default rename behavior) 22 func CreateAndSyncFileAtomically(dir, tmpFile, finalFile string, content []byte, perm os.FileMode) error { 23 tempFilePath := filepath.Join(dir, tmpFile) 24 finalFilePath := filepath.Join(dir, finalFile) 25 if err := CreateAndSyncFile(tempFilePath, content, perm); err != nil { 26 return err 27 } 28 if err := os.Rename(tempFilePath, finalFilePath); err != nil { 29 return err 30 } 31 return nil 32 } 33 34 // CreateAndSyncFile creates a file, writes the content and syncs the file 35 func CreateAndSyncFile(filePath string, content []byte, perm os.FileMode) error { 36 file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm) 37 if err != nil { 38 return errors.Wrapf(err, "error while creating file:%s", filePath) 39 } 40 _, err = file.Write(content) 41 if err != nil { 42 file.Close() 43 return errors.Wrapf(err, "error while writing to file:%s", filePath) 44 } 45 if err = file.Sync(); err != nil { 46 file.Close() 47 return errors.Wrapf(err, "error while synching the file:%s", filePath) 48 } 49 if err := file.Close(); err != nil { 50 return errors.Wrapf(err, "error while closing the file:%s", filePath) 51 } 52 return nil 53 } 54 55 // SyncParentDir fsyncs the parent dir of the given path 56 func SyncParentDir(path string) error { 57 return SyncDir(filepath.Dir(path)) 58 } 59 60 // CreateDirIfMissing makes sure that the dir exists and returns whether the dir is empty 61 func CreateDirIfMissing(dirPath string) (bool, error) { 62 if err := os.MkdirAll(dirPath, 0o755); err != nil { 63 return false, errors.Wrapf(err, "error while creating dir: %s", dirPath) 64 } 65 if err := SyncParentDir(dirPath); err != nil { 66 return false, err 67 } 68 return DirEmpty(dirPath) 69 } 70 71 // DirEmpty returns true if the dir at dirPath is empty 72 func DirEmpty(dirPath string) (bool, error) { 73 f, err := os.Open(dirPath) 74 if err != nil { 75 return false, errors.Wrapf(err, "error opening dir [%s]", dirPath) 76 } 77 defer f.Close() 78 79 _, err = f.Readdir(1) 80 if err == io.EOF { 81 return true, nil 82 } 83 err = errors.Wrapf(err, "error checking if dir [%s] is empty", dirPath) 84 return false, err 85 } 86 87 // FileExists checks whether the given file exists. 88 // If the file exists, this method also returns the size of the file. 89 func FileExists(path string) (bool, int64, error) { 90 info, err := os.Stat(path) 91 if os.IsNotExist(err) { 92 return false, 0, nil 93 } 94 if err != nil { 95 return false, 0, errors.Wrapf(err, "error checking if file [%s] exists", path) 96 } 97 if info.IsDir() { 98 return false, 0, errors.Errorf("the supplied path [%s] is a dir", path) 99 } 100 return true, info.Size(), nil 101 } 102 103 // DirExists returns true if the dir already exists 104 func DirExists(path string) (bool, error) { 105 info, err := os.Stat(path) 106 if os.IsNotExist(err) { 107 return false, nil 108 } 109 if err != nil { 110 return false, errors.Wrapf(err, "error checking if file [%s] exists", path) 111 } 112 if !info.IsDir() { 113 return false, errors.Errorf("the supplied path [%s] exists but is not a dir", path) 114 } 115 return true, nil 116 } 117 118 // ListSubdirs returns the subdirectories 119 func ListSubdirs(dirPath string) ([]string, error) { 120 subdirs := []string{} 121 files, err := ioutil.ReadDir(dirPath) 122 if err != nil { 123 return nil, errors.Wrapf(err, "error reading dir %s", dirPath) 124 } 125 for _, f := range files { 126 if f.IsDir() { 127 subdirs = append(subdirs, f.Name()) 128 } 129 } 130 return subdirs, nil 131 } 132 133 // RemoveContents removes all the files and subdirs under the specified directory. 134 // It returns nil if the specified directory does not exist. 135 func RemoveContents(dir string) error { 136 contents, err := ioutil.ReadDir(dir) 137 if os.IsNotExist(err) { 138 return nil 139 } 140 if err != nil { 141 return errors.Wrapf(err, "error reading directory %s", dir) 142 } 143 144 for _, c := range contents { 145 if err = os.RemoveAll(filepath.Join(dir, c.Name())); err != nil { 146 return errors.Wrapf(err, "error removing %s under directory %s", c.Name(), dir) 147 } 148 } 149 return SyncDir(dir) 150 }