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 ability to set the initial owner of a custom volume #1415

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 10 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2646,3 +2646,13 @@ As part of this, the following configuration options have been added:
## `custom_volume_refresh_exclude_older_snapshots`

This adds support for excluding source snapshots earlier than latest target snapshot.

## `storage_initial_owner`

This adds ability to set the initial owner of a custom volume.

The following configuration options have been added:

* `initial.gid`
* `initial.mode`
* `initial.uid`
3 changes: 3 additions & 0 deletions doc/reference/storage_btrfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ Key | Type | Default | Descr

Key | Type | Condition | Default | Description
:-- | :--- | :-------- | :------ | :----------
`initial.gid` | int | custom volume with content type `filesystem` | same as `volume.initial.uid` or `0` | GID of the volume owner in the instance
`initial.mode` | int | custom volume with content type `filesystem` | same as `volume.initial.mode` or `711` | Mode of the volume in the instance
`initial.uid` | int | custom volume with content type `filesystem` | same as `volume.initial.gid` or `0` | UID of the volume owner in the instance
`security.shared` | bool | custom block volume | same as `volume.security.shared` or `false` | Enable sharing the volume across multiple instances
`security.shifted` | bool | custom volume | same as `volume.security.shifted` or `false` | {{enable_ID_shifting}}
`security.unmapped` | bool | custom volume | same as `volume.security.unmapped` or `false` | Disable ID mapping for the volume
Expand Down
3 changes: 3 additions & 0 deletions doc/reference/storage_ceph.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ Key | Type | Condition | Default
:-- | :--- | :-------- | :------ | :----------
`block.filesystem` | string | block-based volume with content type `filesystem` | same as `volume.block.filesystem` | {{block_filesystem}}
`block.mount_options` | string | block-based volume with content type `filesystem` | same as `volume.block.mount_options` | Mount options for block-backed file system volumes
`initial.gid` | int | custom volume with content type `filesystem` | same as `volume.initial.uid` or `0` | GID of the volume owner in the instance
`initial.mode` | int | custom volume with content type `filesystem` | same as `volume.initial.mode` or `711` | Mode of the volume in the instance
`initial.uid` | int | custom volume with content type `filesystem` | same as `volume.initial.gid` or `0` | UID of the volume owner in the instance
`security.shared` | bool | custom block volume | same as `volume.security.shared` or `false` | Enable sharing the volume across multiple instances
`security.shifted` | bool | custom volume | same as `volume.security.shifted` or `false` | {{enable_ID_shifting}}
`security.unmapped` | bool | custom volume | same as `volume.security.unmapped` or `false` | Disable ID mapping for the volume
Expand Down
3 changes: 3 additions & 0 deletions doc/reference/storage_cephfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ Key | Type | Default

Key | Type | Condition | Default | Description
:-- | :--- | :-------- | :------ | :----------
`initial.gid` | int | custom volume with content type `filesystem` | same as `volume.initial.uid` or `0` | GID of the volume owner in the instance
`initial.mode` | int | custom volume with content type `filesystem` | same as `volume.initial.mode` or `711` | Mode of the volume in the instance
`initial.uid` | int | custom volume with content type `filesystem` | same as `volume.initial.gid` or `0` | UID of the volume owner in the instance
`security.shared` | bool | custom block volume | same as `volume.security.shared` or `false` | Enable sharing the volume across multiple instances
`security.shifted` | bool | custom volume | same as `volume.security.shifted` or `false` | {{enable_ID_shifting}}
`security.unmapped` | bool | custom volume | same as `volume.security.unmapped` or `false` | Disable ID mapping for the volume
Expand Down
3 changes: 3 additions & 0 deletions doc/reference/storage_dir.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ Key | Type | Default

Key | Type | Condition | Default | Description
:-- | :--- | :-------- | :------ | :----------
`initial.gid` | int | custom volume with content type `filesystem` | same as `volume.initial.uid` or `0` | GID of the volume owner in the instance
`initial.mode` | int | custom volume with content type `filesystem` | same as `volume.initial.mode` or `711` | Mode of the volume in the instance
`initial.uid` | int | custom volume with content type `filesystem` | same as `volume.initial.gid` or `0` | UID of the volume owner in the instance
`security.shared` | bool | custom block volume | same as `volume.security.shared` or `false` | Enable sharing the volume across multiple instances
`security.shifted` | bool | custom volume | same as `volume.security.shifted` or `false` | {{enable_ID_shifting}}
`security.unmapped` | bool | custom volume | same as `volume.security.unmapped` or `false` | Disable ID mapping for the volume
Expand Down
3 changes: 3 additions & 0 deletions doc/reference/storage_lvm.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ Key | Type | Condition
:-- | :--- | :------ | :------ | :----------
`block.filesystem` | string | block-based volume with content type `filesystem` | same as `volume.block.filesystem` | {{block_filesystem}}
`block.mount_options` | string | block-based volume with content type `filesystem` | same as `volume.block.mount_options` | Mount options for block-backed file system volumes
`initial.gid` | int | custom volume with content type `filesystem` | same as `volume.initial.uid` or `0` | GID of the volume owner in the instance
`initial.mode` | int | custom volume with content type `filesystem` | same as `volume.initial.mode` or `711` | Mode of the volume in the instance
`initial.uid` | int | custom volume with content type `filesystem` | same as `volume.initial.gid` or `0` | UID of the volume owner in the instance
`lvm.stripes` | string | | same as `volume.lvm.stripes` | Number of stripes to use for new volumes (or thin pool volume)
`lvm.stripes.size` | string | | same as `volume.lvm.stripes.size` | Size of stripes to use (at least 4096 bytes and multiple of 512 bytes)
`security.shifted` | bool | custom volume | same as `volume.security.shifted` or `false` | {{enable_ID_shifting}}
Expand Down
3 changes: 3 additions & 0 deletions doc/reference/storage_zfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ Key | Type | Condition | Default
:-- | :--- | :-------- | :------ | :----------
`block.filesystem` | string | block-based volume with content type `filesystem` (`zfs.block_mode` enabled) | same as `volume.block.filesystem` | {{block_filesystem}}
`block.mount_options` | string | block-based volume with content type `filesystem` (`zfs.block_mode` enabled) | same as `volume.block.mount_options` | Mount options for block-backed file system volumes
`initial.gid` | int | custom volume with content type `filesystem` | same as `volume.initial.uid` or `0` | GID of the volume owner in the instance
`initial.mode` | int | custom volume with content type `filesystem` | same as `volume.initial.mode` or `711` | Mode of the volume in the instance
`initial.uid` | int | custom volume with content type `filesystem` | same as `volume.initial.gid` or `0` | UID of the volume owner in the instance
`security.shared` | bool | custom block volume | same as `volume.security.shared` or `false` | Enable sharing the volume across multiple instances
`security.shifted` | bool | custom volume | same as `volume.security.shifted` or `false` | {{enable_ID_shifting}}
`security.unmapped` | bool | custom volume | same as `volume.security.unmapped` or `false` | Disable ID mapping for the volume
Expand Down
41 changes: 40 additions & 1 deletion internal/server/storage/drivers/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"slices"
"strconv"
"strings"

internalInstance "github.com/lxc/incus/v6/internal/instance"
Expand Down Expand Up @@ -233,8 +234,46 @@ func (v Volume) EnsureMountPath() error {
revert.Add(func() { _ = os.Remove(volPath) })
}

// Set very restrictive mode 0100 for non-custom, non-bucket and non-image volumes.
mode := os.FileMode(0711)
if v.volType == VolumeTypeCustom && v.contentType == ContentTypeFS {
initialMode := v.ExpandedConfig("initial.mode")
if initialMode != "" {
m, err := strconv.ParseInt(initialMode, 8, 0)
if err != nil {
return err
}

mode = os.FileMode(m)
}

uid, gid := 0, 0
var err error
initialUID := v.ExpandedConfig("initial.uid")
if initialUID != "" {
uid, err = strconv.Atoi(initialUID)
if err != nil {
return err
}
}

initialGID := v.ExpandedConfig("initial.gid")
if initialGID != "" {
gid, err = strconv.Atoi(initialGID)
if err != nil {
return err
}
}

// Set the owner of a custom volume if uid or gid have been set.
if uid != 0 || gid != 0 {
err = os.Chown(volPath, uid, gid)
if err != nil {
return err
}
}
}

// Set very restrictive mode 0100 for non-custom, non-bucket and non-image volumes.
if v.volType != VolumeTypeCustom && v.volType != VolumeTypeImage && v.volType != VolumeTypeBucket {
mode = os.FileMode(0100)
}
Expand Down
5 changes: 4 additions & 1 deletion internal/server/storage/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,13 @@ func poolAndVolumeCommonRules(vol *drivers.Volume) map[string]func(string) error
"snapshots.pattern": validate.IsAny,
}

// security.shifted and security.unmapped are only relevant for custom filesystem volumes.
// Options relevant for custom filesystem volumes.
if (vol == nil) || (vol != nil && vol.Type() == drivers.VolumeTypeCustom && vol.ContentType() == drivers.ContentTypeFS) {
rules["security.shifted"] = validate.Optional(validate.IsBool)
rules["security.unmapped"] = validate.Optional(validate.IsBool)
rules["initial.uid"] = validate.Optional(validate.IsInt64)
rules["initial.gid"] = validate.Optional(validate.IsInt64)
rules["initial.mode"] = validate.Optional(validate.IsInt64)
}

// security.shared is only relevant for custom block volumes.
Expand Down
1 change: 1 addition & 0 deletions internal/version/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ var APIExtensions = []string{
"instances_scriptlet_get_instances_count",
"cluster_rebalance",
"custom_volume_refresh_exclude_older_snapshots",
"storage_initial_owner",
}

// APIExtensionsCount returns the number of available API extensions.
Expand Down
22 changes: 22 additions & 0 deletions test/suites/storage_volume_initial_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,28 @@ test_storage_volume_initial_config() {
incus delete c --force
fi

# Test initial owner of a custom volume configuration options.
incus storage volume create "${pool}" testvolume1
incus storage volume create "${pool}" testvolume2 initial.uid=101 initial.gid=101 initial.mode=0700

incus launch testimage c

incus storage volume attach "${pool}" testvolume1 c /testvolume1
incus storage volume attach "${pool}" testvolume2 c /testvolume2

[ "$(incus exec c -- stat -c %u:%g /testvolume1 )" = "0:0" ]
[ "$(incus exec c -- stat -c %a /testvolume1 )" = "711" ]
[ "$(incus exec c -- stat -c %u:%g /testvolume2 )" = "101:101" ]
[ "$(incus exec c -- stat -c %a /testvolume2 )" = "700" ]

incus storage volume detach "${pool}" testvolume1 c
incus storage volume detach "${pool}" testvolume2 c

incus delete c --force

incus storage volume delete "${pool}" testvolume1
incus storage volume delete "${pool}" testvolume2

# Cleanup
incus profile delete "${profile}"

Expand Down
Loading