runtime: Base64 encode the direct volume mountInfo path

This is to avoid accidentally deleting multiple volumes.

Fixes #4020

Signed-off-by: Feng Wang <feng.wang@databricks.com>
This commit is contained in:
Feng Wang 2022-03-22 19:29:10 -07:00
parent 5d0adb2164
commit 354cd3b9b6
4 changed files with 37 additions and 30 deletions

View File

@ -54,7 +54,10 @@ var addCommand = cli.Command{
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return volume.Add(volumePath, mountInfo) if err := volume.Add(volumePath, mountInfo); err != nil {
return cli.NewExitError(err.Error(), 1)
}
return nil
}, },
} }
@ -69,7 +72,10 @@ var removeCommand = cli.Command{
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return volume.Remove(volumePath) if err := volume.Remove(volumePath); err != nil {
return cli.NewExitError(err.Error(), 1)
}
return nil
}, },
} }
@ -86,9 +92,8 @@ var statsCommand = cli.Command{
Action: func(c *cli.Context) (string, error) { Action: func(c *cli.Context) (string, error) {
stats, err := Stats(volumePath) stats, err := Stats(volumePath)
if err != nil { if err != nil {
return "", err return "", cli.NewExitError(err.Error(), 1)
} }
return string(stats), nil return string(stats), nil
}, },
} }
@ -109,7 +114,10 @@ var resizeCommand = cli.Command{
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return Resize(volumePath, size) if err := Resize(volumePath, size); err != nil {
return cli.NewExitError(err.Error(), 1)
}
return nil
}, },
} }

View File

@ -6,13 +6,13 @@
package volume package volume
import ( import (
b64 "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
const ( const (
@ -37,19 +37,20 @@ type MountInfo struct {
// Add writes the mount info of a direct volume into a filesystem path known to Kata Container. // Add writes the mount info of a direct volume into a filesystem path known to Kata Container.
func Add(volumePath string, mountInfo string) error { func Add(volumePath string, mountInfo string) error {
volumeDir := filepath.Join(kataDirectVolumeRootPath, volumePath) volumeDir := filepath.Join(kataDirectVolumeRootPath, b64.URLEncoding.EncodeToString([]byte(volumePath)))
stat, err := os.Stat(volumeDir) stat, err := os.Stat(volumeDir)
if err != nil && !errors.Is(err, os.ErrNotExist) { if err != nil {
return err if !errors.Is(err, os.ErrNotExist) {
} return err
if stat != nil && !stat.IsDir() { }
return fmt.Errorf("%s should be a directory", volumeDir)
}
if errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(volumeDir, 0700); err != nil { if err := os.MkdirAll(volumeDir, 0700); err != nil {
return err return err
} }
} }
if stat != nil && !stat.IsDir() {
return fmt.Errorf("%s should be a directory", volumeDir)
}
var deserialized MountInfo var deserialized MountInfo
if err := json.Unmarshal([]byte(mountInfo), &deserialized); err != nil { if err := json.Unmarshal([]byte(mountInfo), &deserialized); err != nil {
return err return err
@ -60,14 +61,12 @@ func Add(volumePath string, mountInfo string) error {
// Remove deletes the direct volume path including all the files inside it. // Remove deletes the direct volume path including all the files inside it.
func Remove(volumePath string) error { func Remove(volumePath string) error {
// Find the base of the volume path to delete the whole volume path return os.RemoveAll(filepath.Join(kataDirectVolumeRootPath, b64.URLEncoding.EncodeToString([]byte(volumePath))))
base := strings.SplitN(volumePath, string(os.PathSeparator), 2)[0]
return os.RemoveAll(filepath.Join(kataDirectVolumeRootPath, base))
} }
// VolumeMountInfo retrieves the mount info of a direct volume. // VolumeMountInfo retrieves the mount info of a direct volume.
func VolumeMountInfo(volumePath string) (*MountInfo, error) { func VolumeMountInfo(volumePath string) (*MountInfo, error) {
mountInfoFilePath := filepath.Join(kataDirectVolumeRootPath, volumePath, mountInfoFileName) mountInfoFilePath := filepath.Join(kataDirectVolumeRootPath, b64.URLEncoding.EncodeToString([]byte(volumePath)), mountInfoFileName)
if _, err := os.Stat(mountInfoFilePath); err != nil { if _, err := os.Stat(mountInfoFilePath); err != nil {
return nil, err return nil, err
} }
@ -84,16 +83,17 @@ func VolumeMountInfo(volumePath string) (*MountInfo, error) {
// RecordSandboxId associates a sandbox id with a direct volume. // RecordSandboxId associates a sandbox id with a direct volume.
func RecordSandboxId(sandboxId string, volumePath string) error { func RecordSandboxId(sandboxId string, volumePath string) error {
mountInfoFilePath := filepath.Join(kataDirectVolumeRootPath, volumePath, mountInfoFileName) encodedPath := b64.URLEncoding.EncodeToString([]byte(volumePath))
mountInfoFilePath := filepath.Join(kataDirectVolumeRootPath, encodedPath, mountInfoFileName)
if _, err := os.Stat(mountInfoFilePath); err != nil { if _, err := os.Stat(mountInfoFilePath); err != nil {
return err return err
} }
return ioutil.WriteFile(filepath.Join(kataDirectVolumeRootPath, volumePath, sandboxId), []byte(""), 0600) return ioutil.WriteFile(filepath.Join(kataDirectVolumeRootPath, encodedPath, sandboxId), []byte(""), 0600)
} }
func GetSandboxIdForVolume(volumePath string) (string, error) { func GetSandboxIdForVolume(volumePath string) (string, error) {
files, err := ioutil.ReadDir(filepath.Join(kataDirectVolumeRootPath, volumePath)) files, err := ioutil.ReadDir(filepath.Join(kataDirectVolumeRootPath, b64.URLEncoding.EncodeToString([]byte(volumePath))))
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -6,6 +6,7 @@
package volume package volume
import ( import (
b64 "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"os" "os"
@ -22,7 +23,6 @@ func TestAdd(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
defer os.RemoveAll(kataDirectVolumeRootPath) defer os.RemoveAll(kataDirectVolumeRootPath)
var volumePath = "/a/b/c" var volumePath = "/a/b/c"
var basePath = "a"
actual := MountInfo{ actual := MountInfo{
VolumeType: "block", VolumeType: "block",
Device: "/dev/sda", Device: "/dev/sda",
@ -42,14 +42,15 @@ func TestAdd(t *testing.T) {
assert.Equal(t, expected.FsType, actual.FsType) assert.Equal(t, expected.FsType, actual.FsType)
assert.Equal(t, expected.Options, actual.Options) assert.Equal(t, expected.Options, actual.Options)
_, err = os.Stat(filepath.Join(kataDirectVolumeRootPath, b64.URLEncoding.EncodeToString([]byte(volumePath))))
assert.Nil(t, err)
// Remove the file // Remove the file
err = Remove(volumePath) err = Remove(volumePath)
assert.Nil(t, err) assert.Nil(t, err)
_, err = os.Stat(filepath.Join(kataDirectVolumeRootPath, basePath)) _, err = os.Stat(filepath.Join(kataDirectVolumeRootPath, b64.URLEncoding.EncodeToString([]byte(volumePath))))
assert.True(t, errors.Is(err, os.ErrNotExist)) assert.True(t, errors.Is(err, os.ErrNotExist))
_, err = os.Stat(filepath.Join(kataDirectVolumeRootPath))
// Test invalid mount info json assert.Nil(t, err)
assert.Error(t, Add(volumePath, "{invalid json}"))
} }
func TestRecordSandboxId(t *testing.T) { func TestRecordSandboxId(t *testing.T) {

View File

@ -1549,11 +1549,9 @@ func (k *kataAgent) handleBlkOCIMounts(c *Container, spec *specs.Spec) ([]*grpc.
// Each device will be mounted at a unique location within the VM only once. Mounting // Each device will be mounted at a unique location within the VM only once. Mounting
// to the container specific location is handled within the OCI spec. Let's ensure that // to the container specific location is handled within the OCI spec. Let's ensure that
// the storage mount point is unique for each device. This is then utilized as the source // the storage mount point is unique for each device. This is then utilized as the source
// in the OCI spec. If multiple containers mount the same block device, it's refcounted inside // in the OCI spec. If multiple containers mount the same block device, it's ref-counted inside
// the guest by Kata agent. // the guest by Kata agent.
filename := b64.StdEncoding.EncodeToString([]byte(vol.Source)) filename := b64.URLEncoding.EncodeToString([]byte(vol.Source))
// Make the base64 encoding path safe.
filename = strings.ReplaceAll(filename, "/", "_")
path := filepath.Join(kataGuestSandboxStorageDir(), filename) path := filepath.Join(kataGuestSandboxStorageDir(), filename)
// Update applicable OCI mount source // Update applicable OCI mount source