// Copyright (C) 2022 Marius Schellenberger package crypto import ( "cachefs/pkg/provider" osp "cachefs/pkg/provider/os" "encoding/base64" "errors" "io/fs" "os" "path/filepath" "strings" ) var _ provider.FS = (*FS)(nil) type FS struct { fs provider.FS cbc *aesCbc key []byte } func NewFS(fs provider.FS, key []byte) (*FS, error) { if len(key) != 32 { return nil, errors.New("encryption key length must be 32 bytes") } cbc, err := newAesCbc(key) if err != nil { return nil, err } return &FS{ fs: fs, cbc: cbc, key: key, }, nil } func (fs *FS) encPath(path string) string { parts := strings.Split(path, "/") cparts := make([]string, len(parts)) for i, p := range parts { if p == "" { continue } cparts[i] = base64.RawURLEncoding.EncodeToString( fs.cbc.encrypt([]byte(p)), ) } return filepath.Join(cparts...) } func (fs *FS) decPath(path string) (string, error) { parts := strings.Split(path, "/") pparts := make([]string, len(parts)) for i, p := range parts { b, err := base64.RawURLEncoding.DecodeString(p) if err != nil { return "", err } pparts[i] = string(fs.cbc.decrypt(b)) } return filepath.Join(pparts...), nil } func (fs *FS) name(p string) string { return filepath.Join(fs.Root(), p) } func (fs *FS) Stat(p string) (fs.FileInfo, error) { fi, err := fs.fs.Stat(fs.encPath(p)) return stat{FileInfo: fi, name: fs.name(p)}, err } func (fs *FS) Remove(p string) error { return fs.fs.Remove(fs.encPath(p)) } func (fs *FS) Open(p string) (provider.File, error) { c := fs.encPath(p) fi, err := fs.fs.Stat(c) if err != nil { return nil, err } f, err := fs.fs.Open(c) if err != nil { return nil, err } return newFile(fs.key, p, fs, f, fi.IsDir()) } func (fs *FS) OpenFile(p string, flags int, mode os.FileMode) (provider.File, error) { f, err := fs.fs.OpenFile(fs.encPath(p), flags, mode) if err != nil { return nil, err } return newFile(fs.key, p, fs, f, false) } func (fs *FS) MkdirAll(p string, mode os.FileMode) error { return fs.fs.MkdirAll(fs.encPath(p), mode) } func (fs *FS) Root() string { return fs.fs.Root() } func (fs *FS) Fstat(fd uintptr) (int64, error) { osp, ok := fs.fs.(*osp.FS) if ok { return osp.Fstat(fd) } return 0, provider.ErrFstat } func (fs *FS) Close() error { return nil }