From 6816661b0f7948bb77de986398f7b892a58bbd11 Mon Sep 17 00:00:00 2001
From: Chris Lu <chris.lu@gmail.com>
Date: Wed, 6 Jun 2018 02:09:57 -0700
Subject: [PATCH] fixed file handle by file full path

---
 weed/filesys/dir.go        |  9 +-----
 weed/filesys/file.go       | 34 +++++++++-----------
 weed/filesys/filehandle.go | 22 ++++++++++---
 weed/filesys/wfs.go        | 66 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+), 30 deletions(-)

diff --git a/weed/filesys/dir.go b/weed/filesys/dir.go
index bf4eda936..f8fdba2c5 100644
--- a/weed/filesys/dir.go
+++ b/weed/filesys/dir.go
@@ -122,14 +122,7 @@ func (dir *Dir) Create(ctx context.Context, req *fuse.CreateRequest,
 		file := dir.newFile(req.Name, nil)
 		dir.NodeMap[req.Name] = file
 		file.isOpen = true
-		return file, &FileHandle{
-			f:          file,
-			dirtyPages: newDirtyPages(file),
-			RequestId:  req.Header.ID,
-			NodeId:     req.Header.Node,
-			Uid:        req.Uid,
-			Gid:        req.Gid,
-		}, nil
+		return file, dir.wfs.AcquireHandle(file, req.Uid, req.Gid), nil
 	}
 
 	return nil, nil, err
diff --git a/weed/filesys/file.go b/weed/filesys/file.go
index 625fd4f74..6900190e0 100644
--- a/weed/filesys/file.go
+++ b/weed/filesys/file.go
@@ -26,12 +26,14 @@ type File struct {
 	isOpen     bool
 }
 
-func (file *File) Attr(ctx context.Context, attr *fuse.Attr) error {
+func (file *File) fullpath() string {
+	return filepath.Join(file.dir.Path, file.Name)
+}
 
-	fullPath := filepath.Join(file.dir.Path, file.Name)
+func (file *File) Attr(ctx context.Context, attr *fuse.Attr) error {
 
 	if file.attributes == nil || !file.isOpen {
-		item := file.wfs.listDirectoryEntriesCache.Get(fullPath)
+		item := file.wfs.listDirectoryEntriesCache.Get(file.fullpath())
 		if item != nil {
 			entry := item.Value().(*filer_pb.Entry)
 			file.Chunks = entry.Chunks
@@ -54,7 +56,7 @@ func (file *File) Attr(ctx context.Context, attr *fuse.Attr) error {
 				file.attributes = resp.Attributes
 				file.Chunks = resp.Chunks
 
-				glog.V(1).Infof("file attr %v %+v: %d", fullPath, file.attributes, filer2.TotalSize(file.Chunks))
+				glog.V(1).Infof("file attr %v %+v: %d", file.fullpath(), file.attributes, filer2.TotalSize(file.Chunks))
 
 				return nil
 			})
@@ -77,30 +79,26 @@ func (file *File) Attr(ctx context.Context, attr *fuse.Attr) error {
 
 func (file *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
 
-	fullPath := filepath.Join(file.dir.Path, file.Name)
-
-	glog.V(3).Infof("%v file open %+v", fullPath, req)
+	glog.V(3).Infof("%v file open %+v", file.fullpath(), req)
 
 	file.isOpen = true
 
-	return &FileHandle{
-		f:          file,
-		dirtyPages: newDirtyPages(file),
-		RequestId:  req.Header.ID,
-		NodeId:     req.Header.Node,
-		Uid:        req.Uid,
-		Gid:        req.Gid,
-	}, nil
+	handle := file.wfs.AcquireHandle(file, req.Uid, req.Gid)
+
+	resp.Handle = fuse.HandleID(handle.handle)
+
+	glog.V(3).Infof("%v file open handle id = %d", file.fullpath(), handle.handle)
+
+	return handle, nil
 
 }
 
 func (file *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
-	fullPath := filepath.Join(file.dir.Path, file.Name)
 
-	glog.V(3).Infof("%v file setattr %+v", fullPath, req)
+	glog.V(3).Infof("%v file setattr %+v, fh=%d", file.fullpath(), req, req.Handle)
 	if req.Valid.Size() {
 
-		glog.V(3).Infof("%v file setattr set size=%v", fullPath, req.Size)
+		glog.V(3).Infof("%v file setattr set size=%v", file.fullpath(), req.Size)
 		if req.Size == 0 {
 			// fmt.Printf("truncate %v \n", fullPath)
 			file.Chunks = nil
diff --git a/weed/filesys/filehandle.go b/weed/filesys/filehandle.go
index bec240de2..74125dc09 100644
--- a/weed/filesys/filehandle.go
+++ b/weed/filesys/filehandle.go
@@ -19,6 +19,8 @@ type FileHandle struct {
 	dirtyPages    *ContinuousDirtyPages
 	dirtyMetadata bool
 
+	handle uint64
+
 	f         *File
 	RequestId fuse.RequestID // unique ID for request
 	NodeId    fuse.NodeID    // file or directory the request is about
@@ -26,6 +28,15 @@ type FileHandle struct {
 	Gid       uint32         // group ID of process making request
 }
 
+func newFileHandle(file *File, uid, gid uint32) *FileHandle {
+	return &FileHandle{
+		f:          file,
+		dirtyPages: newDirtyPages(file),
+		Uid:        uid,
+		Gid:        gid,
+	}
+}
+
 var _ = fs.Handle(&FileHandle{})
 
 // var _ = fs.HandleReadAller(&FileHandle{})
@@ -36,8 +47,9 @@ var _ = fs.HandleReleaser(&FileHandle{})
 
 func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
 
-	glog.V(4).Infof("%v/%v read fh: [%d,%d)", fh.f.dir.Path, fh.f.Name, req.Offset, req.Offset+int64(req.Size))
+	glog.V(4).Infof("%s read fh %d: [%d,%d)", fh.f.fullpath(), fh.handle, req.Offset, req.Offset+int64(req.Size))
 
+	// this value should come from the filer instead of the old f
 	if len(fh.f.Chunks) == 0 {
 		glog.V(0).Infof("empty fh %v/%v", fh.f.dir.Path, fh.f.Name)
 		return fmt.Errorf("empty file %v/%v", fh.f.dir.Path, fh.f.Name)
@@ -123,7 +135,7 @@ func (fh *FileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *f
 
 	// write the request to volume servers
 
-	glog.V(4).Infof("%+v/%v write fh: [%d,%d)", fh.f.dir.Path, fh.f.Name, req.Offset, req.Offset+int64(len(req.Data)))
+	glog.V(4).Infof("%+v/%v write fh %d: [%d,%d)", fh.f.dir.Path, fh.f.Name, fh.handle, req.Offset, req.Offset+int64(len(req.Data)))
 
 	chunks, err := fh.dirtyPages.AddPage(ctx, req.Offset, req.Data)
 	if err != nil {
@@ -148,7 +160,9 @@ func (fh *FileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *f
 
 func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
 
-	glog.V(4).Infof("%+v/%v release fh", fh.f.dir.Path, fh.f.Name)
+	glog.V(4).Infof("%v release fh %d", fh.f.fullpath(), fh.handle)
+
+	fh.f.wfs.ReleaseHandle(fuse.HandleID(fh.handle))
 
 	fh.f.isOpen = false
 
@@ -160,7 +174,7 @@ func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) err
 func (fh *FileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) error {
 	// fflush works at fh level
 	// send the data to the OS
-	glog.V(4).Infof("%s/%s fh flush %v", fh.f.dir.Path, fh.f.Name, req)
+	glog.V(4).Infof("%s fh %d flush %v", fh.f.fullpath(), fh.handle, req)
 
 	chunk, err := fh.dirtyPages.FlushToStorage(ctx)
 	if err != nil {
diff --git a/weed/filesys/wfs.go b/weed/filesys/wfs.go
index ac7333695..127252058 100644
--- a/weed/filesys/wfs.go
+++ b/weed/filesys/wfs.go
@@ -6,6 +6,9 @@ import (
 	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
 	"github.com/karlseguin/ccache"
 	"google.golang.org/grpc"
+	"sync"
+	"bazil.org/fuse"
+	"github.com/chrislusf/seaweedfs/weed/glog"
 )
 
 type WFS struct {
@@ -14,6 +17,11 @@ type WFS struct {
 	collection                string
 	replication               string
 	chunkSizeLimit            int64
+
+	// contains all open handles
+	handles           []*FileHandle
+	pathToHandleIndex map[string]int
+	pathToHandleLock  sync.Mutex
 }
 
 func NewSeaweedFileSystem(filerGrpcAddress string, collection string, replication string, chunkSizeLimitMB int) *WFS {
@@ -23,6 +31,7 @@ func NewSeaweedFileSystem(filerGrpcAddress string, collection string, replicatio
 		collection:                collection,
 		replication:               replication,
 		chunkSizeLimit:            int64(chunkSizeLimitMB) * 1024 * 1024,
+		pathToHandleIndex:         make(map[string]int),
 	}
 }
 
@@ -42,3 +51,60 @@ func (wfs *WFS) withFilerClient(fn func(filer_pb.SeaweedFilerClient) error) erro
 
 	return fn(client)
 }
+
+func (wfs *WFS) AcquireHandle(file *File, uid, gid uint32) (handle *FileHandle) {
+	wfs.pathToHandleLock.Lock()
+	defer wfs.pathToHandleLock.Unlock()
+
+	fullpath := file.fullpath()
+
+	index, found := wfs.pathToHandleIndex[fullpath]
+	if found && wfs.handles[index] != nil {
+		glog.V(4).Infoln(fullpath, "found handle id", index)
+		return wfs.handles[index]
+	}
+
+	// create a new handler
+	handle = &FileHandle{
+		f:          file,
+		dirtyPages: newDirtyPages(file),
+		Uid:        uid,
+		Gid:        gid,
+	}
+
+	if found && wfs.handles[index] != nil {
+		glog.V(4).Infoln(fullpath, "reuse previous handle id", index)
+		wfs.handles[index] = handle
+		handle.handle = uint64(index)
+		return
+	}
+
+	for i, h := range wfs.handles {
+		if h == nil {
+			wfs.handles[i] = handle
+			handle.handle = uint64(i)
+			wfs.pathToHandleIndex[fullpath] = i
+			glog.V(4).Infoln(fullpath, "reuse handle id", handle.handle)
+			return
+		}
+	}
+
+	wfs.handles = append(wfs.handles, handle)
+	handle.handle = uint64(len(wfs.handles) - 1)
+	println(fullpath, "new handle id", handle.handle)
+	wfs.pathToHandleIndex[fullpath] = int(handle.handle)
+
+	return
+}
+
+func (wfs *WFS) ReleaseHandle(handleId fuse.HandleID) {
+	wfs.pathToHandleLock.Lock()
+	defer wfs.pathToHandleLock.Unlock()
+
+	glog.V(4).Infoln("releasing handle id", handleId, "current handles lengh", len(wfs.handles))
+	if int(handleId) < len(wfs.handles) {
+		wfs.handles[int(handleId)] = nil
+	}
+
+	return
+}