seafile: don't implement IDer as the id is a content hash not a unique ID
Seafile returns a content-addressed id (a git-like hash of the file
contents) rather than a unique per-object identifier. Two different
files with identical contents are therefore given the same id, even on
different servers.
operations.SameObject assumes IDs uniquely identify an object, so when
syncing between Seafile remotes it treated two distinct but
identical-content files as the same object. The guard in NeedTransfer
if Equal(ctx, src, dst) && !SameObject(src, dst)
then evaluated to false for unchanged files (Equal was true because the
sizes matched, and SameObject was true because the ids matched), so
rclone re-uploaded them on every sync.
Seafile has no modtime and no usable content hash, so dropping the IDer
implementation makes SameObject fall back to path comparison and lets
unchanged files be skipped correctly.
Fixes #8300
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Nick Craig-Wood
co-authored by
Claude Fable 5
parent
ed9eec0f20
commit
f2e43b7c14
@@ -12,7 +12,6 @@ import (
|
|||||||
// Object describes a seafile object (also commonly called a file)
|
// Object describes a seafile object (also commonly called a file)
|
||||||
type Object struct {
|
type Object struct {
|
||||||
fs *Fs // what this object is part of
|
fs *Fs // what this object is part of
|
||||||
id string // internal ID of object
|
|
||||||
remote string // The remote path (full path containing library name if target at root)
|
remote string // The remote path (full path containing library name if target at root)
|
||||||
pathInLibrary string // Path of the object without the library name
|
pathInLibrary string // Path of the object without the library name
|
||||||
size int64 // size of the object
|
size int64 // size of the object
|
||||||
@@ -107,7 +106,6 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
}
|
}
|
||||||
// Set the properties from the upload back to the object
|
// Set the properties from the upload back to the object
|
||||||
o.size = uploaded.Size
|
o.size = uploaded.Size
|
||||||
o.id = uploaded.ID
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -118,10 +116,3 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
func (o *Object) Remove(ctx context.Context) error {
|
func (o *Object) Remove(ctx context.Context) error {
|
||||||
return o.fs.deleteFile(ctx, o.libraryID, o.pathInLibrary)
|
return o.fs.deleteFile(ctx, o.libraryID, o.pathInLibrary)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Optional Interface fs.IDer ====================
|
|
||||||
|
|
||||||
// ID returns the ID of the Object if known, or "" if not
|
|
||||||
func (o *Object) ID() string {
|
|
||||||
return o.id
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -550,7 +550,6 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
|
|||||||
o := &Object{
|
o := &Object{
|
||||||
fs: f,
|
fs: f,
|
||||||
libraryID: libraryID,
|
libraryID: libraryID,
|
||||||
id: fileDetails.ID,
|
|
||||||
remote: remote,
|
remote: remote,
|
||||||
pathInLibrary: filePath,
|
pathInLibrary: filePath,
|
||||||
modTime: modTime,
|
modTime: modTime,
|
||||||
@@ -1239,7 +1238,6 @@ func (f *Fs) buildDirEntries(parentPath, libraryID, parentPathInLibrary string,
|
|||||||
} else if entry.Type == api.FileTypeFile {
|
} else if entry.Type == api.FileTypeFile {
|
||||||
object := &Object{
|
object := &Object{
|
||||||
fs: f,
|
fs: f,
|
||||||
id: entry.ID,
|
|
||||||
remote: filePath,
|
remote: filePath,
|
||||||
pathInLibrary: filePathInLibrary,
|
pathInLibrary: filePathInLibrary,
|
||||||
size: entry.Size,
|
size: entry.Size,
|
||||||
@@ -1359,5 +1357,4 @@ var (
|
|||||||
_ fs.UserInfoer = &Fs{}
|
_ fs.UserInfoer = &Fs{}
|
||||||
_ fs.Shutdowner = &Fs{}
|
_ fs.Shutdowner = &Fs{}
|
||||||
_ fs.Object = &Object{}
|
_ fs.Object = &Object{}
|
||||||
_ fs.IDer = &Object{}
|
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user