github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/cmd/snap-update-ns/bootstrap.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package main
    21  
    22  // Use a pre-main helper to switch the mount namespace. This is required as
    23  // golang creates threads at will and setns(..., CLONE_NEWNS) fails if any
    24  // threads apart from the main thread exist.
    25  
    26  /*
    27  
    28  #include <stdlib.h>
    29  #include "bootstrap.h"
    30  
    31  // The bootstrap function is called by the loader before passing
    32  // control to main. We are using `preinit_array` rather than
    33  // `init_array` because the Go linker adds its own initialisation
    34  // function to `init_array`, and having ours run second would defeat
    35  // the purpose of the C bootstrap code.
    36  //
    37  // The `used` attribute ensures that the compiler doesn't oprimise out
    38  // the variable on the mistaken belief that it isn't used.
    39  __attribute__((section(".preinit_array"), used)) static typeof(&bootstrap) init = &bootstrap;
    40  
    41  // NOTE: do not add anything before the following `import "C"'
    42  */
    43  import "C"
    44  
    45  import (
    46  	"errors"
    47  	"fmt"
    48  	"syscall"
    49  	"unsafe"
    50  )
    51  
    52  var (
    53  	// ErrNoNamespace is returned when a snap namespace does not exist.
    54  	ErrNoNamespace = errors.New("cannot update mount namespace that was not created yet")
    55  )
    56  
    57  // IMPORTANT: all the code in this section may be run with elevated privileges
    58  // when invoking snap-update-ns from the setuid snap-confine.
    59  
    60  // BootstrapError returns error (if any) encountered in pre-main C code.
    61  func BootstrapError() error {
    62  	if C.bootstrap_msg == nil {
    63  		return nil
    64  	}
    65  	errno := syscall.Errno(C.bootstrap_errno)
    66  	// Translate EINVAL from setns or ENOENT from open into a dedicated error.
    67  	if errno == syscall.EINVAL || errno == syscall.ENOENT {
    68  		return ErrNoNamespace
    69  	}
    70  	if errno != 0 {
    71  		return fmt.Errorf("%s: %s", C.GoString(C.bootstrap_msg), errno)
    72  	}
    73  	return fmt.Errorf("%s", C.GoString(C.bootstrap_msg))
    74  }
    75  
    76  // This function is here to make clearing the boostrap errors accessible
    77  // from the tests.
    78  func clearBootstrapError() {
    79  	C.bootstrap_msg = nil
    80  	C.bootstrap_errno = 0
    81  }
    82  
    83  // END IMPORTANT
    84  
    85  func makeArgv(args []string) []*C.char {
    86  	// Create argv array with terminating NULL element
    87  	argv := make([]*C.char, len(args)+1)
    88  	for i, arg := range args {
    89  		argv[i] = C.CString(arg)
    90  	}
    91  	return argv
    92  }
    93  
    94  func freeArgv(argv []*C.char) {
    95  	for _, arg := range argv {
    96  		C.free(unsafe.Pointer(arg))
    97  	}
    98  }
    99  
   100  // validateInstanceName checks if snap instance name is valid.
   101  // This also sets bootstrap_msg on failure.
   102  //
   103  // This function is here only to make the C.validate_instance_name
   104  // code testable from go.
   105  func validateInstanceName(instanceName string) int {
   106  	cStr := C.CString(instanceName)
   107  	defer C.free(unsafe.Pointer(cStr))
   108  	return int(C.validate_instance_name(cStr))
   109  }
   110  
   111  // processArguments parses commnad line arguments.
   112  // The argument cmdline is a string with embedded
   113  // NUL bytes, separating particular arguments.
   114  //
   115  // This function is here only to make the C.validate_instance_name
   116  // code testable from go.
   117  func processArguments(args []string) (snapName string, shouldSetNs bool, processUserFstab bool, uid uint) {
   118  	argv := makeArgv(args)
   119  	defer freeArgv(argv)
   120  
   121  	var snapNameOut *C.char
   122  	var shouldSetNsOut C.bool
   123  	var processUserFstabOut C.bool
   124  	var uidOut C.ulong
   125  	C.process_arguments(C.int(len(args)), &argv[0], &snapNameOut, &shouldSetNsOut, &processUserFstabOut, &uidOut)
   126  	if snapNameOut != nil {
   127  		snapName = C.GoString(snapNameOut)
   128  	}
   129  	shouldSetNs = bool(shouldSetNsOut)
   130  	processUserFstab = bool(processUserFstabOut)
   131  	uid = uint(uidOut)
   132  
   133  	return snapName, shouldSetNs, processUserFstab, uid
   134  }