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.

248 lines
6.5 KiB

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