diff --git a/local/local.go b/local/local.go index 1f0ca46d6..698c61467 100644 --- a/local/local.go +++ b/local/local.go @@ -66,11 +66,13 @@ type Fs struct { // Object represents a local filesystem object type Object struct { - fs *Fs // The Fs this object is part of - remote string // The remote path - properly UTF-8 encoded - for rclone - path string // The local path - may not be properly UTF-8 encoded - for OS - info os.FileInfo // Interface for file info (always present) - hashes map[fs.HashType]string // Hashes + fs *Fs // The Fs this object is part of + remote string // The remote path - properly UTF-8 encoded - for rclone + path string // The local path - may not be properly UTF-8 encoded - for OS + size int64 // file metadata - always present + mode os.FileMode + modTime time.Time + hashes map[fs.HashType]string // Hashes } // ------------------------------------------------------------ @@ -159,7 +161,7 @@ func (f *Fs) newObject(remote, dstPath string) *Object { func (f *Fs) newObjectWithInfo(remote, dstPath string, info os.FileInfo) (fs.Object, error) { o := f.newObject(remote, dstPath) if info != nil { - o.info = info + o.setMetadata(info) } else { err := o.lstat() if err != nil { @@ -169,7 +171,7 @@ func (f *Fs) newObjectWithInfo(remote, dstPath string, info os.FileInfo) (fs.Obj return nil, err } } - if o.info.Mode().IsDir() { + if o.mode.IsDir() { return nil, errors.Wrapf(fs.ErrorNotAFile, "%q", remote) } return o, nil @@ -451,7 +453,7 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) { // OK } else if err != nil { return nil, err - } else if !dstObj.info.Mode().IsRegular() { + } else if !dstObj.mode.IsRegular() { // It isn't a file return nil, errors.New("can't move file onto non-file") } @@ -539,14 +541,14 @@ func (o *Object) Remote() string { // Hash returns the requested hash of a file as a lowercase hex string func (o *Object) Hash(r fs.HashType) (string, error) { // Check that the underlying file hasn't changed - oldtime := o.info.ModTime() - oldsize := o.info.Size() + oldtime := o.modTime + oldsize := o.size err := o.lstat() if err != nil { return "", errors.Wrap(err, "hash: failed to stat") } - if !o.info.ModTime().Equal(oldtime) || oldsize != o.info.Size() { + if !o.modTime.Equal(oldtime) || oldsize != o.size { o.hashes = nil } @@ -570,12 +572,12 @@ func (o *Object) Hash(r fs.HashType) (string, error) { // Size returns the size of an object in bytes func (o *Object) Size() int64 { - return o.info.Size() + return o.size } // ModTime returns the modification time of the object func (o *Object) ModTime() time.Time { - return o.info.ModTime() + return o.modTime } // SetModTime sets the modification time of the local fs object @@ -597,7 +599,7 @@ func (o *Object) Storable() bool { return false } } - mode := o.info.Mode() + mode := o.mode // On windows a file with os.ModeSymlink represents a file with reparse points if runtime.GOOS == "windows" && (mode&os.ModeSymlink) != 0 { fs.Debugf(o, "Clearing symlink bit to allow a file with reparse points to be copied") @@ -744,10 +746,19 @@ func (o *Object) Update(in io.Reader, src fs.ObjectInfo, options ...fs.OpenOptio return o.lstat() } +// setMetadata sets the file info from the os.FileInfo passed in +func (o *Object) setMetadata(info os.FileInfo) { + o.size = info.Size() + o.modTime = info.ModTime() + o.mode = info.Mode() +} + // Stat a Object into info func (o *Object) lstat() error { info, err := o.fs.lstat(o.path) - o.info = info + if err == nil { + o.setMetadata(info) + } return err } diff --git a/sftp/sftp.go b/sftp/sftp.go index aac216ab3..ef64f3f20 100644 --- a/sftp/sftp.go +++ b/sftp/sftp.go @@ -66,9 +66,11 @@ type Fs struct { // Object is a remote SFTP file that has been stat'd (so it exists, but is not necessarily open for reading) type Object struct { - fs *Fs - remote string - info os.FileInfo + fs *Fs + remote string + size int64 // size of the object + modTime time.Time // modification time of the object + mode os.FileMode // mode bits from the file } // ObjectReader holds the sftp.File interface to a remote SFTP file opened for reading @@ -270,8 +272,8 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { o := &Object{ fs: f, remote: remote, - info: info, } + o.setMetadata(info) entries = append(entries, o) } } @@ -439,12 +441,12 @@ func (o *Object) Hash(r fs.HashType) (string, error) { // Size returns the size in bytes of the remote sftp file func (o *Object) Size() int64 { - return o.info.Size() + return o.size } // ModTime returns the modification time of the remote sftp file func (o *Object) ModTime() time.Time { - return o.info.ModTime() + return o.modTime } // path returns the native path of the object @@ -452,7 +454,14 @@ func (o *Object) path() string { return path.Join(o.fs.root, o.remote) } -// stat updates the info field in the Object +// setMetadata updates the info in the object from the stat result passed in +func (o *Object) setMetadata(info os.FileInfo) { + o.modTime = info.ModTime() + o.size = info.Size() + o.mode = info.Mode() +} + +// stat updates the info in the Object func (o *Object) stat() error { info, err := o.fs.sftpClient.Stat(o.path()) if err != nil { @@ -464,7 +473,7 @@ func (o *Object) stat() error { if info.IsDir() { return errors.Wrapf(fs.ErrorNotAFile, "%q", o.remote) } - o.info = info + o.setMetadata(info) return nil } @@ -485,7 +494,7 @@ func (o *Object) SetModTime(modTime time.Time) error { // Storable returns whether the remote sftp file is a regular file (not a directory, symbolic link, block device, character device, named pipe, etc) func (o *Object) Storable() bool { - return o.info.Mode().IsRegular() + return o.mode.IsRegular() } // Read from a remote sftp file object reader