cachefs/pkg/provider/crypto/file.go

257 lines
4.3 KiB
Go

package crypto
import (
"cachefs/pkg/provider"
"io"
"io/fs"
"os"
)
var _ provider.File = (*file)(nil)
type file struct {
provider.File
fs *FS
r *reader
w *writer
nonce [NonceSize]byte
offset int64
name string
stat *stat
dir bool
}
func newFile(key []byte, name string, fs *FS, f provider.File, dir bool) (ef provider.File, err error) {
if dir {
return &file{
File: f,
fs: fs,
name: name,
dir: dir,
}, nil
}
nonce, err := initNonce(f)
if err != nil {
return
}
skey := streamKey(key, nonce)
w, err := newWriter(skey, f)
if err != nil {
return
}
r, err := newReader(skey, f)
if err != nil {
return
}
ef = &file{
File: f,
fs: fs,
w: w,
r: r,
name: name,
}
return ef, nil
}
func (f *file) Stat() (fs.FileInfo, error) {
if f.stat != nil {
return f.stat, nil
}
fi, err := f.File.Stat()
if err != nil {
return nil, err
}
if f.dir {
return dir{FileInfo: fi, name: f.name}, nil
}
rs := RealSize(fi.Size())
f.stat = &stat{
FileInfo: fi,
size: rs,
name: f.name,
}
return f.stat, nil
//fmt.Println("rs", rs)
//_, err = f.ReadAt(nil, rs)
//if err != nil && err != io.EOF {
// return nil, err
//}
//f.stat = &stat{
// FileInfo: fi,
// size: rs - (ChunkSize - int64(len(f.r.unread))),
// name: f.name,
//}
//fmt.Println("size", f.stat.Size())
//f.Seek(0, io.SeekStart)
//return f.stat, nil
}
func (f *file) Name() string {
return f.name
}
func (f *file) Fd() uintptr {
osf, ok := f.File.(*os.File)
if ok {
return osf.Fd()
}
return 0
}
type stat struct {
fs.FileInfo
size int64
name string
}
func (s stat) Size() int64 {
return s.size
}
func (s stat) Name() string {
return s.name
}
func (f *file) Truncate(n int64) error {
return f.File.Truncate(Size(n))
}
func (f *file) Close() error {
if f.dir {
return f.File.Close()
}
e1 := f.w.Close()
e2 := f.File.Close()
if e1 != nil {
return e1
}
return e2
}
func (f *file) Seek(offset int64, whence int) (n int64, err error) {
var cn, off, roff int64
switch whence {
case io.SeekStart:
cn, off, roff = align(offset)
f.offset = KDFNonceSize + off
n, err = f.File.Seek(f.offset, whence)
case io.SeekCurrent:
if f.offset == 0 {
f.offset = KDFNonceSize
}
cn, off, roff = align(f.offset + f.r.off + offset)
f.offset = off
_, err = f.File.Seek(f.offset, io.SeekStart)
n = offset
}
f.reset()
if err != nil {
return
}
f.nonce = [NonceSize]byte{}
inc(&f.nonce, cn)
f.r.nonce = f.nonce
f.w.nonce = f.nonce
f.r.off = roff
return
}
func (f *file) reset() {
f.r.err = nil
f.r.unread = f.r.unread[:0]
}
func (f *file) Read(p []byte) (n int, err error) {
n, err = f.r.Read(p)
return
}
func (f *file) Write(p []byte) (n int, err error) {
n, err = f.w.Write(p)
return
}
func (f *file) ReadAt(p []byte, pos int64) (n int, err error) {
cn, _, roff := align(pos)
var last bool
if f.r.cn != cn {
_, err = f.Seek(pos, io.SeekStart)
if err != nil {
return
}
last, err = f.r.readChunk()
if err != nil {
return
}
f.r.cn = cn
}
n = copy(p, f.r.unread[roff:])
if last && len(f.r.unread) == n+int(roff) {
err = io.EOF
}
return
}
func (f *file) WriteAt(data []byte, pos int64) (n int, err error) {
cn, off, woff := align(pos)
c, ok := f.w.cm[cn]
if !ok {
c = &chunkWriter{offset: off + KDFNonceSize, w: f.w, cn: cn}
inc(&c.nonce, cn)
f.w.cm[cn] = c
if f.w.last == nil {
f.w.last = c
} else if cn > f.w.last.cn {
f.w.last = c
}
}
n, err = c.writeAt(data, woff)
if c.full() {
// async flush
go func() {
//_, err = c.flush(notLastChunk)
c.flush(notLastChunk)
}()
delete(f.w.cm, cn)
}
return
}
func (f *file) Readdir(n int) ([]fs.FileInfo, error) {
fis, err := f.File.Readdir(n)
if err != nil {
return nil, err
}
fio := make([]fs.FileInfo, len(fis))
for i, fi := range fis {
name, err := f.fs.decPath(fi.Name())
if err != nil {
return nil, err
}
fio[i] = stat{FileInfo: fi, size: RealSize(fi.Size()), name: name}
}
return fio, nil
}
func (f *file) ReadDir(n int) ([]fs.DirEntry, error) {
fis, err := f.Readdir(n)
if err != nil {
return nil, err
}
fio := make([]fs.DirEntry, len(fis))
for i, fi := range fis {
fio[i] = fs.FileInfoToDirEntry(fi)
}
return fio, nil
}
type dir struct {
fs.FileInfo
name string
}
func (d dir) Name() string {
return d.name
}