Files
warewulf/internal/app/wwctl/overlay/edit/main.go
Adrian Reber e86981fd59 fix(wwctl): Create overlay edit tempfile in tmpdir
When editing an overlay, the temporary file was created within the
overlay directory structure. This could lead to the temporary file
being included in the overlay if not cleaned up properly.

This change modifies the behavior to create the temporary file in the
system's default temporary directory, avoiding any potential issues
with the overlay itself.

Not using this change the temporary file can be seen in the overlay:

 DEBUG  : Using temporary file /usr/share/warewulf/overlays/host/ww-overlay-edit-2879742493
 DEBUG  : Checking if path exists as a file: /usr/share/warewulf/overlays/host/rootfs/etc/hosts.ww
 DEBUG  : ExecInteractive(tee, [/usr/share/warewulf/overlays/host/ww-overlay-edit-2879742493])

 # find /srv | grep ww-overlay
 /srv/warewulf/overlays/host/ww-overlay-edit-2879742493

With this patch applied:

 DEBUG  : Using temporary file /tmp/ww-overlay-edit-266752840
 DEBUG  : Checking if path exists as a file: /usr/share/warewulf/overlays/host/rootfs/etc/hosts.ww
 DEBUG  : ExecInteractive(tee, [/tmp/ww-overlay-edit-266752840])

 # find /srv | grep ww-overlay

Assisted-by: Gemini 2.5 Pro
Signed-off-by: Adrian Reber <areber@redhat.com>
2025-09-04 14:10:13 -06:00

129 lines
3.7 KiB
Go

package edit
import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/pkg/overlay"
"github.com/warewulf/warewulf/internal/pkg/util"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
const initialTemplate = `# This is a Warewulf Template file.
#
# This file (suffix '.ww') will be automatically rewritten without the suffix
# when the overlay is rendered for the individual nodes. Here are some examples
# of macros and logic which can be used within this file:
#
# Node FQDN = {{.Id}}
# Node Cluster = {{.ClusterName}}
# Network Config = {{.NetDevs.eth0.Ipaddr}}, {{.NetDevs.eth0.Hwaddr}}, etc.
#
# Go to the documentation pages for more information:
# https://warewulf.org/docs/main/contents/overlays.html
#
# Keep the following for better reference:
# ---
# This file is autogenerated by warewulf
`
func CobraRunE(cmd *cobra.Command, args []string) (err error) {
overlayName := args[0]
fileName := args[1]
myOverlay, err := overlay.Get(overlayName)
if err != nil {
return err
}
overlayFile := myOverlay.File(fileName)
wwlog.Debug("Will edit overlay file: %s", overlayFile)
overlayFileDir := path.Dir(overlayFile)
if !(util.IsDir(overlayFileDir) || CreateDirs) {
return fmt.Errorf("%s does not exist. Use '--parents' option to create automatically", overlayFileDir)
}
tempFile, tempFileErr := os.CreateTemp("", "ww-overlay-edit-")
if tempFileErr != nil {
return fmt.Errorf("unable to create temporary file for editing: %s", tempFileErr)
}
defer os.Remove(tempFile.Name())
wwlog.Debug("Using temporary file %s", tempFile.Name())
if util.IsFile(overlayFile) {
originalFile, openErr := os.Open(overlayFile)
if openErr != nil {
return fmt.Errorf("unable to open %s: %s", overlayFile, openErr)
}
if _, err := io.Copy(tempFile, originalFile); err != nil {
return fmt.Errorf("unable to copy %s to %s for editing: %s", originalFile.Name(), tempFile.Name(), err)
}
originalFile.Close()
} else if filepath.Ext(overlayFile) == ".ww" {
if _, err := tempFile.Write([]byte(initialTemplate)); err != nil {
return fmt.Errorf("unable to write to %s: %s", tempFile.Name(), err)
}
}
tempFile.Close()
initialFile, err := os.Open(tempFile.Name())
if err != nil {
return fmt.Errorf("unable to open temp file for hashing: %s", err)
}
initialHash, err := util.HashFile(initialFile)
initialFile.Close()
if err != nil {
return fmt.Errorf("unable to calculate initial hash of %s: %s", tempFile.Name(), err)
}
editor := os.Getenv("EDITOR")
if editor == "" {
editor = "/bin/vi"
}
if editorErr := util.ExecInteractive(editor, tempFile.Name()); editorErr != nil {
return fmt.Errorf("editor process exited with an error: %s", editorErr)
}
finalFile, err := os.Open(tempFile.Name())
if err != nil {
return fmt.Errorf("unable to open temp file for final hashing: %s", err)
}
finalHash, err := util.HashFile(finalFile)
finalFile.Close()
if err != nil {
return fmt.Errorf("unable to calculate final hash of %s: %s", tempFile.Name(), err)
}
if initialHash == finalHash {
wwlog.Debug("No change detected. Not updating overlay.")
return nil
}
if !myOverlay.IsSiteOverlay() {
myOverlay, err = myOverlay.CloneToSite()
if err != nil {
return err
}
overlayFile = myOverlay.File(fileName)
overlayFileDir = path.Dir(overlayFile)
}
if CreateDirs {
if err := os.MkdirAll(overlayFileDir, 0755); err != nil {
return fmt.Errorf("could not create directory: %s", overlayFileDir)
}
}
// using CopyFile preserves target file permissions
cerr := util.CopyFile(tempFile.Name(), overlayFile)
if cerr != nil {
return fmt.Errorf("unable to copy data from temp file: %s to target file: %s, err: %s", tempFile.Name(), overlayFile, err)
}
return nil
}