fileentry.go 7.5 KB

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