File CreateMt-Targets.patch of Package warewulf4

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04a40983..a36b77a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - The environment variable `WW_CONTAINER_SHELL` is defined in a `wwctl
   container shell` environment to indicate the container in use. #579
 - Network interface configuration (`ifcfg`) files now include the
-  interface name and type. #574
+  interface name and type. #457
 
 ### Fixed
 
diff --git a/Makefile b/Makefile
index b494dd69..6363b653 100644
--- a/Makefile
+++ b/Makefile
@@ -96,7 +96,7 @@ export GOPROXY
 WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree
 
 # Default target
-all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults print_defaults wwapid wwapic wwapird
+all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults print_defaults wwapid wwapic wwapird print_mnts
 
 # Validate source and build all packages
 build: lint test-it vet all
@@ -247,6 +247,7 @@ config_defaults: vendor cmd/config_defaults/config_defaults.go
 print_defaults: vendor cmd/print_defaults/print_defaults.go
 	cd cmd/print_defaults && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'" -o ../../print_defaults
 
+
 update_configuration: vendor cmd/update_configuration/update_configuration.go
 	cd cmd/update_configuration && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\
 	 -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\
diff --git a/etc/warewulf.conf b/etc/warewulf.conf
index d0a0c4db..a5b9a346 100644
--- a/etc/warewulf.conf
+++ b/etc/warewulf.conf
@@ -29,3 +29,7 @@ nfs:
     mount options: defaults
     mount: false
   systemd name: nfs-server
+container mounts:
+  - source: /etc/resolv.conf
+    dest: /etc/resolv.conf
+    readonly: true
diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go
index dda8b757..bf32ad17 100644
--- a/internal/app/wwctl/container/exec/child/main.go
+++ b/internal/app/wwctl/container/exec/child/main.go
@@ -7,11 +7,15 @@ import (
 	"fmt"
 	"os"
 	"path"
+	"path/filepath"
+	"strings"
 	"syscall"
-	"time"
 
 	"github.com/hpcng/warewulf/internal/pkg/container"
+	"github.com/hpcng/warewulf/internal/pkg/node"
+	"github.com/hpcng/warewulf/internal/pkg/overlay"
 	"github.com/hpcng/warewulf/internal/pkg/util"
+	"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
 	"github.com/hpcng/warewulf/internal/pkg/wwlog"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
@@ -29,43 +33,130 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
 		wwlog.Error("Unknown Warewulf container: %s", containerName)
 		os.Exit(1)
 	}
+	conf, err := warewulfconf.New()
+	if err != nil {
+		wwlog.Verbose("Couldn't get warewulf ocnfiguration: %s", err)
+	}
+	mountPts := conf.MountsContainer
+	mountPts = append(container.InitMountPnts(binds), mountPts...)
+	// check for valid mount points
+	lowerObjects := checkMountPoints(containerName, mountPts)
+	if len(lowerObjects) != 0 {
+		if tempDir == "" {
+			tempDir, err = os.MkdirTemp(os.TempDir(), "overlay")
+			if err != nil {
+				wwlog.Warn("couldn't create temp dir for overlay", err)
+				lowerObjects = []string{}
+				tempDir = ""
+			}
+		}
+		// need to create a overlay, where the lower layer contains
+		// the missing mount points
+		if tempDir != "" {
+			wwlog.Verbose("for ephermal mount use tempdir %s", tempDir)
+			// ignore errors as we are doomed if a tmp dir couldn't be written
+			_ = os.Mkdir(path.Join(tempDir, "work"), os.ModePerm)
+			_ = os.Mkdir(path.Join(tempDir, "lower"), os.ModePerm)
+			_ = os.Mkdir(path.Join(tempDir, "nodeoverlay"), os.ModePerm)
+			for _, obj := range lowerObjects {
+				newFile := ""
+				if !strings.HasSuffix(obj, "/") {
+					newFile = filepath.Base(obj)
+					obj = filepath.Dir(obj)
+				}
+				err = os.MkdirAll(filepath.Join(tempDir, "lower", obj), os.ModePerm)
+				if err != nil {
+					wwlog.Warn("couldn't create directory for mounts: %s", err)
+				}
+				if newFile != "" {
+					desc, err := os.Create(filepath.Join(tempDir, "lower", obj, newFile))
+					if err != nil {
+						wwlog.Warn("couldn't create directory for mounts: %s", err)
+					}
+					defer desc.Close()
+				}
+			}
+		}
+	}
 	containerPath := container.RootFsDir(containerName)
-	fileStat, _ := os.Stat(path.Join(containerPath, "/etc/passwd"))
-	unixStat := fileStat.Sys().(*syscall.Stat_t)
-	passwdTime := time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))
-	fileStat, _ = os.Stat(path.Join(containerPath, "/etc/group"))
-	unixStat = fileStat.Sys().(*syscall.Stat_t)
-	groupTime := time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))
-	wwlog.Debug("passwd: %v", passwdTime)
-	wwlog.Debug("group: %v", groupTime)
-
-	err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")
+	err = syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")
 	if err != nil {
 		return errors.Wrap(err, "failed to mount")
 	}
+	ps1Str := fmt.Sprintf("[%s] Warewulf> ", containerName)
+	if len(lowerObjects) != 0 && nodename == "" {
+		options := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s",
+			path.Join(tempDir, "lower"), containerPath, path.Join(tempDir, "work"))
+		wwlog.Debug("overlay options: %s", options)
+		err = syscall.Mount("overlay", containerPath, "overlay", 0, options)
+		if err != nil {
+			wwlog.Warn(fmt.Sprintf("Couldn't create overlay for ephermal mount points: %s", err))
+		}
+	} else if nodename != "" {
+		nodeDB, err := node.New()
+		if err != nil {
+			wwlog.Error("Could not open node configuration: %s", err)
+			os.Exit(1)
+		}
+
+		nodes, err := nodeDB.FindAllNodes()
+		if err != nil {
+			wwlog.Error("Could not get node list: %s", err)
+			os.Exit(1)
+		}
+		nodes = node.FilterByName(nodes, []string{nodename})
+		if len(nodes) != 1 {
+			wwlog.Error("No single node idendified with %s", nodename)
+			os.Exit(1)
+		}
+		overlays := nodes[0].SystemOverlay.GetSlice()
+		overlays = append(overlays, nodes[0].RuntimeOverlay.GetSlice()...)
+		err = overlay.BuildOverlayIndir(nodes[0], overlays, path.Join(tempDir, "nodeoverlay"))
+		if err != nil {
+			wwlog.Error("Could not build overlay: %s", err)
+			os.Exit(1)
+		}
+		options := fmt.Sprintf("lowerdir=%s:%s:%s",
+			path.Join(tempDir, "lower"), containerPath, path.Join(tempDir, "nodeoverlay"))
+		wwlog.Debug("overlay options: %s", options)
+		err = syscall.Mount("overlay", containerPath, "overlay", 0, options)
+		if err != nil {
+			wwlog.Warn(fmt.Sprintf("Couldn't create overlay for node render overlay: %s", err))
+			os.Exit(1)
+		}
+		ps1Str = fmt.Sprintf("[%s|ro|%s] Warewulf> ", containerName, nodename)
+	}
+	if !util.IsWriteAble(containerPath) && nodename == "" {
+		wwlog.Verbose("mounting %s ro", containerPath)
+		ps1Str = fmt.Sprintf("[%s|ro] Warewulf> ", containerName)
+		err = syscall.Mount(containerPath, containerPath, "", syscall.MS_BIND, "")
+		if err != nil {
+			return errors.Wrap(err, "failed to prepare bind mount")
+		}
+		err = syscall.Mount(containerPath, containerPath, "", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, "")
+		if err != nil {
+			return errors.Wrap(err, "failed to remount ro")
+		}
+	}
 
 	err = syscall.Mount("/dev", path.Join(containerPath, "/dev"), "", syscall.MS_BIND, "")
 	if err != nil {
 		return errors.Wrap(err, "failed to mount /dev")
 	}
 
-	for _, b := range binds {
-		var source string
-		var dest string
-
-		bind := util.SplitValidPaths(b, ":")
-		source = bind[0]
-
-		if len(bind) == 1 {
-			dest = source
-		} else {
-			dest = bind[1]
-		}
-
-		err := syscall.Mount(source, path.Join(containerPath, dest), "", syscall.MS_BIND, "")
+	for _, mntPnt := range mountPts {
+		err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_BIND, "")
 		if err != nil {
-			fmt.Printf("BIND ERROR: %s\n", err)
-			os.Exit(1)
+			wwlog.Warn("Couldn't mount %s to %s: %s", mntPnt.Source, mntPnt.Dest, err)
+		} else if mntPnt.ReadOnly {
+			err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, "")
+			if err != nil {
+				wwlog.Warn("failed to following mount readonly: %s", mntPnt.Source)
+			} else {
+				wwlog.Verbose("mounted readonly from host to container: %s:%s", mntPnt.Source, mntPnt.Dest)
+			}
+		} else {
+			wwlog.Verbose("mounted from host to container: %s:%s", mntPnt.Source, mntPnt.Dest)
 		}
 	}
 
@@ -84,27 +175,39 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
 		return errors.Wrap(err, "failed to mount proc")
 	}
 
-	os.Setenv("PS1", fmt.Sprintf("[%s] Warewulf> ", containerName))
+	os.Setenv("PS1", ps1Str)
 	os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin")
 	os.Setenv("HISTFILE", "/dev/null")
 
-	err = syscall.Exec(args[1], args[1:], os.Environ())
-	if err != nil {
-		wwlog.Error("%s", err)
-		os.Exit(1)
-	}
-	fileStat, _ = os.Stat(path.Join(containerPath, "/etc/passwd"))
-	unixStat = fileStat.Sys().(*syscall.Stat_t)
-	if passwdTime.Before(time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))) {
-		wwlog.Warn("/etc/passwd has been modified, maybe you want to run syncuser")
-	}
-	wwlog.Debug("passwd: %v", time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec)))
-	fileStat, _ = os.Stat(path.Join(containerPath, "/etc/group"))
-	unixStat = fileStat.Sys().(*syscall.Stat_t)
-	if groupTime.Before(time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))) {
-		wwlog.Warn("/etc/group has been modified, maybe you want to run syncuser")
-	}
-	wwlog.Debug("group: %v", time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec)))
-
+	_ = syscall.Exec(args[1], args[1:], os.Environ())
+	/*
+		Exec replaces the actual program, so nothing to do here afterwards
+	*/
 	return nil
 }
+
+/*
+Check if the bind mount points exists in the given container. Returns
+the invalid mount points. Directories always have '/' as suffix
+*/
+func checkMountPoints(containerName string, binds []*warewulfconf.MountEntry) (overlayObjects []string) {
+	overlayObjects = []string{}
+	for _, b := range binds {
+		_, err := os.Stat(b.Source)
+		if err != nil {
+			wwlog.Debug("Couldn't stat %s create no mount point in container", b.Source)
+			continue
+		}
+		wwlog.Debug("Checking in container for %s", path.Join(container.RootFsDir(containerName), b.Dest))
+		if _, err = os.Stat(path.Join(container.RootFsDir(containerName), b.Dest)); err != nil {
+			if os.IsNotExist(err) {
+				if util.IsDir(b.Dest) && !strings.HasSuffix(b.Dest, "/") {
+					b.Dest += "/"
+				}
+				overlayObjects = append(overlayObjects, b.Dest)
+				wwlog.Debug("Container %s, needs following path: %s", containerName, b.Dest)
+			}
+		}
+	}
+	return overlayObjects
+}
diff --git a/internal/app/wwctl/container/exec/child/root.go b/internal/app/wwctl/container/exec/child/root.go
index 0b7dde7c..d7060995 100644
--- a/internal/app/wwctl/container/exec/child/root.go
+++ b/internal/app/wwctl/container/exec/child/root.go
@@ -5,17 +5,21 @@ import "github.com/spf13/cobra"
 var (
 	baseCmd = &cobra.Command{
 		DisableFlagsInUseLine: true,
-		Use:                "__child",
-		Hidden:             true,
-		RunE:               CobraRunE,
-		Args:               cobra.MinimumNArgs(1),
-		FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
+		Use:                   "__child",
+		Hidden:                true,
+		RunE:                  CobraRunE,
+		Args:                  cobra.MinimumNArgs(1),
+		FParseErrWhitelist:    cobra.FParseErrWhitelist{UnknownFlags: true},
 	}
-	binds []string
+	binds    []string
+	tempDir  string
+	nodename string
 )
 
 func init() {
-	baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "bind points")
+	baseCmd.Flags().StringVarP(&nodename, "node", "n", "", "create ro overlay for given node")
+	baseCmd.Flags().StringArrayVarP(&binds, "bind", "b", []string{}, "bind points")
+	baseCmd.Flags().StringVar(&tempDir, "tempdir", "", "tempdir")
 }
 
 // GetRootCommand returns the root cobra.Command for the application.
diff --git a/internal/app/wwctl/container/exec/main.go b/internal/app/wwctl/container/exec/main.go
index 722ba479..bc622de2 100644
--- a/internal/app/wwctl/container/exec/main.go
+++ b/internal/app/wwctl/container/exec/main.go
@@ -17,9 +17,26 @@ import (
 	"github.com/spf13/cobra"
 )
 
+/*
+fork off a process with a new PID space
+*/
 func runContainedCmd(args []string) error {
+	var err error
+	if tempDir == "" {
+		tempDir, err = os.MkdirTemp(os.TempDir(), "overlay")
+		if err != nil {
+			wwlog.Warn("couldn't create temp dir for overlay", err)
+		}
+		defer func() {
+			err = os.RemoveAll(tempDir)
+			if err != nil {
+				wwlog.Warn("Couldn't remove temp dir for ephermal mounts:", err)
+			}
+		}()
+	}
+	logStr := fmt.Sprint(wwlog.GetLogLevel())
 	wwlog.Verbose("Running contained command: %s", args[1:])
-	c := exec.Command("/proc/self/exe", append([]string{"container", "exec", "__child"}, args...)...)
+	c := exec.Command("/proc/self/exe", append([]string{"--loglevel", logStr, "--tempdir", tempDir, "container", "exec", "__child"}, args...)...)
 
 	c.SysProcAttr = &syscall.SysProcAttr{
 		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
@@ -30,6 +47,11 @@ func runContainedCmd(args []string) error {
 
 	if err := c.Run(); err != nil {
 		fmt.Printf("Command exited non-zero, not rebuilding/updating VNFS image\n")
+		// defer is not called before os.Exit(0)
+		err = os.RemoveAll(tempDir)
+		if err != nil {
+			wwlog.Warn("Couldn't remove temp dir for ephermal mounts:", err)
+		}
 		os.Exit(0)
 	}
 	return nil
@@ -38,6 +60,8 @@ func runContainedCmd(args []string) error {
 func CobraRunE(cmd *cobra.Command, args []string) error {
 
 	containerName := args[0]
+	os.Setenv("WW_CONTAINER_SHELL", containerName)
+
 	var allargs []string
 
 	if !container.ValidSource(containerName) {
@@ -48,6 +72,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
 	for _, b := range binds {
 		allargs = append(allargs, "--bind", b)
 	}
+	if nodeName != "" {
+		allargs = append(allargs, "--node", nodeName)
+	}
 	allargs = append(allargs, args...)
 	containerPath := container.RootFsDir(containerName)
 
@@ -109,3 +136,10 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
 
 	return nil
 }
+func SetBinds(myBinds []string) {
+	binds = append(binds, myBinds...)
+}
+
+func SetNode(myNode string) {
+	nodeName = myNode
+}
diff --git a/internal/app/wwctl/container/exec/root.go b/internal/app/wwctl/container/exec/root.go
index db8bea41..e5fc577b 100644
--- a/internal/app/wwctl/container/exec/root.go
+++ b/internal/app/wwctl/container/exec/root.go
@@ -27,12 +27,16 @@ var (
 	}
 	SyncUser bool
 	binds    []string
+	tempDir  string
+	nodeName string
 )
 
 func init() {
 	baseCmd.AddCommand(child.GetCommand())
 	baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "Bind a local path into the container (must exist)")
 	baseCmd.PersistentFlags().BoolVar(&SyncUser, "syncuser", false, "Synchronize UIDs/GIDs from host to container")
+	baseCmd.PersistentFlags().StringVar(&tempDir, "tempdir", "", "Use tempdir for constructing the overlay fs (only used if mount points don't exist in container)")
+	baseCmd.PersistentFlags().StringVarP(&nodeName, "node", "n", "", "Create a read only view of the container for the given node")
 }
 
 // GetRootCommand returns the root cobra.Command for the application.
diff --git a/internal/app/wwctl/container/shell/main.go b/internal/app/wwctl/container/shell/main.go
index 04a095ec..b7c3fbde 100644
--- a/internal/app/wwctl/container/shell/main.go
+++ b/internal/app/wwctl/container/shell/main.go
@@ -4,11 +4,10 @@
 package shell
 
 import (
-	"fmt"
 	"os"
-	"os/exec"
-	"syscall"
+	"path"
 
+	cntexec "github.com/hpcng/warewulf/internal/app/wwctl/container/exec"
 	"github.com/hpcng/warewulf/internal/pkg/container"
 	"github.com/hpcng/warewulf/internal/pkg/wwlog"
 	"github.com/spf13/cobra"
@@ -23,28 +22,32 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
 		wwlog.Error("Unknown Warewulf container: %s", containerName)
 		os.Exit(1)
 	}
-
-	for _, b := range binds {
-		allargs = append(allargs, "--bind", b)
+	/*
+		for _, b := range binds {
+			allargs = append(allargs, "--bind", b)
+		}
+	*/
+	shellName := os.Getenv("SHELL")
+	if !container.ValidSource(containerName) {
+		wwlog.Error("Unknown Warewulf container: %s", containerName)
+		os.Exit(1)
 	}
-	allargs = append(allargs, args...)
-	allargs = append(allargs, "/usr/bin/bash")
-
-	c := exec.Command("/proc/self/exe", append([]string{"container", "exec"}, allargs...)...)
-
-	//c := exec.Command("/bin/sh")
-	c.SysProcAttr = &syscall.SysProcAttr{
-		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
+	var shells []string
+	if shellName == "" {
+		shells = append(shells, "/bin/bash")
+	} else {
+		shells = append(shells, shellName, "/bin/bash")
 	}
-	c.Stdin = os.Stdin
-	c.Stdout = os.Stdout
-	c.Stderr = os.Stderr
-
-	os.Setenv("WW_CONTAINER_SHELL", containerName)
-
-	if err := c.Run(); err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+	for _, s := range shells {
+		if _, err := os.Stat(path.Join(container.RootFsDir(containerName), s)); err == nil {
+			shellName = s
+			break
+		}
 	}
-	return nil
+	args = append(args, shellName)
+	allargs = append(allargs, args...)
+	wwlog.Debug("Calling exec with args: %s", allargs)
+	cntexec.SetBinds(binds)
+	cntexec.SetNode(nodeName)
+	return cntexec.CobraRunE(cmd, allargs)
 }
diff --git a/internal/app/wwctl/container/shell/root.go b/internal/app/wwctl/container/shell/root.go
index 59f6c56b..dcec3f06 100644
--- a/internal/app/wwctl/container/shell/root.go
+++ b/internal/app/wwctl/container/shell/root.go
@@ -22,11 +22,13 @@ var (
 		},
 		FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
 	}
-	binds []string
+	binds    []string
+	nodeName string
 )
 
 func init() {
 	baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "Bind a local path into the container (must exist)")
+	baseCmd.PersistentFlags().StringVarP(&nodeName, "node", "n", "", "Create a read only view of the container for the given node")
 }
 
 // GetRootCommand returns the root cobra.Command for the application.
diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go
index 81fe2a26..cc8927d6 100644
--- a/internal/app/wwctl/root.go
+++ b/internal/app/wwctl/root.go
@@ -31,12 +31,14 @@ var (
 	}
 	verboseArg bool
 	DebugFlag  bool
+	LogLevel   int
 )
 
 func init() {
 	rootCmd.PersistentFlags().BoolVarP(&verboseArg, "verbose", "v", false, "Run with increased verbosity.")
 	rootCmd.PersistentFlags().BoolVarP(&DebugFlag, "debug", "d", false, "Run with debugging messages enabled.")
-
+	rootCmd.PersistentFlags().IntVar(&LogLevel, "loglevel", wwlog.INFO, "Set log level to given string")
+	_ = rootCmd.PersistentFlags().MarkHidden("loglevel")
 	rootCmd.SetUsageTemplate(help.UsageTemplate)
 	rootCmd.SetHelpTemplate(help.HelpTemplate)
 
@@ -65,6 +67,9 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error {
 	} else {
 		wwlog.SetLogLevel(wwlog.INFO)
 	}
+	if LogLevel != wwlog.INFO {
+		wwlog.SetLogLevel(LogLevel)
+	}
 	return nil
 }
 
diff --git a/internal/pkg/api/apiconfig/container/container.go b/internal/pkg/api/apiconfig/container/container.go
index 8bc2ba23..beb29544 100644
--- a/internal/pkg/api/apiconfig/container/container.go
+++ b/internal/pkg/api/apiconfig/container/container.go
@@ -192,12 +192,6 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin
 		return
 	}
 
-	fmt.Printf("Updating the container's /etc/resolv.conf\n")
-	err = util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(cip.Name), "/etc/resolv.conf"))
-	if err != nil {
-		wwlog.Warn("Could not copy /etc/resolv.conf into container: %s", err)
-	}
-
 	fmt.Printf("Building container: %s\n", cip.Name)
 	err = container.Build(cip.Name, true)
 	if err != nil {
diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go
index 757ea873..6683c75c 100644
--- a/internal/pkg/api/container/container.go
+++ b/internal/pkg/api/container/container.go
@@ -197,12 +197,6 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin
 		return
 	}
 
-	wwlog.Info("Updating the container's /etc/resolv.conf")
-	err = util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(cip.Name), "/etc/resolv.conf"))
-	if err != nil {
-		wwlog.Warn("Could not copy /etc/resolv.conf into container: %s", err)
-	}
-
 	err = container.SyncUids(cip.Name, !cip.SyncUser)
 	if err != nil && !cip.SyncUser {
 		err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err)
diff --git a/internal/pkg/buildconfig/defaults.go b/internal/pkg/buildconfig/defaults.go
index 24cb2d40..332a2f6d 100644
--- a/internal/pkg/buildconfig/defaults.go
+++ b/internal/pkg/buildconfig/defaults.go
@@ -19,6 +19,7 @@ var (
 	release        string = "UNDEF"
 	wwclientdir    string = "UNDEF"
 	datadir        string = "UNDEF"
+	tmpdir         string = "UNDEF"
 )
 
 func BINDIR() string {
@@ -90,3 +91,8 @@ func WWCLIENTDIR() string {
 	wwlog.Debug("WWCLIENTDIR = '%s'", wwclientdir)
 	return wwclientdir
 }
+
+func TMPDIR() string {
+	wwlog.Debug("TMPDIR = '%s'", tmpdir)
+	return tmpdir
+}
diff --git a/internal/pkg/buildconfig/setconfigs.go.in b/internal/pkg/buildconfig/setconfigs.go.in
index e10303e3..f049ffeb 100644
--- a/internal/pkg/buildconfig/setconfigs.go.in
+++ b/internal/pkg/buildconfig/setconfigs.go.in
@@ -15,4 +15,5 @@ func init() {
     version = "@VERSION@"
     release = "@RELEASE@"
     wwclientdir = "@WWCLIENTDIR@"
+    tmpdir = "@TMPDIR@"
 }
diff --git a/internal/pkg/container/mountpoints.go b/internal/pkg/container/mountpoints.go
new file mode 100644
index 00000000..727ff35c
--- /dev/null
+++ b/internal/pkg/container/mountpoints.go
@@ -0,0 +1,35 @@
+package container
+
+import (
+	"strings"
+
+	"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
+	"github.com/hpcng/warewulf/internal/pkg/wwlog"
+)
+
+/*
+Create a slice iof MntDetails from a string slice with following
+format "source:[:destination][:readonly]" if destination is not
+given, the source is used as destination
+*/
+func InitMountPnts(binds []string) (mounts []*warewulfconf.MountEntry) {
+	wwlog.Debug("Trying to mount following mount points: %s", mounts)
+	for _, b := range binds {
+		bind := strings.Split(b, ":")
+		dest := bind[0]
+		if len(bind) >= 2 {
+			dest = bind[1]
+		}
+		readonly := false
+		if len(bind) >= 3 && bind[2] == "ro" {
+			readonly = true
+		}
+		mntPnt := warewulfconf.MountEntry{
+			Source:   bind[0],
+			Dest:     dest,
+			ReadOnly: readonly,
+		}
+		mounts = append(mounts, &mntPnt)
+	}
+	return mounts
+}
diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go
index 3f97fbb5..6ad445cb 100644
--- a/internal/pkg/util/util.go
+++ b/internal/pkg/util/util.go
@@ -24,12 +24,16 @@ import (
 
 // reserve some number of cpus for system/warwulfd usage
 var processLimitedReserve int = 4
+
 // maximum number of concurrent spawned processes
-var processLimitedMax = MaxInt(1, runtime.NumCPU() - processLimitedReserve)
+var processLimitedMax = MaxInt(1, runtime.NumCPU()-processLimitedReserve)
+
 // Channel used as semaphore to specififed processLimitedMax
 var processLimitedChan = make(chan int, processLimitedMax)
+
 // Current number of processes started + queued
 var processLimitedNum int32 = 0
+
 // Counter over total history of started processes
 var processLimitedCounter uint32 = 0
 
@@ -53,7 +57,7 @@ func ProcessLimitedStatus() (running int32, queued int32) {
 	return
 }
 
-func MaxInt( a int, b int ) int {
+func MaxInt(a int, b int) int {
 	if a > b {
 		return a
 	}
@@ -65,7 +69,7 @@ func FirstError(errs ...error) (err error) {
 	for _, e := range errs {
 		if err == nil {
 			err = e
-		}else if e != nil {
+		} else if e != nil {
 			wwlog.ErrorExc(e, "Unhandled error")
 		}
 	}
@@ -207,7 +211,7 @@ func ValidateOrDie(message string, pattern string, expr string) {
 	}
 }
 
-//******************************************************************************
+// ******************************************************************************
 func FindFiles(path string) []string {
 	var ret []string
 
@@ -244,7 +248,7 @@ func FindFiles(path string) []string {
 	return ret
 }
 
-//******************************************************************************
+// ******************************************************************************
 func FindFilterFiles(
 	path string,
 	include []string,
@@ -265,7 +269,7 @@ func FindFilterFiles(
 		return ofiles, errors.Wrapf(err, "Failed to change path: %s", path)
 	}
 
-	for i := range(ignore) {
+	for i := range ignore {
 		ignore[i] = strings.TrimLeft(ignore[i], "/")
 		ignore[i] = strings.TrimPrefix(ignore[i], "./")
 		wwlog.Debug("Ignore pattern (%d): %s", i, ignore[i])
@@ -282,7 +286,6 @@ func FindFilterFiles(
 
 	dev := path_stat.Sys().(*syscall.Stat_t).Dev
 
-
 	includeDirs := []string{}
 	ignoreDirs := []string{}
 	err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
@@ -306,13 +309,13 @@ func FindFilterFiles(
 			return nil
 		}
 
-		for _, ignoreDir := range(ignoreDirs) {
+		for _, ignoreDir := range ignoreDirs {
 			if strings.HasPrefix(location, ignoreDir) {
 				wwlog.Debug("Ignored (dir): %s", file)
 				return nil
 			}
 		}
-		for i, pattern := range(ignore) {
+		for i, pattern := range ignore {
 			m, err := filepath.Match(pattern, location)
 			if err != nil {
 				return err
@@ -325,14 +328,14 @@ func FindFilterFiles(
 			}
 		}
 
-		for _, includeDir := range(includeDirs) {
+		for _, includeDir := range includeDirs {
 			if strings.HasPrefix(location, includeDir) {
 				wwlog.Debug("Included (dir): %s", file)
 				ofiles = append(ofiles, location)
 				return nil
 			}
 		}
-		for i, pattern := range(include) {
+		for i, pattern := range include {
 			m, err := filepath.Match(pattern, location)
 			if err != nil {
 				return err
@@ -352,7 +355,7 @@ func FindFilterFiles(
 	return ofiles, err
 }
 
-//******************************************************************************
+// ******************************************************************************
 func ExecInteractive(command string, a ...string) error {
 	wwlog.Debug("ExecInteractive(%s, %s)", command, a)
 	c := exec.Command(command, a...)
@@ -538,20 +541,22 @@ func AppendLines(fileName string, lines []string) error {
 	return nil
 }
 
-/*******************************************************************************
+/*
+******************************************************************************
+
 	Create an archive using cpio
 */
 func CpioCreate(
 	ifiles []string,
 	ofile string,
 	format string,
-	cpio_args ...string ) (err error) {
+	cpio_args ...string) (err error) {
 
 	args := []string{
 		"--quiet",
 		"--create",
 		"-H", format,
-		"--file=" + ofile }
+		"--file=" + ofile}
 
 	args = append(args, cpio_args...)
 
@@ -574,14 +579,16 @@ func CpioCreate(
 		wwlog.Debug(string(out))
 	}
 
-	return FirstError(err, <- err_in)
+	return FirstError(err, <-err_in)
 }
 
-/*******************************************************************************
+/*
+******************************************************************************
+
 	Compress a file using gzip or pigz
 */
 func FileGz(
-	file string ) (err error) {
+	file string) (err error) {
 
 	file_gz := file + ".gz"
 
@@ -608,41 +615,41 @@ func FileGz(
 	proc := exec.Command(
 		compressor,
 		"--keep",
-	 	file )
+		file)
 
 	out, err := proc.CombinedOutput()
 	if len(out) > 0 {
 		outStr := string(out[:])
 		if err != nil && strings.HasSuffix(compressor, "gzip") && strings.Contains(outStr, "unrecognized option") {
-			var		gzippedFile *os.File
-			var     gzipStderr io.ReadCloser
-            
+			var gzippedFile *os.File
+			var gzipStderr io.ReadCloser
+
 			/* Older version of gzip, try it another way: */
 			wwlog.Verbose("%s does not recognize the --keep flag, trying redirected stdout", compressor)
-			
+
 			/* Open the output file for writing: */
 			gzippedFile, err = os.Create(file_gz)
 			if err != nil {
 				return errors.Wrapf(err, "Unable to open compressed image file for writing: %s", file_gz)
 			}
-			
+
 			/* We'll execute gzip with output to stdout and attach stdout to the compressed file we just
 			   created:
-			 */
+			*/
 			proc = exec.Command(
 				compressor,
 				"--stdout",
-				file )
+				file)
 			proc.Stdout = gzippedFile
 			gzipStderr, err = proc.StderrPipe()
 			if err != nil {
 				return errors.Wrapf(err, "Unable to open stderr pipe for compression program: %s", compressor)
 			}
-			
+
 			/* Execute the command: */
 			err = proc.Start()
 			if err != nil {
-                _ = proc.Wait()
+				_ = proc.Wait()
 				gzippedFile.Close()
 				os.Remove(file_gz)
 				err = errors.Wrapf(err, "Unable to successfully execute compression program: %s", compressor)
@@ -664,7 +671,9 @@ func FileGz(
 	return err
 }
 
-/*******************************************************************************
+/*
+******************************************************************************
+
 	Create an archive using cpio
 */
 func BuildFsImage(
@@ -675,7 +684,7 @@ func BuildFsImage(
 	ignore []string,
 	ignore_xdev bool,
 	format string,
-	cpio_args ...string ) (err error) {
+	cpio_args ...string) (err error) {
 
 	err = os.MkdirAll(path.Dir(imagePath), 0755)
 	if err != nil {
@@ -709,16 +718,16 @@ func BuildFsImage(
 		".",
 		include,
 		ignore,
-		ignore_xdev )
+		ignore_xdev)
 	if err != nil {
 		return errors.Wrapf(err, "Failed discovering files for %s: %s", name, rootfsPath)
 	}
 
 	err = CpioCreate(
 		files,
-	 	imagePath,
+		imagePath,
 		format,
- 		cpio_args...)
+		cpio_args...)
 	if err != nil {
 		return errors.Wrapf(err, "Failed creating image for %s: %s", name, imagePath)
 	}
@@ -727,15 +736,17 @@ func BuildFsImage(
 
 	err = FileGz(imagePath)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to compress image for %s: %s", name, imagePath + ".gz")
+		return errors.Wrapf(err, "Failed to compress image for %s: %s", name, imagePath+".gz")
 	}
 
-	wwlog.Info("Compressed image for %s: %s", name, imagePath + ".gz")
+	wwlog.Info("Compressed image for %s: %s", name, imagePath+".gz")
 
 	return nil
 }
 
-/*******************************************************************************
+/*
+******************************************************************************
+
 	Runs wwctl command
 */
 func RunWWCTL(args ...string) (out []byte, err error) {
@@ -744,7 +755,7 @@ func RunWWCTL(args ...string) (out []byte, err error) {
 	running, queued := ProcessLimitedStatus()
 
 	wwlog.Verbose("Starting wwctl process %d (%d running, %d queued): %v",
-		index, running, queued, args )
+		index, running, queued, args)
 
 	proc := exec.Command("wwctl", args...)
 
@@ -787,3 +798,21 @@ func ByteToString(b int64) string {
 	}
 	return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp])
 }
+
+/*
+Check if the w-bit of a file/dir. unix.Access(file,unix.W_OK) will
+not show this.
+*/
+func IsWriteAble(path string) bool {
+	info, err := os.Stat(path)
+	if err != nil {
+		return false
+	}
+
+	// Check if the user bit is enabled in file permission
+	if info.Mode().Perm()&(1<<(uint(7))) == 0 {
+		wwlog.Debug("Write permission bit is not set for: %s", path)
+		return false
+	}
+	return true
+}
diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go
index c3eebf8c..170c94cd 100644
--- a/internal/pkg/warewulfconf/datastructure.go
+++ b/internal/pkg/warewulfconf/datastructure.go
@@ -7,19 +7,20 @@ import (
 )
 
 type ControllerConf struct {
-	WWInternal int           `yaml:"WW_INTERNAL"`
-	Comment    string        `yaml:"comment,omitempty"`
-	Ipaddr     string        `yaml:"ipaddr"`
-	Ipaddr6    string        `yaml:"ipaddr6,omitempty"`
-	Netmask    string        `yaml:"netmask"`
-	Network    string        `yaml:"network,omitempty"`
-	Ipv6net    string        `yaml:"ipv6net,omitempty"`
-	Fqdn       string        `yaml:"fqdn,omitempty"`
-	Warewulf   *WarewulfConf `yaml:"warewulf"`
-	Dhcp       *DhcpConf     `yaml:"dhcp"`
-	Tftp       *TftpConf     `yaml:"tftp"`
-	Nfs        *NfsConf      `yaml:"nfs"`
-	current    bool
+	WWInternal      int           `yaml:"WW_INTERNAL"`
+	Comment         string        `yaml:"comment,omitempty"`
+	Ipaddr          string        `yaml:"ipaddr"`
+	Ipaddr6         string        `yaml:"ipaddr6,omitempty"`
+	Netmask         string        `yaml:"netmask"`
+	Network         string        `yaml:"network,omitempty"`
+	Ipv6net         string        `yaml:"ipv6net,omitempty"`
+	Fqdn            string        `yaml:"fqdn,omitempty"`
+	Warewulf        *WarewulfConf `yaml:"warewulf"`
+	Dhcp            *DhcpConf     `yaml:"dhcp"`
+	Tftp            *TftpConf     `yaml:"tftp"`
+	Nfs             *NfsConf      `yaml:"nfs"`
+	MountsContainer []*MountEntry `yaml:"container mounts"`
+	current         bool
 }
 
 type WarewulfConf struct {
@@ -59,6 +60,16 @@ type NfsExportConf struct {
 	Mount         bool   `default:"true" yaml:"mount"`
 }
 
+/*
+Describe a mount point for a container exec
+*/
+type MountEntry struct {
+	Source   string `yaml:"source" default:"/etc/resolv.conf"`
+	Dest     string `yaml:"dest,omitempty" default:"/etc/resolv.conf"`
+	ReadOnly bool   `yaml:"readonly,omitempty" default:"false"`
+	Options  string `yaml:"options,omitempty"` // ignored at the moment
+}
+
 func (s *NfsConf) Unmarshal(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(s); err != nil {
 		return err
diff --git a/print_mnts b/print_mnts
new file mode 100755
index 00000000..28986c71
Binary files /dev/null and b/print_mnts differ
openSUSE Build Service is sponsored by