Add read-only layer/image/container stores

Implement read-only versions of layer and image store interfaces which
allocate read-only locks and which return errors whenever a write
function is called (which should only be possible after a type
assertion, since they're not part of the read-only interfaces).

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai 2017-06-08 14:10:50 -04:00
parent 198f752fb5
commit a9b1fe6241
3 changed files with 129 additions and 20 deletions

View File

@ -2,7 +2,6 @@ package storage
import (
"encoding/json"
"errors"
"io/ioutil"
"os"
"path/filepath"
@ -11,6 +10,7 @@ import (
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/truncindex"
"github.com/pkg/errors"
)
var (
@ -123,7 +123,7 @@ func (r *imageStore) datapath(id, key string) string {
}
func (r *imageStore) Load() error {
needSave := false
shouldSave := false
rpath := r.imagespath()
data, err := ioutil.ReadFile(rpath)
if err != nil && !os.IsNotExist(err) {
@ -140,23 +140,29 @@ func (r *imageStore) Load() error {
for _, name := range image.Names {
if conflict, ok := names[name]; ok {
r.removeName(conflict, name)
needSave = true
shouldSave = true
}
names[name] = images[n]
}
}
}
if shouldSave && !r.IsReadWrite() {
return errors.New("image store assigns the same name to multiple images")
}
r.images = images
r.idindex = truncindex.NewTruncIndex(idlist)
r.byid = ids
r.byname = names
if needSave {
if shouldSave {
return r.Save()
}
return nil
}
func (r *imageStore) Save() error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the image store at %q", r.imagespath())
}
rpath := r.imagespath()
if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil {
return err
@ -192,6 +198,26 @@ func newImageStore(dir string) (ImageStore, error) {
return &istore, nil
}
func newROImageStore(dir string) (ROImageStore, error) {
lockfile, err := GetROLockfile(filepath.Join(dir, "images.lock"))
if err != nil {
return nil, err
}
lockfile.Lock()
defer lockfile.Unlock()
istore := imageStore{
lockfile: lockfile,
dir: dir,
images: []*Image{},
byid: make(map[string]*Image),
byname: make(map[string]*Image),
}
if err := istore.Load(); err != nil {
return nil, err
}
return &istore, nil
}
func (r *imageStore) lookup(id string) (*Image, bool) {
if image, ok := r.byid[id]; ok {
return image, ok
@ -205,6 +231,9 @@ func (r *imageStore) lookup(id string) (*Image, bool) {
}
func (r *imageStore) ClearFlag(id string, flag string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to clear flags on images at %q", r.imagespath())
}
image, ok := r.lookup(id)
if !ok {
return ErrImageUnknown
@ -214,6 +243,9 @@ func (r *imageStore) ClearFlag(id string, flag string) error {
}
func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to set flags on images at %q", r.imagespath())
}
image, ok := r.lookup(id)
if !ok {
return ErrImageUnknown
@ -223,6 +255,9 @@ func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
}
func (r *imageStore) Create(id string, names []string, layer, metadata string) (image *Image, err error) {
if !r.IsReadWrite() {
return nil, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new images at %q", r.imagespath())
}
if id == "" {
id = stringid.GenerateRandomID()
_, idInUse := r.byid[id]
@ -268,6 +303,9 @@ func (r *imageStore) Metadata(id string) (string, error) {
}
func (r *imageStore) SetMetadata(id, metadata string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify image metadata at %q", r.imagespath())
}
if image, ok := r.lookup(id); ok {
image.Metadata = metadata
return r.Save()
@ -280,6 +318,9 @@ func (r *imageStore) removeName(image *Image, name string) {
}
func (r *imageStore) SetNames(id string, names []string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change image name assignments at %q", r.imagespath())
}
if image, ok := r.lookup(id); ok {
for _, name := range image.Names {
delete(r.byname, name)
@ -297,6 +338,9 @@ func (r *imageStore) SetNames(id string, names []string) error {
}
func (r *imageStore) Delete(id string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete images at %q", r.imagespath())
}
image, ok := r.lookup(id)
if !ok {
return ErrImageUnknown
@ -370,6 +414,9 @@ func (r *imageStore) BigDataNames(id string) ([]string, error) {
}
func (r *imageStore) SetBigData(id, key string, data []byte) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to save data items associated with images at %q", r.imagespath())
}
image, ok := r.lookup(id)
if !ok {
return ErrImageUnknown
@ -404,6 +451,9 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error {
}
func (r *imageStore) Wipe() error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete images at %q", r.imagespath())
}
ids := []string{}
for id := range r.byid {
ids = append(ids, id)

View File

@ -4,7 +4,6 @@ import (
"bytes"
"compress/gzip"
"encoding/json"
"errors"
"io"
"io/ioutil"
"os"
@ -16,6 +15,7 @@ import (
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/truncindex"
"github.com/pkg/errors"
"github.com/vbatts/tar-split/tar/asm"
"github.com/vbatts/tar-split/tar/storage"
)
@ -192,7 +192,7 @@ func (r *layerStore) layerspath() string {
}
func (r *layerStore) Load() error {
needSave := false
shouldSave := false
rpath := r.layerspath()
data, err := ioutil.ReadFile(rpath)
if err != nil && !os.IsNotExist(err) {
@ -211,7 +211,7 @@ func (r *layerStore) Load() error {
for _, name := range layer.Names {
if conflict, ok := names[name]; ok {
r.removeName(conflict, name)
needSave = true
shouldSave = true
}
names[name] = layers[n]
}
@ -222,6 +222,9 @@ func (r *layerStore) Load() error {
}
}
}
if shouldSave && !r.IsReadWrite() {
return errors.New("layer store assigns the same name to multiple layers")
}
mpath := r.mountspath()
data, err = ioutil.ReadFile(mpath)
if err != nil && !os.IsNotExist(err) {
@ -245,9 +248,10 @@ func (r *layerStore) Load() error {
r.byname = names
r.bymount = mounts
err = nil
// Last step: try to remove anything that a previous user of this
// storage area marked for deletion but didn't manage to actually
// delete.
// Last step: if we're writable, try to remove anything that a previous
// user of this storage area marked for deletion but didn't manage to
// actually delete.
if r.IsReadWrite() {
for _, layer := range r.layers {
if cleanup, ok := layer.Flags[incompleteFlag]; ok {
if b, ok := cleanup.(bool); ok && b {
@ -255,17 +259,21 @@ func (r *layerStore) Load() error {
if err != nil {
break
}
needSave = true
shouldSave = true
}
}
}
if needSave {
if shouldSave {
return r.Save()
}
}
return err
}
func (r *layerStore) Save() error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the layer store at %q", r.layerspath())
}
rpath := r.layerspath()
if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil {
return err
@ -327,6 +335,28 @@ func newLayerStore(rundir string, layerdir string, driver drivers.Driver) (Layer
return &rlstore, nil
}
func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (ROLayerStore, error) {
lockfile, err := GetROLockfile(filepath.Join(layerdir, "layers.lock"))
if err != nil {
return nil, err
}
lockfile.Lock()
defer lockfile.Unlock()
rlstore := layerStore{
lockfile: lockfile,
driver: driver,
rundir: rundir,
layerdir: layerdir,
byid: make(map[string]*Layer),
bymount: make(map[string]*Layer),
byname: make(map[string]*Layer),
}
if err := rlstore.Load(); err != nil {
return nil, err
}
return &rlstore, nil
}
func (r *layerStore) lookup(id string) (*Layer, bool) {
if layer, ok := r.byid[id]; ok {
return layer, ok
@ -340,6 +370,9 @@ func (r *layerStore) lookup(id string) (*Layer, bool) {
}
func (r *layerStore) ClearFlag(id string, flag string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to clear flags on layers at %q", r.layerspath())
}
layer, ok := r.lookup(id)
if !ok {
return ErrLayerUnknown
@ -349,6 +382,9 @@ func (r *layerStore) ClearFlag(id string, flag string) error {
}
func (r *layerStore) SetFlag(id string, flag string, value interface{}) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to set flags on layers at %q", r.layerspath())
}
layer, ok := r.lookup(id)
if !ok {
return ErrLayerUnknown
@ -362,6 +398,9 @@ func (r *layerStore) Status() ([][2]string, error) {
}
func (r *layerStore) Put(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}, diff archive.Reader) (layer *Layer, size int64, err error) {
if !r.IsReadWrite() {
return nil, -1, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new layers at %q", r.layerspath())
}
size = -1
if err := os.MkdirAll(r.rundir, 0700); err != nil {
return nil, -1, err
@ -453,6 +492,9 @@ func (r *layerStore) Create(id, parent string, names []string, mountLabel string
}
func (r *layerStore) Mount(id, mountLabel string) (string, error) {
if !r.IsReadWrite() {
return "", errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
}
layer, ok := r.lookup(id)
if !ok {
return "", ErrLayerUnknown
@ -478,6 +520,9 @@ func (r *layerStore) Mount(id, mountLabel string) (string, error) {
}
func (r *layerStore) Unmount(id string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
}
layer, ok := r.lookup(id)
if !ok {
layerByMount, ok := r.bymount[filepath.Clean(id)]
@ -507,6 +552,9 @@ func (r *layerStore) removeName(layer *Layer, name string) {
}
func (r *layerStore) SetNames(id string, names []string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change layer name assignments at %q", r.layerspath())
}
if layer, ok := r.lookup(id); ok {
for _, name := range layer.Names {
delete(r.byname, name)
@ -531,6 +579,9 @@ func (r *layerStore) Metadata(id string) (string, error) {
}
func (r *layerStore) SetMetadata(id, metadata string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify layer metadata at %q", r.layerspath())
}
if layer, ok := r.lookup(id); ok {
layer.Metadata = metadata
return r.Save()
@ -543,6 +594,9 @@ func (r *layerStore) tspath(id string) string {
}
func (r *layerStore) Delete(id string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath())
}
layer, ok := r.lookup(id)
if !ok {
return ErrLayerUnknown
@ -595,6 +649,9 @@ func (r *layerStore) Get(id string) (*Layer, error) {
}
func (r *layerStore) Wipe() error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath())
}
ids := []string{}
for id := range r.byid {
ids = append(ids, id)

View File

@ -52,6 +52,8 @@ var (
ErrIncompleteOptions = errors.New("missing necessary StoreOptions")
// ErrSizeUnknown is returned when the caller asks for the size of a big data item, but the Store couldn't determine the answer.
ErrSizeUnknown = errors.New("size is not known")
// ErrStoreIsReadOnly is returned when the caller makes a call to a read-only store that would require modifying its contents.
ErrStoreIsReadOnly = errors.New("called a write method on a read-only store")
// DefaultStoreOptions is a reasonable default set of options.
DefaultStoreOptions StoreOptions
stores []*store