file_entry.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package basefs
  2. import (
  3. "io"
  4. "io/ioutil"
  5. "net/http"
  6. "os"
  7. "time"
  8. "github.com/jacobsa/fuse"
  9. "github.com/jacobsa/fuse/fuseops"
  10. drive "google.golang.org/api/drive/v3"
  11. )
  12. //FileEntry entry to handle files
  13. type FileEntry struct {
  14. //parent *FileEntry
  15. fs *BaseFS
  16. GID string // google driveID
  17. GFile *drive.File // GDrive file // Interface maybe?
  18. Name string // local name
  19. // fuseops
  20. Inode fuseops.InodeID
  21. Attr fuseops.InodeAttributes // Cached attributes
  22. // cache file
  23. tempFile *os.File // Cached file
  24. // childs
  25. //children []*FileEntry // children
  26. }
  27. // Why?
  28. func (fe *FileEntry) HasParentGID(gid string) bool {
  29. // Exceptional case
  30. if fe.Inode == fuseops.RootInodeID {
  31. return false
  32. }
  33. if gid == "" { // We are looking in root
  34. if fe.GFile == nil {
  35. return true
  36. }
  37. if len(fe.GFile.Parents) == 0 {
  38. return true
  39. }
  40. }
  41. if fe.GFile == nil { // Case gid is not empty and GFile is null
  42. return false
  43. }
  44. for _, pgid := range fe.GFile.Parents {
  45. if pgid == gid {
  46. return true
  47. }
  48. }
  49. return false
  50. }
  51. // SetGFile update attributes and set drive.File
  52. func (fe *FileEntry) SetGFile(gfile *drive.File) { // Should remove from here maybe?
  53. if gfile == nil {
  54. fe.GFile = nil
  55. } else {
  56. fe.GFile = gfile
  57. }
  58. // GetAttribute from GFile somehow
  59. // Create Attribute
  60. attr := fuseops.InodeAttributes{}
  61. attr.Nlink = 1
  62. attr.Uid = fe.fs.Config.UID
  63. attr.Gid = fe.fs.Config.GID
  64. attr.Mode = os.FileMode(0644) // default
  65. if gfile != nil {
  66. attr.Size = uint64(gfile.Size)
  67. attr.Crtime, _ = time.Parse(time.RFC3339, gfile.CreatedTime)
  68. attr.Ctime = attr.Crtime // Set CTime to created, although it is change inode metadata
  69. attr.Mtime, _ = time.Parse(time.RFC3339, gfile.ModifiedTime)
  70. attr.Atime = attr.Mtime // Set access time to modified, not sure if gdrive has access time
  71. if gfile.MimeType == "application/vnd.google-apps.folder" {
  72. attr.Mode = os.FileMode(0755) | os.ModeDir
  73. }
  74. fe.GID = gfile.Id
  75. }
  76. fe.Attr = attr
  77. }
  78. // Sync cached , upload to gdrive
  79. func (fe *FileEntry) Sync() (err error) {
  80. if fe.tempFile == nil {
  81. return
  82. }
  83. fe.tempFile.Sync()
  84. fe.tempFile.Seek(0, io.SeekStart)
  85. ngFile := &drive.File{}
  86. up := fe.fs.Client.Files.Update(fe.GFile.Id, ngFile)
  87. upFile, err := up.Media(fe.tempFile).Fields(fileFields).Do()
  88. fe.SetGFile(upFile) // update local GFile entry
  89. return
  90. }
  91. //ClearCache remove local file
  92. func (fe *FileEntry) ClearCache() (err error) {
  93. if fe.tempFile == nil {
  94. return
  95. }
  96. fe.tempFile.Close()
  97. os.Remove(fe.tempFile.Name())
  98. fe.tempFile = nil
  99. return
  100. }
  101. // Cache download GDrive file to a temporary local file or return already created file
  102. func (fe *FileEntry) Cache() *os.File {
  103. if fe.tempFile != nil {
  104. return fe.tempFile
  105. }
  106. var res *http.Response
  107. var err error
  108. // Export GDocs (Special google doc documents needs to be exported make a config somewhere for this)
  109. switch fe.GFile.MimeType { // Make this somewhat optional special case
  110. case "application/vnd.google-apps.document":
  111. log.Println("Exporting as: text/markdown")
  112. res, err = fe.fs.Client.Files.Export(fe.GFile.Id, "text/plain").Download()
  113. case "application/vnd.google-apps.spreadsheet":
  114. log.Println("Exporting as: text/csv")
  115. res, err = fe.fs.Client.Files.Export(fe.GFile.Id, "text/csv").Download()
  116. default:
  117. res, err = fe.fs.Client.Files.Get(fe.GFile.Id).Download()
  118. }
  119. if err != nil {
  120. log.Println("Error from GDrive API", err)
  121. return nil
  122. }
  123. defer res.Body.Close()
  124. // Local copy
  125. fe.tempFile, err = ioutil.TempFile(os.TempDir(), "gdfs") // TODO: const this elsewhere
  126. if err != nil {
  127. log.Println("Error creating temp file")
  128. return nil
  129. }
  130. io.Copy(fe.tempFile, res.Body)
  131. fe.tempFile.Seek(0, io.SeekStart)
  132. return fe.tempFile
  133. }
  134. func (fe *FileEntry) Truncate() (err error) { // DriverTruncate
  135. // Delete and create another on truncate 0
  136. err = fe.fs.Client.Files.Delete(fe.GFile.Id).Do() // XXX: Careful on this
  137. createdFile, err := fe.fs.Client.Files.Create(&drive.File{Parents: fe.GFile.Parents, Name: fe.GFile.Name}).Fields(fileFields).Do()
  138. if err != nil {
  139. return fuse.EINVAL // ??
  140. }
  141. fe.SetGFile(createdFile) // Set new file
  142. return
  143. }
  144. // IsDir returns true if entry is a directory:w
  145. func (fe *FileEntry) IsDir() bool {
  146. return fe.Attr.Mode&os.ModeDir == os.ModeDir
  147. }