123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- package basefs
- import (
- "io"
- "io/ioutil"
- "net/http"
- "os"
- "time"
- "github.com/jacobsa/fuse"
- "github.com/jacobsa/fuse/fuseops"
- drive "google.golang.org/api/drive/v3"
- )
- //FileEntry entry to handle files
- type FileEntry struct {
- //parent *FileEntry
- fs *BaseFS
- GID string // google driveID
- GFile *drive.File // GDrive file // Interface maybe?
- Name string // local name
- // fuseops
- Inode fuseops.InodeID
- Attr fuseops.InodeAttributes // Cached attributes
- // cache file
- tempFile *os.File // Cached file
- // childs
- //children []*FileEntry // children
- }
- // Why?
- func (fe *FileEntry) HasParentGID(gid string) bool {
- // Exceptional case
- if fe.Inode == fuseops.RootInodeID {
- return false
- }
- if gid == "" { // We are looking in root
- if fe.GFile == nil {
- return true
- }
- if len(fe.GFile.Parents) == 0 {
- return true
- }
- }
- if fe.GFile == nil { // Case gid is not empty and GFile is null
- return false
- }
- for _, pgid := range fe.GFile.Parents {
- if pgid == gid {
- return true
- }
- }
- return false
- }
- // SetGFile update attributes and set drive.File
- func (fe *FileEntry) SetGFile(gfile *drive.File) { // Should remove from here maybe?
- if gfile == nil {
- fe.GFile = nil
- } else {
- fe.GFile = gfile
- }
- // GetAttribute from GFile somehow
- // Create Attribute
- attr := fuseops.InodeAttributes{}
- attr.Nlink = 1
- attr.Uid = fe.fs.Config.UID
- attr.Gid = fe.fs.Config.GID
- attr.Mode = os.FileMode(0644) // default
- if gfile != nil {
- attr.Size = uint64(gfile.Size)
- attr.Crtime, _ = time.Parse(time.RFC3339, gfile.CreatedTime)
- attr.Ctime = attr.Crtime // Set CTime to created, although it is change inode metadata
- attr.Mtime, _ = time.Parse(time.RFC3339, gfile.ModifiedTime)
- attr.Atime = attr.Mtime // Set access time to modified, not sure if gdrive has access time
- if gfile.MimeType == "application/vnd.google-apps.folder" {
- attr.Mode = os.FileMode(0755) | os.ModeDir
- }
- fe.GID = gfile.Id
- }
- fe.Attr = attr
- }
- // Sync cached , upload to gdrive
- func (fe *FileEntry) Sync() (err error) {
- if fe.tempFile == nil {
- return
- }
- fe.tempFile.Sync()
- fe.tempFile.Seek(0, io.SeekStart)
- ngFile := &drive.File{}
- up := fe.fs.Client.Files.Update(fe.GFile.Id, ngFile)
- upFile, err := up.Media(fe.tempFile).Fields(fileFields).Do()
- fe.SetGFile(upFile) // update local GFile entry
- return
- }
- //ClearCache remove local file
- func (fe *FileEntry) ClearCache() (err error) {
- if fe.tempFile == nil {
- return
- }
- fe.tempFile.Close()
- os.Remove(fe.tempFile.Name())
- fe.tempFile = nil
- return
- }
- // Cache download GDrive file to a temporary local file or return already created file
- func (fe *FileEntry) Cache() *os.File {
- if fe.tempFile != nil {
- return fe.tempFile
- }
- var res *http.Response
- var err error
- // Export GDocs (Special google doc documents needs to be exported make a config somewhere for this)
- switch fe.GFile.MimeType { // Make this somewhat optional special case
- case "application/vnd.google-apps.document":
- log.Println("Exporting as: text/markdown")
- res, err = fe.fs.Client.Files.Export(fe.GFile.Id, "text/plain").Download()
- case "application/vnd.google-apps.spreadsheet":
- log.Println("Exporting as: text/csv")
- res, err = fe.fs.Client.Files.Export(fe.GFile.Id, "text/csv").Download()
- default:
- res, err = fe.fs.Client.Files.Get(fe.GFile.Id).Download()
- }
- if err != nil {
- log.Println("Error from GDrive API", err)
- return nil
- }
- defer res.Body.Close()
- // Local copy
- fe.tempFile, err = ioutil.TempFile(os.TempDir(), "gdfs") // TODO: const this elsewhere
- if err != nil {
- log.Println("Error creating temp file")
- return nil
- }
- io.Copy(fe.tempFile, res.Body)
- fe.tempFile.Seek(0, io.SeekStart)
- return fe.tempFile
- }
- func (fe *FileEntry) Truncate() (err error) { // DriverTruncate
- // Delete and create another on truncate 0
- err = fe.fs.Client.Files.Delete(fe.GFile.Id).Do() // XXX: Careful on this
- createdFile, err := fe.fs.Client.Files.Create(&drive.File{Parents: fe.GFile.Parents, Name: fe.GFile.Name}).Fields(fileFields).Do()
- if err != nil {
- return fuse.EINVAL // ??
- }
- fe.SetGFile(createdFile) // Set new file
- return
- }
- // IsDir returns true if entry is a directory:w
- func (fe *FileEntry) IsDir() bool {
- return fe.Attr.Mode&os.ModeDir == os.ModeDir
- }
|