github.com/cdoern/storage@v1.12.13/pkg/archive/archive_linux.go (about)

     1  package archive
     2  
     3  import (
     4  	"archive/tar"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"syscall"
     9  
    10  	"github.com/containers/storage/pkg/idtools"
    11  	"github.com/containers/storage/pkg/system"
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  func getWhiteoutConverter(format WhiteoutFormat, data interface{}) tarWhiteoutConverter {
    16  	if format == OverlayWhiteoutFormat {
    17  		if rolayers, ok := data.([]string); ok && len(rolayers) > 0 {
    18  			return overlayWhiteoutConverter{rolayers: rolayers}
    19  		}
    20  		return overlayWhiteoutConverter{rolayers: nil}
    21  	}
    22  	return nil
    23  }
    24  
    25  type overlayWhiteoutConverter struct {
    26  	rolayers []string
    27  }
    28  
    29  func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
    30  	// convert whiteouts to AUFS format
    31  	if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
    32  		// we just rename the file and make it normal
    33  		dir, filename := filepath.Split(hdr.Name)
    34  		hdr.Name = filepath.Join(dir, WhiteoutPrefix+filename)
    35  		hdr.Mode = 0600
    36  		hdr.Typeflag = tar.TypeReg
    37  		hdr.Size = 0
    38  	}
    39  
    40  	if fi.Mode()&os.ModeDir != 0 {
    41  		// convert opaque dirs to AUFS format by writing an empty file with the whiteout prefix
    42  		opaque, err := system.Lgetxattr(path, "trusted.overlay.opaque")
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  		if len(opaque) == 1 && opaque[0] == 'y' {
    47  			if hdr.Xattrs != nil {
    48  				delete(hdr.Xattrs, "trusted.overlay.opaque")
    49  			}
    50  			// If there are no lower layers, then it can't have been deleted in this layer.
    51  			if len(o.rolayers) == 0 {
    52  				return nil, nil
    53  			}
    54  			// At this point, we have a directory that's opaque.  If it appears in one of the lower
    55  			// layers, then it was newly-created here, so it wasn't also deleted here.
    56  			for _, rolayer := range o.rolayers {
    57  				stat, statErr := os.Stat(filepath.Join(rolayer, hdr.Name))
    58  				if statErr != nil && !os.IsNotExist(statErr) && !isENOTDIR(statErr) {
    59  					// Not sure what happened here.
    60  					return nil, statErr
    61  				}
    62  				if statErr == nil {
    63  					if stat.Mode()&os.ModeCharDevice != 0 {
    64  						// It's a whiteout for this directory, so it can't have been
    65  						// both deleted and recreated in the layer we're diffing.
    66  						s := stat.Sys().(*syscall.Stat_t)
    67  						if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
    68  							return nil, nil
    69  						}
    70  					}
    71  					// It's not whiteout, so it was there in the older layer, so we need to
    72  					// add a whiteout for this item in this layer.
    73  					// create a header for the whiteout file
    74  					// it should inherit some properties from the parent, but be a regular file
    75  					wo = &tar.Header{
    76  						Typeflag:   tar.TypeReg,
    77  						Mode:       hdr.Mode & int64(os.ModePerm),
    78  						Name:       filepath.Join(hdr.Name, WhiteoutOpaqueDir),
    79  						Size:       0,
    80  						Uid:        hdr.Uid,
    81  						Uname:      hdr.Uname,
    82  						Gid:        hdr.Gid,
    83  						Gname:      hdr.Gname,
    84  						AccessTime: hdr.AccessTime,
    85  						ChangeTime: hdr.ChangeTime,
    86  					}
    87  					break
    88  				}
    89  				for dir := filepath.Dir(hdr.Name); dir != "" && dir != "." && dir != string(os.PathSeparator); dir = filepath.Dir(dir) {
    90  					// Check for whiteout for a parent directory in a parent layer.
    91  					stat, statErr := os.Stat(filepath.Join(rolayer, dir))
    92  					if statErr != nil && !os.IsNotExist(statErr) && !isENOTDIR(statErr) {
    93  						// Not sure what happened here.
    94  						return nil, statErr
    95  					}
    96  					if statErr == nil {
    97  						if stat.Mode()&os.ModeCharDevice != 0 {
    98  							// If it's whiteout for a parent directory, then the
    99  							// original directory wasn't inherited into this layer,
   100  							// so we don't need to emit whiteout for it.
   101  							s := stat.Sys().(*syscall.Stat_t)
   102  							if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
   103  								return nil, nil
   104  							}
   105  						}
   106  					}
   107  				}
   108  			}
   109  		}
   110  	}
   111  
   112  	return
   113  }
   114  
   115  func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
   116  	base := filepath.Base(path)
   117  	dir := filepath.Dir(path)
   118  
   119  	// if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay
   120  	if base == WhiteoutOpaqueDir {
   121  		err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0)
   122  		// don't write the file itself
   123  		return false, err
   124  	}
   125  
   126  	// if a file was deleted and we are using overlay, we need to create a character device
   127  	if strings.HasPrefix(base, WhiteoutPrefix) {
   128  		originalBase := base[len(WhiteoutPrefix):]
   129  		originalPath := filepath.Join(dir, originalBase)
   130  
   131  		if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
   132  			return false, err
   133  		}
   134  		if err := idtools.SafeChown(originalPath, hdr.Uid, hdr.Gid); err != nil {
   135  			return false, err
   136  		}
   137  
   138  		// don't write the file itself
   139  		return false, nil
   140  	}
   141  
   142  	return true, nil
   143  }