From 6c43d2aa3bc5b8f809693745999d21674fd7ce14 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys <hanwen@google.com> Date: Sun, 24 Feb 2013 14:17:53 +0100 Subject: [PATCH] Support FALLOCATE opcode. --- fuse/api.go | 3 +++ fuse/defaultfile.go | 4 ++++ fuse/defaultnode.go | 4 ++++ fuse/defaultraw.go | 4 ++++ fuse/files.go | 14 ++++++++++++++ fuse/fsops.go | 7 +++++++ fuse/lockingfs.go | 5 +++++ fuse/loopback.go | 3 +++ fuse/loopback_test.go | 16 ++++++++++++++++ fuse/opcode.go | 17 +++++++++++++---- fuse/pathfs.go | 20 ++++++++++++++++++++ 11 files changed, 93 insertions(+), 4 deletions(-) diff --git a/fuse/api.go b/fuse/api.go index 7012989..d0e7fbd 100644 --- a/fuse/api.go +++ b/fuse/api.go @@ -79,6 +79,7 @@ type FsNode interface { Chown(file File, uid uint32, gid uint32, context *Context) (code Status) Truncate(file File, size uint64, context *Context) (code Status) Utimens(file File, atime *time.Time, mtime *time.Time, context *Context) (code Status) + Fallocate(file File, off uint64, size uint64, mode uint32, context *Context) (code Status) StatFs() *StatfsOut } @@ -180,6 +181,7 @@ type File interface { Chown(uid uint32, gid uint32) Status Chmod(perms uint32) Status Utimens(atime *time.Time, mtime *time.Time) Status + Allocate(off uint64, size uint64, mode uint32) (code Status) } // The result of Read is an array of bytes, but for performance @@ -321,6 +323,7 @@ type RawFileSystem interface { Write(*Context, *raw.WriteIn, []byte) (written uint32, code Status) Flush(context *Context, input *raw.FlushIn) Status Fsync(*Context, *raw.FsyncIn) (code Status) + Fallocate(Context *Context, in *raw.FallocateIn) (code Status) // Directory handling OpenDir(out *raw.OpenOut, context *Context, input *raw.OpenIn) (status Status) diff --git a/fuse/defaultfile.go b/fuse/defaultfile.go index 50f21b6..9b7d4ea 100644 --- a/fuse/defaultfile.go +++ b/fuse/defaultfile.go @@ -65,3 +65,7 @@ func (f *DefaultFile) Chmod(perms uint32) Status { func (f *DefaultFile) Ioctl(input *raw.IoctlIn) (output *raw.IoctlOut, data []byte, code Status) { return nil, nil, ENOSYS } + +func (f *DefaultFile) Allocate(off uint64, size uint64, mode uint32) (code Status) { + return ENOSYS +} diff --git a/fuse/defaultnode.go b/fuse/defaultnode.go index 84505cc..ade80e3 100644 --- a/fuse/defaultnode.go +++ b/fuse/defaultnode.go @@ -154,3 +154,7 @@ func (n *DefaultFsNode) Truncate(file File, size uint64, context *Context) (code func (n *DefaultFsNode) Utimens(file File, atime *time.Time, mtime *time.Time, context *Context) (code Status) { return ENOSYS } + +func (n *DefaultFsNode) Fallocate(file File, off uint64, size uint64, mode uint32, context *Context) (code Status) { + return ENOSYS +} diff --git a/fuse/defaultraw.go b/fuse/defaultraw.go index 83bfaff..9d9820f 100644 --- a/fuse/defaultraw.go +++ b/fuse/defaultraw.go @@ -131,3 +131,7 @@ func (fs *DefaultRawFileSystem) ReleaseDir(context *Context, input *raw.ReleaseI func (fs *DefaultRawFileSystem) FsyncDir(context *Context, input *raw.FsyncIn) (code Status) { return ENOSYS } + +func (fs *DefaultRawFileSystem) Fallocate(context *Context, in *raw.FallocateIn) (code Status) { + return ENOSYS +} diff --git a/fuse/files.go b/fuse/files.go index 9fe2369..cf74f50 100644 --- a/fuse/files.go +++ b/fuse/files.go @@ -193,6 +193,16 @@ func (f *LoopbackFile) GetAttr(a *Attr) Status { return OK } +func (f *LoopbackFile) Allocate(off uint64, sz uint64, mode uint32) Status { + f.lock.Lock() + err := syscall.Fallocate(int(f.File.Fd()), mode, int64(off), int64(sz)) + f.lock.Unlock() + if err != nil { + return ToStatus(err) + } + return OK +} + //////////////////////////////////////////////////////////////// // ReadOnlyFile is a wrapper that denies writable operations @@ -226,3 +236,7 @@ func (f *ReadOnlyFile) Chmod(mode uint32) Status { func (f *ReadOnlyFile) Chown(uid uint32, gid uint32) Status { return EPERM } + +func (f *ReadOnlyFile) Allocate(off uint64, sz uint64, mode uint32) Status { + return EPERM +} diff --git a/fuse/fsops.go b/fuse/fsops.go index 6ad7da2..7513380 100644 --- a/fuse/fsops.go +++ b/fuse/fsops.go @@ -214,6 +214,13 @@ func (c *FileSystemConnector) SetAttr(out *raw.AttrOut, context *Context, input return code } +func (c *FileSystemConnector) Fallocate(context *Context, in *raw.FallocateIn) (code Status) { + n := c.toInode(context.NodeId) + opened := n.mount.getOpenedFile(in.Fh) + + return n.fsInode.Fallocate(opened, in.Offset, in.Length, in.Mode, context) +} + func (c *FileSystemConnector) Readlink(context *Context) (out []byte, code Status) { n := c.toInode(context.NodeId) return n.fsInode.Readlink(context) diff --git a/fuse/lockingfs.go b/fuse/lockingfs.go index 1fe9a9f..02fd9c8 100644 --- a/fuse/lockingfs.go +++ b/fuse/lockingfs.go @@ -332,6 +332,11 @@ func (fs *LockingRawFileSystem) StatFs(out *StatfsOut, context *Context) (code S return fs.RawFS.StatFs(out, context) } +func (fs *LockingRawFileSystem) Fallocate(c *Context, in *raw.FallocateIn) (code Status) { + defer fs.locked()() + return fs.RawFS.Fallocate(c, in) +} + func (fs *LockingRawFileSystem) String() string { defer fs.locked()() return fmt.Sprintf("Locked(%s)", fs.RawFS.String()) diff --git a/fuse/loopback.go b/fuse/loopback.go index cfb6781..93ef403 100644 --- a/fuse/loopback.go +++ b/fuse/loopback.go @@ -166,3 +166,6 @@ func (fs *LoopbackFileSystem) Create(path string, flags uint32, mode uint32, con f, err := os.OpenFile(fs.GetPath(path), int(flags)|os.O_CREATE, os.FileMode(mode)) return &LoopbackFile{File: f}, ToStatus(err) } + + + diff --git a/fuse/loopback_test.go b/fuse/loopback_test.go index 748068c..c3f0aa3 100644 --- a/fuse/loopback_test.go +++ b/fuse/loopback_test.go @@ -815,3 +815,19 @@ func TestUmask(t *testing.T) { t.Errorf("got %o, expect mode %o for file %s", got, expect, fn) } } + +func TestFallocate(t *testing.T) { + ts := NewTestCase(t) + defer ts.Cleanup() + rwFile, err := os.OpenFile(ts.mnt+"/file", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) + CheckSuccess(err) + defer rwFile.Close() + err = syscall.Fallocate(int(rwFile.Fd()), 0, 1024, 4096) + CheckSuccess(err) + fi, err := os.Lstat(ts.orig + "/file") + CheckSuccess(err) + if fi.Size() < (1024+4096) { + t.Fatalf("fallocate should have changed file size. Got %d bytes", + fi.Size()) + } +} diff --git a/fuse/opcode.go b/fuse/opcode.go index fbdeecf..e1bc7ed 100644 --- a/fuse/opcode.go +++ b/fuse/opcode.go @@ -54,13 +54,14 @@ const ( _OP_POLL = int32(40) _OP_NOTIFY_REPLY = int32(41) _OP_BATCH_FORGET = int32(42) + _OP_FALLOCATE = int32(43) // protocol version 19. // Ugh - what will happen if FUSE introduces a new opcode here? - _OP_NOTIFY_ENTRY = int32(51) - _OP_NOTIFY_INODE = int32(52) - _OP_NOTIFY_DELETE = int32(53) + _OP_NOTIFY_ENTRY = int32(100) + _OP_NOTIFY_INODE = int32(101) + _OP_NOTIFY_DELETE = int32(102) // protocol version 18 - _OPCODE_COUNT = int32(54) + _OPCODE_COUNT = int32(103) ) //////////////////////////////////////////////////////////////// @@ -337,6 +338,10 @@ func doDestroy(state *MountState, req *request) { req.status = OK } +func doFallocate(state *MountState, req *request) { + req.status = state.fileSystem.Fallocate(&req.context, (*raw.FallocateIn)(req.inData)) +} + //////////////////////////////////////////////////////////////// type operationFunc func(*MountState, *request) @@ -410,6 +415,7 @@ func init() { _OP_BMAP: unsafe.Sizeof(raw.BmapIn{}), _OP_IOCTL: unsafe.Sizeof(raw.IoctlIn{}), _OP_POLL: unsafe.Sizeof(raw.PollIn{}), + _OP_FALLOCATE: unsafe.Sizeof(raw.FallocateIn{}), } { operationHandlers[op].InputSize = sz } @@ -483,6 +489,7 @@ func init() { _OP_NOTIFY_ENTRY: "NOTIFY_ENTRY", _OP_NOTIFY_INODE: "NOTIFY_INODE", _OP_NOTIFY_DELETE: "NOTIFY_DELETE", + _OP_FALLOCATE: "FALLOCATE", } { operationHandlers[op].Name = v } @@ -521,6 +528,7 @@ func init() { _OP_STATFS: doStatFs, _OP_IOCTL: doIoctl, _OP_DESTROY: doDestroy, + _OP_FALLOCATE: doFallocate, } { operationHandlers[op].Func = v } @@ -565,6 +573,7 @@ func init() { _OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*raw.MkdirIn)(ptr) }, _OP_RELEASE: func(ptr unsafe.Pointer) interface{} { return (*raw.ReleaseIn)(ptr) }, _OP_RELEASEDIR: func(ptr unsafe.Pointer) interface{} { return (*raw.ReleaseIn)(ptr) }, + _OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*raw.FallocateIn)(ptr) }, } { operationHandlers[op].DecodeIn = f } diff --git a/fuse/pathfs.go b/fuse/pathfs.go index c9e4ed9..361f588 100644 --- a/fuse/pathfs.go +++ b/fuse/pathfs.go @@ -640,3 +640,23 @@ func (n *pathInode) Utimens(file File, atime *time.Time, mtime *time.Time, conte } return code } + +func (n *pathInode) Fallocate(file File, off uint64, size uint64, mode uint32, context *Context) (code Status) { + if file != nil { + code = file.Allocate(off, size, mode) + if code.Ok() { + return code + } + } + + files := n.inode.Files(O_ANYWRITE) + for _, f := range files { + // TODO - pass context + code = f.Allocate(off, size, mode) + if code.Ok() { + return code + } + } + + return code +} -- 2.30.9