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