Kaynağa Gözat

Added GOPATH to Makefile

luis 7 yıl önce
ebeveyn
işleme
eafafad357

+ 2 - 2
Makefile

@@ -2,10 +2,10 @@ GOPATH=$(CURDIR)/deps:$(CURDIR)
 DIST=./DIST
 
 all:
-	go build -o DIST/gdrivemount gdrivemount/cmd/gdrivemount
+	GOPATH=$(GOPATH) go build -o DIST/gdrivemount gdrivemount/cmd/gdrivemount
 
 deps:
-	go get -v ./src/gdrivemount/cmd/gdrivemount
+	GOPATH=$(GOPATH) go get -v ./src/gdrivemount/cmd/gdrivemount
 
 test:
 	cd test; ../DIST/gdrivemount mount

+ 1 - 1
deps/src/golang.org/x/crypto

@@ -1 +1 @@
-Subproject commit 69be088f860613049aa58c65154d1b1d32bbdf90
+Subproject commit 51714a8c4ac1764f07ab4127d7f739351ced4759

+ 1 - 0
src/gdrivemount/cmd/gdrivemount/main.go

@@ -31,6 +31,7 @@ func main() {
 	server := fuseutil.NewFileSystemServer(gdriveFS)
 
 	mfs, err := fuse.Mount(flag.Arg(0), server, &fuse.MountConfig{DebugLogger: prettylog.New("fuse"), ErrorLogger: prettylog.New("fuse-err")})
+	//mfs, err := fuse.Mount(flag.Arg(0), server, &fuse.MountConfig{})
 	if err != nil {
 		log.Fatal("Failed mounting path", flag.Arg(0))
 	}

+ 128 - 19
src/gdrivemount/fileentry.go

@@ -2,8 +2,10 @@ package gdrivemount
 
 import (
 	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
 	"os"
-	"strconv"
 	"strings"
 	"time"
 
@@ -11,17 +13,112 @@ import (
 	drive "google.golang.org/api/drive/v3"
 )
 
+//FileEntry entry to handle files
 type FileEntry struct {
-	fs    *GDriveFS
-	GFile *drive.File
-	isDir bool
-	Name  string // local name
+	parent *FileEntry
+	fs     *GDriveFS
+	GFile  *drive.File
+	isDir  bool
+	Name   string // local name
+	// fuseops
 	Inode fuseops.InodeID
 	Attr  fuseops.InodeAttributes
-	// inode?
-	//fileMap map[string]*FileEntry
+
+	// cache file
+	tempFile *os.File
+	// childs
 	fileList []*FileEntry // children
-	//fileMap map[string]*FileEntry // Children?
+}
+
+func (fe *FileEntry) removeEntry(entry *FileEntry) {
+	toremove := -1
+	for i, v := range fe.fileList {
+		if v == entry {
+			toremove = i
+			break
+		}
+	}
+
+	if toremove == -1 {
+		return
+	}
+	fe.fileList = append(fe.fileList[:toremove], fe.fileList[toremove+1:]...)
+}
+
+// useful for debug to count the entries
+func (fe *FileEntry) count() int {
+	count := 0
+
+	for _, c := range fe.fileList {
+		count += c.count()
+	}
+	return count + len(fe.fileList)
+
+}
+
+// Sync cached , upload
+// Clear cache too
+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.srv.Files.Update(fe.GFile.Id, ngFile)
+	_, err = up.Media(fe.tempFile).Do()
+
+	return
+
+}
+func (fe *FileEntry) clearCache() (err error) {
+	if fe.tempFile == nil {
+		return
+	}
+	//fe.sync() // Sync?
+	fe.tempFile.Close()
+	os.Remove(fe.tempFile.Name())
+	fe.tempFile = nil
+	return
+}
+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 {
+	case "application/vnd.google-apps.document":
+		log.Println("Exporting as: text/markdown")
+		res, err = fe.fs.srv.Files.Export(fe.GFile.Id, "text/plain").Download()
+	case "application/vnd.google-apps.spreadsheet":
+		log.Println("Exporting as: text/csv")
+		res, err = fe.fs.srv.Files.Export(fe.GFile.Id, "text/csv").Download()
+	default:
+		res, err = fe.fs.srv.Files.Get(fe.GFile.Id).Download()
+	}
+
+	if err != nil {
+		log.Println("MimeType:", fe.GFile.MimeType)
+		log.Println("Error from GDrive API", err)
+		return nil
+	}
+	defer res.Body.Close()
+
+	// Local copy
+	fe.tempFile, err = ioutil.TempFile(os.TempDir(), "gdfs")
+	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
+
 }
 
 // Load append whatever?
@@ -44,17 +141,13 @@ func (fe *FileEntry) appendGFile(f *drive.File) *FileEntry {
 		}
 	}
 
-	uid, _ := strconv.Atoi(fe.fs.osuser.Uid)
-	gid, _ := strconv.Atoi(fe.fs.osuser.Gid)
-
 	// Create Attribute
 	attr := fuseops.InodeAttributes{}
 	attr.Nlink = 1
 	attr.Size = uint64(f.Size)
 	// Temp
-	attr.Uid = uint32(uid)
-	attr.Gid = uint32(gid)
-
+	attr.Uid = fe.fs.getUID()
+	attr.Gid = fe.fs.getGID()
 	attr.Crtime, _ = time.Parse(time.RFC3339, f.CreatedTime)
 	attr.Mtime, _ = time.Parse(time.RFC3339, f.ModifiedTime)
 	attr.Mode = os.FileMode(0644) // default
@@ -62,15 +155,31 @@ func (fe *FileEntry) appendGFile(f *drive.File) *FileEntry {
 		attr.Mode = os.FileMode(0755) | os.ModeDir
 	}
 	// Create an entry
+
+	// lock from find inode to fileList append
 	entry := &FileEntry{
-		fs:    fe.fs,
-		GFile: f,
-		Name:  name,                    // we dont need name
-		Inode: fe.fs.findUnusedInode(), // Lock somehow
-		Attr:  attr,
+		parent: fe,
+		fs:     fe.fs,
+		GFile:  f,
+		Name:   name,                    // we dont need name
+		Inode:  fe.fs.findUnusedInode(), // Lock somehow
+		Attr:   attr,
 		//fileMap: map[string]*FileEntry{},
 		fileList: []*FileEntry{},
 	}
+	if entry.Inode == 4 {
+		log.Println("Created entry for:", name, " with inode:", entry.Inode)
+		log.Printf("It will be added to Parent: '%s' (%d)", fe.Name, fe.Inode)
+		tparent := fe.parent
+		for {
+			if tparent == nil {
+				break
+			}
+			log.Printf("which have the parent '%s' (%d)", tparent.Name, tparent.Inode)
+			tparent = tparent.parent
+
+		}
+	}
 
 	fe.fileList = append(fe.fileList, entry)
 	//fe.fileMap[f.Name] = entry

+ 1 - 14
src/gdrivemount/gdrive.go

@@ -93,7 +93,7 @@ func getConfigPath() (string, error) {
 	return configDir, nil
 }
 
-func GetDrive() *drive.Service {
+func GetDriveService() *drive.Service {
 
 	configPath, err := getConfigPath()
 	if err != nil {
@@ -120,16 +120,3 @@ func GetDrive() *drive.Service {
 	return srv
 
 }
-
-/*func main() {
-	srv := GetDrive()
-	r, err := srv.Files.List().PageSize(10).Fields("nextPageToken, files(id, name)").Do()
-	if err != nil {
-		log.Fatalf("Unable to retrieve files: %v", err)
-	}
-
-	for _, v := range r.Files {
-		log.Printf("%s (%s) %d\n", v.Name, v.Id, v.Size)
-	}
-
-}*/

+ 267 - 224
src/gdrivemount/gdrive-fuse.go

@@ -1,10 +1,8 @@
+// gdrivemount implements a google drive fuse driver
 package gdrivemount
 
 import (
-	"fmt"
 	"io"
-	"io/ioutil"
-	"net/http"
 	"os"
 	"os/user"
 	"strconv"
@@ -26,54 +24,50 @@ var (
 	log = prettylog.New("gdrivemount")
 )
 
-type FileHandle struct {
-	entry        *FileEntry
-	tempFile     *os.File
+type fileHandle struct {
+	handleID fuseops.HandleID
+	entry    *FileEntry
+	//tempFile     *os.File
 	uploadOnDone bool
+	// Testing
+	entries []fuseutil.Dirent
+	buf     []byte
 }
 
 /*type DirEntry struct {
 	file *FileEntry
 }*/
 
+// GDriveFS handler
 type GDriveFS struct {
 	fuseutil.NotImplementedFileSystem
 	srv *drive.Service
 
 	osuser *user.User
 	root   *FileEntry // hiearchy reference
-	//fileList []*FileEntry // All files
 
-	fileHandles map[fuseops.HandleID]*FileHandle
+	fileHandles map[fuseops.HandleID]*fileHandle
 }
 
 ////////////////////////////////////////////////////////
 // TOOLS & HELPERS
 ////////////////////////////////////////////////////////
 
-// Load append whatever?
+// appendFile solve parent before add,
 func (fs *GDriveFS) appendFile(f *drive.File) *FileEntry {
 
 	fil := fs.root.findByGID(f.Id, true)
 	if fil != nil { // ignore existing ID
 		return nil
 	}
-	name := f.Name
-	for count := 1; ; {
-		found := fs.root.findByName(name, true)
-		if found == nil {
-			break
-		}
-		count++
-		name = fmt.Sprintf("(%d) %s", count, f.Name)
-	}
-
-	//fs.fileList = append(fs.fileList, entry) // Globally add in list
 
 	var entry *FileEntry
 	if len(f.Parents) == 0 {
 		entry = fs.root.appendGFile(f) // = append(fs.root.fileList, entry)
 	}
+	if len(f.Parents) > 1 {
+		log.Println("This one has more than 1 parent:", f.Parents)
+	}
 	for _, parent := range f.Parents { // hierarchy add
 		parentEntry := fs.root.findByGID(parent, true)
 		if parentEntry == nil {
@@ -82,13 +76,7 @@ func (fs *GDriveFS) appendFile(f *drive.File) *FileEntry {
 
 		// Here
 		entry = parentEntry.appendGFile(f)
-		if parentEntry.Name == "work" {
-			log.Println("Adding file to: ", f.Name, "Count:", len(parentEntry.fileList))
-
-		}
 	}
-	//fs.findByGID(f.Parents
-	//fs.root.fileList = append(fs.root.fileList, entry)
 	return entry
 }
 
@@ -110,18 +98,21 @@ func (fs *GDriveFS) defaultAttributes() fuseops.InodeAttributes {
 	}
 }
 
-func (fs *GDriveFS) findUnusedHandle() fuseops.HandleID {
+func (fs *GDriveFS) findUnusedHandle() *fileHandle {
+	// Lock here instead
 
 	var handle fuseops.HandleID
 
 	for handle = 1; handle < 99999; handle++ {
 		_, ok := fs.fileHandles[handle]
 		if !ok {
-			return handle
+			break
 		}
 	}
+	fh := &fileHandle{handleID: handle}
+	fs.fileHandles[handle] = fh
 
-	return 0 // ERR
+	return fh
 }
 
 func (fs *GDriveFS) findUnusedInode() fuseops.InodeID {
@@ -132,38 +123,13 @@ func (fs *GDriveFS) findUnusedInode() fuseops.InodeID {
 			return inode
 		}
 	}
-	return 0
-}
+	if inode == 4 {
+		log.Println("Inode is 4")
 
-/*
-func (fs *GDriveFS) findByName(name string) *FileEntry {
-	// Split path maybe
-	for _, v := range fs.fileList {
-		if v.Name == name {
-			return v
-		}
-	}
-	return nil
-}
-func (fs *GDriveFS) findByGID(id string) *FileEntry {
-	for _, v := range fs.fileList {
-		if v.GFile.Id == id {
-			return v
-		}
 	}
-	return nil
+	log.Println("0 Inode ODD")
+	return 0
 }
-func (fs *GDriveFS) findByInode(inode fuseops.InodeID) *FileEntry {
-	if inode == 1 {
-		return fs.root
-	}
-	for _, v := range fs.fileList {
-		if v.Inode == inode {
-			return v
-		}
-	}
-	return nil
-}*/
 
 func NewGDriveFS() *GDriveFS {
 
@@ -172,9 +138,17 @@ func NewGDriveFS() *GDriveFS {
 		log.Fatalf("Unable to fetch current user:", err)
 	}
 	fs := &GDriveFS{}
-	fs.srv = GetDrive()
+	fs.osuser = osuser
+	fs.srv = GetDriveService()
 	fs.root = &FileEntry{
-		fs:    fs,
+		fs: fs,
+		Attr: fuseops.InodeAttributes{
+			Mode:  os.FileMode(0755) | os.ModeDir,
+			Nlink: 1,
+			Size:  4096,
+			Uid:   fs.getUID(),
+			Gid:   fs.getGID(),
+		},
 		GFile: nil,
 		Inode: fuseops.RootInodeID,
 		Name:  "",
@@ -182,28 +156,36 @@ func NewGDriveFS() *GDriveFS {
 		fileList: []*FileEntry{},
 		isDir:    true,
 	}
-	fs.fileHandles = map[fuseops.HandleID]*FileHandle{}
-	fs.osuser = osuser
+	fs.fileHandles = map[fuseops.HandleID]*fileHandle{}
 
-	fs.root.appendGFile(&drive.File{Id: "0", Name: "Loading..."})
+	entry := fs.root.appendGFile(&drive.File{Id: "0", Name: "Loading..."})
+	entry.Attr.Mode = os.FileMode(0)
 
 	go fs.refresh() // async fetch
 
 	return fs
 }
 
+// Cache somewhere
+func (fs *GDriveFS) getUID() uint32 {
+	uid, _ := strconv.Atoi(fs.osuser.Uid)
+	return uint32(uid)
+}
+func (fs *GDriveFS) getGID() uint32 {
+	gid, _ := strconv.Atoi(fs.osuser.Gid)
+	return uint32(gid)
+}
+
 // Reload service files
 func (fs *GDriveFS) refresh() {
 	fileMap := map[string]*drive.File{} // Temporary map by google drive ID
 
 	gdFields := googleapi.Field("nextPageToken, files(id,name,size,mimeType,parents,createdTime,modifiedTime)")
-
 	log.Println("Loading file entries from gdrive")
 	r, err := fs.srv.Files.List().PageSize(1000).
-		//SupportsTeamDrives(true).
-		//IncludeTeamDriveItems(true).
+		SupportsTeamDrives(true).
+		IncludeTeamDriveItems(true).
 		Fields(gdFields).
-		//IncludeTeamDriveItems(true).
 		Do()
 	if err != nil {
 		panic(err)
@@ -217,12 +199,7 @@ func (fs *GDriveFS) refresh() {
 	// Rest of the page
 	for r.NextPageToken != "" {
 		log.Println("Loading next page")
-		r, err = fs.srv.Files.List().
-			//	SupportsTeamDrives(true).
-			//	IncludeTeamDriveItems(true).
-			PageToken(r.NextPageToken).
-			//Fields("nextPageToken, files(id,name,size,mimeType,parents,createdTime)").
-			Do()
+		r, err = fs.srv.Files.List().PageToken(r.NextPageToken).Do()
 		if err != nil {
 			panic(err)
 		}
@@ -236,7 +213,8 @@ func (fs *GDriveFS) refresh() {
 		log.Fatal("Unable to retrieve files", err)
 	}
 
-	fs.root.fileList = []*FileEntry{} // clear
+	fs.root.fileList = []*FileEntry{} // clear fileList
+	// Helper func to recurse
 	// Everything loaded we add to our entries
 	var appendParentOf func(f *drive.File)
 	appendParentOf = func(f *drive.File) {
@@ -259,50 +237,94 @@ func (fs *GDriveFS) refresh() {
 		fs.appendFile(f)
 	}
 	log.Println("Refresh done")
+
+	log.Println("File count:", fs.root.count())
 }
 
 // OpenDir return nil error allows open dir
 func (fs *GDriveFS) OpenDir(ctx context.Context, op *fuseops.OpenDirOp) (err error) {
 
 	entry := fs.root.findByInode(op.Inode, true)
-
 	if entry == nil {
 		return fuse.ENOENT
 	}
 
-	log.Println("Found dir:", entry.Name)
-	log.Println("Count of entries:", len(entry.fileList))
-	handle := fs.findUnusedHandle()
-
-	fs.fileHandles[handle] = &FileHandle{entry: entry, tempFile: nil, uploadOnDone: false}
-	op.Handle = handle
+	fh := fs.findUnusedHandle()
+	fh.entry = entry
+	op.Handle = fh.handleID
 
 	return // No error allow, dir open
 }
 
 // ReadDir lists files into readdirop
 func (fs *GDriveFS) ReadDir(ctx context.Context, op *fuseops.ReadDirOp) (err error) {
-	if op.Inode == 1 {
-		log.Println("Reading root inode, offset:", op.Offset)
-	}
-	dir, ok := fs.fileHandles[op.Handle]
+	fh, ok := fs.fileHandles[op.Handle]
 	if !ok {
 		log.Fatal("Handle does not exists")
 	}
 
-	log.Println("Dir:", dir)
-	entries := []*FileEntry{}
-	for _, v := range dir.entry.fileList {
-		entries = append(entries, v)
+	if op.Offset == 0 { // Rebuild/rewind dir list
+		fh.entries = []fuseutil.Dirent{}
+
+		//fh.buf = make([]byte, 1000000) // Temp bigbuf somehow
+		//written := 0
+
+		for i, v := range fh.entry.fileList {
+			fusetype := fuseutil.DT_File
+			if v.isDir {
+				fusetype = fuseutil.DT_Directory
+			}
+			dirEnt := fuseutil.Dirent{
+				Inode:  v.Inode,
+				Name:   v.Name,
+				Type:   fusetype,
+				Offset: fuseops.DirOffset(i) + 1,
+			}
+			//	written += fuseutil.WriteDirent(fh.buf[written:], dirEnt)
+			fh.entries = append(fh.entries, dirEnt)
+		}
 	}
 
-	if op.Offset > fuseops.DirOffset(len(entries)) {
-		err = fuse.EIO
-		log.Println("Err in offset")
-		return
+	// local Buftest
+
+	// Raw copy
+	/*log.Println("Just copy data off:", op.Offset)
+	for {
+		n := copy(op.Dst[op.BytesRead:], fh.buf[op.Offset:])
+		if n == 0 {
+
+			break
+		}
+		log.Println("Written:", n)
+		op.BytesRead += n
+	}*/
+
+	index := int(op.Offset)
+	if index > len(fh.entries) {
+		return fuse.EINVAL
+	}
+	if index > 0 {
+		index++
+	}
+	count := 0
+	for i := index; i < len(fh.entries); i++ {
+		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], fh.entries[i])
+		//log.Println("Written:", n)
+		if n == 0 {
+			break
+		}
+		op.BytesRead += n
+		count++
 	}
+	//log.Printf("Written %d Dirent: '%s' TotalSent: %d", count, fh.entry.Name, op.BytesRead)
+
+	/*log.Println("Sending from offset:", op.Offset)
 	entries = entries[op.Offset:]
 
+	log.Println("Total entries for this tree:", len(dir.entry.fileList))
+
+	count := 0
+	offCount := int(op.Offset)
 	// Resume at the specified offset into the array.
 	for i, v := range entries {
 		fusetype := fuseutil.DT_File
@@ -317,158 +339,117 @@ func (fs *GDriveFS) ReadDir(ctx context.Context, op *fuseops.ReadDirOp) (err err
 		}
 		//log.Println("Entry offset:", dirEnt.Offset)
 		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], dirEnt)
-		if n == 0 {
+		if n == 0 { // let go
+			log.Println("Broke the Write")
 			break
 		}
+
+		count++
+		offCount++
 		op.BytesRead += n
 	}
-	log.Println("Readed:", op.BytesRead, "bytes")
-
+	log.Println("BytesRead:", op.BytesRead, " count:", count, " offCount:", offCount, " last:", len(dir.entry.fileList)-count)*/
 	return
 }
 
-// We dont do nothing on gdrive for now
+// SetInodeAttributes Not sure what attributes gdrive support we just leave this blank for now
 func (fs *GDriveFS) SetInodeAttributes(ctx context.Context, op *fuseops.SetInodeAttributesOp) (err error) {
-
 	return
 }
 
 //GetInodeAttributes return attributes
 func (fs *GDriveFS) GetInodeAttributes(ctx context.Context, op *fuseops.GetInodeAttributesOp) (err error) {
-	log.Println("Loading inode attr for inod:", op.Inode)
-	now := time.Now()
-	var attr fuseops.InodeAttributes
-	if op.Inode == fuseops.RootInodeID {
-		attr = fs.defaultAttributes()
-		attr.Mode = 0755 | os.ModeDir
-		attr.Atime = time.Now()
-		attr.Mtime = time.Now()
-		attr.Crtime = time.Now()
 
-	} else {
-		f := fs.root.findByInode(op.Inode, true)
-		attr = f.Attr
+	f := fs.root.findByInode(op.Inode, true)
+	if f == nil {
+		return fuse.ENOENT
 	}
-
-	op.Attributes = attr
-	op.AttributesExpiration = now.Add(3 * time.Second)
+	op.Attributes = f.Attr
+	op.AttributesExpiration = time.Now().Add(time.Minute)
 
 	return
 }
+
+// ReleaseDirHandle deletes file handle entry
 func (fs *GDriveFS) ReleaseDirHandle(ctx context.Context, op *fuseops.ReleaseDirHandleOp) (err error) {
 	delete(fs.fileHandles, op.Handle)
 	return
 }
 
+// LookUpInode based on Parent and Name we return a self cached inode
 func (fs *GDriveFS) LookUpInode(ctx context.Context, op *fuseops.LookUpInodeOp) (err error) {
-	log.Println("Lookup for:", op.Name, "in inode:", op.Parent)
-
-	parent := fs.root.findByInode(op.Parent, true)
-	now := time.Now()
 
-	if parent == nil {
+	parentFile := fs.root.findByInode(op.Parent, true) // true means transverse all
+	if parentFile == nil {
 		return fuse.ENOENT
 	}
-	// Transverse all?
-	f := parent.findByName(op.Name, false)
+
+	now := time.Now()
+	// Transverse only local
+	f := parentFile.findByName(op.Name, false)
 	if f == nil {
-		err = fuse.ENOENT
-		return
+		return fuse.ENOENT
 	}
 
 	op.Entry = fuseops.ChildInodeEntry{
 		Attributes:           f.Attr,
 		Child:                f.Inode,
-		AttributesExpiration: now.Add(1 * time.Hour),
-		EntryExpiration:      now.Add(1 * time.Hour),
+		AttributesExpiration: now.Add(time.Minute),
+		EntryExpiration:      now.Add(time.Minute),
 	}
-
 	return
 }
 
-func (fs *GDriveFS) StatFS(ctx context.Context, op *fuseops.StatFSOp) (err error) {
+// StatFS basically allows StatFS to run
+/*func (fs *GDriveFS) StatFS(ctx context.Context, op *fuseops.StatFSOp) (err error) {
 	return
-}
+}*/
 
+// ForgetInode allows to forgetInode
 func (fs *GDriveFS) ForgetInode(ctx context.Context, op *fuseops.ForgetInodeOp) (err error) {
 	return
 }
 
+// GetXAttr special attributes
 func (fs *GDriveFS) GetXAttr(ctx context.Context, op *fuseops.GetXattrOp) (err error) {
-	err = fuse.ENOATTR
 	return
 }
 
 //////////////////////////////////////////////////////////////////////////
 // File OPS
 //////////////////////////////////////////////////////////////////////////
+
+// OpenFile creates a temporary handle to be handled on read or write
 func (fs *GDriveFS) OpenFile(ctx context.Context, op *fuseops.OpenFileOp) (err error) {
-	log.Println("Reading inode:", op.Inode)
 	f := fs.root.findByInode(op.Inode, true) // might not exists
 
-	log.Println("Opening file:", f.Name, " with flags:")
-
 	// Generate new handle
-	handleID := fs.findUnusedHandle()
-	fs.fileHandles[handleID] = &FileHandle{entry: f}
-	op.Handle = handleID
+	fh := fs.findUnusedHandle()
+	fh.entry = f
+
+	op.Handle = fh.handleID
 	op.UseDirectIO = true
 
 	return
 }
 
+// ReadFile  if the first time we download the google drive file into a local temporary file
 func (fs *GDriveFS) ReadFile(ctx context.Context, op *fuseops.ReadFileOp) (err error) {
-
-	log.Println("Reading from:", op.Offset)
-
 	lf := fs.fileHandles[op.Handle]
 
-	if lf.tempFile == nil {
-		// Do this on read actually
-		var res *http.Response
-
-		// Export GDocs
-		switch lf.entry.GFile.MimeType {
-		case "application/vnd.google-apps.document":
-			log.Println("Exporting as: text/markdown")
-			res, err = fs.srv.Files.Export(lf.entry.GFile.Id, "text/plain").Download()
-		case "application/vnd.google-apps.spreadsheet":
-			log.Println("Exporting as: text/csv")
-			res, err = fs.srv.Files.Export(lf.entry.GFile.Id, "text/csv").Download()
-		default:
-			res, err = fs.srv.Files.Get(lf.entry.GFile.Id).Download()
-		}
-
-		if err != nil {
-			log.Println("MimeType:", lf.entry.GFile.MimeType)
-			log.Println("Error from GDrive API", err)
-			err = fuse.EINVAL
-			return
-		}
-		defer res.Body.Close()
-
-		// Local copy
-		lf.tempFile, err = ioutil.TempFile(os.TempDir(), "gdfs")
-		if err != nil {
-			log.Println("Error creating temp file")
-			return fuse.ENOSYS
-		}
-		io.Copy(lf.tempFile, res.Body)
-
-		lf.tempFile.Seek(0, io.SeekStart)
-	}
-
-	// Somewhat wrong to read file to temp everytime
-	op.BytesRead, err = lf.tempFile.ReadAt(op.Dst, op.Offset)
+	localFile := lf.entry.cache()
+	op.BytesRead, err = localFile.ReadAt(op.Dst, op.Offset)
 	if err != nil {
 		log.Println("Err reading file", err)
 	}
-	if err == io.EOF {
+	if err == io.EOF { // fuse does not expect a EOF
 		err = nil
 	}
 
 	return
 }
+
+// CreateFile creates empty file in google Drive and returns its ID and attributes, only allows file creation on 'My Drive'
 func (fs *GDriveFS) CreateFile(ctx context.Context, op *fuseops.CreateFileOp) (err error) {
 
 	parentFile := fs.root.findByInode(op.Parent, true)
@@ -477,8 +458,7 @@ func (fs *GDriveFS) CreateFile(ctx context.Context, op *fuseops.CreateFileOp) (e
 		return
 	}
 	// Only write on My Drive for now
-	if parentFile.Name != "My Drive" || parentFile.Inode == fuseops.RootInodeID {
-		log.Println("Parent:", parentFile.Name)
+	if parentFile.Inode == fuseops.RootInodeID {
 		err = fuse.ENOATTR
 		return
 	}
@@ -502,31 +482,33 @@ func (fs *GDriveFS) CreateFile(ctx context.Context, op *fuseops.CreateFileOp) (e
 		err = fuse.EINVAL
 		return
 	}
-	log.Println("Created file with inode:", entry.Inode)
+	log.Println("File Created", entry)
 
+	localFile := entry.cache()
+	if localFile == nil {
+		return fuse.EINVAL
+	}
 	// Associate a temp file to a new handle
 	// Local copy
-	localFile, err := ioutil.TempFile(os.TempDir(), "gdfs")
-	if err != nil {
-		log.Println("Error creating temp file")
-		err = fuse.ENOSYS
-		return
-	}
 	// Lock
-	handleID := fs.findUnusedHandle()
-	fs.fileHandles[handleID] = &FileHandle{entry, localFile, true}
+	fh := fs.findUnusedHandle()
+	fh.entry = entry
+	fh.uploadOnDone = true
 	//
-	op.Handle = handleID
+	op.Handle = fh.handleID
 	op.Entry = fuseops.ChildInodeEntry{
 		Attributes:           entry.Attr,
 		Child:                entry.Inode,
-		AttributesExpiration: time.Now().Add(1 * time.Minute),
+		AttributesExpiration: time.Now().Add(time.Minute),
 		EntryExpiration:      time.Now().Add(time.Minute),
 	}
+	op.Mode = entry.Attr.Mode
 
 	return
 }
 
+// WriteFile as ReadFile it creates a temporary file on first read
+// Maybe the ReadFile should be called here aswell to cache current contents since we are using writeAt
 func (fs *GDriveFS) WriteFile(ctx context.Context, op *fuseops.WriteFileOp) (err error) {
 	lf, ok := fs.fileHandles[op.Handle]
 	if !ok {
@@ -534,16 +516,12 @@ func (fs *GDriveFS) WriteFile(ctx context.Context, op *fuseops.WriteFileOp) (err
 		return
 	}
 
-	if lf.tempFile == nil {
-		// Local copy
-		lf.tempFile, err = ioutil.TempFile(os.TempDir(), "gdfs")
-		if err != nil {
-			log.Println("Error creating temp file")
-			return fuse.ENOSYS
-		}
-
+	localFile := lf.entry.cache()
+	if localFile == nil {
+		return fuse.EINVAL
 	}
-	_, err = lf.tempFile.WriteAt(op.Data, op.Offset)
+
+	_, err = localFile.WriteAt(op.Data, op.Offset)
 	if err != nil {
 		err = fuse.EIO
 		return
@@ -553,36 +531,24 @@ func (fs *GDriveFS) WriteFile(ctx context.Context, op *fuseops.WriteFileOp) (err
 	return
 }
 
+// ReleaseFileHandle closes and deletes any temporary files, upload in case if changed locally
 func (fs *GDriveFS) ReleaseFileHandle(ctx context.Context, op *fuseops.ReleaseFileHandleOp) (err error) {
 	lf := fs.fileHandles[op.Handle]
 
 	if lf.uploadOnDone {
-		lf.tempFile.Sync()
-		lf.tempFile.Seek(0, io.SeekStart)
-		// Upload somehow
-		ngfile := &drive.File{}
-		up := fs.srv.Files.Update(lf.entry.GFile.Id, ngfile)
+		err = lf.entry.sync()
 		if err != nil {
-			err = fuse.EINVAL
-			return
+			return fuse.EINVAL
 		}
-		updatedFile, err := up.Media(lf.tempFile).Do()
-		//updatedFile, err := up.ResumableMedia(ctx, lf.tempFile, tstat.Size(), "text/txt").Do()
-		if err != nil {
-			log.Println("Err on Media:", err)
-			return fuse.EIO
-		}
-		log.Println("What to do with this?:", updatedFile)
-	}
-	if lf.tempFile != nil {
-		lf.tempFile.Close()
-		os.Remove(lf.tempFile.Name())
 	}
+	lf.entry.clearCache()
 	delete(fs.fileHandles, op.Handle)
 
 	//go fs.refresh()
 	return
 }
+
+// Unlink remove file and remove from local cache entry
 func (fs *GDriveFS) Unlink(ctx context.Context, op *fuseops.UnlinkOp) (err error) {
 	parentEntry := fs.root.findByInode(op.Parent, true)
 	if parentEntry == nil {
@@ -597,21 +563,98 @@ func (fs *GDriveFS) Unlink(ctx context.Context, op *fuseops.UnlinkOp) (err error
 		return fuse.EIO
 	}
 
-	toremove := -1
-	for k, v := range parentEntry.fileList {
-		if v == fileEntry {
-			toremove = k
-			break
-		}
+	parentEntry.removeEntry(fileEntry)
+
+	return
+}
+
+// FlushFile just returns no error, maybe upload should be handled here
+func (fs *GDriveFS) FlushFile(ctx context.Context, op *fuseops.FlushFileOp) (err error) {
+	return
+}
+
+// MkDir creates a directory on a parent dir
+func (fs *GDriveFS) MkDir(ctx context.Context, op *fuseops.MkDirOp) (err error) {
+
+	parentFile := fs.root.findByInode(op.Parent, true)
+	if parentFile == nil {
+		return fuse.ENOENT
+	}
+	if parentFile.Inode == fuseops.RootInodeID {
+		return fuse.ENOATTR
+	}
+
+	// Should check existent first too
+	fi, err := fs.srv.Files.Create(&drive.File{
+		Parents:  []string{parentFile.GFile.Id},
+		MimeType: "application/vnd.google-apps.folder",
+		Name:     op.Name,
+	}).Do()
+	if err != nil {
+		return fuse.ENOATTR
+	}
+	entry := parentFile.appendGFile(fi)
+	if entry == nil {
+		return fuse.EINVAL
 	}
 
-	if toremove != -1 {
-		parentEntry.fileList = append(parentEntry.fileList[:toremove], parentEntry.fileList[toremove+1:]...)
+	op.Entry = fuseops.ChildInodeEntry{
+		Attributes:           entry.Attr,
+		Child:                entry.Inode,
+		AttributesExpiration: time.Now().Add(time.Minute),
+		EntryExpiration:      time.Now().Add(time.Microsecond),
 	}
 
 	return
 }
 
-func (fs *GDriveFS) FlushFile(ctx context.Context, op *fuseops.FlushFileOp) (err error) {
+func (fs *GDriveFS) RmDir(ctx context.Context, op *fuseops.RmDirOp) (err error) {
+
+	parentFile := fs.root.findByInode(op.Parent, true)
+	if parentFile == nil {
+		return fuse.ENOENT
+	}
+
+	theFile := parentFile.findByName(op.Name, false)
+
+	err = fs.srv.Files.Delete(theFile.GFile.Id).Do()
+	if err != nil {
+		return fuse.ENOTEMPTY
+	}
+
+	parentFile.removeEntry(theFile)
+
+	// Remove from entry somehow
+
 	return
 }
+
+func (fs *GDriveFS) Rename(ctx context.Context, op *fuseops.RenameOp) (err error) {
+	oldParentFile := fs.root.findByInode(op.OldParent, true)
+	if oldParentFile == nil {
+		return fuse.ENOENT
+	}
+	newParentFile := fs.root.findByInode(op.NewParent, true)
+	if newParentFile == nil {
+		return fuse.ENOENT
+	}
+
+	oldFile := oldParentFile.findByName(op.OldName, false)
+
+	ngFile := &drive.File{
+		Name: op.NewName,
+	}
+
+	updateCall := fs.srv.Files.Update(oldFile.GFile.Id, ngFile)
+	if oldParentFile != newParentFile {
+		updateCall.RemoveParents(oldParentFile.GFile.Id)
+		updateCall.AddParents(newParentFile.GFile.Id)
+	}
+	updatedFile, err := updateCall.Do()
+
+	oldParentFile.removeEntry(oldFile)
+	newParentFile.appendGFile(updatedFile)
+
+	return
+
+}

+ 0 - 153
src/gdrivemount/gdrive-fuse.go.old

@@ -1,153 +0,0 @@
-package gdrivemount
-
-import (
-	"fmt"
-	"io"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"os"
-
-	"github.com/hanwen/go-fuse/fuse"
-	"github.com/hanwen/go-fuse/fuse/nodefs"
-	"github.com/hanwen/go-fuse/fuse/pathfs"
-	drive "google.golang.org/api/drive/v3"
-)
-
-// GDriveFS google drive fuse FS
-type GDriveFS struct {
-	pathfs.FileSystem
-	srv *drive.Service
-
-	// Cache files somehow??
-	cacheFiles *drive.FileList
-	fileMap    map[string]*drive.File
-}
-
-func (fs *GDriveFS) refresh() {
-	r, err := fs.srv.Files.List().Do()
-	if err != nil {
-		log.Println("Unable to retrieve files")
-	}
-
-	for _, v := range r.Files {
-		name := v.Name
-		count := 1
-		for {
-			_, ok := fs.fileMap[name]
-			if ok { // already exists
-				count++
-				name = fmt.Sprintf("(%d) %s", count, v.Name)
-			} else {
-				fs.fileMap[name] = v
-				break
-			}
-		}
-	}
-	log.Println("Refresh done")
-}
-
-// NewGDriveFS fuse driver for google drive
-func NewGDriveFS() *GDriveFS {
-	srv := GetDrive()
-	fs := GDriveFS{FileSystem: pathfs.NewDefaultFileSystem(), srv: srv}
-	fs.fileMap = map[string]*drive.File{}
-	fs.FileSystem.SetDebug(true)
-
-	fs.refresh()
-
-	return &fs
-}
-
-func (fs *GDriveFS) GetXAttr(name string, attribute string, context *fuse.Context) ([]byte, fuse.Status) {
-	return []byte(""), fuse.ENOSYS
-}
-func (fs *GDriveFS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
-	// root
-	if name == "" {
-		return &fuse.Attr{Mode: fuse.S_IFDIR | 0755}, fuse.OK
-	}
-
-	f, ok := fs.fileMap[name]
-	// file not found
-	if !ok { // file not found
-		log.Println("File not found:", name)
-		return nil, fuse.ENOENT
-	}
-	// folder
-	if f.MimeType == "application/vnd.google-apps.folder" {
-		return &fuse.Attr{Mode: fuse.S_IFDIR | 0755}, fuse.OK
-	}
-
-	// Regular file
-	return &fuse.Attr{Mode: fuse.S_IFREG | 0644}, fuse.OK
-
-	//return nil, fuse.ENOENT
-}
-
-func (fs *GDriveFS) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
-	if name == "" { // root
-		res := []fuse.DirEntry{}
-
-		/*r, err := fs.srv.Files.List().Do()
-		if err != nil {
-			log.Println("Unable to retrieve drive Client")
-		}*/
-
-		for k, v := range fs.fileMap {
-			var mode uint32
-			mode = fuse.S_IFREG
-			if v.MimeType == "application/vnd.google-apps.folder" {
-				mode = fuse.S_IFDIR
-			}
-			res = append(res, fuse.DirEntry{Name: k, Mode: mode})
-		}
-		return res, fuse.OK
-	}
-	log.Println("No ent for:", name)
-	return nil, fuse.ENOENT
-
-}
-func (fs *GDriveFS) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
-
-	log.Println("Opening file:", name, " with flags:", flags)
-	f, ok := fs.fileMap[name]
-	if !ok {
-		return nil, fuse.ENOENT
-	}
-
-	var res *http.Response
-	var err error
-
-	switch f.MimeType {
-	case "application/vnd.google-apps.document":
-		res, err = fs.srv.Files.Export(f.Id, "text/plain").Download()
-	case "application/vnd.google-apps.spreadsheet":
-		log.Println("Exporting as: text/csv")
-		res, err = fs.srv.Files.Export(f.Id, "text/csv").Download()
-	default:
-		res, err = fs.srv.Files.Get(f.Id).Download()
-	}
-
-	if err != nil {
-		log.Println("MimeType:", f.MimeType)
-		log.Println("Error from GDrive API", err)
-		return nil, fuse.EPERM
-	}
-	defer res.Body.Close()
-
-	localFile, err := ioutil.TempFile(os.TempDir(), "gdfs")
-	if err != nil {
-		log.Println("Error creating temp file")
-		return nil, fuse.ENOSYS
-	}
-	io.Copy(localFile, res.Body)
-
-	localFile.Seek(0, io.SeekStart)
-
-	//localFile.Close()
-	//data, err := ioutil.ReadFile(localFile.Name())
-	// return nodefs.NewDataFile(data), fuse.OK
-	return nodefs.NewLoopbackFile(localFile), fuse.OK
-
-}