fileentry.go 6.5 KB

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