Skip to content

Commit

Permalink
Merge pull request #138 from AkihiroSuda/xattr
Browse files Browse the repository at this point in the history
fs: add WithAllowXAttrErrors CopyOpt
  • Loading branch information
estesp authored Dec 3, 2018
2 parents bea7585 + a664d80 commit 004b464
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 17 deletions.
45 changes: 40 additions & 5 deletions fs/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,49 @@ var bufferPool = &sync.Pool{
},
}

// XAttrErrorHandlers transform a non-nil xattr error.
// Return nil to ignore an error.
// xattrKey can be empty for listxattr operation.
type XAttrErrorHandler func(dst, src, xattrKey string, err error) error

type copyDirOpts struct {
xeh XAttrErrorHandler
}

type CopyDirOpt func(*copyDirOpts) error

// WithXAttrErrorHandler allows specifying XAttrErrorHandler
// If nil XAttrErrorHandler is specified (default), CopyDir stops
// on a non-nil xattr error.
func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt {
return func(o *copyDirOpts) error {
o.xeh = xeh
return nil
}
}

// WithAllowXAttrErrors allows ignoring xattr errors.
func WithAllowXAttrErrors() CopyDirOpt {
xeh := func(dst, src, xattrKey string, err error) error {
return nil
}
return WithXAttrErrorHandler(xeh)
}

// CopyDir copies the directory from src to dst.
// Most efficient copy of files is attempted.
func CopyDir(dst, src string) error {
func CopyDir(dst, src string, opts ...CopyDirOpt) error {
var o copyDirOpts
for _, opt := range opts {
if err := opt(&o); err != nil {
return err
}
}
inodes := map[uint64]string{}
return copyDirectory(dst, src, inodes)
return copyDirectory(dst, src, inodes, &o)
}

func copyDirectory(dst, src string, inodes map[uint64]string) error {
func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
stat, err := os.Stat(src)
if err != nil {
return errors.Wrapf(err, "failed to stat %s", src)
Expand Down Expand Up @@ -75,7 +110,7 @@ func copyDirectory(dst, src string, inodes map[uint64]string) error {

switch {
case fi.IsDir():
if err := copyDirectory(target, source, inodes); err != nil {
if err := copyDirectory(target, source, inodes, o); err != nil {
return err
}
continue
Expand Down Expand Up @@ -111,7 +146,7 @@ func copyDirectory(dst, src string, inodes map[uint64]string) error {
return errors.Wrap(err, "failed to copy file info")
}

if err := copyXAttrs(target, source); err != nil {
if err := copyXAttrs(target, source, o.xeh); err != nil {
return errors.Wrap(err, "failed to copy xattrs")
}
}
Expand Down
30 changes: 23 additions & 7 deletions fs/copy_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ func copyFileContent(dst, src *os.File) error {
dstFd := int(dst.Fd())

for size > 0 {
// Ensure that we are never trying to copy more than SSIZE_MAX at a
// time and at the same time avoids overflows when the file is larger
// than 4GB on 32-bit systems.
// Ensure that we are never trying to copy more than SSIZE_MAX at a
// time and at the same time avoids overflows when the file is larger
// than 4GB on 32-bit systems.
var copySize int
if size > maxSSizeT {
copySize = int(maxSSizeT)
Expand All @@ -101,18 +101,34 @@ func copyFileContent(dst, src *os.File) error {
return nil
}

func copyXAttrs(dst, src string) error {
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
xattrKeys, err := sysx.LListxattr(src)
if err != nil {
return errors.Wrapf(err, "failed to list xattrs on %s", src)
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
if xeh != nil {
e = xeh(dst, src, "", e)
}
return e
}
for _, xattr := range xattrKeys {
data, err := sysx.LGetxattr(src, xattr)
if err != nil {
return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
continue
}
}
return e
}
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
continue
}
}
return e
}
}

Expand Down
24 changes: 20 additions & 4 deletions fs/copy_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,34 @@ func copyFileContent(dst, src *os.File) error {
return err
}

func copyXAttrs(dst, src string) error {
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
xattrKeys, err := sysx.LListxattr(src)
if err != nil {
return errors.Wrapf(err, "failed to list xattrs on %s", src)
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
if xeh != nil {
e = xeh(dst, src, "", e)
}
return e
}
for _, xattr := range xattrKeys {
data, err := sysx.LGetxattr(src, xattr)
if err != nil {
return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
continue
}
}
return e
}
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
continue
}
}
return e
}
}

Expand Down
2 changes: 1 addition & 1 deletion fs/copy_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func copyFileContent(dst, src *os.File) error {
return err
}

func copyXAttrs(dst, src string) error {
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
return nil
}

Expand Down

0 comments on commit 004b464

Please sign in to comment.