diff --git a/archive/archive.go b/archive/archive.go index 16c01993b7..8bb0a8ed1c 100644 --- a/archive/archive.go +++ b/archive/archive.go @@ -165,6 +165,13 @@ func addTarFile(path, name string, tw *tar.Writer) error { hdr.Devmajor = int64(major(uint64(stat.Rdev))) hdr.Devminor = int64(minor(uint64(stat.Rdev))) } + + } + + capability, _ := Lgetxattr(path, "security.capability") + if capability != nil { + hdr.Xattrs = make(map[string]string) + hdr.Xattrs["security.capability"] = string(capability) } if err := tw.WriteHeader(hdr); err != nil { @@ -251,6 +258,12 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader) e return err } + for key, value := range hdr.Xattrs { + if err := Lsetxattr(path, key, []byte(value), 0); err != nil { + return err + } + } + // There is no LChmod, so ignore mode for symlink. Also, this // must happen after chown, as that can modify the file mode if hdr.Typeflag != tar.TypeSymlink { diff --git a/archive/changes.go b/archive/changes.go index b46b13bbe7..a9eba8196f 100644 --- a/archive/changes.go +++ b/archive/changes.go @@ -1,6 +1,7 @@ package archive import ( + "bytes" "code.google.com/p/go/src/pkg/archive/tar" "fmt" "github.com/dotcloud/docker/utils" @@ -126,10 +127,11 @@ func Changes(layers []string, rw string) ([]Change, error) { } type FileInfo struct { - parent *FileInfo - name string - stat syscall.Stat_t - children map[string]*FileInfo + parent *FileInfo + name string + stat syscall.Stat_t + children map[string]*FileInfo + capability []byte } func (root *FileInfo) LookUp(path string) *FileInfo { @@ -200,7 +202,8 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { oldStat.Rdev != newStat.Rdev || // Don't look at size for dirs, its not a good measure of change (oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) || - !sameFsTimeSpec(getLastModification(oldStat), getLastModification(newStat)) { + !sameFsTimeSpec(getLastModification(oldStat), getLastModification(newStat)) || + bytes.Compare(oldChild.capability, newChild.capability) != 0 { change := Change{ Path: newChild.path(), Kind: ChangeModify, @@ -275,6 +278,8 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) { return err } + info.capability, _ = Lgetxattr(path, "security.capability") + parent.children[info.name] = info return nil diff --git a/archive/stat_linux.go b/archive/stat_linux.go index f87a99c55a..2910ce5bab 100644 --- a/archive/stat_linux.go +++ b/archive/stat_linux.go @@ -37,3 +37,56 @@ func UtimesNano(path string, ts []syscall.Timespec) error { } return nil } + +// Returns a nil slice and nil error if the xattr is not set +func Lgetxattr(path string, attr string) ([]byte, error) { + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return nil, err + } + attrBytes, err := syscall.BytePtrFromString(attr) + if err != nil { + return nil, err + } + + dest := make([]byte, 128) + destBytes := unsafe.Pointer(&dest[0]) + sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) + if errno == syscall.ENODATA { + return nil, nil + } + if errno == syscall.ERANGE { + dest = make([]byte, sz) + destBytes := unsafe.Pointer(&dest[0]) + sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) + } + if errno != 0 { + return nil, errno + } + + return dest[:sz], nil +} + +var _zero uintptr + +func Lsetxattr(path string, attr string, data []byte, flags int) error { + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + attrBytes, err := syscall.BytePtrFromString(attr) + if err != nil { + return err + } + var dataBytes unsafe.Pointer + if len(data) > 0 { + dataBytes = unsafe.Pointer(&data[0]) + } else { + dataBytes = unsafe.Pointer(&_zero) + } + _, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/archive/stat_unsupported.go b/archive/stat_unsupported.go index 50ca461867..99f1fbc7da 100644 --- a/archive/stat_unsupported.go +++ b/archive/stat_unsupported.go @@ -19,3 +19,11 @@ func LUtimesNano(path string, ts []syscall.Timespec) error { func UtimesNano(path string, ts []syscall.Timespec) error { return ErrNotImplemented } + +func Lgetxattr(path string, attr string) ([]byte, error) { + return nil, ErrNotImplemented +} + +func Lsetxattr(path string, attr string, data []byte, flags int) error { + return ErrNotImplemented +}