Skip to content

Commit

Permalink
Modify storage to allow callers to determine if a mount point is mounted
Browse files Browse the repository at this point in the history
Add force to umount to force the umount of a container image
Add an interface to indicate whether or not the layer is mounted
Add a boolean return from unmount to indicate when the layer is really unmounted

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan committed Jul 17, 2018
1 parent 124de68 commit 1075a73
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 23 deletions.
4 changes: 2 additions & 2 deletions cmd/containers-storage/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func copyContent(flags *mflag.FlagSet, action string, m storage.Store, args []st
return 1
}
target = filepath.Join(targetMount, targetParts[1])
defer m.Unmount(targetLayer.ID)
defer m.Unmount(targetLayer.ID, false)
}
args = args[:len(args)-1]
for _, srcSpec := range args {
Expand All @@ -83,7 +83,7 @@ func copyContent(flags *mflag.FlagSet, action string, m storage.Store, args []st
return 1
}
source = filepath.Join(sourceMount, sourceParts[1])
defer m.Unmount(sourceLayer.ID)
defer m.Unmount(sourceLayer.ID, false)
}
archiver := chrootarchive.NewArchiverWithChown(tarIDMappings, chownOpts, untarIDMappings)
if err := archiver.CopyWithTar(source, target); err != nil {
Expand Down
1 change: 1 addition & 0 deletions cmd/containers-storage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type command struct {
var (
commands = []command{}
jsonOutput = false
force = false
)

func main() {
Expand Down
48 changes: 47 additions & 1 deletion cmd/containers-storage/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ func unmount(flags *mflag.FlagSet, action string, m storage.Store, args []string
mes := []mountPointError{}
errors := false
for _, arg := range args {
err := m.Unmount(arg)
mounted, err := m.Unmount(arg, force)
errText := ""
if err != nil {
errText = fmt.Sprintf("%v", err)
errors = true
} else {
if !mounted {
fmt.Printf("%s mountpoint unmounted\n", arg)
}
}
mes = append(mes, mountPointError{arg, errText})
}
Expand All @@ -74,6 +78,37 @@ func unmount(flags *mflag.FlagSet, action string, m storage.Store, args []string
return 0
}

func mounted(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
mes := []mountPointError{}
errors := false
for _, arg := range args {
mounted, err := m.Mounted(arg)
errText := ""
if err != nil {
errText = fmt.Sprintf("%v", err)
errors = true
} else {
if mounted {
fmt.Printf("%s mounted\n", arg)
}
}
mes = append(mes, mountPointError{arg, errText})
}
if jsonOutput {
json.NewEncoder(os.Stdout).Encode(mes)
} else {
for _, me := range mes {
if me.Error != "" {
fmt.Fprintf(os.Stderr, "%s checking if %s is mounted\n", me.Error, me.ID)
}
}
}
if errors {
return 1
}
return 0
}

func init() {
commands = append(commands, command{
names: []string{"mount"},
Expand All @@ -92,6 +127,17 @@ func init() {
usage: "Unmount a layer or container",
minArgs: 1,
action: unmount,
addFlags: func(flags *mflag.FlagSet, cmd *command) {
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
flags.BoolVar(&force, []string{"-force", "f"}, jsonOutput, "Force the umount")
},
})
commands = append(commands, command{
names: []string{"mounted"},
optionsHelp: "LayerOrContainerNameOrID",
usage: "Check if a file system is mounted",
minArgs: 1,
action: mounted,
addFlags: func(flags *mflag.FlagSet, cmd *command) {
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
},
Expand Down
37 changes: 25 additions & 12 deletions layers.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ type LayerStore interface {
Mount(id, mountLabel string) (string, error)

// Unmount unmounts a layer when it is no longer in use.
Unmount(id string) error
Unmount(id string, force bool) (bool, error)

// Mounted returns whether or not the layer is mounted.
Mounted(id string) (bool, error)

// ParentOwners returns the UIDs and GIDs of parents of the layer's mountpoint
// for which the layer's UID and GID maps don't contain corresponding entries.
Expand Down Expand Up @@ -624,6 +627,14 @@ func (r *layerStore) Create(id string, parent *Layer, names []string, mountLabel
return r.CreateWithFlags(id, parent, names, mountLabel, options, moreOptions, writeable, nil)
}

func (r *layerStore) Mounted(id string) (bool, error) {
layer, ok := r.lookup(id)
if !ok {
return false, ErrLayerUnknown
}
return layer.MountCount > 0, nil
}

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())
Expand Down Expand Up @@ -652,21 +663,24 @@ func (r *layerStore) Mount(id, mountLabel string) (string, error) {
return mountpoint, err
}

func (r *layerStore) Unmount(id string) error {
func (r *layerStore) Unmount(id string, force bool) (bool, error) {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
return false, 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)]
if !ok {
return ErrLayerUnknown
return false, ErrLayerUnknown
}
layer = layerByMount
}
if force {
layer.MountCount = 1
}
if layer.MountCount > 1 {
layer.MountCount--
return r.Save()
return true, r.Save()
}
err := r.driver.Put(id)
if err == nil || os.IsNotExist(err) {
Expand All @@ -675,9 +689,9 @@ func (r *layerStore) Unmount(id string) error {
}
layer.MountCount--
layer.MountPoint = ""
err = r.Save()
return false, r.Save()
}
return err
return true, err
}

func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) {
Expand Down Expand Up @@ -797,10 +811,8 @@ func (r *layerStore) Delete(id string) error {
return ErrLayerUnknown
}
id = layer.ID
for layer.MountCount > 0 {
if err := r.Unmount(id); err != nil {
return err
}
if _, err := r.Unmount(id, true); err != nil {
return err
}
err := r.driver.Remove(id)
if err == nil {
Expand Down Expand Up @@ -917,7 +929,8 @@ func (s *simpleGetCloser) Get(path string) (io.ReadCloser, error) {
}

func (s *simpleGetCloser) Close() error {
return s.r.Unmount(s.id)
_, err := s.r.Unmount(s.id, false)

This comment has been minimized.

Copy link
@runcom

runcom Nov 9, 2018

Member

why this is false when Unmount'ing? could it be the issue with devmapper regression in #233

@rhatdan @nalind

return err
}

func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
Expand Down
2 changes: 1 addition & 1 deletion layers_ffjson.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 27 additions & 7 deletions store.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,11 @@ type Store interface {
Mount(id, mountLabel string) (string, error)

// Unmount attempts to unmount a layer, image, or container, given an ID, a
// name, or a mount path.
Unmount(id string) error
// name, or a mount path. Returns whether or not the layer is still mounted.
Unmount(id string, force bool) (bool, error)

// Unmount attempts to discover whether the specified id is mounted.
Mounted(id string) (bool, error)

// Changes returns a summary of the changes which would need to be made
// to one layer to make its contents the same as a second layer. If
Expand Down Expand Up @@ -2245,23 +2248,40 @@ func (s *store) Mount(id, mountLabel string) (string, error) {
return "", ErrLayerUnknown
}

func (s *store) Unmount(id string) error {
func (s *store) Mounted(id string) (bool, error) {
if layerID, err := s.ContainerLayerID(id); err == nil {
id = layerID
}
rlstore, err := s.LayerStore()
if err != nil {
return err
return false, err
}
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
rlstore.Load()
}

return rlstore.Mounted(id)
}

func (s *store) Unmount(id string, force bool) (bool, error) {
if layerID, err := s.ContainerLayerID(id); err == nil {
id = layerID
}
rlstore, err := s.LayerStore()
if err != nil {
return false, err
}
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
rlstore.Load()
}
if rlstore.Exists(id) {
return rlstore.Unmount(id)
return rlstore.Unmount(id, force)
}
return ErrLayerUnknown
return false, ErrLayerUnknown
}

func (s *store) Changes(from, to string) ([]archive.Change, error) {
Expand Down Expand Up @@ -2811,7 +2831,7 @@ func (s *store) Shutdown(force bool) ([]string, error) {
mounted = append(mounted, layer.ID)
if force {
for layer.MountCount > 0 {
err2 := rlstore.Unmount(layer.ID)
_, err2 := rlstore.Unmount(layer.ID, force)
if err2 != nil {
if err == nil {
err = err2
Expand Down
81 changes: 81 additions & 0 deletions tests/mount-layer.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bats

load helpers

@test "mount-layer" {
# Create a layer.
run storage --debug=false create-layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
layer="$output"

# Mount the layer.
run storage --debug=false mount $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
# Check if layer is mounted.
run storage --debug=false mounted $layer
[ "$status" -eq 0 ]
[ "$output" == "$layer mounted" ]
# Unmount the layer.
run storage --debug=false unmount $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
# Make sure layer is not mounted.
run storage --debug=false mounted $layer
[ "$status" -eq 0 ]
[ "$output" == "" ]

# Mount the layer twice.
run storage --debug=false mount $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
run storage --debug=false mount $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
# Check if layer is mounted.
run storage --debug=false mounted $layer
[ "$status" -eq 0 ]
[ "$output" == "$layer mounted" ]
# Unmount the second layer.
run storage --debug=false unmount $layer
[ "$status" -eq 0 ]
[ "$output" == "" ]
# Check if layer is mounted.
run storage --debug=false mounted $layer
[ "$status" -eq 0 ]
[ "$output" == "$layer mounted" ]
# Unmount the first layer.
run storage --debug=false unmount $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
# Make sure layer is not mounted.
run storage --debug=false mounted $layer
[ "$status" -eq 0 ]
[ "$output" == "" ]


# Mount the layer twice and force umount.
run storage --debug=false mount $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
run storage --debug=false mount $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
# Check if layer is mounted.
run storage --debug=false mounted $layer
[ "$status" -eq 0 ]
[ "$output" == "$layer mounted" ]
# Unmount all layers.
run storage --debug=false unmount --force $layer
[ "$status" -eq 0 ]
[ "$output" != "" ]
# Make sure no layers are mounted.
run storage --debug=false mounted $layer
[ "$status" -eq 0 ]
[ "$output" == "" ]

# Delete the first layer
run storage delete-layer $layer
[ "$status" -eq 0 ]
}

0 comments on commit 1075a73

Please sign in to comment.