-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #295 from rhatdan/rootless
Move Storage features in pkg/util from libpod to containers/storage
- Loading branch information
Showing
4 changed files
with
263 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
package storage | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"os/user" | ||
"path/filepath" | ||
"strings" | ||
"syscall" | ||
|
||
"github.com/BurntSushi/toml" | ||
"github.com/containers/storage/pkg/idtools" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping | ||
func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*IDMappingOptions, error) { | ||
options := IDMappingOptions{ | ||
HostUIDMapping: true, | ||
HostGIDMapping: true, | ||
} | ||
if subGIDMap == "" && subUIDMap != "" { | ||
subGIDMap = subUIDMap | ||
} | ||
if subUIDMap == "" && subGIDMap != "" { | ||
subUIDMap = subGIDMap | ||
} | ||
if len(GIDMapSlice) == 0 && len(UIDMapSlice) != 0 { | ||
GIDMapSlice = UIDMapSlice | ||
} | ||
if len(UIDMapSlice) == 0 && len(GIDMapSlice) != 0 { | ||
UIDMapSlice = GIDMapSlice | ||
} | ||
if len(UIDMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 { | ||
UIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())} | ||
} | ||
if len(GIDMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 { | ||
GIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())} | ||
} | ||
|
||
if subUIDMap != "" && subGIDMap != "" { | ||
mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to create NewIDMappings for uidmap=%s gidmap=%s", subUIDMap, subGIDMap) | ||
} | ||
options.UIDMap = mappings.UIDs() | ||
options.GIDMap = mappings.GIDs() | ||
} | ||
parsedUIDMap, err := idtools.ParseIDMap(UIDMapSlice, "UID") | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to create ParseUIDMap UID=%s", UIDMapSlice) | ||
} | ||
parsedGIDMap, err := idtools.ParseIDMap(GIDMapSlice, "GID") | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to create ParseGIDMap GID=%s", UIDMapSlice) | ||
} | ||
options.UIDMap = append(options.UIDMap, parsedUIDMap...) | ||
options.GIDMap = append(options.GIDMap, parsedGIDMap...) | ||
if len(options.UIDMap) > 0 { | ||
options.HostUIDMapping = false | ||
} | ||
if len(options.GIDMap) > 0 { | ||
options.HostGIDMapping = false | ||
} | ||
return &options, nil | ||
} | ||
|
||
// GetRootlessRuntimeDir returns the runtime directory when running as non root | ||
func GetRootlessRuntimeDir(rootlessUid int) (string, error) { | ||
runtimeDir := os.Getenv("XDG_RUNTIME_DIR") | ||
if runtimeDir == "" { | ||
tmpDir := fmt.Sprintf("/run/user/%d", rootlessUid) | ||
st, err := os.Stat(tmpDir) | ||
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Getuid() && st.Mode().Perm() == 0700 { | ||
return tmpDir, nil | ||
} | ||
} | ||
tmpDir := fmt.Sprintf("%s/%d", os.TempDir(), rootlessUid) | ||
if err := os.MkdirAll(tmpDir, 0700); err != nil { | ||
logrus.Errorf("failed to create %s: %v", tmpDir, err) | ||
} else { | ||
st, err := os.Stat(tmpDir) | ||
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Getuid() && st.Mode().Perm() == 0700 { | ||
return tmpDir, nil | ||
} | ||
} | ||
home, err := homeDir() | ||
if err != nil { | ||
return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty") | ||
} | ||
resolvedHome, err := filepath.EvalSymlinks(home) | ||
if err != nil { | ||
return "", errors.Wrapf(err, "cannot resolve %s", home) | ||
} | ||
return filepath.Join(resolvedHome, "rundir"), nil | ||
} | ||
|
||
// getRootlessDirInfo returns the parent path of where the storage for containers and | ||
// volumes will be in rootless mode | ||
func getRootlessDirInfo(rootlessUid int) (string, string, error) { | ||
rootlessRuntime, err := GetRootlessRuntimeDir(rootlessUid) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
|
||
dataDir := os.Getenv("XDG_DATA_HOME") | ||
if dataDir == "" { | ||
home, err := homeDir() | ||
if err != nil { | ||
return "", "", errors.Wrapf(err, "neither XDG_DATA_HOME nor HOME was set non-empty") | ||
} | ||
// runc doesn't like symlinks in the rootfs path, and at least | ||
// on CoreOS /home is a symlink to /var/home, so resolve any symlink. | ||
resolvedHome, err := filepath.EvalSymlinks(home) | ||
if err != nil { | ||
return "", "", errors.Wrapf(err, "cannot resolve %s", home) | ||
} | ||
dataDir = filepath.Join(resolvedHome, ".local", "share") | ||
} | ||
return dataDir, rootlessRuntime, nil | ||
} | ||
|
||
// getRootlessStorageOpts returns the storage opts for containers running as non root | ||
func getRootlessStorageOpts(rootlessUid int) (StoreOptions, error) { | ||
var opts StoreOptions | ||
|
||
dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUid) | ||
if err != nil { | ||
return opts, err | ||
} | ||
opts.RunRoot = rootlessRuntime | ||
opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") | ||
if path, err := exec.LookPath("fuse-overlayfs"); err == nil { | ||
opts.GraphDriverName = "overlay" | ||
opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)} | ||
} else { | ||
opts.GraphDriverName = "vfs" | ||
} | ||
return opts, nil | ||
} | ||
|
||
type tomlOptionsConfig struct { | ||
MountProgram string `toml:"mount_program"` | ||
} | ||
|
||
func getTomlStorage(storeOptions *StoreOptions) *tomlConfig { | ||
config := new(tomlConfig) | ||
|
||
config.Storage.Driver = storeOptions.GraphDriverName | ||
config.Storage.RunRoot = storeOptions.RunRoot | ||
config.Storage.GraphRoot = storeOptions.GraphRoot | ||
for _, i := range storeOptions.GraphDriverOptions { | ||
s := strings.Split(i, "=") | ||
if s[0] == "overlay.mount_program" { | ||
config.Storage.Options.MountProgram = s[1] | ||
} | ||
} | ||
|
||
return config | ||
} | ||
|
||
// DefaultStoreOptions returns the default storage ops for containers | ||
func DefaultStoreOptions(rootless bool, rootlessUid int) (StoreOptions, error) { | ||
var ( | ||
defaultRootlessRunRoot string | ||
defaultRootlessGraphRoot string | ||
err error | ||
) | ||
storageOpts := defaultStoreOptions | ||
if rootless { | ||
storageOpts, err = getRootlessStorageOpts(rootlessUid) | ||
if err != nil { | ||
return storageOpts, err | ||
} | ||
} | ||
|
||
storageConf, err := DefaultConfigFile(rootless) | ||
if err != nil { | ||
return storageOpts, err | ||
} | ||
if _, err = os.Stat(storageConf); err == nil { | ||
defaultRootlessRunRoot = storageOpts.RunRoot | ||
defaultRootlessGraphRoot = storageOpts.GraphRoot | ||
storageOpts = StoreOptions{} | ||
ReloadConfigurationFile(storageConf, &storageOpts) | ||
} | ||
|
||
if !os.IsNotExist(err) { | ||
return storageOpts, errors.Wrapf(err, "cannot stat %s", storageConf) | ||
} | ||
|
||
if rootless { | ||
if err == nil { | ||
// If the file did not specify a graphroot or runroot, | ||
// set sane defaults so we don't try and use root-owned | ||
// directories | ||
if storageOpts.RunRoot == "" { | ||
storageOpts.RunRoot = defaultRootlessRunRoot | ||
} | ||
if storageOpts.GraphRoot == "" { | ||
storageOpts.GraphRoot = defaultRootlessGraphRoot | ||
} | ||
} else { | ||
if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil { | ||
return storageOpts, errors.Wrapf(err, "cannot make directory %s", filepath.Dir(storageConf)) | ||
} | ||
file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) | ||
if err != nil { | ||
return storageOpts, errors.Wrapf(err, "cannot open %s", storageConf) | ||
} | ||
|
||
tomlConfiguration := getTomlStorage(&storageOpts) | ||
defer file.Close() | ||
enc := toml.NewEncoder(file) | ||
if err := enc.Encode(tomlConfiguration); err != nil { | ||
os.Remove(storageConf) | ||
|
||
return storageOpts, errors.Wrapf(err, "failed to encode %s", storageConf) | ||
} | ||
} | ||
} | ||
return storageOpts, nil | ||
} | ||
|
||
func homeDir() (string, error) { | ||
home := os.Getenv("HOME") | ||
if home == "" { | ||
usr, err := user.Current() | ||
if err != nil { | ||
return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty") | ||
} | ||
home = usr.HomeDir | ||
} | ||
return home, nil | ||
} |