Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mount: recovery read only file handler #4524

Merged
merged 14 commits into from
Mar 20, 2024
7 changes: 4 additions & 3 deletions pkg/fuse/fuse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@ func mount(url, mp string) {
}))

conf := &vfs.Config{
Meta: metaConf,
Format: *format,
Chunk: &chunkConf,
Meta: metaConf,
Format: *format,
Chunk: &chunkConf,
FuseOpts: &vfs.FuseOptions{},
}

err = m.NewSession(true)
Expand Down
22 changes: 20 additions & 2 deletions pkg/vfs/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,16 @@ func (h *handle) Close() {
}
}

func (v *VFS) newHandle(inode Ino) *handle {
func (v *VFS) newHandle(inode Ino, readOnly bool) *handle {
v.hanleM.Lock()
defer v.hanleM.Unlock()
var lowBits uint64
if readOnly {
lowBits = 1
}
for v.handleIno[v.nextfh] > 0 || v.nextfh&1 != lowBits {
v.nextfh++ // skip recovered fd
}
fh := v.nextfh
h := &handle{inode: inode, fh: fh}
v.nextfh++
Expand All @@ -184,6 +191,8 @@ func (v *VFS) findAllHandles(inode Ino) []*handle {
return hs2
}

const O_RECOVERED = 1 << 31 // is recovered fd

func (v *VFS) findHandle(inode Ino, fh uint64) *handle {
v.hanleM.Lock()
defer v.hanleM.Unlock()
Expand All @@ -192,6 +201,15 @@ func (v *VFS) findHandle(inode Ino, fh uint64) *handle {
return f
}
}
if fh&1 == 1 && inode != controlInode {
f := &handle{inode: inode, fh: fh, flags: O_RECOVERED}
f.cond = utils.NewCond(f)
v.handles[inode] = append(v.handles[inode], f)
if v.handleIno[fh] == 0 {
v.handleIno[fh] = inode
}
return f
}
return nil
}

Expand All @@ -215,7 +233,7 @@ func (v *VFS) releaseHandle(inode Ino, fh uint64) {
}

func (v *VFS) newFileHandle(inode Ino, length uint64, flags uint32) uint64 {
h := v.newHandle(inode)
h := v.newHandle(inode, (flags&O_ACCMODE) == syscall.O_RDONLY)
h.Lock()
defer h.Unlock()
h.flags = flags
Expand Down
2 changes: 1 addition & 1 deletion pkg/vfs/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (v *VFS) getControlHandle(pid uint32) uint64 {
defer controlMutex.Unlock()
fh := controlHandlers[pid]
if fh == 0 {
h := v.newHandle(controlInode)
h := v.newHandle(controlInode, false)
fh = h.fh
controlHandlers[pid] = fh
}
Expand Down
58 changes: 48 additions & 10 deletions pkg/vfs/vfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ func (v *VFS) Opendir(ctx Context, ino Ino, flags uint32) (fh uint64, err syscal
return
}
}
fh = v.newHandle(ino).fh
fh = v.newHandle(ino, true).fh
return
}

Expand Down Expand Up @@ -519,7 +519,7 @@ func (v *VFS) Open(ctx Context, ino Ino, flags uint32) (entry *meta.Entry, fh ui
err = syscall.EACCES
return
}
h := v.newHandle(ino)
h := v.newHandle(ino, true)
fh = h.fh
n := getInternalNode(ino)
if n == nil {
Expand Down Expand Up @@ -644,21 +644,37 @@ func (v *VFS) Release(ctx Context, ino Ino, fh uint64) {
}
}

func hasReadPerm(flag uint32) bool {
return (flag & O_ACCMODE) != syscall.O_WRONLY
}

func (v *VFS) Read(ctx Context, ino Ino, buf []byte, off uint64, fh uint64) (n int, err syscall.Errno) {
size := uint32(len(buf))
if IsSpecialNode(ino) {
if ino == controlInode && runtime.GOOS == "darwin" {
fh = v.getControlHandle(ctx.Pid())
}
h := v.findHandle(ino, fh)
if h == nil {
err = syscall.EBADF
return
}
switch ino {
case statsInode:
h.data = collectMetrics(v.registry)
case configInode:
v.Conf.Format = v.Meta.GetFormat()
if v.UpdateFormat != nil {
v.UpdateFormat(&v.Conf.Format)
}
v.Conf.Format.RemoveSecret()
h.data, _ = json.MarshalIndent(v.Conf, "", " ")
}

if ino == logInode {
n = readAccessLog(fh, buf)
} else {
defer func() { logit(ctx, "read (%d,%d,%d,%d): %s (%d)", ino, size, off, fh, strerr(err), n) }()
if ino == controlInode && runtime.GOOS == "darwin" {
fh = v.getControlHandle(ctx.Pid())
}
h := v.findHandle(ino, fh)
if h == nil {
err = syscall.EBADF
return
}
h.Lock()
defer h.Unlock()
if off < h.off {
Expand Down Expand Up @@ -687,6 +703,22 @@ func (v *VFS) Read(ctx Context, ino Ino, buf []byte, off uint64, fh uint64) (n i
err = syscall.EBADF
return
}
if h.flags&O_RECOVERED != 0 {
// recovered
var attr Attr
err = v.Meta.Open(ctx, ino, syscall.O_RDONLY, &attr)
if err != 0 {
v.releaseHandle(ino, fh)
err = syscall.EBADF
return
}
h.Lock()
v.UpdateLength(ino, &attr)
h.flags = syscall.O_RDONLY
h.reader = v.reader.Open(h.inode, attr.Length)
h.Unlock()
}

if off >= maxFileSize || off+uint64(size) >= maxFileSize {
err = syscall.EFBIG
return
Expand All @@ -695,6 +727,12 @@ func (v *VFS) Read(ctx Context, ino Ino, buf []byte, off uint64, fh uint64) (n i
err = syscall.EBADF
return
}

// there could be read operation for write-only if kernel writeback is enabled
if !v.Conf.FuseOpts.EnableWriteback && !hasReadPerm(h.flags) {
err = syscall.EBADF
return
}
if !h.Rlock(ctx) {
err = syscall.EINTR
return
Expand Down
3 changes: 2 additions & 1 deletion pkg/vfs/vfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func createTestVFS() (*VFS, object.ObjectStorage) {
CacheSize: 10,
CacheDir: "memory",
},
FuseOpts: &FuseOptions{},
}
blob, _ := object.CreateStorage("mem", "", "", "", "")
registry := prometheus.NewRegistry() // replace default so only JuiceFS metrics are exposed
Expand Down Expand Up @@ -205,7 +206,7 @@ func TestVFSIO(t *testing.T) {
t.Fatalf("write file: %s", e)
}
var attr meta.Attr
if e = v.Truncate(ctx, fe.Inode, (100<<20)+2, 1, &attr); e != 0 {
if e = v.Truncate(ctx, fe.Inode, (100<<20)+2, fh, &attr); e != 0 {
t.Fatalf("truncate file: %s", e)
}
if n, e := v.CopyFileRange(ctx, fe.Inode, fh, 0, fe.Inode, fh, 10<<20, 10, 0); e != 0 || n != 10 {
Expand Down
Loading