diff --git a/AUTHORS b/AUTHORS index 11cd83d14..129dd3969 100644 --- a/AUTHORS +++ b/AUTHORS @@ -703,6 +703,7 @@ Joost Cassee Jordan Jordan Arentsen Jordan Sissel +Jordan Williams Jose Diaz-Gonzalez Joseph Anthony Pasquale Holsten Joseph Hager diff --git a/docs/containers-storage.conf.5.md b/docs/containers-storage.conf.5.md index ba6fcaae1..7f9e00348 100644 --- a/docs/containers-storage.conf.5.md +++ b/docs/containers-storage.conf.5.md @@ -29,7 +29,10 @@ The `storage` table supports the following options: **driver**="" container storage driver Default Copy On Write (COW) container storage driver. Valid drivers are "overlay", "vfs", "devmapper", "aufs", "btrfs", and "zfs". Some drivers (for example, "zfs", "btrfs", and "aufs") may not work if your kernel lacks support for the filesystem. - This field is requiered to guarantee proper operation. + This field is required to guarantee proper operation. + Valid rootless drivers are "btrfs", "overlay", and "vfs". + Rootless users default to the driver defined in the system configuration when possible. + When the system configuration uses an unsupported rootless driver, rootless users default to "overlay" if available, otherwise "vfs". **graphroot**="" container storage graph dir (default: "/var/lib/containers/storage") diff --git a/types/options.go b/types/options.go index 8cd89c1ab..38794120f 100644 --- a/types/options.go +++ b/types/options.go @@ -149,6 +149,17 @@ type StoreOptions struct { AutoNsMaxSize uint32 `json:"auto_userns_max_size,omitempty"` } +// isRootlessDriver returns true if the given storage driver is valid for containers running as non root +func isRootlessDriver(driver string) bool { + validDrivers := map[string]bool{ + "btrfs": true, + "overlay": true, + "overlay2": true, + "vfs": true, + } + return validDrivers[driver] +} + // getRootlessStorageOpts returns the storage opts for containers running as non root func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOptions, error) { var opts StoreOptions @@ -163,7 +174,13 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti } else { opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") } - opts.GraphDriverName = os.Getenv("STORAGE_DRIVER") + + if driver := systemOpts.GraphDriverName; isRootlessDriver(driver) { + opts.GraphDriverName = driver + } + if driver := os.Getenv("STORAGE_DRIVER"); driver != "" { + opts.GraphDriverName = driver + } if opts.GraphDriverName == "" || opts.GraphDriverName == "overlay" { if path, err := exec.LookPath("fuse-overlayfs"); err == nil { opts.GraphDriverName = "overlay" diff --git a/types/options_test.go b/types/options_test.go new file mode 100644 index 000000000..2ee285671 --- /dev/null +++ b/types/options_test.go @@ -0,0 +1,99 @@ +package types + +import ( + "fmt" + "os" + "testing" + + "gotest.tools/assert" +) + +func TestGetRootlessStorageOpts(t *testing.T) { + envDriver, envDriverSet := os.LookupEnv("STORAGE_DRIVER") + os.Unsetenv("STORAGE_DRIVER") + + const vfsDriver = "vfs" + const overlayDriver = "overlay" + + t.Run("systemDriver=btrfs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "btrfs" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "btrfs") + }) + + t.Run("systemDriver=overlay", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = overlayDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, overlayDriver) + }) + + t.Run("systemDriver=overlay2", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "overlay2" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "overlay2") + }) + + t.Run("systemDriver=vfs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = vfsDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, vfsDriver) + }) + + t.Run("systemDriver=aufs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "aufs" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Assert(t, storageOpts.GraphDriverName == overlayDriver || storageOpts.GraphDriverName == vfsDriver, fmt.Sprintf("The rootless driver should be set to 'overlay' or 'vfs' not '%v'", storageOpts.GraphDriverName)) + }) + + t.Run("systemDriver=devmapper", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "devmapper" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Assert(t, storageOpts.GraphDriverName == overlayDriver || storageOpts.GraphDriverName == vfsDriver, fmt.Sprintf("The rootless driver should be set to 'overlay' or 'vfs' not '%v'", storageOpts.GraphDriverName)) + }) + + t.Run("systemDriver=zfs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "zfs" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Assert(t, storageOpts.GraphDriverName == overlayDriver || storageOpts.GraphDriverName == vfsDriver, fmt.Sprintf("The rootless driver should be set to 'overlay' or 'vfs' not '%v'", storageOpts.GraphDriverName)) + }) + + t.Run("STORAGE_DRIVER=btrfs", func(t *testing.T) { + os.Setenv("STORAGE_DRIVER", "btrfs") + defer os.Unsetenv("STORAGE_DRIVER") + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = vfsDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "btrfs") + }) + + t.Run("STORAGE_DRIVER=zfs", func(t *testing.T) { + os.Setenv("STORAGE_DRIVER", "zfs") + defer os.Unsetenv("STORAGE_DRIVER") + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = vfsDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "zfs") + }) + + if envDriverSet { + os.Setenv("STORAGE_DRIVER", envDriver) + } else { + os.Unsetenv("STORAGE_DRIVER") + } +}