Finished filling out the fuseops package, for #3.

geesefs-0-30-9
Aaron Jacobs 2015-03-24 15:06:33 +11:00
commit d44e63937d
2 changed files with 501 additions and 18 deletions

264
fuseops/convert.go Normal file
View File

@ -0,0 +1,264 @@
// 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 fuseops contains implementations of the fuse.Op interface that may
// be returned by fuse.Connection.ReadOp. See documentation in that package for
// more.
package fuseops
import (
"time"
"github.com/jacobsa/bazilfuse"
"golang.org/x/net/context"
)
// Convert the supplied bazilfuse request struct to an Op, returning nil if it
// is unknown.
//
// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
func Convert(r bazilfuse.Request) (o Op) {
var co *commonOp
switch typed := r.(type) {
case *bazilfuse.InitRequest:
to := &InitOp{}
o = to
co = &to.commonOp
case *bazilfuse.LookupRequest:
to := &LookUpInodeOp{
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
}
o = to
co = &to.commonOp
case *bazilfuse.GetattrRequest:
to := &GetInodeAttributesOp{
Inode: InodeID(typed.Header.Node),
}
o = to
co = &to.commonOp
case *bazilfuse.SetattrRequest:
to := &SetInodeAttributesOp{
Inode: InodeID(typed.Header.Node),
}
o = to
co = &to.commonOp
case *bazilfuse.MkdirRequest:
to := &MkDirOp{
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
Mode: typed.Mode,
}
o = to
co = &to.commonOp
case *bazilfuse.CreateRequest:
to := &CreateFileOp{
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
Mode: typed.Mode,
Flags: typed.Flags,
}
o = to
co = &to.commonOp
case *bazilfuse.RemoveRequest:
if typed.Dir {
to := &RmDirOp{
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
}
o = to
co = &to.commonOp
} else {
to := &UnlinkOp{
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
}
o = to
co = &to.commonOp
}
case *bazilfuse.OpenRequest:
if typed.Dir {
to := &OpenDirOp{
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
}
o = to
co = &to.commonOp
} else {
to := &OpenFileOp{
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
}
o = to
co = &to.commonOp
}
case *bazilfuse.ReadRequest:
if typed.Dir {
to := &ReadDirOp{
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
Offset: DirOffset(typed.Offset),
Size: typed.Size,
}
o = to
co = &to.commonOp
} else {
to := &ReadFileOp{
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
Offset: typed.Offset,
Size: typed.Size,
}
o = to
co = &to.commonOp
}
case *bazilfuse.ReleaseRequest:
if typed.Dir {
to := &ReleaseDirHandleOp{
Handle: HandleID(typed.Handle),
}
o = to
co = &to.commonOp
} else {
to := &ReadFileOp{
Handle: HandleID(typed.Handle),
}
o = to
co = &to.commonOp
}
case *bazilfuse.WriteRequest:
to := &WriteFileOp{
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
Data: typed.Data,
Offset: typed.Offset,
}
o = to
co = &to.commonOp
case *bazilfuse.FsyncRequest:
// We don't currently support this for directories.
if typed.Dir {
return
}
to := &SyncFileOp{
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
}
o = to
co = &to.commonOp
case *bazilfuse.FlushRequest:
to := &FlushFileOp{
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
}
o = to
co = &to.commonOp
default:
return
}
co.init(r)
return
}
func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
return bazilfuse.Attr{
Inode: uint64(inode),
Size: attr.Size,
Mode: attr.Mode,
Nlink: uint32(attr.Nlink),
Atime: attr.Atime,
Mtime: attr.Mtime,
Ctime: attr.Ctime,
Crtime: attr.Crtime,
Uid: attr.Uid,
Gid: attr.Gid,
}
}
// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
// package converts time.Duration values to this form in a straightforward
// way (cf. http://goo.gl/FJhV8j).
//
// So negative durations are right out. There is no need to cap the positive
// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
// time.Duration.
d = t.Sub(time.Now())
if d < 0 {
d = 0
}
return
}
func convertChildInodeEntry(
in *ChildInodeEntry,
out *bazilfuse.LookupResponse) {
out.Node = bazilfuse.NodeID(in.Child)
out.Generation = uint64(in.Generation)
out.Attr = convertAttributes(in.Child, in.Attributes)
out.AttrValid = convertExpirationTime(in.AttributesExpiration)
out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
// A helper for embedding common behavior.
type commonOp struct {
ctx context.Context
r bazilfuse.Request
}
func (o *commonOp) init(r bazilfuse.Request) {
o.ctx = context.Background()
o.r = r
}
func (o *commonOp) Header() OpHeader {
bh := o.r.Hdr()
return OpHeader{
Uid: bh.Uid,
Gid: bh.Gid,
}
}
func (o *commonOp) Context() context.Context {
return o.ctx
}
func (o *commonOp) respondErr(err error) {
if err != nil {
panic("Expect non-nil here.")
}
o.r.RespondError(err)
}

View File

@ -22,12 +22,38 @@ import (
"time"
"github.com/jacobsa/bazilfuse"
"golang.org/x/net/context"
)
type Op interface {
// Return the fields common to all operations.
Header() OpHeader
// A context that can be used for long-running operations.
Context() context.Context
// Repond to the operation with the supplied error. If there is no error, set
// any necessary output fields and then call Respond(nil).
Respond(error)
}
////////////////////////////////////////////////////////////////////////
// Setup
////////////////////////////////////////////////////////////////////////
// Sent once when mounting the file system. It must succeed in order for the
// mount to succeed.
type InitOp struct {
Header OpHeader
commonOp
}
func (o *InitOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.InitRequest).Respond(&bazilfuse.InitResponse{})
}
////////////////////////////////////////////////////////////////////////
@ -37,7 +63,7 @@ type InitOp struct {
// Look up a child by name within a parent directory. The kernel sends this
// when resolving user paths to dentry structs, which are then cached.
type LookUpInodeOp struct {
Header OpHeader
commonOp
// The ID of the directory inode to which the child belongs.
Parent InodeID
@ -57,12 +83,23 @@ type LookUpInodeOp struct {
Entry ChildInodeEntry
}
func (o *LookUpInodeOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.LookupResponse{}
convertChildInodeEntry(&o.Entry, &resp)
o.r.(*bazilfuse.LookupRequest).Respond(&resp)
}
// Refresh the attributes for an inode whose ID was previously returned in a
// LookUpInodeOp. The kernel sends this when the FUSE VFS layer's cache of
// inode attributes is stale. This is controlled by the AttributesExpiration
// field of ChildInodeEntry, etc.
type GetInodeAttributesOp struct {
Header OpHeader
commonOp
// The inode of interest.
Inode InodeID
@ -74,12 +111,26 @@ type GetInodeAttributesOp struct {
AttributesExpiration time.Time
}
func (o *GetInodeAttributesOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.GetattrResponse{
Attr: convertAttributes(o.Inode, o.Attributes),
AttrValid: convertExpirationTime(o.AttributesExpiration),
}
o.r.(*bazilfuse.GetattrRequest).Respond(&resp)
}
// Change attributes for an inode.
//
// The kernel sends this for obvious cases like chmod(2), and for less obvious
// cases like ftrunctate(2).
type SetInodeAttributesOp struct {
Header OpHeader
commonOp
// The inode of interest.
Inode InodeID
@ -97,10 +148,24 @@ type SetInodeAttributesOp struct {
AttributesExpiration time.Time
}
func (o *SetInodeAttributesOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.SetattrResponse{
Attr: convertAttributes(o.Inode, o.Attributes),
AttrValid: convertExpirationTime(o.AttributesExpiration),
}
o.r.(*bazilfuse.SetattrRequest).Respond(&resp)
}
// Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The
// kernel sends this when removing an inode from its internal caches.
type ForgetInodeOp struct {
Header OpHeader
commonOp
// The inode to be forgotten. The kernel guarantees that the node ID will not
// be used in further calls to the file system (unless it is reissued by the
@ -108,6 +173,15 @@ type ForgetInodeOp struct {
ID InodeID
}
func (o *ForgetInodeOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.ForgetRequest).Respond()
}
////////////////////////////////////////////////////////////////////////
// Inode creation
////////////////////////////////////////////////////////////////////////
@ -120,7 +194,7 @@ type ForgetInodeOp struct {
// http://goo.gl/FZpLu5). But volatile file systems and paranoid non-volatile
// file systems should check for the reasons described below on CreateFile.
type MkDirOp struct {
Header OpHeader
commonOp
// The ID of parent directory inode within which to create the child.
Parent InodeID
@ -133,6 +207,16 @@ type MkDirOp struct {
Entry ChildInodeEntry
}
func (o *MkDirOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.MkdirResponse{}
o.r.(*bazilfuse.MkdirRequest).Respond(&resp)
}
// Create a file inode and open it.
//
// The kernel sends this when the user asks to open a file with the O_CREAT
@ -147,7 +231,7 @@ type MkDirOp struct {
// course particularly applies to file systems that are volatile from the
// kernel's point of view.
type CreateFileOp struct {
Header OpHeader
commonOp
// The ID of parent directory inode within which to create the child file.
Parent InodeID
@ -173,6 +257,22 @@ type CreateFileOp struct {
Handle HandleID
}
func (o *CreateFileOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.CreateResponse{
OpenResponse: bazilfuse.OpenResponse{
Handle: bazilfuse.HandleID(o.Handle),
},
}
convertChildInodeEntry(&o.Entry, &resp.LookupResponse)
o.r.(*bazilfuse.CreateRequest).Respond(&resp)
}
////////////////////////////////////////////////////////////////////////
// Unlinking
////////////////////////////////////////////////////////////////////////
@ -185,7 +285,7 @@ type CreateFileOp struct {
//
// Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf)
type RmDirOp struct {
Header OpHeader
commonOp
// The ID of parent directory inode, and the name of the directory being
// removed within it.
@ -193,13 +293,22 @@ type RmDirOp struct {
Name string
}
func (o *RmDirOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.RemoveRequest).Respond()
}
// Unlink a file from its parent. If this brings the inode's link count to
// zero, the inode should be deleted once the kernel sends ForgetInodeOp. It
// may still be referenced before then if a user still has the file open.
//
// Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C)
type UnlinkOp struct {
Header OpHeader
commonOp
// The ID of parent directory inode, and the name of the file being removed
// within it.
@ -207,6 +316,15 @@ type UnlinkOp struct {
Name string
}
func (o *UnlinkOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.RemoveRequest).Respond()
}
////////////////////////////////////////////////////////////////////////
// Directory handles
////////////////////////////////////////////////////////////////////////
@ -218,7 +336,7 @@ type UnlinkOp struct {
// user-space process. On OS X it may not be sent for every open(2) (cf.
// https://github.com/osxfuse/osxfuse/issues/199).
type OpenDirOp struct {
Header OpHeader
commonOp
// The ID of the inode to be opened.
Inode InodeID
@ -237,9 +355,22 @@ type OpenDirOp struct {
Handle HandleID
}
func (o *OpenDirOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.OpenResponse{
Handle: bazilfuse.HandleID(o.Handle),
}
o.r.(*bazilfuse.OpenRequest).Respond(&resp)
}
// Read entries from a directory previously opened with OpenDir.
type ReadDirOp struct {
Header OpHeader
commonOp
// The directory inode that we are reading, and the handle previously
// returned by OpenDir when opening that inode.
@ -327,6 +458,19 @@ type ReadDirOp struct {
Data []byte
}
func (o *ReadDirOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.ReadResponse{
Data: o.Data,
}
o.r.(*bazilfuse.ReadRequest).Respond(&resp)
}
// Release a previously-minted directory handle. The kernel sends this when
// there are no more references to an open directory: all file descriptors are
// closed and all memory mappings are unmapped.
@ -334,7 +478,7 @@ type ReadDirOp struct {
// The kernel guarantees that the handle ID will not be used in further ops
// sent to the file system (unless it is reissued by the file system).
type ReleaseDirHandleOp struct {
Header OpHeader
commonOp
// The handle ID to be released. The kernel guarantees that this ID will not
// be used in further calls to the file system (unless it is reissued by the
@ -342,6 +486,15 @@ type ReleaseDirHandleOp struct {
Handle HandleID
}
func (o *ReleaseDirHandleOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.ReleaseRequest).Respond()
}
////////////////////////////////////////////////////////////////////////
// File handles
////////////////////////////////////////////////////////////////////////
@ -353,7 +506,7 @@ type ReleaseDirHandleOp struct {
// process. On OS X it may not be sent for every open(2)
// (cf.https://github.com/osxfuse/osxfuse/issues/199).
type OpenFileOp struct {
Header OpHeader
commonOp
// The ID of the inode to be opened.
Inode InodeID
@ -371,13 +524,26 @@ type OpenFileOp struct {
Handle HandleID
}
func (o *OpenFileOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.OpenResponse{
Handle: bazilfuse.HandleID(o.Handle),
}
o.r.(*bazilfuse.OpenRequest).Respond(&resp)
}
// Read data from a file previously opened with CreateFile or OpenFile.
//
// Note that this op is not sent for every call to read(2) by the end user;
// some reads may be served by the page cache. See notes on WriteFileOp for
// more.
type ReadFileOp struct {
Header OpHeader
commonOp
// The file inode that we are reading, and the handle previously returned by
// CreateFile or OpenFile when opening that inode.
@ -400,6 +566,19 @@ type ReadFileOp struct {
Data []byte
}
func (o *ReadFileOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.ReadResponse{
Data: o.Data,
}
o.r.(*bazilfuse.ReadRequest).Respond(&resp)
}
// Write data to a file previously opened with CreateFile or OpenFile.
//
// When the user writes data using write(2), the write goes into the page
@ -429,7 +608,7 @@ type ReadFileOp struct {
// flush request.
//
type WriteFileOp struct {
Header OpHeader
commonOp
// The file inode that we are modifying, and the handle previously returned
// by CreateFile or OpenFile when opening that inode.
@ -467,6 +646,19 @@ type WriteFileOp struct {
Data []byte
}
func (o *WriteFileOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
resp := bazilfuse.WriteResponse{
Size: len(o.Data),
}
o.r.(*bazilfuse.WriteRequest).Respond(&resp)
}
// Synchronize the current contents of an open file to storage.
//
// vfs.txt documents this as being called for by the fsync(2) system call
@ -484,13 +676,22 @@ type WriteFileOp struct {
// See also: FlushFileOp, which may perform a similar function when closing a
// file (but which is not used in "real" file systems).
type SyncFileOp struct {
Header OpHeader
commonOp
// The file and handle being sync'd.
Inode InodeID
Handle HandleID
}
func (o *SyncFileOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.FsyncRequest).Respond()
}
// Flush the current state of an open file to storage upon closing a file
// descriptor.
//
@ -539,13 +740,22 @@ type SyncFileOp struct {
// to at least schedule a real flush, and maybe do it immediately in order to
// return any errors that occur.
type FlushFileOp struct {
Header OpHeader
commonOp
// The file and handle being flushed.
Inode InodeID
Handle HandleID
}
func (o *FlushFileOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.FlushRequest).Respond()
}
// Release a previously-minted file handle. The kernel calls this when there
// are no more references to an open file: all file descriptors are closed
// and all memory mappings are unmapped.
@ -553,10 +763,19 @@ type FlushFileOp struct {
// The kernel guarantees that the handle ID will not be used in further calls
// to the file system (unless it is reissued by the file system).
type ReleaseFileHandleOp struct {
Header OpHeader
commonOp
// The handle ID to be released. The kernel guarantees that this ID will not
// be used in further calls to the file system (unless it is reissued by the
// file system).
Handle HandleID
}
func (o *ReleaseFileHandleOp) Respond(err error) {
if err != nil {
o.commonOp.respondErr(err)
return
}
o.r.(*bazilfuse.ReleaseRequest).Respond()
}