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  }