package crypto import ( "cachefs/pkg/provider" "io" "io/fs" ) var _ provider.File = (*file)(nil) type file struct { provider.File fs *FS r *reader w *writer nonce [NonceSize]byte offset int64 name string 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 } return &file{ File: f, fs: fs, w: w, r: r, name: name, }, nil } func (f *file) Stat() (fs.FileInfo, error) { fi, err := f.File.Stat() return stat{FileInfo: fi, name: f.name}, err } func (f *file) Name() string { return f.name } func (file) Fd() uintptr { return 0 } type stat struct { fs.FileInfo name string } func (s stat) Size() int64 { return RealSize(s.FileInfo.Size()) } func (s stat) Name() string { return s.name } 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.nonce = [NonceSize]byte{} inc(&f.nonce, cn) f.r.nonce = f.nonce f.r.off = roff f.r.err = nil f.w.nonce = f.nonce f.r.unread = f.r.unread[:0] return } 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) { _, _, roff := align(pos) //cn, off, roff := align(pos) f.Seek(pos, io.SeekStart) //fmt.Println("readAt:", cn, off+KDFNonceSize, roff, pos) _, err = f.r.readChunk() if err != nil { return } n = copy(p, f.r.unread[roff:]) return } func (f *file) WriteAt(data []byte, pos int64) (n int, err error) { cn, off, woff := align(pos) //fmt.Println("writeAt:", cn, off+KDFNonceSize, woff, 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() { _, err = c.flush(notLastChunk) //fmt.Println("flush:", cn) 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] = dir{FileInfo: fi, 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 }