file_entry.go 6.2 KB

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