chore: remove tracking inode change time (#10579)

Signed-off-by: Jakob Borg <jakob@kastelo.net>
This commit is contained in:
Jakob Borg
2026-03-22 20:24:03 -07:00
committed by GitHub
parent 3b05ba2a8f
commit b39c56f82d
15 changed files with 266 additions and 414 deletions
-1
View File
@@ -1786,7 +1786,6 @@ func fileIntfJSONMap(f protocol.FileInfo) map[string]interface{} {
"version": jsonVersionVector(f.FileVersion()),
"localFlags": f.FileLocalFlags(),
"platform": f.PlatformData(),
"inodeChange": f.InodeChangeTime(),
"blocksHash": f.FileBlocksHash(),
"previousBlocksHash": f.PreviousBlocksHash,
}
-22
View File
@@ -1,22 +0,0 @@
// Copyright (C) 2022 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
//go:build darwin || freebsd || netbsd
// +build darwin freebsd netbsd
package fs
import (
"syscall"
"time"
)
func (fi basicFileInfo) InodeChangeTime() time.Time {
if sys, ok := fi.Sys().(*syscall.Stat_t); ok {
return time.Unix(0, sys.Ctimespec.Nano())
}
return time.Time{}
}
-30
View File
@@ -1,30 +0,0 @@
// Copyright (C) 2022 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
//go:build aix || dragonfly || linux || openbsd || solaris || illumos
// +build aix dragonfly linux openbsd solaris illumos
package fs
import (
"syscall"
"time"
"github.com/syncthing/syncthing/lib/build"
)
func (fi basicFileInfo) InodeChangeTime() time.Time {
// On Android, mtime and inode-change-time fluctuate, which can cause
// conflicts even when nothing has been modified on the device itself.
// Ref: https://forum.syncthing.net/t/keep-getting-conflicts-generated-on-android-device-for-files-modified-only-on-a-desktop-pc/19060
if build.IsAndroid {
return time.Time{}
}
if sys, ok := fi.FileInfo.Sys().(*syscall.Stat_t); ok {
return time.Unix(0, sys.Ctim.Nano())
}
return time.Time{}
}
-5
View File
@@ -10,7 +10,6 @@ import (
"os"
"path/filepath"
"strings"
"time"
)
var execExts map[string]bool
@@ -58,10 +57,6 @@ func (e basicFileInfo) Group() int {
return -1
}
func (basicFileInfo) InodeChangeTime() time.Time {
return time.Time{}
}
// osFileInfo converts e to os.FileInfo that is suitable
// to be passed to os.SameFile.
func (e *basicFileInfo) osFileInfo() os.FileInfo {
-4
View File
@@ -1024,7 +1024,3 @@ func (f *fakeFileInfo) Group() int {
func (*fakeFileInfo) Sys() interface{} {
return nil
}
func (*fakeFileInfo) InodeChangeTime() time.Time {
return time.Time{}
}
-1
View File
@@ -101,7 +101,6 @@ type FileInfo interface {
IsSymlink() bool
Owner() int
Group() int
InodeChangeTime() time.Time // may be zero if not supported
}
// FileMode is similar to os.FileMode
-29
View File
@@ -1789,19 +1789,6 @@ loop:
lastFile = job.file
}
if !job.file.IsDeleted() && !job.file.IsInvalid() {
// Now that the file is finalized, grab possibly updated
// inode change time from disk into the local FileInfo. We
// use this change time to check for changes to xattrs etc
// on next scan.
if err := f.updateFileInfoChangeTime(&job.file); err != nil {
// This means on next scan the likely incorrect change time
// (resp. whatever caused the error) will cause this file to
// change. Log at info level to leave a trace if a user
// notices, but no need to warn
f.sl.Warn("Failed to update metadata at database commit", slogutil.FilePath(job.file.Name), slogutil.Error(err))
}
}
job.file.Sequence = 0
batch.Append(job.file)
@@ -2187,22 +2174,6 @@ func (f *sendReceiveFolder) withLimiter(ctx context.Context, fn func() error) er
return fn()
}
// updateFileInfoChangeTime updates the inode change time in the FileInfo,
// because that depends on the current, new, state of the file on disk.
func (f *sendReceiveFolder) updateFileInfoChangeTime(file *protocol.FileInfo) error {
info, err := f.mtimefs.Lstat(file.Name)
if err != nil {
return err
}
if ct := info.InodeChangeTime(); !ct.IsZero() {
file.InodeChangeNs = ct.UnixNano()
} else {
file.InodeChangeNs = 0
}
return nil
}
// A []FileError is sent as part of an event and will be JSON serialized.
type FileError struct {
Path string `json:"path"`
-3
View File
@@ -647,9 +647,6 @@ func TestDeleteIgnorePerms(t *testing.T) {
fi, err := scanner.CreateFileInfo(stat, name, ffs, false, false, config.XattrFilter{})
must(t, err)
ffs.Chmod(name, 0o600)
if info, err := ffs.Stat(name); err == nil {
fi.InodeChangeNs = info.InodeChangeTime().UnixNano()
}
scanChan := make(chan string, 1)
err = f.checkToBeDeleted(fi, fi, true, scanChan)
must(t, err)
+6 -24
View File
@@ -139,10 +139,6 @@ type FileInfo struct {
// the protocol.
LocalFlags FlagLocal
// The time when the inode was last changed (i.e., permissions, xattrs
// etc changed). This is host-local, not sent over the wire.
InodeChangeNs int64
// The size of the data appended to the encrypted file on disk. This is
// host-local, not sent over the wire.
EncryptionTrailerSize int
@@ -184,7 +180,6 @@ func (f *FileInfo) ToWire(withInternalFields bool) *bep.FileInfo {
}
if withInternalFields {
w.LocalFlags = uint32(f.LocalFlags)
w.InodeChangeNs = f.InodeChangeNs
w.EncryptionTrailerSize = int32(f.EncryptionTrailerSize)
}
return w
@@ -284,7 +279,6 @@ type FileInfoWithoutBlocks interface {
GetBlockSize() int32
GetPlatform() *bep.PlatformData
GetLocalFlags() uint32
GetInodeChangeNs() int64
GetEncryptionTrailerSize() int32
GetDeleted() bool
GetInvalid() bool
@@ -322,7 +316,6 @@ func fileInfoFromWireWithBlocks(w FileInfoWithoutBlocks, blocks []BlockInfo) Fil
func FileInfoFromDB(w *bep.FileInfo) FileInfo {
f := FileInfoFromWire(w)
f.LocalFlags = FlagLocal(w.LocalFlags)
f.InodeChangeNs = w.InodeChangeNs
f.EncryptionTrailerSize = int(w.EncryptionTrailerSize)
return f
}
@@ -330,7 +323,6 @@ func FileInfoFromDB(w *bep.FileInfo) FileInfo {
func FileInfoFromDBTruncated(w FileInfoWithoutBlocks) FileInfo {
f := fileInfoFromWireWithBlocks(w, nil)
f.LocalFlags = FlagLocal(w.GetLocalFlags())
f.InodeChangeNs = w.GetInodeChangeNs()
f.EncryptionTrailerSize = int(w.GetEncryptionTrailerSize())
f.truncated = true
return f
@@ -339,14 +331,14 @@ func FileInfoFromDBTruncated(w FileInfoWithoutBlocks) FileInfo {
func (f FileInfo) String() string {
switch f.Type {
case FileInfoTypeDirectory:
return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.Platform, f.InodeChangeTime())
return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, Platform:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.Platform)
case FileInfoTypeFile:
return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d, NumBlocks:%d, BlocksHash:%x, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.RawBlockSize, len(f.Blocks), f.BlocksHash, f.Platform, f.InodeChangeTime())
return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d, NumBlocks:%d, BlocksHash:%x, Platform:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.RawBlockSize, len(f.Blocks), f.BlocksHash, f.Platform)
case FileInfoTypeSymlink, FileInfoTypeSymlinkDirectory, FileInfoTypeSymlinkFile:
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.SymlinkTarget, f.Platform, f.InodeChangeTime())
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q, Platform:%v}",
f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.SymlinkTarget, f.Platform)
default:
panic("mystery file type detected")
}
@@ -462,10 +454,6 @@ func (f FileInfo) PlatformData() PlatformData {
return f.Platform
}
func (f FileInfo) InodeChangeTime() time.Time {
return time.Unix(0, f.InodeChangeNs)
}
func (f FileInfo) FileBlocksHash() []byte {
return f.BlocksHash
}
@@ -514,12 +502,6 @@ func (f FileInfo) isEquivalent(other FileInfo, comp FileInfoComparison) bool {
return false
}
// If we care about either ownership or xattrs, are recording inode change
// times and it changed, they are not equal.
if !(comp.IgnoreOwnership && comp.IgnoreXattrs) && 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
+5 -6
View File
@@ -121,12 +121,11 @@ func (fakeInfo) ModTime() time.Time { return time.Unix(1234567890, 0) }
func (f fakeInfo) IsDir() bool {
return strings.Contains(filepath.Base(f.name), "dir") || f.name == "."
}
func (f fakeInfo) IsRegular() bool { return !f.IsDir() }
func (fakeInfo) IsSymlink() bool { return false }
func (fakeInfo) Owner() int { return 0 }
func (fakeInfo) Group() int { return 0 }
func (fakeInfo) Sys() interface{} { return nil }
func (fakeInfo) InodeChangeTime() time.Time { return time.Time{} }
func (f fakeInfo) IsRegular() bool { return !f.IsDir() }
func (fakeInfo) IsSymlink() bool { return false }
func (fakeInfo) Owner() int { return 0 }
func (fakeInfo) Group() int { return 0 }
func (fakeInfo) Sys() interface{} { return nil }
type fakeFile struct {
name string
-6
View File
@@ -740,12 +740,6 @@ func CreateFileInfo(fi fs.FileInfo, name string, filesystem fs.Filesystem, scanO
}
}
if ct := fi.InodeChangeTime(); !ct.IsZero() {
f.InodeChangeNs = ct.UnixNano()
} else {
f.InodeChangeNs = 0
}
if fi.IsSymlink() {
f.Type = protocol.FileInfoTypeSymlink
target, err := filesystem.ReadSymlink(name)