mirror of
https://github.com/rclone/rclone.git
synced 2025-01-11 00:40:03 +01:00
local: add --local-time-type to use mtime/atime/btime/ctime as the time
Fixes #7484
This commit is contained in:
parent
854a36c4ab
commit
ac6ba11d22
@ -36,6 +36,27 @@ const devUnset = 0xdeadbeefcafebabe // a d
|
|||||||
const linkSuffix = ".rclonelink" // The suffix added to a translated symbolic link
|
const linkSuffix = ".rclonelink" // The suffix added to a translated symbolic link
|
||||||
const useReadDir = (runtime.GOOS == "windows" || runtime.GOOS == "plan9") // these OSes read FileInfos directly
|
const useReadDir = (runtime.GOOS == "windows" || runtime.GOOS == "plan9") // these OSes read FileInfos directly
|
||||||
|
|
||||||
|
// timeType allows the user to choose what exactly ModTime() returns
|
||||||
|
type timeType = fs.Enum[timeTypeChoices]
|
||||||
|
|
||||||
|
const (
|
||||||
|
mTime timeType = iota
|
||||||
|
aTime
|
||||||
|
bTime
|
||||||
|
cTime
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeTypeChoices struct{}
|
||||||
|
|
||||||
|
func (timeTypeChoices) Choices() []string {
|
||||||
|
return []string{
|
||||||
|
mTime: "mtime",
|
||||||
|
aTime: "atime",
|
||||||
|
bTime: "btime",
|
||||||
|
cTime: "ctime",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Register with Fs
|
// Register with Fs
|
||||||
func init() {
|
func init() {
|
||||||
fsi := &fs.RegInfo{
|
fsi := &fs.RegInfo{
|
||||||
@ -213,6 +234,42 @@ when copying to a CIFS mount owned by another user. If this option is
|
|||||||
enabled, rclone will no longer update the modtime after copying a file.`,
|
enabled, rclone will no longer update the modtime after copying a file.`,
|
||||||
Default: false,
|
Default: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: "time_type",
|
||||||
|
Help: `Set what kind of time is returned.
|
||||||
|
|
||||||
|
Normally rclone does all operations on the mtime or Modification time.
|
||||||
|
|
||||||
|
If you set this flag then rclone will return the Modified time as whatever
|
||||||
|
you set here. So if you use "rclone lsl --local-time-type ctime" then
|
||||||
|
you will see ctimes in the listing.
|
||||||
|
|
||||||
|
If the OS doesn't support returning the time_type specified then rclone
|
||||||
|
will silently replace it with the modification time which all OSes support.
|
||||||
|
|
||||||
|
- mtime is supported by all OSes
|
||||||
|
- atime is supported on all OSes except: plan9, js
|
||||||
|
- btime is only supported on: Windows, macOS, freebsd, netbsd
|
||||||
|
- ctime is supported on all Oses except: Windows, plan9, js
|
||||||
|
|
||||||
|
Note that setting the time will still set the modified time so this is
|
||||||
|
only useful for reading.
|
||||||
|
`,
|
||||||
|
Default: mTime,
|
||||||
|
Advanced: true,
|
||||||
|
Examples: []fs.OptionExample{{
|
||||||
|
Value: mTime.String(),
|
||||||
|
Help: "The last modification time.",
|
||||||
|
}, {
|
||||||
|
Value: aTime.String(),
|
||||||
|
Help: "The last access time.",
|
||||||
|
}, {
|
||||||
|
Value: bTime.String(),
|
||||||
|
Help: "The creation time.",
|
||||||
|
}, {
|
||||||
|
Value: cTime.String(),
|
||||||
|
Help: "The last status change time.",
|
||||||
|
}},
|
||||||
}, {
|
}, {
|
||||||
Name: config.ConfigEncoding,
|
Name: config.ConfigEncoding,
|
||||||
Help: config.ConfigEncodingHelp,
|
Help: config.ConfigEncodingHelp,
|
||||||
@ -237,6 +294,7 @@ type Options struct {
|
|||||||
NoPreAllocate bool `config:"no_preallocate"`
|
NoPreAllocate bool `config:"no_preallocate"`
|
||||||
NoSparse bool `config:"no_sparse"`
|
NoSparse bool `config:"no_sparse"`
|
||||||
NoSetModTime bool `config:"no_set_modtime"`
|
NoSetModTime bool `config:"no_set_modtime"`
|
||||||
|
TimeType timeType `config:"time_type"`
|
||||||
Enc encoder.MultiEncoder `config:"encoding"`
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1132,7 +1190,7 @@ func (file *localOpenFile) Read(p []byte) (n int, err error) {
|
|||||||
if oldsize != fi.Size() {
|
if oldsize != fi.Size() {
|
||||||
return 0, fserrors.NoLowLevelRetryError(fmt.Errorf("can't copy - source file is being updated (size changed from %d to %d)", oldsize, fi.Size()))
|
return 0, fserrors.NoLowLevelRetryError(fmt.Errorf("can't copy - source file is being updated (size changed from %d to %d)", oldsize, fi.Size()))
|
||||||
}
|
}
|
||||||
if !oldtime.Equal(fi.ModTime()) {
|
if !oldtime.Equal(readTime(file.o.fs.opt.TimeType, fi)) {
|
||||||
return 0, fserrors.NoLowLevelRetryError(fmt.Errorf("can't copy - source file is being updated (mod time changed from %v to %v)", oldtime, fi.ModTime()))
|
return 0, fserrors.NoLowLevelRetryError(fmt.Errorf("can't copy - source file is being updated (mod time changed from %v to %v)", oldtime, fi.ModTime()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1428,7 +1486,7 @@ func (o *Object) setMetadata(info os.FileInfo) {
|
|||||||
}
|
}
|
||||||
o.fs.objectMetaMu.Lock()
|
o.fs.objectMetaMu.Lock()
|
||||||
o.size = info.Size()
|
o.size = info.Size()
|
||||||
o.modTime = info.ModTime()
|
o.modTime = readTime(o.fs.opt.TimeType, info)
|
||||||
o.mode = info.Mode()
|
o.mode = info.Mode()
|
||||||
o.fs.objectMetaMu.Unlock()
|
o.fs.objectMetaMu.Unlock()
|
||||||
// Read the size of the link.
|
// Read the size of the link.
|
||||||
|
@ -5,12 +5,31 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read the time specified from the os.FileInfo
|
||||||
|
func readTime(t timeType, fi os.FileInfo) time.Time {
|
||||||
|
stat, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
fs.Debugf(nil, "didn't return Stat_t as expected")
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case aTime:
|
||||||
|
return time.Unix(stat.Atimespec.Unix())
|
||||||
|
case bTime:
|
||||||
|
return time.Unix(stat.Birthtimespec.Unix())
|
||||||
|
case cTime:
|
||||||
|
return time.Unix(stat.Ctimespec.Unix())
|
||||||
|
}
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Read the metadata from the file into metadata where possible
|
// Read the metadata from the file into metadata where possible
|
||||||
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
||||||
info, err := o.fs.lstat(o.path)
|
info, err := o.fs.lstat(o.path)
|
||||||
|
@ -5,8 +5,10 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
@ -18,6 +20,22 @@ var (
|
|||||||
readMetadataFromFileFn func(o *Object, m *fs.Metadata) (err error)
|
readMetadataFromFileFn func(o *Object, m *fs.Metadata) (err error)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read the time specified from the os.FileInfo
|
||||||
|
func readTime(t timeType, fi os.FileInfo) time.Time {
|
||||||
|
stat, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
fs.Debugf(nil, "didn't return Stat_t as expected")
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case aTime:
|
||||||
|
return time.Unix(stat.Atim.Unix())
|
||||||
|
case cTime:
|
||||||
|
return time.Unix(stat.Ctim.Unix())
|
||||||
|
}
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Read the metadata from the file into metadata where possible
|
// Read the metadata from the file into metadata where possible
|
||||||
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
||||||
statxCheckOnce.Do(func() {
|
statxCheckOnce.Do(func() {
|
||||||
|
@ -5,10 +5,17 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read the time specified from the os.FileInfo
|
||||||
|
func readTime(t timeType, fi os.FileInfo) time.Time {
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Read the metadata from the file into metadata where possible
|
// Read the metadata from the file into metadata where possible
|
||||||
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
||||||
info, err := o.fs.lstat(o.path)
|
info, err := o.fs.lstat(o.path)
|
||||||
|
@ -5,12 +5,29 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read the time specified from the os.FileInfo
|
||||||
|
func readTime(t timeType, fi os.FileInfo) time.Time {
|
||||||
|
stat, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
fs.Debugf(nil, "didn't return Stat_t as expected")
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case aTime:
|
||||||
|
return time.Unix(stat.Atim.Unix())
|
||||||
|
case cTime:
|
||||||
|
return time.Unix(stat.Ctim.Unix())
|
||||||
|
}
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Read the metadata from the file into metadata where possible
|
// Read the metadata from the file into metadata where possible
|
||||||
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
||||||
info, err := o.fs.lstat(o.path)
|
info, err := o.fs.lstat(o.path)
|
||||||
|
@ -5,12 +5,29 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read the time specified from the os.FileInfo
|
||||||
|
func readTime(t timeType, fi os.FileInfo) time.Time {
|
||||||
|
stat, ok := fi.Sys().(*syscall.Win32FileAttributeData)
|
||||||
|
if !ok {
|
||||||
|
fs.Debugf(nil, "didn't return Win32FileAttributeData as expected")
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case aTime:
|
||||||
|
return time.Unix(0, stat.LastAccessTime.Nanoseconds())
|
||||||
|
case bTime:
|
||||||
|
return time.Unix(0, stat.CreationTime.Nanoseconds())
|
||||||
|
}
|
||||||
|
return fi.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Read the metadata from the file into metadata where possible
|
// Read the metadata from the file into metadata where possible
|
||||||
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
func (o *Object) readMetadataFromFile(m *fs.Metadata) (err error) {
|
||||||
info, err := o.fs.lstat(o.path)
|
info, err := o.fs.lstat(o.path)
|
||||||
|
Loading…
Reference in New Issue
Block a user