fileentry.go 6.5 KB

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