Skip to content

Commit 13ce410

Browse files
committed
A few niceties
1. If not privileged, don't try to do the mount, as the final bind or overlay mount will fail. 2. If something goes wrong, clean up the destdir/meta.
1 parent 1335860 commit 13ce410

File tree

3 files changed

+102
-16
lines changed

3 files changed

+102
-16
lines changed

README.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,10 @@ atomfs umount mnt
1313
Longer example:
1414
```
1515
serge@jerom ~$ lxc-usernsexec -s
16-
# unshare -m -- /bin/bash
17-
root@jerom ~$ cd delme/stacker-squash/
18-
root@jerom ~/delme/stacker-squash$ ~/src/atomfs/atomfs mount oci:smtest-squashfs dest
19-
/home/serge/delme/stacker-squash/dest/meta/mounts/5a27f94ae0691bd617c65cc99544994439acbda359c6375e103f4c099d7ab54c is not yet mounted...
20-
/home/serge/delme/stacker-squash/dest/meta/mounts/369694e2bd95a30c8c742dbde3b21ad91a84ed829b36b41a76985025157dfd52 is not yet mounted...
21-
root@jerom ~/delme/stacker-squash$ ls dest
22-
bin dev etc home hw proc root sys tmp usr var xxx
23-
root@jerom ~/delme/stacker-squash$ touch dest/yyy
24-
root@jerom ~/delme/stacker-squash$ ~/src/atomfs/atomfs umount dest
25-
root@jerom ~/delme/stacker-squash$ ls dest/meta/upper/
26-
xxx yyy
16+
root@jerom ~$ atomfs mount zothub:busybox-squashfs dest
17+
root@jerom ~$ ls dest
18+
bin dev etc home lib lib64 root tmp usr var
19+
root@jerom ~$ atomfs umount dest
2720
```
2821

2922
# Implementation details
@@ -34,3 +27,12 @@ onto $metadir/ro. Then if a readonly mount is requested
3427
$metadir/ro is bind mounted onto $metadir. Otherwise, we create
3528
$metadir/work and $metadir/upper, and use these to do a rw
3629
overlay mount of $metadir/ro onto $mountpoint.
30+
31+
Note that if you simply call `umount` on the mountpoint, then
32+
you will be left with all the individual squashfs mounts under
33+
`dest/mounts/*/`.
34+
35+
Note that you do need to be root in your namespace in order to
36+
do the final bind or overlay mount. (We could get around this
37+
by using fuse-overlay, but creating a namespace seems overall
38+
tidy).

mount.go

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package main
22

33
import (
44
"fmt"
5+
"os"
6+
"os/exec"
57
"path/filepath"
68
"strings"
79
"syscall"
810

911
"github.com/urfave/cli"
1012
"golang.org/x/sys/unix"
1113
satomfs "stackerbuild.io/stacker/pkg/atomfs"
14+
"stackerbuild.io/stacker/pkg/squashfs"
1215
)
1316

1417
var mountCmd = cli.Command{
@@ -42,20 +45,39 @@ func findImage(ctx *cli.Context) (string, string, error) {
4245
}
4346

4447
func doMount(ctx *cli.Context) error {
45-
if ctx.NArg() != 2 {
46-
return fmt.Errorf("source and destination required")
48+
if !amPrivileged() {
49+
fmt.Println("Please run as root, or in a user namespace")
50+
fmt.Println(" You could try:")
51+
fmt.Println("\tlxc-usernsexec -s -- /bin/bash")
52+
fmt.Println(" or")
53+
fmt.Println("\tunshare -Umr -- /bin/bash")
54+
fmt.Println("then run from that shell")
55+
os.Exit(1)
4756
}
48-
4957
ocidir, tag, err := findImage(ctx)
5058
if err != nil {
5159
return err
5260
}
5361

5462
target := ctx.Args()[1]
5563
metadir := filepath.Join(target, "meta")
64+
65+
complete := false
66+
67+
defer func() {
68+
if !complete {
69+
cleanupDest(metadir)
70+
}
71+
}()
72+
73+
if PathExists(metadir) {
74+
return fmt.Errorf("%q exists: cowardly refusing to mess with it", metadir)
75+
}
76+
5677
if err := EnsureDir(metadir); err != nil {
5778
return err
5879
}
80+
5981
rodest := filepath.Join(metadir, "ro")
6082
if err = EnsureDir(rodest); err != nil {
6183
return err
@@ -84,13 +106,71 @@ func doMount(ctx *cli.Context) error {
84106
err = overlay(target, rodest, metadir)
85107
}
86108

109+
complete = true
110+
return nil
111+
}
112+
113+
func cleanupDest(metadir string) {
114+
fmt.Printf("Failure detected: cleaning up %q", metadir)
115+
rodest := filepath.Join(metadir, "ro")
116+
if PathExists(rodest) {
117+
if err := unix.Unmount(rodest, 0); err != nil {
118+
fmt.Printf("Failed unmounting %q: %v", rodest, err)
119+
}
120+
}
121+
122+
mountsdir := filepath.Join(metadir, "mounts")
123+
entries, err := os.ReadDir(mountsdir)
87124
if err != nil {
88-
satomfs.Umount(rodest)
89-
return err
125+
fmt.Printf("Failed reading contents of %q: %v", mountsdir, err)
126+
os.RemoveAll(metadir)
127+
return
128+
}
129+
130+
wd, err := os.Getwd()
131+
if err != nil {
132+
fmt.Printf("Failed getting working directory")
133+
os.RemoveAll(metadir)
134+
}
135+
for _, e := range entries {
136+
n := filepath.Base(e.Name())
137+
if n == "workaround" {
138+
continue
139+
}
140+
if strings.HasSuffix(n, ".log") {
141+
continue
142+
}
143+
p := filepath.Join(wd, mountsdir, e.Name())
144+
if err := squashUmount(p); err != nil {
145+
fmt.Printf("Failed unmounting %q: %v\n", p, err)
146+
}
147+
}
148+
os.RemoveAll(metadir)
149+
}
150+
151+
func RunCommand(args ...string) error {
152+
cmd := exec.Command(args[0], args[1:]...)
153+
output, err := cmd.CombinedOutput()
154+
if err != nil {
155+
return fmt.Errorf("%s: %s: %s", strings.Join(args, " "), err, string(output))
90156
}
91157
return nil
92158
}
93159

160+
func amPrivileged() bool {
161+
if os.Geteuid() == 0 {
162+
return true
163+
}
164+
return false
165+
}
166+
167+
func squashUmount(p string) error {
168+
if amPrivileged() {
169+
return squashfs.Umount(p)
170+
}
171+
return RunCommand("fusermount", "-u", p)
172+
}
173+
94174
func overlay(target, rodest, metadir string) error {
95175
workdir := filepath.Join(metadir, "work")
96176
if err := EnsureDir(workdir); err != nil {

umount.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,9 @@ func doUmount(ctx *cli.Context) error {
8585
return fmt.Errorf("Encountered errors %d: %v", len(errs), errs)
8686
}
8787

88+
if err := os.RemoveAll(metadir); err != nil {
89+
return fmt.Errorf("Failed removing %q: %v", metadir, err)
90+
}
91+
8892
return nil
8993
}

0 commit comments

Comments
 (0)