[no-relnote] Move root to separate file

Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
Evan Lezar 2025-02-25 17:11:59 +02:00
parent 6b037a0dde
commit b598826ff2
No known key found for this signature in database
2 changed files with 86 additions and 37 deletions

View File

@ -0,0 +1,46 @@
/**
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
**/
package ldcache
import (
"os"
"path/filepath"
"github.com/moby/sys/symlink"
)
// A containerRoot represents the root filesystem of a container.
type containerRoot string
// hasPath checks whether the specified path exists in the root.
func (r containerRoot) hasPath(path string) bool {
resolved, err := r.resolve(path)
if err != nil {
return false
}
if _, err := os.Stat(resolved); err != nil && os.IsNotExist(err) {
return false
}
return true
}
// resolve returns the absolute path including root path.
// Symlinks are resolved, but are guaranteed to resolve in the root.
func (r containerRoot) resolve(path string) (string, error) {
absolute := filepath.Clean(filepath.Join(string(r), path))
return symlink.FollowSymlinkInScope(absolute, string(r))
}

View File

@ -31,6 +31,15 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci" "github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
) )
const (
// ldsoconfdFilenamePattern specifies the pattern for the filename
// in ld.so.conf.d that includes references to the specified directories.
// The 00-nvcr prefix is chosen to ensure that these libraries have a
// higher precedence than other libraries on the system, but lower than
// the 00-cuda-compat that is included in some containers.
ldsoconfdFilenamePattern = "00-nvcr-*.conf"
)
type command struct { type command struct {
logger logger.Interface logger logger.Interface
} }
@ -100,18 +109,20 @@ func (m command) run(c *cli.Context, cfg *options) error {
return fmt.Errorf("failed to load container state: %v", err) return fmt.Errorf("failed to load container state: %v", err)
} }
containerRoot, err := s.GetContainerRoot() containerRootDir, err := s.GetContainerRoot()
if err != nil { if err != nil {
return fmt.Errorf("failed to determined container root: %v", err) return fmt.Errorf("failed to determined container root: %v", err)
} }
ldconfigPath := m.resolveLDConfigPath(cfg.ldconfigPath) ldconfigPath := m.resolveLDConfigPath(cfg.ldconfigPath)
args := []string{filepath.Base(ldconfigPath)} args := []string{filepath.Base(ldconfigPath)}
if containerRoot != "" { if containerRootDir != "" {
args = append(args, "-r", containerRoot) args = append(args, "-r", containerRootDir)
} }
if root(containerRoot).hasPath("/etc/ld.so.cache") { containerRoot := containerRoot(containerRootDir)
if containerRoot.hasPath("/etc/ld.so.cache") {
args = append(args, "-C", "/etc/ld.so.cache") args = append(args, "-C", "/etc/ld.so.cache")
} else { } else {
m.logger.Debugf("No ld.so.cache found, skipping update") m.logger.Debugf("No ld.so.cache found, skipping update")
@ -119,8 +130,8 @@ func (m command) run(c *cli.Context, cfg *options) error {
} }
folders := cfg.folders.Value() folders := cfg.folders.Value()
if root(containerRoot).hasPath("/etc/ld.so.conf.d") { if containerRoot.hasPath("/etc/ld.so.conf.d") {
err := m.createConfig(containerRoot, folders) err := m.createLdsoconfdFile(containerRoot, ldsoconfdFilenamePattern, folders...)
if err != nil { if err != nil {
return fmt.Errorf("failed to update ld.so.conf.d: %v", err) return fmt.Errorf("failed to update ld.so.conf.d: %v", err)
} }
@ -136,16 +147,6 @@ func (m command) run(c *cli.Context, cfg *options) error {
return syscall.Exec(ldconfigPath, args, nil) return syscall.Exec(ldconfigPath, args, nil)
} }
type root string
func (r root) hasPath(path string) bool {
_, err := os.Stat(filepath.Join(string(r), path))
if err != nil && os.IsNotExist(err) {
return false
}
return true
}
// resolveLDConfigPath determines the LDConfig path to use for the system. // resolveLDConfigPath determines the LDConfig path to use for the system.
// On systems such as Ubuntu where `/sbin/ldconfig` is a wrapper around // On systems such as Ubuntu where `/sbin/ldconfig` is a wrapper around
// /sbin/ldconfig.real, the latter is returned. // /sbin/ldconfig.real, the latter is returned.
@ -153,44 +154,46 @@ func (m command) resolveLDConfigPath(path string) string {
return strings.TrimPrefix(config.NormalizeLDConfigPath("@"+path), "@") return strings.TrimPrefix(config.NormalizeLDConfigPath("@"+path), "@")
} }
// createConfig creates (or updates) /etc/ld.so.conf.d/00-nvcr-<RANDOM_STRING>.conf in the container // createLdsoconfdFile creates a file at /etc/ld.so.conf.d/ in the specified root.
// to include the required paths. // The file is created at /etc/ld.so.conf.d/{{ .pattern }} using `CreateTemp` and
// Note that the 00-nvcr prefix is chosen to ensure that these libraries have // contains the specified directories on each line.
// a higher precedence than other libraries on the system but are applied AFTER func (m command) createLdsoconfdFile(in containerRoot, pattern string, dirs ...string) error {
// 00-cuda-compat.conf. if len(dirs) == 0 {
func (m command) createConfig(root string, folders []string) error { m.logger.Debugf("No directories to add to /etc/ld.so.conf")
if len(folders) == 0 {
m.logger.Debugf("No folders to add to /etc/ld.so.conf")
return nil return nil
} }
if err := os.MkdirAll(filepath.Join(root, "/etc/ld.so.conf.d"), 0755); err != nil { ldsoconfdDir, err := in.resolve("/etc/ld.so.conf.d")
return fmt.Errorf("failed to create ld.so.conf.d: %v", err) if err != nil {
return err
}
if err := os.MkdirAll(ldsoconfdDir, 0755); err != nil {
return fmt.Errorf("failed to create ld.so.conf.d: %w", err)
} }
configFile, err := os.CreateTemp(filepath.Join(root, "/etc/ld.so.conf.d"), "00-nvcr-*.conf") configFile, err := os.CreateTemp(ldsoconfdDir, pattern)
if err != nil { if err != nil {
return fmt.Errorf("failed to create config file: %v", err) return fmt.Errorf("failed to create config file: %w", err)
} }
defer configFile.Close() defer configFile.Close()
m.logger.Debugf("Adding folders %v to %v", folders, configFile.Name()) m.logger.Debugf("Adding directories %v to %v", dirs, configFile.Name())
configured := make(map[string]bool) added := make(map[string]bool)
for _, folder := range folders { for _, dir := range dirs {
if configured[folder] { if added[dir] {
continue continue
} }
_, err = configFile.WriteString(fmt.Sprintf("%s\n", folder)) _, err = configFile.WriteString(fmt.Sprintf("%s\n", dir))
if err != nil { if err != nil {
return fmt.Errorf("failed to update ld.so.conf.d: %v", err) return fmt.Errorf("failed to update config file: %w", err)
} }
configured[folder] = true added[dir] = true
} }
// The created file needs to be world readable for the cases where the container is run as a non-root user. // The created file needs to be world readable for the cases where the container is run as a non-root user.
if err := os.Chmod(configFile.Name(), 0644); err != nil { if err := configFile.Chmod(0644); err != nil {
return fmt.Errorf("failed to chmod config file: %v", err) return fmt.Errorf("failed to chmod config file: %w", err)
} }
return nil return nil