fileentry.go 6.4 KB

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