diff --git a/weed/mount/filehandle.go b/weed/mount/filehandle.go new file mode 100644 index 000000000..551394262 --- /dev/null +++ b/weed/mount/filehandle.go @@ -0,0 +1,16 @@ +package mount + +import "github.com/hanwen/go-fuse/v2/fuse" + +func (wfs *WFS) AcquireHandle(inode uint64, uid, gid uint32) (fileHandle *FileHandle, code fuse.Status) { + _, entry, status := wfs.maybeReadEntry(inode) + if status == fuse.OK { + fileHandle = wfs.fhmap.GetFileHandle(inode) + fileHandle.entry = entry + } + return +} + +func (wfs *WFS) ReleaseHandle(handleId FileHandleId) { + wfs.fhmap.ReleaseByHandle(handleId) +} diff --git a/weed/mount/filehandle_map.go b/weed/mount/filehandle_map.go new file mode 100644 index 000000000..ca010dabb --- /dev/null +++ b/weed/mount/filehandle_map.go @@ -0,0 +1,79 @@ +package mount + +import ( + "github.com/chrislusf/seaweedfs/weed/pb/filer_pb" + "sync" +) + +type FileHandleId uint64 + +type FileHandleToInode struct { + sync.RWMutex + nextFh FileHandleId + inode2fh map[uint64]*FileHandle + fh2inode map[FileHandleId]uint64 +} +type FileHandle struct { + fh FileHandleId + counter int64 + entry *filer_pb.Entry + inode uint64 +} + +func NewFileHandleToInode() *FileHandleToInode { + return &FileHandleToInode{ + inode2fh: make(map[uint64]*FileHandle), + fh2inode: make(map[FileHandleId]uint64), + nextFh: 0, + } +} + +func (i *FileHandleToInode) GetFileHandle(inode uint64) *FileHandle { + i.Lock() + defer i.Unlock() + fh, found := i.inode2fh[inode] + if !found { + fh = &FileHandle{ + fh: i.nextFh, + counter: 1, + inode: inode, + } + i.nextFh++ + i.inode2fh[inode] = fh + i.fh2inode[fh.fh] = inode + } else { + fh.counter++ + } + return fh +} + +func (i *FileHandleToInode) ReleaseByInode(inode uint64) { + i.Lock() + defer i.Unlock() + fh, found := i.inode2fh[inode] + if found { + fh.counter-- + if fh.counter <= 0 { + delete(i.inode2fh, inode) + delete(i.fh2inode, fh.fh) + } + } +} +func (i *FileHandleToInode) ReleaseByHandle(fh FileHandleId) { + i.Lock() + defer i.Unlock() + inode, found := i.fh2inode[fh] + if found { + fhHandle, fhFound := i.inode2fh[inode] + if !fhFound { + delete(i.fh2inode, fh) + } else { + fhHandle.counter-- + if fhHandle.counter <= 0 { + delete(i.inode2fh, inode) + delete(i.fh2inode, fhHandle.fh) + } + } + + } +} diff --git a/weed/mount/inode_to_path.go b/weed/mount/inode_to_path.go index 529ecadda..590531397 100644 --- a/weed/mount/inode_to_path.go +++ b/weed/mount/inode_to_path.go @@ -37,7 +37,6 @@ func (i *InodeToPath) Lookup(path util.FullPath) uint64 { i.nextInodeId++ i.path2inode[path] = inode i.inode2path[inode] = &InodeEntry{path, 1} - println("add", path, inode) } else { i.inode2path[inode].nlookup++ } diff --git a/weed/mount/weedfs.go b/weed/mount/weedfs.go index a36e4dc97..1e9f07df9 100644 --- a/weed/mount/weedfs.go +++ b/weed/mount/weedfs.go @@ -60,6 +60,7 @@ type WFS struct { root Directory signature int32 inodeToPath *InodeToPath + fhmap *FileHandleToInode } func NewSeaweedFileSystem(option *Option) *WFS { @@ -68,6 +69,7 @@ func NewSeaweedFileSystem(option *Option) *WFS { option: option, signature: util.RandomInt32(), inodeToPath: NewInodeToPath(), + fhmap: NewFileHandleToInode(), } wfs.root = Directory{ diff --git a/weed/mount/weedfs_file_io.go b/weed/mount/weedfs_file_io.go index 0a31c3d70..7c8d1babc 100644 --- a/weed/mount/weedfs_file_io.go +++ b/weed/mount/weedfs_file_io.go @@ -4,65 +4,6 @@ import ( "github.com/hanwen/go-fuse/v2/fuse" ) -/** - * Read data - * - * Read should send exactly the number of bytes requested except - * on EOF or error, otherwise the rest of the data will be - * substituted with zeroes. An exception to this is when the file - * has been opened in 'direct_io' mode, in which case the return - * value of the read system call will reflect the return value of - * this operation. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_iov - * fuse_reply_data - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size number of bytes to read - * @param off offset to read from - * @param fi file information - */ -func (wfs *WFS) Read(cancel <-chan struct{}, in *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) { - return nil, fuse.ENOSYS -} - -/** - * Write data - * - * Write should return exactly the number of bytes requested - * except on error. An exception to this is when the file has - * been opened in 'direct_io' mode, in which case the return value - * of the write system call will reflect the return value of this - * operation. - * - * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is - * expected to reset the setuid and setgid bits. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * Valid replies: - * fuse_reply_write - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param buf data to write - * @param size number of bytes to write - * @param off offset to write to - * @param fi file information - */ -func (wfs *WFS) Write(cancel <-chan struct{}, in *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) { - return 0, fuse.ENOSYS -} - /** * Open a file * @@ -120,49 +61,11 @@ func (wfs *WFS) Write(cancel <-chan struct{}, in *fuse.WriteIn, data []byte) (wr * @param fi file information */ func (wfs *WFS) Open(cancel <-chan struct{}, in *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) { - return fuse.ENOSYS -} - -/** - * Flush method - * - * This is called on each close() of the opened file. - * - * Since file descriptors can be duplicated (dup, dup2, fork), for - * one open call there may be many flush calls. - * - * Filesystems shouldn't assume that flush will always be called - * after some writes, or that if will be called at all. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * NOTE: the name of the method is misleading, since (unlike - * fsync) the filesystem is not forced to flush pending writes. - * One reason to flush data is if the filesystem wants to return - * write errors during close. However, such use is non-portable - * because POSIX does not require [close] to wait for delayed I/O to - * complete. - * - * If the filesystem supports file locking operations (setlk, - * getlk) it should remove all locks belonging to 'fi->owner'. - * - * If this request is answered with an error code of ENOSYS, - * this is treated as success and future calls to flush() will - * succeed automatically without being send to the filesystem - * process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * - * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html - */ -func (wfs *WFS) Flush(cancel <-chan struct{}, in *fuse.FlushIn) fuse.Status { - return fuse.ENOSYS + fileHandle, code := wfs.AcquireHandle(in.NodeId, in.Uid, in.Gid) + if code == fuse.OK { + out.Fh = uint64(fileHandle.fh) + } + return code } /** @@ -191,27 +94,5 @@ func (wfs *WFS) Flush(cancel <-chan struct{}, in *fuse.FlushIn) fuse.Status { * @param fi file information */ func (wfs *WFS) Release(cancel <-chan struct{}, in *fuse.ReleaseIn) { -} - -/** - * Synchronize file contents - * - * If the datasync parameter is non-zero, then only the user data - * should be flushed, not the meta data. - * - * If this request is answered with an error code of ENOSYS, - * this is treated as success and future calls to fsync() will - * succeed automatically without being send to the filesystem - * process. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param datasync flag indicating if only data should be flushed - * @param fi file information - */ -func (wfs *WFS) Fsync(cancel <-chan struct{}, in *fuse.FsyncIn) (code fuse.Status) { - return fuse.ENOSYS + wfs.ReleaseHandle(FileHandleId(in.Fh)) } diff --git a/weed/mount/weedfs_file_read.go b/weed/mount/weedfs_file_read.go new file mode 100644 index 000000000..d9ad1f4ea --- /dev/null +++ b/weed/mount/weedfs_file_read.go @@ -0,0 +1,34 @@ +package mount + +import ( + "github.com/hanwen/go-fuse/v2/fuse" +) + +/** + * Read data + * + * Read should send exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the file + * has been opened in 'direct_io' mode, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_iov + * fuse_reply_data + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size number of bytes to read + * @param off offset to read from + * @param fi file information + */ +func (wfs *WFS) Read(cancel <-chan struct{}, in *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) { + return nil, fuse.ENOSYS +} diff --git a/weed/mount/weedfs_file_sync.go b/weed/mount/weedfs_file_sync.go new file mode 100644 index 000000000..1b89c1ecb --- /dev/null +++ b/weed/mount/weedfs_file_sync.go @@ -0,0 +1,70 @@ +package mount + +import ( + "github.com/hanwen/go-fuse/v2/fuse" +) + +/** + * Flush method + * + * This is called on each close() of the opened file. + * + * Since file descriptors can be duplicated (dup, dup2, fork), for + * one open call there may be many flush calls. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * NOTE: the name of the method is misleading, since (unlike + * fsync) the filesystem is not forced to flush pending writes. + * One reason to flush data is if the filesystem wants to return + * write errors during close. However, such use is non-portable + * because POSIX does not require [close] to wait for delayed I/O to + * complete. + * + * If the filesystem supports file locking operations (setlk, + * getlk) it should remove all locks belonging to 'fi->owner'. + * + * If this request is answered with an error code of ENOSYS, + * this is treated as success and future calls to flush() will + * succeed automatically without being send to the filesystem + * process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * + * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html + */ +func (wfs *WFS) Flush(cancel <-chan struct{}, in *fuse.FlushIn) fuse.Status { + return fuse.ENOSYS +} + +/** + * Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * If this request is answered with an error code of ENOSYS, + * this is treated as success and future calls to fsync() will + * succeed automatically without being send to the filesystem + * process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ +func (wfs *WFS) Fsync(cancel <-chan struct{}, in *fuse.FsyncIn) (code fuse.Status) { + return fuse.ENOSYS +} diff --git a/weed/mount/weedfs_file_write.go b/weed/mount/weedfs_file_write.go new file mode 100644 index 000000000..72152d72e --- /dev/null +++ b/weed/mount/weedfs_file_write.go @@ -0,0 +1,35 @@ +package mount + +import ( + "github.com/hanwen/go-fuse/v2/fuse" +) + +/** + * Write data + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the file has + * been opened in 'direct_io' mode, in which case the return value + * of the write system call will reflect the return value of this + * operation. + * + * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is + * expected to reset the setuid and setgid bits. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_write + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param buf data to write + * @param size number of bytes to write + * @param off offset to write to + * @param fi file information + */ +func (wfs *WFS) Write(cancel <-chan struct{}, in *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) { + return 0, fuse.ENOSYS +}