github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/oci/layer/tar_extract.go (about)

     1  /*
     2   * umoci: Umoci Modifies Open Containers' Images
     3   * Copyright (C) 2016-2020 SUSE LLC
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *    http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package layer
    19  
    20  import (
    21  	"archive/tar"
    22  	"bytes"
    23  	"fmt"
    24  	"io"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/apex/log"
    31  	securejoin "github.com/cyphar/filepath-securejoin"
    32  	"github.com/opencontainers/umoci/pkg/fseval"
    33  	"github.com/opencontainers/umoci/pkg/system"
    34  	"github.com/opencontainers/umoci/third_party/shared"
    35  	"github.com/pkg/errors"
    36  	"golang.org/x/sys/unix"
    37  )
    38  
    39  // inUserNamespace is a cached return value of shared.RunningInUserNS(). We
    40  // compute this once globally rather than for each unpack. It won't change (we
    41  // would hope) after we check it the first time.
    42  var inUserNamespace = shared.RunningInUserNS()
    43  
    44  // TarExtractor represents a tar file to be extracted.
    45  type TarExtractor struct {
    46  	// mapOptions is the set of mapping options to use when extracting
    47  	// filesystem layers.
    48  	mapOptions MapOptions
    49  
    50  	// partialRootless indicates whether "partial rootless" tricks should be
    51  	// applied in our extraction. Rootless and userns execution have some
    52  	// similar tricks necessary, but not all rootless tricks should be applied
    53  	// when running in a userns -- hence the term "partial rootless" tricks.
    54  	partialRootless bool
    55  
    56  	// fsEval is an fseval.FsEval used for extraction.
    57  	fsEval fseval.FsEval
    58  
    59  	// upperPaths are paths that have either been extracted in the execution of
    60  	// this TarExtractor or are ancestors of paths extracted. The purpose of
    61  	// having this stored in-memory is to be able to handle opaque whiteouts as
    62  	// well as some other possible ordering issues with malformed archives (the
    63  	// downside of this approach is that it takes up memory -- we could switch
    64  	// to a trie if necessary). These paths are relative to the tar root but
    65  	// are fully symlink-expanded so no need to worry about that line noise.
    66  	upperPaths map[string]struct{}
    67  
    68  	// enotsupWarned is a flag set when we encounter the first ENOTSUP error
    69  	// dealing with xattrs. This is used to ensure extraction to a destination
    70  	// file system that does not support xattrs raises a single warning, rather
    71  	// than a warning for every file, which can amount to 1000s of messages that
    72  	// scroll a terminal, and may obscure other more important warnings.
    73  	enotsupWarned bool
    74  
    75  	// keepDirlinks is the corresponding flag from the UnpackOptions
    76  	// supplied when this TarExtractor was constructed.
    77  	keepDirlinks bool
    78  
    79  	// whiteoutMode indicates how this TarExtractor will handle whiteouts.
    80  	whiteoutMode WhiteoutMode
    81  }
    82  
    83  // NewTarExtractor creates a new TarExtractor.
    84  func NewTarExtractor(opt UnpackOptions) *TarExtractor {
    85  	fsEval := fseval.Default
    86  	if opt.MapOptions.Rootless {
    87  		fsEval = fseval.Rootless
    88  	}
    89  
    90  	return &TarExtractor{
    91  		mapOptions:      opt.MapOptions,
    92  		partialRootless: opt.MapOptions.Rootless || inUserNamespace,
    93  		fsEval:          fsEval,
    94  		upperPaths:      make(map[string]struct{}),
    95  		enotsupWarned:   false,
    96  		keepDirlinks:    opt.KeepDirlinks,
    97  		whiteoutMode:    opt.WhiteoutMode,
    98  	}
    99  }
   100  
   101  // restoreMetadata applies the state described in tar.Header to the filesystem
   102  // at the given path. No sanity checking is done of the tar.Header's pathname
   103  // or other information. In addition, no mapping is done of the header.
   104  func (te *TarExtractor) restoreMetadata(path string, hdr *tar.Header) error {
   105  	// Some of the tar.Header fields don't match the OS API.
   106  	fi := hdr.FileInfo()
   107  
   108  	// Get the _actual_ file info to figure out if the path is a symlink.
   109  	isSymlink := hdr.Typeflag == tar.TypeSymlink
   110  	if realFi, err := te.fsEval.Lstat(path); err == nil {
   111  		isSymlink = realFi.Mode()&os.ModeSymlink == os.ModeSymlink
   112  	}
   113  
   114  	// Apply the owner. If we are rootless then "user.rootlesscontainers" has
   115  	// already been set up by unmapHeader, so nothing to do here.
   116  	if !te.mapOptions.Rootless {
   117  		// NOTE: This is not done through fsEval.
   118  		if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil {
   119  			return errors.Wrapf(err, "restore chown metadata: %s", path)
   120  		}
   121  	}
   122  
   123  	// We cannot apply hdr.Mode to symlinks, because symlinks don't have a mode
   124  	// of their own (they're special in that way). We have to apply this after
   125  	// we've applied the owner because setuid bits are cleared when changing
   126  	// owner (in rootless we don't care because we're always the owner).
   127  	if !isSymlink {
   128  		if err := te.fsEval.Chmod(path, fi.Mode()); err != nil {
   129  			return errors.Wrapf(err, "restore chmod metadata: %s", path)
   130  		}
   131  	}
   132  
   133  	// Apply access and modified time. Note that some archives won't fill the
   134  	// atime and mtime fields, so we have to set them to a more sane value.
   135  	// Otherwise Linux will start screaming at us, and nobody wants that.
   136  	mtime := hdr.ModTime
   137  	if mtime.IsZero() {
   138  		// XXX: Should we instead default to atime if it's non-zero?
   139  		mtime = time.Now()
   140  	}
   141  	atime := hdr.AccessTime
   142  	if atime.IsZero() {
   143  		// Default to the mtime.
   144  		atime = mtime
   145  	}
   146  
   147  	// Apply xattrs. In order to make sure that we *only* have the xattr set we
   148  	// want, we first clear the set of xattrs from the file then apply the ones
   149  	// set in the tar.Header.
   150  	err := te.fsEval.Lclearxattrs(path, ignoreXattrs)
   151  	if err != nil {
   152  		if errors.Cause(err) != unix.ENOTSUP {
   153  			return errors.Wrapf(err, "clear xattr metadata: %s", path)
   154  		}
   155  		if !te.enotsupWarned {
   156  			log.Warnf("xattr{%s} ignoring ENOTSUP on clearxattrs", path)
   157  			log.Warnf("xattr{%s} destination filesystem does not support xattrs, further warnings will be suppressed", path)
   158  			te.enotsupWarned = true
   159  		} else {
   160  			log.Debugf("xattr{%s} ignoring ENOTSUP on clearxattrs", path)
   161  		}
   162  	}
   163  
   164  	for name, value := range hdr.Xattrs {
   165  		value := []byte(value)
   166  
   167  		// Forbidden xattrs should never be touched.
   168  		if _, skip := ignoreXattrs[name]; skip {
   169  			// If the xattr is already set to the requested value, don't bail.
   170  			// The reason for this logic is kinda convoluted, but effectively
   171  			// because restoreMetadata is called with the *on-disk* metadata we
   172  			// run the risk of things like "security.selinux" being included in
   173  			// that metadata (and thus tripping the forbidden xattr error). By
   174  			// only touching xattrs that have a different value we are somewhat
   175  			// more efficient and we don't have to special case parent restore.
   176  			// Of course this will only ever impact ignoreXattrs.
   177  			if oldValue, err := te.fsEval.Lgetxattr(path, name); err == nil {
   178  				if bytes.Equal(value, oldValue) {
   179  					log.Debugf("restore xattr metadata: skipping already-set xattr %q: %s", name, hdr.Name)
   180  					continue
   181  				}
   182  			}
   183  			log.Warnf("xattr{%s} ignoring forbidden xattr: %q", hdr.Name, name)
   184  			continue
   185  		}
   186  		if err := te.fsEval.Lsetxattr(path, name, value, 0); err != nil {
   187  			// In rootless mode, some xattrs will fail (security.capability).
   188  			// This is _fine_ as long as we're not running as root (in which
   189  			// case we shouldn't be ignoring xattrs that we were told to set).
   190  			//
   191  			// TODO: We should translate all security.capability capabilities
   192  			//       into v3 capabilities, which allow us to write them as
   193  			//       unprivileged users (we also would need to translate them
   194  			//       back when creating archives).
   195  			if te.partialRootless && os.IsPermission(errors.Cause(err)) {
   196  				log.Warnf("rootless{%s} ignoring (usually) harmless EPERM on setxattr %q", hdr.Name, name)
   197  				continue
   198  			}
   199  			// We cannot do much if we get an ENOTSUP -- this usually means
   200  			// that extended attributes are simply unsupported by the
   201  			// underlying filesystem (such as AUFS or NFS).
   202  			if errors.Cause(err) == unix.ENOTSUP {
   203  				if !te.enotsupWarned {
   204  					log.Warnf("xattr{%s} ignoring ENOTSUP on setxattr %q", hdr.Name, name)
   205  					log.Warnf("xattr{%s} destination filesystem does not support xattrs, further warnings will be suppressed", path)
   206  					te.enotsupWarned = true
   207  				} else {
   208  					log.Debugf("xattr{%s} ignoring ENOTSUP on clearxattrs", path)
   209  				}
   210  				continue
   211  			}
   212  			return errors.Wrapf(err, "restore xattr metadata: %s", path)
   213  		}
   214  	}
   215  
   216  	if err := te.fsEval.Lutimes(path, atime, mtime); err != nil {
   217  		return errors.Wrapf(err, "restore lutimes metadata: %s", path)
   218  	}
   219  
   220  	return nil
   221  }
   222  
   223  // applyMetadata applies the state described in tar.Header to the filesystem at
   224  // the given path, using the state of the TarExtractor to remap information
   225  // within the header. This should only be used with headers from a tar layer
   226  // (not from the filesystem). No sanity checking is done of the tar.Header's
   227  // pathname or other information.
   228  func (te *TarExtractor) applyMetadata(path string, hdr *tar.Header) error {
   229  	// Modify the header.
   230  	if err := unmapHeader(hdr, te.mapOptions); err != nil {
   231  		return errors.Wrap(err, "unmap header")
   232  	}
   233  
   234  	// Restore it on the filesystme.
   235  	return te.restoreMetadata(path, hdr)
   236  }
   237  
   238  // isDirlink returns whether the given path is a link to a directory (or a
   239  // dirlink in rsync(1) parlance) which is used by --keep-dirlink to see whether
   240  // we should extract through the link or clobber the link with a directory (in
   241  // the case where we see a directory to extract and a symlink already exists
   242  // there).
   243  func (te *TarExtractor) isDirlink(root string, path string) (bool, error) {
   244  	// Make sure it exists and is a symlink.
   245  	if _, err := te.fsEval.Readlink(path); err != nil {
   246  		return false, errors.Wrap(err, "read dirlink")
   247  	}
   248  
   249  	// Technically a string.TrimPrefix would also work...
   250  	unsafePath, err := filepath.Rel(root, path)
   251  	if err != nil {
   252  		return false, errors.Wrap(err, "get relative-to-root path")
   253  	}
   254  
   255  	// It should be noted that SecureJoin will evaluate all symlinks in the
   256  	// path, so we don't need to loop over it or anything like that. It'll just
   257  	// be done for us (in UnpackEntry only the dirname(3) is evaluated but here
   258  	// we evaluate the whole thing).
   259  	targetPath, err := securejoin.SecureJoinVFS(root, unsafePath, te.fsEval)
   260  	if err != nil {
   261  		// We hit a symlink loop -- which is fine but that means that this
   262  		// cannot be considered a dirlink.
   263  		if errno := InnerErrno(err); errno == unix.ELOOP {
   264  			err = nil
   265  		}
   266  		return false, errors.Wrap(err, "sanitize old target")
   267  	}
   268  
   269  	targetInfo, err := te.fsEval.Lstat(targetPath)
   270  	if err != nil {
   271  		// ENOENT or similar just means that it's a broken symlink, which
   272  		// means we have to overwrite it (but it's an allowed case).
   273  		if securejoin.IsNotExist(err) {
   274  			err = nil
   275  		}
   276  		return false, err
   277  	}
   278  
   279  	return targetInfo.IsDir(), nil
   280  }
   281  
   282  func (te *TarExtractor) ociWhiteout(root string, dir string, file string) error {
   283  	isOpaque := file == whOpaque
   284  	file = strings.TrimPrefix(file, whPrefix)
   285  
   286  	// We have to be quite careful here. While the most intuitive way of
   287  	// handling whiteouts would be to just RemoveAll without prejudice, We
   288  	// have to be careful here. If there is a whiteout entry for a file
   289  	// *after* a normal entry (in the same layer) then the whiteout must
   290  	// not remove the new entry. We handle this by keeping track of
   291  	// whichpaths have been touched by this layer's extraction (these form
   292  	// the "upperdir"). We also have to handle cases where a directory has
   293  	// been marked for deletion, but a child has been extracted in this
   294  	// layer.
   295  
   296  	path := filepath.Join(dir, file)
   297  	if isOpaque {
   298  		path = dir
   299  	}
   300  
   301  	// If the root doesn't exist we've got nothing to do.
   302  	// XXX: We currently cannot error out if a layer asks us to remove a
   303  	//      non-existent path with this implementation (because we don't
   304  	//      know if it was implicitly removed by another whiteout). In
   305  	//      future we could add lowerPaths that would help track whether
   306  	//      another whiteout caused the removal to "fail" or if the path
   307  	//      was actually missing -- which would allow us to actually error
   308  	//      out here if the layer is invalid).
   309  	if _, err := te.fsEval.Lstat(path); err != nil {
   310  		// Need to use securejoin.IsNotExist to handle ENOTDIR.
   311  		if securejoin.IsNotExist(err) {
   312  			err = nil
   313  		}
   314  		return errors.Wrap(err, "check whiteout target")
   315  	}
   316  
   317  	// Walk over the path to remove it. We remove a given path as soon as
   318  	// it isn't present in upperPaths (which includes ancestors of paths
   319  	// we've extracted so we only need to look up the one path). Otherwise
   320  	// we iterate over any children and try again. The only difference
   321  	// between opaque whiteouts and regular whiteouts is that we don't
   322  	// delete the directory itself with opaque whiteouts.
   323  	err := te.fsEval.Walk(path, func(subpath string, info os.FileInfo, err error) error {
   324  		// If we are passed an error, bail unless it's ENOENT.
   325  		if err != nil {
   326  			// If something was deleted outside of our knowledge it's not
   327  			// the end of the world. In principle this shouldn't happen
   328  			// though, so we log it for posterity.
   329  			if os.IsNotExist(errors.Cause(err)) {
   330  				log.Debugf("whiteout removal hit already-deleted path: %s", subpath)
   331  				err = filepath.SkipDir
   332  			}
   333  			return err
   334  		}
   335  
   336  		// Get the relative form of subpath to root to match
   337  		// te.upperPaths.
   338  		upperPath, err := filepath.Rel(root, subpath)
   339  		if err != nil {
   340  			return errors.Wrap(err, "find relative-to-root [should never happen]")
   341  		}
   342  
   343  		// Remove the path only if it hasn't been touched.
   344  		if _, ok := te.upperPaths[upperPath]; !ok {
   345  			// Opaque whiteouts don't remove the directory itself, so skip
   346  			// the top-level directory.
   347  			if isOpaque && CleanPath(path) == CleanPath(subpath) {
   348  				return nil
   349  			}
   350  
   351  			// Purge the path. We skip anything underneath (if it's a
   352  			// directory) since we just purged it -- and we don't want to
   353  			// hit ENOENT during iteration for no good reason.
   354  			err := errors.Wrap(te.fsEval.RemoveAll(subpath), "whiteout subpath")
   355  			if err == nil && info.IsDir() {
   356  				err = filepath.SkipDir
   357  			}
   358  			return err
   359  		}
   360  		return nil
   361  	})
   362  	return errors.Wrap(err, "whiteout remove")
   363  }
   364  
   365  func (te *TarExtractor) overlayFSWhiteout(dir string, file string) error {
   366  	isOpaque := file == whOpaque
   367  
   368  	// if this is an opaque whiteout, whiteout the directory
   369  	if isOpaque {
   370  		err := te.fsEval.Lsetxattr(dir, "trusted.overlay.opaque", []byte("y"), 0)
   371  		return errors.Wrapf(err, "couldn't set overlayfs whiteout attr for %s", dir)
   372  	}
   373  
   374  	// otherwise, white out the file itself.
   375  	p := filepath.Join(dir, strings.TrimPrefix(file, whPrefix))
   376  	if err := os.RemoveAll(p); err != nil && !os.IsNotExist(err) {
   377  		return errors.Wrapf(err, "couldn't create overlayfs whiteout for %s", p)
   378  	}
   379  
   380  	err := te.fsEval.Mknod(p, unix.S_IFCHR|0666, unix.Mkdev(0, 0))
   381  	return errors.Wrapf(err, "couldn't create overlayfs whiteout for %s", p)
   382  }
   383  
   384  // UnpackEntry extracts the given tar.Header to the provided root, ensuring
   385  // that the layer state is consistent with the layer state that produced the
   386  // tar archive being iterated over. This does handle whiteouts, so a tar.Header
   387  // that represents a whiteout will result in the path being removed.
   388  func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) (Err error) {
   389  	// Make the paths safe.
   390  	hdr.Name = CleanPath(hdr.Name)
   391  	root = filepath.Clean(root)
   392  
   393  	log.WithFields(log.Fields{
   394  		"root": root,
   395  		"path": hdr.Name,
   396  		"type": hdr.Typeflag,
   397  	}).Debugf("unpacking entry")
   398  
   399  	// Get directory and filename, but we have to safely get the directory
   400  	// component of the path. SecureJoinVFS will evaluate the path itself,
   401  	// which we don't want (we're clever enough to handle the actual path being
   402  	// a symlink).
   403  	unsafeDir, file := filepath.Split(hdr.Name)
   404  	if filepath.Join("/", hdr.Name) == "/" {
   405  		// If we got an entry for the root, then unsafeDir is the full path.
   406  		unsafeDir, file = hdr.Name, "."
   407  		// If we're being asked to change the root type, bail because they may
   408  		// change it to a symlink which we could inadvertently follow.
   409  		if hdr.Typeflag != tar.TypeDir {
   410  			return errors.New("malicious tar entry -- refusing to change type of root directory")
   411  		}
   412  	}
   413  	dir, err := securejoin.SecureJoinVFS(root, unsafeDir, te.fsEval)
   414  	if err != nil {
   415  		return errors.Wrap(err, "sanitise symlinks in root")
   416  	}
   417  	path := filepath.Join(dir, file)
   418  
   419  	// Before we do anything, get the state of dir. Because we might be adding
   420  	// or removing files, our parent directory might be modified in the
   421  	// process. As a result, we want to be able to restore the old state
   422  	// (because we only apply state that we find in the archive we're iterating
   423  	// over). We can safely ignore an error here, because a non-existent
   424  	// directory will be fixed by later archive entries.
   425  	if dirFi, err := te.fsEval.Lstat(dir); err == nil && path != dir {
   426  		// FIXME: This is really stupid.
   427  		// #nosec G104
   428  		link, _ := te.fsEval.Readlink(dir)
   429  		dirHdr, err := tar.FileInfoHeader(dirFi, link)
   430  		if err != nil {
   431  			return errors.Wrap(err, "convert dirFi to dirHdr")
   432  		}
   433  
   434  		// More faking to trick restoreMetadata to actually restore the directory.
   435  		dirHdr.Typeflag = tar.TypeDir
   436  		dirHdr.Linkname = ""
   437  
   438  		// os.Lstat doesn't get the list of xattrs by default. We need to fill
   439  		// this explicitly. Note that while Go's "archive/tar" takes strings,
   440  		// in Go strings can be arbitrary byte sequences so this doesn't
   441  		// restrict the possible values.
   442  		// TODO: Move this to a separate function so we can share it with
   443  		//       tar_generate.go.
   444  		xattrs, err := te.fsEval.Llistxattr(dir)
   445  		if err != nil {
   446  			if errors.Cause(err) != unix.ENOTSUP {
   447  				return errors.Wrap(err, "get dirHdr.Xattrs")
   448  			}
   449  			if !te.enotsupWarned {
   450  				log.Warnf("xattr{%s} ignoring ENOTSUP on llistxattr", dir)
   451  				log.Warnf("xattr{%s} destination filesystem does not support xattrs, further warnings will be suppressed", path)
   452  				te.enotsupWarned = true
   453  			} else {
   454  				log.Debugf("xattr{%s} ignoring ENOTSUP on clearxattrs", path)
   455  			}
   456  		}
   457  		if len(xattrs) > 0 {
   458  			dirHdr.Xattrs = map[string]string{}
   459  			for _, xattr := range xattrs {
   460  				value, err := te.fsEval.Lgetxattr(dir, xattr)
   461  				if err != nil {
   462  					return errors.Wrap(err, "get xattr")
   463  				}
   464  				dirHdr.Xattrs[xattr] = string(value)
   465  			}
   466  		}
   467  
   468  		// Ensure that after everything we correctly re-apply the old metadata.
   469  		// We don't map this header because we're restoring files that already
   470  		// existed on the filesystem, not from a tar layer.
   471  		defer func() {
   472  			// Only overwrite the error if there wasn't one already.
   473  			if err := te.restoreMetadata(dir, dirHdr); err != nil {
   474  				if Err == nil {
   475  					Err = errors.Wrap(err, "restore parent directory")
   476  				}
   477  			}
   478  		}()
   479  	}
   480  
   481  	// Currently the spec doesn't specify what the hdr.Typeflag of whiteout
   482  	// files is meant to be. We specifically only produce regular files
   483  	// ('\x00') but it could be possible that someone produces a different
   484  	// Typeflag, expecting that the path is the only thing that matters in a
   485  	// whiteout entry.
   486  	if strings.HasPrefix(file, whPrefix) {
   487  		switch te.whiteoutMode {
   488  		case OCIStandardWhiteout:
   489  			return te.ociWhiteout(root, dir, file)
   490  		case OverlayFSWhiteout:
   491  			return te.overlayFSWhiteout(dir, file)
   492  		default:
   493  			return errors.Errorf("unknown whiteout mode %d", te.whiteoutMode)
   494  		}
   495  	}
   496  
   497  	// Get information about the path. This has to be done after we've dealt
   498  	// with whiteouts because it turns out that lstat(2) will return EPERM if
   499  	// you try to stat a whiteout on AUFS.
   500  	fi, err := te.fsEval.Lstat(path)
   501  	if err != nil {
   502  		// File doesn't exist, just switch fi to the file header.
   503  		fi = hdr.FileInfo()
   504  	}
   505  
   506  	// Attempt to create the parent directory of the path we're unpacking.
   507  	// We do a MkdirAll here because even though you need to have a tar entry
   508  	// for every component of a new path, applyMetadata will correct any
   509  	// inconsistencies.
   510  	// FIXME: We have to make this consistent, since if the tar archive doesn't
   511  	//        have entries for some of these components we won't be able to
   512  	//        verify that we have consistent results during unpacking.
   513  	if err := te.fsEval.MkdirAll(dir, 0777); err != nil {
   514  		return errors.Wrap(err, "mkdir parent")
   515  	}
   516  
   517  	isDirlink := false
   518  	// We remove whatever existed at the old path to clobber it so that
   519  	// creating a new path will not break. The only exception is if the path is
   520  	// a directory in both the layer and the current filesystem, in which case
   521  	// we don't delete it for obvious reasons. In all other cases we clobber.
   522  	//
   523  	// Note that this will cause hard-links in the "lower" layer to not be able
   524  	// to point to "upper" layer inodes even if the extracted type is the same
   525  	// as the old one, however it is not clear whether this is something a user
   526  	// would expect anyway. In addition, this will incorrectly deal with a
   527  	// TarLink that is present before the "upper" entry in the layer but the
   528  	// "lower" file still exists (so the hard-link would point to the old
   529  	// inode). It's not clear if such an archive is actually valid though.
   530  	if !fi.IsDir() || hdr.Typeflag != tar.TypeDir {
   531  		// If we are in --keep-dirlinks mode and the existing fs object is a
   532  		// symlink to a directory (with the pending object is a directory), we
   533  		// don't remove the symlink (and instead allow subsequent objects to be
   534  		// just written through the symlink into the directory). This is a very
   535  		// specific usecase where layers that were generated independently from
   536  		// each other (on different base filesystems) end up with weird things
   537  		// like /lib64 being a symlink only sometimes but you never want to
   538  		// delete libraries (not just the ones that were under the "real"
   539  		// directory).
   540  		//
   541  		// TODO: This code should also handle a pending symlink entry where the
   542  		//       existing object is a directory. I'm not sure how we could
   543  		//       disambiguate this from a symlink-to-a-file but I imagine that
   544  		//       this is something that would also be useful in the same vein
   545  		//       as --keep-dirlinks (which currently only prevents clobbering
   546  		//       in the opposite case).
   547  		if te.keepDirlinks &&
   548  			fi.Mode()&os.ModeSymlink == os.ModeSymlink && hdr.Typeflag == tar.TypeDir {
   549  			isDirlink, err = te.isDirlink(root, path)
   550  			if err != nil {
   551  				return errors.Wrap(err, "check is dirlink")
   552  			}
   553  		}
   554  		if !(isDirlink && te.keepDirlinks) {
   555  			if err := te.fsEval.RemoveAll(path); err != nil {
   556  				return errors.Wrap(err, "clobber old path")
   557  			}
   558  		}
   559  	}
   560  
   561  	// Now create or otherwise modify the state of the path. Right now, either
   562  	// the type of path matches hdr or the path doesn't exist. Note that we
   563  	// don't care about umasks or the initial mode here, since applyMetadata
   564  	// will fix all of that for us.
   565  	switch hdr.Typeflag {
   566  	// regular file
   567  	case tar.TypeReg, tar.TypeRegA:
   568  		// Create a new file, then just copy the data.
   569  		fh, err := te.fsEval.Create(path)
   570  		if err != nil {
   571  			return errors.Wrap(err, "create regular")
   572  		}
   573  		defer fh.Close()
   574  
   575  		// We need to make sure that we copy all of the bytes.
   576  		n, err := system.Copy(fh, r)
   577  		if int64(n) != hdr.Size {
   578  			if err != nil {
   579  				err = errors.Wrapf(err, "short write")
   580  			} else {
   581  				err = io.ErrShortWrite
   582  			}
   583  		}
   584  		if err != nil {
   585  			return errors.Wrap(err, "unpack to regular file")
   586  		}
   587  
   588  		// Force close here so that we don't affect the metadata.
   589  		if err := fh.Close(); err != nil {
   590  			return errors.Wrap(err, "close unpacked regular file")
   591  		}
   592  
   593  	// directory
   594  	case tar.TypeDir:
   595  		if isDirlink {
   596  			break
   597  		}
   598  
   599  		// Attempt to create the directory. We do a MkdirAll here because even
   600  		// though you need to have a tar entry for every component of a new
   601  		// path, applyMetadata will correct any inconsistencies.
   602  		if err := te.fsEval.MkdirAll(path, 0777); err != nil {
   603  			return errors.Wrap(err, "mkdirall")
   604  		}
   605  
   606  	// hard link, symbolic link
   607  	case tar.TypeLink, tar.TypeSymlink:
   608  		linkname := hdr.Linkname
   609  
   610  		// Hardlinks and symlinks act differently when it comes to the scoping.
   611  		// In both cases, we have to just unlink and then re-link the given
   612  		// path. But the function used and the argument are slightly different.
   613  		var linkFn func(string, string) error
   614  		switch hdr.Typeflag {
   615  		case tar.TypeLink:
   616  			linkFn = te.fsEval.Link
   617  			// Because hardlinks are inode-based we need to scope the link to
   618  			// the rootfs using SecureJoinVFS. As before, we need to be careful
   619  			// that we don't resolve the last part of the link path (in case
   620  			// the user actually wanted to hardlink to a symlink).
   621  			unsafeLinkDir, linkFile := filepath.Split(CleanPath(linkname))
   622  			linkDir, err := securejoin.SecureJoinVFS(root, unsafeLinkDir, te.fsEval)
   623  			if err != nil {
   624  				return errors.Wrap(err, "sanitise hardlink target in root")
   625  			}
   626  			linkname = filepath.Join(linkDir, linkFile)
   627  		case tar.TypeSymlink:
   628  			linkFn = te.fsEval.Symlink
   629  		}
   630  
   631  		// Link the new one.
   632  		if err := linkFn(linkname, path); err != nil {
   633  			// FIXME: Currently this can break if tar hardlink entries occur
   634  			//        before we hit the entry those hardlinks link to. I have a
   635  			//        feeling that such archives are invalid, but the correct
   636  			//        way of handling this is to delay link creation until the
   637  			//        very end. Unfortunately this won't work with symlinks
   638  			//        (which can link to directories).
   639  			return errors.Wrap(err, "link")
   640  		}
   641  
   642  	// character device node, block device node
   643  	case tar.TypeChar, tar.TypeBlock:
   644  		// In rootless mode we have no choice but to fake this, since mknod(2)
   645  		// doesn't work as an unprivileged user here.
   646  		//
   647  		// TODO: We need to add the concept of a fake block device in
   648  		//       "user.rootlesscontainers", because this workaround suffers
   649  		//       from the obvious issue that if the file is touched (even the
   650  		//       metadata) then it will be incorrectly copied into the layer.
   651  		//       This would break distribution images fairly badly.
   652  		if te.partialRootless {
   653  			log.Warnf("rootless{%s} creating empty file in place of device %d:%d", hdr.Name, hdr.Devmajor, hdr.Devminor)
   654  			fh, err := te.fsEval.Create(path)
   655  			if err != nil {
   656  				return errors.Wrap(err, "create rootless block")
   657  			}
   658  			defer fh.Close()
   659  			if err := fh.Chmod(0); err != nil {
   660  				return errors.Wrap(err, "chmod 0 rootless block")
   661  			}
   662  			goto out
   663  		}
   664  
   665  		// Otherwise the handling is the same as a FIFO.
   666  		fallthrough
   667  	// fifo node
   668  	case tar.TypeFifo:
   669  		// We have to remove and then create the device. In the FIFO case we
   670  		// could choose not to do so, but we do it anyway just to be on the
   671  		// safe side.
   672  
   673  		mode := system.Tarmode(hdr.Typeflag)
   674  		dev := unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor))
   675  
   676  		// Create the node.
   677  		if err := te.fsEval.Mknod(path, os.FileMode(int64(mode)|hdr.Mode), dev); err != nil {
   678  			return errors.Wrap(err, "mknod")
   679  		}
   680  
   681  	// We should never hit any other headers (Go abstracts them away from us),
   682  	// and we can't handle any custom Tar extensions. So just error out.
   683  	default:
   684  		return fmt.Errorf("unpack entry: %s: unknown typeflag '\\x%x'", hdr.Name, hdr.Typeflag)
   685  	}
   686  
   687  out:
   688  	// Apply the metadata, which will apply any mappings necessary. We don't
   689  	// apply metadata for hardlinks, because hardlinks don't have any separate
   690  	// metadata from their link (and the tar headers might not be filled).
   691  	if hdr.Typeflag != tar.TypeLink {
   692  		if err := te.applyMetadata(path, hdr); err != nil {
   693  			return errors.Wrap(err, "apply hdr metadata")
   694  		}
   695  	}
   696  
   697  	// Everything is done -- the path now exists. Add it (and all its
   698  	// ancestors) to the set of upper paths. We first have to figure out the
   699  	// proper path corresponding to hdr.Name though.
   700  	upperPath, err := filepath.Rel(root, path)
   701  	if err != nil {
   702  		// Really shouldn't happen because of the guarantees of SecureJoinVFS.
   703  		return errors.Wrap(err, "find relative-to-root [should never happen]")
   704  	}
   705  	for pth := upperPath; pth != filepath.Dir(pth); pth = filepath.Dir(pth) {
   706  		te.upperPaths[pth] = struct{}{}
   707  	}
   708  	return nil
   709  }