| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| // place for data-side helper functions |
| |
| package grpcfs |
| |
| import ( |
| "context" |
| "fmt" |
| "grpcfs/pb" |
| "log" |
| "os" |
| "path/filepath" |
| "sync" |
| "sync/atomic" |
| |
| "github.com/jacobsa/fuse/fuseops" |
| "github.com/jacobsa/fuse/fuseutil" |
| ) |
| |
| var ( |
| uid = uint32(os.Getuid()) |
| gid = uint32(os.Getgid()) |
| allocatedInodeId uint64 = fuseops.RootInodeID |
| ) |
| |
| type Inode interface { |
| Id() fuseops.InodeID |
| Path() string |
| String() string |
| Attributes() (*fuseops.InodeAttributes, error) |
| ListChildren(inodes *sync.Map) ([]*fuseutil.Dirent, error) |
| Contents() ([]byte, error) |
| } |
| |
| func getOrCreateInode(inodes *sync.Map, fsClient pb.FuseServiceClient, ctx context.Context, parentId fuseops.InodeID, name string) (Inode, error) { |
| log.Print("inode.getOrCreateInode - called. ", name) |
| parent, found := inodes.Load(parentId) |
| if !found { |
| log.Print("inode.getOrCreateInode - no parent Inode: ", parentId) |
| return nil, nil |
| } |
| parentPath := parent.(Inode).Path() |
| path := filepath.Join(parentPath, name) |
| log.Print("inode.getOrCreateInode - resolved path: ", path) |
| |
| fileInfo, err := getStat(fsClient, ctx, path) |
| if err != nil { |
| log.Print("inode.getOrCreateInode - no path stats: ", path) |
| return nil, err |
| } |
| log.Print("inode.getOrCreateInode - got file stats: ", path, fileInfo) |
| // stat, _ := fileInfo.Sys().(*Sys) |
| |
| entry, _ := NewInode(path, fsClient) |
| // entry := &inodeEntry{ |
| // id: fuseops.InodeID(stat.Ino), |
| // path: path, |
| // client: fsClient, |
| // } |
| storedEntry, _ := inodes.LoadOrStore(entry.Id(), entry) |
| return storedEntry.(Inode), nil |
| } |
| |
| func nextInodeID() (next fuseops.InodeID) { |
| nextInodeId := atomic.AddUint64(&allocatedInodeId, 1) |
| return fuseops.InodeID(nextInodeId) |
| } |
| |
| type inodeEntry struct { |
| id fuseops.InodeID |
| path string |
| client pb.FuseServiceClient |
| } |
| |
| func NewInode(path string, client pb.FuseServiceClient) (Inode, error) { |
| return &inodeEntry{ |
| id: nextInodeID(), |
| path: path, |
| client: client, |
| }, nil |
| } |
| |
| func (in *inodeEntry) Id() fuseops.InodeID { |
| return in.id |
| } |
| |
| func (in *inodeEntry) Path() string { |
| return in.path |
| } |
| |
| func (in *inodeEntry) String() string { |
| return fmt.Sprintf("%v::%v", in.id, in.path) |
| } |
| |
| func (in *inodeEntry) Attributes() (*fuseops.InodeAttributes, error) { |
| log.Print("inodeEntry.Attributes - called. ", in.path) |
| fileInfo, err := getStat(in.client, context.TODO(), in.path) |
| if err != nil { |
| return &fuseops.InodeAttributes{}, err |
| } |
| |
| return &fuseops.InodeAttributes{ |
| Size: uint64(fileInfo.Size()), |
| Nlink: 1, |
| Mode: fileInfo.Mode(), |
| Mtime: fileInfo.ModTime(), |
| Uid: uid, |
| Gid: gid, |
| }, nil |
| } |
| |
| func (in *inodeEntry) ListChildren(inodes *sync.Map) ([]*fuseutil.Dirent, error) { |
| log.Print("inodeEntry.ListChildren - called. ", in.path) |
| children, err := readDir(in.client, context.TODO(), in.path) |
| if err != nil { |
| log.Print("inodeEntry.ListChildren - error in readDir. ", in.path) |
| return nil, err |
| } |
| dirents := []*fuseutil.Dirent{} |
| for i, child := range children { |
| |
| childInode, err := getOrCreateInode(inodes, in.client, context.TODO(), in.id, child.Name()) |
| if err != nil || childInode == nil { |
| continue |
| } |
| |
| var childType fuseutil.DirentType |
| if child.IsDir() { |
| childType = fuseutil.DT_Directory |
| } else if child.Type()&os.ModeSymlink != 0 { |
| childType = fuseutil.DT_Link |
| } else { |
| childType = fuseutil.DT_File |
| } |
| |
| dirents = append(dirents, &fuseutil.Dirent{ |
| Offset: fuseops.DirOffset(i + 1), |
| Inode: childInode.Id(), |
| Name: child.Name(), |
| Type: childType, |
| }) |
| } |
| return dirents, nil |
| } |
| |
| func (in *inodeEntry) Contents() ([]byte, error) { |
| log.Print("inodeEntry.Contents - called. ", in.path) |
| res, err := readFile(in.client, context.TODO(), in.path) |
| return res, err |
| } |