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
@@ -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)
26 changes: 24 additions & 2 deletions pkg/vfs/handle.go
Original file line number Diff line number Diff line change
@@ -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++
@@ -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()
@@ -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
}

@@ -214,8 +232,12 @@ func (v *VFS) releaseHandle(inode Ino, fh uint64) {
}
}

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

func (v *VFS) newFileHandle(inode Ino, length uint64, flags uint32) uint64 {
h := v.newHandle(inode)
h := v.newHandle(inode, hasReadPerm(flags))
zhijian-pro marked this conversation as resolved.
Show resolved Hide resolved
h.Lock()
defer h.Unlock()
h.flags = flags
2 changes: 1 addition & 1 deletion pkg/vfs/internal.go
Original file line number Diff line number Diff line change
@@ -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
}
54 changes: 44 additions & 10 deletions pkg/vfs/vfs.go
Original file line number Diff line number Diff line change
@@ -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
}

@@ -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 {
@@ -647,18 +647,30 @@ func (v *VFS) Release(ctx Context, ino Ino, fh uint64) {
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 {
@@ -687,6 +699,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
@@ -695,6 +723,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.EACCES
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

   EBADF  fd is not a valid file descriptor or is not open for reading.

return
}
if !h.Rlock(ctx) {
err = syscall.EINTR
return
3 changes: 2 additions & 1 deletion pkg/vfs/vfs_test.go
Original file line number Diff line number Diff line change
@@ -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
@@ -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 {