github.com/demonoid81/containerd@v1.3.4/sys/mount_linux.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sys 18 19 import ( 20 "runtime" 21 "syscall" 22 "unsafe" 23 24 "github.com/containerd/containerd/log" 25 "github.com/pkg/errors" 26 "golang.org/x/sys/unix" 27 ) 28 29 // FMountat performs mount from the provided directory. 30 func FMountat(dirfd uintptr, source, target, fstype string, flags uintptr, data string) error { 31 var ( 32 sourceP, targetP, fstypeP, dataP *byte 33 pid uintptr 34 err error 35 errno, status syscall.Errno 36 ) 37 38 sourceP, err = syscall.BytePtrFromString(source) 39 if err != nil { 40 return err 41 } 42 43 targetP, err = syscall.BytePtrFromString(target) 44 if err != nil { 45 return err 46 } 47 48 fstypeP, err = syscall.BytePtrFromString(fstype) 49 if err != nil { 50 return err 51 } 52 53 if data != "" { 54 dataP, err = syscall.BytePtrFromString(data) 55 if err != nil { 56 return err 57 } 58 } 59 60 runtime.LockOSThread() 61 defer runtime.UnlockOSThread() 62 63 var pipefds [2]int 64 if err := syscall.Pipe2(pipefds[:], syscall.O_CLOEXEC); err != nil { 65 return errors.Wrap(err, "failed to open pipe") 66 } 67 68 defer func() { 69 // close both ends of the pipe in a deferred function, since open file 70 // descriptor table is shared with child 71 syscall.Close(pipefds[0]) 72 syscall.Close(pipefds[1]) 73 }() 74 75 pid, errno = forkAndMountat(dirfd, 76 uintptr(unsafe.Pointer(sourceP)), 77 uintptr(unsafe.Pointer(targetP)), 78 uintptr(unsafe.Pointer(fstypeP)), 79 flags, 80 uintptr(unsafe.Pointer(dataP)), 81 pipefds[1], 82 ) 83 84 if errno != 0 { 85 return errors.Wrap(errno, "failed to fork thread") 86 } 87 88 defer func() { 89 _, err := unix.Wait4(int(pid), nil, 0, nil) 90 for err == syscall.EINTR { 91 _, err = unix.Wait4(int(pid), nil, 0, nil) 92 } 93 94 if err != nil { 95 log.L.WithError(err).Debugf("failed to find pid=%d process", pid) 96 } 97 }() 98 99 _, _, errno = syscall.RawSyscall(syscall.SYS_READ, 100 uintptr(pipefds[0]), 101 uintptr(unsafe.Pointer(&status)), 102 unsafe.Sizeof(status)) 103 if errno != 0 { 104 return errors.Wrap(errno, "failed to read pipe") 105 } 106 107 if status != 0 { 108 return errors.Wrap(status, "failed to mount") 109 } 110 111 return nil 112 } 113 114 // forkAndMountat will fork thread, change working dir and mount. 115 // 116 // precondition: the runtime OS thread must be locked. 117 func forkAndMountat(dirfd uintptr, source, target, fstype, flags, data uintptr, pipefd int) (pid uintptr, errno syscall.Errno) { 118 119 // block signal during clone 120 beforeFork() 121 122 // the cloned thread shares the open file descriptor, but the thread 123 // never be reused by runtime. 124 pid, _, errno = syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|syscall.CLONE_FILES, 0, 0, 0, 0, 0) 125 if errno != 0 || pid != 0 { 126 // restore all signals 127 afterFork() 128 return 129 } 130 131 // restore all signals 132 afterForkInChild() 133 134 // change working dir 135 _, _, errno = syscall.RawSyscall(syscall.SYS_FCHDIR, dirfd, 0, 0) 136 if errno != 0 { 137 goto childerr 138 } 139 _, _, errno = syscall.RawSyscall6(syscall.SYS_MOUNT, source, target, fstype, flags, data, 0) 140 141 childerr: 142 _, _, errno = syscall.RawSyscall(syscall.SYS_WRITE, uintptr(pipefd), uintptr(unsafe.Pointer(&errno)), unsafe.Sizeof(errno)) 143 syscall.RawSyscall(syscall.SYS_EXIT, uintptr(errno), 0, 0) 144 panic("unreachable") 145 }