Refactor syncuser to reduce walking the container fs

- Closes #1209

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2024-05-02 13:28:57 -06:00
parent 36a508319c
commit 6b8ca378d8
5 changed files with 52 additions and 61 deletions

View File

@@ -42,12 +42,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Replace reference to docusaurus with Sphinx
- `wwctl container import` now only runs syncuser if explicitly requested. #1212
### Fixed
- Block unprivileged requests for arbitrary overlays in secure mode. #1215
- Fix installation docs to use github.com/warewulf instead of github.com/hpcng. #1219
- Fix the issue that warewulf.conf parse does not support CIDR format. #1130
- Reduce the number of times syncuser walks the container file system. #1209
### Security

View File

@@ -121,7 +121,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
}
wwlog.Debug("group: %v", time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec)))
if syncuids && SyncUser {
err = container.SyncUids(containerName, true)
err = container.SyncUids(containerName, false)
if err != nil {
wwlog.Error("Error in user sync, fix error and run 'syncuser' manually, but trying to build container: %s", err)
}

View File

@@ -16,7 +16,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
if !container.ValidName(containerName) {
return fmt.Errorf("%s is not a valid container", containerName)
}
err := container.SyncUids(containerName, !write)
err := container.SyncUids(containerName, write)
if err != nil {
wwlog.Error("Error in synchronize: %s", err)
os.Exit(1)

View File

@@ -231,12 +231,11 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin
return
}
SyncUserShowOnly := !cip.SyncUser
err = container.SyncUids(cip.Name, SyncUserShowOnly)
if err != nil {
err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err)
wwlog.Error(err.Error())
if cip.SyncUser {
if cip.SyncUser {
err = container.SyncUids(cip.Name, true)
if err != nil {
err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err)
wwlog.Error(err.Error())
return
}
}

View File

@@ -25,7 +25,7 @@ const groupPath = "/etc/group"
// container. Files in the container are updated to match the new
// numeric id assignments.
//
// If showOnly is true, the container is not actually updated, but
// If write is false, the container is not actually updated, but
// relevant log entries and output are generated.
//
// Any I/O errors are returned unmodified.
@@ -33,7 +33,8 @@ const groupPath = "/etc/group"
// A conflict arises if the container has an entry with the same id as
// an entry in the host and the host does not have an entry with the
// same name. In this case, an error is returned.
func SyncUids(containerName string, showOnly bool) error {
func SyncUids(containerName string, write bool) error {
wwlog.Debug("SyncUids(containerName=%v, write=%v)", containerName, write)
containerPath := RootFsDir(containerName)
containerPasswdPath := path.Join(containerPath, passwdPath)
containerGroupPath := path.Join(containerPath, groupPath)
@@ -60,22 +61,17 @@ func SyncUids(containerName string, showOnly bool) error {
return err
}
if err := passwdSync.findUserFiles(containerPath); err != nil {
return err
}
if err := groupSync.findGroupFiles(containerPath); err != nil {
return err
}
passwdSync.log("passwd")
groupSync.log("group")
if showOnly {
if passwdSync.needsSync() || groupSync.needsSync() {
wwlog.Info("uid/gid not synced: run `wwctl container syncuser --write %s`", containerName)
} else {
wwlog.Info("uid/gid already synced")
}
} else {
if err := passwdSync.findUserFiles(containerPath); err != nil {
return err
}
if err := groupSync.findGroupFiles(containerPath); err != nil {
return err
}
if write {
if err := passwdSync.chownUserFiles(); err != nil {
return err
}
@@ -89,6 +85,12 @@ func SyncUids(containerName string, showOnly bool) error {
return err
}
wwlog.Info("uid/gid synced for container %s", containerName)
} else {
if passwdSync.needsSync() || groupSync.needsSync() {
wwlog.Info("uid/gid not synced: run `wwctl container syncuser --write %s`", containerName)
} else {
wwlog.Info("uid/gid already synced")
}
}
return nil
@@ -224,16 +226,34 @@ func (db syncDB) readFromContainer(fileName string) error { return db.read(fileN
// If byGid is true, files with a matching gid are
// recorded. Otherwise, files with a matching uid are recorded.
func (db syncDB) findFiles(containerPath string, byGid bool) error {
for name, ids := range db {
if err := ids.findFiles(containerPath, byGid); err != nil {
return err
wwlog.Debug("findFiles(containerPath=%v, byGid=%v)", containerPath, byGid)
syncIDs := make(map[int]string)
for name, info := range db {
if info.inHost() && info.inContainer() && !info.match() {
wwlog.Debug("syncID[%v] = %v", info.ContainerID, name)
syncIDs[info.ContainerID] = name
}
if len(ids.ContainerFiles) > 0 {
wwlog.Debug("files for %s (%v -> %v, gid: %v): %v", name, ids.ContainerID, ids.HostID, byGid, ids.ContainerFiles)
}
db[name] = ids
}
return nil
return filepath.Walk(containerPath, func(filePath string, fileInfo fs.FileInfo, err error) error {
if stat, ok := fileInfo.Sys().(*syscall.Stat_t); ok {
var id int
if byGid {
id = int(stat.Gid)
} else {
id = int(stat.Uid)
}
if name, ok := syncIDs[id]; ok {
info := db[name]
wwlog.Debug("findFiles: %s: (%v -> %v, gid: %v)", filePath, info.ContainerID, info.HostID, byGid)
info.ContainerFiles = append(info.ContainerFiles, filePath)
db[name] = info
} else {
wwlog.Debug("findFiles: %s", filePath)
}
}
return nil
})
}
// findUserFiles is equivalent to findFiles(containerPath, false)
@@ -378,36 +398,6 @@ func (info *syncInfo) differ() bool {
return info.inContainer() && info.inHost() && info.HostID != info.ContainerID
}
// findFiles walks containerPath to find files owned by the name
// represented by info and records them in info.ContainerFiles.
//
// If byGid is true, the file's gid is checked; otherwise, the file's
// uid is checked.
func (info *syncInfo) findFiles(containerPath string, byGid bool) error {
var containerFiles []string
if info.inHost() && !info.match() {
if err := filepath.Walk(containerPath, func(filePath string, fileInfo fs.FileInfo, err error) error {
wwlog.Debug("findFiles: %s", filePath)
if stat, ok := fileInfo.Sys().(*syscall.Stat_t); ok {
var id int
if byGid {
id = int(stat.Gid)
} else {
id = int(stat.Uid)
}
if id == info.ContainerID {
containerFiles = append(containerFiles, filePath)
}
}
return nil
}); err != nil {
return err
}
}
info.ContainerFiles = containerFiles
return nil
}
// chownFiles updates the files recorded in info.ContainerFiles to use
// the numerical IDs from the host.
//