Merge branch 'ctk-hook-chmod-improve-eperm-handling' into 'main'

nvidia-ctk hook chmod: Improve permission error handling

See merge request nvidia/container-toolkit/container-toolkit!496
This commit is contained in:
Evan Lezar 2023-11-21 11:05:03 +00:00
commit adc516fd59

View File

@ -17,14 +17,15 @@
package chmod package chmod
import ( import (
"errors"
"fmt" "fmt"
"io/fs"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"syscall"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger" "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci" "github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -35,7 +36,8 @@ type command struct {
type config struct { type config struct {
paths cli.StringSlice paths cli.StringSlice
mode string modeStr string
mode fs.FileMode
containerSpec string containerSpec string
} }
@ -72,7 +74,7 @@ func (m command) build() *cli.Command {
&cli.StringFlag{ &cli.StringFlag{
Name: "mode", Name: "mode",
Usage: "Specify the file mode", Usage: "Specify the file mode",
Destination: &cfg.mode, Destination: &cfg.modeStr,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "container-spec", Name: "container-spec",
@ -85,10 +87,16 @@ func (m command) build() *cli.Command {
} }
func validateFlags(c *cli.Context, cfg *config) error { func validateFlags(c *cli.Context, cfg *config) error {
if strings.TrimSpace(cfg.mode) == "" { if strings.TrimSpace(cfg.modeStr) == "" {
return fmt.Errorf("a non-empty mode must be specified") return fmt.Errorf("a non-empty mode must be specified")
} }
modeInt, err := strconv.ParseUint(cfg.modeStr, 8, 32)
if err != nil {
return fmt.Errorf("failed to parse mode as octal: %v", err)
}
cfg.mode = fs.FileMode(modeInt)
for _, p := range cfg.paths.Value() { for _, p := range cfg.paths.Value() {
if strings.TrimSpace(p) == "" { if strings.TrimSpace(p) == "" {
return fmt.Errorf("paths must not be empty") return fmt.Errorf("paths must not be empty")
@ -112,34 +120,38 @@ func (m command) run(c *cli.Context, cfg *config) error {
return fmt.Errorf("empty container root detected") return fmt.Errorf("empty container root detected")
} }
paths := m.getPaths(containerRoot, cfg.paths.Value()) paths := m.getPaths(containerRoot, cfg.paths.Value(), cfg.mode)
if len(paths) == 0 { if len(paths) == 0 {
m.logger.Debugf("No paths specified; exiting") m.logger.Debugf("No paths specified; exiting")
return nil return nil
} }
locator := lookup.NewExecutableLocator(m.logger, "") for _, path := range paths {
targets, err := locator.Locate("chmod") err = os.Chmod(path, cfg.mode)
if err != nil { // in some cases this is not an issue (e.g. whole /dev mounted), see #143
return fmt.Errorf("failed to locate chmod: %v", err) if errors.Is(err, fs.ErrPermission) {
m.logger.Debugf("Ignoring permission error with chmod: %v", err)
err = nil
}
} }
chmodPath := targets[0]
args := append([]string{filepath.Base(chmodPath), cfg.mode}, paths...) return err
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
return syscall.Exec(chmodPath, args, nil)
} }
// getPaths updates the specified paths relative to the root. // getPaths updates the specified paths relative to the root.
func (m command) getPaths(root string, paths []string) []string { func (m command) getPaths(root string, paths []string, desiredMode fs.FileMode) []string {
var pathsInRoot []string var pathsInRoot []string
for _, f := range paths { for _, f := range paths {
path := filepath.Join(root, f) path := filepath.Join(root, f)
if _, err := os.Stat(path); err != nil { stat, err := os.Stat(path)
if err != nil {
m.logger.Debugf("Skipping path %q: %v", path, err) m.logger.Debugf("Skipping path %q: %v", path, err)
continue continue
} }
if (stat.Mode()&(fs.ModePerm|fs.ModeSetuid|fs.ModeSetgid|fs.ModeSticky))^desiredMode == 0 {
m.logger.Debugf("Skipping path %q: already desired mode", path)
continue
}
pathsInRoot = append(pathsInRoot, path) pathsInRoot = append(pathsInRoot, path)
} }