1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
package archive
import (
"archive/tar"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/system"
"golang.org/x/sys/unix"
)
func getOverlayOpaqueXattrName() string {
return GetOverlayXattrName("opaque")
}
func GetWhiteoutConverter(format WhiteoutFormat, data interface{}) TarWhiteoutConverter {
if format == OverlayWhiteoutFormat {
if rolayers, ok := data.([]string); ok && len(rolayers) > 0 {
return overlayWhiteoutConverter{rolayers: rolayers}
}
return overlayWhiteoutConverter{rolayers: nil}
}
return nil
}
type overlayWhiteoutConverter struct {
rolayers []string
}
func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err 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 = 0
hdr.Typeflag = tar.TypeReg
hdr.Size = 0
}
if fi.Mode()&os.ModeDir != 0 {
// convert opaque dirs to AUFS format by writing an empty file with the whiteout prefix
opaque, err := system.Lgetxattr(path, getOverlayOpaqueXattrName())
if err != nil {
return nil, err
}
if len(opaque) == 1 && opaque[0] == 'y' {
if hdr.Xattrs != nil {
delete(hdr.Xattrs, getOverlayOpaqueXattrName())
}
// If there are no lower layers, then it can't have been deleted in this layer.
if len(o.rolayers) == 0 {
return nil, nil
}
// At this point, we have a directory that's opaque. If it appears in one of the lower
// layers, then it was newly-created here, so it wasn't also deleted here.
for _, rolayer := range o.rolayers {
stat, statErr := os.Stat(filepath.Join(rolayer, hdr.Name))
if statErr != nil && !os.IsNotExist(statErr) && !isENOTDIR(statErr) {
// Not sure what happened here.
return nil, statErr
}
if statErr == nil {
if stat.Mode()&os.ModeCharDevice != 0 {
if isWhiteOut(stat) {
return nil, nil
}
}
// It's not whiteout, so it was there in the older layer, so we need to
// add a whiteout for this item in this layer.
// create a header for the whiteout file
// it should inherit some properties from the parent, but be a regular file
wo = &tar.Header{
Typeflag: tar.TypeReg,
Mode: hdr.Mode & int64(os.ModePerm),
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir),
Size: 0,
Uid: hdr.Uid,
Uname: hdr.Uname,
Gid: hdr.Gid,
Gname: hdr.Gname,
AccessTime: hdr.AccessTime,
ChangeTime: hdr.ChangeTime,
}
break
}
for dir := filepath.Dir(hdr.Name); dir != "" && dir != "." && dir != string(os.PathSeparator); dir = filepath.Dir(dir) {
// Check for whiteout for a parent directory in a parent layer.
stat, statErr := os.Stat(filepath.Join(rolayer, dir))
if statErr != nil && !os.IsNotExist(statErr) && !isENOTDIR(statErr) {
// Not sure what happened here.
return nil, statErr
}
if statErr == nil {
if stat.Mode()&os.ModeCharDevice != 0 {
// If it's whiteout for a parent directory, then the
// original directory wasn't inherited into this layer,
// so we don't need to emit whiteout for it.
if isWhiteOut(stat) {
return nil, nil
}
}
}
}
}
}
}
return
}
func (overlayWhiteoutConverter) ConvertReadWithHandler(hdr *tar.Header, path string, handler TarWhiteoutHandler) (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 {
err := handler.Setxattr(dir, getOverlayOpaqueXattrName(), []byte{'y'})
// 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 := handler.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
// If someone does:
// rm -rf /foo/bar
// in an image, some tools will generate a layer with:
// /.wh.foo
// /foo/.wh.bar
// and when doing the second mknod(), we will fail with
// ENOTDIR, since the previous /foo was mknod()'d as a
// character device node and not a directory.
if isENOTDIR(err) {
return false, nil
}
return false, err
}
if err := handler.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
return false, err
}
// don't write the file itself
return false, nil
}
return true, nil
}
type directHandler struct {
}
func (d directHandler) Setxattr(path, name string, value []byte) error {
return unix.Setxattr(path, name, value, 0)
}
func (d directHandler) Mknod(path string, mode uint32, dev int) error {
return unix.Mknod(path, mode, dev)
}
func (d directHandler) Chown(path string, uid, gid int) error {
return idtools.SafeChown(path, uid, gid)
}
func (o overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
var handler directHandler
return o.ConvertReadWithHandler(hdr, path, handler)
}
func isWhiteOut(stat os.FileInfo) bool {
s := stat.Sys().(*syscall.Stat_t)
return major(uint64(s.Rdev)) == 0 && minor(uint64(s.Rdev)) == 0
}
func GetFileOwner(path string) (uint32, uint32, uint32, error) {
f, err := os.Stat(path)
if err != nil {
return 0, 0, 0, err
}
s, ok := f.Sys().(*syscall.Stat_t)
if ok {
return s.Uid, s.Gid, s.Mode & 07777, nil
}
return 0, 0, uint32(f.Mode()), nil
}
|