diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go index f7d89d30..4e6fe533 100644 --- a/internal/app/wwctl/container/exec/child/main.go +++ b/internal/app/wwctl/container/exec/child/main.go @@ -29,16 +29,23 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } containerPath := container.RootFsDir(containerName) - - mountFlags := uintptr(syscall.MS_PRIVATE | syscall.MS_REC) - if unix.Access(containerPath, unix.W_OK) != nil { - wwlog.Verbose("Path %s is readonly, mounting %s as ro", containerPath, containerName) - mountFlags |= syscall.MS_RDONLY - } - err := syscall.Mount("", "/", "", mountFlags, "") + 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 !util.IsWriteAble(containerPath) { + 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, fmt.Sprintf("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, fmt.Sprintf("failed to remount ro")) + } + } err = syscall.Mount("/dev", path.Join(containerPath, "/dev"), "", syscall.MS_BIND, "") if err != nil { @@ -80,7 +87,7 @@ 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") 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 +}