diff --git a/daemons/qbittorrent/daemon.go b/daemons/qbittorrent/daemon.go index a7b88a3..0f6aafb 100644 --- a/daemons/qbittorrent/daemon.go +++ b/daemons/qbittorrent/daemon.go @@ -160,6 +160,11 @@ func (fs *Daemon) GetTorrentFS(ctx context.Context, sourcePath string, file vfs. ctx, span := trace.Start(ctx, "GetTorrentFS") defer span.End() + stat, err := file.Info() + if err != nil { + return nil, err + } + log := fs.log.With(slog.String("file", file.Name())) ih, err := readInfoHash(ctx, file) @@ -185,7 +190,7 @@ func (fs *Daemon) GetTorrentFS(ctx context.Context, sourcePath string, file vfs. fs.sourceFiles[sourcePath] = ih.HexString() fs.sourceFilesMu.Unlock() - return newTorrentFS(ctx, fs.ur, fs.client, file.Name(), ih.HexString(), torrentPath) + return newTorrentFS(ctx, fs.client, file.Name(), ih.HexString(), stat.ModTime(), torrentPath) } func (d *Daemon) syncTorrentState(ctx context.Context, file vfs.File, ih metainfo.Hash, torrentPath string) error { diff --git a/daemons/qbittorrent/fs.go b/daemons/qbittorrent/fs.go index ca5ecbb..8c2dd17 100644 --- a/daemons/qbittorrent/fs.go +++ b/daemons/qbittorrent/fs.go @@ -15,7 +15,6 @@ import ( "git.kmsign.ru/royalcat/tstor/pkg/qbittorrent" "git.kmsign.ru/royalcat/tstor/pkg/rlog" - "git.kmsign.ru/royalcat/tstor/pkg/uring" "git.kmsign.ru/royalcat/tstor/src/vfs" "github.com/iceber/iouring-go" ) @@ -26,6 +25,7 @@ type FS struct { name string hash string dataDir string // directory where torrent files are stored + modTime time.Time ur *iouring.IOURing @@ -43,7 +43,7 @@ type fileEntry struct { var _ vfs.Filesystem = (*FS)(nil) -func newTorrentFS(ctx context.Context, ur *iouring.IOURing, client *cacheClient, name string, hash string, dataDir string) (*FS, error) { +func newTorrentFS(ctx context.Context, client *cacheClient, name string, hash string, modTime time.Time, dataDir string) (*FS, error) { ctx, span := trace.Start(ctx, "newTorrentFS") defer span.End() @@ -60,21 +60,20 @@ func newTorrentFS(ctx context.Context, ur *iouring.IOURing, client *cacheClient, entries[vfs.AbsPath(cnt.Name)] = fileEntry{ Content: cnt, - FileInfo: vfs.NewFileInfo(cnt.Name, cnt.Size), + FileInfo: vfs.NewFileInfo(cnt.Name, cnt.Size, modTime), } } return &FS{ - client: client, - name: name, - hash: hash, + client: client, + name: name, + hash: hash, + modTime: modTime, dataDir: dataDir, entries: entries, - ur: ur, - log: rlog.Component("qbittorrent", "fs"), FilesystemPrototype: vfs.FilesystemPrototype(name), @@ -115,7 +114,7 @@ func (f *FS) Stat(ctx context.Context, name string) (fs.FileInfo, error) { name = vfs.AbsPath(path.Clean(name)) if vfs.IsRoot(name) { - return vfs.NewDirInfo(f.name), nil + return vfs.NewDirInfo(f.name, f.modTime), nil } if entry, ok := f.entries[name]; ok { @@ -124,7 +123,7 @@ func (f *FS) Stat(ctx context.Context, name string) (fs.FileInfo, error) { for p := range f.entries { if strings.HasPrefix(p, name) { - return vfs.NewDirInfo(name), nil + return vfs.NewDirInfo(name, f.modTime), nil } } @@ -219,23 +218,24 @@ func openFile(ctx context.Context, ur *iouring.IOURing, client *cacheClient, tor pieceSize: props.PieceSize, fileSize: content.Size, - file: uring.NewFile(ur, file), + file: file, offset: 0, }, nil } type File struct { - client *cacheClient - hash string - torrentDir string - filePath string // path inside a torrent directory - contentIndex int - pieceSize int - fileSize int64 + client *cacheClient + hash string + torrentModTime time.Time + torrentDir string + filePath string // path inside a torrent directory + contentIndex int + pieceSize int + fileSize int64 mu sync.Mutex - file *uring.File + file *os.File offset int64 } @@ -243,7 +243,7 @@ var _ vfs.File = (*File)(nil) // Info implements vfs.File. func (f *File) Info() (fs.FileInfo, error) { - return &fileInfo{name: path.Base(f.filePath), size: f.fileSize}, nil + return vfs.NewFileInfo(path.Base(f.filePath), f.fileSize, f.torrentModTime), nil } // IsDir implements vfs.File. @@ -345,7 +345,7 @@ func (f *File) Read(ctx context.Context, p []byte) (int, error) { return 0, err } - n, err := f.file.ReadAt(ctx, p, f.offset) + n, err := f.file.ReadAt(p, f.offset) f.offset += int64(n) return n, err } @@ -356,7 +356,7 @@ func (f *File) ReadAt(ctx context.Context, p []byte, off int64) (int, error) { return 0, err } - return f.file.ReadAt(ctx, p, off) + return f.file.ReadAt(p, off) } // Size implements vfs.File. @@ -371,42 +371,43 @@ func (f *File) Type() fs.FileMode { // Close implements vfs.File. func (f *File) Close(ctx context.Context) error { - return f.file.Close(ctx) + return f.file.Close() } -type fileInfo struct { - name string - size int64 -} +// type fileInfo struct { +// name string +// size int64 +// modTime time.Time +// } -var _ fs.FileInfo = (*fileInfo)(nil) +// var _ fs.FileInfo = (*fileInfo)(nil) -// IsDir implements fs.FileInfo. -func (f *fileInfo) IsDir() bool { - return false -} +// // IsDir implements fs.FileInfo. +// func (f *fileInfo) IsDir() bool { +// return false +// } -// ModTime implements fs.FileInfo. -func (f *fileInfo) ModTime() time.Time { - return time.Time{} -} +// // ModTime implements fs.FileInfo. +// func (f *fileInfo) ModTime() time.Time { +// return f.modTime +// } -// Mode implements fs.FileInfo. -func (f *fileInfo) Mode() fs.FileMode { - return vfs.ModeFileRO -} +// // Mode implements fs.FileInfo. +// func (f *fileInfo) Mode() fs.FileMode { +// return vfs.ModeFileRO +// } -// Name implements fs.FileInfo. -func (f *fileInfo) Name() string { - return f.name -} +// // Name implements fs.FileInfo. +// func (f *fileInfo) Name() string { +// return f.name +// } -// Size implements fs.FileInfo. -func (f *fileInfo) Size() int64 { - return f.size -} +// // Size implements fs.FileInfo. +// func (f *fileInfo) Size() int64 { +// return f.size +// } -// Sys implements fs.FileInfo. -func (f *fileInfo) Sys() any { - return nil -} +// // Sys implements fs.FileInfo. +// func (f *fileInfo) Sys() any { +// return nil +// } diff --git a/daemons/rclone/rclone.go b/daemons/rclone/rclone.go index 6092166..3fbcd72 100644 --- a/daemons/rclone/rclone.go +++ b/daemons/rclone/rclone.go @@ -3,6 +3,7 @@ package rclone import ( "context" "io/fs" + "time" "git.kmsign.ru/royalcat/tstor/src/vfs" rclonefs "github.com/rclone/rclone/fs" @@ -78,7 +79,7 @@ func (f *fileWrapper) Close(ctx context.Context) error { // Info implements vfs.File. func (f *fileWrapper) Info() (fs.FileInfo, error) { - return vfs.NewFileInfo(f.name, f.Size()), nil + return vfs.NewFileInfo(f.name, f.Size(), time.Time{}), nil } // IsDir implements vfs.File. diff --git a/daemons/torrent/fs.go b/daemons/torrent/fs.go index e367197..48daa61 100644 --- a/daemons/torrent/fs.go +++ b/daemons/torrent/fs.go @@ -450,7 +450,7 @@ func (tf *torrentFile) Type() fs.FileMode { } func (tf *torrentFile) Info() (fs.FileInfo, error) { - return vfs.NewFileInfo(tf.name, tf.file.Length()), nil + return vfs.NewFileInfo(tf.name, tf.file.Length(), time.Time{}), nil } func (tf *torrentFile) Size() int64 { diff --git a/daemons/ytdlp/fs.go b/daemons/ytdlp/fs.go index 0c822c3..ac91ebf 100644 --- a/daemons/ytdlp/fs.go +++ b/daemons/ytdlp/fs.go @@ -4,6 +4,7 @@ import ( "context" "io/fs" "os" + "time" "git.kmsign.ru/royalcat/tstor/pkg/ctxbilly" "git.kmsign.ru/royalcat/tstor/src/vfs" @@ -53,7 +54,7 @@ func (s *SourceFS) ReadDir(ctx context.Context, path string) ([]fs.DirEntry, err entries := make([]fs.DirEntry, 0, len(infos)) for _, info := range infos { - entries = append(entries, vfs.NewFileInfo(info.Name(), info.Size())) + entries = append(entries, vfs.NewFileInfo(info.Name(), info.Size(), time.Time{})) } return entries, nil diff --git a/src/export/webdav/fs.go b/src/export/webdav/fs.go index f8e5407..a83b53b 100644 --- a/src/export/webdav/fs.go +++ b/src/export/webdav/fs.go @@ -94,10 +94,15 @@ type webDAVFile struct { } func newFile(ctx context.Context, name string, f vfs.File, df func() ([]os.FileInfo, error)) *webDAVFile { + info, err := f.Info() + if err != nil { + info = newFileInfo(name, f.Size(), f.IsDir()) + } + return &webDAVFile{ ctx: ctx, f: f, - fi: NewFileInfo(name, f.Size(), f.IsDir()), + fi: info, dirFunc: df, } } @@ -185,7 +190,7 @@ type webDAVFileInfo struct { isDir bool } -func NewFileInfo(name string, size int64, isDir bool) *webDAVFileInfo { +func newFileInfo(name string, size int64, isDir bool) *webDAVFileInfo { return &webDAVFileInfo{ name: name, size: size, @@ -210,8 +215,7 @@ func (wdfi *webDAVFileInfo) Mode() os.FileMode { } func (wdfi *webDAVFileInfo) ModTime() time.Time { - // TODO fix it - return time.Now() + return time.Time{} } func (wdfi *webDAVFileInfo) IsDir() bool { diff --git a/src/vfs/archive.go b/src/vfs/archive.go index 6bac4a9..4296ee2 100644 --- a/src/vfs/archive.go +++ b/src/vfs/archive.go @@ -119,7 +119,7 @@ func NewArchive(ctx context.Context, archivePath, name string, f File, size int6 // FIXME configurable files["/.forcegallery"] = fileEntry{ - FileInfo: NewFileInfo("/.forcegallery", 0), + FileInfo: NewFileInfo("/.forcegallery", 0, time.Time{}), open: func(ctx context.Context) (File, error) { return NewMemoryFile(".forcegallery", []byte{}), nil }, @@ -173,7 +173,7 @@ func (afs *ArchiveFS) Stat(ctx context.Context, filename string) (fs.FileInfo, e for p, _ := range afs.files { if strings.HasPrefix(p, filename) { - return NewDirInfo(path.Base(filename)), nil + return NewDirInfo(path.Base(filename), time.Time{}), nil } } @@ -245,7 +245,7 @@ func (d *archiveFile) Type() fs.FileMode { } func (d *archiveFile) Info() (fs.FileInfo, error) { - return NewFileInfo(d.name, d.size), nil + return NewFileInfo(d.name, d.size, time.Time{}), nil } func (d *archiveFile) Size() int64 { @@ -431,7 +431,7 @@ func RarLoader(ctx context.Context, archivePath string, f File, size int64) (map rr := newRandomReaderFromLinear(archiveFileIndex{archiveHash: hash, filename: header.Name}, header.UnPackedSize, af) out[AbsPath(header.Name)] = fileEntry{ - FileInfo: NewFileInfo(header.Name, header.UnPackedSize), + FileInfo: NewFileInfo(header.Name, header.UnPackedSize, header.ModificationTime), open: func(ctx context.Context) (File, error) { return newArchiveFile(header.Name, header.UnPackedSize, rr), nil }, diff --git a/src/vfs/default.go b/src/vfs/default.go index e6581bf..2c7cfd8 100644 --- a/src/vfs/default.go +++ b/src/vfs/default.go @@ -2,13 +2,14 @@ package vfs import ( "io/fs" + "time" ) type DefaultFS string // Info implements Filesystem. func (d DefaultFS) Info() (fs.FileInfo, error) { - return NewDirInfo(string(d)), nil + return NewDirInfo(string(d), time.Time{}), nil } // IsDir implements Filesystem. diff --git a/src/vfs/dir.go b/src/vfs/dir.go index 823c996..05f4730 100644 --- a/src/vfs/dir.go +++ b/src/vfs/dir.go @@ -4,6 +4,7 @@ import ( "context" "io/fs" "path" + "time" ) var _ File = &dirFile{} @@ -25,7 +26,7 @@ func (d *dirFile) Close(ctx context.Context) error { // Info implements File. func (d *dirFile) Info() (fs.FileInfo, error) { - return NewDirInfo(d.name), nil + return NewDirInfo(d.name, time.Time{}), nil } // IsDir implements File. diff --git a/src/vfs/dummy.go b/src/vfs/dummy.go index a1ce9b3..3e75db4 100644 --- a/src/vfs/dummy.go +++ b/src/vfs/dummy.go @@ -36,7 +36,7 @@ func (d *DummyFs) FsName() string { // Stat implements Filesystem. func (*DummyFs) Stat(ctx context.Context, filename string) (fs.FileInfo, error) { - return NewFileInfo(path.Base(filename), 0), nil // TODO + return NewFileInfo(path.Base(filename), 0, time.Time{}), nil // TODO } func (d *DummyFs) Open(ctx context.Context, filename string) (File, error) { @@ -50,8 +50,8 @@ func (d *DummyFs) Unlink(ctx context.Context, filename string) error { func (d *DummyFs) ReadDir(ctx context.Context, path string) ([]fs.DirEntry, error) { if path == "/dir/here" { return []fs.DirEntry{ - NewFileInfo("file1.txt", 0), - NewFileInfo("file2.txt", 0), + NewFileInfo("file1.txt", 0, time.Time{}), + NewFileInfo("file2.txt", 0, time.Time{}), }, nil } @@ -60,7 +60,7 @@ func (d *DummyFs) ReadDir(ctx context.Context, path string) ([]fs.DirEntry, erro // Info implements Filesystem. func (d *DummyFs) Info() (fs.FileInfo, error) { - return NewDirInfo(d.name), nil + return NewDirInfo(d.name, time.Time{}), nil } // IsDir implements Filesystem. @@ -101,7 +101,7 @@ func (d *DummyFile) Type() fs.FileMode { // Stat implements File. func (d *DummyFile) Info() (fs.FileInfo, error) { - return NewFileInfo(d.name, 0), nil + return NewFileInfo(d.name, 0, time.Time{}), nil } func (d *DummyFile) Size() int64 { diff --git a/src/vfs/fs.go b/src/vfs/fs.go index 93891ea..c023d29 100644 --- a/src/vfs/fs.go +++ b/src/vfs/fs.go @@ -55,7 +55,7 @@ type fileInfo struct { var _ fs.FileInfo = &fileInfo{} var _ fs.DirEntry = &fileInfo{} -func NewDirInfo(name string) *fileInfo { +func NewDirInfo(name string, modTime time.Time) *fileInfo { return &fileInfo{ name: path.Base(name), size: 0, @@ -63,7 +63,7 @@ func NewDirInfo(name string) *fileInfo { } } -func NewFileInfo(name string, size int64) *fileInfo { +func NewFileInfo(name string, size int64, modTime time.Time) *fileInfo { return &fileInfo{ name: path.Base(name), size: size, @@ -116,7 +116,7 @@ type FilesystemPrototype string // Info implements Filesystem. func (p FilesystemPrototype) Info() (fs.FileInfo, error) { - return NewDirInfo(string(p)), nil + return NewDirInfo(string(p), time.Time{}), nil } // IsDir implements Filesystem. diff --git a/src/vfs/fs_test.go b/src/vfs/fs_test.go index 2c73af0..3ab3d7a 100644 --- a/src/vfs/fs_test.go +++ b/src/vfs/fs_test.go @@ -3,6 +3,7 @@ package vfs import ( "io/fs" "testing" + "time" "github.com/stretchr/testify/require" ) @@ -12,7 +13,7 @@ func TestFileinfo(t *testing.T) { require := require.New(t) - fi := NewFileInfo("abc/name", 42) + fi := NewFileInfo("abc/name", 42, time.Time{}) require.Equal("name", fi.Name()) require.False(fi.IsDir()) @@ -29,7 +30,7 @@ func TestDirInfo(t *testing.T) { require := require.New(t) - fi := NewDirInfo("abc/name") + fi := NewDirInfo("abc/name", time.Time{}) require.True(fi.IsDir()) require.Equal("name", fi.Name()) diff --git a/src/vfs/memory.go b/src/vfs/memory.go index 67cec4f..28245f7 100644 --- a/src/vfs/memory.go +++ b/src/vfs/memory.go @@ -42,7 +42,7 @@ func (fs *MemoryFs) FsName() string { // Info implements Filesystem. func (fs *MemoryFs) Info() (fs.FileInfo, error) { - return NewDirInfo(fs.name), nil + return NewDirInfo(fs.name, time.Time{}), nil } // IsDir implements Filesystem. @@ -86,7 +86,7 @@ func (mfs *MemoryFs) Stat(ctx context.Context, filename string) (fs.FileInfo, er if !ok { return nil, ErrNotExist } - return NewFileInfo(path.Base(filename), file.Size()), nil + return NewFileInfo(path.Base(filename), file.Size(), time.Time{}), nil } // Unlink implements Filesystem. @@ -124,7 +124,7 @@ func (d *MemoryFile) Type() fs.FileMode { } func (d *MemoryFile) Info() (fs.FileInfo, error) { - return NewFileInfo(d.name, int64(d.data.Len())), nil + return NewFileInfo(d.name, int64(d.data.Len()), time.Time{}), nil } func (d *MemoryFile) Size() int64 { diff --git a/src/vfs/os.go b/src/vfs/os.go index a874e1c..4c896fd 100644 --- a/src/vfs/os.go +++ b/src/vfs/os.go @@ -7,6 +7,7 @@ import ( "os" "path" "sync" + "time" ) type OsFS struct { @@ -18,7 +19,7 @@ var _ Filesystem = (*OsFS)(nil) // Stat implements Filesystem. func (fs *OsFS) Stat(ctx context.Context, filename string) (fs.FileInfo, error) { if path.Clean(filename) == Separator { - return NewDirInfo(Separator), nil + return NewDirInfo(Separator, time.Time{}), nil } info, err := os.Stat(path.Join(fs.hostDir, filename)) @@ -58,7 +59,7 @@ func (o *OsFS) ReadDir(ctx context.Context, dir string) ([]fs.DirEntry, error) { // Info implements Filesystem. func (fs *OsFS) Info() (fs.FileInfo, error) { - return NewDirInfo(fs.Name()), nil + return NewDirInfo(fs.Name(), time.Time{}), nil } // IsDir implements Filesystem. diff --git a/src/vfs/resolver.go b/src/vfs/resolver.go index 7347215..c762a01 100644 --- a/src/vfs/resolver.go +++ b/src/vfs/resolver.go @@ -389,11 +389,16 @@ func ListDirFromFiles[F File](m map[string]F, name string) ([]fs.DirEntry, error name = AddTrailSlash(path.Clean(name)) for p, f := range m { if strings.HasPrefix(p, name) { + stat, err := f.Info() + if err != nil { + return nil, err + } + parts := strings.Split(trimRelPath(p, name), Separator) if len(parts) == 1 { - out = append(out, NewFileInfo(parts[0], f.Size())) + out = append(out, NewFileInfo(parts[0], f.Size(), stat.ModTime())) } else { - out = append(out, NewDirInfo(parts[0])) + out = append(out, NewDirInfo(parts[0], stat.ModTime())) } } @@ -413,11 +418,12 @@ func ListDirFromInfo(m map[string]fs.FileInfo, name string) ([]fs.DirEntry, erro name = AddTrailSlash(path.Clean(name)) for p, f := range m { if strings.HasPrefix(p, name) { + parts := strings.Split(trimRelPath(p, name), Separator) if len(parts) == 1 { - out = append(out, NewFileInfo(parts[0], f.Size())) + out = append(out, NewFileInfo(parts[0], f.Size(), f.ModTime())) } else { - out = append(out, NewDirInfo(parts[0])) + out = append(out, NewDirInfo(parts[0], f.ModTime())) } }