mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-26 10:34:24 +01:00
The ItemLock API allows for taking shared and exclusive locks on all items. For virtcontainers, this is specialized into taking locks on the Lock item, and will be used for sandbox locking. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
312 lines
6.8 KiB
Go
312 lines
6.8 KiB
Go
// Copyright (c) 2019 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package store
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/kata-containers/runtime/virtcontainers/pkg/uuid"
|
|
opentracing "github.com/opentracing/opentracing-go"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
// ConfigurationFile is the file name used for every JSON sandbox configuration.
|
|
ConfigurationFile string = "config.json"
|
|
|
|
// StateFile is the file name storing a sandbox state.
|
|
StateFile = "state.json"
|
|
|
|
// NetworkFile is the file name storing a sandbox network.
|
|
NetworkFile = "network.json"
|
|
|
|
// HypervisorFile is the file name storing a hypervisor's state.
|
|
HypervisorFile = "hypervisor.json"
|
|
|
|
// AgentFile is the file name storing an agent's state.
|
|
AgentFile = "agent.json"
|
|
|
|
// ProcessFile is the file name storing a container process.
|
|
ProcessFile = "process.json"
|
|
|
|
// LockFile is the file name locking the usage of a resource.
|
|
LockFile = "lock"
|
|
|
|
// MountsFile is the file name storing a container's mount points.
|
|
MountsFile = "mounts.json"
|
|
|
|
// DevicesFile is the file name storing a container's devices.
|
|
DevicesFile = "devices.json"
|
|
)
|
|
|
|
// DirMode is the permission bits used for creating a directory
|
|
const DirMode = os.FileMode(0750) | os.ModeDir
|
|
|
|
// StoragePathSuffix is the suffix used for all storage paths
|
|
//
|
|
// Note: this very brief path represents "virtcontainers". It is as
|
|
// terse as possible to minimise path length.
|
|
const StoragePathSuffix = "vc"
|
|
|
|
// SandboxPathSuffix is the suffix used for sandbox storage
|
|
const SandboxPathSuffix = "sbs"
|
|
|
|
// VMPathSuffix is the suffix used for guest VMs.
|
|
const VMPathSuffix = "vm"
|
|
|
|
// ConfigStoragePath is the sandbox configuration directory.
|
|
// It will contain one config.json file for each created sandbox.
|
|
var ConfigStoragePath = filepath.Join("/var/lib", StoragePathSuffix, SandboxPathSuffix)
|
|
|
|
// RunStoragePath is the sandbox runtime directory.
|
|
// It will contain one state.json and one lock file for each created sandbox.
|
|
var RunStoragePath = filepath.Join("/run", StoragePathSuffix, SandboxPathSuffix)
|
|
|
|
// RunVMStoragePath is the vm directory.
|
|
// It will contain all guest vm sockets and shared mountpoints.
|
|
var RunVMStoragePath = filepath.Join("/run", StoragePathSuffix, VMPathSuffix)
|
|
|
|
func itemToFile(item Item) (string, error) {
|
|
switch item {
|
|
case Configuration:
|
|
return ConfigurationFile, nil
|
|
case State:
|
|
return StateFile, nil
|
|
case Network:
|
|
return NetworkFile, nil
|
|
case Hypervisor:
|
|
return HypervisorFile, nil
|
|
case Agent:
|
|
return AgentFile, nil
|
|
case Process:
|
|
return ProcessFile, nil
|
|
case Lock:
|
|
return LockFile, nil
|
|
case Mounts:
|
|
return MountsFile, nil
|
|
case Devices, DeviceIDs:
|
|
return DevicesFile, nil
|
|
}
|
|
|
|
return "", fmt.Errorf("Unknown item %s", item)
|
|
}
|
|
|
|
type filesystem struct {
|
|
ctx context.Context
|
|
|
|
path string
|
|
rawPath string
|
|
|
|
lockTokens map[string]*os.File
|
|
}
|
|
|
|
// Logger returns a logrus logger appropriate for logging Store filesystem messages
|
|
func (f *filesystem) logger() *logrus.Entry {
|
|
return storeLog.WithFields(logrus.Fields{
|
|
"subsystem": "store",
|
|
"backend": "filesystem",
|
|
"path": f.path,
|
|
})
|
|
}
|
|
|
|
func (f *filesystem) trace(name string) (opentracing.Span, context.Context) {
|
|
if f.ctx == nil {
|
|
f.logger().WithField("type", "bug").Error("trace called before context set")
|
|
f.ctx = context.Background()
|
|
}
|
|
|
|
span, ctx := opentracing.StartSpanFromContext(f.ctx, name)
|
|
|
|
span.SetTag("subsystem", "store")
|
|
span.SetTag("type", "filesystem")
|
|
span.SetTag("path", f.path)
|
|
|
|
return span, ctx
|
|
}
|
|
|
|
func (f *filesystem) itemToPath(item Item) (string, error) {
|
|
fileName, err := itemToFile(item)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return filepath.Join(f.path, fileName), nil
|
|
}
|
|
|
|
func (f *filesystem) initialize() error {
|
|
_, err := os.Stat(f.path)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
// Our root path does not exist, we need to create the initial layout:
|
|
// The root directory, a lock file and a raw files directory.
|
|
|
|
// Root directory
|
|
if err := os.MkdirAll(f.path, DirMode); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Raw directory
|
|
if err := os.MkdirAll(f.rawPath, DirMode); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Lock
|
|
lockPath := filepath.Join(f.path, LockFile)
|
|
|
|
lockFile, err := os.Create(lockPath)
|
|
if err != nil {
|
|
f.delete()
|
|
return err
|
|
}
|
|
lockFile.Close()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *filesystem) new(ctx context.Context, path string, host string) error {
|
|
f.ctx = ctx
|
|
f.path = path
|
|
f.rawPath = filepath.Join(f.path, "raw")
|
|
f.lockTokens = make(map[string]*os.File)
|
|
|
|
f.logger().Debugf("New filesystem store backend for %s", path)
|
|
|
|
return f.initialize()
|
|
}
|
|
|
|
func (f *filesystem) delete() error {
|
|
return os.RemoveAll(f.path)
|
|
}
|
|
|
|
func (f *filesystem) load(item Item, data interface{}) error {
|
|
span, _ := f.trace("load")
|
|
defer span.Finish()
|
|
|
|
span.SetTag("item", item)
|
|
|
|
filePath, err := f.itemToPath(item)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fileData, err := ioutil.ReadFile(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := json.Unmarshal(fileData, data); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *filesystem) store(item Item, data interface{}) error {
|
|
span, _ := f.trace("store")
|
|
defer span.Finish()
|
|
|
|
span.SetTag("item", item)
|
|
|
|
filePath, err := f.itemToPath(item)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file, err := os.Create(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
jsonOut, err := json.Marshal(data)
|
|
if err != nil {
|
|
return fmt.Errorf("Could not marshall data: %s", err)
|
|
}
|
|
file.Write(jsonOut)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *filesystem) raw(id string) (string, error) {
|
|
span, _ := f.trace("raw")
|
|
defer span.Finish()
|
|
|
|
span.SetTag("id", id)
|
|
|
|
// We must use the item ID.
|
|
if id != "" {
|
|
filePath := filepath.Join(f.rawPath, id)
|
|
file, err := os.Create(filePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return filesystemScheme + "://" + file.Name(), nil
|
|
}
|
|
|
|
// Generate a random temporary file.
|
|
file, err := ioutil.TempFile(f.rawPath, "raw-")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer file.Close()
|
|
|
|
return filesystemScheme + "://" + file.Name(), nil
|
|
}
|
|
|
|
func (f *filesystem) lock(item Item, exclusive bool) (string, error) {
|
|
itemPath, err := f.itemToPath(item)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
itemFile, err := os.Open(itemPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var lockType int
|
|
if exclusive {
|
|
lockType = syscall.LOCK_EX
|
|
} else {
|
|
lockType = syscall.LOCK_SH
|
|
}
|
|
|
|
if err := syscall.Flock(int(itemFile.Fd()), lockType); err != nil {
|
|
itemFile.Close()
|
|
return "", err
|
|
}
|
|
|
|
token := uuid.Generate().String()
|
|
f.lockTokens[token] = itemFile
|
|
|
|
return token, nil
|
|
}
|
|
|
|
func (f *filesystem) unlock(item Item, token string) error {
|
|
itemFile := f.lockTokens[token]
|
|
if itemFile == nil {
|
|
return fmt.Errorf("No lock for token %s", token)
|
|
}
|
|
|
|
if err := syscall.Flock(int(itemFile.Fd()), syscall.LOCK_UN); err != nil {
|
|
return err
|
|
}
|
|
|
|
itemFile.Close()
|
|
delete(f.lockTokens, token)
|
|
|
|
return nil
|
|
}
|