Use bind filer for mounts

The bind filter supports bind-like mounts and volume mounts. It also
allows us to have read-only mounts.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
Gabriel Adrian Samfira
2022-10-12 00:03:11 +03:00
parent d591bb0421
commit 36dc2782c4
4 changed files with 493 additions and 73 deletions

View File

@@ -36,8 +36,16 @@ var (
// Mount to the provided target.
func (m *Mount) mount(target string) error {
readOnly := false
for _, option := range m.Options {
if option == "ro" {
readOnly = true
break
}
}
if m.Type == "bind" {
if err := m.bindMount(target); err != nil {
if err := ApplyFileBinding(target, m.Source, readOnly); err != nil {
return fmt.Errorf("failed to bind-mount to %s: %w", target, err)
}
return nil
@@ -81,12 +89,12 @@ func (m *Mount) mount(target string) error {
return fmt.Errorf("failed to get volume path for layer %s: %w", m.Source, err)
}
if err = setVolumeMountPoint(target, volume); err != nil {
if err = ApplyFileBinding(target, volume, readOnly); err != nil {
return fmt.Errorf("failed to set volume mount path for layer %s: %w", m.Source, err)
}
defer func() {
if err != nil {
deleteVolumeMountPoint(target)
RemoveFileBinding(target)
}
}()
@@ -120,50 +128,60 @@ func (m *Mount) GetParentPaths() ([]string, error) {
// Unmount the mount at the provided path
func Unmount(mount string, flags int) error {
var err error
mount = filepath.Clean(mount)
// Helpfully, both reparse points and symlinks look like links to Go
// Less-helpfully, ReadLink cannot return \\?\Volume{GUID} for a volume mount,
// and ends up returning the directory we gave it for some reason.
if mountTarget, err := os.Readlink(mount); err != nil {
// Not a mount point.
// This isn't an error, per the EINVAL handling in the Linux version
return nil
} else if mount != filepath.Clean(mountTarget) {
// Directory symlink
if err := bindUnmount(mount); err != nil {
return fmt.Errorf("failed to bind-unmount from %s: %w", mount, err)
}
return nil
}
layerPathb, err := os.ReadFile(mount + ":" + sourceStreamName)
// This should expand paths like ADMINI~1 and PROGRA~1 to long names.
mount, err = getFinalPath(mount)
if err != nil {
return fmt.Errorf("failed to retrieve source for layer %s: %w", mount, err)
return fmt.Errorf("fetching real path: %w", err)
}
layerPath := string(layerPathb)
if err := deleteVolumeMountPoint(mount); err != nil {
return fmt.Errorf("failed failed to release volume mount path for layer %s: %w", mount, err)
mounts, err := GetBindMappings(mount)
if err != nil {
return fmt.Errorf("fetching bind mappings: %w", err)
}
var (
home, layerID = filepath.Split(layerPath)
di = hcsshim.DriverInfo{
HomeDir: home,
found := false
for _, mnt := range mounts {
if mnt.MountPoint == mount {
found = true
break
}
)
if err := hcsshim.UnprepareLayer(di, layerID); err != nil {
return fmt.Errorf("failed to unprepare layer %s: %w", mount, err)
}
if !found {
// not a mount point
return nil
}
if err := hcsshim.DeactivateLayer(di, layerID); err != nil {
return fmt.Errorf("failed to deactivate layer %s: %w", mount, err)
adsFile := mount + ":" + sourceStreamName
var layerPath string
if _, err := os.Lstat(adsFile); err == nil {
layerPathb, err := os.ReadFile(mount + ":" + sourceStreamName)
if err != nil {
return fmt.Errorf("failed to retrieve source for layer %s: %w", mount, err)
}
layerPath = string(layerPathb)
}
if err := RemoveFileBinding(mount); err != nil {
return fmt.Errorf("removing mount: %w", err)
}
if layerPath != "" {
var (
home, layerID = filepath.Split(layerPath)
di = hcsshim.DriverInfo{
HomeDir: home,
}
)
if err := hcsshim.UnprepareLayer(di, layerID); err != nil {
return fmt.Errorf("failed to unprepare layer %s: %w", mount, err)
}
if err := hcsshim.DeactivateLayer(di, layerID); err != nil {
return fmt.Errorf("failed to deactivate layer %s: %w", mount, err)
}
}
return nil
}
@@ -183,21 +201,13 @@ func UnmountRecursive(mount string, flags int) error {
}
func (m *Mount) bindMount(target string) error {
readOnly := false
for _, option := range m.Options {
if option == "ro" {
return fmt.Errorf("read-only bind mount: %w", ErrNotImplementOnWindows)
readOnly = true
break
}
}
if err := os.Remove(target); err != nil {
return err
}
// TODO: We don't honour the Read-Only flag.
// It's possible that Windows simply lacks this.
return os.Symlink(m.Source, target)
}
func bindUnmount(target string) error {
return os.Remove(target)
return ApplyFileBinding(target, m.Source, readOnly)
}