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 }