Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add force to umount to force the umount of a container image #198

Merged
merged 1 commit into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")

This comment was marked as resolved.

},
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)
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 ]
}