github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/persist/fs/fs.go (about) 1 // Copyright (c) 2016 Intel Corporation 2 // Copyright (c) 2019 Huawei Corporation 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package fs 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "path/filepath" 15 "syscall" 16 17 persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api" 18 "github.com/sirupsen/logrus" 19 ) 20 21 // persistFile is the file name for JSON sandbox/container configuration 22 const persistFile = "persist.json" 23 24 // dirMode is the permission bits used for creating a directory 25 const dirMode = os.FileMode(0700) | os.ModeDir 26 27 // fileMode is the permission bits used for creating a file 28 const fileMode = os.FileMode(0600) 29 30 // storagePathSuffix is the suffix used for all storage paths 31 // 32 // Note: this very brief path represents "virtcontainers". It is as 33 // terse as possible to minimise path length. 34 const storagePathSuffix = "vc" 35 36 // sandboxPathSuffix is the suffix used for sandbox storage 37 const sandboxPathSuffix = "sbs" 38 39 // vmPathSuffix is the suffix used for guest VMs. 40 const vmPathSuffix = "vm" 41 42 // FS storage driver implementation 43 type FS struct { 44 sandboxState *persistapi.SandboxState 45 containerState map[string]persistapi.ContainerState 46 storageRootPath string 47 driverName string 48 } 49 50 var fsLog = logrus.WithField("source", "virtcontainers/persist/fs") 51 52 // Logger returns a logrus logger appropriate for logging Store messages 53 func (fs *FS) Logger() *logrus.Entry { 54 return fsLog.WithFields(logrus.Fields{ 55 "subsystem": "persist", 56 "driver": fs.driverName, 57 }) 58 } 59 60 // Init FS persist driver and return abstract PersistDriver 61 func Init() (persistapi.PersistDriver, error) { 62 return &FS{ 63 sandboxState: &persistapi.SandboxState{}, 64 containerState: make(map[string]persistapi.ContainerState), 65 storageRootPath: filepath.Join("/run", storagePathSuffix), 66 driverName: "fs", 67 }, nil 68 } 69 70 func (fs *FS) sandboxDir(sandboxID string) (string, error) { 71 return filepath.Join(fs.RunStoragePath(), sandboxID), nil 72 } 73 74 // ToDisk sandboxState and containerState to disk 75 func (fs *FS) ToDisk(ss persistapi.SandboxState, cs map[string]persistapi.ContainerState) (retErr error) { 76 id := ss.SandboxContainer 77 if id == "" { 78 return fmt.Errorf("sandbox container id required") 79 } 80 81 fs.sandboxState = &ss 82 fs.containerState = cs 83 84 sandboxDir, err := fs.sandboxDir(id) 85 if err != nil { 86 return err 87 } 88 89 if err := os.MkdirAll(sandboxDir, dirMode); err != nil { 90 return err 91 } 92 93 // if error happened, destroy all dirs 94 defer func() { 95 if retErr != nil { 96 if err := fs.Destroy(id); err != nil { 97 fs.Logger().WithError(err).Errorf("failed to destroy dirs") 98 } 99 } 100 }() 101 102 // persist sandbox configuration data 103 sandboxFile := filepath.Join(sandboxDir, persistFile) 104 f, err := os.OpenFile(sandboxFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fileMode) 105 if err != nil { 106 return err 107 } 108 defer f.Close() 109 110 if err := json.NewEncoder(f).Encode(fs.sandboxState); err != nil { 111 return err 112 } 113 114 var dirCreationErr error 115 var createdDirs []string 116 defer func() { 117 if dirCreationErr != nil && len(createdDirs) > 0 { 118 for _, dir := range createdDirs { 119 os.RemoveAll(dir) 120 } 121 } 122 }() 123 // persist container configuration data 124 for cid, cstate := range fs.containerState { 125 cdir := filepath.Join(sandboxDir, cid) 126 if dirCreationErr = os.MkdirAll(cdir, dirMode); dirCreationErr != nil { 127 return dirCreationErr 128 } 129 createdDirs = append(createdDirs, cdir) 130 131 cfile := filepath.Join(cdir, persistFile) 132 cf, err := os.OpenFile(cfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode) 133 if err != nil { 134 return err 135 } 136 137 defer cf.Close() 138 if err := json.NewEncoder(cf).Encode(cstate); err != nil { 139 return err 140 } 141 } 142 143 // Walk sandbox dir and find container. 144 files, err := ioutil.ReadDir(sandboxDir) 145 if err != nil { 146 return err 147 } 148 149 // Remove non-existing containers 150 for _, file := range files { 151 if !file.IsDir() { 152 continue 153 } 154 // Container dir exists. 155 cid := file.Name() 156 157 // Container should be removed when container id doesn't exist in cs. 158 if _, ok := cs[cid]; !ok { 159 if err := os.RemoveAll(filepath.Join(sandboxDir, cid)); err != nil { 160 return err 161 } 162 } 163 } 164 return nil 165 } 166 167 // FromDisk restores state for sandbox with name sid 168 func (fs *FS) FromDisk(sid string) (persistapi.SandboxState, map[string]persistapi.ContainerState, error) { 169 ss := persistapi.SandboxState{} 170 if sid == "" { 171 return ss, nil, fmt.Errorf("restore requires sandbox id") 172 } 173 174 sandboxDir, err := fs.sandboxDir(sid) 175 if err != nil { 176 return ss, nil, err 177 } 178 179 // get sandbox configuration from persist data 180 sandboxFile := filepath.Join(sandboxDir, persistFile) 181 f, err := os.OpenFile(sandboxFile, os.O_RDONLY, fileMode) 182 if err != nil { 183 return ss, nil, err 184 } 185 defer f.Close() 186 187 if err := json.NewDecoder(f).Decode(fs.sandboxState); err != nil { 188 return ss, nil, err 189 } 190 191 // walk sandbox dir and find container 192 files, err := ioutil.ReadDir(sandboxDir) 193 if err != nil { 194 return ss, nil, err 195 } 196 197 for _, file := range files { 198 if !file.IsDir() { 199 continue 200 } 201 202 cid := file.Name() 203 cfile := filepath.Join(sandboxDir, cid, persistFile) 204 cf, err := os.OpenFile(cfile, os.O_RDONLY, fileMode) 205 if err != nil { 206 // if persist.json doesn't exist, ignore and go to next 207 if os.IsNotExist(err) { 208 continue 209 } 210 return ss, nil, err 211 } 212 213 defer cf.Close() 214 var cstate persistapi.ContainerState 215 if err := json.NewDecoder(cf).Decode(&cstate); err != nil { 216 return ss, nil, err 217 } 218 219 fs.containerState[cid] = cstate 220 } 221 222 return *fs.sandboxState, fs.containerState, nil 223 } 224 225 // Destroy removes everything from disk 226 func (fs *FS) Destroy(sandboxID string) error { 227 if sandboxID == "" { 228 return fmt.Errorf("sandbox container id required") 229 } 230 231 sandboxDir, err := fs.sandboxDir(sandboxID) 232 if err != nil { 233 return err 234 } 235 236 if err := os.RemoveAll(sandboxDir); err != nil { 237 return err 238 } 239 return nil 240 } 241 242 func (fs *FS) Lock(sandboxID string, exclusive bool) (func() error, error) { 243 if sandboxID == "" { 244 return nil, fmt.Errorf("sandbox container id required") 245 } 246 247 sandboxDir, err := fs.sandboxDir(sandboxID) 248 if err != nil { 249 return nil, err 250 } 251 252 f, err := os.Open(sandboxDir) 253 if err != nil { 254 return nil, err 255 } 256 257 var lockType int 258 if exclusive { 259 lockType = syscall.LOCK_EX 260 } else { 261 lockType = syscall.LOCK_SH 262 } 263 264 if err := syscall.Flock(int(f.Fd()), lockType); err != nil { 265 f.Close() 266 return nil, err 267 } 268 269 unlockFunc := func() error { 270 defer f.Close() 271 if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil { 272 return err 273 } 274 275 return nil 276 } 277 return unlockFunc, nil 278 } 279 280 func (fs *FS) GlobalWrite(relativePath string, data []byte) error { 281 path := filepath.Join(fs.storageRootPath, relativePath) 282 path, err := filepath.Abs(filepath.Clean(path)) 283 if err != nil { 284 return fmt.Errorf("failed to find abs path for %q: %v", relativePath, err) 285 } 286 287 dir := filepath.Dir(path) 288 289 _, err = os.Stat(dir) 290 if os.IsNotExist(err) { 291 if err := os.MkdirAll(dir, dirMode); err != nil { 292 fs.Logger().WithError(err).WithField("directory", dir).Error("failed to create dir") 293 return err 294 } 295 } else if err != nil { 296 return err 297 } 298 299 f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, fileMode) 300 if err != nil { 301 fs.Logger().WithError(err).WithField("file", path).Error("failed to open file for writing") 302 return err 303 } 304 defer f.Close() 305 306 if _, err := f.Write(data); err != nil { 307 fs.Logger().WithError(err).WithField("file", path).Error("failed to write file") 308 return err 309 } 310 return nil 311 } 312 313 func (fs *FS) GlobalRead(relativePath string) ([]byte, error) { 314 path := filepath.Join(fs.storageRootPath, relativePath) 315 path, err := filepath.Abs(filepath.Clean(path)) 316 if err != nil { 317 return nil, fmt.Errorf("failed to find abs path for %q: %v", relativePath, err) 318 } 319 320 f, err := os.Open(path) 321 if err != nil { 322 fs.Logger().WithError(err).WithField("file", path).Error("failed to open file for reading") 323 return nil, err 324 } 325 defer f.Close() 326 327 data, err := ioutil.ReadAll(f) 328 if err != nil { 329 fs.Logger().WithError(err).WithField("file", path).Error("failed to read file") 330 return nil, err 331 } 332 return data, nil 333 } 334 335 func (fs *FS) RunStoragePath() string { 336 return filepath.Join(fs.storageRootPath, sandboxPathSuffix) 337 } 338 339 func (fs *FS) RunVMStoragePath() string { 340 return filepath.Join(fs.storageRootPath, vmPathSuffix) 341 }