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.

259 lines
6.8 KiB

5 years ago
7 years ago
6 years ago
6 years ago
6 years ago
5 years ago
5 years ago
5 years ago
  1. package filesys
  2. import (
  3. "context"
  4. "fmt"
  5. "math"
  6. "os"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/karlseguin/ccache"
  11. "google.golang.org/grpc"
  12. "github.com/chrislusf/seaweedfs/weed/filer2"
  13. "github.com/chrislusf/seaweedfs/weed/glog"
  14. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  15. "github.com/chrislusf/seaweedfs/weed/util"
  16. "github.com/seaweedfs/fuse"
  17. "github.com/seaweedfs/fuse/fs"
  18. )
  19. type Option struct {
  20. FilerGrpcAddress string
  21. GrpcDialOption grpc.DialOption
  22. FilerMountRootPath string
  23. Collection string
  24. Replication string
  25. TtlSec int32
  26. ChunkSizeLimit int64
  27. DataCenter string
  28. DirListCacheLimit int64
  29. EntryCacheTtl time.Duration
  30. Umask os.FileMode
  31. MountUid uint32
  32. MountGid uint32
  33. MountMode os.FileMode
  34. MountCtime time.Time
  35. MountMtime time.Time
  36. }
  37. var _ = fs.FS(&WFS{})
  38. var _ = fs.FSStatfser(&WFS{})
  39. type WFS struct {
  40. option *Option
  41. listDirectoryEntriesCache *ccache.Cache
  42. // contains all open handles, protected by handlesLock
  43. handlesLock sync.Mutex
  44. handles []*FileHandle
  45. pathToHandleIndex map[filer2.FullPath]int
  46. bufPool sync.Pool
  47. stats statsCache
  48. // nodes, protected by nodesLock
  49. nodesLock sync.Mutex
  50. nodes map[uint64]fs.Node
  51. root fs.Node
  52. }
  53. type statsCache struct {
  54. filer_pb.StatisticsResponse
  55. lastChecked int64 // unix time in seconds
  56. }
  57. func NewSeaweedFileSystem(option *Option) *WFS {
  58. wfs := &WFS{
  59. option: option,
  60. listDirectoryEntriesCache: ccache.New(ccache.Configure().MaxSize(option.DirListCacheLimit * 3).ItemsToPrune(100)),
  61. pathToHandleIndex: make(map[filer2.FullPath]int),
  62. bufPool: sync.Pool{
  63. New: func() interface{} {
  64. return make([]byte, option.ChunkSizeLimit)
  65. },
  66. },
  67. nodes: make(map[uint64]fs.Node),
  68. }
  69. wfs.root = &Dir{Path: wfs.option.FilerMountRootPath, wfs: wfs}
  70. return wfs
  71. }
  72. func (wfs *WFS) Root() (fs.Node, error) {
  73. return wfs.root, nil
  74. }
  75. func (wfs *WFS) WithFilerClient(ctx context.Context, fn func(context.Context, filer_pb.SeaweedFilerClient) error) error {
  76. err := util.WithCachedGrpcClient(ctx, func(ctx2 context.Context, grpcConnection *grpc.ClientConn) error {
  77. client := filer_pb.NewSeaweedFilerClient(grpcConnection)
  78. return fn(ctx2, client)
  79. }, wfs.option.FilerGrpcAddress, wfs.option.GrpcDialOption)
  80. if err == nil {
  81. return nil
  82. }
  83. if strings.Contains(err.Error(), "context canceled") {
  84. glog.V(2).Infoln("retry context canceled request...")
  85. return util.WithCachedGrpcClient(context.Background(), func(ctx2 context.Context, grpcConnection *grpc.ClientConn) error {
  86. client := filer_pb.NewSeaweedFilerClient(grpcConnection)
  87. return fn(ctx2, client)
  88. }, wfs.option.FilerGrpcAddress, wfs.option.GrpcDialOption)
  89. }
  90. return err
  91. }
  92. func (wfs *WFS) AcquireHandle(file *File, uid, gid uint32) (fileHandle *FileHandle) {
  93. fullpath := file.fullpath()
  94. glog.V(4).Infof("%s AcquireHandle uid=%d gid=%d", fullpath, uid, gid)
  95. wfs.handlesLock.Lock()
  96. defer wfs.handlesLock.Unlock()
  97. index, found := wfs.pathToHandleIndex[fullpath]
  98. if found && wfs.handles[index] != nil {
  99. glog.V(2).Infoln(fullpath, "found fileHandle id", index)
  100. return wfs.handles[index]
  101. }
  102. fileHandle = newFileHandle(file, uid, gid)
  103. for i, h := range wfs.handles {
  104. if h == nil {
  105. wfs.handles[i] = fileHandle
  106. fileHandle.handle = uint64(i)
  107. wfs.pathToHandleIndex[fullpath] = i
  108. glog.V(4).Infof("%s reuse fh %d", fullpath, fileHandle.handle)
  109. return
  110. }
  111. }
  112. wfs.handles = append(wfs.handles, fileHandle)
  113. fileHandle.handle = uint64(len(wfs.handles) - 1)
  114. wfs.pathToHandleIndex[fullpath] = int(fileHandle.handle)
  115. glog.V(4).Infof("%s new fh %d", fullpath, fileHandle.handle)
  116. return
  117. }
  118. func (wfs *WFS) ReleaseHandle(fullpath filer2.FullPath, handleId fuse.HandleID) {
  119. wfs.handlesLock.Lock()
  120. defer wfs.handlesLock.Unlock()
  121. glog.V(4).Infof("%s ReleaseHandle id %d current handles length %d", fullpath, handleId, len(wfs.handles))
  122. delete(wfs.pathToHandleIndex, fullpath)
  123. if int(handleId) < len(wfs.handles) {
  124. wfs.handles[int(handleId)] = nil
  125. }
  126. return
  127. }
  128. // Statfs is called to obtain file system metadata. Implements fuse.FSStatfser
  129. func (wfs *WFS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
  130. glog.V(4).Infof("reading fs stats: %+v", req)
  131. if wfs.stats.lastChecked < time.Now().Unix()-20 {
  132. err := wfs.WithFilerClient(ctx, func(ctx context.Context, client filer_pb.SeaweedFilerClient) error {
  133. request := &filer_pb.StatisticsRequest{
  134. Collection: wfs.option.Collection,
  135. Replication: wfs.option.Replication,
  136. Ttl: fmt.Sprintf("%ds", wfs.option.TtlSec),
  137. }
  138. glog.V(4).Infof("reading filer stats: %+v", request)
  139. resp, err := client.Statistics(ctx, request)
  140. if err != nil {
  141. glog.V(0).Infof("reading filer stats %v: %v", request, err)
  142. return err
  143. }
  144. glog.V(4).Infof("read filer stats: %+v", resp)
  145. wfs.stats.TotalSize = resp.TotalSize
  146. wfs.stats.UsedSize = resp.UsedSize
  147. wfs.stats.FileCount = resp.FileCount
  148. wfs.stats.lastChecked = time.Now().Unix()
  149. return nil
  150. })
  151. if err != nil {
  152. glog.V(0).Infof("filer Statistics: %v", err)
  153. return err
  154. }
  155. }
  156. totalDiskSize := wfs.stats.TotalSize
  157. usedDiskSize := wfs.stats.UsedSize
  158. actualFileCount := wfs.stats.FileCount
  159. // Compute the total number of available blocks
  160. resp.Blocks = totalDiskSize / blockSize
  161. // Compute the number of used blocks
  162. numBlocks := uint64(usedDiskSize / blockSize)
  163. // Report the number of free and available blocks for the block size
  164. resp.Bfree = resp.Blocks - numBlocks
  165. resp.Bavail = resp.Blocks - numBlocks
  166. resp.Bsize = uint32(blockSize)
  167. // Report the total number of possible files in the file system (and those free)
  168. resp.Files = math.MaxInt64
  169. resp.Ffree = math.MaxInt64 - actualFileCount
  170. // Report the maximum length of a name and the minimum fragment size
  171. resp.Namelen = 1024
  172. resp.Frsize = uint32(blockSize)
  173. return nil
  174. }
  175. func (wfs *WFS) cacheGet(path filer2.FullPath) *filer_pb.Entry {
  176. item := wfs.listDirectoryEntriesCache.Get(string(path))
  177. if item != nil && !item.Expired() {
  178. return item.Value().(*filer_pb.Entry)
  179. }
  180. return nil
  181. }
  182. func (wfs *WFS) cacheSet(path filer2.FullPath, entry *filer_pb.Entry, ttl time.Duration) {
  183. if entry == nil {
  184. wfs.listDirectoryEntriesCache.Delete(string(path))
  185. } else {
  186. wfs.listDirectoryEntriesCache.Set(string(path), entry, ttl)
  187. }
  188. }
  189. func (wfs *WFS) cacheDelete(path filer2.FullPath) {
  190. wfs.listDirectoryEntriesCache.Delete(string(path))
  191. }
  192. func (wfs *WFS) getNode(fullpath filer2.FullPath, fn func() fs.Node) fs.Node {
  193. wfs.nodesLock.Lock()
  194. defer wfs.nodesLock.Unlock()
  195. node, found := wfs.nodes[fullpath.AsInode()]
  196. if found {
  197. return node
  198. }
  199. node = fn()
  200. if node != nil {
  201. wfs.nodes[fullpath.AsInode()] = node
  202. }
  203. return node
  204. }
  205. func (wfs *WFS) forgetNode(fullpath filer2.FullPath) {
  206. wfs.nodesLock.Lock()
  207. defer wfs.nodesLock.Unlock()
  208. delete(wfs.nodes, fullpath.AsInode())
  209. }