From 3a4bdab90c24eeffdee9c4aaca3524b25b252b09 Mon Sep 17 00:00:00 2001 From: Kadin Sayani Date: Thu, 31 Oct 2024 05:09:00 -0600 Subject: [PATCH 1/6] lxd/device: Use device `Clone()` function rather than duplicate config copy logic Signed-off-by: Kadin Sayani --- lxd/device/device_utils_unix.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lxd/device/device_utils_unix.go b/lxd/device/device_utils_unix.go index f3b344463b49..1bde28dfd88b 100644 --- a/lxd/device/device_utils_unix.go +++ b/lxd/device/device_utils_unix.go @@ -326,10 +326,7 @@ func unixDeviceSetup(s *state.State, devicesPath string, typePrefix string, devi // device to ascertain these attributes. If defaultMode is true or mode is supplied in the device // config then the origin device does not need to be accessed for its file mode. func unixDeviceSetupCharNum(s *state.State, devicesPath string, typePrefix string, deviceName string, m deviceConfig.Device, major uint32, minor uint32, path string, defaultMode bool, runConf *deviceConfig.RunConfig) error { - configCopy := deviceConfig.Device{} - for k, v := range m { - configCopy[k] = v - } + configCopy := m.Clone() // Overridng these in the config copy should avoid the need for unixDeviceSetup to stat // the origin device to ascertain this information. @@ -347,10 +344,7 @@ func unixDeviceSetupCharNum(s *state.State, devicesPath string, typePrefix strin // device to ascertain these attributes. If defaultMode is true or mode is supplied in the device // config then the origin device does not need to be accessed for its file mode. func unixDeviceSetupBlockNum(s *state.State, devicesPath string, typePrefix string, deviceName string, m deviceConfig.Device, major uint32, minor uint32, path string, defaultMode bool, runConf *deviceConfig.RunConfig) error { - configCopy := deviceConfig.Device{} - for k, v := range m { - configCopy[k] = v - } + configCopy := m.Clone() // Overridng these in the config copy should avoid the need for unixDeviceSetup to stat // the origin device to ascertain this information. From 03614b916a2583bddcd4a0c239a29d12f5fc4b52 Mon Sep 17 00:00:00 2001 From: Kadin Sayani Date: Thu, 31 Oct 2024 05:22:47 -0600 Subject: [PATCH 2/6] lxd/device: Fix comment typos Signed-off-by: Kadin Sayani --- lxd/device/device_utils_unix.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/device/device_utils_unix.go b/lxd/device/device_utils_unix.go index 1bde28dfd88b..9a99df377cad 100644 --- a/lxd/device/device_utils_unix.go +++ b/lxd/device/device_utils_unix.go @@ -328,7 +328,7 @@ func unixDeviceSetup(s *state.State, devicesPath string, typePrefix string, devi func unixDeviceSetupCharNum(s *state.State, devicesPath string, typePrefix string, deviceName string, m deviceConfig.Device, major uint32, minor uint32, path string, defaultMode bool, runConf *deviceConfig.RunConfig) error { configCopy := m.Clone() - // Overridng these in the config copy should avoid the need for unixDeviceSetup to stat + // Overriding these in the config copy should avoid the need for unixDeviceSetup to stat // the origin device to ascertain this information. configCopy["type"] = "unix-char" configCopy["major"] = fmt.Sprintf("%d", major) @@ -346,7 +346,7 @@ func unixDeviceSetupCharNum(s *state.State, devicesPath string, typePrefix strin func unixDeviceSetupBlockNum(s *state.State, devicesPath string, typePrefix string, deviceName string, m deviceConfig.Device, major uint32, minor uint32, path string, defaultMode bool, runConf *deviceConfig.RunConfig) error { configCopy := m.Clone() - // Overridng these in the config copy should avoid the need for unixDeviceSetup to stat + // Overriding these in the config copy should avoid the need for unixDeviceSetup to stat // the origin device to ascertain this information. configCopy["type"] = "unix-block" configCopy["major"] = fmt.Sprintf("%d", major) From d90b89a2c2dd8765890d1b950cd072f0f21b42b7 Mon Sep 17 00:00:00 2001 From: Kadin Sayani Date: Tue, 12 Nov 2024 07:14:00 -0700 Subject: [PATCH 3/6] lxd/device: Add named return results to `unixDeviceAttributes` function Signed-off-by: Kadin Sayani --- lxd/device/device_utils_unix.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lxd/device/device_utils_unix.go b/lxd/device/device_utils_unix.go index 9a99df377cad..6bb66cf9a6b5 100644 --- a/lxd/device/device_utils_unix.go +++ b/lxd/device/device_utils_unix.go @@ -20,17 +20,16 @@ import ( // unixDefaultMode default mode to create unix devices with if not specified in device config. const unixDefaultMode = 0660 -// unixDeviceAttributes returns the decice type, major and minor numbers for a device. -func unixDeviceAttributes(path string) (string, uint32, uint32, error) { +// unixDeviceAttributes returns the device type, major and minor numbers for a device. +func unixDeviceAttributes(path string) (dType string, major uint32, minor uint32, err error) { // Get a stat struct from the provided path stat := unix.Stat_t{} - err := unix.Stat(path, &stat) + err = unix.Stat(path, &stat) if err != nil { return "", 0, 0, err } // Check what kind of file it is - dType := "" if stat.Mode&unix.S_IFMT == unix.S_IFBLK { dType = "b" } else if stat.Mode&unix.S_IFMT == unix.S_IFCHR { @@ -40,8 +39,9 @@ func unixDeviceAttributes(path string) (string, uint32, uint32, error) { } // Return the device information - major := unix.Major(uint64(stat.Rdev)) - minor := unix.Minor(uint64(stat.Rdev)) + major = unix.Major(uint64(stat.Rdev)) + minor = unix.Minor(uint64(stat.Rdev)) + return dType, major, minor, nil } From 3eaf25aee6a4963f2d6b7315247377d6f8f29dd1 Mon Sep 17 00:00:00 2001 From: Kadin Sayani Date: Thu, 14 Nov 2024 08:45:14 -0700 Subject: [PATCH 4/6] api: Add `unix_hotplug_ownership_inherit` extension Signed-off-by: Kadin Sayani --- doc/api-extensions.md | 4 ++++ shared/version/api.go | 1 + 2 files changed, 5 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 9ec7dcfe6a63..149cb1ccb659 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -2520,3 +2520,7 @@ contains the total available logical CPUs available when LXD started. ## `vm_limits_cpu_pin_strategy` Adds a new {config:option}`instance-resource-limits:limits.cpu.pin_strategy` configuration option for virtual machines. This option controls the CPU pinning strategy. When set to `none`, CPU auto pinning is disabled. When set to `auto`, CPU auto pinning is enabled. + +## `unix_hotplug_ownership_inherit` + +Adds a new {config:option}`device-unix-hotplug-device-conf:ownership.inherit` configuration option for `unix-hotplug` devices. This option controls whether the device inherits ownership (GID and UID) from the host. When set to `true`, host ownership is inherited. When set to `false`, host ownership is not inherited and ownership can be configured by setting {config:option}`device-unix-hotplug-device-conf:gid` and {config:option}`device-unix-hotplug-device-conf:uid`. diff --git a/shared/version/api.go b/shared/version/api.go index 84a109007f67..196693107f01 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -423,6 +423,7 @@ var APIExtensions = []string{ "network_ovn_uplink_vlan", "state_logical_cpus", "vm_limits_cpu_pin_strategy", + "unix_hotplug_ownership_inherit", } // APIExtensionsCount returns the number of available API extensions. From f81ac845265a085fc105b8b7cda359b2a43ee3eb Mon Sep 17 00:00:00 2001 From: Kadin Sayani Date: Wed, 13 Nov 2024 09:55:26 -0700 Subject: [PATCH 5/6] lxd/device: Add `ownership.inherit` setting for `unix-hotplug` devices Signed-off-by: Kadin Sayani --- lxd/device/device_utils_unix.go | 44 +++++++++++++++++++++++++-------- lxd/device/unix_hotplug.go | 12 +++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/lxd/device/device_utils_unix.go b/lxd/device/device_utils_unix.go index 6bb66cf9a6b5..aee1a41d3bcf 100644 --- a/lxd/device/device_utils_unix.go +++ b/lxd/device/device_utils_unix.go @@ -45,6 +45,20 @@ func unixDeviceAttributes(path string) (dType string, major uint32, minor uint32 return dType, major, minor, nil } +// unixDeviceOwnership returns the ownership (gid and uid) for a device. +func unixDeviceOwnership(path string) (gid uint32, uid uint32, err error) { + stat := unix.Stat_t{} + err = unix.Stat(path, &stat) + if err != nil { + return 0, 0, err + } + + gid = stat.Gid + uid = stat.Uid + + return gid, uid, nil +} + // unixDeviceModeOct converts a string unix octal mode to an int. func unixDeviceModeOct(strmode string) (int, error) { i, err := strconv.ParseInt(strmode, 8, 32) @@ -97,7 +111,7 @@ func unixDeviceDestPath(m deviceConfig.Device) string { // defaultMode is set as true, then the device is created with the supplied or default mode (0660) // respectively, otherwise the origin device's mode is used. If the device config doesn't contain a // type field then it defaults to created a unix-char device. The ownership of the created device -// defaults to root (0) but can be specified with the uid and gid fields in the device config map. +// defaults to root (0) but can be specified with the uid and gid fields in the device config map. If ownership.inherit is set to true, the device ownership is inherited from the host. // It returns a UnixDevice containing information about the device created. func UnixDeviceCreate(s *state.State, idmapSet *idmap.IdmapSet, devicesPath string, prefix string, m deviceConfig.Device, defaultMode bool) (*UnixDevice, error) { var err error @@ -170,18 +184,28 @@ func UnixDeviceCreate(s *state.State, idmapSet *idmap.IdmapSet, devicesPath stri d.Type = "c" } - // Get the device owner. - if m["uid"] != "" { - d.UID, err = strconv.Atoi(m["uid"]) + if shared.IsTrue(m["ownership.inherit"]) { + gid, uid, err := unixDeviceOwnership(srcPath) if err != nil { - return nil, fmt.Errorf("Invalid uid %s in device %s", m["uid"], srcPath) + return nil, fmt.Errorf("Failed to retrieve host ownership of device %s: %w", srcPath, err) } - } - if m["gid"] != "" { - d.GID, err = strconv.Atoi(m["gid"]) - if err != nil { - return nil, fmt.Errorf("Invalid gid %s in device %s", m["gid"], srcPath) + d.GID = int(gid) + d.UID = int(uid) + } else { + // Get the device owner. + if m["uid"] != "" { + d.UID, err = strconv.Atoi(m["uid"]) + if err != nil { + return nil, fmt.Errorf("Invalid uid %s in device %s", m["uid"], srcPath) + } + } + + if m["gid"] != "" { + d.GID, err = strconv.Atoi(m["gid"]) + if err != nil { + return nil, fmt.Errorf("Invalid gid %s in device %s", m["gid"], srcPath) + } } } diff --git a/lxd/device/unix_hotplug.go b/lxd/device/unix_hotplug.go index fdd259879028..500141c6b0c5 100644 --- a/lxd/device/unix_hotplug.go +++ b/lxd/device/unix_hotplug.go @@ -73,6 +73,14 @@ func (d *unixHotplug) validateConfig(instConf instance.ConfigReader) error { "gid": unixValidUserID, "mode": unixValidOctalFileMode, "required": validate.Optional(validate.IsBool), + + // lxdmeta:generate(entities=device-unix-hotplug; group=device-conf; key=ownership.inherit) + // + // --- + // type: bool + // defaultdesc: `false` + // shortdesc: Whether this device inherits ownership (GID and UID) from the host + "ownership.inherit": validate.Optional(validate.IsBool), } err := d.config.Validate(rules) @@ -84,6 +92,10 @@ func (d *unixHotplug) validateConfig(instConf instance.ConfigReader) error { return fmt.Errorf("Unix hotplug devices require a vendorid or a productid") } + if d.config["gid"] != "" || d.config["uid"] != "" && shared.IsTrue(d.config["ownership.inherit"]) { + return fmt.Errorf("Unix hotplug device ownership cannot be inherited from host while GID or UID is set") + } + return nil } From 2f191aae503017922932cdb8ecea866d2af5e4cc Mon Sep 17 00:00:00 2001 From: Kadin Sayani Date: Wed, 13 Nov 2024 10:18:56 -0700 Subject: [PATCH 6/6] metadata: Run `update-metadata` Signed-off-by: Kadin Sayani --- doc/metadata.txt | 7 +++++++ lxd/metadata/configuration.json | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/doc/metadata.txt b/doc/metadata.txt index 8e753e76e403..bb9f6cba66a5 100644 --- a/doc/metadata.txt +++ b/doc/metadata.txt @@ -1545,6 +1545,13 @@ See {ref}`devices-unix-char-hotplugging` for more information. ``` +```{config:option} ownership.inherit device-unix-hotplug-device-conf +:defaultdesc: "`false`" +:shortdesc: "Whether this device inherits ownership (GID and UID) from the host" +:type: "bool" + +``` + ```{config:option} productid device-unix-hotplug-device-conf :shortdesc: "Product ID of the Unix device" :type: "string" diff --git a/lxd/metadata/configuration.json b/lxd/metadata/configuration.json index 8b2d6ebdecd8..a1324420db0b 100644 --- a/lxd/metadata/configuration.json +++ b/lxd/metadata/configuration.json @@ -1783,6 +1783,14 @@ "type": "integer" } }, + { + "ownership.inherit": { + "defaultdesc": "`false`", + "longdesc": "", + "shortdesc": "Whether this device inherits ownership (GID and UID) from the host", + "type": "bool" + } + }, { "productid": { "longdesc": "",