filehandle.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package filesys
  2. import (
  3. "context"
  4. "fmt"
  5. "mime"
  6. "path"
  7. "time"
  8. "github.com/gabriel-vasile/mimetype"
  9. "github.com/chrislusf/seaweedfs/weed/filer2"
  10. "github.com/chrislusf/seaweedfs/weed/glog"
  11. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  12. "github.com/seaweedfs/fuse"
  13. "github.com/seaweedfs/fuse/fs"
  14. )
  15. type FileHandle struct {
  16. // cache file has been written to
  17. dirtyPages *ContinuousDirtyPages
  18. contentType string
  19. dirtyMetadata bool
  20. handle uint64
  21. f *File
  22. RequestId fuse.RequestID // unique ID for request
  23. NodeId fuse.NodeID // file or directory the request is about
  24. Uid uint32 // user ID of process making request
  25. Gid uint32 // group ID of process making request
  26. }
  27. func newFileHandle(file *File, uid, gid uint32) *FileHandle {
  28. return &FileHandle{
  29. f: file,
  30. dirtyPages: newDirtyPages(file),
  31. Uid: uid,
  32. Gid: gid,
  33. }
  34. }
  35. var _ = fs.Handle(&FileHandle{})
  36. // var _ = fs.HandleReadAller(&FileHandle{})
  37. var _ = fs.HandleReader(&FileHandle{})
  38. var _ = fs.HandleFlusher(&FileHandle{})
  39. var _ = fs.HandleWriter(&FileHandle{})
  40. var _ = fs.HandleReleaser(&FileHandle{})
  41. func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
  42. glog.V(4).Infof("%s read fh %d: [%d,%d)", fh.f.fullpath(), fh.handle, req.Offset, req.Offset+int64(req.Size))
  43. buff := make([]byte, req.Size)
  44. totalRead, err := fh.readFromChunks(ctx, buff, req.Offset)
  45. if err == nil {
  46. dirtyOffset, dirtySize := fh.readFromDirtyPages(ctx, buff, req.Offset)
  47. if totalRead+req.Offset < dirtyOffset+int64(dirtySize) {
  48. totalRead = dirtyOffset + int64(dirtySize) - req.Offset
  49. }
  50. }
  51. resp.Data = buff[:totalRead]
  52. if err != nil {
  53. glog.Errorf("file handle read %s: %v", fh.f.fullpath(), err)
  54. return fuse.EIO
  55. }
  56. return err
  57. }
  58. func (fh *FileHandle) readFromDirtyPages(ctx context.Context, buff []byte, startOffset int64) (offset int64, size int) {
  59. return fh.dirtyPages.ReadDirtyData(ctx, buff, startOffset)
  60. }
  61. func (fh *FileHandle) readFromChunks(ctx context.Context, buff []byte, offset int64) (int64, error) {
  62. // this value should come from the filer instead of the old f
  63. if len(fh.f.entry.Chunks) == 0 {
  64. glog.V(1).Infof("empty fh %v", fh.f.fullpath())
  65. return 0, nil
  66. }
  67. if fh.f.entryViewCache == nil {
  68. fh.f.entryViewCache = filer2.NonOverlappingVisibleIntervals(fh.f.entry.Chunks)
  69. }
  70. chunkViews := filer2.ViewFromVisibleIntervals(fh.f.entryViewCache, offset, len(buff))
  71. totalRead, err := filer2.ReadIntoBuffer(ctx, fh.f.wfs, fh.f.fullpath(), buff, chunkViews, offset)
  72. if err != nil {
  73. glog.Errorf("file handle read %s: %v", fh.f.fullpath(), err)
  74. }
  75. return totalRead, err
  76. }
  77. // Write to the file handle
  78. func (fh *FileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
  79. // write the request to volume servers
  80. fh.f.entry.Attributes.FileSize = uint64(max(req.Offset+int64(len(req.Data)), int64(fh.f.entry.Attributes.FileSize)))
  81. // glog.V(0).Infof("%v write [%d,%d)", fh.f.fullpath(), req.Offset, req.Offset+int64(len(req.Data)))
  82. chunks, err := fh.dirtyPages.AddPage(ctx, req.Offset, req.Data)
  83. if err != nil {
  84. glog.Errorf("%v write fh %d: [%d,%d): %v", fh.f.fullpath(), fh.handle, req.Offset, req.Offset+int64(len(req.Data)), err)
  85. return fuse.EIO
  86. }
  87. resp.Size = len(req.Data)
  88. if req.Offset == 0 {
  89. // detect mime type
  90. detectedMIME := mimetype.Detect(req.Data)
  91. fh.contentType = detectedMIME.String()
  92. if ext := path.Ext(fh.f.Name); ext != detectedMIME.Extension() {
  93. fh.contentType = mime.TypeByExtension(ext)
  94. }
  95. fh.dirtyMetadata = true
  96. }
  97. if len(chunks) > 0 {
  98. fh.f.addChunks(chunks)
  99. fh.dirtyMetadata = true
  100. }
  101. return nil
  102. }
  103. func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
  104. glog.V(4).Infof("%v release fh %d", fh.f.fullpath(), fh.handle)
  105. fh.f.isOpen--
  106. if fh.f.isOpen <= 0 {
  107. fh.dirtyPages.releaseResource()
  108. fh.f.wfs.ReleaseHandle(fh.f.fullpath(), fuse.HandleID(fh.handle))
  109. }
  110. return nil
  111. }
  112. func (fh *FileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) error {
  113. // fflush works at fh level
  114. // send the data to the OS
  115. glog.V(4).Infof("%s fh %d flush %v", fh.f.fullpath(), fh.handle, req)
  116. chunks, err := fh.dirtyPages.FlushToStorage(ctx)
  117. if err != nil {
  118. glog.Errorf("flush %s: %v", fh.f.fullpath(), err)
  119. return fuse.EIO
  120. }
  121. fh.f.addChunks(chunks)
  122. if len(chunks) > 0 {
  123. fh.dirtyMetadata = true
  124. }
  125. if !fh.dirtyMetadata {
  126. return nil
  127. }
  128. err = fh.f.wfs.WithFilerClient(ctx, func(ctx context.Context, client filer_pb.SeaweedFilerClient) error {
  129. if fh.f.entry.Attributes != nil {
  130. fh.f.entry.Attributes.Mime = fh.contentType
  131. fh.f.entry.Attributes.Uid = req.Uid
  132. fh.f.entry.Attributes.Gid = req.Gid
  133. fh.f.entry.Attributes.Mtime = time.Now().Unix()
  134. fh.f.entry.Attributes.Crtime = time.Now().Unix()
  135. fh.f.entry.Attributes.FileMode = uint32(0777 &^ fh.f.wfs.option.Umask)
  136. }
  137. request := &filer_pb.CreateEntryRequest{
  138. Directory: fh.f.dir.Path,
  139. Entry: fh.f.entry,
  140. }
  141. glog.V(3).Infof("%s set chunks: %v", fh.f.fullpath(), len(fh.f.entry.Chunks))
  142. for i, chunk := range fh.f.entry.Chunks {
  143. glog.V(3).Infof("%s chunks %d: %v [%d,%d)", fh.f.fullpath(), i, chunk.FileId, chunk.Offset, chunk.Offset+int64(chunk.Size))
  144. }
  145. chunks, garbages := filer2.CompactFileChunks(fh.f.entry.Chunks)
  146. fh.f.entry.Chunks = chunks
  147. // fh.f.entryViewCache = nil
  148. if err := filer_pb.CreateEntry(ctx, client, request); err != nil {
  149. glog.Errorf("update fh: %v", err)
  150. return fmt.Errorf("update fh: %v", err)
  151. }
  152. fh.f.wfs.deleteFileChunks(ctx, garbages)
  153. for i, chunk := range garbages {
  154. glog.V(3).Infof("garbage %s chunks %d: %v [%d,%d)", fh.f.fullpath(), i, chunk.FileId, chunk.Offset, chunk.Offset+int64(chunk.Size))
  155. }
  156. return nil
  157. })
  158. if err == nil {
  159. fh.dirtyMetadata = false
  160. }
  161. if err != nil {
  162. glog.Errorf("%v fh %d flush: %v", fh.f.fullpath(), fh.handle, err)
  163. return fuse.EIO
  164. }
  165. return nil
  166. }