file_entry.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package gdrivefs
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. _ "log"
  7. "net/http"
  8. "os"
  9. "strings"
  10. "time"
  11. "github.com/jacobsa/fuse/fuseops"
  12. drive "google.golang.org/api/drive/v3"
  13. )
  14. //FileEntry entry to handle files
  15. type FileEntry struct {
  16. //parent *FileEntry
  17. container *FileContainer
  18. //fs *GDriveFS
  19. GFile *drive.File // GDrive file
  20. isDir bool // Is dir
  21. Name string // local name
  22. // fuseops
  23. Inode fuseops.InodeID
  24. Attr fuseops.InodeAttributes // Cached attributes
  25. // cache file
  26. tempFile *os.File // Cached file
  27. // childs
  28. children []*FileEntry // children
  29. }
  30. func (fe *FileEntry) AddChild(child *FileEntry) {
  31. //child.parent = fe // is this needed at all?
  32. // Solve name here?
  33. fe.children = append(fe.children, child)
  34. }
  35. func (fe *FileEntry) RemoveChild(child *FileEntry) {
  36. toremove := -1
  37. for i, v := range fe.children {
  38. if v == child {
  39. toremove = i
  40. break
  41. }
  42. }
  43. if toremove == -1 {
  44. return
  45. }
  46. fe.children = append(fe.children[:toremove], fe.children[toremove+1:]...)
  47. }
  48. // useful for debug to count children
  49. /*func (fe *FileEntry) Count() int {
  50. count := 0
  51. for _, c := range fe.children {
  52. count += c.Count()
  53. }
  54. return count + len(fe.children)
  55. }*/
  56. // SetGFile update attributes and set drive.File
  57. func (fe *FileEntry) SetGFile(f *drive.File) {
  58. // Create Attribute
  59. attr := fuseops.InodeAttributes{}
  60. attr.Nlink = 1
  61. attr.Size = uint64(f.Size)
  62. //attr.Size = uint64(f.QuotaBytesUsed)
  63. // Temp
  64. attr.Uid = fe.container.uid
  65. attr.Gid = fe.container.gid
  66. attr.Crtime, _ = time.Parse(time.RFC3339, f.CreatedTime)
  67. attr.Ctime = attr.Crtime // Set CTime to created, although it is change inode metadata
  68. attr.Mtime, _ = time.Parse(time.RFC3339, f.ModifiedTime)
  69. attr.Atime = attr.Mtime // Set access time to modified, not sure if gdrive has access time
  70. attr.Mode = os.FileMode(0644) // default
  71. if f.MimeType == "application/vnd.google-apps.folder" {
  72. attr.Mode = os.FileMode(0755) | os.ModeDir
  73. }
  74. fe.GFile = f
  75. fe.Attr = attr
  76. }
  77. // Sync cached , upload to gdrive
  78. func (fe *FileEntry) Sync() (err error) {
  79. if fe.tempFile == nil {
  80. return
  81. }
  82. fe.tempFile.Sync()
  83. fe.tempFile.Seek(0, io.SeekStart)
  84. ngFile := &drive.File{}
  85. up := fe.container.fs.client.Files.Update(fe.GFile.Id, ngFile)
  86. upFile, err := up.Media(fe.tempFile).Do()
  87. fe.SetGFile(upFile) // update local GFile entry
  88. return
  89. }
  90. //ClearCache remove local file
  91. func (fe *FileEntry) ClearCache() (err error) {
  92. if fe.tempFile == nil {
  93. return
  94. }
  95. fe.tempFile.Close()
  96. os.Remove(fe.tempFile.Name())
  97. fe.tempFile = nil
  98. return
  99. }
  100. // Cache download GDrive file to a temporary local file or return already created file
  101. func (fe *FileEntry) Cache() *os.File {
  102. if fe.tempFile != nil {
  103. return fe.tempFile
  104. }
  105. var res *http.Response
  106. var err error
  107. // Export GDocs (Special google doc documents needs to be exported make a config somewhere for this)
  108. switch fe.GFile.MimeType { // Make this somewhat optional
  109. case "application/vnd.google-apps.document":
  110. log.Println("Exporting as: text/markdown")
  111. res, err = fe.container.fs.client.Files.Export(fe.GFile.Id, "text/plain").Download()
  112. case "application/vnd.google-apps.spreadsheet":
  113. log.Println("Exporting as: text/csv")
  114. res, err = fe.container.fs.client.Files.Export(fe.GFile.Id, "text/csv").Download()
  115. default:
  116. res, err = fe.container.fs.client.Files.Get(fe.GFile.Id).Download()
  117. }
  118. if err != nil {
  119. log.Println("MimeType:", fe.GFile.MimeType)
  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. // Find the right parent?
  135. // WRONG
  136. func (fe *FileEntry) solveAppendGFile(f *drive.File, inode fuseops.InodeID) *FileEntry {
  137. fil := fe.container.FindByGID(f.Id)
  138. if fil != nil { // ignore existing ID
  139. return fil
  140. }
  141. if len(f.Parents) == 0 {
  142. return fe.AppendGFile(f, inode) // = append(fs.root.fileList, entry)
  143. }
  144. for _, parent := range f.Parents { // hierarchy add
  145. parentEntry := fe.container.FindByGID(parent)
  146. if parentEntry == nil {
  147. log.Fatalln("Non existent parent", parent)
  148. }
  149. // Here
  150. return parentEntry.AppendGFile(f, inode)
  151. }
  152. return nil
  153. }
  154. // Load append whatever?
  155. // append file to this tree
  156. func (fe *FileEntry) AppendGFile(f *drive.File, inode fuseops.InodeID) *FileEntry {
  157. name := f.Name
  158. count := 1
  159. nameParts := strings.Split(f.Name, ".")
  160. for {
  161. en := fe.FindByName(name, false) // locally only
  162. if en == nil { // ok we want no value
  163. break
  164. }
  165. count++
  166. if len(nameParts) > 1 {
  167. name = fmt.Sprintf("%s(%d).%s", nameParts[0], count, strings.Join(nameParts[1:], "."))
  168. } else {
  169. name = fmt.Sprintf("%s(%d)", nameParts[0], count)
  170. }
  171. }
  172. // Create an entry
  173. //log.Println("Creating new file entry for name:", name, "for GFile:", f.Name)
  174. // lock from find inode to fileList append
  175. entry := fe.container.FileEntry(inode)
  176. entry.Name = name
  177. entry.SetGFile(f)
  178. fe.AddChild(entry)
  179. //fe.fileList = append(fe.fileList, entry)
  180. //fe.fileMap[f.Name] = entry
  181. return entry
  182. }
  183. //FindByInode find by Inode or return self
  184. func (fe *FileEntry) FindByInode(inode fuseops.InodeID, recurse bool) *FileEntry {
  185. if inode == fe.Inode {
  186. return fe // return self
  187. }
  188. // Recurse??
  189. for _, e := range fe.children {
  190. if e.Inode == inode {
  191. return e
  192. }
  193. if recurse {
  194. re := e.FindByInode(inode, recurse)
  195. if re != nil {
  196. return re
  197. }
  198. }
  199. }
  200. // For each child we findByInode
  201. return nil
  202. }
  203. // FindByName return a child entry by name
  204. func (fe *FileEntry) FindByName(name string, recurse bool) *FileEntry {
  205. // Recurse??
  206. for _, e := range fe.children {
  207. if e.Name == name {
  208. return e
  209. }
  210. if recurse {
  211. re := e.FindByName(name, recurse)
  212. if re != nil {
  213. return re
  214. }
  215. }
  216. }
  217. // For each child we findByInode
  218. return nil
  219. }
  220. // FindByGID find by google drive ID
  221. /*func (fe *FileEntry) FindByGID(gdriveID string, recurse bool) *FileEntry {
  222. // Recurse??
  223. for _, e := range fe.children {
  224. if e.GFile.Id == gdriveID {
  225. return e
  226. }
  227. if recurse {
  228. re := e.FindByGID(gdriveID, recurse)
  229. if re != nil {
  230. return re
  231. }
  232. }
  233. }
  234. // For each child we findByInode
  235. return nil
  236. }*/