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.

203 lines
5.4 KiB

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