// Copyright 2019 the Go-FUSE Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package fs import ( "context" "sync" // "time" "syscall" "github.com/hanwen/go-fuse/v2/fuse" "golang.org/x/sys/unix" ) // NewLoopbackFile creates a FileHandle out of a file descriptor. All // operations are implemented. func NewLoopbackFile(fd int) FileHandle { return &loopbackFile{fd: fd} } type loopbackFile struct { mu sync.Mutex fd int } var _ = (FileHandle)((*loopbackFile)(nil)) var _ = (FileReleaser)((*loopbackFile)(nil)) var _ = (FileGetattrer)((*loopbackFile)(nil)) var _ = (FileReader)((*loopbackFile)(nil)) var _ = (FileWriter)((*loopbackFile)(nil)) var _ = (FileGetlker)((*loopbackFile)(nil)) var _ = (FileSetlker)((*loopbackFile)(nil)) var _ = (FileSetlkwer)((*loopbackFile)(nil)) var _ = (FileLseeker)((*loopbackFile)(nil)) var _ = (FileFlusher)((*loopbackFile)(nil)) var _ = (FileFsyncer)((*loopbackFile)(nil)) var _ = (FileSetattrer)((*loopbackFile)(nil)) var _ = (FileAllocater)((*loopbackFile)(nil)) func (f *loopbackFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) { f.mu.Lock() defer f.mu.Unlock() r := fuse.ReadResultFd(uintptr(f.fd), off, len(buf)) return r, OK } func (f *loopbackFile) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) { f.mu.Lock() defer f.mu.Unlock() n, err := syscall.Pwrite(f.fd, data, off) return uint32(n), ToErrno(err) } func (f *loopbackFile) Release(ctx context.Context) syscall.Errno { f.mu.Lock() defer f.mu.Unlock() if f.fd != -1 { err := syscall.Close(f.fd) f.fd = -1 return ToErrno(err) } return syscall.EBADF } func (f *loopbackFile) Flush(ctx context.Context) syscall.Errno { f.mu.Lock() defer f.mu.Unlock() // Since Flush() may be called for each dup'd fd, we don't // want to really close the file, we just want to flush. This // is achieved by closing a dup'd fd. newFd, err := syscall.Dup(f.fd) if err != nil { return ToErrno(err) } err = syscall.Close(newFd) return ToErrno(err) } func (f *loopbackFile) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) { f.mu.Lock() defer f.mu.Unlock() r := ToErrno(syscall.Fsync(f.fd)) return r } const ( _OFD_GETLK = 36 _OFD_SETLK = 37 _OFD_SETLKW = 38 ) func (f *loopbackFile) Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (errno syscall.Errno) { f.mu.Lock() defer f.mu.Unlock() flk := syscall.Flock_t{} lk.ToFlockT(&flk) errno = ToErrno(syscall.FcntlFlock(uintptr(f.fd), _OFD_GETLK, &flk)) out.FromFlockT(&flk) return } func (f *loopbackFile) Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) { return f.setLock(ctx, owner, lk, flags, false) } func (f *loopbackFile) Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) { return f.setLock(ctx, owner, lk, flags, true) } func (f *loopbackFile) setLock(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, blocking bool) (errno syscall.Errno) { f.mu.Lock() defer f.mu.Unlock() if (flags & fuse.FUSE_LK_FLOCK) != 0 { var op int switch lk.Typ { case syscall.F_RDLCK: op = syscall.LOCK_SH case syscall.F_WRLCK: op = syscall.LOCK_EX case syscall.F_UNLCK: op = syscall.LOCK_UN default: return syscall.EINVAL } if !blocking { op |= syscall.LOCK_NB } return ToErrno(syscall.Flock(f.fd, op)) } else { flk := syscall.Flock_t{} lk.ToFlockT(&flk) var op int if blocking { op = _OFD_SETLKW } else { op = _OFD_SETLK } return ToErrno(syscall.FcntlFlock(uintptr(f.fd), op, &flk)) } } func (f *loopbackFile) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno { if errno := f.setAttr(ctx, in); errno != 0 { return errno } return f.Getattr(ctx, out) } func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) syscall.Errno { f.mu.Lock() defer f.mu.Unlock() var errno syscall.Errno if mode, ok := in.GetMode(); ok { errno = ToErrno(syscall.Fchmod(f.fd, mode)) if errno != 0 { return errno } } uid32, uOk := in.GetUID() gid32, gOk := in.GetGID() if uOk || gOk { uid := -1 gid := -1 if uOk { uid = int(uid32) } if gOk { gid = int(gid32) } errno = ToErrno(syscall.Fchown(f.fd, uid, gid)) if errno != 0 { return errno } } mtime, mok := in.GetMTime() atime, aok := in.GetATime() if mok || aok { ap := &atime mp := &mtime if !aok { ap = nil } if !mok { mp = nil } errno = f.utimens(ap, mp) if errno != 0 { return errno } } if sz, ok := in.GetSize(); ok { errno = ToErrno(syscall.Ftruncate(f.fd, int64(sz))) if errno != 0 { return errno } } return OK } func (f *loopbackFile) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno { f.mu.Lock() defer f.mu.Unlock() st := syscall.Stat_t{} err := syscall.Fstat(f.fd, &st) if err != nil { return ToErrno(err) } a.FromStat(&st) return OK } func (f *loopbackFile) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) { f.mu.Lock() defer f.mu.Unlock() n, err := unix.Seek(f.fd, int64(off), int(whence)) return uint64(n), ToErrno(err) }