You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
4.9 KiB

7 years ago
7 years ago
6 years ago
6 years ago
6 years ago
  1. package filesys
  2. import (
  3. "context"
  4. "fmt"
  5. "math"
  6. "os"
  7. "sync"
  8. "time"
  9. "github.com/chrislusf/seaweedfs/weed/glog"
  10. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  11. "github.com/chrislusf/seaweedfs/weed/util"
  12. "github.com/karlseguin/ccache"
  13. "github.com/seaweedfs/fuse"
  14. "github.com/seaweedfs/fuse/fs"
  15. "google.golang.org/grpc"
  16. )
  17. type Option struct {
  18. FilerGrpcAddress string
  19. GrpcDialOption grpc.DialOption
  20. FilerMountRootPath string
  21. Collection string
  22. Replication string
  23. TtlSec int32
  24. ChunkSizeLimit int64
  25. DataCenter string
  26. DirListingLimit int
  27. EntryCacheTtl time.Duration
  28. MountUid uint32
  29. MountGid uint32
  30. MountMode os.FileMode
  31. }
  32. var _ = fs.FS(&WFS{})
  33. var _ = fs.FSStatfser(&WFS{})
  34. type WFS struct {
  35. option *Option
  36. listDirectoryEntriesCache *ccache.Cache
  37. // contains all open handles
  38. handles []*FileHandle
  39. pathToHandleIndex map[string]int
  40. pathToHandleLock sync.Mutex
  41. bufPool sync.Pool
  42. stats statsCache
  43. }
  44. type statsCache struct {
  45. filer_pb.StatisticsResponse
  46. lastChecked int64 // unix time in seconds
  47. }
  48. func NewSeaweedFileSystem(option *Option) *WFS {
  49. wfs := &WFS{
  50. option: option,
  51. listDirectoryEntriesCache: ccache.New(ccache.Configure().MaxSize(1024 * 8).ItemsToPrune(100)),
  52. pathToHandleIndex: make(map[string]int),
  53. bufPool: sync.Pool{
  54. New: func() interface{} {
  55. return make([]byte, option.ChunkSizeLimit)
  56. },
  57. },
  58. }
  59. return wfs
  60. }
  61. func (wfs *WFS) Root() (fs.Node, error) {
  62. return &Dir{Path: wfs.option.FilerMountRootPath, wfs: wfs}, nil
  63. }
  64. func (wfs *WFS) withFilerClient(ctx context.Context, fn func(filer_pb.SeaweedFilerClient) error) error {
  65. return util.WithCachedGrpcClient(ctx, func(grpcConnection *grpc.ClientConn) error {
  66. client := filer_pb.NewSeaweedFilerClient(grpcConnection)
  67. return fn(client)
  68. }, wfs.option.FilerGrpcAddress, wfs.option.GrpcDialOption)
  69. }
  70. func (wfs *WFS) AcquireHandle(file *File, uid, gid uint32) (fileHandle *FileHandle) {
  71. wfs.pathToHandleLock.Lock()
  72. defer wfs.pathToHandleLock.Unlock()
  73. fullpath := file.fullpath()
  74. index, found := wfs.pathToHandleIndex[fullpath]
  75. if found && wfs.handles[index] != nil {
  76. glog.V(2).Infoln(fullpath, "found fileHandle id", index)
  77. return wfs.handles[index]
  78. }
  79. fileHandle = newFileHandle(file, uid, gid)
  80. for i, h := range wfs.handles {
  81. if h == nil {
  82. wfs.handles[i] = fileHandle
  83. fileHandle.handle = uint64(i)
  84. wfs.pathToHandleIndex[fullpath] = i
  85. glog.V(4).Infoln(fullpath, "reuse fileHandle id", fileHandle.handle)
  86. return
  87. }
  88. }
  89. wfs.handles = append(wfs.handles, fileHandle)
  90. fileHandle.handle = uint64(len(wfs.handles) - 1)
  91. glog.V(2).Infoln(fullpath, "new fileHandle id", fileHandle.handle)
  92. wfs.pathToHandleIndex[fullpath] = int(fileHandle.handle)
  93. return
  94. }
  95. func (wfs *WFS) ReleaseHandle(fullpath string, handleId fuse.HandleID) {
  96. wfs.pathToHandleLock.Lock()
  97. defer wfs.pathToHandleLock.Unlock()
  98. glog.V(4).Infof("%s releasing handle id %d current handles length %d", fullpath, handleId, len(wfs.handles))
  99. delete(wfs.pathToHandleIndex, fullpath)
  100. if int(handleId) < len(wfs.handles) {
  101. wfs.handles[int(handleId)] = nil
  102. }
  103. return
  104. }
  105. // Statfs is called to obtain file system metadata. Implements fuse.FSStatfser
  106. func (wfs *WFS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
  107. glog.V(4).Infof("reading fs stats: %+v", req)
  108. if wfs.stats.lastChecked < time.Now().Unix()-20 {
  109. err := wfs.withFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  110. request := &filer_pb.StatisticsRequest{
  111. Collection: wfs.option.Collection,
  112. Replication: wfs.option.Replication,
  113. Ttl: fmt.Sprintf("%ds", wfs.option.TtlSec),
  114. }
  115. glog.V(4).Infof("reading filer stats: %+v", request)
  116. resp, err := client.Statistics(ctx, request)
  117. if err != nil {
  118. glog.V(0).Infof("reading filer stats %v: %v", request, err)
  119. return err
  120. }
  121. glog.V(4).Infof("read filer stats: %+v", resp)
  122. wfs.stats.TotalSize = resp.TotalSize
  123. wfs.stats.UsedSize = resp.UsedSize
  124. wfs.stats.FileCount = resp.FileCount
  125. wfs.stats.lastChecked = time.Now().Unix()
  126. return nil
  127. })
  128. if err != nil {
  129. glog.V(0).Infof("filer Statistics: %v", err)
  130. return err
  131. }
  132. }
  133. totalDiskSize := wfs.stats.TotalSize
  134. usedDiskSize := wfs.stats.UsedSize
  135. actualFileCount := wfs.stats.FileCount
  136. // Compute the total number of available blocks
  137. resp.Blocks = totalDiskSize / blockSize
  138. // Compute the number of used blocks
  139. numBlocks := uint64(usedDiskSize / blockSize)
  140. // Report the number of free and available blocks for the block size
  141. resp.Bfree = resp.Blocks - numBlocks
  142. resp.Bavail = resp.Blocks - numBlocks
  143. resp.Bsize = uint32(blockSize)
  144. // Report the total number of possible files in the file system (and those free)
  145. resp.Files = math.MaxInt64
  146. resp.Ffree = math.MaxInt64 - actualFileCount
  147. // Report the maximum length of a name and the minimum fragment size
  148. resp.Namelen = 1024
  149. resp.Frsize = uint32(blockSize)
  150. return nil
  151. }