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  }