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.

267 lines
7.3 KiB

7 years ago
5 years ago
5 years ago
6 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
  1. package filesys
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/chrislusf/seaweedfs/weed/filer"
  6. "github.com/chrislusf/seaweedfs/weed/wdclient"
  7. "math"
  8. "os"
  9. "path"
  10. "sync"
  11. "time"
  12. "google.golang.org/grpc"
  13. "github.com/chrislusf/seaweedfs/weed/util/grace"
  14. "github.com/seaweedfs/fuse"
  15. "github.com/seaweedfs/fuse/fs"
  16. "github.com/chrislusf/seaweedfs/weed/filesys/meta_cache"
  17. "github.com/chrislusf/seaweedfs/weed/glog"
  18. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  19. "github.com/chrislusf/seaweedfs/weed/util"
  20. "github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
  21. )
  22. type Option struct {
  23. MountDirectory string
  24. FilerAddress string
  25. FilerGrpcAddress string
  26. GrpcDialOption grpc.DialOption
  27. FilerMountRootPath string
  28. Collection string
  29. Replication string
  30. TtlSec int32
  31. ChunkSizeLimit int64
  32. ConcurrentWriters int
  33. CacheDir string
  34. CacheSizeMB int64
  35. DataCenter string
  36. EntryCacheTtl time.Duration
  37. Umask os.FileMode
  38. MountUid uint32
  39. MountGid uint32
  40. MountMode os.FileMode
  41. MountCtime time.Time
  42. MountMtime time.Time
  43. OutsideContainerClusterMode bool // whether the mount runs outside SeaweedFS containers
  44. Cipher bool // whether encrypt data on volume server
  45. UidGidMapper *meta_cache.UidGidMapper
  46. }
  47. var _ = fs.FS(&WFS{})
  48. var _ = fs.FSStatfser(&WFS{})
  49. type WFS struct {
  50. option *Option
  51. // contains all open handles, protected by handlesLock
  52. handlesLock sync.Mutex
  53. handles map[uint64]*FileHandle
  54. bufPool sync.Pool
  55. stats statsCache
  56. root fs.Node
  57. fsNodeCache *FsCache
  58. chunkCache *chunk_cache.TieredChunkCache
  59. metaCache *meta_cache.MetaCache
  60. signature int32
  61. // throttle writers
  62. concurrentWriters *util.LimitedConcurrentExecutor
  63. Server *fs.Server
  64. }
  65. type statsCache struct {
  66. filer_pb.StatisticsResponse
  67. lastChecked int64 // unix time in seconds
  68. }
  69. func NewSeaweedFileSystem(option *Option) *WFS {
  70. wfs := &WFS{
  71. option: option,
  72. handles: make(map[uint64]*FileHandle),
  73. bufPool: sync.Pool{
  74. New: func() interface{} {
  75. return make([]byte, option.ChunkSizeLimit)
  76. },
  77. },
  78. signature: util.RandomInt32(),
  79. }
  80. cacheUniqueId := util.Md5String([]byte(option.MountDirectory + option.FilerGrpcAddress + option.FilerMountRootPath + util.Version()))[0:8]
  81. cacheDir := path.Join(option.CacheDir, cacheUniqueId)
  82. if option.CacheSizeMB > 0 {
  83. os.MkdirAll(cacheDir, os.FileMode(0777)&^option.Umask)
  84. wfs.chunkCache = chunk_cache.NewTieredChunkCache(256, cacheDir, option.CacheSizeMB, 1024*1024)
  85. }
  86. wfs.metaCache = meta_cache.NewMetaCache(path.Join(cacheDir, "meta"), util.FullPath(option.FilerMountRootPath), option.UidGidMapper, func(filePath util.FullPath) {
  87. fsNode := wfs.fsNodeCache.GetFsNode(filePath)
  88. if fsNode != nil {
  89. if file, ok := fsNode.(*File); ok {
  90. if err := wfs.Server.InvalidateNodeData(file); err != nil {
  91. glog.V(4).Infof("InvalidateNodeData %s : %v", filePath, err)
  92. }
  93. file.clearEntry()
  94. }
  95. }
  96. dir, name := filePath.DirAndName()
  97. parent := wfs.root
  98. if dir != "/" {
  99. parent = wfs.fsNodeCache.GetFsNode(util.FullPath(dir))
  100. }
  101. if parent != nil {
  102. if err := wfs.Server.InvalidateEntry(parent, name); err != nil {
  103. glog.V(4).Infof("InvalidateEntry %s : %v", filePath, err)
  104. }
  105. }
  106. })
  107. startTime := time.Now()
  108. go meta_cache.SubscribeMetaEvents(wfs.metaCache, wfs.signature, wfs, wfs.option.FilerMountRootPath, startTime.UnixNano())
  109. grace.OnInterrupt(func() {
  110. wfs.metaCache.Shutdown()
  111. })
  112. entry, _ := filer_pb.GetEntry(wfs, util.FullPath(wfs.option.FilerMountRootPath))
  113. wfs.root = &Dir{name: wfs.option.FilerMountRootPath, wfs: wfs, entry: entry}
  114. wfs.fsNodeCache = newFsCache(wfs.root)
  115. if wfs.option.ConcurrentWriters > 0 {
  116. wfs.concurrentWriters = util.NewLimitedConcurrentExecutor(wfs.option.ConcurrentWriters)
  117. }
  118. return wfs
  119. }
  120. func (wfs *WFS) Root() (fs.Node, error) {
  121. return wfs.root, nil
  122. }
  123. func (wfs *WFS) AcquireHandle(file *File, uid, gid uint32) (fileHandle *FileHandle) {
  124. fullpath := file.fullpath()
  125. glog.V(4).Infof("AcquireHandle %s uid=%d gid=%d", fullpath, uid, gid)
  126. wfs.handlesLock.Lock()
  127. defer wfs.handlesLock.Unlock()
  128. inodeId := file.fullpath().AsInode()
  129. if file.isOpen > 0 {
  130. existingHandle, found := wfs.handles[inodeId]
  131. if found && existingHandle != nil {
  132. file.isOpen++
  133. return existingHandle
  134. }
  135. }
  136. fileHandle = newFileHandle(file, uid, gid)
  137. file.maybeLoadEntry(context.Background())
  138. file.isOpen++
  139. wfs.handles[inodeId] = fileHandle
  140. fileHandle.handle = inodeId
  141. return
  142. }
  143. func (wfs *WFS) ReleaseHandle(fullpath util.FullPath, handleId fuse.HandleID) {
  144. wfs.handlesLock.Lock()
  145. defer wfs.handlesLock.Unlock()
  146. glog.V(4).Infof("%s ReleaseHandle id %d current handles length %d", fullpath, handleId, len(wfs.handles))
  147. delete(wfs.handles, fullpath.AsInode())
  148. return
  149. }
  150. // Statfs is called to obtain file system metadata. Implements fuse.FSStatfser
  151. func (wfs *WFS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
  152. glog.V(4).Infof("reading fs stats: %+v", req)
  153. if wfs.stats.lastChecked < time.Now().Unix()-20 {
  154. err := wfs.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
  155. request := &filer_pb.StatisticsRequest{
  156. Collection: wfs.option.Collection,
  157. Replication: wfs.option.Replication,
  158. Ttl: fmt.Sprintf("%ds", wfs.option.TtlSec),
  159. }
  160. glog.V(4).Infof("reading filer stats: %+v", request)
  161. resp, err := client.Statistics(context.Background(), request)
  162. if err != nil {
  163. glog.V(0).Infof("reading filer stats %v: %v", request, err)
  164. return err
  165. }
  166. glog.V(4).Infof("read filer stats: %+v", resp)
  167. wfs.stats.TotalSize = resp.TotalSize
  168. wfs.stats.UsedSize = resp.UsedSize
  169. wfs.stats.FileCount = resp.FileCount
  170. wfs.stats.lastChecked = time.Now().Unix()
  171. return nil
  172. })
  173. if err != nil {
  174. glog.V(0).Infof("filer Statistics: %v", err)
  175. return err
  176. }
  177. }
  178. totalDiskSize := wfs.stats.TotalSize
  179. usedDiskSize := wfs.stats.UsedSize
  180. actualFileCount := wfs.stats.FileCount
  181. // Compute the total number of available blocks
  182. resp.Blocks = totalDiskSize / blockSize
  183. // Compute the number of used blocks
  184. numBlocks := uint64(usedDiskSize / blockSize)
  185. // Report the number of free and available blocks for the block size
  186. resp.Bfree = resp.Blocks - numBlocks
  187. resp.Bavail = resp.Blocks - numBlocks
  188. resp.Bsize = uint32(blockSize)
  189. // Report the total number of possible files in the file system (and those free)
  190. resp.Files = math.MaxInt64
  191. resp.Ffree = math.MaxInt64 - actualFileCount
  192. // Report the maximum length of a name and the minimum fragment size
  193. resp.Namelen = 1024
  194. resp.Frsize = uint32(blockSize)
  195. return nil
  196. }
  197. func (wfs *WFS) mapPbIdFromFilerToLocal(entry *filer_pb.Entry) {
  198. if entry.Attributes == nil {
  199. return
  200. }
  201. entry.Attributes.Uid, entry.Attributes.Gid = wfs.option.UidGidMapper.FilerToLocal(entry.Attributes.Uid, entry.Attributes.Gid)
  202. }
  203. func (wfs *WFS) mapPbIdFromLocalToFiler(entry *filer_pb.Entry) {
  204. if entry.Attributes == nil {
  205. return
  206. }
  207. entry.Attributes.Uid, entry.Attributes.Gid = wfs.option.UidGidMapper.LocalToFiler(entry.Attributes.Uid, entry.Attributes.Gid)
  208. }
  209. func (wfs *WFS) LookupFn() wdclient.LookupFileIdFunctionType {
  210. if wfs.option.OutsideContainerClusterMode {
  211. return func(fileId string) (targetUrls []string, err error) {
  212. return []string{"http://" + wfs.option.FilerAddress + "/?proxyChunkId=" + fileId}, nil
  213. }
  214. }
  215. return filer.LookupFn(wfs)
  216. }