seek, load only with priority, qbittorrent

This commit is contained in:
royalcat 2024-08-23 01:16:16 +03:00
parent e517332a65
commit ae4501ae21
26 changed files with 1357 additions and 623 deletions
pkg/ctxbilly

View file

@ -1,355 +1,355 @@
package ctxbilly
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
// import (
// "context"
// "errors"
// "fmt"
// "os"
// "path/filepath"
// "strings"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/iceber/iouring-go"
)
// securejoin "github.com/cyphar/filepath-securejoin"
// "github.com/iceber/iouring-go"
// )
func NewURingFS() (*UringFS, error) {
ur, err := iouring.New(64, iouring.WithAsync())
if err != nil {
return nil, err
}
return &UringFS{
ur: ur,
}, nil
}
var _ Filesystem = (*UringFS)(nil)
const (
defaultDirectoryMode = 0o755
defaultCreateMode = 0o666
)
// UringFS is a fs implementation based on the OS filesystem which is bound to
// a base dir.
// Prefer this fs implementation over ChrootOS.
//
// Behaviours of note:
// 1. Read and write operations can only be directed to files which descends
// from the base dir.
// 2. Symlinks don't have their targets modified, and therefore can point
// to locations outside the base dir or to non-existent paths.
// 3. Readlink and Lstat ensures that the link file is located within the base
// dir, evaluating any symlinks that file or base dir may contain.
type UringFS struct {
ur *iouring.IOURing
baseDir string
}
func newBoundOS(d string) *UringFS {
return &UringFS{baseDir: d}
}
func (fs *UringFS) Create(ctx context.Context, filename string) (File, error) {
return fs.OpenFile(ctx, filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode)
}
func (fs *UringFS) OpenFile(ctx context.Context, filename string, flag int, perm os.FileMode) (File, error) {
fn, err := fs.abs(filename)
if err != nil {
return nil, err
}
f, err := os.OpenFile(fn, flag, perm)
if err != nil {
return nil, err
}
return newFile(fs.ur, f)
}
func (fs *UringFS) ReadDir(ctx context.Context, path string) ([]os.FileInfo, error) {
dir, err := fs.abs(path)
if err != nil {
return nil, err
}
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
infos := make([]os.FileInfo, 0, len(entries))
for _, v := range entries {
info, err := v.Info()
if err != nil {
return nil, err
}
infos = append(infos, info)
}
return infos, nil
}
func (fs *UringFS) Rename(ctx context.Context, from, to string) error {
f, err := fs.abs(from)
if err != nil {
return err
}
t, err := fs.abs(to)
if err != nil {
return err
}
// MkdirAll for target name.
if err := fs.createDir(t); err != nil {
return err
}
return os.Rename(f, t)
}
func (fs *UringFS) MkdirAll(ctx context.Context, path string, perm os.FileMode) error {
dir, err := fs.abs(path)
if err != nil {
return err
}
return os.MkdirAll(dir, perm)
}
func (fs *UringFS) Stat(ctx context.Context, filename string) (os.FileInfo, error) {
filename, err := fs.abs(filename)
if err != nil {
return nil, err
}
return os.Stat(filename)
}
func (fs *UringFS) Remove(ctx context.Context, filename string) error {
fn, err := fs.abs(filename)
if err != nil {
return err
}
return os.Remove(fn)
}
func (fs *UringFS) Join(elem ...string) string {
return filepath.Join(elem...)
}
func (fs *UringFS) RemoveAll(path string) error {
dir, err := fs.abs(path)
if err != nil {
return err
}
return os.RemoveAll(dir)
}
func (fs *UringFS) Symlink(ctx context.Context, target, link string) error {
ln, err := fs.abs(link)
if err != nil {
return err
}
// MkdirAll for containing dir.
if err := fs.createDir(ln); err != nil {
return err
}
return os.Symlink(target, ln)
}
func (fs *UringFS) Lstat(ctx context.Context, filename string) (os.FileInfo, error) {
filename = filepath.Clean(filename)
if !filepath.IsAbs(filename) {
filename = filepath.Join(fs.baseDir, filename)
}
if ok, err := fs.insideBaseDirEval(filename); !ok {
return nil, err
}
return os.Lstat(filename)
}
func (fs *UringFS) Readlink(ctx context.Context, link string) (string, error) {
if !filepath.IsAbs(link) {
link = filepath.Clean(filepath.Join(fs.baseDir, link))
}
if ok, err := fs.insideBaseDirEval(link); !ok {
return "", err
}
return os.Readlink(link)
}
// Chroot returns a new OS filesystem, with the base dir set to the
// result of joining the provided path with the underlying base dir.
// func (fs *UringFS) Chroot(path string) (Filesystem, error) {
// joined, err := securejoin.SecureJoin(fs.baseDir, path)
// func NewURingFS() (*UringFS, error) {
// ur, err := iouring.New(64, iouring.WithAsync())
// if err != nil {
// return nil, err
// }
// return newBoundOS(joined), nil
// return &UringFS{
// ur: ur,
// }, nil
// }
// Root returns the current base dir of the billy.Filesystem.
// This is required in order for this implementation to be a drop-in
// replacement for other upstream implementations (e.g. memory and osfs).
func (fs *UringFS) Root() string {
return fs.baseDir
}
// var _ Filesystem = (*UringFS)(nil)
func (fs *UringFS) createDir(fullpath string) error {
dir := filepath.Dir(fullpath)
if dir != "." {
if err := os.MkdirAll(dir, defaultDirectoryMode); err != nil {
return err
}
}
// const (
// defaultDirectoryMode = 0o755
// defaultCreateMode = 0o666
// )
return nil
}
// // UringFS is a fs implementation based on the OS filesystem which is bound to
// // a base dir.
// // Prefer this fs implementation over ChrootOS.
// //
// // Behaviours of note:
// // 1. Read and write operations can only be directed to files which descends
// // from the base dir.
// // 2. Symlinks don't have their targets modified, and therefore can point
// // to locations outside the base dir or to non-existent paths.
// // 3. Readlink and Lstat ensures that the link file is located within the base
// // dir, evaluating any symlinks that file or base dir may contain.
// type UringFS struct {
// ur *iouring.IOURing
// baseDir string
// }
// abs transforms filename to an absolute path, taking into account the base dir.
// Relative paths won't be allowed to ascend the base dir, so `../file` will become
// `/working-dir/file`.
//
// Note that if filename is a symlink, the returned address will be the target of the
// symlink.
func (fs *UringFS) abs(filename string) (string, error) {
if filename == fs.baseDir {
filename = string(filepath.Separator)
}
// func newBoundOS(d string) *UringFS {
// return &UringFS{baseDir: d}
// }
path, err := securejoin.SecureJoin(fs.baseDir, filename)
if err != nil {
return "", nil
}
// func (fs *UringFS) Create(ctx context.Context, filename string) (File, error) {
// return fs.OpenFile(ctx, filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode)
// }
return path, nil
}
// func (fs *UringFS) OpenFile(ctx context.Context, filename string, flag int, perm os.FileMode) (File, error) {
// fn, err := fs.abs(filename)
// if err != nil {
// return nil, err
// }
// insideBaseDirEval checks whether filename is contained within
// a dir that is within the fs.baseDir, by first evaluating any symlinks
// that either filename or fs.baseDir may contain.
func (fs *UringFS) insideBaseDirEval(filename string) (bool, error) {
dir, err := filepath.EvalSymlinks(filepath.Dir(filename))
if dir == "" || os.IsNotExist(err) {
dir = filepath.Dir(filename)
}
wd, err := filepath.EvalSymlinks(fs.baseDir)
if wd == "" || os.IsNotExist(err) {
wd = fs.baseDir
}
if filename != wd && dir != wd && !strings.HasPrefix(dir, wd+string(filepath.Separator)) {
return false, fmt.Errorf("path outside base dir")
}
return true, nil
}
// f, err := os.OpenFile(fn, flag, perm)
// if err != nil {
// return nil, err
// }
func newFile(fsur *iouring.IOURing, f *os.File) (*URingFile, error) {
ur, err := iouring.New(64, iouring.WithAttachWQ(fsur))
if err != nil {
return nil, err
}
// return newFile(fs.ur, f)
// }
return &URingFile{
ur: ur,
f: f,
}, nil
}
// func (fs *UringFS) ReadDir(ctx context.Context, path string) ([]os.FileInfo, error) {
// dir, err := fs.abs(path)
// if err != nil {
// return nil, err
// }
type URingFile struct {
ur *iouring.IOURing
f *os.File
}
// entries, err := os.ReadDir(dir)
// if err != nil {
// return nil, err
// }
// infos := make([]os.FileInfo, 0, len(entries))
// for _, v := range entries {
// info, err := v.Info()
// if err != nil {
// return nil, err
// }
// Close implements File.
func (o *URingFile) Close(ctx context.Context) error {
return errors.Join(o.ur.UnregisterFile(o.f), o.Close(ctx))
}
// infos = append(infos, info)
// }
// Name implements File.
func (o *URingFile) Name() string {
return o.f.Name()
}
// return infos, nil
// }
// Read implements File.
func (o *URingFile) Read(ctx context.Context, p []byte) (n int, err error) {
req, err := o.ur.Read(o.f, p, nil)
if err != nil {
return 0, err
}
defer req.Cancel()
// func (fs *UringFS) Rename(ctx context.Context, from, to string) error {
// f, err := fs.abs(from)
// if err != nil {
// return err
// }
// t, err := fs.abs(to)
// if err != nil {
// return err
// }
select {
case <-req.Done():
return req.GetRes()
case <-ctx.Done():
req.Cancel()
<-req.Done()
return req.GetRes()
}
}
// // MkdirAll for target name.
// if err := fs.createDir(t); err != nil {
// return err
// }
// ReadAt implements File.
func (o *URingFile) ReadAt(ctx context.Context, p []byte, off int64) (n int, err error) {
req, err := o.ur.Pread(o.f, p, uint64(off), nil)
if err != nil {
return 0, err
}
defer req.Cancel()
// return os.Rename(f, t)
// }
select {
case <-req.Done():
return req.GetRes()
case <-ctx.Done():
req.Cancel()
<-req.Done()
return req.GetRes()
}
}
// func (fs *UringFS) MkdirAll(ctx context.Context, path string, perm os.FileMode) error {
// dir, err := fs.abs(path)
// if err != nil {
// return err
// }
// return os.MkdirAll(dir, perm)
// }
// Write implements File.
func (o *URingFile) Write(ctx context.Context, p []byte) (n int, err error) {
req, err := o.ur.Write(o.f, p, nil)
if err != nil {
return 0, err
}
defer req.Cancel()
// func (fs *UringFS) Stat(ctx context.Context, filename string) (os.FileInfo, error) {
// filename, err := fs.abs(filename)
// if err != nil {
// return nil, err
// }
// return os.Stat(filename)
// }
select {
case <-req.Done():
return req.GetRes()
case <-ctx.Done():
req.Cancel()
<-req.Done()
return req.GetRes()
}
}
// func (fs *UringFS) Remove(ctx context.Context, filename string) error {
// fn, err := fs.abs(filename)
// if err != nil {
// return err
// }
// return os.Remove(fn)
// }
// WriteAt implements File.
func (o *URingFile) WriteAt(ctx context.Context, p []byte, off int64) (n int, err error) {
req, err := o.ur.Pwrite(o.f, p, uint64(off), nil)
if err != nil {
return 0, err
}
defer req.Cancel()
// func (fs *UringFS) Join(elem ...string) string {
// return filepath.Join(elem...)
// }
select {
case <-req.Done():
return req.GetRes()
case <-ctx.Done():
req.Cancel()
<-req.Done()
return req.GetRes()
}
}
// func (fs *UringFS) RemoveAll(path string) error {
// dir, err := fs.abs(path)
// if err != nil {
// return err
// }
// return os.RemoveAll(dir)
// }
// Seek implements File.
func (o *URingFile) Seek(offset int64, whence int) (int64, error) {
return o.f.Seek(offset, whence)
}
// func (fs *UringFS) Symlink(ctx context.Context, target, link string) error {
// ln, err := fs.abs(link)
// if err != nil {
// return err
// }
// // MkdirAll for containing dir.
// if err := fs.createDir(ln); err != nil {
// return err
// }
// return os.Symlink(target, ln)
// }
// Truncate implements File.
func (o *URingFile) Truncate(ctx context.Context, size int64) error {
return o.f.Truncate(size)
}
// func (fs *UringFS) Lstat(ctx context.Context, filename string) (os.FileInfo, error) {
// filename = filepath.Clean(filename)
// if !filepath.IsAbs(filename) {
// filename = filepath.Join(fs.baseDir, filename)
// }
// if ok, err := fs.insideBaseDirEval(filename); !ok {
// return nil, err
// }
// return os.Lstat(filename)
// }
var _ File = (*URingFile)(nil)
// func (fs *UringFS) Readlink(ctx context.Context, link string) (string, error) {
// if !filepath.IsAbs(link) {
// link = filepath.Clean(filepath.Join(fs.baseDir, link))
// }
// if ok, err := fs.insideBaseDirEval(link); !ok {
// return "", err
// }
// return os.Readlink(link)
// }
// // Chroot returns a new OS filesystem, with the base dir set to the
// // result of joining the provided path with the underlying base dir.
// // func (fs *UringFS) Chroot(path string) (Filesystem, error) {
// // joined, err := securejoin.SecureJoin(fs.baseDir, path)
// // if err != nil {
// // return nil, err
// // }
// // return newBoundOS(joined), nil
// // }
// // Root returns the current base dir of the billy.Filesystem.
// // This is required in order for this implementation to be a drop-in
// // replacement for other upstream implementations (e.g. memory and osfs).
// func (fs *UringFS) Root() string {
// return fs.baseDir
// }
// func (fs *UringFS) createDir(fullpath string) error {
// dir := filepath.Dir(fullpath)
// if dir != "." {
// if err := os.MkdirAll(dir, defaultDirectoryMode); err != nil {
// return err
// }
// }
// return nil
// }
// // abs transforms filename to an absolute path, taking into account the base dir.
// // Relative paths won't be allowed to ascend the base dir, so `../file` will become
// // `/working-dir/file`.
// //
// // Note that if filename is a symlink, the returned address will be the target of the
// // symlink.
// func (fs *UringFS) abs(filename string) (string, error) {
// if filename == fs.baseDir {
// filename = string(filepath.Separator)
// }
// path, err := securejoin.SecureJoin(fs.baseDir, filename)
// if err != nil {
// return "", nil
// }
// return path, nil
// }
// // insideBaseDirEval checks whether filename is contained within
// // a dir that is within the fs.baseDir, by first evaluating any symlinks
// // that either filename or fs.baseDir may contain.
// func (fs *UringFS) insideBaseDirEval(filename string) (bool, error) {
// dir, err := filepath.EvalSymlinks(filepath.Dir(filename))
// if dir == "" || os.IsNotExist(err) {
// dir = filepath.Dir(filename)
// }
// wd, err := filepath.EvalSymlinks(fs.baseDir)
// if wd == "" || os.IsNotExist(err) {
// wd = fs.baseDir
// }
// if filename != wd && dir != wd && !strings.HasPrefix(dir, wd+string(filepath.Separator)) {
// return false, fmt.Errorf("path outside base dir")
// }
// return true, nil
// }
// func newFile(fsur *iouring.IOURing, f *os.File) (*URingFile, error) {
// ur, err := iouring.New(64, iouring.WithAttachWQ(fsur))
// if err != nil {
// return nil, err
// }
// return &URingFile{
// ur: ur,
// f: f,
// }, nil
// }
// type URingFile struct {
// ur *iouring.IOURing
// f *os.File
// }
// // Close implements File.
// func (o *URingFile) Close(ctx context.Context) error {
// return errors.Join(o.ur.UnregisterFile(o.f), o.Close(ctx))
// }
// // Name implements File.
// func (o *URingFile) Name() string {
// return o.f.Name()
// }
// // Read implements File.
// func (o *URingFile) Read(ctx context.Context, p []byte) (n int, err error) {
// req, err := o.ur.Read(o.f, p, nil)
// if err != nil {
// return 0, err
// }
// defer req.Cancel()
// select {
// case <-req.Done():
// return req.GetRes()
// case <-ctx.Done():
// req.Cancel()
// <-req.Done()
// return req.GetRes()
// }
// }
// // ReadAt implements File.
// func (o *URingFile) ReadAt(ctx context.Context, p []byte, off int64) (n int, err error) {
// req, err := o.ur.Pread(o.f, p, uint64(off), nil)
// if err != nil {
// return 0, err
// }
// defer req.Cancel()
// select {
// case <-req.Done():
// return req.GetRes()
// case <-ctx.Done():
// req.Cancel()
// <-req.Done()
// return req.GetRes()
// }
// }
// // Write implements File.
// func (o *URingFile) Write(ctx context.Context, p []byte) (n int, err error) {
// req, err := o.ur.Write(o.f, p, nil)
// if err != nil {
// return 0, err
// }
// defer req.Cancel()
// select {
// case <-req.Done():
// return req.GetRes()
// case <-ctx.Done():
// req.Cancel()
// <-req.Done()
// return req.GetRes()
// }
// }
// // WriteAt implements File.
// func (o *URingFile) WriteAt(ctx context.Context, p []byte, off int64) (n int, err error) {
// req, err := o.ur.Pwrite(o.f, p, uint64(off), nil)
// if err != nil {
// return 0, err
// }
// defer req.Cancel()
// select {
// case <-req.Done():
// return req.GetRes()
// case <-ctx.Done():
// req.Cancel()
// <-req.Done()
// return req.GetRes()
// }
// }
// // Seek implements File.
// func (o *URingFile) Seek(offset int64, whence int) (int64, error) {
// return o.f.Seek(offset, whence)
// }
// // Truncate implements File.
// func (o *URingFile) Truncate(ctx context.Context, size int64) error {
// return o.f.Truncate(size)
// }
// var _ File = (*URingFile)(nil)