github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/utils/cmsg.go (about) 1 package utils 2 3 /* 4 * Copyright 2016, 2017 SUSE LLC 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 import ( 20 "fmt" 21 "os" 22 "runtime" 23 24 "golang.org/x/sys/unix" 25 ) 26 27 // MaxNameLen is the maximum length of the name of a file descriptor being sent 28 // using SendFile. The name of the file handle returned by RecvFile will never be 29 // larger than this value. 30 const MaxNameLen = 4096 31 32 // oobSpace is the size of the oob slice required to store a single FD. Note 33 // that unix.UnixRights appears to make the assumption that fd is always int32, 34 // so sizeof(fd) = 4. 35 var oobSpace = unix.CmsgSpace(4) 36 37 // RecvFile waits for a file descriptor to be sent over the given AF_UNIX 38 // socket. The file name of the remote file descriptor will be recreated 39 // locally (it is sent as non-auxiliary data in the same payload). 40 func RecvFile(socket *os.File) (_ *os.File, Err error) { 41 name := make([]byte, MaxNameLen) 42 oob := make([]byte, oobSpace) 43 44 sockfd := socket.Fd() 45 n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, unix.MSG_CMSG_CLOEXEC) 46 if err != nil { 47 return nil, err 48 } 49 if n >= MaxNameLen || oobn != oobSpace { 50 return nil, fmt.Errorf("recvfile: incorrect number of bytes read (n=%d oobn=%d)", n, oobn) 51 } 52 // Truncate. 53 name = name[:n] 54 oob = oob[:oobn] 55 56 scms, err := unix.ParseSocketControlMessage(oob) 57 if err != nil { 58 return nil, err 59 } 60 61 // We cannot control how many SCM_RIGHTS we receive, and upon receiving 62 // them all of the descriptors are installed in our fd table, so we need to 63 // parse all of the SCM_RIGHTS we received in order to close all of the 64 // descriptors on error. 65 var fds []int 66 defer func() { 67 for i, fd := range fds { 68 if i == 0 && Err == nil { 69 // Only close the first one on error. 70 continue 71 } 72 // Always close extra ones. 73 _ = unix.Close(fd) 74 } 75 }() 76 var lastErr error 77 for _, scm := range scms { 78 if scm.Header.Type == unix.SCM_RIGHTS { 79 scmFds, err := unix.ParseUnixRights(&scm) 80 if err != nil { 81 lastErr = err 82 } else { 83 fds = append(fds, scmFds...) 84 } 85 } 86 } 87 if lastErr != nil { 88 return nil, lastErr 89 } 90 91 // We do this after collecting the fds to make sure we close them all when 92 // returning an error here. 93 if len(scms) != 1 { 94 return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms)) 95 } 96 if len(fds) != 1 { 97 return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds)) 98 } 99 return os.NewFile(uintptr(fds[0]), string(name)), nil 100 } 101 102 // SendFile sends a file over the given AF_UNIX socket. file.Name() is also 103 // included so that if the other end uses RecvFile, the file will have the same 104 // name information. 105 func SendFile(socket *os.File, file *os.File) error { 106 name := file.Name() 107 if len(name) >= MaxNameLen { 108 return fmt.Errorf("sendfd: filename too long: %s", name) 109 } 110 err := SendRawFd(socket, name, file.Fd()) 111 runtime.KeepAlive(file) 112 return err 113 } 114 115 // SendRawFd sends a specific file descriptor over the given AF_UNIX socket. 116 func SendRawFd(socket *os.File, msg string, fd uintptr) error { 117 oob := unix.UnixRights(int(fd)) 118 return unix.Sendmsg(int(socket.Fd()), []byte(msg), oob, nil, 0) 119 }