This adds support for syncing extended attributes on supported filesystem on Linux, macOS, FreeBSD and NetBSD. Windows is currently excluded because the APIs seem onerous and annoying and frankly the uses cases seem few and far between. On Unixes this also covers ACLs as those are stored as extended attributes. Similar to ownership syncing this will optional & opt-in, which two settings controlling the main behavior: one to "sync" xattrs (read & write) and another one to "scan" xattrs (only read them so other devices can "sync" them, but not apply any locally). Co-authored-by: Tomasz Wilczyński <twilczynski@naver.com>
This commit is contained in:
co-authored by
Tomasz Wilczyński
parent
8065cf7e97
commit
6cac308bcd
+834
-194
File diff suppressed because it is too large
Load Diff
@@ -44,6 +44,8 @@ type FileIntf interface {
|
||||
FilePermissions() uint32
|
||||
FileModifiedBy() ShortID
|
||||
ModTime() time.Time
|
||||
PlatformData() PlatformData
|
||||
InodeChangeTime() time.Time
|
||||
}
|
||||
|
||||
func (Hello) Magic() uint32 {
|
||||
@@ -160,6 +162,14 @@ func (f FileInfo) FileModifiedBy() ShortID {
|
||||
return f.ModifiedBy
|
||||
}
|
||||
|
||||
func (f FileInfo) PlatformData() PlatformData {
|
||||
return f.Platform
|
||||
}
|
||||
|
||||
func (f FileInfo) InodeChangeTime() time.Time {
|
||||
return time.Unix(0, f.InodeChangeNs)
|
||||
}
|
||||
|
||||
// WinsConflict returns true if "f" is the one to choose when it is in
|
||||
// conflict with "other".
|
||||
func WinsConflict(f, other FileIntf) bool {
|
||||
@@ -196,6 +206,7 @@ type FileInfoComparison struct {
|
||||
IgnoreBlocks bool
|
||||
IgnoreFlags uint32
|
||||
IgnoreOwnership bool
|
||||
IgnoreXattrs bool
|
||||
}
|
||||
|
||||
func (f FileInfo) IsEquivalent(other FileInfo, modTimeWindow time.Duration) bool {
|
||||
@@ -233,6 +244,12 @@ func (f FileInfo) isEquivalent(other FileInfo, comp FileInfoComparison) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we are recording inode change times and it changed, they are not
|
||||
// equal.
|
||||
if f.InodeChangeNs != 0 && other.InodeChangeNs != 0 && f.InodeChangeNs != other.InodeChangeNs {
|
||||
return false
|
||||
}
|
||||
|
||||
// Mask out the ignored local flags before checking IsInvalid() below
|
||||
f.LocalFlags &^= comp.IgnoreFlags
|
||||
other.LocalFlags &^= comp.IgnoreFlags
|
||||
@@ -252,11 +269,26 @@ func (f FileInfo) isEquivalent(other FileInfo, comp FileInfoComparison) bool {
|
||||
}
|
||||
}
|
||||
if f.Platform.Windows != nil && other.Platform.Windows != nil {
|
||||
if *f.Platform.Windows != *other.Platform.Windows {
|
||||
if f.Platform.Windows.OwnerName != other.Platform.Windows.OwnerName ||
|
||||
f.Platform.Windows.OwnerIsGroup != other.Platform.Windows.OwnerIsGroup {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if !comp.IgnoreXattrs && f.Platform != other.Platform {
|
||||
if !xattrsEqual(f.Platform.Linux, other.Platform.Linux) {
|
||||
return false
|
||||
}
|
||||
if !xattrsEqual(f.Platform.Darwin, other.Platform.Darwin) {
|
||||
return false
|
||||
}
|
||||
if !xattrsEqual(f.Platform.FreeBSD, other.Platform.FreeBSD) {
|
||||
return false
|
||||
}
|
||||
if !xattrsEqual(f.Platform.NetBSD, other.Platform.NetBSD) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if !comp.IgnorePerms && !f.NoPermissions && !other.NoPermissions && !PermsEqual(f.Permissions, other.Permissions) {
|
||||
return false
|
||||
@@ -308,6 +340,76 @@ func (f FileInfo) BlocksEqual(other FileInfo) bool {
|
||||
return blocksEqual(f.Blocks, other.Blocks)
|
||||
}
|
||||
|
||||
// Xattrs is a convenience method to return the extended attributes of the
|
||||
// file for the current platform.
|
||||
func (f *PlatformData) Xattrs() []Xattr {
|
||||
switch {
|
||||
case build.IsLinux && f.Linux != nil:
|
||||
return f.Linux.Xattrs
|
||||
case build.IsDarwin && f.Darwin != nil:
|
||||
return f.Darwin.Xattrs
|
||||
case build.IsFreeBSD && f.FreeBSD != nil:
|
||||
return f.FreeBSD.Xattrs
|
||||
case build.IsNetBSD && f.NetBSD != nil:
|
||||
return f.NetBSD.Xattrs
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetXattrs is a convenience method to set the extended attributes of the
|
||||
// file for the current platform.
|
||||
func (p *PlatformData) SetXattrs(xattrs []Xattr) {
|
||||
switch {
|
||||
case build.IsLinux:
|
||||
if p.Linux == nil {
|
||||
p.Linux = &XattrData{}
|
||||
}
|
||||
p.Linux.Xattrs = xattrs
|
||||
|
||||
case build.IsDarwin:
|
||||
if p.Darwin == nil {
|
||||
p.Darwin = &XattrData{}
|
||||
}
|
||||
p.Darwin.Xattrs = xattrs
|
||||
|
||||
case build.IsFreeBSD:
|
||||
if p.FreeBSD == nil {
|
||||
p.FreeBSD = &XattrData{}
|
||||
}
|
||||
p.FreeBSD.Xattrs = xattrs
|
||||
|
||||
case build.IsNetBSD:
|
||||
if p.NetBSD == nil {
|
||||
p.NetBSD = &XattrData{}
|
||||
}
|
||||
p.NetBSD.Xattrs = xattrs
|
||||
}
|
||||
}
|
||||
|
||||
// MergeWith copies platform data from other, for platforms where it's not
|
||||
// already set on p.
|
||||
func (p *PlatformData) MergeWith(other *PlatformData) {
|
||||
if p.Unix == nil {
|
||||
p.Unix = other.Unix
|
||||
}
|
||||
if p.Windows == nil {
|
||||
p.Windows = other.Windows
|
||||
}
|
||||
if p.Linux == nil {
|
||||
p.Linux = other.Linux
|
||||
}
|
||||
if p.Darwin == nil {
|
||||
p.Darwin = other.Darwin
|
||||
}
|
||||
if p.FreeBSD == nil {
|
||||
p.FreeBSD = other.FreeBSD
|
||||
}
|
||||
if p.NetBSD == nil {
|
||||
p.NetBSD = other.NetBSD
|
||||
}
|
||||
}
|
||||
|
||||
// blocksEqual returns whether two slices of blocks are exactly the same hash
|
||||
// and index pair wise.
|
||||
func blocksEqual(a, b []BlockInfo) bool {
|
||||
@@ -438,3 +540,23 @@ func (x *FileInfoType) UnmarshalJSON(data []byte) error {
|
||||
*x = FileInfoType(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
func xattrsEqual(a, b *XattrData) bool {
|
||||
if a == nil || b == nil {
|
||||
// Having no data on either side means we have nothing to compare
|
||||
// to, and we consider that equal.
|
||||
return true
|
||||
}
|
||||
if len(a.Xattrs) != len(b.Xattrs) {
|
||||
return false
|
||||
}
|
||||
for i := range a.Xattrs {
|
||||
if a.Xattrs[i].Name != b.Xattrs[i].Name {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(a.Xattrs[i].Value, b.Xattrs[i].Value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user