203 lines
4.6 KiB
Go
203 lines
4.6 KiB
Go
// Copyright 2015 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed 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.
|
|
|
|
package roloopbackfs
|
|
|
|
import (
|
|
"golang.org/x/net/context"
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/jacobsa/fuse"
|
|
"github.com/jacobsa/fuse/fuseops"
|
|
"github.com/jacobsa/fuse/fuseutil"
|
|
)
|
|
|
|
type readonlyLoopbackFs struct {
|
|
fuseutil.NotImplementedFileSystem
|
|
loopbackPath string
|
|
inodes *sync.Map
|
|
logger *log.Logger
|
|
}
|
|
|
|
var _ fuseutil.FileSystem = &readonlyLoopbackFs{}
|
|
|
|
// Create a file system that mirrors an existing physical path, in a readonly mode
|
|
|
|
func NewReadonlyLoopbackServer(loopbackPath string, logger *log.Logger) (server fuse.Server, err error) {
|
|
|
|
if _, err = os.Stat(loopbackPath); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
inodes := &sync.Map{}
|
|
root := &inodeEntry{
|
|
id: fuseops.RootInodeID,
|
|
path: loopbackPath,
|
|
}
|
|
inodes.Store(root.Id(), root)
|
|
server = fuseutil.NewFileSystemServer(&readonlyLoopbackFs{
|
|
loopbackPath: loopbackPath,
|
|
inodes: inodes,
|
|
logger: logger,
|
|
})
|
|
return
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) StatFS(
|
|
ctx context.Context,
|
|
op *fuseops.StatFSOp) error {
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) LookUpInode(
|
|
ctx context.Context,
|
|
op *fuseops.LookUpInodeOp) error {
|
|
entry, err := getOrCreateInode(fs.inodes, op.Parent, op.Name)
|
|
if err != nil {
|
|
fs.logger.Printf("fs.LookUpInode for '%v' on '%v': %v", entry, op.Name, err)
|
|
return fuse.EIO
|
|
}
|
|
if entry == nil {
|
|
return fuse.ENOENT
|
|
}
|
|
outputEntry := &op.Entry
|
|
outputEntry.Child = entry.Id()
|
|
attributes, err := entry.Attributes()
|
|
if err != nil {
|
|
fs.logger.Printf("fs.LookUpInode.Attributes for '%v' on '%v': %v", entry, op.Name, err)
|
|
return fuse.EIO
|
|
}
|
|
outputEntry.Attributes = *attributes
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) GetInodeAttributes(
|
|
ctx context.Context,
|
|
op *fuseops.GetInodeAttributesOp) error {
|
|
var entry, found = fs.inodes.Load(op.Inode)
|
|
if !found {
|
|
return fuse.ENOENT
|
|
}
|
|
attributes, err := entry.(Inode).Attributes()
|
|
if err != nil {
|
|
fs.logger.Printf("fs.GetInodeAttributes for '%v': %v", entry, err)
|
|
return fuse.EIO
|
|
}
|
|
op.Attributes = *attributes
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) OpenDir(
|
|
ctx context.Context,
|
|
op *fuseops.OpenDirOp) error {
|
|
// Allow opening any directory.
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) ReadDir(
|
|
ctx context.Context,
|
|
op *fuseops.ReadDirOp) error {
|
|
var entry, found = fs.inodes.Load(op.Inode)
|
|
if !found {
|
|
return fuse.ENOENT
|
|
}
|
|
children, err := entry.(Inode).ListChildren(fs.inodes)
|
|
if err != nil {
|
|
fs.logger.Printf("fs.ReadDir for '%v': %v", entry, err)
|
|
return fuse.EIO
|
|
}
|
|
|
|
if op.Offset > fuseops.DirOffset(len(children)) {
|
|
return fuse.EIO
|
|
}
|
|
|
|
children = children[op.Offset:]
|
|
|
|
for _, child := range children {
|
|
bytesWritten := fuseutil.WriteDirent(op.Dst[op.BytesRead:], *child)
|
|
if bytesWritten == 0 {
|
|
break
|
|
}
|
|
op.BytesRead += bytesWritten
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) OpenFile(
|
|
ctx context.Context,
|
|
op *fuseops.OpenFileOp) error {
|
|
// Allow opening any file.
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) ReadFile(
|
|
ctx context.Context,
|
|
op *fuseops.ReadFileOp) error {
|
|
var entry, found = fs.inodes.Load(op.Inode)
|
|
if !found {
|
|
return fuse.ENOENT
|
|
}
|
|
contents, err := entry.(Inode).Contents()
|
|
if err != nil {
|
|
fs.logger.Printf("fs.ReadFile for '%v': %v", entry, err)
|
|
return fuse.EIO
|
|
}
|
|
|
|
if op.Offset > int64(len(contents)) {
|
|
return fuse.EIO
|
|
}
|
|
|
|
contents = contents[op.Offset:]
|
|
op.BytesRead = copy(op.Dst, contents)
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) ReleaseDirHandle(
|
|
ctx context.Context,
|
|
op *fuseops.ReleaseDirHandleOp) error {
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) GetXattr(
|
|
ctx context.Context,
|
|
op *fuseops.GetXattrOp) error {
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) ListXattr(
|
|
ctx context.Context,
|
|
op *fuseops.ListXattrOp) error {
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) ForgetInode(
|
|
ctx context.Context,
|
|
op *fuseops.ForgetInodeOp) error {
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) ReleaseFileHandle(
|
|
ctx context.Context,
|
|
op *fuseops.ReleaseFileHandleOp) error {
|
|
return nil
|
|
}
|
|
|
|
func (fs *readonlyLoopbackFs) FlushFile(
|
|
ctx context.Context,
|
|
op *fuseops.FlushFileOp) error {
|
|
return nil
|
|
}
|