Skip to content

Commit

Permalink
lxd/instance/drivers/lxc: detect if we need to remove a target direct…
Browse files Browse the repository at this point in the history
…ory when unmounting

When a disk device is removed while relying on a host directory and mapped to a target within a container,
we detect if the target directory has been created by LXD or not in order to not delete the content of a target
directory during the unmount.

Example:

- Let say we mounted a custom host empty directory (`test`) on the existing `/opt` directory of the container,
when unmounted (`lxc device remove ...`), the target `/opt` won't be removed, because we marked it as NOT being created
by LXD at mount time.

- If, however, we created a custom empty target directory at mount time:
`lxc config device add u1 test disk source=/home/user/test path=/new_dir`,
the directory `new_dir` will be created on the target instance and if we decide to unmount `test`,
the target `/new_dir` will be removed because is has been created by LXD

Signed-off-by: Gabriel Mougard <[email protected]>
  • Loading branch information
gabrielmougard committed Jan 8, 2024
1 parent 0b23fd2 commit d71a0fa
Showing 1 changed file with 47 additions and 9 deletions.
56 changes: 47 additions & 9 deletions lxd/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1713,10 +1713,29 @@ func (d *lxc) deviceHandleMounts(mounts []deviceConfig.MountEntryItem) error {
}
}

// If the mount is a directory, create the LXD marker file.
if shared.IsDir(mount.DevPath) {
// Connect to files API.
files, err := d.FileSFTP()
if err != nil {
return err
}

defer func() { _ = files.Close() }()

_, err = files.Lstat(mount.TargetPath)
if err != nil {
err = d.deviceVolatileSetFunc(mount.DevName)(map[string]string{"last_state.created_path": mount.TargetPath})
if err != nil {
return fmt.Errorf("Error updating volatile for the device: %w", err)
}
}
}

// Mount it into the container.
err := d.insertMount(mount.DevPath, mount.TargetPath, mount.FSType, flags, idmapType)
if err != nil {
return fmt.Errorf("Failed to add mount for device inside container: %s", err)
return fmt.Errorf("Failed to add mount for device inside container: %w", err)
}
} else {
relativeTargetPath := strings.TrimPrefix(mount.TargetPath, "/")
Expand All @@ -1731,17 +1750,36 @@ func (d *lxc) deviceHandleMounts(mounts []deviceConfig.MountEntryItem) error {

_, err = files.Lstat(relativeTargetPath)
if err == nil {
err := d.removeMount(mount.TargetPath)
if err != nil {
return fmt.Errorf("Error unmounting the device path inside container: %s", err)
removeTargetFiles := false

// Check if the target path hasn't been created by LXD
mountConf := d.deviceVolatileGetFunc(mount.DevName)()
for k, v := range mountConf {
if k == "last_state.created_path" && v == mount.TargetPath {
removeTargetFiles = true
break
}
}

err = files.Remove(relativeTargetPath)
err = d.removeMount(mount.TargetPath)
if err != nil {
// Only warn here and don't fail as removing a directory
// mount may fail if there was already files inside
// directory before it was mouted over preventing delete.
d.logger.Warn("Could not remove the device path inside container", logger.Ctx{"err": err})
return fmt.Errorf("Error unmounting the device path inside container: %w", err)
}

if removeTargetFiles {
err = files.Remove(relativeTargetPath)
if err != nil {
// Only warn here and don't fail as removing a directory
// mount may fail if there was already files inside
// directory before it was mouted over preventing delete.
d.logger.Warn("Could not remove the device path inside container", logger.Ctx{"err": err})
}
} else {
// Remove option from the mount.
err = d.deviceVolatileSetFunc(mount.DevName)(map[string]string{"last_state.created_path": ""})
if err != nil {
return fmt.Errorf("Error updating volatile for the device: %w", err)
}
}
}
}
Expand Down

0 comments on commit d71a0fa

Please sign in to comment.