fileentry.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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. fs *FuseHandler
  18. GFile *drive.File // GDrive file
  19. isDir bool // Is dir
  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.core.Config.UID
  64. attr.Gid = fe.core.Config.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.fs.srv.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.fs.srv.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.fs.srv.Files.Export(fe.GFile.Id, "text/csv").Download()
  114. default:
  115. res, err = fe.fs.srv.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.FindByGID(f.Id, true)
  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.FindByGID(parent, true)
  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 := NewFileEntry(fe.fs)
  175. entry.Name = name
  176. entry.SetGFile(f)
  177. entry.Inode = inode
  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. }
  237. func (fe *FileEntry) FindUnusedInode() fuseops.InodeID {
  238. var inode fuseops.InodeID
  239. for inode = 2; inode < 99999; inode++ {
  240. f := fe.FindByInode(inode, true)
  241. if f == nil {
  242. return inode
  243. }
  244. }
  245. log.Println("0 Inode ODD")
  246. return 0
  247. }