file_entry.go 4.3 KB

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