github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/runtime/engines/singularity/rpc/server/server.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package server
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"runtime"
    12  	"strconv"
    13  	"strings"
    14  	"syscall"
    15  
    16  	args "github.com/sylabs/singularity/internal/pkg/runtime/engines/singularity/rpc"
    17  	"github.com/sylabs/singularity/internal/pkg/sylog"
    18  	"github.com/sylabs/singularity/internal/pkg/util/mainthread"
    19  	"github.com/sylabs/singularity/internal/pkg/util/user"
    20  	"github.com/sylabs/singularity/pkg/util/fs/proc"
    21  	"github.com/sylabs/singularity/pkg/util/loop"
    22  )
    23  
    24  var diskGID = -1
    25  
    26  // Methods is a receiver type.
    27  type Methods int
    28  
    29  // Mount performs a mount with the specified arguments.
    30  func (t *Methods) Mount(arguments *args.MountArgs, reply *int) (err error) {
    31  	mainthread.Execute(func() {
    32  		err = syscall.Mount(arguments.Source, arguments.Target, arguments.Filesystem, arguments.Mountflags, arguments.Data)
    33  	})
    34  	return err
    35  }
    36  
    37  // Mkdir performs a mkdir with the specified arguments.
    38  func (t *Methods) Mkdir(arguments *args.MkdirArgs, reply *int) (err error) {
    39  	mainthread.Execute(func() {
    40  		oldmask := syscall.Umask(0)
    41  		err = os.Mkdir(arguments.Path, arguments.Perm)
    42  		syscall.Umask(oldmask)
    43  	})
    44  	return err
    45  }
    46  
    47  // Chroot performs a chroot with the specified arguments.
    48  func (t *Methods) Chroot(arguments *args.ChrootArgs, reply *int) error {
    49  	root := arguments.Root
    50  
    51  	sylog.Debugf("Change current directory to %s", root)
    52  	if err := syscall.Chdir(root); err != nil {
    53  		return fmt.Errorf("failed to change directory to %s", root)
    54  	}
    55  
    56  	switch arguments.Method {
    57  	case "pivot":
    58  		// idea taken from libcontainer (and also LXC developers) to avoid
    59  		// creation of temporary directory or use of existing directory
    60  		// for pivot_root.
    61  
    62  		sylog.Debugf("Hold reference to host / directory")
    63  		oldroot, err := os.Open("/")
    64  		if err != nil {
    65  			return fmt.Errorf("failed to open host root directory: %s", err)
    66  		}
    67  		defer oldroot.Close()
    68  
    69  		sylog.Debugf("Called pivot_root on %s\n", root)
    70  		if err := syscall.PivotRoot(".", "."); err != nil {
    71  			return fmt.Errorf("pivot_root %s: %s", root, err)
    72  		}
    73  
    74  		sylog.Debugf("Change current directory to host / directory")
    75  		if err := syscall.Fchdir(int(oldroot.Fd())); err != nil {
    76  			return fmt.Errorf("failed to change directory to old root: %s", err)
    77  		}
    78  
    79  		sylog.Debugf("Apply slave mount propagation for host / directory")
    80  		if err := syscall.Mount("", ".", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
    81  			return fmt.Errorf("failed to apply slave mount propagation for host / directory: %s", err)
    82  		}
    83  
    84  		sylog.Debugf("Called unmount(/, syscall.MNT_DETACH)\n")
    85  		if err := syscall.Unmount(".", syscall.MNT_DETACH); err != nil {
    86  			return fmt.Errorf("unmount pivot_root dir %s", err)
    87  		}
    88  	case "move":
    89  		sylog.Debugf("Move %s as / directory", root)
    90  		if err := syscall.Mount(".", "/", "", syscall.MS_MOVE, ""); err != nil {
    91  			return fmt.Errorf("failed to move %s as / directory: %s", root, err)
    92  		}
    93  
    94  		sylog.Debugf("Chroot to %s", root)
    95  		if err := syscall.Chroot("."); err != nil {
    96  			return fmt.Errorf("chroot failed: %s", err)
    97  		}
    98  	case "chroot":
    99  		sylog.Debugf("Chroot to %s", root)
   100  		if err := syscall.Chroot("."); err != nil {
   101  			return fmt.Errorf("chroot failed: %s", err)
   102  		}
   103  	}
   104  
   105  	sylog.Debugf("Changing directory to / to avoid getpwd issues\n")
   106  	if err := syscall.Chdir("/"); err != nil {
   107  		return fmt.Errorf("chdir / %s", err)
   108  	}
   109  	return nil
   110  }
   111  
   112  // LoopDevice attaches a loop device with the specified arguments.
   113  func (t *Methods) LoopDevice(arguments *args.LoopArgs, reply *int) error {
   114  	var image *os.File
   115  
   116  	loopdev := &loop.Device{}
   117  	loopdev.MaxLoopDevices = arguments.MaxDevices
   118  	loopdev.Info = &arguments.Info
   119  	loopdev.Shared = arguments.Shared
   120  
   121  	if strings.HasPrefix(arguments.Image, "/proc/self/fd/") {
   122  		strFd := strings.TrimPrefix(arguments.Image, "/proc/self/fd/")
   123  		fd, err := strconv.ParseUint(strFd, 10, 32)
   124  		if err != nil {
   125  			return fmt.Errorf("failed to convert image file descriptor: %v", err)
   126  		}
   127  		image = os.NewFile(uintptr(fd), "")
   128  	} else {
   129  		var err error
   130  		image, err = os.OpenFile(arguments.Image, arguments.Mode, 0600)
   131  		if err != nil {
   132  			return fmt.Errorf("could not open image file: %v", err)
   133  		}
   134  	}
   135  
   136  	if diskGID == -1 {
   137  		if gr, err := user.GetGrNam("disk"); err == nil {
   138  			diskGID = int(gr.GID)
   139  		} else {
   140  			diskGID = 0
   141  		}
   142  	}
   143  
   144  	runtime.LockOSThread()
   145  	syscall.Setfsuid(0)
   146  	syscall.Setfsgid(diskGID)
   147  	defer runtime.UnlockOSThread()
   148  	defer syscall.Setfsuid(os.Getuid())
   149  	defer syscall.Setfsgid(os.Getgid())
   150  
   151  	err := loopdev.AttachFromFile(image, arguments.Mode, reply)
   152  	if err != nil {
   153  		return fmt.Errorf("could not attach image file to loop device: %v", err)
   154  	}
   155  	return nil
   156  }
   157  
   158  // SetHostname sets hostname with the specified arguments.
   159  func (t *Methods) SetHostname(arguments *args.HostnameArgs, reply *int) error {
   160  	return syscall.Sethostname([]byte(arguments.Hostname))
   161  }
   162  
   163  // HasNamespace checks if host namespace and container namespace
   164  // are different and sets reply to 0 or 1.
   165  func (t *Methods) HasNamespace(arguments *args.HasNamespaceArgs, reply *int) error {
   166  	*reply = 0
   167  	has, err := proc.HasNamespace(arguments.Pid, arguments.NsType)
   168  	if err != nil {
   169  		return err
   170  	}
   171  	if has {
   172  		*reply = 1
   173  	}
   174  	return nil
   175  }
   176  
   177  // SetFsID sets filesystem uid and gid.
   178  func (t *Methods) SetFsID(arguments *args.SetFsIDArgs, reply *int) error {
   179  	mainthread.Execute(func() {
   180  		syscall.Setfsuid(arguments.UID)
   181  		syscall.Setfsgid(arguments.GID)
   182  	})
   183  	return nil
   184  }