github.com/cdoern/storage@v1.12.13/drivers/chown.go (about)

     1  package graphdriver
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/containers/storage/pkg/idtools"
    11  	"github.com/containers/storage/pkg/reexec"
    12  )
    13  
    14  const (
    15  	chownByMapsCmd = "storage-chown-by-maps"
    16  )
    17  
    18  func init() {
    19  	reexec.Register(chownByMapsCmd, chownByMapsMain)
    20  }
    21  
    22  func chownByMapsMain() {
    23  	if len(os.Args) < 2 {
    24  		fmt.Fprintf(os.Stderr, "requires mapping configuration on stdin and directory path")
    25  		os.Exit(1)
    26  	}
    27  	// Read and decode our configuration.
    28  	discreteMaps := [4][]idtools.IDMap{}
    29  	config := bytes.Buffer{}
    30  	if _, err := config.ReadFrom(os.Stdin); err != nil {
    31  		fmt.Fprintf(os.Stderr, "error reading configuration: %v", err)
    32  		os.Exit(1)
    33  	}
    34  	if err := json.Unmarshal(config.Bytes(), &discreteMaps); err != nil {
    35  		fmt.Fprintf(os.Stderr, "error decoding configuration: %v", err)
    36  		os.Exit(1)
    37  	}
    38  	// Try to chroot.  This may not be possible, and on some systems that
    39  	// means we just Chdir() to the directory, so from here on we should be
    40  	// using relative paths.
    41  	if err := chrootOrChdir(os.Args[1]); err != nil {
    42  		fmt.Fprintf(os.Stderr, "error chrooting to %q: %v", os.Args[1], err)
    43  		os.Exit(1)
    44  	}
    45  	// Build the mapping objects.
    46  	toContainer := idtools.NewIDMappingsFromMaps(discreteMaps[0], discreteMaps[1])
    47  	if len(toContainer.UIDs()) == 0 && len(toContainer.GIDs()) == 0 {
    48  		toContainer = nil
    49  	}
    50  	toHost := idtools.NewIDMappingsFromMaps(discreteMaps[2], discreteMaps[3])
    51  	if len(toHost.UIDs()) == 0 && len(toHost.GIDs()) == 0 {
    52  		toHost = nil
    53  	}
    54  	chown := func(path string, info os.FileInfo, err error) error {
    55  		if err != nil {
    56  			return fmt.Errorf("error walking to %q: %v", path, err)
    57  		}
    58  		if path == "." {
    59  			return nil
    60  		}
    61  		return platformLChown(path, info, toHost, toContainer)
    62  	}
    63  	if err := filepath.Walk(".", chown); err != nil {
    64  		fmt.Fprintf(os.Stderr, "error during chown: %v", err)
    65  		os.Exit(1)
    66  	}
    67  	os.Exit(0)
    68  }
    69  
    70  // ChownPathByMaps walks the filesystem tree, changing the ownership
    71  // information using the toContainer and toHost mappings, using them to replace
    72  // on-disk owner UIDs and GIDs which are "host" values in the first map with
    73  // UIDs and GIDs for "host" values from the second map which correspond to the
    74  // same "container" IDs.
    75  func ChownPathByMaps(path string, toContainer, toHost *idtools.IDMappings) error {
    76  	if toContainer == nil {
    77  		toContainer = &idtools.IDMappings{}
    78  	}
    79  	if toHost == nil {
    80  		toHost = &idtools.IDMappings{}
    81  	}
    82  
    83  	config, err := json.Marshal([4][]idtools.IDMap{toContainer.UIDs(), toContainer.GIDs(), toHost.UIDs(), toHost.GIDs()})
    84  	if err != nil {
    85  		return err
    86  	}
    87  	cmd := reexec.Command(chownByMapsCmd, path)
    88  	cmd.Stdin = bytes.NewReader(config)
    89  	output, err := cmd.CombinedOutput()
    90  	if len(output) > 0 && err != nil {
    91  		return fmt.Errorf("%v: %s", err, string(output))
    92  	}
    93  	if err != nil {
    94  		return err
    95  	}
    96  	if len(output) > 0 {
    97  		return fmt.Errorf("%s", string(output))
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  type naiveLayerIDMapUpdater struct {
   104  	ProtoDriver
   105  }
   106  
   107  // NewNaiveLayerIDMapUpdater wraps the ProtoDriver in a LayerIDMapUpdater that
   108  // uses ChownPathByMaps to update the ownerships in a layer's filesystem tree.
   109  func NewNaiveLayerIDMapUpdater(driver ProtoDriver) LayerIDMapUpdater {
   110  	return &naiveLayerIDMapUpdater{ProtoDriver: driver}
   111  }
   112  
   113  // UpdateLayerIDMap walks the layer's filesystem tree, changing the ownership
   114  // information using the toContainer and toHost mappings, using them to replace
   115  // on-disk owner UIDs and GIDs which are "host" values in the first map with
   116  // UIDs and GIDs for "host" values from the second map which correspond to the
   117  // same "container" IDs.
   118  func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
   119  	driver := n.ProtoDriver
   120  	options := MountOpts{
   121  		MountLabel: mountLabel,
   122  	}
   123  	layerFs, err := driver.Get(id, options)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	defer func() {
   128  		driver.Put(id)
   129  	}()
   130  
   131  	return ChownPathByMaps(layerFs, toContainer, toHost)
   132  }
   133  
   134  // SupportsShifting tells whether the driver support shifting of the UIDs/GIDs in an userNS
   135  func (n *naiveLayerIDMapUpdater) SupportsShifting() bool {
   136  	return false
   137  }