github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/utils/os/fs/filesystem.go (about) 1 // Copyright © 2022 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fs 16 17 import ( 18 "archive/tar" 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 24 "github.com/sirupsen/logrus" 25 "golang.org/x/sys/unix" 26 ) 27 28 var FS = NewFilesystem() 29 30 type Interface interface { 31 Stat(name string) (os.FileInfo, error) 32 Rename(oldPath, newPath string) error 33 MkdirAll(path string) error 34 MkTmpdir(path string) (string, error) 35 CopyFile(src, dst string) (int64, error) 36 CopyDir(srcPath, dstPath string) error 37 RemoveAll(path ...string) error 38 GetFilesSize(paths []string) (int64, error) 39 } 40 41 type filesystem struct{} 42 43 func (f filesystem) Stat(name string) (os.FileInfo, error) { 44 return os.Stat(name) 45 } 46 47 func (f filesystem) Rename(oldPath, newPath string) error { 48 // remove newPath before mv files to target 49 _, err := f.Stat(newPath) 50 if err == nil { 51 err = f.RemoveAll(newPath) 52 if err != nil { 53 return err 54 } 55 } 56 57 // create dir if filepath.Dir(newPath) not exist 58 _, err = f.Stat(filepath.Dir(newPath)) 59 if err != nil { 60 err = f.MkdirAll(filepath.Dir(newPath)) 61 if err != nil { 62 return err 63 } 64 } 65 66 return os.Rename(oldPath, newPath) 67 } 68 69 func (f filesystem) RemoveAll(path ...string) error { 70 for _, fi := range path { 71 err := os.RemoveAll(fi) 72 if err != nil { 73 return fmt.Errorf("failed to clean file %s: %v", fi, err) 74 } 75 } 76 return nil 77 } 78 79 func (f filesystem) MkdirAll(path string) error { 80 return os.MkdirAll(path, os.ModePerm) 81 } 82 83 func (f filesystem) MkTmpdir(path string) (string, error) { 84 tempDir, err := os.MkdirTemp(path, ".DTmp-") 85 if err != nil { 86 return "", err 87 } 88 return tempDir, os.MkdirAll(tempDir, os.ModePerm) 89 } 90 91 func (f filesystem) CopyDir(srcPath, dstPath string) error { 92 err := f.MkdirAll(dstPath) 93 if err != nil { 94 return err 95 } 96 97 fis, err := os.ReadDir(srcPath) 98 if err != nil { 99 return err 100 } 101 for _, fi := range fis { 102 src := filepath.Join(srcPath, fi.Name()) 103 dst := filepath.Join(dstPath, fi.Name()) 104 if fi.IsDir() { 105 err = f.CopyDir(src, dst) 106 if err != nil { 107 return err 108 } 109 } else { 110 _, err = f.CopyFile(src, dst) 111 if err != nil { 112 return err 113 } 114 } 115 } 116 return nil 117 } 118 119 func (f filesystem) CopyFile(src, dst string) (int64, error) { 120 sourceFileStat, err := os.Stat(src) 121 if err != nil { 122 return 0, err 123 } 124 125 header, err := tar.FileInfoHeader(sourceFileStat, src) 126 if err != nil { 127 return 0, fmt.Errorf("failed to get file info header for %s, err: %v", src, err) 128 } 129 130 if sourceFileStat.Mode()&os.ModeCharDevice != 0 && header.Devminor == 0 && header.Devmajor == 0 { 131 err = unix.Mknod(dst, unix.S_IFCHR, 0) 132 if err != nil { 133 return 0, err 134 } 135 return 0, os.Chown(dst, header.Uid, header.Gid) 136 } 137 138 if !sourceFileStat.Mode().IsRegular() { 139 return 0, fmt.Errorf("%s is not a regular file", src) 140 } 141 142 source, err := os.Open(filepath.Clean(src)) 143 if err != nil { 144 return 0, err 145 } 146 defer func() { 147 if err := source.Close(); err != nil { 148 logrus.Errorf("failed to close file: %v", err) 149 } 150 }() 151 //will overwrite dst when dst is existed 152 destination, err := os.Create(filepath.Clean(dst)) 153 if err != nil { 154 return 0, err 155 } 156 defer func() { 157 if err := destination.Close(); err != nil { 158 logrus.Errorf("failed to close file: %v", err) 159 } 160 }() 161 err = destination.Chmod(sourceFileStat.Mode()) 162 if err != nil { 163 return 0, err 164 } 165 166 err = os.Chown(dst, header.Uid, header.Gid) 167 if err != nil { 168 return 0, err 169 } 170 nBytes, err := io.Copy(destination, source) 171 return nBytes, err 172 } 173 174 func (f filesystem) GetFilesSize(paths []string) (int64, error) { 175 var size int64 176 for i := range paths { 177 s, err := f.getFileSize(paths[i]) 178 if err != nil { 179 return 0, err 180 } 181 size += s 182 } 183 return size, nil 184 } 185 186 func (f filesystem) getFileSize(path string) (size int64, err error) { 187 _, err = os.Stat(path) 188 if err != nil { 189 return 190 } 191 err = filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { 192 if !info.IsDir() { 193 size += info.Size() 194 } 195 return err 196 }) 197 return size, err 198 } 199 200 func NewFilesystem() Interface { 201 return filesystem{} 202 }