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:
TowyTowy
2026-07-09 17:50:26 +01:00
committed by Nick Craig-Wood
co-authored by Claude Fable 5
parent ed9eec0f20
commit f2e43b7c14
2 changed files with 0 additions and 12 deletions
-9
View File
@@ -12,7 +12,6 @@ import (
// Object describes a seafile object (also commonly called a file)
type Object struct {
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)
pathInLibrary string // Path of the object without the library name
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
o.size = uploaded.Size
o.id = uploaded.ID
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 {
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
}
-3
View File
@@ -550,7 +550,6 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
o := &Object{
fs: f,
libraryID: libraryID,
id: fileDetails.ID,
remote: remote,
pathInLibrary: filePath,
modTime: modTime,
@@ -1239,7 +1238,6 @@ func (f *Fs) buildDirEntries(parentPath, libraryID, parentPathInLibrary string,
} else if entry.Type == api.FileTypeFile {
object := &Object{
fs: f,
id: entry.ID,
remote: filePath,
pathInLibrary: filePathInLibrary,
size: entry.Size,
@@ -1359,5 +1357,4 @@ var (
_ fs.UserInfoer = &Fs{}
_ fs.Shutdowner = &Fs{}
_ fs.Object = &Object{}
_ fs.IDer = &Object{}
)