187 lines
4 KiB
Go
187 lines
4 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
|
|
unionfs "github.com/hanwen/go-fuse/v2/fs"
|
|
"github.com/hanwen/go-fuse/v2/fuse"
|
|
)
|
|
|
|
var (
|
|
_ unionfs.NodeReader = (*unionFsFile)(nil)
|
|
_ unionfs.NodeWriter = (*unionFsFile)(nil)
|
|
_ unionfs.NodeOpener = (*unionFsFile)(nil)
|
|
_ unionfs.NodeReleaser = (*unionFsFile)(nil)
|
|
_ unionfs.NodeGetattrer = (*unionFsFile)(nil)
|
|
_ unionfs.NodeSetattrer = (*unionFsFile)(nil)
|
|
)
|
|
|
|
type unionFsFile struct {
|
|
unionfs.Inode
|
|
targetPath string
|
|
readWrite bool
|
|
}
|
|
|
|
func (r *unionFsFile) Getattr(ctx context.Context, f unionfs.FileHandle, out *fuse.AttrOut) syscall.Errno {
|
|
if f == nil {
|
|
return getFileAttributes(r.targetPath, out)
|
|
}
|
|
|
|
handle, ok := f.(unionfs.FileGetattrer)
|
|
if !ok {
|
|
return syscall.ENOTSUP
|
|
}
|
|
|
|
return handle.Getattr(ctx, out)
|
|
}
|
|
|
|
func (r *unionFsFile) Setattr(ctx context.Context, f unionfs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
|
|
if f == nil {
|
|
return setFileAttributes(r.targetPath, in, out)
|
|
}
|
|
|
|
handle, ok := f.(unionfs.FileSetattrer)
|
|
if !ok {
|
|
return syscall.ENOTSUP
|
|
}
|
|
|
|
return handle.Setattr(ctx, in, out)
|
|
}
|
|
|
|
func (r *unionFsFile) Open(_ context.Context, _ uint32) (fh unionfs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
|
f, err := os.Open(r.targetPath)
|
|
if err != nil {
|
|
return nil, 0, unionfs.ToErrno(err)
|
|
}
|
|
|
|
return &osFileHandle{file: f}, fuse.FOPEN_DIRECT_IO, unionfs.OK
|
|
}
|
|
|
|
func (r *unionFsFile) Read(_ context.Context, f unionfs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
|
|
handle, ok := f.(*osFileHandle)
|
|
if !ok {
|
|
return nil, syscall.EINVAL
|
|
}
|
|
|
|
n, err := handle.file.ReadAt(dest, off)
|
|
switch {
|
|
case err == nil || errors.Is(err, io.EOF):
|
|
return fuse.ReadResultData(dest[:n]), unionfs.OK
|
|
default:
|
|
return nil, unionfs.ToErrno(err)
|
|
}
|
|
}
|
|
|
|
func (r *unionFsFile) Write(_ context.Context, f unionfs.FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno) {
|
|
if !r.readWrite {
|
|
return 0, syscall.EROFS
|
|
}
|
|
|
|
handle, ok := f.(*osFileHandle)
|
|
if !ok {
|
|
return 0, syscall.EINVAL
|
|
}
|
|
|
|
n, err := handle.file.WriteAt(data, off)
|
|
|
|
return uint32(n), unionfs.ToErrno(err)
|
|
}
|
|
|
|
func (r *unionFsFile) Release(_ context.Context, f unionfs.FileHandle) syscall.Errno {
|
|
handle, ok := f.(*osFileHandle)
|
|
if !ok {
|
|
return syscall.EINVAL
|
|
}
|
|
|
|
if err := handle.file.Close(); err != nil {
|
|
return unionfs.ToErrno(err)
|
|
}
|
|
|
|
return unionfs.OK
|
|
}
|
|
|
|
var (
|
|
_ unionfs.FileSetattrer = (*osFileHandle)(nil)
|
|
_ unionfs.FileGetattrer = (*osFileHandle)(nil)
|
|
)
|
|
|
|
type osFileHandle struct {
|
|
file *os.File
|
|
}
|
|
|
|
func (o *osFileHandle) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
|
|
return getFileAttributes(o.file.Name(), out)
|
|
}
|
|
|
|
func (o *osFileHandle) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
|
|
return setFileAttributes(o.file.Name(), in, out)
|
|
}
|
|
|
|
func getFileAttributes(filePath string, out *fuse.AttrOut) syscall.Errno {
|
|
st := syscall.Stat_t{}
|
|
err := syscall.Lstat(filePath, &st)
|
|
if err != nil {
|
|
return unionfs.ToErrno(err)
|
|
}
|
|
out.FromStat(&st)
|
|
|
|
return unionfs.OK
|
|
}
|
|
|
|
func setFileAttributes(filePath string, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
|
|
if m, ok := in.GetMode(); ok {
|
|
if err := syscall.Chmod(filePath, m); err != nil {
|
|
return unionfs.ToErrno(err)
|
|
}
|
|
}
|
|
|
|
uid, uok := in.GetUID()
|
|
gid, gok := in.GetGID()
|
|
if uok || gok {
|
|
suid := -1
|
|
sgid := -1
|
|
if uok {
|
|
suid = int(uid)
|
|
}
|
|
if gok {
|
|
sgid = int(gid)
|
|
}
|
|
if err := syscall.Chown(filePath, suid, sgid); err != nil {
|
|
return unionfs.ToErrno(err)
|
|
}
|
|
}
|
|
|
|
mtime, mok := in.GetMTime()
|
|
atime, aok := in.GetATime()
|
|
|
|
if mok || aok {
|
|
ap := &atime
|
|
mp := &mtime
|
|
if !aok {
|
|
ap = nil
|
|
}
|
|
if !mok {
|
|
mp = nil
|
|
}
|
|
var ts [2]syscall.Timespec
|
|
ts[0] = fuse.UtimeToTimespec(ap)
|
|
ts[1] = fuse.UtimeToTimespec(mp)
|
|
|
|
if err := syscall.UtimesNano(filePath, ts[:]); err != nil {
|
|
return unionfs.ToErrno(err)
|
|
}
|
|
}
|
|
|
|
if sz, ok := in.GetSize(); ok {
|
|
if err := syscall.Truncate(filePath, int64(sz)); err != nil {
|
|
return unionfs.ToErrno(err)
|
|
}
|
|
}
|
|
|
|
return getFileAttributes(filePath, out)
|
|
}
|