github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/container_opts_unix.go (about)

     1  // +build !windows
     2  
     3  /*
     4     Copyright The containerd Authors.
     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  package containerd
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"syscall"
    27  
    28  	"github.com/containerd/containerd/containers"
    29  	"github.com/containerd/containerd/errdefs"
    30  	"github.com/containerd/containerd/mount"
    31  	"github.com/opencontainers/image-spec/identity"
    32  )
    33  
    34  // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
    35  // filesystem to be used by a container with user namespaces
    36  func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts {
    37  	return withRemappedSnapshotBase(id, i, uid, gid, false)
    38  }
    39  
    40  // WithRemappedSnapshotView is similar to WithRemappedSnapshot but rootfs is mounted as read-only.
    41  func WithRemappedSnapshotView(id string, i Image, uid, gid uint32) NewContainerOpts {
    42  	return withRemappedSnapshotBase(id, i, uid, gid, true)
    43  }
    44  
    45  func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool) NewContainerOpts {
    46  	return func(ctx context.Context, client *Client, c *containers.Container) error {
    47  		diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), client.platform)
    48  		if err != nil {
    49  			return err
    50  		}
    51  
    52  		var (
    53  			parent   = identity.ChainID(diffIDs).String()
    54  			usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid)
    55  		)
    56  		c.Snapshotter, err = client.resolveSnapshotterName(ctx, c.Snapshotter)
    57  		if err != nil {
    58  			return err
    59  		}
    60  		snapshotter, err := client.getSnapshotter(ctx, c.Snapshotter)
    61  		if err != nil {
    62  			return err
    63  		}
    64  		if _, err := snapshotter.Stat(ctx, usernsID); err == nil {
    65  			if _, err := snapshotter.Prepare(ctx, id, usernsID); err == nil {
    66  				c.SnapshotKey = id
    67  				c.Image = i.Name()
    68  				return nil
    69  			} else if !errdefs.IsNotFound(err) {
    70  				return err
    71  			}
    72  		}
    73  		mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent)
    74  		if err != nil {
    75  			return err
    76  		}
    77  		if err := remapRootFS(ctx, mounts, uid, gid); err != nil {
    78  			snapshotter.Remove(ctx, usernsID)
    79  			return err
    80  		}
    81  		if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil {
    82  			return err
    83  		}
    84  		if readonly {
    85  			_, err = snapshotter.View(ctx, id, usernsID)
    86  		} else {
    87  			_, err = snapshotter.Prepare(ctx, id, usernsID)
    88  		}
    89  		if err != nil {
    90  			return err
    91  		}
    92  		c.SnapshotKey = id
    93  		c.Image = i.Name()
    94  		return nil
    95  	}
    96  }
    97  
    98  func remapRootFS(ctx context.Context, mounts []mount.Mount, uid, gid uint32) error {
    99  	return mount.WithTempMount(ctx, mounts, func(root string) error {
   100  		return filepath.Walk(root, incrementFS(root, uid, gid))
   101  	})
   102  }
   103  
   104  func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc {
   105  	return func(path string, info os.FileInfo, err error) error {
   106  		if err != nil {
   107  			return err
   108  		}
   109  		var (
   110  			stat = info.Sys().(*syscall.Stat_t)
   111  			u, g = int(stat.Uid + uidInc), int(stat.Gid + gidInc)
   112  		)
   113  		// be sure the lchown the path as to not de-reference the symlink to a host file
   114  		return os.Lchown(path, u, g)
   115  	}
   116  }