forked from toolshed/abra
		
	
		
			
				
	
	
		
			108 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package archive
 | |
| 
 | |
| import (
 | |
| 	"archive/tar"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/moby/sys/userns"
 | |
| 	"golang.org/x/sys/unix"
 | |
| )
 | |
| 
 | |
| func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
 | |
| 	if format == OverlayWhiteoutFormat {
 | |
| 		return overlayWhiteoutConverter{}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type overlayWhiteoutConverter struct{}
 | |
| 
 | |
| func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, _ error) {
 | |
| 	// convert whiteouts to AUFS format
 | |
| 	if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
 | |
| 		// we just rename the file and make it normal
 | |
| 		dir, filename := filepath.Split(hdr.Name)
 | |
| 		hdr.Name = filepath.Join(dir, WhiteoutPrefix+filename)
 | |
| 		hdr.Mode = 0o600
 | |
| 		hdr.Typeflag = tar.TypeReg
 | |
| 		hdr.Size = 0
 | |
| 	}
 | |
| 
 | |
| 	if fi.Mode()&os.ModeDir == 0 {
 | |
| 		// FIXME(thaJeztah): return a sentinel error instead of nil, nil
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	opaqueXattrName := "trusted.overlay.opaque"
 | |
| 	if userns.RunningInUserNS() {
 | |
| 		opaqueXattrName = "user.overlay.opaque"
 | |
| 	}
 | |
| 
 | |
| 	// convert opaque dirs to AUFS format by writing an empty file with the prefix
 | |
| 	opaque, err := lgetxattr(path, opaqueXattrName)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if len(opaque) != 1 || opaque[0] != 'y' {
 | |
| 		// FIXME(thaJeztah): return a sentinel error instead of nil, nil
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 	delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
 | |
| 
 | |
| 	// create a header for the whiteout file
 | |
| 	// it should inherit some properties from the parent, but be a regular file
 | |
| 	return &tar.Header{
 | |
| 		Typeflag:   tar.TypeReg,
 | |
| 		Mode:       hdr.Mode & int64(os.ModePerm),
 | |
| 		Name:       filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
 | |
| 		Size:       0,
 | |
| 		Uid:        hdr.Uid,
 | |
| 		Uname:      hdr.Uname,
 | |
| 		Gid:        hdr.Gid,
 | |
| 		Gname:      hdr.Gname,
 | |
| 		AccessTime: hdr.AccessTime,
 | |
| 		ChangeTime: hdr.ChangeTime,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
 | |
| 	base := filepath.Base(path)
 | |
| 	dir := filepath.Dir(path)
 | |
| 
 | |
| 	// if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay
 | |
| 	if base == WhiteoutOpaqueDir {
 | |
| 		opaqueXattrName := "trusted.overlay.opaque"
 | |
| 		if userns.RunningInUserNS() {
 | |
| 			opaqueXattrName = "user.overlay.opaque"
 | |
| 		}
 | |
| 
 | |
| 		err := unix.Setxattr(dir, opaqueXattrName, []byte{'y'}, 0)
 | |
| 		if err != nil {
 | |
| 			return false, fmt.Errorf("setxattr('%s', %s=y): %w", dir, opaqueXattrName, err)
 | |
| 		}
 | |
| 		// don't write the file itself
 | |
| 		return false, err
 | |
| 	}
 | |
| 
 | |
| 	// if a file was deleted and we are using overlay, we need to create a character device
 | |
| 	if strings.HasPrefix(base, WhiteoutPrefix) {
 | |
| 		originalBase := base[len(WhiteoutPrefix):]
 | |
| 		originalPath := filepath.Join(dir, originalBase)
 | |
| 
 | |
| 		if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
 | |
| 			return false, fmt.Errorf("failed to mknod('%s', S_IFCHR, 0): %w", originalPath, err)
 | |
| 		}
 | |
| 		if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
 | |
| 			return false, err
 | |
| 		}
 | |
| 
 | |
| 		// don't write the file itself
 | |
| 		return false, nil
 | |
| 	}
 | |
| 
 | |
| 	return true, nil
 | |
| }
 |