1/* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15*/ 16 17package fstest 18 19import ( 20 "bytes" 21 "io" 22 "math/rand" 23 "os" 24 "path/filepath" 25 "syscall" 26 "time" 27) 28 29// Applier applies single file changes 30type Applier interface { 31 Apply(root string) error 32} 33 34type applyFn func(root string) error 35 36func (a applyFn) Apply(root string) error { 37 return a(root) 38} 39 40// CreateFile returns a file applier which creates a file as the 41// provided name with the given content and permission. 42func CreateFile(name string, content []byte, perm os.FileMode) Applier { 43 f := func() io.Reader { 44 return bytes.NewReader(content) 45 } 46 return writeFileStream(name, f, perm) 47} 48 49// CreateRandomFile returns a file applier which creates a file with random 50// content of the given size using the given seed and permission. 51func CreateRandomFile(name string, seed, size int64, perm os.FileMode) Applier { 52 f := func() io.Reader { 53 return io.LimitReader(rand.New(rand.NewSource(seed)), size) 54 } 55 return writeFileStream(name, f, perm) 56} 57 58// writeFileStream returns a file applier which creates a file as the 59// provided name with the given content from the provided i/o stream and permission. 60func writeFileStream(name string, stream func() io.Reader, perm os.FileMode) Applier { 61 return applyFn(func(root string) (retErr error) { 62 fullPath := filepath.Join(root, name) 63 f, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) 64 if err != nil { 65 return err 66 } 67 defer func() { 68 err := f.Close() 69 if err != nil && retErr == nil { 70 retErr = err 71 } 72 }() 73 _, err = io.Copy(f, stream()) 74 if err != nil { 75 return err 76 } 77 return os.Chmod(fullPath, perm) 78 }) 79} 80 81// Remove returns a file applier which removes the provided file name 82func Remove(name string) Applier { 83 return applyFn(func(root string) error { 84 return os.Remove(filepath.Join(root, name)) 85 }) 86} 87 88// RemoveAll returns a file applier which removes the provided file name 89// as in os.RemoveAll 90func RemoveAll(name string) Applier { 91 return applyFn(func(root string) error { 92 return os.RemoveAll(filepath.Join(root, name)) 93 }) 94} 95 96// CreateDir returns a file applier to create the directory with 97// the provided name and permission 98func CreateDir(name string, perm os.FileMode) Applier { 99 return applyFn(func(root string) error { 100 fullPath := filepath.Join(root, name) 101 if err := os.MkdirAll(fullPath, perm); err != nil { 102 return err 103 } 104 return os.Chmod(fullPath, perm) 105 }) 106} 107 108// Rename returns a file applier which renames a file 109func Rename(old, new string) Applier { 110 return applyFn(func(root string) error { 111 return os.Rename(filepath.Join(root, old), filepath.Join(root, new)) 112 }) 113} 114 115// Chown returns a file applier which changes the ownership of a file 116func Chown(name string, uid, gid int) Applier { 117 return applyFn(func(root string) error { 118 return os.Chown(filepath.Join(root, name), uid, gid) 119 }) 120} 121 122// Chtimes changes access and mod time of file. 123// Use Lchtimes for symbolic links. 124func Chtimes(name string, atime, mtime time.Time) Applier { 125 return applyFn(func(root string) error { 126 return os.Chtimes(filepath.Join(root, name), atime, mtime) 127 }) 128} 129 130// Chmod returns a file applier which changes the file permission 131func Chmod(name string, perm os.FileMode) Applier { 132 return applyFn(func(root string) error { 133 return os.Chmod(filepath.Join(root, name), perm) 134 }) 135} 136 137// Symlink returns a file applier which creates a symbolic link 138func Symlink(oldname, newname string) Applier { 139 return applyFn(func(root string) error { 140 return os.Symlink(oldname, filepath.Join(root, newname)) 141 }) 142} 143 144// Link returns a file applier which creates a hard link 145func Link(oldname, newname string) Applier { 146 return applyFn(func(root string) error { 147 return os.Link(filepath.Join(root, oldname), filepath.Join(root, newname)) 148 }) 149} 150 151// TODO: Make platform specific, windows applier is always no-op 152//func Mknod(name string, mode int32, dev int) Applier { 153// return func(root string) error { 154// return return syscall.Mknod(path, mode, dev) 155// } 156//} 157 158func CreateSocket(name string, perm os.FileMode) Applier { 159 return applyFn(func(root string) error { 160 fullPath := filepath.Join(root, name) 161 fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) 162 if err != nil { 163 return err 164 } 165 defer syscall.Close(fd) 166 sa := &syscall.SockaddrUnix{Name: fullPath} 167 if err := syscall.Bind(fd, sa); err != nil { 168 return err 169 } 170 return os.Chmod(fullPath, perm) 171 }) 172} 173 174// Apply returns a new applier from the given appliers 175func Apply(appliers ...Applier) Applier { 176 return applyFn(func(root string) error { 177 for _, a := range appliers { 178 if err := a.Apply(root); err != nil { 179 return err 180 } 181 } 182 return nil 183 }) 184} 185