buildr/internal/storage/files_linux.go
Peter 1261932bdc
All checks were successful
continuous-integration/drone/push Build is passing
refactor: apply golangci-lint findings
2023-06-22 19:16:00 +02:00

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)
}