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 }