diff --git a/glide.lock b/glide.lock index d672abaf6..8883c9c5a 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 68a51bafc057773f62a8e605407c8c42a225ac3dc913cdb235aa0f5708ea380c -updated: 2019-11-12T09:46:52.613515-08:00 +hash: de1d84db7b83aa397b566cac8696d4bee1c2dea8702392d50c06d94f4ef47e37 +updated: 2019-12-26T16:09:32.457359-08:00 imports: - name: bazil.org/fuse version: 371fbbdaa8987b715bdd21d6adc4c9b20155f748 @@ -48,6 +48,8 @@ imports: - assert - name: github.com/swiftstack/cstruct version: 5f7d9b06c7662151d3a3c20cecf0ed5eaae3582f +- name: github.com/swiftstack/fission + version: 20b89d29bbc578a4e1de90c5a8611a2ba1491a94 - name: github.com/swiftstack/sortedmap version: 6f9ef66702b23f47054e69be97a26feb6e7301b1 - name: go.etcd.io/etcd diff --git a/glide.yaml b/glide.yaml index 31b111b73..c2dadf04a 100644 --- a/glide.yaml +++ b/glide.yaml @@ -4,6 +4,8 @@ import: version: 1.1.0 - package: github.com/swiftstack/sortedmap version: 1.6.1 +- package: github.com/swiftstack/fission + version: 1.0.3 - package: bazil.org/fuse version: 371fbbdaa8987b715bdd21d6adc4c9b20155f748 subpackages: diff --git a/jrpcfs/api.go b/jrpcfs/api.go index de43d01ce..c785a32b3 100644 --- a/jrpcfs/api.go +++ b/jrpcfs/api.go @@ -345,6 +345,26 @@ type LookupRequest struct { Basename string } +// LookupPlusRequest is the request object for RpcLookupPlus. +type LookupPlusRequest struct { + InodeHandle + Basename string +} + +// LookupPlusReply is the reply object for RpcLookupPlus. +type LookupPlusReply struct { + InodeNumber int64 + StatStruct +} + +// AccessRequest is the request object for RpcAccess. +type AccessRequest struct { + InodeHandle + UserID int32 + GroupID int32 + AccessMode uint32 +} + // MkdirRequest is the request object for RpcMkdir. type MkdirRequest struct { InodeHandle diff --git a/jrpcfs/filesystem.go b/jrpcfs/filesystem.go index ceefd72d1..dff46d0c3 100644 --- a/jrpcfs/filesystem.go +++ b/jrpcfs/filesystem.go @@ -1338,6 +1338,58 @@ func (s *Server) RpcLookup(in *LookupRequest, reply *InodeReply) (err error) { return } +func (s *Server) RpcLookupPlus(in *LookupPlusRequest, reply *LookupPlusReply) (err error) { + enterGate() + defer leaveGate() + + flog := logger.TraceEnter("in.", in) + defer func() { flog.TraceExitErr("reply.", err, reply) }() + defer func() { rpcEncodeError(&err) }() // Encode error for return by RPC + + mountHandle, err := lookupMountHandleByMountIDAsString(in.MountID) + if nil != err { + return + } + + ino, err := mountHandle.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.InodeNumber(in.InodeNumber), in.Basename) + if nil != err { + return + } + + stat, err := mountHandle.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, ino) + if nil != err { + return + } + + reply.InodeNumber = int64(uint64(ino)) + reply.StatStruct.fsStatToStatStruct(stat) + + return +} + +func (s *Server) RpcAccess(in *AccessRequest, reply *InodeReply) (err error) { + enterGate() + defer leaveGate() + + flog := logger.TraceEnter("in.", in) + defer func() { flog.TraceExitErr("reply.", err, reply) }() + defer func() { rpcEncodeError(&err) }() // Encode error for return by RPC + + mountHandle, err := lookupMountHandleByMountIDAsString(in.MountID) + if nil != err { + return + } + + ok := mountHandle.Access(inode.InodeUserID(in.UserID), inode.InodeGroupID(in.GroupID), nil, inode.InodeNumber(in.InodeNumber), inode.InodeMode(in.AccessMode)) + if ok { + err = nil + } else { + err = blunder.NewError(blunder.PermDeniedError, "EACCES") + } + + return +} + func (s *Server) RpcMkdir(in *MkdirRequest, reply *InodeReply) (err error) { enterGate() defer leaveGate() @@ -1929,7 +1981,7 @@ func (s *Server) RpcStatVFS(in *StatVFSRequest, reply *StatVFS) (err error) { return } -func (s *Server) RpcSymlink(in *SymlinkRequest, reply *Reply) (err error) { +func (s *Server) RpcSymlink(in *SymlinkRequest, reply *InodeReply) (err error) { enterGate() defer leaveGate() @@ -1942,7 +1994,8 @@ func (s *Server) RpcSymlink(in *SymlinkRequest, reply *Reply) (err error) { return } - _, err = mountHandle.Symlink(inode.InodeUserID(in.UserID), inode.InodeGroupID(in.GroupID), nil, inode.InodeNumber(in.InodeNumber), in.Basename, in.Target) + ino, err := mountHandle.Symlink(inode.InodeUserID(in.UserID), inode.InodeGroupID(in.GroupID), nil, inode.InodeNumber(in.InodeNumber), in.Basename, in.Target) + reply.InodeNumber = int64(uint64(ino)) return } diff --git a/pfs_middleware/pfs_middleware/rpc.py b/pfs_middleware/pfs_middleware/rpc.py index d4e731b38..feee9d934 100644 --- a/pfs_middleware/pfs_middleware/rpc.py +++ b/pfs_middleware/pfs_middleware/rpc.py @@ -24,6 +24,7 @@ import uuid allow_read_only = { + "Server.RpcAccess", "Server.RpcFetchExtentMapChunk", "Server.RpcGetAccount", "Server.RpcGetContainer", @@ -32,6 +33,7 @@ "Server.RpcGetXAttr", "Server.RpcListXAttr", "Server.RpcLookup", + "Server.RpcLookupPlus", "Server.RpcMountByAccountName", "Server.RpcMountByVolumeName", "Server.RpcPing", @@ -45,6 +47,7 @@ } allow_read_write = { + "Server.RpcAccess", "Server.RpcChmod", "Server.RpcChown", "Server.RpcCreate", @@ -61,6 +64,7 @@ "Server.RpcListXAttr", "Server.RpcLog", "Server.RpcLookup", + "Server.RpcLookupPlus", "Server.RpcMiddlewareMkdir", "Server.RpcMiddlewarePost", "Server.RpcMkdir", diff --git a/pfsagentd/README.md b/pfsagentd/README.md index 1f5ec9623..9e860d06c 100644 --- a/pfsagentd/README.md +++ b/pfsagentd/README.md @@ -52,16 +52,20 @@ ExclusiveFileLimit: 100 DirtyFileLimit: 50 MaxFlushSize: 10485760 MaxFlushTime: 10s -ReadOnly: false LogFilePath: # Unless starting with '/', relative to $CWD; Blank to disable LogToConsole: true TraceEnabled: false HTTPServerIPAddr: 127.0.0.1 HTTPServerTCPPort: 9090 +ReadDirPlusEnabled: false +XAttrEnabled: false +EntryDuration: 10s AttrDuration: 10s AttrBlockSize: 65536 -LookupEntryDuration: 10s ReaddirMaxEntries: 1024 +FUSEMaxBackground: 100 +FUSECongestionThreshhold: 0 +FUSEMaxWrite: 131072 # Linux max is 128KiB ``` In the above example, some important fields are as follows: diff --git a/pfsagentd/io.go b/pfsagentd/file_inode.go similarity index 87% rename from pfsagentd/io.go rename to pfsagentd/file_inode.go index b5fef3d1b..d170b99ba 100644 --- a/pfsagentd/io.go +++ b/pfsagentd/file_inode.go @@ -11,235 +11,48 @@ import ( "sync/atomic" "time" - "bazil.org/fuse" + "github.com/swiftstack/sortedmap" "github.com/swiftstack/ProxyFS/inode" "github.com/swiftstack/ProxyFS/jrpcfs" - "github.com/swiftstack/sortedmap" ) -func handleReadRequestFileInodeCase(request *fuse.ReadRequest) { +// doFlushIfNecessary (the non-receiver form) is currently necessary due to the lack +// of Lease Management whereby an implicitly deleted fileInode (due to a DoUnlink() +// or DoRename/DoRename2() call or equivalent somewhere) would revoke such Lease +// causing any in-flight LogSegment PUTs to be flushed first. In the meantime, this +// func will do the flush if necessary based on what *this* PFSAgent instance is +// doing. +// +func doFlushIfNecessary(dirInodeNumber inode.InodeNumber, name []byte) { var ( - curObjectOffset uint64 - err error - fileInode *fileInodeStruct - grantedLock *fileInodeLockRequestStruct - logSegmentCacheElement *logSegmentCacheElementStruct - logSegmentCacheElementBufEndingPosition uint64 - logSegmentCacheElementBufRemainingLen uint64 - logSegmentCacheElementBufSelectedLen uint64 - logSegmentCacheElementBufStartingPosition uint64 - readPlan []interface{} - readPlanSpan uint64 - readPlanStepAsInterface interface{} - readPlanStepAsMultiObjectExtentStruct *multiObjectExtentStruct - readPlanStepAsSingleObjectExtentWithLink *singleObjectExtentWithLinkStruct - readPlanStepRemainingLength uint64 - response *fuse.ReadResponse + err error + fileInode *fileInodeStruct + lookupReply *jrpcfs.InodeReply + lookupRequest *jrpcfs.LookupRequest ) - _ = atomic.AddUint64(&globals.metrics.FUSE_ReadRequestFileInodeCase_calls, 1) - - fileInode = referenceFileInode(inode.InodeNumber(request.Header.Node)) - defer fileInode.dereference() + lookupRequest = &jrpcfs.LookupRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(dirInodeNumber), + }, + Basename: string(name[:]), + } - grantedLock = fileInode.getSharedLock() - defer grantedLock.release() + lookupReply = &jrpcfs.InodeReply{} - err = fileInode.populateExtentMap(uint64(request.Offset), uint64(request.Size)) + err = doJRPCRequest("Server.RpcLookup", lookupRequest, lookupReply) if nil != err { - request.RespondError(fuse.EIO) + // Assume the fileInode simply did not exist, so just return return } - readPlan, readPlanSpan = fileInode.getReadPlan(uint64(request.Offset), uint64(request.Size)) - - if (nil == readPlan) || (0 == readPlanSpan) { - response = &fuse.ReadResponse{ - Data: make([]byte, 0), - } - } else { - response = &fuse.ReadResponse{ - Data: make([]byte, 0, readPlanSpan), - } - - for _, readPlanStepAsInterface = range readPlan { - switch readPlanStepAsInterface.(type) { - case *multiObjectExtentStruct: - readPlanStepAsMultiObjectExtentStruct = readPlanStepAsInterface.(*multiObjectExtentStruct) - - if "" == readPlanStepAsMultiObjectExtentStruct.objectName { - // Zero-fill for readPlanStep.length - - response.Data = append(response.Data, make([]byte, readPlanStepAsMultiObjectExtentStruct.length)...) - } else { - // Fetch LogSegment data... possibly crossing LogSegmentCacheLine boundaries - - curObjectOffset = readPlanStepAsMultiObjectExtentStruct.objectOffset - readPlanStepRemainingLength = readPlanStepAsMultiObjectExtentStruct.length - - for readPlanStepRemainingLength > 0 { - logSegmentCacheElement = fetchLogSegmentCacheLine(readPlanStepAsMultiObjectExtentStruct.containerName, readPlanStepAsMultiObjectExtentStruct.objectName, curObjectOffset) - - if logSegmentCacheElementStateGetFailed == logSegmentCacheElement.state { - request.RespondError(fuse.EIO) - return - } - - logSegmentCacheElementBufStartingPosition = curObjectOffset - logSegmentCacheElement.startingOffset - logSegmentCacheElementBufRemainingLen = uint64(len(logSegmentCacheElement.buf)) - logSegmentCacheElementBufStartingPosition - - if logSegmentCacheElementBufRemainingLen <= readPlanStepRemainingLength { - logSegmentCacheElementBufSelectedLen = logSegmentCacheElementBufRemainingLen - } else { - logSegmentCacheElementBufSelectedLen = readPlanStepRemainingLength - } - - logSegmentCacheElementBufEndingPosition = logSegmentCacheElementBufStartingPosition + logSegmentCacheElementBufSelectedLen - - response.Data = append(response.Data, logSegmentCacheElement.buf[logSegmentCacheElementBufStartingPosition:logSegmentCacheElementBufEndingPosition]...) - - curObjectOffset += logSegmentCacheElementBufSelectedLen - readPlanStepRemainingLength -= logSegmentCacheElementBufSelectedLen - } - } - case *singleObjectExtentWithLinkStruct: - readPlanStepAsSingleObjectExtentWithLink = readPlanStepAsInterface.(*singleObjectExtentWithLinkStruct) - - if nil == readPlanStepAsSingleObjectExtentWithLink.chunkedPutContext { - // Zero-fill for readPlanStep.length - - response.Data = append(response.Data, make([]byte, readPlanStepAsSingleObjectExtentWithLink.length)...) - } else { - // Fetch LogSegment data... from readPlanStepAsSingleObjectExtentWithLink.chunkedPutContextStruct - - _ = atomic.AddUint64(&globals.metrics.LogSegmentPUTReadHits, 1) - - response.Data = append(response.Data, readPlanStepAsSingleObjectExtentWithLink.chunkedPutContext.buf[readPlanStepAsSingleObjectExtentWithLink.objectOffset:readPlanStepAsSingleObjectExtentWithLink.objectOffset+readPlanStepAsSingleObjectExtentWithLink.length]...) - } - default: - logFatalf("getReadPlan() returned an invalid readPlanStep: %v", readPlanStepAsInterface) - } - } - } - - _ = atomic.AddUint64(&globals.metrics.FUSE_ReadRequestFileInodeCase_bytes, uint64(len(response.Data))) - - request.Respond(response) -} - -func handleWriteRequest(request *fuse.WriteRequest) { - var ( - chunkedPutContext *chunkedPutContextStruct - chunkedPutContextElement *list.Element - fileInode *fileInodeStruct - grantedLock *fileInodeLockRequestStruct - response *fuse.WriteResponse - sendChanFlushFlag bool - singleObjectExtent *singleObjectExtentStruct - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_WriteRequest_calls, 1) - - fileInode = referenceFileInode(inode.InodeNumber(request.Header.Node)) - grantedLock = fileInode.getExclusiveLock() - - if 0 == fileInode.chunkedPutList.Len() { - // No chunkedPutContext is present (so none can be open), so open one - - _ = atomic.AddUint64(&globals.metrics.LogSegmentPUTs, 1) - - chunkedPutContext = &chunkedPutContextStruct{ - fileSize: fileInode.extentMapFileSize, - buf: make([]byte, 0), - fileInode: fileInode, - state: chunkedPutContextStateOpen, - sendChan: make(chan bool, chunkedPutContextSendChanBufferSize), - wakeChan: make(chan bool, chunkedPutContextWakeChanBufferSize), - flushRequested: false, - } - - chunkedPutContext.extentMap = sortedmap.NewLLRBTree(sortedmap.CompareUint64, chunkedPutContext) - chunkedPutContext.chunkedPutListElement = fileInode.chunkedPutList.PushBack(chunkedPutContext) - - fileInode.reference() - - pruneFileInodeDirtyListIfNecessary() - - globals.Lock() - fileInode.dirtyListElement = globals.fileInodeDirtyList.PushBack(fileInode) - globals.Unlock() - - chunkedPutContext.fileInode.Add(1) - go chunkedPutContext.sendDaemon() - } else { - globals.Lock() - globals.fileInodeDirtyList.MoveToBack(fileInode.dirtyListElement) - globals.Unlock() - - chunkedPutContextElement = fileInode.chunkedPutList.Back() - chunkedPutContext = chunkedPutContextElement.Value.(*chunkedPutContextStruct) - - if chunkedPutContextStateOpen == chunkedPutContext.state { - // Use this most recent (and open) chunkedPutContext - } else { - // Most recent chunkedPutContext is closed, so open a new one - - _ = atomic.AddUint64(&globals.metrics.LogSegmentPUTs, 1) - - chunkedPutContext = &chunkedPutContextStruct{ - fileSize: fileInode.extentMapFileSize, - buf: make([]byte, 0), - fileInode: fileInode, - state: chunkedPutContextStateOpen, - sendChan: make(chan bool, chunkedPutContextSendChanBufferSize), - wakeChan: make(chan bool, chunkedPutContextWakeChanBufferSize), - flushRequested: false, - } - - chunkedPutContext.extentMap = sortedmap.NewLLRBTree(sortedmap.CompareUint64, chunkedPutContext) - chunkedPutContext.chunkedPutListElement = fileInode.chunkedPutList.PushBack(chunkedPutContext) - - fileInode.reference() - - chunkedPutContext.fileInode.Add(1) - go chunkedPutContext.sendDaemon() - } - } - - singleObjectExtent = &singleObjectExtentStruct{ - fileOffset: uint64(request.Offset), - objectOffset: uint64(len(chunkedPutContext.buf)), - length: uint64(len(request.Data)), - } - - chunkedPutContext.mergeSingleObjectExtent(singleObjectExtent) - - if (singleObjectExtent.fileOffset + singleObjectExtent.length) > chunkedPutContext.fileSize { - chunkedPutContext.fileSize = singleObjectExtent.fileOffset + singleObjectExtent.length - } - - chunkedPutContext.buf = append(chunkedPutContext.buf, request.Data...) - - sendChanFlushFlag = (uint64(len(chunkedPutContext.buf)) >= globals.config.MaxFlushSize) - - if sendChanFlushFlag { - chunkedPutContext.state = chunkedPutContextStateClosing - } - - grantedLock.release() + fileInode = referenceFileInode(inode.InodeNumber(lookupReply.InodeNumber)) - chunkedPutContext.sendChan <- sendChanFlushFlag + fileInode.doFlushIfNecessary() fileInode.dereference() - - response = &fuse.WriteResponse{ - Size: len(request.Data), - } - - _ = atomic.AddUint64(&globals.metrics.FUSE_WriteRequest_bytes, uint64(response.Size)) - - request.Respond(response) } func (fileInode *fileInodeStruct) doFlushIfNecessary() { @@ -576,6 +389,11 @@ func (chunkedPutContext *chunkedPutContextStruct) Read(p []byte) (n int, err err grantedLock *fileInodeLockRequestStruct ) + chunkedPutContext.inRead = true + defer func() { + chunkedPutContext.inRead = false + }() + grantedLock = chunkedPutContext.fileInode.getExclusiveLock() n = len(chunkedPutContext.buf) - chunkedPutContext.pos @@ -622,6 +440,16 @@ func (chunkedPutContext *chunkedPutContextStruct) Read(p []byte) (n int, err err } func (chunkedPutContext *chunkedPutContextStruct) Close() (err error) { + // Make sure Read() gets a chance to cleanly exit + + if chunkedPutContext.inRead { + chunkedPutContext.wakeChan <- false + + for chunkedPutContext.inRead { + time.Sleep(chunkedPutContextExitReadPollingRate) + } + } + // To ensure retry resends all the data, reset pos chunkedPutContext.pos = 0 @@ -1814,7 +1642,7 @@ func (chunkedPutContext *chunkedPutContextStruct) mergeSingleObjectExtent(newExt // curExtentIndex left pointing to subsequent extent (if any) for next loop iteration } else { - // curExtent is overlapped "on the left" by newExtent... so tuncate and move curExtent + // curExtent is overlapped "on the left" by newExtent... so truncate and move curExtent postLength = (curExtent.fileOffset + curExtent.length) - (newExtent.fileOffset + newExtent.length) diff --git a/pfsagentd/fission.go b/pfsagentd/fission.go new file mode 100644 index 000000000..583aeaae4 --- /dev/null +++ b/pfsagentd/fission.go @@ -0,0 +1,2201 @@ +package main + +import ( + "container/list" + "fmt" + "log" + "os" + "sync/atomic" + "syscall" + "time" + + "github.com/swiftstack/fission" + "github.com/swiftstack/sortedmap" + + "github.com/swiftstack/ProxyFS/fs" + "github.com/swiftstack/ProxyFS/inode" + "github.com/swiftstack/ProxyFS/jrpcfs" +) + +const ( + initOutFlagsMaskReadDirPlusEnabled = fission.InitFlagsAsyncRead | fission.InitFlagsBigWrites | fission.InitFlagsDontMask | fission.InitFlagsAutoInvalData | fission.InitFlagsDoReadDirPlus + initOutFlagsMaskReadDirPlusDisabled = fission.InitFlagsAsyncRead | fission.InitFlagsBigWrites | fission.InitFlagsDontMask | fission.InitFlagsAutoInvalData +) + +func performMountFUSE() { + var ( + err error + ) + + globals.fissionVolume = fission.NewVolume( + globals.config.FUSEVolumeName, // volumeName string + globals.config.FUSEMountPointPath, // mountpointDirPath string + 0, // mountFlags uintptr + globals.config.FUSEMaxWrite, // initOutMaxWrite uint32 + &globals, // callbacks fission.Callbacks + newLogger(), // logger *log.Logger + globals.fissionErrChan) // errChan chan error + + err = globals.fissionVolume.DoMount() + if nil != err { + log.Fatalf("fissionVolume.DoMount() failed: %v", err) + } +} + +func performUnmountFUSE() { + var ( + err error + ) + + err = globals.fissionVolume.DoUnmount() + if nil != err { + log.Fatalf("fissionVolume.DoUnmount() failed: %v", err) + } +} + +func convertErrToErrno(err error, defaultErrno syscall.Errno) (errno syscall.Errno) { + var ( + convertErr error + possibleErrno syscall.Errno + ) + + _, convertErr = fmt.Sscanf(err.Error(), "errno: %v", &possibleErrno) + if nil == convertErr { + errno = possibleErrno + } else { + errno = defaultErrno + } + + return +} + +func fixAttrSizes(attr *fission.Attr) { + var ( + chunkedPutContext *chunkedPutContextStruct + chunkedPutContextElement *list.Element + fileInode *fileInodeStruct + ok bool + ) + + if syscall.S_IFREG == (attr.Mode & syscall.S_IFMT) { + globals.Lock() + fileInode, ok = globals.fileInodeMap[inode.InodeNumber(attr.Ino)] + if ok { + if 0 < fileInode.chunkedPutList.Len() { + chunkedPutContextElement = fileInode.chunkedPutList.Back() + chunkedPutContext = chunkedPutContextElement.Value.(*chunkedPutContextStruct) + attr.Size = chunkedPutContext.fileSize + } + } + globals.Unlock() + + attr.Blocks = attr.Size + (globals.config.AttrBlockSize - 1) + attr.Blocks /= globals.config.AttrBlockSize + } else { + attr.Size = 0 + attr.Blocks = 0 + } + + attr.BlkSize = uint32(globals.config.AttrBlockSize) +} + +func nsToUnixTime(ns uint64) (sec uint64, nsec uint32) { + sec = ns / 1e9 + nsec = uint32(ns - (sec * 1e9)) + return +} + +func unixTimeToNs(sec uint64, nsec uint32) (ns uint64) { + ns = (sec * 1e9) + uint64(nsec) + return +} + +func (dummy *globalsStruct) DoLookup(inHeader *fission.InHeader, lookupIn *fission.LookupIn) (lookupOut *fission.LookupOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + err error + lookupPlusReply *jrpcfs.LookupPlusReply + lookupPlusRequest *jrpcfs.LookupPlusRequest + mTimeNSec uint32 + mTimeSec uint64 + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoLookup_calls, 1) + + lookupPlusRequest = &jrpcfs.LookupPlusRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + Basename: string(lookupIn.Name[:]), + } + + lookupPlusReply = &jrpcfs.LookupPlusReply{} + + err = doJRPCRequest("Server.RpcLookupPlus", lookupPlusRequest, lookupPlusReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + aTimeSec, aTimeNSec = nsToUnixTime(lookupPlusReply.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(lookupPlusReply.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(lookupPlusReply.CTimeNs) + + lookupOut = &fission.LookupOut{ + EntryOut: fission.EntryOut{ + NodeID: uint64(lookupPlusReply.InodeNumber), + Generation: 0, + EntryValidSec: globals.entryValidSec, + AttrValidSec: globals.attrValidSec, + EntryValidNSec: globals.entryValidNSec, + AttrValidNSec: globals.attrValidNSec, + Attr: fission.Attr{ + Ino: uint64(lookupPlusReply.InodeNumber), + Size: lookupPlusReply.Size, // fixAttrSizes() will correct this if necessary + Blocks: 0, // fixAttrSizes() will compute this + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: lookupPlusReply.FileMode, + NLink: uint32(lookupPlusReply.NumLinks), + UID: lookupPlusReply.UserID, + GID: lookupPlusReply.GroupID, + RDev: 0, + BlkSize: 0, // fixAttrSizes() will set this + Padding: 0, + }, + }, + } + + fixAttrSizes(&lookupOut.EntryOut.Attr) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoForget(inHeader *fission.InHeader, forgetIn *fission.ForgetIn) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoForget_calls, 1) + return +} + +func (dummy *globalsStruct) DoGetAttr(inHeader *fission.InHeader, getAttrIn *fission.GetAttrIn) (getAttrOut *fission.GetAttrOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + chunkedPutContext *chunkedPutContextStruct + chunkedPutContextElement *list.Element + err error + fileInode *fileInodeStruct + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + mTimeNSec uint32 + mTimeSec uint64 + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoGetAttr_calls, 1) + + globals.Lock() + + fileInode, ok = globals.fileInodeMap[inode.InodeNumber(inHeader.NodeID)] + if ok { + if nil == fileInode.cachedStat { + globals.Unlock() + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoGetAttr_cache_misses, 1) + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + globals.Lock() + + if nil == fileInode.cachedStat { + fileInode.cachedStat = getStatReply + } else { + // It's ok to use the cachedStat somebody else set in the meantime + } + } else { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoGetAttr_cache_hits, 1) + } + + if 0 < fileInode.chunkedPutList.Len() { + chunkedPutContextElement = fileInode.chunkedPutList.Back() + chunkedPutContext = chunkedPutContextElement.Value.(*chunkedPutContextStruct) + fileInode.cachedStat.Size = chunkedPutContext.fileSize + } + + aTimeSec, aTimeNSec = nsToUnixTime(fileInode.cachedStat.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(fileInode.cachedStat.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(fileInode.cachedStat.CTimeNs) + + getAttrOut = &fission.GetAttrOut{ + AttrValidSec: globals.attrValidSec, + AttrValidNSec: globals.attrValidNSec, + Dummy: 0, + Attr: fission.Attr{ + Ino: inHeader.NodeID, + Size: fileInode.cachedStat.Size, // fixAttrSizes() will correct this if necessary + Blocks: 0, // fixAttrSizes() will compute this + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: fileInode.cachedStat.FileMode, + NLink: uint32(fileInode.cachedStat.NumLinks), + UID: fileInode.cachedStat.UserID, + GID: fileInode.cachedStat.GroupID, + RDev: 0, + BlkSize: 0, // fixAttrSizes() will set this + Padding: 0, + }, + } + + globals.Unlock() + } else { + globals.Unlock() + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoGetAttr_cache_misses, 1) + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + aTimeSec, aTimeNSec = nsToUnixTime(getStatReply.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(getStatReply.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(getStatReply.CTimeNs) + + getAttrOut = &fission.GetAttrOut{ + AttrValidSec: globals.attrValidSec, + AttrValidNSec: globals.attrValidNSec, + Dummy: 0, + Attr: fission.Attr{ + Ino: inHeader.NodeID, + Size: getStatReply.Size, // fixAttrSizes() will correct this if necessary + Blocks: 0, // fixAttrSizes() will compute this + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: getStatReply.FileMode, + NLink: uint32(getStatReply.NumLinks), + UID: getStatReply.UserID, + GID: getStatReply.GroupID, + RDev: 0, + BlkSize: 0, // fixAttrSizes() will set this + Padding: 0, + }, + } + } + + fixAttrSizes(&getAttrOut.Attr) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoSetAttr(inHeader *fission.InHeader, setAttrIn *fission.SetAttrIn) (setAttrOut *fission.SetAttrOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + chmodReply *jrpcfs.Reply + chmodRequest *jrpcfs.ChmodRequest + chownReply *jrpcfs.Reply + chownRequest *jrpcfs.ChownRequest + chunkedPutContext *chunkedPutContextStruct + chunkedPutContextElement *list.Element + err error + fileInode *fileInodeStruct + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + grantedLock *fileInodeLockRequestStruct + mTimeNSec uint32 + mTimeSec uint64 + ok bool + resizeReply *jrpcfs.Reply + resizeRequest *jrpcfs.ResizeRequest + setTimeReply *jrpcfs.Reply + setTimeRequest *jrpcfs.SetTimeRequest + settingAtime bool + settingAtimeAndOrMtime bool + settingGID bool + settingMode bool + settingMtime bool + settingSize bool + settingUID bool + timeNow time.Time + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoSetAttr_calls, 1) + + // Since we are modifying it, invalidate cached stat if present + + globals.Lock() + fileInode, ok = globals.fileInodeMap[inode.InodeNumber(inHeader.NodeID)] + if ok { + fileInode.cachedStat = nil + } + globals.Unlock() + + if setAttrIn.Valid != (setAttrIn.Valid & (fission.SetAttrInValidMode | fission.SetAttrInValidUID | fission.SetAttrInValidGID | fission.SetAttrInValidSize | fission.SetAttrInValidATime | fission.SetAttrInValidMTime | fission.SetAttrInValidFH | fission.SetAttrInValidATimeNow | fission.SetAttrInValidMTimeNow | fission.SetAttrInValidLockOwner)) { + errno = syscall.ENOSYS + return + } + + settingMode = (0 != (setAttrIn.Valid & fission.SetAttrInValidMode)) + + settingUID = (0 != (setAttrIn.Valid & fission.SetAttrInValidUID)) + settingGID = (0 != (setAttrIn.Valid & fission.SetAttrInValidGID)) + + settingSize = (0 != (setAttrIn.Valid & fission.SetAttrInValidSize)) + + settingAtime = (0 != (setAttrIn.Valid & fission.SetAttrInValidATime)) || (0 != (setAttrIn.Valid & fission.SetAttrInValidATimeNow)) + settingMtime = (0 != (setAttrIn.Valid & fission.SetAttrInValidMTime)) || (0 != (setAttrIn.Valid & fission.SetAttrInValidMTimeNow)) + + settingAtimeAndOrMtime = settingAtime || settingMtime + + // TODO: Verify it is ok to accept but ignore fission.SetAttrInValidFH in setAttrIn.Valid + // TODO: Verify it is ok to accept but ignore fission.SetAttrInValidLockOwner in setAttrIn.Valid + + if settingMode { + chmodRequest = &jrpcfs.ChmodRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + FileMode: setAttrIn.Mode & uint32(os.ModePerm), + } + + chmodReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcChmod", chmodRequest, chmodReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + } + + if settingUID || settingGID { + chownRequest = &jrpcfs.ChownRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + if settingUID { + chownRequest.UserID = int32(setAttrIn.UID) + } else { + chownRequest.UserID = -1 + } + + if settingGID { + chownRequest.GroupID = int32(setAttrIn.GID) + } else { + chownRequest.GroupID = -1 + } + + chownReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcChown", chownRequest, chownReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + } + + if settingSize { + fileInode = referenceFileInode(inode.InodeNumber(inHeader.NodeID)) + + fileInode.doFlushIfNecessary() + + grantedLock = fileInode.getExclusiveLock() + + if fileInode.extentMapFileSize > setAttrIn.Size { + fileInode.extentMapFileSize = setAttrIn.Size + } + + pruneExtentMap(fileInode.extentMap, setAttrIn.Size) + + chunkedPutContextElement = fileInode.chunkedPutList.Front() + + for nil != chunkedPutContextElement { + chunkedPutContext, ok = chunkedPutContextElement.Value.(*chunkedPutContextStruct) + if !ok { + logFatalf("chunkedPutContextElement.Value.(*chunkedPutContextStruct) returned !ok") + } + + if chunkedPutContext.fileSize > setAttrIn.Size { + chunkedPutContext.fileSize = setAttrIn.Size + } + + pruneExtentMap(chunkedPutContext.extentMap, setAttrIn.Size) + + chunkedPutContextElement = chunkedPutContextElement.Next() + } + + resizeRequest = &jrpcfs.ResizeRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + NewSize: setAttrIn.Size, + } + + resizeReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcResize", resizeRequest, resizeReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + grantedLock.release() + + fileInode.dereference() + } + + if settingAtimeAndOrMtime { + setTimeRequest = &jrpcfs.SetTimeRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + StatStruct: jrpcfs.StatStruct{ + MTimeNs: uint64(0), // Updated below if settingMtime + ATimeNs: uint64(0), // Updated below if settingAtime + }, + } + + timeNow = time.Now() + + if settingMtime { + if 0 != (setAttrIn.Valid & fission.SetAttrInValidMTimeNow) { + setTimeRequest.MTimeNs = uint64(timeNow.UnixNano()) + } else { + setTimeRequest.MTimeNs = unixTimeToNs(setAttrIn.MTimeSec, setAttrIn.MTimeNSec) + } + } + if settingAtime { + if 0 != (setAttrIn.Valid & fission.SetAttrInValidATimeNow) { + setTimeRequest.ATimeNs = uint64(timeNow.UnixNano()) + } else { + setTimeRequest.ATimeNs = unixTimeToNs(setAttrIn.ATimeSec, setAttrIn.ATimeNSec) + } + } + + setTimeReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcSetTime", setTimeRequest, setTimeReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + } + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + aTimeSec, aTimeNSec = nsToUnixTime(getStatReply.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(getStatReply.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(getStatReply.CTimeNs) + + setAttrOut = &fission.SetAttrOut{ + AttrValidSec: globals.attrValidSec, + AttrValidNSec: globals.attrValidNSec, + Dummy: 0, + Attr: fission.Attr{ + Ino: inHeader.NodeID, + Size: getStatReply.Size, // fixAttrSizes() will correct this if necessary + Blocks: 0, // fixAttrSizes() will compute this + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: getStatReply.FileMode, + NLink: uint32(getStatReply.NumLinks), + UID: getStatReply.UserID, + GID: getStatReply.GroupID, + RDev: 0, + BlkSize: 0, // fixAttrSizes() will set this + Padding: 0, + }, + } + + fixAttrSizes(&setAttrOut.Attr) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoReadLink(inHeader *fission.InHeader) (readLinkOut *fission.ReadLinkOut, errno syscall.Errno) { + var ( + err error + readSymlinkReply *jrpcfs.ReadSymlinkReply + readSymlinkRequest *jrpcfs.ReadSymlinkRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoReadLink_calls, 1) + + readSymlinkRequest = &jrpcfs.ReadSymlinkRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + readSymlinkReply = &jrpcfs.ReadSymlinkReply{} + + err = doJRPCRequest("Server.RpcReadSymlink", readSymlinkRequest, readSymlinkReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + readLinkOut = &fission.ReadLinkOut{ + Data: []byte(readSymlinkReply.Target), + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoSymLink(inHeader *fission.InHeader, symLinkIn *fission.SymLinkIn) (symLinkOut *fission.SymLinkOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + err error + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + mTimeNSec uint32 + mTimeSec uint64 + symlinkReply *jrpcfs.InodeReply + symlinkRequest *jrpcfs.SymlinkRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoSymLink_calls, 1) + + symlinkRequest = &jrpcfs.SymlinkRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + Basename: string(symLinkIn.Name[:]), + Target: string(symLinkIn.Data[:]), + UserID: int32(inHeader.UID), + GroupID: int32(inHeader.GID), + } + + symlinkReply = &jrpcfs.InodeReply{} + + err = doJRPCRequest("Server.RpcSymlink", symlinkRequest, symlinkReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: symlinkReply.InodeNumber, + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + aTimeSec, aTimeNSec = nsToUnixTime(getStatReply.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(getStatReply.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(getStatReply.CTimeNs) + + symLinkOut = &fission.SymLinkOut{ + EntryOut: fission.EntryOut{ + NodeID: uint64(symlinkReply.InodeNumber), + Generation: 0, + EntryValidSec: globals.entryValidSec, + AttrValidSec: globals.attrValidSec, + EntryValidNSec: globals.entryValidNSec, + AttrValidNSec: globals.attrValidNSec, + Attr: fission.Attr{ + Ino: uint64(symlinkReply.InodeNumber), + Size: 0, + Blocks: 0, + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: getStatReply.FileMode, + NLink: uint32(getStatReply.NumLinks), + UID: getStatReply.UserID, + GID: getStatReply.GroupID, + RDev: 0, + BlkSize: 0, + Padding: 0, + }, + }, + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoMkNod(inHeader *fission.InHeader, mkNodIn *fission.MkNodIn) (mkNodOut *fission.MkNodOut, errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoMkNod_calls, 1) + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoMkDir(inHeader *fission.InHeader, mkDirIn *fission.MkDirIn) (mkDirOut *fission.MkDirOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + err error + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + mkdirReply *jrpcfs.InodeReply + mkdirRequest *jrpcfs.MkdirRequest + mTimeNSec uint32 + mTimeSec uint64 + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoMkDir_calls, 1) + + mkdirRequest = &jrpcfs.MkdirRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + Basename: string(mkDirIn.Name[:]), + UserID: int32(inHeader.UID), + GroupID: int32(inHeader.GID), + FileMode: mkDirIn.Mode & uint32(os.ModePerm), + } + + mkdirReply = &jrpcfs.InodeReply{} + + err = doJRPCRequest("Server.RpcMkdir", mkdirRequest, mkdirReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: mkdirReply.InodeNumber, + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + aTimeSec, aTimeNSec = nsToUnixTime(getStatReply.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(getStatReply.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(getStatReply.CTimeNs) + + mkDirOut = &fission.MkDirOut{ + EntryOut: fission.EntryOut{ + NodeID: uint64(mkdirReply.InodeNumber), + Generation: 0, + EntryValidSec: globals.entryValidSec, + AttrValidSec: globals.attrValidSec, + EntryValidNSec: globals.entryValidNSec, + AttrValidNSec: globals.attrValidNSec, + Attr: fission.Attr{ + Ino: uint64(mkdirReply.InodeNumber), + Size: 0, + Blocks: 0, + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: getStatReply.FileMode, + NLink: uint32(getStatReply.NumLinks), + UID: getStatReply.UserID, + GID: getStatReply.GroupID, + RDev: 0, + BlkSize: 0, + Padding: 0, + }, + }, + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoUnlink(inHeader *fission.InHeader, unlinkIn *fission.UnlinkIn) (errno syscall.Errno) { + var ( + err error + unlinkReply *jrpcfs.Reply + unlinkRequest *jrpcfs.UnlinkRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoUnlink_calls, 1) + + // TODO: Remove this once Lease Management makes this unnecessary + doFlushIfNecessary(inode.InodeNumber(inHeader.NodeID), unlinkIn.Name) + + unlinkRequest = &jrpcfs.UnlinkRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + Basename: string(unlinkIn.Name[:]), + } + + unlinkReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcUnlink", unlinkRequest, unlinkReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRmDir(inHeader *fission.InHeader, rmDirIn *fission.RmDirIn) (errno syscall.Errno) { + var ( + err error + unlinkReply *jrpcfs.Reply + unlinkRequest *jrpcfs.UnlinkRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoRmDir_calls, 1) + + unlinkRequest = &jrpcfs.UnlinkRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + Basename: string(rmDirIn.Name[:]), + } + + unlinkReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcRmdir", unlinkRequest, unlinkReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRename(inHeader *fission.InHeader, renameIn *fission.RenameIn) (errno syscall.Errno) { + var ( + err error + renameReply *jrpcfs.Reply + renameRequest *jrpcfs.RenameRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoRename_calls, 1) + + // TODO: Remove this once Lease Management makes this unnecessary + doFlushIfNecessary(inode.InodeNumber(renameIn.NewDir), renameIn.NewName) + + renameRequest = &jrpcfs.RenameRequest{ + MountID: globals.mountID, + SrcDirInodeNumber: int64(inHeader.NodeID), + SrcBasename: string(renameIn.OldName[:]), + DstDirInodeNumber: int64(renameIn.NewDir), + DstBasename: string(renameIn.NewName[:]), + } + + renameReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcRename", renameRequest, renameReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoLink(inHeader *fission.InHeader, linkIn *fission.LinkIn) (linkOut *fission.LinkOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + err error + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + linkReply *jrpcfs.Reply + linkRequest *jrpcfs.LinkRequest + mTimeNSec uint32 + mTimeSec uint64 + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoLink_calls, 1) + + linkRequest = &jrpcfs.LinkRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + Basename: string(linkIn.Name[:]), + TargetInodeNumber: int64(linkIn.OldNodeID), + } + + linkReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcLink", linkRequest, linkReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(linkIn.OldNodeID), + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + aTimeSec, aTimeNSec = nsToUnixTime(getStatReply.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(getStatReply.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(getStatReply.CTimeNs) + + linkOut = &fission.LinkOut{ + EntryOut: fission.EntryOut{ + NodeID: linkIn.OldNodeID, + Generation: 0, + EntryValidSec: globals.entryValidSec, + AttrValidSec: globals.attrValidSec, + EntryValidNSec: globals.entryValidNSec, + AttrValidNSec: globals.attrValidNSec, + Attr: fission.Attr{ + Ino: linkIn.OldNodeID, + Size: getStatReply.Size, // fixAttrSizes() will correct this if necessary + Blocks: 0, // fixAttrSizes() will compute this + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: getStatReply.FileMode, + NLink: uint32(getStatReply.NumLinks), + UID: getStatReply.UserID, + GID: getStatReply.GroupID, + RDev: 0, + BlkSize: 0, // fixAttrSizes() will set this + Padding: 0, + }, + }, + } + + fixAttrSizes(&linkOut.EntryOut.Attr) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoOpen(inHeader *fission.InHeader, openIn *fission.OpenIn) (openOut *fission.OpenOut, errno syscall.Errno) { + var ( + err error + fhSet fhSetType + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoOpen_calls, 1) + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + if syscall.S_IFREG != (getStatReply.FileMode & syscall.S_IFMT) { + errno = syscall.EINVAL + return + } + + globals.Lock() + + globals.lastFH++ + + globals.fhToInodeNumberMap[globals.lastFH] = inHeader.NodeID + + fhSet, ok = globals.inodeNumberToFHMap[inHeader.NodeID] + if !ok { + fhSet = make(fhSetType) + } + fhSet[globals.lastFH] = struct{}{} + globals.inodeNumberToFHMap[inHeader.NodeID] = fhSet + + openOut = &fission.OpenOut{ + FH: globals.lastFH, + OpenFlags: 0, + Padding: 0, + } + + globals.Unlock() + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRead(inHeader *fission.InHeader, readIn *fission.ReadIn) (readOut *fission.ReadOut, errno syscall.Errno) { + var ( + curObjectOffset uint64 + err error + fhInodeNumber uint64 + fileInode *fileInodeStruct + grantedLock *fileInodeLockRequestStruct + logSegmentCacheElement *logSegmentCacheElementStruct + logSegmentCacheElementBufEndingPosition uint64 + logSegmentCacheElementBufRemainingLen uint64 + logSegmentCacheElementBufSelectedLen uint64 + logSegmentCacheElementBufStartingPosition uint64 + ok bool + readPlan []interface{} + readPlanSpan uint64 + readPlanStepAsInterface interface{} + readPlanStepAsMultiObjectExtentStruct *multiObjectExtentStruct + readPlanStepAsSingleObjectExtentWithLink *singleObjectExtentWithLinkStruct + readPlanStepRemainingLength uint64 + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoRead_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[readIn.FH] + if !ok { + logFatalf("DoRead(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, readIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoRead(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, readIn.FH, fhInodeNumber) + } + + globals.Unlock() + + fileInode = referenceFileInode(inode.InodeNumber(inHeader.NodeID)) + defer fileInode.dereference() + + grantedLock = fileInode.getSharedLock() + defer grantedLock.release() + + err = fileInode.populateExtentMap(uint64(readIn.Offset), uint64(readIn.Size)) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + readPlan, readPlanSpan = fileInode.getReadPlan(uint64(readIn.Offset), uint64(readIn.Size)) + + if (nil == readPlan) || (0 == readPlanSpan) { + readOut = &fission.ReadOut{ + Data: make([]byte, 0), + } + } else { + readOut = &fission.ReadOut{ + Data: make([]byte, 0, readPlanSpan), + } + + for _, readPlanStepAsInterface = range readPlan { + switch readPlanStepAsInterface.(type) { + case *multiObjectExtentStruct: + readPlanStepAsMultiObjectExtentStruct = readPlanStepAsInterface.(*multiObjectExtentStruct) + + if "" == readPlanStepAsMultiObjectExtentStruct.objectName { + // Zero-fill for readPlanStep.length + + readOut.Data = append(readOut.Data, make([]byte, readPlanStepAsMultiObjectExtentStruct.length)...) + } else { + // Fetch LogSegment data... possibly crossing LogSegmentCacheLine boundaries + + curObjectOffset = readPlanStepAsMultiObjectExtentStruct.objectOffset + readPlanStepRemainingLength = readPlanStepAsMultiObjectExtentStruct.length + + for readPlanStepRemainingLength > 0 { + logSegmentCacheElement = fetchLogSegmentCacheLine(readPlanStepAsMultiObjectExtentStruct.containerName, readPlanStepAsMultiObjectExtentStruct.objectName, curObjectOffset) + + if logSegmentCacheElementStateGetFailed == logSegmentCacheElement.state { + errno = syscall.EIO + return + } + + logSegmentCacheElementBufStartingPosition = curObjectOffset - logSegmentCacheElement.startingOffset + logSegmentCacheElementBufRemainingLen = uint64(len(logSegmentCacheElement.buf)) - logSegmentCacheElementBufStartingPosition + + if logSegmentCacheElementBufRemainingLen <= readPlanStepRemainingLength { + logSegmentCacheElementBufSelectedLen = logSegmentCacheElementBufRemainingLen + } else { + logSegmentCacheElementBufSelectedLen = readPlanStepRemainingLength + } + + logSegmentCacheElementBufEndingPosition = logSegmentCacheElementBufStartingPosition + logSegmentCacheElementBufSelectedLen + + readOut.Data = append(readOut.Data, logSegmentCacheElement.buf[logSegmentCacheElementBufStartingPosition:logSegmentCacheElementBufEndingPosition]...) + + curObjectOffset += logSegmentCacheElementBufSelectedLen + readPlanStepRemainingLength -= logSegmentCacheElementBufSelectedLen + } + } + case *singleObjectExtentWithLinkStruct: + readPlanStepAsSingleObjectExtentWithLink = readPlanStepAsInterface.(*singleObjectExtentWithLinkStruct) + + if nil == readPlanStepAsSingleObjectExtentWithLink.chunkedPutContext { + // Zero-fill for readPlanStep.length + + readOut.Data = append(readOut.Data, make([]byte, readPlanStepAsSingleObjectExtentWithLink.length)...) + } else { + // Fetch LogSegment data... from readPlanStepAsSingleObjectExtentWithLink.chunkedPutContextStruct + + _ = atomic.AddUint64(&globals.metrics.LogSegmentPUTReadHits, 1) + + readOut.Data = append(readOut.Data, readPlanStepAsSingleObjectExtentWithLink.chunkedPutContext.buf[readPlanStepAsSingleObjectExtentWithLink.objectOffset:readPlanStepAsSingleObjectExtentWithLink.objectOffset+readPlanStepAsSingleObjectExtentWithLink.length]...) + } + default: + logFatalf("getReadPlan() returned an invalid readPlanStep: %v", readPlanStepAsInterface) + } + } + } + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoRead_bytes, uint64(len(readOut.Data))) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoWrite(inHeader *fission.InHeader, writeIn *fission.WriteIn) (writeOut *fission.WriteOut, errno syscall.Errno) { + var ( + chunkedPutContext *chunkedPutContextStruct + chunkedPutContextElement *list.Element + fileInode *fileInodeStruct + grantedLock *fileInodeLockRequestStruct + sendChanFlushFlag bool + singleObjectExtent *singleObjectExtentStruct + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoWrite_calls, 1) + + fileInode = referenceFileInode(inode.InodeNumber(inHeader.NodeID)) + grantedLock = fileInode.getExclusiveLock() + + if 0 == fileInode.chunkedPutList.Len() { + // No chunkedPutContext is present (so none can be open), so open one + + _ = atomic.AddUint64(&globals.metrics.LogSegmentPUTs, 1) + + chunkedPutContext = &chunkedPutContextStruct{ + fileSize: fileInode.extentMapFileSize, + buf: make([]byte, 0), + fileInode: fileInode, + state: chunkedPutContextStateOpen, + sendChan: make(chan bool, chunkedPutContextSendChanBufferSize), + wakeChan: make(chan bool, chunkedPutContextWakeChanBufferSize), + inRead: false, + flushRequested: false, + } + + chunkedPutContext.extentMap = sortedmap.NewLLRBTree(sortedmap.CompareUint64, chunkedPutContext) + chunkedPutContext.chunkedPutListElement = fileInode.chunkedPutList.PushBack(chunkedPutContext) + + fileInode.reference() + + pruneFileInodeDirtyListIfNecessary() + + globals.Lock() + fileInode.dirtyListElement = globals.fileInodeDirtyList.PushBack(fileInode) + globals.Unlock() + + chunkedPutContext.fileInode.Add(1) + go chunkedPutContext.sendDaemon() + } else { + globals.Lock() + globals.fileInodeDirtyList.MoveToBack(fileInode.dirtyListElement) + globals.Unlock() + + chunkedPutContextElement = fileInode.chunkedPutList.Back() + chunkedPutContext = chunkedPutContextElement.Value.(*chunkedPutContextStruct) + + if chunkedPutContextStateOpen == chunkedPutContext.state { + // Use this most recent (and open) chunkedPutContext + } else { + // Most recent chunkedPutContext is closed, so open a new one + + _ = atomic.AddUint64(&globals.metrics.LogSegmentPUTs, 1) + + chunkedPutContext = &chunkedPutContextStruct{ + fileSize: fileInode.extentMapFileSize, + buf: make([]byte, 0), + fileInode: fileInode, + state: chunkedPutContextStateOpen, + sendChan: make(chan bool, chunkedPutContextSendChanBufferSize), + wakeChan: make(chan bool, chunkedPutContextWakeChanBufferSize), + inRead: false, + flushRequested: false, + } + + chunkedPutContext.extentMap = sortedmap.NewLLRBTree(sortedmap.CompareUint64, chunkedPutContext) + chunkedPutContext.chunkedPutListElement = fileInode.chunkedPutList.PushBack(chunkedPutContext) + + fileInode.reference() + + chunkedPutContext.fileInode.Add(1) + go chunkedPutContext.sendDaemon() + } + } + + singleObjectExtent = &singleObjectExtentStruct{ + fileOffset: uint64(writeIn.Offset), + objectOffset: uint64(len(chunkedPutContext.buf)), + length: uint64(len(writeIn.Data)), + } + + chunkedPutContext.mergeSingleObjectExtent(singleObjectExtent) + + if (singleObjectExtent.fileOffset + singleObjectExtent.length) > chunkedPutContext.fileSize { + chunkedPutContext.fileSize = singleObjectExtent.fileOffset + singleObjectExtent.length + } + + chunkedPutContext.buf = append(chunkedPutContext.buf, writeIn.Data...) + + sendChanFlushFlag = (uint64(len(chunkedPutContext.buf)) >= globals.config.MaxFlushSize) + + if sendChanFlushFlag { + chunkedPutContext.state = chunkedPutContextStateClosing + } + + grantedLock.release() + + chunkedPutContext.sendChan <- sendChanFlushFlag + + fileInode.dereference() + + writeOut = &fission.WriteOut{ + Size: uint32(len(writeIn.Data)), + Padding: 0, + } + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoWrite_bytes, uint64(writeOut.Size)) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoStatFS(inHeader *fission.InHeader) (statFSOut *fission.StatFSOut, errno syscall.Errno) { + var ( + err error + statVFSRequest *jrpcfs.StatVFSRequest + statVFSReply *jrpcfs.StatVFS + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoStatFS_calls, 1) + + statVFSRequest = &jrpcfs.StatVFSRequest{ + MountID: globals.mountID, + } + + statVFSReply = &jrpcfs.StatVFS{} + + err = doJRPCRequest("Server.RpcStatVFS", statVFSRequest, statVFSReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + statFSOut = &fission.StatFSOut{ + KStatFS: fission.KStatFS{ + Blocks: statVFSReply.TotalBlocks, + BFree: statVFSReply.FreeBlocks, + BAvail: statVFSReply.AvailBlocks, + Files: statVFSReply.TotalInodes, + FFree: statVFSReply.FreeInodes, + BSize: uint32(statVFSReply.BlockSize), + NameLen: uint32(statVFSReply.MaxFilenameLen), + FRSize: uint32(statVFSReply.FragmentSize), + Padding: 0, + Spare: [6]uint32{0, 0, 0, 0, 0, 0}, + }, + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRelease(inHeader *fission.InHeader, releaseIn *fission.ReleaseIn) (errno syscall.Errno) { + var ( + fhInodeNumber uint64 + fhSet fhSetType + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoRelease_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[releaseIn.FH] + if !ok { + logFatalf("DoRelease(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, releaseIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoRelease(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, releaseIn.FH, fhInodeNumber) + } + + delete(globals.fhToInodeNumberMap, releaseIn.FH) + + fhSet, ok = globals.inodeNumberToFHMap[inHeader.NodeID] + if !ok { + logFatalf("DoRelease(NodeID=%v,FH=%v) called for unknown NodeID", inHeader.NodeID, releaseIn.FH) + } + + _, ok = fhSet[releaseIn.FH] + if !ok { + logFatalf("DoRelease(NodeID=%v,FH=%v) called for FH missing from fhSet: %v", inHeader.NodeID, releaseIn.FH, fhSet) + } + + delete(fhSet, releaseIn.FH) + + if 0 == len(fhSet) { + delete(globals.inodeNumberToFHMap, inHeader.NodeID) + } else { + globals.inodeNumberToFHMap[inHeader.NodeID] = fhSet + } + + globals.Unlock() + + errno = 0 + return +} + +func (dummy *globalsStruct) DoFSync(inHeader *fission.InHeader, fSyncIn *fission.FSyncIn) (errno syscall.Errno) { + var ( + fhInodeNumber uint64 + fileInode *fileInodeStruct + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoFSync_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[fSyncIn.FH] + if !ok { + logFatalf("DoFSync(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, fSyncIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoFSync(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, fSyncIn.FH, fhInodeNumber) + } + + globals.Unlock() + + fileInode = referenceFileInode(inode.InodeNumber(inHeader.NodeID)) + fileInode.doFlushIfNecessary() + fileInode.dereference() + + errno = 0 + return +} + +func (dummy *globalsStruct) DoSetXAttr(inHeader *fission.InHeader, setXAttrIn *fission.SetXAttrIn) (errno syscall.Errno) { + var ( + err error + setXAttrReply *jrpcfs.Reply + setXAttrRequest *jrpcfs.SetXAttrRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoSetXAttr_calls, 1) + + if !globals.config.XAttrEnabled { + errno = syscall.ENOSYS + return + } + + setXAttrRequest = &jrpcfs.SetXAttrRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + AttrName: string(setXAttrIn.Name[:]), + AttrValue: setXAttrIn.Data[:], + AttrFlags: fs.SetXAttrCreateOrReplace, + } + + setXAttrReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcSetXAttr", setXAttrRequest, setXAttrReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoSetXAttr_bytes, uint64(len(setXAttrIn.Data))) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoGetXAttr(inHeader *fission.InHeader, getXAttrIn *fission.GetXAttrIn) (getXAttrOut *fission.GetXAttrOut, errno syscall.Errno) { + var ( + err error + getXAttrReply *jrpcfs.GetXAttrReply + getXAttrRequest *jrpcfs.GetXAttrRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoGetXAttr_calls, 1) + + if !globals.config.XAttrEnabled { + errno = syscall.ENOSYS + return + } + + getXAttrRequest = &jrpcfs.GetXAttrRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + AttrName: string(getXAttrIn.Name[:]), + } + + getXAttrReply = &jrpcfs.GetXAttrReply{} + + err = doJRPCRequest("Server.RpcGetXAttr", getXAttrRequest, getXAttrReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + if 0 == getXAttrIn.Size { + getXAttrOut = &fission.GetXAttrOut{ + Size: uint32(len(getXAttrReply.AttrValue)), + Padding: 0, + Data: make([]byte, 0), + } + errno = 0 + return + } + + if uint32(len(getXAttrReply.AttrValue)) > getXAttrIn.Size { + errno = syscall.ERANGE + return + } + + getXAttrOut = &fission.GetXAttrOut{ + Size: uint32(len(getXAttrReply.AttrValue)), + Padding: 0, + Data: getXAttrReply.AttrValue, + } + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoGetXAttr_bytes, uint64(getXAttrOut.Size)) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoListXAttr(inHeader *fission.InHeader, listXAttrIn *fission.ListXAttrIn) (listXAttrOut *fission.ListXAttrOut, errno syscall.Errno) { + var ( + err error + listXAttrReply *jrpcfs.ListXAttrReply + listXAttrRequest *jrpcfs.ListXAttrRequest + totalSize uint32 + xAttrIndex int + xAttrName string + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoListXAttr_calls, 1) + + if !globals.config.XAttrEnabled { + errno = syscall.ENOSYS + return + } + + listXAttrRequest = &jrpcfs.ListXAttrRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + listXAttrReply = &jrpcfs.ListXAttrReply{} + + err = doJRPCRequest("Server.RpcListXAttr", listXAttrRequest, listXAttrReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + totalSize = 0 + + for _, xAttrName = range listXAttrReply.AttrNames { + totalSize += uint32(len(xAttrName) + 1) + } + + if 0 == listXAttrIn.Size { + listXAttrOut = &fission.ListXAttrOut{ + Size: totalSize, + Padding: 0, + Name: make([][]byte, 0), + } + errno = 0 + return + } + + listXAttrOut = &fission.ListXAttrOut{ + Size: totalSize, // unnecessary + Padding: 0, + Name: make([][]byte, len(listXAttrReply.AttrNames)), + } + + for xAttrIndex, xAttrName = range listXAttrReply.AttrNames { + listXAttrOut.Name[xAttrIndex] = []byte(xAttrName) + } + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoListXAttr_names, uint64(len(listXAttrOut.Name))) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRemoveXAttr(inHeader *fission.InHeader, removeXAttrIn *fission.RemoveXAttrIn) (errno syscall.Errno) { + var ( + err error + removeXAttrReply *jrpcfs.Reply + removeXAttrRequest *jrpcfs.RemoveXAttrRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoRemoveXAttr_calls, 1) + + if !globals.config.XAttrEnabled { + errno = syscall.ENOSYS + return + } + + removeXAttrRequest = &jrpcfs.RemoveXAttrRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + AttrName: string(removeXAttrIn.Name[:]), + } + + removeXAttrReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcRemoveXAttr", removeXAttrRequest, removeXAttrReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoFlush(inHeader *fission.InHeader, flushIn *fission.FlushIn) (errno syscall.Errno) { + var ( + fhInodeNumber uint64 + ok bool + ) + _ = atomic.AddUint64(&globals.metrics.FUSE_DoFlush_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[flushIn.FH] + if !ok { + logFatalf("DoFlush(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, flushIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoFlush(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, flushIn.FH, fhInodeNumber) + } + + globals.Unlock() + + errno = 0 + return +} + +func (dummy *globalsStruct) DoInit(inHeader *fission.InHeader, initIn *fission.InitIn) (initOut *fission.InitOut, errno syscall.Errno) { + var ( + initOutFlags uint32 + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoInit_calls, 1) + + if globals.config.ReadDirPlusEnabled { + initOutFlags = initIn.Flags & initOutFlagsMaskReadDirPlusEnabled + } else { + initOutFlags = initIn.Flags & initOutFlagsMaskReadDirPlusDisabled + } + + initOut = &fission.InitOut{ + Major: initIn.Major, + Minor: initIn.Minor, + MaxReadAhead: initIn.MaxReadAhead, + Flags: initOutFlags, + MaxBackground: globals.config.FUSEMaxBackground, + CongestionThreshhold: globals.config.FUSECongestionThreshhold, + MaxWrite: globals.config.FUSEMaxWrite, + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoOpenDir(inHeader *fission.InHeader, openDirIn *fission.OpenDirIn) (openDirOut *fission.OpenDirOut, errno syscall.Errno) { + var ( + err error + fhSet fhSetType + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoOpenDir_calls, 1) + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + if syscall.S_IFDIR != (getStatReply.FileMode & syscall.S_IFMT) { + errno = syscall.ENOTDIR + return + } + + globals.Lock() + + globals.lastFH++ + + globals.fhToInodeNumberMap[globals.lastFH] = inHeader.NodeID + + fhSet, ok = globals.inodeNumberToFHMap[inHeader.NodeID] + if !ok { + fhSet = make(fhSetType) + } + fhSet[globals.lastFH] = struct{}{} + globals.inodeNumberToFHMap[inHeader.NodeID] = fhSet + + openDirOut = &fission.OpenDirOut{ + FH: globals.lastFH, + OpenFlags: 0, + Padding: 0, + } + + globals.Unlock() + + errno = 0 + return +} + +func (dummy *globalsStruct) DoReadDir(inHeader *fission.InHeader, readDirIn *fission.ReadDirIn) (readDirOut *fission.ReadDirOut, errno syscall.Errno) { + var ( + curSize uint32 + dirEntIndex uint64 + dirEntNameLenAligned uint32 + dirEnt fission.DirEnt + dirEntSize uint32 + dirEntry *jrpcfs.DirEntry + err error + fhInodeNumber uint64 + maxEntries uint64 + numEntries uint64 + ok bool + readdirByLocRequest *jrpcfs.ReaddirByLocRequest + readdirReply *jrpcfs.ReaddirReply + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoReadDir_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[readDirIn.FH] + if !ok { + logFatalf("DoReadDir(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, readDirIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoReadDir(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, readDirIn.FH, fhInodeNumber) + } + + globals.Unlock() + + maxEntries = (uint64(readDirIn.Size) + fission.DirEntFixedPortionSize + 1 - 1) / (fission.DirEntFixedPortionSize + 1) + + readdirByLocRequest = &jrpcfs.ReaddirByLocRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + MaxEntries: maxEntries, + PrevDirEntLocation: int64(readDirIn.Offset) - 1, + } + + readdirReply = &jrpcfs.ReaddirReply{} + + err = doJRPCRequest("Server.RpcReaddirByLoc", readdirByLocRequest, readdirReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + numEntries = uint64(len(readdirReply.DirEnts)) + + readDirOut = &fission.ReadDirOut{ + DirEnt: make([]fission.DirEnt, 0, numEntries), + } + + curSize = 0 + + for dirEntIndex = 0; dirEntIndex < numEntries; dirEntIndex++ { + dirEntry = &readdirReply.DirEnts[dirEntIndex] + + dirEntNameLenAligned = (uint32(len(dirEntry.Basename)) + (fission.DirEntAlignment - 1)) & ^uint32(fission.DirEntAlignment-1) + dirEntSize = fission.DirEntFixedPortionSize + dirEntNameLenAligned + + if (curSize + dirEntSize) > readDirIn.Size { + break + } + + dirEnt = fission.DirEnt{ + Ino: uint64(dirEntry.InodeNumber), + Off: uint64(dirEntry.NextDirLocation), + NameLen: uint32(len(dirEntry.Basename)), // unnecessary + Type: uint32(dirEntry.FileType), + Name: []byte(dirEntry.Basename), + } + + readDirOut.DirEnt = append(readDirOut.DirEnt, dirEnt) + + curSize += dirEntSize + } + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoReadDir_entries, uint64(len(readDirOut.DirEnt))) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoReleaseDir(inHeader *fission.InHeader, releaseDirIn *fission.ReleaseDirIn) (errno syscall.Errno) { + var ( + fhInodeNumber uint64 + fhSet fhSetType + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoReleaseDir_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[releaseDirIn.FH] + if !ok { + logFatalf("DoReleaseDir(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, releaseDirIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoReleaseDir(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, releaseDirIn.FH, fhInodeNumber) + } + + delete(globals.fhToInodeNumberMap, releaseDirIn.FH) + + fhSet, ok = globals.inodeNumberToFHMap[inHeader.NodeID] + if !ok { + logFatalf("DoReleaseDir(NodeID=%v,FH=%v) called for unknown NodeID", inHeader.NodeID, releaseDirIn.FH) + } + + _, ok = fhSet[releaseDirIn.FH] + if !ok { + logFatalf("DoReleaseDir(NodeID=%v,FH=%v) called for FH missing from fhSet: %v", inHeader.NodeID, releaseDirIn.FH, fhSet) + } + + delete(fhSet, releaseDirIn.FH) + + if 0 == len(fhSet) { + delete(globals.inodeNumberToFHMap, inHeader.NodeID) + } else { + globals.inodeNumberToFHMap[inHeader.NodeID] = fhSet + } + + globals.Unlock() + + errno = 0 + return +} + +func (dummy *globalsStruct) DoFSyncDir(inHeader *fission.InHeader, fSyncDirIn *fission.FSyncDirIn) (errno syscall.Errno) { + var ( + fhInodeNumber uint64 + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoFSyncDir_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[fSyncDirIn.FH] + if !ok { + logFatalf("DoFSync(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, fSyncDirIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoFSync(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, fSyncDirIn.FH, fhInodeNumber) + } + + globals.Unlock() + + errno = 0 + return +} + +func (dummy *globalsStruct) DoGetLK(inHeader *fission.InHeader, getLKIn *fission.GetLKIn) (getLKOut *fission.GetLKOut, errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoGetLK_calls, 1) + errno = syscall.ENOSYS + return +} +func (dummy *globalsStruct) DoSetLK(inHeader *fission.InHeader, setLKIn *fission.SetLKIn) (errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoSetLK_calls, 1) + errno = syscall.ENOSYS + return +} +func (dummy *globalsStruct) DoSetLKW(inHeader *fission.InHeader, setLKWIn *fission.SetLKWIn) (errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoSetLKW_calls, 1) + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoAccess(inHeader *fission.InHeader, accessIn *fission.AccessIn) (errno syscall.Errno) { + var ( + err error + accessReply *jrpcfs.Reply + accessRequest *jrpcfs.AccessRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoAccess_calls, 1) + + accessRequest = &jrpcfs.AccessRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + UserID: int32(inHeader.UID), + GroupID: int32(inHeader.GID), + AccessMode: accessIn.Mask, + } + + accessReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcAccess", accessRequest, accessReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoCreate(inHeader *fission.InHeader, createIn *fission.CreateIn) (createOut *fission.CreateOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + createReply *jrpcfs.InodeReply + createRequest *jrpcfs.CreateRequest + err error + fhSet fhSetType + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + mTimeNSec uint32 + mTimeSec uint64 + ok bool + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoCreate_calls, 1) + + createRequest = &jrpcfs.CreateRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + Basename: string(createIn.Name[:]), + UserID: int32(inHeader.UID), + GroupID: int32(inHeader.GID), + FileMode: createIn.Mode & uint32(os.ModePerm), + } + + createReply = &jrpcfs.InodeReply{} + + err = doJRPCRequest("Server.RpcCreate", createRequest, createReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + getStatRequest = &jrpcfs.GetStatRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: createReply.InodeNumber, + }, + } + + getStatReply = &jrpcfs.StatStruct{} + + err = doJRPCRequest("Server.RpcGetStat", getStatRequest, getStatReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + aTimeSec, aTimeNSec = nsToUnixTime(getStatReply.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(getStatReply.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(getStatReply.CTimeNs) + + globals.Lock() + + globals.lastFH++ + + globals.fhToInodeNumberMap[globals.lastFH] = uint64(createReply.InodeNumber) + + fhSet, ok = globals.inodeNumberToFHMap[uint64(createReply.InodeNumber)] + if !ok { + fhSet = make(fhSetType) + } + fhSet[globals.lastFH] = struct{}{} + globals.inodeNumberToFHMap[uint64(createReply.InodeNumber)] = fhSet + + createOut = &fission.CreateOut{ + EntryOut: fission.EntryOut{ + NodeID: uint64(createReply.InodeNumber), + Generation: 0, + EntryValidSec: globals.entryValidSec, + AttrValidSec: globals.attrValidSec, + EntryValidNSec: globals.entryValidNSec, + AttrValidNSec: globals.attrValidNSec, + Attr: fission.Attr{ + Ino: uint64(createReply.InodeNumber), + Size: getStatReply.Size, // fixAttrSizes() will correct this if necessary + Blocks: 0, // fixAttrSizes() will compute this + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: getStatReply.FileMode, + NLink: uint32(getStatReply.NumLinks), + UID: getStatReply.UserID, + GID: getStatReply.GroupID, + RDev: 0, + BlkSize: 0, // fixAttrSizes() will set this + Padding: 0, + }, + }, + FH: globals.lastFH, + OpenFlags: fission.FOpenResponseDirectIO, + Padding: 0, + } + + globals.Unlock() + + fixAttrSizes(&createOut.Attr) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoInterrupt(inHeader *fission.InHeader, interruptIn *fission.InterruptIn) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoInterrupt_calls, 1) + return +} + +func (dummy *globalsStruct) DoBMap(inHeader *fission.InHeader, bMapIn *fission.BMapIn) (bMapOut *fission.BMapOut, errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoBMap_calls, 1) + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoDestroy(inHeader *fission.InHeader) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoDestroy_calls, 1) +} + +func (dummy *globalsStruct) DoPoll(inHeader *fission.InHeader, pollIn *fission.PollIn) (pollOut *fission.PollOut, errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoPoll_calls, 1) + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoBatchForget(inHeader *fission.InHeader, batchForgetIn *fission.BatchForgetIn) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoBatchForget_calls, 1) + _ = atomic.AddUint64(&globals.metrics.FUSE_DoBatchForget_nodes, uint64(len(batchForgetIn.Forget))) + return +} + +func (dummy *globalsStruct) DoFAllocate(inHeader *fission.InHeader, fAllocateIn *fission.FAllocateIn) (errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoFAllocate_calls, 1) + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoReadDirPlus(inHeader *fission.InHeader, readDirPlusIn *fission.ReadDirPlusIn) (readDirPlusOut *fission.ReadDirPlusOut, errno syscall.Errno) { + var ( + aTimeNSec uint32 + aTimeSec uint64 + cTimeNSec uint32 + cTimeSec uint64 + curSize uint32 + dirEntIndex uint64 + dirEntNameLenAligned uint32 + dirEntPlus fission.DirEntPlus + dirEntPlusSize uint32 + dirEntry *jrpcfs.DirEntry + err error + fhInodeNumber uint64 + mTimeNSec uint32 + mTimeSec uint64 + maxEntries uint64 + numEntries uint64 + ok bool + readdirPlusByLocRequest *jrpcfs.ReaddirPlusByLocRequest + readdirPlusReply *jrpcfs.ReaddirPlusReply + statStruct *jrpcfs.StatStruct + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoReadDirPlus_calls, 1) + + globals.Lock() + + fhInodeNumber, ok = globals.fhToInodeNumberMap[readDirPlusIn.FH] + if !ok { + logFatalf("DoReadDirPlus(NodeID=%v,FH=%v) called for unknown FH", inHeader.NodeID, readDirPlusIn.FH) + } + if fhInodeNumber != inHeader.NodeID { + logFatalf("DoReadDirPlus(NodeID=%v,FH=%v) called for FH associated with NodeID=%v", inHeader.NodeID, readDirPlusIn.FH, fhInodeNumber) + } + + globals.Unlock() + + maxEntries = (uint64(readDirPlusIn.Size) + fission.DirEntPlusFixedPortionSize + 1 - 1) / (fission.DirEntPlusFixedPortionSize + 1) + + readdirPlusByLocRequest = &jrpcfs.ReaddirPlusByLocRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(inHeader.NodeID), + }, + MaxEntries: maxEntries, + PrevDirEntLocation: int64(readDirPlusIn.Offset) - 1, + } + + readdirPlusReply = &jrpcfs.ReaddirPlusReply{} + + err = doJRPCRequest("Server.RpcReaddirPlusByLoc", readdirPlusByLocRequest, readdirPlusReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + numEntries = uint64(len(readdirPlusReply.DirEnts)) + if numEntries != uint64(len(readdirPlusReply.StatEnts)) { + logFatalf("DoReadDirPlus(NodeID=%v,FH=%v) fetched mismatched number of DirEnts (%v) & StatEnts (%v)", inHeader.NodeID, readDirPlusIn.FH, len(readdirPlusReply.DirEnts), len(readdirPlusReply.StatEnts)) + } + + readDirPlusOut = &fission.ReadDirPlusOut{ + DirEntPlus: make([]fission.DirEntPlus, 0, numEntries), + } + + curSize = 0 + + for dirEntIndex = 0; dirEntIndex < numEntries; dirEntIndex++ { + dirEntry = &readdirPlusReply.DirEnts[dirEntIndex] + statStruct = &readdirPlusReply.StatEnts[dirEntIndex] + + dirEntNameLenAligned = (uint32(len(dirEntry.Basename)) + (fission.DirEntAlignment - 1)) & ^uint32(fission.DirEntAlignment-1) + dirEntPlusSize = fission.DirEntPlusFixedPortionSize + dirEntNameLenAligned + + if (curSize + dirEntPlusSize) > readDirPlusIn.Size { + break + } + + aTimeSec, aTimeNSec = nsToUnixTime(statStruct.ATimeNs) + mTimeSec, mTimeNSec = nsToUnixTime(statStruct.MTimeNs) + cTimeSec, cTimeNSec = nsToUnixTime(statStruct.CTimeNs) + + dirEntPlus = fission.DirEntPlus{ + EntryOut: fission.EntryOut{ + NodeID: uint64(dirEntry.InodeNumber), + Generation: 0, + EntryValidSec: globals.entryValidSec, + AttrValidSec: globals.attrValidSec, + EntryValidNSec: globals.entryValidNSec, + AttrValidNSec: globals.attrValidNSec, + Attr: fission.Attr{ + Ino: uint64(dirEntry.InodeNumber), + Size: statStruct.Size, // fixAttrSizes() will correct this if necessary + Blocks: 0, // fixAttrSizes() will compute this + ATimeSec: aTimeSec, + MTimeSec: mTimeSec, + CTimeSec: cTimeSec, + ATimeNSec: aTimeNSec, + MTimeNSec: mTimeNSec, + CTimeNSec: cTimeNSec, + Mode: statStruct.FileMode, + NLink: uint32(statStruct.NumLinks), + UID: statStruct.UserID, + GID: statStruct.GroupID, + RDev: 0, + BlkSize: 0, // fixAttrSizes() will set this + Padding: 0, + }, + }, + DirEnt: fission.DirEnt{ + Ino: uint64(dirEntry.InodeNumber), + Off: uint64(dirEntry.NextDirLocation), + NameLen: uint32(len(dirEntry.Basename)), // unnecessary + Type: uint32(dirEntry.FileType), + Name: []byte(dirEntry.Basename), + }, + } + + fixAttrSizes(&dirEntPlus.EntryOut.Attr) + + readDirPlusOut.DirEntPlus = append(readDirPlusOut.DirEntPlus, dirEntPlus) + + curSize += dirEntPlusSize + } + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoReadDirPlus_entries, uint64(len(readDirPlusOut.DirEntPlus))) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRename2(inHeader *fission.InHeader, rename2In *fission.Rename2In) (errno syscall.Errno) { + var ( + err error + renameReply *jrpcfs.Reply + renameRequest *jrpcfs.RenameRequest + ) + + _ = atomic.AddUint64(&globals.metrics.FUSE_DoRename2_calls, 1) + + // TODO: Remove this once Lease Management makes this unnecessary + doFlushIfNecessary(inode.InodeNumber(rename2In.NewDir), rename2In.NewName) + + renameRequest = &jrpcfs.RenameRequest{ + MountID: globals.mountID, + SrcDirInodeNumber: int64(inHeader.NodeID), + SrcBasename: string(rename2In.OldName[:]), + DstDirInodeNumber: int64(rename2In.NewDir), + DstBasename: string(rename2In.NewName[:]), + } + + renameReply = &jrpcfs.Reply{} + + err = doJRPCRequest("Server.RpcRename", renameRequest, renameReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoLSeek(inHeader *fission.InHeader, lSeekIn *fission.LSeekIn) (lSeekOut *fission.LSeekOut, errno syscall.Errno) { + _ = atomic.AddUint64(&globals.metrics.FUSE_DoLSeek_calls, 1) + errno = syscall.ENOSYS + return +} diff --git a/pfsagentd/functional_test.go b/pfsagentd/functional_test.go index 37b15e3a4..e76af8a58 100644 --- a/pfsagentd/functional_test.go +++ b/pfsagentd/functional_test.go @@ -16,7 +16,7 @@ var ( testFileLastByte byte ) -func HIDETestSimpleWriteReadClose(t *testing.T) { +func TestSimpleWriteReadClose(t *testing.T) { const ( testFileName = "testSimpleWriteReadCloseFileName" totalBytes int64 = 100 @@ -72,7 +72,7 @@ func HIDETestSimpleWriteReadClose(t *testing.T) { testTeardown(t) } -func HIDETestRandomOverwrites(t *testing.T) { +func TestRandomOverwrites(t *testing.T) { const ( testFileName = "testRandomeOverwritesFileName" totalBytes int64 = 1000 @@ -340,7 +340,7 @@ func testExhaustiveOverwrites(t *testing.T, prefix string, reopenBeforeEachWrite verifyTestFileContents(t, prefix, "Overlapping one existing extent on the right, another existing extent entirely, & a 3rd existing extent on the left") } -func HIDETestExhaustiveOverwrites(t *testing.T) { +func TestExhaustiveOverwrites(t *testing.T) { const ( testFileName = "testExhaustiveOverwritesFileName" ) diff --git a/pfsagentd/fuse.go b/pfsagentd/fuse.go deleted file mode 100644 index 5c6a92270..000000000 --- a/pfsagentd/fuse.go +++ /dev/null @@ -1,1389 +0,0 @@ -package main - -// The following implements the Low Level FUSE upcalls for presenting a ProxyFS Volume locally. -// -// Useful URLs for understanding Bazil FUSE APIs and data structures: -// -// https://gowalker.org/bazil.org/fuse - specific to Bazil FUSE -// https://docs.racket-lang.org/fuse/index.html - generic to FUSE -// -// Bazil FUSE source provides insite into the mapping from FUSE ops to Request structs: -// -// vendor/bazil.org/fuse/fuse_kernel.go - FUSE op names -// vendor/bazil.org/fuse/fuse.go - mapping from FUSE ops to Request structs -// -// JSON RPCs are implemented in packages fs, inode, & jrpcfs: -// -// fs/api.go -// inode/api.go -// jrpcfs/api.go - -import ( - "container/list" - "io" - "os" - "os/exec" - "path" - "reflect" - "sync/atomic" - "syscall" - "time" - - "bazil.org/fuse" - - "github.com/swiftstack/ProxyFS/fs" - "github.com/swiftstack/ProxyFS/inode" - "github.com/swiftstack/ProxyFS/jrpcfs" -) - -const ( - maxUnmountRetryCount uint32 = 100 - unmountRetryGap = 100 * time.Millisecond -) - -func performMountFUSE() { - var ( - curRetryCount uint32 - err error - fuseMountPointPathBaseName string - lazyUnmountCmd *exec.Cmd - mountPointContainingDirDevice int64 - mountPointDevice int64 - ) - - err = fuse.Unmount(globals.config.FUSEMountPointPath) - if nil != err { - logTracef("pre-fuse.Unmount() in performMount() returned: %v", err) - } - - mountPointContainingDirDevice = fetchInodeDevice("path.Dir([Agent]FUSEMountPointPath", path.Dir(globals.config.FUSEMountPointPath)) - mountPointDevice = fetchInodeDevice("[Agent]FUSEMountPointPath", globals.config.FUSEMountPointPath) - - if mountPointDevice != mountPointContainingDirDevice { - // Presumably, the mount point is (still) currently mounted, so attempt to unmount it first - - lazyUnmountCmd = exec.Command("fusermount", "-uz", globals.config.FUSEMountPointPath) - err = lazyUnmountCmd.Run() - if nil != err { - logFatal(err) - } - - curRetryCount = 0 - - for mountPointDevice != mountPointContainingDirDevice { - time.Sleep(unmountRetryGap) // Try again in a bit - curRetryCount++ - if curRetryCount >= maxUnmountRetryCount { - logFatalf("mountPointDevice != mountPointContainingDirDevice MaxRetryCount exceeded") - } - mountPointDevice = fetchInodeDevice("[Agent]FUSEMountPointPath", globals.config.FUSEMountPointPath) - } - } - - fuseMountPointPathBaseName = path.Base(globals.config.FUSEMountPointPath) - - if globals.config.ReadOnly { - globals.fuseConn, err = fuse.Mount( - globals.config.FUSEMountPointPath, - fuse.AllowOther(), - fuse.AsyncRead(), - fuse.DefaultPermissions(), // so handleAccessRequest() should not be called - fuse.ExclCreate(), - fuse.FSName(fuseMountPointPathBaseName), - fuse.NoAppleDouble(), - fuse.NoAppleXattr(), - fuse.ReadOnly(), - fuse.Subtype("ProxyFS"), - fuse.VolumeName(fuseMountPointPathBaseName), - ) - } else { - globals.fuseConn, err = fuse.Mount( - globals.config.FUSEMountPointPath, - fuse.AllowOther(), - fuse.AsyncRead(), - fuse.DefaultPermissions(), // so handleAccessRequest() should not be called - fuse.ExclCreate(), - fuse.FSName(fuseMountPointPathBaseName), - fuse.NoAppleDouble(), - fuse.NoAppleXattr(), - fuse.Subtype("ProxyFS"), - fuse.VolumeName(fuseMountPointPathBaseName), - ) - } - if nil != err { - logFatal(err) - } - - go serveFuse() - - <-globals.fuseConn.Ready - if nil != globals.fuseConn.MountError { - logFatal(globals.fuseConn.MountError) - } - - logInfof("Now serving on %s", globals.config.FUSEMountPointPath) -} - -func fetchInodeDevice(pathTitle string, path string) (inodeDevice int64) { - var ( - err error - fi os.FileInfo - ok bool - stat *syscall.Stat_t - ) - - fi, err = os.Stat(path) - if nil != err { - if os.IsNotExist(err) { - logFatalf("%s path (%s) not found", pathTitle, path) - } else { - logFatalf("%s path (%s) os.Stat() failed: %v", pathTitle, path, err) - } - } - if nil == fi.Sys() { - logFatalf("%s path (%s) had empty os.Stat()", pathTitle, path) - } - stat, ok = fi.Sys().(*syscall.Stat_t) - if !ok { - logFatalf("%s path (%s) fi.Sys().(*syscall.Stat_t) returned !ok", pathTitle, path) - } - - inodeDevice = int64(stat.Dev) - - return -} - -func performUnmountFUSE() { - var ( - err error - ) - - err = fuse.Unmount(globals.config.FUSEMountPointPath) - if nil != err { - logFatal(err) - } - - logInfof("Unmounted from %s", globals.config.FUSEMountPointPath) -} - -func serveFuse() { - var ( - err error - request fuse.Request - ) - - for { - // Fetch next *fuse.Request... exiting on fuseConn error - - request, err = globals.fuseConn.ReadRequest() - if nil != err { - if io.EOF == err { - logTracef("exiting serveFuse() due to io.EOF") - return - } - logErrorf("serveFuse() exiting due to err: %v", err) - return - } - - logTracef("serveFuse() got %v", reflect.ValueOf(request).Type()) - - switch request.(type) { - case *fuse.AccessRequest: - handleAccessRequest(request.(*fuse.AccessRequest)) - case *fuse.CreateRequest: - handleCreateRequest(request.(*fuse.CreateRequest)) - case *fuse.DestroyRequest: - handleDestroyRequest(request.(*fuse.DestroyRequest)) - case *fuse.ExchangeDataRequest: - handleExchangeDataRequest(request.(*fuse.ExchangeDataRequest)) - case *fuse.FlushRequest: - handleFlushRequest(request.(*fuse.FlushRequest)) - case *fuse.ForgetRequest: - handleForgetRequest(request.(*fuse.ForgetRequest)) - case *fuse.FsyncRequest: - handleFsyncRequest(request.(*fuse.FsyncRequest)) - case *fuse.GetattrRequest: - handleGetattrRequest(request.(*fuse.GetattrRequest)) - case *fuse.GetxattrRequest: - handleGetxattrRequest(request.(*fuse.GetxattrRequest)) - case *fuse.InitRequest: - handleInitRequest(request.(*fuse.InitRequest)) - case *fuse.InterruptRequest: - handleInterruptRequest(request.(*fuse.InterruptRequest)) - case *fuse.LinkRequest: - handleLinkRequest(request.(*fuse.LinkRequest)) - case *fuse.ListxattrRequest: - handleListxattrRequest(request.(*fuse.ListxattrRequest)) - case *fuse.LookupRequest: - handleLookupRequest(request.(*fuse.LookupRequest)) - case *fuse.MkdirRequest: - handleMkdirRequest(request.(*fuse.MkdirRequest)) - case *fuse.MknodRequest: - handleMknodRequest(request.(*fuse.MknodRequest)) - case *fuse.OpenRequest: - handleOpenRequest(request.(*fuse.OpenRequest)) - case *fuse.ReadRequest: - handleReadRequest(request.(*fuse.ReadRequest)) - case *fuse.ReadlinkRequest: - handleReadlinkRequest(request.(*fuse.ReadlinkRequest)) - case *fuse.ReleaseRequest: - handleReleaseRequest(request.(*fuse.ReleaseRequest)) - case *fuse.RemoveRequest: - handleRemoveRequest(request.(*fuse.RemoveRequest)) - case *fuse.RemovexattrRequest: - handleRemovexattrRequest(request.(*fuse.RemovexattrRequest)) - case *fuse.RenameRequest: - handleRenameRequest(request.(*fuse.RenameRequest)) - case *fuse.SetattrRequest: - handleSetattrRequest(request.(*fuse.SetattrRequest)) - case *fuse.SetxattrRequest: - handleSetxattrRequest(request.(*fuse.SetxattrRequest)) - case *fuse.StatfsRequest: - handleStatfsRequest(request.(*fuse.StatfsRequest)) - case *fuse.SymlinkRequest: - handleSymlinkRequest(request.(*fuse.SymlinkRequest)) - case *fuse.WriteRequest: - handleWriteRequest(request.(*fuse.WriteRequest)) // See io.go - default: - // Bazil FUSE punted the not-understood opCode... so just reject it - _ = atomic.AddUint64(&globals.metrics.FUSE_UnknownRequest_calls, 1) - request.RespondError(fuse.ENOTSUP) - } - } -} - -func handleAccessRequest(request *fuse.AccessRequest) { - _ = atomic.AddUint64(&globals.metrics.FUSE_AccessRequest_calls, 1) - - logFatalf("handleAccessRequest() should not have been called due to DefaultPermissions() passed to fuse.Mount()") -} - -func handleCreateRequest(request *fuse.CreateRequest) { - var ( - embeddedLookupResponse *fuse.LookupResponse - embeddedOpenResponse *fuse.OpenResponse - err error - createReply *jrpcfs.Reply - createRequest *jrpcfs.CreateRequest - response *fuse.CreateResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_CreateRequest_calls, 1) - - createRequest = &jrpcfs.CreateRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - Basename: request.Name, - UserID: int32(request.Uid), - GroupID: int32(request.Gid), - FileMode: uint32(request.Mode & os.ModePerm), - } - - createReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcCreate", createRequest, createReply) - if nil != err { - request.RespondError(err) - return - } - - embeddedLookupResponse, err = lookupRequestHelper(request.Node, request.Name) - if nil != err { - request.RespondError(err) - return - } - - embeddedOpenResponse = openRequestHelper(embeddedLookupResponse.Node, false) - - response = &fuse.CreateResponse{ - LookupResponse: *embeddedLookupResponse, - OpenResponse: *embeddedOpenResponse, - } - - request.Respond(response) -} - -// handleDestroyRequest is called just before an unmount. While we could enforce -// that no subsequent upcalls will be made, this is not necessary. -// -func handleDestroyRequest(request *fuse.DestroyRequest) { - _ = atomic.AddUint64(&globals.metrics.FUSE_DestroyRequest_calls, 1) - - request.Respond() -} - -func handleExchangeDataRequest(request *fuse.ExchangeDataRequest) { - _ = atomic.AddUint64(&globals.metrics.FUSE_ExchangeDataRequest_calls, 1) - - request.RespondError(fuse.ENOTSUP) -} - -// handleFlushRequest is called to sync/flush a previously opened FileInode or -// DirInode at time of close. This is not to be confused with handleFsyncRequest -// that is triggered at any time prior to close. -// -func handleFlushRequest(request *fuse.FlushRequest) { - var ( - fileInode *fileInodeStruct - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_FlushRequest_calls, 1) - - fileInode = referenceFileInode(inode.InodeNumber(request.Header.Node)) - fileInode.doFlushIfNecessary() - fileInode.dereference() - - request.Respond() -} - -// handleForgetRequest provides a "hint" to no longer cache info about an Inode. -// As the InodeCache is managed with its own eviction logic, this becomes a no-op. -// -func handleForgetRequest(request *fuse.ForgetRequest) { - _ = atomic.AddUint64(&globals.metrics.FUSE_ForgetRequest_calls, 1) - - request.Respond() -} - -// handleFsyncRequest is called to sync/flush a previously opened FileInode or -// DirInode. This is not to be confused with handleFlushRequest that is actually -// the operation performced at time of close. -// -func handleFsyncRequest(request *fuse.FsyncRequest) { - var ( - fileInode *fileInodeStruct - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_FsyncRequest_calls, 1) - - fileInode = referenceFileInode(inode.InodeNumber(request.Header.Node)) - fileInode.doFlushIfNecessary() - fileInode.dereference() - - request.Respond() -} - -func getattrRequestHelper(node fuse.NodeID) (attr *fuse.Attr, err error) { - var ( - chunkedPutContext *chunkedPutContextStruct - chunkedPutContextElement *list.Element - fileInode *fileInodeStruct - getStatRequest *jrpcfs.GetStatRequest - mode os.FileMode - ok bool - statStruct *jrpcfs.StatStruct - ) - - getStatRequest = &jrpcfs.GetStatRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(node), - }, - } - - statStruct = &jrpcfs.StatStruct{} - - err = doJRPCRequest("Server.RpcGetStat", getStatRequest, statStruct) - if nil != err { - return - } - - switch inode.InodeMode(statStruct.FileMode) & inode.PosixModeType { - case inode.PosixModeDir: - mode = os.ModeDir | os.FileMode(statStruct.FileMode&uint32(inode.PosixModePerm)) - case inode.PosixModeFile: - mode = os.FileMode(statStruct.FileMode & uint32(inode.PosixModePerm)) - - // Potentially need to override statStruct.Size if being locally modified - - globals.Lock() - fileInode, ok = globals.fileInodeMap[inode.InodeNumber(node)] - if ok { - if 0 < fileInode.chunkedPutList.Len() { - chunkedPutContextElement = fileInode.chunkedPutList.Back() - chunkedPutContext = chunkedPutContextElement.Value.(*chunkedPutContextStruct) - statStruct.Size = chunkedPutContext.fileSize - } - } - globals.Unlock() - case inode.PosixModeSymlink: - mode = os.ModeSymlink | os.FileMode(statStruct.FileMode&uint32(inode.PosixModePerm)) - default: - logFatalf("Server.RpcGetStat returned unrecognized inode.InodeMode: 0x%08X", statStruct.FileMode) - } - - attr = &fuse.Attr{ - Valid: globals.config.AttrDuration, - Inode: uint64(node), - Size: statStruct.Size, - Blocks: statStruct.Size / globals.config.AttrBlockSize, - Atime: time.Unix(0, int64(statStruct.ATimeNs)), - Mtime: time.Unix(0, int64(statStruct.MTimeNs)), - Ctime: time.Unix(0, int64(statStruct.CTimeNs)), - Crtime: time.Unix(0, int64(statStruct.CRTimeNs)), - Mode: mode, - Nlink: uint32(statStruct.NumLinks), - Uid: statStruct.UserID, - Gid: statStruct.GroupID, - Rdev: uint32(0), - Flags: uint32(0), - BlockSize: uint32(globals.config.AttrBlockSize), - } - - return -} - -func handleGetattrRequest(request *fuse.GetattrRequest) { - var ( - attr *fuse.Attr - err error - response *fuse.GetattrResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_GetattrRequest_calls, 1) - - attr, err = getattrRequestHelper(request.Header.Node) - if nil != err { - request.RespondError(fuse.ENOENT) - return - } - - response = &fuse.GetattrResponse{ - Attr: *attr, - } - - request.Respond(response) -} - -func handleGetxattrRequest(request *fuse.GetxattrRequest) { - var ( - err error - getXAttrReply *jrpcfs.GetXAttrReply - getXAttrRequest *jrpcfs.GetXAttrRequest - response *fuse.GetxattrResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_GetxattrRequest_calls, 1) - - if (0 == request.Size) && (0 != request.Position) { - request.RespondError(fuse.ERANGE) - return - } - - getXAttrRequest = &jrpcfs.GetXAttrRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - AttrName: request.Name, - } - - getXAttrReply = &jrpcfs.GetXAttrReply{} - - err = doJRPCRequest("Server.RpcGetXAttr", getXAttrRequest, getXAttrReply) - if nil != err { - request.RespondError(fuseMissingXAttrErrno) - return - } - - if 0 == request.Size { - response = &fuse.GetxattrResponse{ - Xattr: getXAttrReply.AttrValue, - } - } else { - if int(request.Position) >= len(getXAttrReply.AttrValue) { - response = &fuse.GetxattrResponse{ - Xattr: make([]byte, 0), - } - } else if int(request.Position+request.Size) < len(getXAttrReply.AttrValue) { - response = &fuse.GetxattrResponse{ - Xattr: getXAttrReply.AttrValue[request.Position:(request.Position + request.Size)], - } - } else { - response = &fuse.GetxattrResponse{ - Xattr: getXAttrReply.AttrValue[request.Position:], - } - } - } - - request.Respond(response) -} - -func handleInitRequest(request *fuse.InitRequest) { - _ = atomic.AddUint64(&globals.metrics.FUSE_InitRequest_calls, 1) - - logFatalf("handleInitRequest() should not have been called... fuse.Mount() supposedly took care of it") -} - -func handleInterruptRequest(request *fuse.InterruptRequest) { - _ = atomic.AddUint64(&globals.metrics.FUSE_InterruptRequest_calls, 1) - - request.RespondError(fuse.ENOTSUP) -} - -func handleLinkRequest(request *fuse.LinkRequest) { - var ( - err error - linkReply *jrpcfs.Reply - linkRequest *jrpcfs.LinkRequest - response *fuse.LookupResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_LinkRequest_calls, 1) - - linkRequest = &jrpcfs.LinkRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - Basename: request.NewName, - TargetInodeNumber: int64(request.OldNode), - } - - linkReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcLink", linkRequest, linkReply) - if nil != err { - request.RespondError(err) - return - } - - response, err = lookupRequestHelper(request.Node, request.NewName) - if nil != err { - request.RespondError(err) - return - } - - request.Respond(response) -} - -// handleListxattrRequest makes the assumption that the Position parameter -// refers to the byte offset from the beginning of the first byte of the -// first attrName and that each attrName is followed by an ASCII NULL. -// As a consequence, it seems reasonable that attrName's are likely all -// ASCII characters. Alas, this does not explain the meaning of a value -// for Position that does not land at the beginning of an attrName. -// -func handleListxattrRequest(request *fuse.ListxattrRequest) { - var ( - attrName string - attrNamesBuf []byte - err error - listXAttrReply *jrpcfs.ListXAttrReply - listXAttrRequest *jrpcfs.ListXAttrRequest - response *fuse.ListxattrResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_ListxattrRequest_calls, 1) - - if (0 == request.Size) && (0 != request.Position) { - request.RespondError(fuse.ERANGE) - return - } - - listXAttrRequest = &jrpcfs.ListXAttrRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - } - - listXAttrReply = &jrpcfs.ListXAttrReply{} - - err = doJRPCRequest("Server.RpcListXAttr", listXAttrRequest, listXAttrReply) - - if nil == err { - attrNamesBuf = make([]byte, 0) - - for _, attrName = range listXAttrReply.AttrNames { - attrNamesBuf = append(attrNamesBuf, []byte(attrName)...) - attrNamesBuf = append(attrNamesBuf, byte(0)) - } - - if 0 == request.Size { - response = &fuse.ListxattrResponse{ - Xattr: attrNamesBuf, - } - } else { - if int(request.Position) >= len(attrNamesBuf) { - response = &fuse.ListxattrResponse{ - Xattr: make([]byte, 0), - } - } else if int(request.Position+request.Size) < len(attrNamesBuf) { - response = &fuse.ListxattrResponse{ - Xattr: attrNamesBuf[request.Position:(request.Position + request.Size)], - } - } else { - response = &fuse.ListxattrResponse{ - Xattr: attrNamesBuf[request.Position:], - } - } - } - } else { - response = &fuse.ListxattrResponse{ - Xattr: make([]byte, 0), - } - } - - request.Respond(response) -} - -func lookupRequestHelper(node fuse.NodeID, name string) (response *fuse.LookupResponse, err error) { - var ( - inodeReply *jrpcfs.InodeReply - getStatRequest *jrpcfs.GetStatRequest - lookupRequest *jrpcfs.LookupRequest - mode os.FileMode - statStruct *jrpcfs.StatStruct - ) - - lookupRequest = &jrpcfs.LookupRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(node), - }, - Basename: name, - } - - inodeReply = &jrpcfs.InodeReply{} - - err = doJRPCRequest("Server.RpcLookup", lookupRequest, inodeReply) - if nil != err { - return - } - - getStatRequest = &jrpcfs.GetStatRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: inodeReply.InodeNumber, - }, - } - - statStruct = &jrpcfs.StatStruct{} - - err = doJRPCRequest("Server.RpcGetStat", getStatRequest, statStruct) - if nil != err { - return - } - - switch inode.InodeMode(statStruct.FileMode) & inode.PosixModeType { - case inode.PosixModeDir: - mode = os.ModeDir | os.FileMode(statStruct.FileMode&uint32(inode.PosixModePerm)) - case inode.PosixModeFile: - mode = os.FileMode(statStruct.FileMode & uint32(inode.PosixModePerm)) - case inode.PosixModeSymlink: - mode = os.ModeSymlink | os.FileMode(statStruct.FileMode&uint32(inode.PosixModePerm)) - default: - logFatalf("Server.RpcGetStat returned unrecognized inode.InodeMode: 0x%08X", statStruct.FileMode) - } - - response = &fuse.LookupResponse{ - Node: fuse.NodeID(inodeReply.InodeNumber), - Generation: 0, - EntryValid: globals.config.LookupEntryDuration, - Attr: fuse.Attr{ - Valid: globals.config.AttrDuration, - Inode: uint64(inodeReply.InodeNumber), - Size: statStruct.Size, - Blocks: statStruct.Size / globals.config.AttrBlockSize, - Atime: time.Unix(0, int64(statStruct.ATimeNs)), - Mtime: time.Unix(0, int64(statStruct.MTimeNs)), - Ctime: time.Unix(0, int64(statStruct.CTimeNs)), - Crtime: time.Unix(0, int64(statStruct.CRTimeNs)), - Mode: mode, - Nlink: uint32(statStruct.NumLinks), - Uid: statStruct.UserID, - Gid: statStruct.GroupID, - Rdev: uint32(0), - Flags: uint32(0), - BlockSize: uint32(globals.config.AttrBlockSize), - }, - } - - return -} - -func handleLookupRequest(request *fuse.LookupRequest) { - var ( - err error - response *fuse.LookupResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_LookupRequest_calls, 1) - - response, err = lookupRequestHelper(request.Node, request.Name) - if nil != err { - request.RespondError(fuse.ENOENT) - return - } - - request.Respond(response) -} - -func handleMkdirRequest(request *fuse.MkdirRequest) { - var ( - embeddedLookupResponse *fuse.LookupResponse - err error - mkdirReply *jrpcfs.Reply - mkdirRequest *jrpcfs.MkdirRequest - response *fuse.MkdirResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_MkdirRequest_calls, 1) - - mkdirRequest = &jrpcfs.MkdirRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - Basename: request.Name, - UserID: int32(request.Uid), - GroupID: int32(request.Gid), - FileMode: uint32(request.Mode & os.ModePerm), - } - - mkdirReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcMkdir", mkdirRequest, mkdirReply) - if nil != err { - request.RespondError(err) - return - } - - embeddedLookupResponse, err = lookupRequestHelper(request.Node, request.Name) - if nil != err { - request.RespondError(err) - return - } - - response = &fuse.MkdirResponse{ - LookupResponse: *embeddedLookupResponse, - } - - request.Respond(response) -} - -func handleMknodRequest(request *fuse.MknodRequest) { - _ = atomic.AddUint64(&globals.metrics.FUSE_MknodRequest_calls, 1) - - request.RespondError(fuse.ENOTSUP) -} - -func openRequestHelper(node fuse.NodeID, dir bool) (response *fuse.OpenResponse) { - var ( - flags fuse.OpenResponseFlags - handleID fuse.HandleID - ) - - globals.Lock() - - handleID = globals.lastHandleID + 1 - globals.lastHandleID = handleID - - globals.handleTable[handleID] = &handleStruct{ - InodeNumber: inode.InodeNumber(node), - prevDirEntLocation: -1, - } - - globals.Unlock() - - if dir { - flags = fuse.OpenResponseFlags(0) - } else { - flags = fuse.OpenDirectIO - } - - response = &fuse.OpenResponse{ - Handle: handleID, - Flags: flags, - } - - return -} - -func handleOpenRequest(request *fuse.OpenRequest) { - var ( - response *fuse.OpenResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_OpenRequest_calls, 1) - - response = openRequestHelper(request.Node, request.Dir) - - request.Respond(response) -} - -func handleReadRequest(request *fuse.ReadRequest) { - var ( - dirent fuse.Dirent - direntType fuse.DirentType - dirEntry jrpcfs.DirEntry - err error - handle *handleStruct - ok bool - readdirByLocRequest *jrpcfs.ReaddirByLocRequest - readdirReply *jrpcfs.ReaddirReply - response *fuse.ReadResponse - responseDataLenBeforeAppend int - ) - - if request.Dir { - _ = atomic.AddUint64(&globals.metrics.FUSE_ReadRequestDirInodeCase_calls, 1) - - handle, ok = globals.handleTable[request.Handle] - if !ok { - request.RespondError(fuse.ESTALE) - return - } - - if 0 == request.Offset { - handle.prevDirEntLocation = -1 - } - - readdirByLocRequest = &jrpcfs.ReaddirByLocRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - MaxEntries: globals.config.ReaddirMaxEntries, - PrevDirEntLocation: handle.prevDirEntLocation, - } - - readdirReply = &jrpcfs.ReaddirReply{} - - err = doJRPCRequest("Server.RpcReaddirByLoc", readdirByLocRequest, readdirReply) - if nil != err { - request.RespondError(err) - return - } - - response = &fuse.ReadResponse{ - Data: make([]byte, 0, request.Size), - } - - for _, dirEntry = range readdirReply.DirEnts { - switch inode.InodeType(dirEntry.FileType) { - case inode.DirType: - direntType = fuse.DT_Dir - case inode.FileType: - direntType = fuse.DT_File - case inode.SymlinkType: - direntType = fuse.DT_Link - default: - direntType = fuse.DT_Unknown - } - - dirent.Inode = uint64(dirEntry.InodeNumber) - dirent.Type = direntType - dirent.Name = dirEntry.Basename - - responseDataLenBeforeAppend = len(response.Data) - - response.Data = fuse.AppendDirent(response.Data, dirent) - if len(response.Data) > request.Size { - response.Data = response.Data[:responseDataLenBeforeAppend] - break - } - - handle.prevDirEntLocation++ - } - - request.Respond(response) - } else { - handleReadRequestFileInodeCase(request) // See io.go - } -} - -func handleReadlinkRequest(request *fuse.ReadlinkRequest) { - var ( - err error - readSymlinkReply *jrpcfs.ReadSymlinkReply - readSymlinkRequest *jrpcfs.ReadSymlinkRequest - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_ReadlinkRequest_calls, 1) - - readSymlinkRequest = &jrpcfs.ReadSymlinkRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - } - - readSymlinkReply = &jrpcfs.ReadSymlinkReply{} - - err = doJRPCRequest("Server.RpcReadSymlink", readSymlinkRequest, readSymlinkReply) - if nil != err { - request.RespondError(err) - return - } - - request.Respond(readSymlinkReply.Target) -} - -func handleReleaseRequest(request *fuse.ReleaseRequest) { - var ( - fileInode *fileInodeStruct - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_ReleaseRequest_calls, 1) - - if !request.Dir && (0 != (request.ReleaseFlags & fuse.ReleaseFlush)) { - fileInode = referenceFileInode(inode.InodeNumber(request.Header.Node)) - fileInode.doFlushIfNecessary() - fileInode.dereference() - } - - globals.Lock() - - delete(globals.handleTable, request.Handle) - - globals.Unlock() - - request.Respond() -} - -func handleRemoveRequest(request *fuse.RemoveRequest) { - var ( - err error - unlinkReply *jrpcfs.Reply - unlinkRequest *jrpcfs.UnlinkRequest - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_RemoveRequest_calls, 1) - - unlinkRequest = &jrpcfs.UnlinkRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - Basename: request.Name, - } - - unlinkReply = &jrpcfs.Reply{} - - if request.Dir { - err = doJRPCRequest("Server.RpcRmdir", unlinkRequest, unlinkReply) - } else { - err = doJRPCRequest("Server.RpcUnlink", unlinkRequest, unlinkReply) - } - - if nil != err { - request.RespondError(err) - return - } - - request.Respond() -} - -func handleRemovexattrRequest(request *fuse.RemovexattrRequest) { - var ( - err error - removeXAttrReply *jrpcfs.Reply - removeXAttrRequest *jrpcfs.RemoveXAttrRequest - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_RemovexattrRequest_calls, 1) - - removeXAttrRequest = &jrpcfs.RemoveXAttrRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - AttrName: request.Name, - } - - removeXAttrReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcRemoveXAttr", removeXAttrRequest, removeXAttrReply) - if nil != err { - request.RespondError(fuseMissingXAttrErrno) - return - } - - request.Respond() -} - -func handleRenameRequest(request *fuse.RenameRequest) { - var ( - err error - renameReply *jrpcfs.Reply - renameRequest *jrpcfs.RenameRequest - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_RenameRequest_calls, 1) - - renameRequest = &jrpcfs.RenameRequest{ - MountID: globals.mountID, - SrcDirInodeNumber: int64(request.Header.Node), - SrcBasename: request.OldName, - DstDirInodeNumber: int64(request.NewDir), - DstBasename: request.NewName, - } - - renameReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcRename", renameRequest, renameReply) - if nil != err { - request.RespondError(err) - return - } - - request.Respond() -} - -func handleSetattrRequest(request *fuse.SetattrRequest) { - var ( - attr *fuse.Attr - chmodReply *jrpcfs.Reply - chmodRequest *jrpcfs.ChmodRequest - chownReply *jrpcfs.Reply - chownRequest *jrpcfs.ChownRequest - chunkedPutContext *chunkedPutContextStruct - chunkedPutContextElement *list.Element - err error - fileInode *fileInodeStruct - ok bool - newAtime *time.Time - newMtime *time.Time - resizeReply *jrpcfs.Reply - resizeRequest *jrpcfs.ResizeRequest - response *fuse.SetattrResponse - setTimeReply *jrpcfs.Reply - setTimeRequest *jrpcfs.SetTimeRequest - settingAtime bool - settingAtimeAndOrMtime bool - settingGID bool - settingMode bool - settingMtime bool - settingSize bool - settingUID bool - timeNow time.Time - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_SetattrRequest_calls, 1) - - // TODO: Verify it is ok to accept but ignore fuse.SetattrHandle in request.Valid - // TODO: Verify it is ok to accept but ignore fuse.SetattrLockOwner in request.Valid - - if (request.Valid & (fuse.SetattrMode | fuse.SetattrUid | fuse.SetattrGid | fuse.SetattrSize | fuse.SetattrAtime | fuse.SetattrMtime | fuse.SetattrHandle | fuse.SetattrAtimeNow | fuse.SetattrMtimeNow | fuse.SetattrLockOwner)) != request.Valid { - // request.Valid contains non-supported bits - request.RespondError(fuse.ENOTSUP) - return - } - - settingMode = (0 != (request.Valid & fuse.SetattrMode)) - - settingUID = (0 != (request.Valid & fuse.SetattrUid)) - settingGID = (0 != (request.Valid & fuse.SetattrGid)) - - settingSize = (0 != (request.Valid & fuse.SetattrSize)) - - settingAtime = (0 != (request.Valid & fuse.SetattrAtime)) || (0 != (request.Valid & fuse.SetattrAtimeNow)) - settingMtime = (0 != (request.Valid & fuse.SetattrMtime)) || (0 != (request.Valid & fuse.SetattrMtimeNow)) - - settingAtimeAndOrMtime = settingAtime || settingMtime - - if settingMode { - chmodRequest = &jrpcfs.ChmodRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - FileMode: uint32(request.Mode & os.ModePerm), - } - - chmodReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcChmod", chmodRequest, chmodReply) - if nil != err { - request.RespondError(err) - return - } - } - - if settingUID || settingGID { - chownRequest = &jrpcfs.ChownRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - } - - if settingUID { - chownRequest.UserID = int32(request.Uid) - } else { - chownRequest.UserID = -1 - } - - if settingGID { - chownRequest.GroupID = int32(request.Gid) - } else { - chownRequest.GroupID = -1 - } - - chownReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcChown", chownRequest, chownReply) - if nil != err { - request.RespondError(err) - return - } - } - - if settingSize { - globals.Lock() - - fileInode, ok = globals.fileInodeMap[inode.InodeNumber(request.Header.Node)] - - if ok { - if fileInode.extentMapFileSize > request.Size { - fileInode.extentMapFileSize = request.Size - } - - pruneExtentMap(fileInode.extentMap, request.Size) - - chunkedPutContextElement = fileInode.chunkedPutList.Front() - - for nil != chunkedPutContextElement { - chunkedPutContext, ok = chunkedPutContextElement.Value.(*chunkedPutContextStruct) - if !ok { - logFatalf("chunkedPutContextElement.Value.(*chunkedPutContextStruct) returned !ok") - } - - if chunkedPutContext.fileSize > request.Size { - chunkedPutContext.fileSize = request.Size - } - - pruneExtentMap(chunkedPutContext.extentMap, request.Size) - - chunkedPutContextElement = chunkedPutContextElement.Next() - } - } - - globals.Unlock() - - resizeRequest = &jrpcfs.ResizeRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - NewSize: request.Size, - } - - resizeReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcResize", resizeRequest, resizeReply) - if nil != err { - request.RespondError(err) - return - } - } - - if settingAtimeAndOrMtime { - timeNow = time.Now() - - setTimeRequest = &jrpcfs.SetTimeRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - StatStruct: jrpcfs.StatStruct{ - MTimeNs: uint64(0), // Updated below if settingMtime - ATimeNs: uint64(0), // Updated below if settingAtime - }, - } - - if settingMtime { - if 0 != (request.Valid & fuse.SetattrMtimeNow) { - newMtime = &timeNow - } else { - newMtime = &request.Mtime - } - setTimeRequest.MTimeNs = uint64(newMtime.UnixNano()) - } - if settingAtime { - if 0 != (request.Valid & fuse.SetattrAtimeNow) { - newAtime = &timeNow - } else { - newAtime = &request.Atime - } - setTimeRequest.ATimeNs = uint64(newAtime.UnixNano()) - } - - setTimeReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcSetTime", setTimeRequest, setTimeReply) - if nil != err { - request.RespondError(err) - return - } - } - - attr, err = getattrRequestHelper(request.Header.Node) - if nil != err { - request.RespondError(err) - return - } - - response = &fuse.SetattrResponse{ - Attr: *attr, - } - - request.Respond(response) -} - -// handleSetxattrRequest supports creating and modifying an existing Extended Attribute. -// This is a bit limited w.r.t. what FUSE allows, but Bazil FUSE has left support for -// either requiring the Extended Attribute to previously not exist or to entirely replace -// it. In any case, due to the support for modifying an existing Extended Attribute, this -// func performs a Read-Modify-Write sequence. Note, also, that holes created from e.g. -// out-of-order Extended Attribute fragment writes are written as zeroes. Finally, as there -// is no way to discern between a modification starting at Position 0 and an entirely new -// value, we will adopt the convention that a Position 0 request is explicitly a new -// value (and, hence, avoid the Read-Modify-Write sequence). -// -func handleSetxattrRequest(request *fuse.SetxattrRequest) { - var ( - attrValue []byte - err error - getXAttrReply *jrpcfs.GetXAttrReply - getXAttrRequest *jrpcfs.GetXAttrRequest - paddingNeeded int - setXAttrReply *jrpcfs.Reply - setXAttrRequest *jrpcfs.SetXAttrRequest - xattrReplacementByte byte - xattrReplacementIndex int - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_SetxattrRequest_calls, 1) - - if 0 == request.Position { - attrValue = request.Xattr - } else { - getXAttrRequest = &jrpcfs.GetXAttrRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - AttrName: request.Name, - } - - getXAttrReply = &jrpcfs.GetXAttrReply{} - - err = doJRPCRequest("Server.RpcGetXAttr", getXAttrRequest, getXAttrReply) - if nil == err { - attrValue = getXAttrReply.AttrValue - paddingNeeded = int(request.Position) + len(request.Xattr) - len(attrValue) - if 0 < paddingNeeded { - attrValue = append(attrValue, make([]byte, paddingNeeded)...) - } - for xattrReplacementIndex, xattrReplacementByte = range request.Xattr { - attrValue[xattrReplacementIndex+int(request.Position)] = xattrReplacementByte - } - } else { - attrValue = append(make([]byte, request.Position), request.Xattr...) - } - } - - setXAttrRequest = &jrpcfs.SetXAttrRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - AttrName: request.Name, - AttrValue: attrValue, - AttrFlags: fs.SetXAttrCreateOrReplace, - } - - setXAttrReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcSetXAttr", setXAttrRequest, setXAttrReply) - if nil != err { - request.RespondError(fuseMissingXAttrErrno) - return - } - - request.Respond() -} - -func handleStatfsRequest(request *fuse.StatfsRequest) { - var ( - err error - response *fuse.StatfsResponse - statVFS *jrpcfs.StatVFS - statVFSRequest *jrpcfs.StatVFSRequest - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_StatfsRequest_calls, 1) - - statVFSRequest = &jrpcfs.StatVFSRequest{ - MountID: globals.mountID, - } - - statVFS = &jrpcfs.StatVFS{} - - err = doJRPCRequest("Server.RpcStatVFS", statVFSRequest, statVFS) - if nil != err { - request.RespondError(err) - return - } - - response = &fuse.StatfsResponse{ - Blocks: statVFS.TotalBlocks, - Bfree: statVFS.FreeBlocks, - Bavail: statVFS.AvailBlocks, - Files: statVFS.TotalInodes, - Ffree: statVFS.FreeInodes, - Bsize: uint32(statVFS.BlockSize), - Namelen: uint32(statVFS.MaxFilenameLen), - Frsize: uint32(statVFS.FragmentSize), - } - - request.Respond(response) -} - -func handleSymlinkRequest(request *fuse.SymlinkRequest) { - var ( - embeddedLookupResponse *fuse.LookupResponse - err error - symlinkReply *jrpcfs.Reply - symlinkRequest *jrpcfs.SymlinkRequest - response *fuse.SymlinkResponse - ) - - _ = atomic.AddUint64(&globals.metrics.FUSE_SymlinkRequest_calls, 1) - - symlinkRequest = &jrpcfs.SymlinkRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(request.Header.Node), - }, - Basename: request.NewName, - Target: request.Target, - UserID: int32(request.Uid), - GroupID: int32(request.Gid), - } - - symlinkReply = &jrpcfs.Reply{} - - err = doJRPCRequest("Server.RpcSymlink", symlinkRequest, symlinkReply) - if nil != err { - request.RespondError(err) - return - } - - embeddedLookupResponse, err = lookupRequestHelper(request.Node, request.NewName) - if nil != err { - request.RespondError(err) - return - } - - response = &fuse.SymlinkResponse{ - LookupResponse: *embeddedLookupResponse, - } - - request.Respond(response) -} diff --git a/pfsagentd/fuse_darwin.go b/pfsagentd/fuse_darwin.go deleted file mode 100644 index 2578a5d81..000000000 --- a/pfsagentd/fuse_darwin.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import ( - "bazil.org/fuse" -) - -const fuseMissingXAttrErrno = fuse.ENOATTR diff --git a/pfsagentd/fuse_linux.go b/pfsagentd/fuse_linux.go deleted file mode 100644 index fc1c056f8..000000000 --- a/pfsagentd/fuse_linux.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import ( - "bazil.org/fuse" -) - -const fuseMissingXAttrErrno = fuse.ENODATA diff --git a/pfsagentd/globals.go b/pfsagentd/globals.go index 9fec3feb1..57a6d11f4 100644 --- a/pfsagentd/globals.go +++ b/pfsagentd/globals.go @@ -13,6 +13,7 @@ import ( "bazil.org/fuse" + "github.com/swiftstack/fission" "github.com/swiftstack/sortedmap" "github.com/swiftstack/ProxyFS/conf" @@ -44,16 +45,20 @@ type configStruct struct { DirtyFileLimit uint64 MaxFlushSize uint64 MaxFlushTime time.Duration - ReadOnly bool LogFilePath string // Unless starting with '/', relative to $CWD; == "" means disabled LogToConsole bool TraceEnabled bool HTTPServerIPAddr string HTTPServerTCPPort uint16 + ReadDirPlusEnabled bool + XAttrEnabled bool + EntryDuration time.Duration AttrDuration time.Duration AttrBlockSize uint64 - LookupEntryDuration time.Duration ReaddirMaxEntries uint64 + FUSEMaxBackground uint16 + FUSECongestionThreshhold uint16 + FUSEMaxWrite uint32 } type fileInodeLockRequestStruct struct { @@ -148,17 +153,21 @@ type chunkedPutContextStruct struct { pos int // ObjectOffset just after last sent chunk sendChan chan bool // Wake-up sendDaemon with a new chunk or to flush wakeChan chan bool // Wake-up Read callback to respond with a chunk and/or return EOF + inRead bool // Set when in Read() as a hint to Close() to help Read() cleanly exit flushRequested bool // Set to remember that a flush has been requested of *chunkedPutContextStruct.Read() } const ( chunkedPutContextSendChanBufferSize = 16 chunkedPutContextWakeChanBufferSize = 16 + + chunkedPutContextExitReadPollingRate = time.Millisecond ) type fileInodeStruct struct { sync.WaitGroup // Used to await completion of all chunkedPutContext's inode.InodeNumber + cachedStat *jrpcfs.StatStruct references uint64 leaseState fileInodeLeaseStateType sharedLockHolders *list.List // Elements are fileInodeLockRequestStructs.holdersElement's @@ -186,11 +195,7 @@ type fileInodeStruct struct { dirtyListElement *list.Element // Element on globals.fileInodeDirtyList (or nil) } -type handleStruct struct { - inode.InodeNumber - prevDirEntLocation int64 // -1 is used if fuse.ReadRequest on DirInode specifies Offset == 0 - // Otherwise, just assumes caller wants to keep going... -} +type fhSetType map[uint64]struct{} type logSegmentCacheElementStateType uint8 @@ -220,41 +225,63 @@ type logSegmentCacheElementStruct struct { // In order to utilize Go Reflection, these field names must be capitalized (i.e. Global). type metricsStruct struct { - FUSE_AccessRequest_calls uint64 - FUSE_CreateRequest_calls uint64 - FUSE_DestroyRequest_calls uint64 - FUSE_ExchangeDataRequest_calls uint64 - FUSE_FlushRequest_calls uint64 - FUSE_ForgetRequest_calls uint64 - FUSE_FsyncRequest_calls uint64 - FUSE_GetattrRequest_calls uint64 - FUSE_GetxattrRequest_calls uint64 - FUSE_InitRequest_calls uint64 - FUSE_InterruptRequest_calls uint64 - FUSE_LinkRequest_calls uint64 - FUSE_ListxattrRequest_calls uint64 - FUSE_LookupRequest_calls uint64 - FUSE_MkdirRequest_calls uint64 - FUSE_MknodRequest_calls uint64 - FUSE_OpenRequest_calls uint64 - FUSE_ReadlinkRequest_calls uint64 - FUSE_ReleaseRequest_calls uint64 - FUSE_RemoveRequest_calls uint64 - FUSE_RemovexattrRequest_calls uint64 - FUSE_RenameRequest_calls uint64 - FUSE_SetattrRequest_calls uint64 - FUSE_SetxattrRequest_calls uint64 - FUSE_StatfsRequest_calls uint64 - FUSE_SymlinkRequest_calls uint64 - - FUSE_UnknownRequest_calls uint64 - - FUSE_ReadRequestDirInodeCase_calls uint64 - FUSE_ReadRequestFileInodeCase_calls uint64 - FUSE_WriteRequest_calls uint64 - - FUSE_ReadRequestFileInodeCase_bytes uint64 - FUSE_WriteRequest_bytes uint64 + FUSE_DoLookup_calls uint64 + FUSE_DoForget_calls uint64 + FUSE_DoGetAttr_calls uint64 + FUSE_DoSetAttr_calls uint64 + FUSE_DoReadLink_calls uint64 + FUSE_DoSymLink_calls uint64 + FUSE_DoMkNod_calls uint64 + FUSE_DoMkDir_calls uint64 + FUSE_DoUnlink_calls uint64 + FUSE_DoRmDir_calls uint64 + FUSE_DoRename_calls uint64 + FUSE_DoLink_calls uint64 + FUSE_DoOpen_calls uint64 + FUSE_DoRead_calls uint64 + FUSE_DoWrite_calls uint64 + FUSE_DoStatFS_calls uint64 + FUSE_DoRelease_calls uint64 + FUSE_DoFSync_calls uint64 + FUSE_DoSetXAttr_calls uint64 + FUSE_DoGetXAttr_calls uint64 + FUSE_DoListXAttr_calls uint64 + FUSE_DoRemoveXAttr_calls uint64 + FUSE_DoFlush_calls uint64 + FUSE_DoInit_calls uint64 + FUSE_DoOpenDir_calls uint64 + FUSE_DoReadDir_calls uint64 + FUSE_DoReleaseDir_calls uint64 + FUSE_DoFSyncDir_calls uint64 + FUSE_DoGetLK_calls uint64 + FUSE_DoSetLK_calls uint64 + FUSE_DoSetLKW_calls uint64 + FUSE_DoAccess_calls uint64 + FUSE_DoCreate_calls uint64 + FUSE_DoInterrupt_calls uint64 + FUSE_DoBMap_calls uint64 + FUSE_DoDestroy_calls uint64 + FUSE_DoPoll_calls uint64 + FUSE_DoBatchForget_calls uint64 + FUSE_DoFAllocate_calls uint64 + FUSE_DoReadDirPlus_calls uint64 + FUSE_DoRename2_calls uint64 + FUSE_DoLSeek_calls uint64 + + FUSE_DoGetAttr_cache_hits uint64 + FUSE_DoGetAttr_cache_misses uint64 + + FUSE_DoRead_bytes uint64 + FUSE_DoWrite_bytes uint64 + + FUSE_DoReadDir_entries uint64 + FUSE_DoReadDirPlus_entries uint64 + + FUSE_DoSetXAttr_bytes uint64 + FUSE_DoGetXAttr_bytes uint64 + FUSE_DoListXAttr_names uint64 + + FUSE_DoBatchForget_nodes uint64 ReadCacheHits uint64 ReadCacheMisses uint64 @@ -268,12 +295,17 @@ type metricsStruct struct { HTTPRequestRetryLimitExceededCount uint64 HTTPRequestsRequiringReauthorization uint64 HTTPRequestRetries uint64 + HTTPRequestsInFlight uint64 } type globalsStruct struct { sync.Mutex config configStruct logFile *os.File // == nil if configStruct.LogFilePath == "" + entryValidSec uint64 + entryValidNSec uint32 + attrValidSec uint64 + attrValidNSec uint32 httpServer *http.Server httpServerWG sync.WaitGroup httpClient *http.Client @@ -283,16 +315,19 @@ type globalsStruct struct { swiftAccountURL string // swiftStorageURL with AccountName forced to config.SwiftAccountName mountID jrpcfs.MountIDAsString rootDirInodeNumber uint64 + fissionErrChan chan error + fissionVolume fission.Volume fuseConn *fuse.Conn jrpcLastID uint64 fileInodeMap map[inode.InodeNumber]*fileInodeStruct fileInodeDirtyList *list.List // LRU of fileInode's with non-empty chunkedPutList leaseRequestChan chan *fileInodeLeaseRequestStruct - unleasedFileInodeCacheLRU *list.List // Front() is oldest fileInodeStruct.cacheLRUElement - sharedLeaseFileInodeCacheLRU *list.List // Front() is oldest fileInodeStruct.cacheLRUElement - exclusiveLeaseFileInodeCacheLRU *list.List // Front() is oldest fileInodeStruct.cacheLRUElement - lastHandleID fuse.HandleID - handleTable map[fuse.HandleID]*handleStruct + unleasedFileInodeCacheLRU *list.List // Front() is oldest fileInodeStruct.cacheLRUElement + sharedLeaseFileInodeCacheLRU *list.List // Front() is oldest fileInodeStruct.cacheLRUElement + exclusiveLeaseFileInodeCacheLRU *list.List // Front() is oldest fileInodeStruct.cacheLRUElement + fhToInodeNumberMap map[uint64]uint64 // Key == FH; Value == InodeNumber + inodeNumberToFHMap map[uint64]fhSetType // Key == InodeNumber; Value == set of FH's + lastFH uint64 // Valid FH's start at 1 logSegmentCacheMap map[logSegmentCacheElementKeyStruct]*logSegmentCacheElementStruct logSegmentCacheLRU *list.List // Front() is oldest logSegmentCacheElementStruct.cacheLRUElement metrics *metricsStruct @@ -433,11 +468,6 @@ func initializeGlobals(confMap conf.ConfMap) { logFatal(err) } - globals.config.ReadOnly, err = confMap.FetchOptionValueBool("Agent", "ReadOnly") - if nil != err { - logFatal(err) - } - err = confMap.VerifyOptionValueIsEmpty("Agent", "LogFilePath") if nil == err { globals.config.LogFilePath = "" @@ -472,6 +502,21 @@ func initializeGlobals(confMap conf.ConfMap) { logFatal(err) } + globals.config.ReadDirPlusEnabled, err = confMap.FetchOptionValueBool("Agent", "ReadDirPlusEnabled") + if nil != err { + logFatal(err) + } + + globals.config.XAttrEnabled, err = confMap.FetchOptionValueBool("Agent", "XAttrEnabled") + if nil != err { + logFatal(err) + } + + globals.config.EntryDuration, err = confMap.FetchOptionValueDuration("Agent", "EntryDuration") + if nil != err { + logFatal(err) + } + globals.config.AttrDuration, err = confMap.FetchOptionValueDuration("Agent", "AttrDuration") if nil != err { logFatal(err) @@ -485,12 +530,22 @@ func initializeGlobals(confMap conf.ConfMap) { logFatalf("AttrBlockSize must be non-zero and fit in a uint32") } - globals.config.LookupEntryDuration, err = confMap.FetchOptionValueDuration("Agent", "LookupEntryDuration") + globals.config.ReaddirMaxEntries, err = confMap.FetchOptionValueUint64("Agent", "ReaddirMaxEntries") if nil != err { logFatal(err) } - globals.config.ReaddirMaxEntries, err = confMap.FetchOptionValueUint64("Agent", "ReaddirMaxEntries") + globals.config.FUSEMaxBackground, err = confMap.FetchOptionValueUint16("Agent", "FUSEMaxBackground") + if nil != err { + logFatal(err) + } + + globals.config.FUSECongestionThreshhold, err = confMap.FetchOptionValueUint16("Agent", "FUSECongestionThreshhold") + if nil != err { + logFatal(err) + } + + globals.config.FUSEMaxWrite, err = confMap.FetchOptionValueUint32("Agent", "FUSEMaxWrite") if nil != err { logFatal(err) } @@ -499,6 +554,9 @@ func initializeGlobals(confMap conf.ConfMap) { logInfof("\n%s", configJSONified) + globals.entryValidSec, globals.entryValidNSec = nsToUnixTime(uint64(globals.config.EntryDuration)) + globals.attrValidSec, globals.attrValidNSec = nsToUnixTime(uint64(globals.config.AttrDuration)) + defaultTransport, ok = http.DefaultTransport.(*http.Transport) if !ok { log.Fatalf("http.DefaultTransport not a *http.Transport") @@ -544,6 +602,8 @@ func initializeGlobals(confMap conf.ConfMap) { updateAuthTokenAndAccountURL() + globals.fissionErrChan = make(chan error) + globals.jrpcLastID = 1 globals.fileInodeMap = make(map[inode.InodeNumber]*fileInodeStruct) @@ -558,8 +618,10 @@ func initializeGlobals(confMap conf.ConfMap) { globals.sharedLeaseFileInodeCacheLRU = list.New() globals.exclusiveLeaseFileInodeCacheLRU = list.New() - globals.lastHandleID = 0 - globals.handleTable = make(map[fuse.HandleID]*handleStruct) + globals.fhToInodeNumberMap = make(map[uint64]uint64) + globals.inodeNumberToFHMap = make(map[uint64]fhSetType) + + globals.lastFH = 0 globals.logSegmentCacheMap = make(map[logSegmentCacheElementKeyStruct]*logSegmentCacheElementStruct) globals.logSegmentCacheLRU = list.New() @@ -582,12 +644,18 @@ func uninitializeGlobals() { leaseRequest.Wait() globals.logFile = nil + globals.entryValidSec = 0 + globals.entryValidNSec = 0 + globals.attrValidSec = 0 + globals.attrValidNSec = 0 globals.httpServer = nil globals.httpClient = nil globals.retryDelay = nil globals.swiftAuthWaitGroup = nil globals.swiftAuthToken = "" globals.swiftAccountURL = "" + globals.fissionErrChan = nil + globals.fissionVolume = nil globals.fuseConn = nil globals.jrpcLastID = 0 globals.fileInodeMap = nil @@ -596,8 +664,9 @@ func uninitializeGlobals() { globals.unleasedFileInodeCacheLRU = nil globals.sharedLeaseFileInodeCacheLRU = nil globals.exclusiveLeaseFileInodeCacheLRU = nil - globals.lastHandleID = 0 - globals.handleTable = nil + globals.fhToInodeNumberMap = nil + globals.inodeNumberToFHMap = nil + globals.lastFH = 0 globals.logSegmentCacheMap = nil globals.logSegmentCacheLRU = nil globals.metrics = nil diff --git a/pfsagentd/http_server.go b/pfsagentd/http_server.go index f2490c3e7..f74a1506d 100644 --- a/pfsagentd/http_server.go +++ b/pfsagentd/http_server.go @@ -6,6 +6,7 @@ import ( "log" "net" "net/http" + "net/http/pprof" "reflect" "runtime" "strconv" @@ -75,6 +76,16 @@ func serveGet(responseWriter http.ResponseWriter, request *http.Request) { path = strings.TrimRight(request.URL.Path, "/") switch path { + case "/debug/pprof": + pprof.Index(responseWriter, request) + case "/debug/pprof/cmdline": + pprof.Cmdline(responseWriter, request) + case "/debug/pprof/profile": + pprof.Profile(responseWriter, request) + case "/debug/pprof/symbol": + pprof.Symbol(responseWriter, request) + case "/debug/pprof/trace": + pprof.Trace(responseWriter, request) case "/metrics": serveGetOfMetrics(responseWriter, request) case "/version": diff --git a/pfsagentd/inode.go b/pfsagentd/inode.go index fbf07001f..90d2cfa5d 100644 --- a/pfsagentd/inode.go +++ b/pfsagentd/inode.go @@ -60,6 +60,7 @@ func referenceFileInode(inodeNumber inode.InodeNumber) (fileInode *fileInodeStru } else { fileInode = &fileInodeStruct{ InodeNumber: inodeNumber, + cachedStat: nil, references: 1, leaseState: fileInodeLeaseStateNone, sharedLockHolders: list.New(), diff --git a/pfsagentd/log.go b/pfsagentd/log.go index f1a3f4984..f78a20134 100644 --- a/pfsagentd/log.go +++ b/pfsagentd/log.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "os" "time" ) @@ -63,3 +64,12 @@ func logf(level string, format string, args ...interface{}) { fmt.Fprintln(os.Stderr, logMsg) } } + +func newLogger() *log.Logger { + return log.New(&globals, "", 0) +} + +func (dummy *globalsStruct) Write(p []byte) (n int, err error) { + logf("FISSION", "%s", string(p[:])) + return 0, nil +} diff --git a/pfsagentd/main.go b/pfsagentd/main.go index 8dc585e69..149ef4d57 100644 --- a/pfsagentd/main.go +++ b/pfsagentd/main.go @@ -62,9 +62,15 @@ func main() { serveHTTP() - // Await any of specified signals - - _ = <-signalChan + // Await any of specified signals or fission exit + + select { + case _ = <-signalChan: + // Normal termination due to one of the above registered signals + case err = <-globals.fissionErrChan: + // Unexpected exit of fission.Volume + logFatalf("unexpected error from package fission: %v", err) + } // Stop serving HTTP diff --git a/pfsagentd/pfsagent.conf b/pfsagentd/pfsagent.conf index b6ae66a60..4624b9cc7 100644 --- a/pfsagentd/pfsagent.conf +++ b/pfsagentd/pfsagent.conf @@ -22,13 +22,17 @@ ExclusiveFileLimit: 100 DirtyFileLimit: 50 MaxFlushSize: 10485760 MaxFlushTime: 10s -ReadOnly: false LogFilePath: /var/log/pfsagentd.log # Unless starting with '/', relative to $CWD; Blank to disable LogToConsole: true TraceEnabled: false HTTPServerIPAddr: 127.0.0.1 HTTPServerTCPPort: 9090 +ReadDirPlusEnabled: false +XAttrEnabled: false +EntryDuration: 10s AttrDuration: 10s AttrBlockSize: 65536 -LookupEntryDuration: 10s ReaddirMaxEntries: 1024 +FUSEMaxBackground: 100 +FUSECongestionThreshhold: 0 +FUSEMaxWrite: 131072 # Linux max is 128KiB diff --git a/pfsagentd/request.go b/pfsagentd/request.go index ef36d12b0..e2dc023bd 100644 --- a/pfsagentd/request.go +++ b/pfsagentd/request.go @@ -9,7 +9,6 @@ import ( "sync/atomic" "time" - "github.com/swiftstack/ProxyFS/fs" "github.com/swiftstack/ProxyFS/jrpcfs" ) @@ -27,12 +26,6 @@ func doMountProxyFS() { AuthGroupID: 0, } - if globals.config.ReadOnly { - mountRequest.MountOptions = uint64(fs.MountReadOnly) - } else { - mountRequest.MountOptions = 0 - } - mountReply = &jrpcfs.MountByAccountNameReply{} err = doJRPCRequest("Server.RpcMountByAccountName", mountRequest, mountReply) @@ -117,7 +110,9 @@ func doHTTPRequest(request *http.Request, okStatusCodes ...int) (response *http. request.Header["X-Auth-Token"] = []string{swiftAuthToken} + _ = atomic.AddUint64(&globals.metrics.HTTPRequestsInFlight, 1) response, err = globals.httpClient.Do(request) + _ = atomic.AddUint64(&globals.metrics.HTTPRequestsInFlight, ^uint64(0)) if nil != err { _ = atomic.AddUint64(&globals.metrics.HTTPRequestSubmissionFailures, 1) logErrorf("doHTTPRequest(%s %s) failed to submit request: %v", request.Method, request.URL.String(), err) @@ -153,6 +148,16 @@ func doHTTPRequest(request *http.Request, okStatusCodes ...int) (response *http. updateAuthTokenAndAccountURL() } else { logWarnf("doHTTPRequest(%s %s) needs to retry due to unexpected http.Status: %s", request.Method, request.URL.String(), response.Status) + + // Close request.Body at this time just in case... + // + // It appears that net/http.Do() will actually return + // even if it has an outstanding Read() call to + // request.Body.Read() and calling request.Body.Close() + // will give it a chance to force request.Body.Read() + // to exit cleanly. + + _ = request.Body.Close() } time.Sleep(globals.retryDelay[retryIndex]) diff --git a/pfsagentd/setup_teardown_test.go b/pfsagentd/setup_teardown_test.go index ae33a7aaa..94f81114e 100644 --- a/pfsagentd/setup_teardown_test.go +++ b/pfsagentd/setup_teardown_test.go @@ -87,16 +87,20 @@ func testSetup(t *testing.T) { "Agent.DirtyFileLimit=50", "Agent.MaxFlushSize=10485760", "Agent.MaxFlushTime=10s", - "Agent.ReadOnly=false", "Agent.LogFilePath=", "Agent.LogToConsole=false", "Agent.TraceEnabled=false", "Agent.HTTPServerIPAddr=127.0.0.1", "Agent.HTTPServerTCPPort=9091", + "Agent.ReadDirPlusEnabled=false", + "Agent.XAttrEnabled=false", + "Agent.EntryDuration=10s", "Agent.AttrDuration=10s", "Agent.AttrBlockSize=65536", - "Agent.LookupEntryDuration=10s", "Agent.ReaddirMaxEntries=1024", + "Agent.FUSEMaxBackground=100", + "Agent.FUSECongestionThreshhold=0", + "Agent.FUSEMaxWrite=131072", // Linux max... 128KiB is good enough for testing "Stats.IPAddr=localhost", "Stats.UDPPort=52184", diff --git a/vendor/github.com/swiftstack/fission/LICENSE b/vendor/github.com/swiftstack/fission/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/swiftstack/fission/Makefile b/vendor/github.com/swiftstack/fission/Makefile new file mode 100644 index 000000000..916e0f7cc --- /dev/null +++ b/vendor/github.com/swiftstack/fission/Makefile @@ -0,0 +1,12 @@ +all: fmt install + +.PHONY: all clean fmt install + +clean: + go clean -i . + +fmt: + go fmt . + +install: + go install . diff --git a/vendor/github.com/swiftstack/fission/README.md b/vendor/github.com/swiftstack/fission/README.md new file mode 100644 index 000000000..73e240602 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/README.md @@ -0,0 +1,101 @@ +# fission + +Go package enabling the implementation of a multi-threaded low-level FUSE file system. + +## API Reference + +``` +package fission + +// Volume represents a file system instance. A Volume is provisioned by calling +// NewVolume(). After recording the returned interface from NewVolume(), a single +// call to DoMount() kicks off the mounting process and the caller should expect +// to see the various callbacks listed in the Callbacks interface. This will +// continue a single call to DoUnmount() is made after which the Volume instance +// may be safely discarded. +// +type Volume interface { + // DoMount is invoked on a Volume interface to perform the FUSE mount and + // begin receiving the various callbacks listed in the Callbacks interface. + // + DoMount() (err error) + + // DoUnmount is invoked on a Volume interface to perform the FUSE unmount. + // Upon return, no callbacks will be made and the Volume instance may be + // safely discarded. + // + DoUnmount() (err error) +} + +// Callbacks is the interface declaring the various callbacks that will be issued +// in response to a Volume instance while it is mounted. Note that some callbacks +// are expected to return both an error as well as a struct pointer. In the case of an +// error, the struct pointer should be as it will not be written to /dev/fuse. +// Finally, one callback is special: DoInit(). Provisioning a Volume instance involved +// providing the InitOut.MaxWrite to allow configuring the buffer pool used by the +// /dev/fuse read loop (including, of course, the reception of the InitIn up-call). +// The implementation of DoInit, therefore, should not expect returning an InitOut +// with a different MaxWrite to be honored. +// +type Callbacks interface { + DoLookup(inHeader *InHeader, lookupIn *LookupIn) (lookupOut *LookupOut, errno syscall.Errno) + DoForget(inHeader *InHeader, forgetIn *ForgetIn) + DoGetAttr(inHeader *InHeader, getAttrIn *GetAttrIn) (getAttrOut *GetAttrOut, errno syscall.Errno) + DoSetAttr(inHeader *InHeader, setAttrIn *SetAttrIn) (setAttrOut *SetAttrOut, errno syscall.Errno) + DoReadLink(inHeader *InHeader) (readLinkOut *ReadLinkOut, errno syscall.Errno) + DoSymLink(inHeader *InHeader, symLinkIn *SymLinkIn) (symLinkOut *SymLinkOut, errno syscall.Errno) + DoMkNod(inHeader *InHeader, mkNodIn *MkNodIn) (mkNodOut *MkNodOut, errno syscall.Errno) + DoMkDir(inHeader *InHeader, mkDirIn *MkDirIn) (mkDirOut *MkDirOut, errno syscall.Errno) + DoUnlink(inHeader *InHeader, unlinkIn *UnlinkIn) (errno syscall.Errno) + DoRmDir(inHeader *InHeader, rmDirIn *RmDirIn) (errno syscall.Errno) + DoRename(inHeader *InHeader, renameIn *RenameIn) (errno syscall.Errno) + DoLink(inHeader *InHeader, linkIn *LinkIn) (linkOut *LinkOut, errno syscall.Errno) + DoOpen(inHeader *InHeader, openIn *OpenIn) (openOut *OpenOut, errno syscall.Errno) + DoRead(inHeader *InHeader, readIn *ReadIn) (readOut *ReadOut, errno syscall.Errno) + DoWrite(inHeader *InHeader, writeIn *WriteIn) (writeOut *WriteOut, errno syscall.Errno) + DoStatFS(inHeader *InHeader) (statFSOut *StatFSOut, errno syscall.Errno) + DoRelease(inHeader *InHeader, releaseIn *ReleaseIn) (errno syscall.Errno) + DoFSync(inHeader *InHeader, fSyncIn *FSyncIn) (errno syscall.Errno) + DoSetXAttr(inHeader *InHeader, setXAttrIn *SetXAttrIn) (errno syscall.Errno) + DoGetXAttr(inHeader *InHeader, getXAttrIn *GetXAttrIn) (getXAttrOut *GetXAttrOut, errno syscall.Errno) + DoListXAttr(inHeader *InHeader, listXAttrIn *ListXAttrIn) (listXAttrOut *ListXAttrOut, errno syscall.Errno) + DoRemoveXAttr(inHeader *InHeader, removeXAttrIn *RemoveXAttrIn) (errno syscall.Errno) + DoFlush(inHeader *InHeader, flushIn *FlushIn) (errno syscall.Errno) + DoInit(inHeader *InHeader, initIn *InitIn) (initOut *InitOut, errno syscall.Errno) + DoOpenDir(inHeader *InHeader, openDirIn *OpenDirIn) (openDirOut *OpenDirOut, errno syscall.Errno) + DoReadDir(inHeader *InHeader, readDirIn *ReadDirIn) (readDirOut *ReadDirOut, errno syscall.Errno) + DoReleaseDir(inHeader *InHeader, releaseDirIn *ReleaseDirIn) (errno syscall.Errno) + DoFsyncDir(inHeader *InHeader, fsyncDirIn *FsyncDirIn) (errno syscall.Errno) + DoGetLK(inHeader *InHeader, getLKIn *GetLKIn) (getLKOut *GetLKOut, errno syscall.Errno) + DoSetLK(inHeader *InHeader, setLKIn *SetLKIn) (errno syscall.Errno) + DoSetLKW(inHeader *InHeader, setLKWIn *SetLKWIn) (errno syscall.Errno) + DoAccess(inHeader *InHeader, accessIn *AccessIn) (errno syscall.Errno) + DoCreate(inHeader *InHeader, createIn *CreateIn) (createOut *CreateOut, errno syscall.Errno) + DoInterrupt(inHeader *InHeader, interruptIn *InterruptIn) + DoBMap(inHeader *InHeader, bMapIn *BMapIn) (bMapOut *BMapOut, errno syscall.Errno) + DoDestroy(inHeader *InHeader) + DoPoll(inHeader *InHeader, pollIn *PollIn) (pollOut *PollOut, errno syscall.Errno) + DoBatchForget(inHeader *InHeader, batchForgetIn *BatchForgetIn) + DoFAllocate(inHeader *InHeader, fAllocateIn *FAllocateIn) (errno syscall.Errno) + DoReadDirPlus(inHeader *InHeader, readDirPlusIn *ReadDirPlusIn) (readDirPlusOut *ReadDirPlusOut, errno syscall.Errno) + DoRename2(inHeader *InHeader, rename2In *Rename2In) (errno syscall.Errno) + DoLSeek(inHeader *InHeader, lSeekIn *LSeekIn) (lSeekOut *LSeekOut, errno syscall.Errno) +} + +// NewVolume is called to create a Volume instance. Various callbacks listed in the Callbacks interface +// will be made while the Volume is mounted. Note that the caller provides a value for InitOut.MaxWrite +// at the time the Volume instance is provisioned so that the package may properly setup the read loop +// against /dev/fuse prior to reception of an InitIn request. A chan error is also supplied to enable +// the Volume to indicate that it is no longer servicing FUSE upcalls (e.g. as a result of an intentional +// DoUnmount() call or some unexpected error reading from /dev/fuse). +// +func NewVolume(volumeName string, mountpointDirPath string, mountFlags uintptr, initOutMaxWrite uint32, callbacks Callbacks, logger *log.Logger, errChan chan error) (volume Volume) +``` + +## Contributors + + * ed@swiftstack.com + +## License + +See the included LICENSE file diff --git a/vendor/github.com/swiftstack/fission/api.go b/vendor/github.com/swiftstack/fission/api.go new file mode 100644 index 000000000..7ffebe019 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/api.go @@ -0,0 +1,885 @@ +package fission + +import ( + "log" + "syscall" +) + +// Volume represents a file system instance. A Volume is provisioned by calling +// NewVolume(). After recording the returned interface from NewVolume(), a single +// call to DoMount() kicks off the mounting process and the caller should expect +// to see the various callbacks listed in the Callbacks interface. This will +// continue a single call to DoUnmount() is made after which the Volume instance +// may be safely discarded. +// +type Volume interface { + // DoMount is invoked on a Volume interface to perform the FUSE mount and + // begin receiving the various callbacks listed in the Callbacks interface. + // + DoMount() (err error) + + // DoUnmount is invoked on a Volume interface to perform the FUSE unmount. + // Upon return, no callbacks will be made and the Volume instance may be + // safely discarded. + // + DoUnmount() (err error) +} + +// Callbacks is the interface declaring the various callbacks that will be issued +// in response to a Volume instance while it is mounted. Note that some callbacks +// are expected to return both an error as well as a struct pointer. In the case of an +// error, the struct pointer should be as it will not be written to /dev/fuse. +// Finally, one callback is special: DoInit(). Provisioning a Volume instance involved +// providing the InitOut.MaxWrite to allow configuring the buffer pool used by the +// /dev/fuse read loop (including, of course, the reception of the InitIn up-call). +// The implementation of DoInit, therefore, should not expect returning an InitOut +// with a different MaxWrite to be honored. +// +type Callbacks interface { + DoLookup(inHeader *InHeader, lookupIn *LookupIn) (lookupOut *LookupOut, errno syscall.Errno) + DoForget(inHeader *InHeader, forgetIn *ForgetIn) + DoGetAttr(inHeader *InHeader, getAttrIn *GetAttrIn) (getAttrOut *GetAttrOut, errno syscall.Errno) + DoSetAttr(inHeader *InHeader, setAttrIn *SetAttrIn) (setAttrOut *SetAttrOut, errno syscall.Errno) + DoReadLink(inHeader *InHeader) (readLinkOut *ReadLinkOut, errno syscall.Errno) + DoSymLink(inHeader *InHeader, symLinkIn *SymLinkIn) (symLinkOut *SymLinkOut, errno syscall.Errno) + DoMkNod(inHeader *InHeader, mkNodIn *MkNodIn) (mkNodOut *MkNodOut, errno syscall.Errno) + DoMkDir(inHeader *InHeader, mkDirIn *MkDirIn) (mkDirOut *MkDirOut, errno syscall.Errno) + DoUnlink(inHeader *InHeader, unlinkIn *UnlinkIn) (errno syscall.Errno) + DoRmDir(inHeader *InHeader, rmDirIn *RmDirIn) (errno syscall.Errno) + DoRename(inHeader *InHeader, renameIn *RenameIn) (errno syscall.Errno) + DoLink(inHeader *InHeader, linkIn *LinkIn) (linkOut *LinkOut, errno syscall.Errno) + DoOpen(inHeader *InHeader, openIn *OpenIn) (openOut *OpenOut, errno syscall.Errno) + DoRead(inHeader *InHeader, readIn *ReadIn) (readOut *ReadOut, errno syscall.Errno) + DoWrite(inHeader *InHeader, writeIn *WriteIn) (writeOut *WriteOut, errno syscall.Errno) + DoStatFS(inHeader *InHeader) (statFSOut *StatFSOut, errno syscall.Errno) + DoRelease(inHeader *InHeader, releaseIn *ReleaseIn) (errno syscall.Errno) + DoFSync(inHeader *InHeader, fSyncIn *FSyncIn) (errno syscall.Errno) + DoSetXAttr(inHeader *InHeader, setXAttrIn *SetXAttrIn) (errno syscall.Errno) + DoGetXAttr(inHeader *InHeader, getXAttrIn *GetXAttrIn) (getXAttrOut *GetXAttrOut, errno syscall.Errno) + DoListXAttr(inHeader *InHeader, listXAttrIn *ListXAttrIn) (listXAttrOut *ListXAttrOut, errno syscall.Errno) + DoRemoveXAttr(inHeader *InHeader, removeXAttrIn *RemoveXAttrIn) (errno syscall.Errno) + DoFlush(inHeader *InHeader, flushIn *FlushIn) (errno syscall.Errno) + DoInit(inHeader *InHeader, initIn *InitIn) (initOut *InitOut, errno syscall.Errno) + DoOpenDir(inHeader *InHeader, openDirIn *OpenDirIn) (openDirOut *OpenDirOut, errno syscall.Errno) + DoReadDir(inHeader *InHeader, readDirIn *ReadDirIn) (readDirOut *ReadDirOut, errno syscall.Errno) + DoReleaseDir(inHeader *InHeader, releaseDirIn *ReleaseDirIn) (errno syscall.Errno) + DoFSyncDir(inHeader *InHeader, fSyncDirIn *FSyncDirIn) (errno syscall.Errno) + DoGetLK(inHeader *InHeader, getLKIn *GetLKIn) (getLKOut *GetLKOut, errno syscall.Errno) + DoSetLK(inHeader *InHeader, setLKIn *SetLKIn) (errno syscall.Errno) + DoSetLKW(inHeader *InHeader, setLKWIn *SetLKWIn) (errno syscall.Errno) + DoAccess(inHeader *InHeader, accessIn *AccessIn) (errno syscall.Errno) + DoCreate(inHeader *InHeader, createIn *CreateIn) (createOut *CreateOut, errno syscall.Errno) + DoInterrupt(inHeader *InHeader, interruptIn *InterruptIn) + DoBMap(inHeader *InHeader, bMapIn *BMapIn) (bMapOut *BMapOut, errno syscall.Errno) + DoDestroy(inHeader *InHeader) + DoPoll(inHeader *InHeader, pollIn *PollIn) (pollOut *PollOut, errno syscall.Errno) + DoBatchForget(inHeader *InHeader, batchForgetIn *BatchForgetIn) + DoFAllocate(inHeader *InHeader, fAllocateIn *FAllocateIn) (errno syscall.Errno) + DoReadDirPlus(inHeader *InHeader, readDirPlusIn *ReadDirPlusIn) (readDirPlusOut *ReadDirPlusOut, errno syscall.Errno) + DoRename2(inHeader *InHeader, rename2In *Rename2In) (errno syscall.Errno) + DoLSeek(inHeader *InHeader, lSeekIn *LSeekIn) (lSeekOut *LSeekOut, errno syscall.Errno) +} + +// NewVolume is called to create a Volume instance. Various callbacks listed in the Callbacks interface +// will be made while the Volume is mounted. Note that the caller provides a value for InitOut.MaxWrite +// at the time the Volume instance is provisioned so that the package may properly setup the read loop +// against /dev/fuse prior to reception of an InitIn request. A chan error is also supplied to enable +// the Volume to indicate that it is no longer servicing FUSE upcalls (e.g. as a result of an intentional +// DoUnmount() call or some unexpected error reading from /dev/fuse). +// +func NewVolume(volumeName string, mountpointDirPath string, mountFlags uintptr, initOutMaxWrite uint32, callbacks Callbacks, logger *log.Logger, errChan chan error) (volume Volume) { + volume = newVolume(volumeName, mountpointDirPath, mountFlags, initOutMaxWrite, callbacks, logger, errChan) + return +} + +const AttrSize = 88 + +type Attr struct { + Ino uint64 + Size uint64 + Blocks uint64 + ATimeSec uint64 + MTimeSec uint64 + CTimeSec uint64 + ATimeNSec uint32 + MTimeNSec uint32 + CTimeNSec uint32 + Mode uint32 + NLink uint32 + UID uint32 + GID uint32 + RDev uint32 + BlkSize uint32 + Padding uint32 +} + +const KStatFSSize = 80 + +type KStatFS struct { + Blocks uint64 + BFree uint64 + BAvail uint64 + Files uint64 + FFree uint64 + BSize uint32 + NameLen uint32 + FRSize uint32 + Padding uint32 + Spare [6]uint32 +} + +const FileLockSize = 24 + +type FileLock struct { + Start uint64 + End uint64 + Type uint32 + PID uint32 +} + +const ( + SetAttrInValidMode = uint32(1) << iota + SetAttrInValidUID + SetAttrInValidGID + SetAttrInValidSize + SetAttrInValidATime + SetAttrInValidMTime + SetAttrInValidFH + SetAttrInValidATimeNow + SetAttrInValidMTimeNow + SetAttrInValidLockOwner +) + +const ( + FOpenRequestRDONLY = uint32(syscall.O_RDONLY) + FOpenRequestWRONLY = uint32(syscall.O_WRONLY) + FOpenRequestRDWR = uint32(syscall.O_RDWR) + FOpenRequestAPPEND = uint32(syscall.O_APPEND) + FOpenRequestCREAT = uint32(syscall.O_CREAT) + FOpenRequestEXCL = uint32(syscall.O_EXCL) + FOpenRequestSYNC = uint32(syscall.O_SYNC) + FOpenRequestTRUNC = uint32(syscall.O_TRUNC) +) + +const ( + FOpenResponseDirectIO = uint32(1) << iota + FOpenResponseKeepCache + FOpenResponseNonSeekable + FOpenResponseCacheDir + FOpenResponseStream +) + +const ( + InitFlagsAsyncRead = uint32(1) << iota + InitFlagsPosixLocks + InitFlagsFileOps + InitFlagsAtomicOTrunc + InitFlagsExportSupport + InitFlagsBigWrites + InitFlagsDontMask + InitFlagsSpliceWrite + InitFlagsSpliceMove + InitFlagsSpliceRead + InitFlagsFLockLocks + InitFlagsHasIoctlDir + InitFlagsAutoInvalData + InitFlagsDoReadDirPlus + InitFlagsReaddirplusAuto + InitFlagsAsyncDio + InitFlagsWritebackCache + InitFlagsNoOpenSupport + InitFlagsParallelDirops + InitFlagsHandleKillpriv + InitFlagsPosixACL + InitFlagsAbortError + InitFlagsMaxPages + InitFlagsCacheSymlinks + InitFlagsNoOpendirSupport + InitFlagsExplicitInvalData + InitFlagsMapAlignment +) + +const ( + ReleaseFlush = uint32(1) << iota + ReleaseFLockUnlock +) + +const ( + GetAttrFH uint32 = uint32(1) << iota +) + +const ( + LKFLock uint32 = uint32(1) << iota +) + +const ( + WriteCache = uint32(1) << iota + WriteLockOwner + WriteKillPriv +) + +const ( + ReadLockOwner = uint32(1) << (iota + 1) +) + +const ( + IoctlCompat = uint32(1) << iota + IoctlUnrestricted + IoctlRetry + Ioctl32Bit + IoctlDir + IoctlCompatX32 +) + +const IoctlIovecSize = 16 + +type IoctlIovec struct { + Base uint64 + Len uint64 +} + +const IoctlMaxIOV = 256 + +const ( + PollScheduleNotify = uint32(1) << iota +) + +const ( + FSyncFDataSync = uint32(1) << iota +) + +const ( + NofifyPoll = iota + 1 + NotifyInvalInode + NotifyInvalEntry + NotifyStore + NotifyRetrieve + NotifyDelete + NotifyCodeMax +) + +const EntryOutSize = 40 + AttrSize + +type EntryOut struct { + NodeID uint64 + Generation uint64 + EntryValidSec uint64 + AttrValidSec uint64 + EntryValidNSec uint32 + AttrValidNSec uint32 + Attr +} + +const ForgetOneSize = 16 + +type ForgetOne struct { + NodeID uint64 + NLookup uint64 +} + +const DirEntAlignment = 8 // applies to both DirEnt and DirEntPlus + +const DirEntFixedPortionSize = 24 // + len(Name) and rounded up to DirEntAlignment boundary + +type DirEnt struct { + Ino uint64 + Off uint64 // position of next DirEnt + NameLen uint32 // automatically computed ( == len(Name) ) + Type uint32 + Name []byte +} + +const DirEntPlusFixedPortionSize = EntryOutSize + DirEntFixedPortionSize // + len(DirEnt.Name) and rounded up to DirEntAlignment boundary + +type DirEntPlus struct { + EntryOut + DirEnt +} + +const ( + OpCodeLookup = uint32(1) + OpCodeForget = uint32(2) // no reply + OpCodeGetAttr = uint32(3) + OpCodeSetAttr = uint32(4) + OpCodeReadLink = uint32(5) + OpCodeSymLink = uint32(6) + + OpCodeMkNod = uint32(8) + OpCodeMkDir = uint32(9) + OpCodeUnlink = uint32(10) + OpCodeRmDir = uint32(11) + OpCodeRename = uint32(12) + OpCodeLink = uint32(13) + OpCodeOpen = uint32(14) + OpCodeRead = uint32(15) + OpCodeWrite = uint32(16) + OpCodeStatFS = uint32(17) + OpCodeRelease = uint32(18) + + OpCodeFSync = uint32(20) + OpCodeSetXAttr = uint32(21) + OpCodeGetXAttr = uint32(22) + OpCodeListXAttr = uint32(23) + OpCodeRemoveXAttr = uint32(24) + OpCodeFlush = uint32(25) + OpCodeInit = uint32(26) + OpCodeOpenDir = uint32(27) + OpCodeReadDir = uint32(28) + OpCodeReleaseDir = uint32(29) + OpCodeFSyncDir = uint32(30) + OpCodeGetLK = uint32(31) + OpCodeSetLK = uint32(32) + OpCodeSetLKW = uint32(33) + OpCodeAccess = uint32(34) + OpCodeCreate = uint32(35) + OpCodeInterrupt = uint32(36) // no reply + OpCodeBMap = uint32(37) + OpCodeDestroy = uint32(38) // no reply + OpCodeIoCtl = uint32(39) // unsupported + OpCodePoll = uint32(40) + OpCodeNotifyReply = uint32(41) // unsupported + OpCodeBatchForget = uint32(42) // no reply + OpCodeFAllocate = uint32(43) + OpCodeReadDirPlus = uint32(44) + OpCodeRename2 = uint32(45) + OpCodeLSeek = uint32(46) + OpCodeCopyFileRange = uint32(47) // unsupported + OpCodeSetupMapping = uint32(48) // unsupported + OpCodeRemoveMapping = uint32(49) // unsupported + + OpCodeCuseInit = uint32(4096) // unsupported +) + +const InHeaderSize = 40 + +type InHeader struct { + Len uint32 // includes InHeaderSize and any payload + OpCode uint32 // one of const OpCode* + Unique uint64 + NodeID uint64 + UID uint32 + GID uint32 + PID uint32 + Padding uint32 +} + +const OutHeaderSize = 16 + +type OutHeader struct { + Len uint32 // automatically computed; includes OutHeaderSize and any payload + Error int32 + Unique uint64 +} + +type LookupIn struct { + Name []byte +} + +const LookupOutSize = EntryOutSize + +type LookupOut struct { + EntryOut +} + +const ForgetInSize = 8 + +type ForgetIn struct { + NLookup uint64 +} + +const GetAttrInSize = 16 + +type GetAttrIn struct { + Flags uint32 // mask of const GetAttrInFlags* bits + Dummy uint32 + FH uint64 +} + +const GetAttrOutSize = 16 + AttrSize + +type GetAttrOut struct { + AttrValidSec uint64 + AttrValidNSec uint32 + Dummy uint32 + Attr +} + +const SetAttrInSize = 88 + +type SetAttrIn struct { + Valid uint32 // mask of const SetAttrInValid* bits + Padding uint32 + FH uint64 + Size uint64 + LockOwner uint64 + ATimeSec uint64 + MTimeSec uint64 + Unused2 uint64 + ATimeNSec uint32 + MTimeNSec uint32 + Unused3 uint32 + Mode uint32 + Unused4 uint32 + UID uint32 + GID uint32 + Unused5 uint32 +} + +const SetAttrOutSize = 16 + AttrSize + +type SetAttrOut struct { + AttrValidSec uint64 + AttrValidNSec uint32 + Dummy uint32 + Attr +} + +type ReadLinkOut struct { + Data []byte +} + +type SymLinkIn struct { + Name []byte + Data []byte // byte(0) separated from Name +} + +const SymLinkOutSize = EntryOutSize + +type SymLinkOut struct { + EntryOut +} + +const MkNodInFixedPortionSize = 16 // + len(Name) + +type MkNodIn struct { + Mode uint32 + RDev uint32 + UMask uint32 + Padding uint32 + Name []byte +} + +const MkNodOutSize = EntryOutSize + +type MkNodOut struct { + EntryOut +} + +const MkDirInFixedPortionSize = 8 // + len(Name) + +type MkDirIn struct { + Mode uint32 + UMask uint32 + Name []byte +} + +const MkDirOutSize = EntryOutSize + +type MkDirOut struct { + EntryOut +} + +type UnlinkIn struct { + Name []byte +} + +type RmDirIn struct { + Name []byte +} + +const RenameInFixedPortionSize = 8 // + len(OldName) + 1 + len(NewName) + +type RenameIn struct { + NewDir uint64 + OldName []byte + NewName []byte // byte(0) separated from OldName +} + +const LinkInFixedPortionSize = 8 // + len(Name) + +type LinkIn struct { + OldNodeID uint64 + Name []byte +} + +const LinkOutSize = EntryOutSize + +type LinkOut struct { + EntryOut +} + +const OpenInSize = 8 + +type OpenIn struct { + Flags uint32 // mask of const FOpenRequest* bits + Unused uint32 +} + +const OpenOutSize = 16 + +type OpenOut struct { + FH uint64 + OpenFlags uint32 // mask of const FOpenResponse* bits + Padding uint32 +} + +const ReadInSize = 40 + +type ReadIn struct { + FH uint64 + Offset uint64 + Size uint32 + ReadFlags uint32 + LockOwner uint64 + Flags uint32 + Padding uint32 +} + +type ReadOut struct { + Data []byte +} + +const WriteInFixedPortionSize = 40 // + len(Data) a.k.a. Size + +type WriteIn struct { + FH uint64 + Offset uint64 + Size uint32 + WriteFlags uint32 + LockOwner uint64 + Flags uint32 + Padding uint32 + Data []byte +} + +const WriteOutSize = 8 + +type WriteOut struct { + Size uint32 + Padding uint32 +} + +const StatFSOutSize = KStatFSSize + +type StatFSOut struct { + KStatFS +} + +const ReleaseInSize = 24 + +type ReleaseIn struct { + FH uint64 + Flags uint32 + ReleaseFlags uint32 + LockOwner uint64 +} + +const FSyncInSize = 16 + +type FSyncIn struct { + FH uint64 + FsyncFlags uint32 + Padding uint32 +} + +const SetXAttrInFixedPortionSize = 8 // + len(Name) + 1 + len(Data) + +type SetXAttrIn struct { + Size uint32 // == len(Name) + 1 + len(Data) + Flags uint32 + Name []byte + Data []byte // byte(0) separated from Name +} + +const GetXAttrInFixedPortionSize = 8 // + len(Name) + +type GetXAttrIn struct { + Size uint32 // == max len(GetXAttrOut.Data) + Padding uint32 + Name []byte +} + +const GetXAttrOutSizeOnlySize = 8 + +type GetXAttrOut struct { + Size uint32 // only returned if GetXAttrIn.Size == 0 + Padding uint32 // only returned if GetXAttrIn.Size == 0 + Data []byte // only returned if GetXAttrIn.Size != 0 +} + +const ListXAttrInSize = 8 + +type ListXAttrIn struct { + Size uint32 // == max len(ListXAttrOut.Name) with a '\0' between each Name element + Padding uint32 +} + +const ListXAttrOutSizeOnlySize = 8 + +type ListXAttrOut struct { + Size uint32 // only returned if ListXAttrIn.Size == 0... SUM(each Name + trailing '\0') + Padding uint32 // only returned if ListXAttrIn.Size == 0 + Name [][]byte // only returned if ListXAttrIn.Size != 0... each with trailing '\0' +} + +type RemoveXAttrIn struct { + Name []byte +} + +const FlushInSize = 24 + +type FlushIn struct { + FH uint64 + Unused uint32 + Padding uint32 + LockOwner uint64 +} + +const InitInSize = 16 + +type InitIn struct { + Major uint32 + Minor uint32 + MaxReadAhead uint32 + Flags uint32 // mask of const InitFlags* bits +} + +const InitOutSize = 24 + +type InitOut struct { + Major uint32 + Minor uint32 + MaxReadAhead uint32 + Flags uint32 // mask of const InitFlags* bits + MaxBackground uint16 + CongestionThreshhold uint16 + MaxWrite uint32 +} + +const OpenDirInSize = 8 + +type OpenDirIn struct { + Flags uint32 + Unused uint32 +} + +const OpenDirOutSize = 16 + +type OpenDirOut struct { + FH uint64 + OpenFlags uint32 // mask of const FOpen* bits + Padding uint32 +} + +const ReadDirInSize = 40 + +type ReadDirIn struct { + FH uint64 + Offset uint64 + Size uint32 + ReadFlags uint32 + LockOwner uint64 + Flags uint32 + Padding uint32 +} + +type ReadDirOut struct { + DirEnt []DirEnt // aligned on DirEntAlignment boundaries +} + +const ReleaseDirInSize = 24 + +type ReleaseDirIn struct { + FH uint64 + Flags uint32 + ReleaseFlags uint32 + LockOwner uint64 +} + +const FSyncDirInSize = 16 + +type FSyncDirIn struct { + FH uint64 + FsyncFlags uint32 + Padding uint32 +} + +const GetLKInSize = 16 + FileLockSize + 8 + +type GetLKIn struct { + FH uint64 + Owner uint64 + FileLock + LKFlags uint32 + Padding uint32 +} + +const GetLKOutSize = FileLockSize + +type GetLKOut struct { + FileLock +} + +const SetLKInSize = 16 + FileLockSize + 8 + +type SetLKIn struct { + FH uint64 + Owner uint64 + FileLock + LKFlags uint32 + Padding uint32 +} + +const SetLKWInSize = 16 + FileLockSize + 8 + +type SetLKWIn struct { + FH uint64 + Owner uint64 + FileLock + LKFlags uint32 + Padding uint32 +} + +const AccessInSize = 8 + +type AccessIn struct { + Mask uint32 + Padding uint32 +} + +const CreateInFixedPortionSize = 16 // + len(Name) + +type CreateIn struct { + Flags uint32 // mask of const FOpenRequest* bits + Mode uint32 + UMask uint32 + Padding uint32 + Name []byte +} + +const CreateOutSize = EntryOutSize + 16 + +type CreateOut struct { + EntryOut + FH uint64 + OpenFlags uint32 // mask of const FOpenResponse* bits + Padding uint32 +} + +const InterruptInSize = 8 + +type InterruptIn struct { + Unique uint64 +} + +const BMapInSize = 16 + +type BMapIn struct { + Block uint64 + BlockSize uint32 + Padding uint32 +} + +const BMapOutSize = 8 + +type BMapOut struct { + Block uint64 +} + +const IoCtlInFixedPortionSize = 32 // + len(InBuf) a.k.a. InSize + +type IoCtlIn struct { // unsupported + FH uint64 + Flags uint32 + Cmd uint32 + Arg uint64 // == a uintptr to InBuf + InSize uint32 + OutSize uint32 + InBuf []byte // == nil if InSize == 0 +} + +const IoCtlOutFixedPortionSize = 16 // + (InIovs * IoctlIovecSize) + (OutIovs * IoctlIovecSize) + +type IoCtlOut struct { // unsupported + Result uint32 + Flags uint32 + InIovs uint32 + OutIovs uint32 + InIov []IoctlIovec // len(IoIov) == InIovs + OutIov []IoctlIovec // len(OutIov) == OutIovs +} + +const PollInSize = 24 + +type PollIn struct { + FH uint64 + KH uint64 + Flags uint32 + Events uint32 +} + +const PollOutSize = 8 + +type PollOut struct { + REvents uint32 + Padding uint32 +} + +const BatchForgetInFixedPortionSize = 8 // + (Count * ForgetOneSize) + +type BatchForgetIn struct { + Count uint32 + Dummy uint32 + Forget []ForgetOne // len(Forget) == Count +} + +const FAllocateInSize = 32 + +type FAllocateIn struct { + FH uint64 + Offset uint64 + Length uint64 + Mode uint32 + Padding uint32 +} + +const ReadDirPlusInSize = 40 + +type ReadDirPlusIn struct { + FH uint64 + Offset uint64 + Size uint32 + ReadFlags uint32 + LockOwner uint64 + Flags uint32 + Padding uint32 +} + +type ReadDirPlusOut struct { + DirEntPlus []DirEntPlus // aligned on DirEntAlignment boundaries +} + +const Rename2InFixedPortionSize = 16 // + len(OldName) + 1 + len(NewName) + +type Rename2In struct { + NewDir uint64 + Flags uint32 + Padding uint32 + OldName []byte + NewName []byte // byte(0) separated from OldName +} + +const LSeekInSize = 24 + +type LSeekIn struct { + FH uint64 + Offset uint64 + Whence uint32 + Padding uint32 +} + +const LSeekOutSize = 8 + +type LSeekOut struct { + Offset uint64 +} diff --git a/vendor/github.com/swiftstack/fission/callbacks.go b/vendor/github.com/swiftstack/fission/callbacks.go new file mode 100644 index 000000000..84a9f8608 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/callbacks.go @@ -0,0 +1,1589 @@ +package fission + +import ( + "bytes" + "syscall" + "unsafe" +) + +func (volume *volumeStruct) doLookup(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + lookupIn *LookupIn + lookupOut *LookupOut + outPayload []byte + ) + + lookupIn = &LookupIn{ + Name: cloneByteSlice(devFuseFDReadBufPayload, true), + } + + lookupOut, errno = volume.callbacks.DoLookup(inHeader, lookupIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, LookupOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = lookupOut.EntryOut.NodeID + *(*uint64)(unsafe.Pointer(&outPayload[8])) = lookupOut.EntryOut.Generation + *(*uint64)(unsafe.Pointer(&outPayload[16])) = lookupOut.EntryOut.EntryValidSec + *(*uint64)(unsafe.Pointer(&outPayload[24])) = lookupOut.EntryOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[32])) = lookupOut.EntryOut.EntryValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[36])) = lookupOut.EntryOut.AttrValidNSec + + *(*uint64)(unsafe.Pointer(&outPayload[40])) = lookupOut.EntryOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[48])) = lookupOut.EntryOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[56])) = lookupOut.EntryOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[64])) = lookupOut.EntryOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[72])) = lookupOut.EntryOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[80])) = lookupOut.EntryOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[88])) = lookupOut.EntryOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[92])) = lookupOut.EntryOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[96])) = lookupOut.EntryOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[100])) = lookupOut.EntryOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[104])) = lookupOut.EntryOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[108])) = lookupOut.EntryOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[112])) = lookupOut.EntryOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[116])) = lookupOut.EntryOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[120])) = lookupOut.EntryOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[124])) = lookupOut.EntryOut.Attr.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doForget(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + forgetIn *ForgetIn + ) + + if len(devFuseFDReadBufPayload) != ForgetInSize { + volume.logger.Printf("Call to doForget() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + return + } + + forgetIn = &ForgetIn{ + NLookup: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + } + + volume.callbacks.DoForget(inHeader, forgetIn) +} + +func (volume *volumeStruct) doGetAttr(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + getAttrIn *GetAttrIn + getAttrOut *GetAttrOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) != GetAttrInSize { + volume.logger.Printf("Call to doGetAttr() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + getAttrIn = &GetAttrIn{ + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Dummy: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + } + + getAttrOut, errno = volume.callbacks.DoGetAttr(inHeader, getAttrIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, GetAttrOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = getAttrOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[8])) = getAttrOut.AttrValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[12])) = getAttrOut.Dummy + + *(*uint64)(unsafe.Pointer(&outPayload[16])) = getAttrOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[24])) = getAttrOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[32])) = getAttrOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[40])) = getAttrOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[48])) = getAttrOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[56])) = getAttrOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[64])) = getAttrOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[68])) = getAttrOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[72])) = getAttrOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[76])) = getAttrOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[80])) = getAttrOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[84])) = getAttrOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[88])) = getAttrOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[92])) = getAttrOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[96])) = getAttrOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[100])) = getAttrOut.Attr.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doSetAttr(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + outPayload []byte + setAttrIn *SetAttrIn + setAttrOut *SetAttrOut + ) + + if len(devFuseFDReadBufPayload) != SetAttrInSize { + volume.logger.Printf("Call to doSetAttr() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + setAttrIn = &SetAttrIn{ + Valid: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Size: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + ATimeSec: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + MTimeSec: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[40])), + Unused2: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[48])), + ATimeNSec: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[56])), + MTimeNSec: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[60])), + Unused3: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[64])), + Mode: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[68])), + Unused4: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[72])), + UID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[76])), + GID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[80])), + Unused5: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[84])), + } + + setAttrOut, errno = volume.callbacks.DoSetAttr(inHeader, setAttrIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, SetAttrOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = setAttrOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[8])) = setAttrOut.AttrValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[12])) = setAttrOut.Dummy + + *(*uint64)(unsafe.Pointer(&outPayload[16])) = setAttrOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[24])) = setAttrOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[32])) = setAttrOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[40])) = setAttrOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[48])) = setAttrOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[56])) = setAttrOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[64])) = setAttrOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[68])) = setAttrOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[72])) = setAttrOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[76])) = setAttrOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[80])) = setAttrOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[84])) = setAttrOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[88])) = setAttrOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[92])) = setAttrOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[96])) = setAttrOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[100])) = setAttrOut.Attr.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doReadLink(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + outPayload []byte + readLinkOut *ReadLinkOut + ) + + if len(devFuseFDReadBufPayload) != 0 { + volume.logger.Printf("Call to doReadLink() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + readLinkOut, errno = volume.callbacks.DoReadLink(inHeader) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = readLinkOut.Data + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doSymLink(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + nameDataSplit [][]byte + outPayload []byte + symLinkIn *SymLinkIn + symLinkOut *SymLinkOut + ) + + nameDataSplit = bytes.SplitN(devFuseFDReadBufPayload, []byte{0}, 2) + if len(nameDataSplit) != 2 { + volume.logger.Printf("Call to doSymLink() with bad devFuseFDReadBufPayload") + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + symLinkIn = &SymLinkIn{ + Name: cloneByteSlice(nameDataSplit[0], false), + Data: cloneByteSlice(nameDataSplit[1], true), + } + + symLinkOut, errno = volume.callbacks.DoSymLink(inHeader, symLinkIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, SymLinkOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = symLinkOut.EntryOut.NodeID + *(*uint64)(unsafe.Pointer(&outPayload[8])) = symLinkOut.EntryOut.Generation + *(*uint64)(unsafe.Pointer(&outPayload[16])) = symLinkOut.EntryOut.EntryValidSec + *(*uint64)(unsafe.Pointer(&outPayload[24])) = symLinkOut.EntryOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[32])) = symLinkOut.EntryOut.EntryValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[36])) = symLinkOut.EntryOut.AttrValidNSec + + *(*uint64)(unsafe.Pointer(&outPayload[40])) = symLinkOut.EntryOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[48])) = symLinkOut.EntryOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[56])) = symLinkOut.EntryOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[64])) = symLinkOut.EntryOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[72])) = symLinkOut.EntryOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[80])) = symLinkOut.EntryOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[88])) = symLinkOut.EntryOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[92])) = symLinkOut.EntryOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[96])) = symLinkOut.EntryOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[100])) = symLinkOut.EntryOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[104])) = symLinkOut.EntryOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[108])) = symLinkOut.EntryOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[112])) = symLinkOut.EntryOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[116])) = symLinkOut.EntryOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[120])) = symLinkOut.EntryOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[124])) = symLinkOut.EntryOut.Attr.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doMkNod(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + mkNodIn *MkNodIn + mkNodOut *MkNodOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) < MkNodInFixedPortionSize { + volume.logger.Printf("Call to doMkNod() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + mkNodIn = &MkNodIn{ + Mode: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + RDev: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + UMask: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + Name: cloneByteSlice(devFuseFDReadBufPayload[MkNodInFixedPortionSize:], true), + } + + mkNodOut, errno = volume.callbacks.DoMkNod(inHeader, mkNodIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, MkNodOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = mkNodOut.EntryOut.NodeID + *(*uint64)(unsafe.Pointer(&outPayload[8])) = mkNodOut.EntryOut.Generation + *(*uint64)(unsafe.Pointer(&outPayload[16])) = mkNodOut.EntryOut.EntryValidSec + *(*uint64)(unsafe.Pointer(&outPayload[24])) = mkNodOut.EntryOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[32])) = mkNodOut.EntryOut.EntryValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[36])) = mkNodOut.EntryOut.AttrValidNSec + + *(*uint64)(unsafe.Pointer(&outPayload[40])) = mkNodOut.EntryOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[48])) = mkNodOut.EntryOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[56])) = mkNodOut.EntryOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[64])) = mkNodOut.EntryOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[72])) = mkNodOut.EntryOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[80])) = mkNodOut.EntryOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[88])) = mkNodOut.EntryOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[92])) = mkNodOut.EntryOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[96])) = mkNodOut.EntryOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[100])) = mkNodOut.EntryOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[104])) = mkNodOut.EntryOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[108])) = mkNodOut.EntryOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[112])) = mkNodOut.EntryOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[116])) = mkNodOut.EntryOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[120])) = mkNodOut.EntryOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[124])) = mkNodOut.EntryOut.Attr.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doMkDir(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + mkDirIn *MkDirIn + mkDirOut *MkDirOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) < MkDirInFixedPortionSize { + volume.logger.Printf("Call to doMkNod() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + mkDirIn = &MkDirIn{ + Mode: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + UMask: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + Name: cloneByteSlice(devFuseFDReadBufPayload[MkDirInFixedPortionSize:], true), + } + + mkDirOut, errno = volume.callbacks.DoMkDir(inHeader, mkDirIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, MkDirOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = mkDirOut.EntryOut.NodeID + *(*uint64)(unsafe.Pointer(&outPayload[8])) = mkDirOut.EntryOut.Generation + *(*uint64)(unsafe.Pointer(&outPayload[16])) = mkDirOut.EntryOut.EntryValidSec + *(*uint64)(unsafe.Pointer(&outPayload[24])) = mkDirOut.EntryOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[32])) = mkDirOut.EntryOut.EntryValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[36])) = mkDirOut.EntryOut.AttrValidNSec + + *(*uint64)(unsafe.Pointer(&outPayload[40])) = mkDirOut.EntryOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[48])) = mkDirOut.EntryOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[56])) = mkDirOut.EntryOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[64])) = mkDirOut.EntryOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[72])) = mkDirOut.EntryOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[80])) = mkDirOut.EntryOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[88])) = mkDirOut.EntryOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[92])) = mkDirOut.EntryOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[96])) = mkDirOut.EntryOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[100])) = mkDirOut.EntryOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[104])) = mkDirOut.EntryOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[108])) = mkDirOut.EntryOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[112])) = mkDirOut.EntryOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[116])) = mkDirOut.EntryOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[120])) = mkDirOut.EntryOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[124])) = mkDirOut.EntryOut.Attr.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doUnlink(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + unlinkIn *UnlinkIn + ) + + unlinkIn = &UnlinkIn{ + Name: cloneByteSlice(devFuseFDReadBufPayload, true), + } + + errno = volume.callbacks.DoUnlink(inHeader, unlinkIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doRmDir(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + rmDirIn *RmDirIn + ) + + rmDirIn = &RmDirIn{ + Name: cloneByteSlice(devFuseFDReadBufPayload, true), + } + + errno = volume.callbacks.DoRmDir(inHeader, rmDirIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doRename(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + oldNameNewNameSplit [][]byte + renameIn *RenameIn + ) + + if len(devFuseFDReadBufPayload) < RenameInFixedPortionSize { + volume.logger.Printf("Call to doRename() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + oldNameNewNameSplit = bytes.SplitN(devFuseFDReadBufPayload[RenameInFixedPortionSize:], []byte{0}, 2) + if len(oldNameNewNameSplit) != 2 { + volume.logger.Printf("Call to doRename() with bad devFuseFDReadBufPayload") + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + renameIn = &RenameIn{ + NewDir: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + OldName: cloneByteSlice(oldNameNewNameSplit[0], false), + NewName: cloneByteSlice(oldNameNewNameSplit[1], true), + } + + errno = volume.callbacks.DoRename(inHeader, renameIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doLink(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + linkIn *LinkIn + linkOut *LinkOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) < LinkInFixedPortionSize { + volume.logger.Printf("Call to doLink() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + linkIn = &LinkIn{ + OldNodeID: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Name: cloneByteSlice(devFuseFDReadBufPayload[LinkInFixedPortionSize:], true), + } + + linkOut, errno = volume.callbacks.DoLink(inHeader, linkIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, LinkOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = linkOut.EntryOut.NodeID + *(*uint64)(unsafe.Pointer(&outPayload[8])) = linkOut.EntryOut.Generation + *(*uint64)(unsafe.Pointer(&outPayload[16])) = linkOut.EntryOut.EntryValidSec + *(*uint64)(unsafe.Pointer(&outPayload[24])) = linkOut.EntryOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[32])) = linkOut.EntryOut.EntryValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[36])) = linkOut.EntryOut.AttrValidNSec + + *(*uint64)(unsafe.Pointer(&outPayload[40])) = linkOut.EntryOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[48])) = linkOut.EntryOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[56])) = linkOut.EntryOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[64])) = linkOut.EntryOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[72])) = linkOut.EntryOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[80])) = linkOut.EntryOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[88])) = linkOut.EntryOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[92])) = linkOut.EntryOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[96])) = linkOut.EntryOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[100])) = linkOut.EntryOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[104])) = linkOut.EntryOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[108])) = linkOut.EntryOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[112])) = linkOut.EntryOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[116])) = linkOut.EntryOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[120])) = linkOut.EntryOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[124])) = linkOut.EntryOut.Attr.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doOpen(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + openIn *OpenIn + openOut *OpenOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) != OpenInSize { + volume.logger.Printf("Call to doOpen() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + openIn = &OpenIn{ + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Unused: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + } + + openOut, errno = volume.callbacks.DoOpen(inHeader, openIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, OpenOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = openOut.FH + *(*uint32)(unsafe.Pointer(&outPayload[8])) = openOut.OpenFlags + *(*uint32)(unsafe.Pointer(&outPayload[12])) = openOut.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doRead(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + outPayload []byte + readIn *ReadIn + readOut *ReadOut + ) + + if len(devFuseFDReadBufPayload) != ReadInSize { + volume.logger.Printf("Call to doRead() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + readIn = &ReadIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Offset: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Size: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + ReadFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[20])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[36])), + } + + readOut, errno = volume.callbacks.DoRead(inHeader, readIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = readOut.Data + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doWrite(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + outPayload []byte + writeIn *WriteIn + writeOut *WriteOut + ) + + if len(devFuseFDReadBufPayload) < WriteInFixedPortionSize { + volume.logger.Printf("Call to doWrite() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + writeIn = &WriteIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Offset: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Size: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + WriteFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[20])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[36])), + Data: cloneByteSlice(devFuseFDReadBufPayload[WriteInFixedPortionSize:], false), + } + + if len(writeIn.Data) != int(writeIn.Size) { + volume.logger.Printf("Call to doWrite() with bad Size == %v expected %v", writeIn.Size, len(writeIn.Data)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + writeOut, errno = volume.callbacks.DoWrite(inHeader, writeIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, WriteOutSize) + + *(*uint32)(unsafe.Pointer(&outPayload[0])) = writeOut.Size + *(*uint32)(unsafe.Pointer(&outPayload[4])) = writeOut.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doStatFS(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + outPayload []byte + statFSOut *StatFSOut + ) + + if len(devFuseFDReadBufPayload) != 0 { + volume.logger.Printf("Call to doStatFS() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + } + + statFSOut, errno = volume.callbacks.DoStatFS(inHeader) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, StatFSOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = statFSOut.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[8])) = statFSOut.BFree + *(*uint64)(unsafe.Pointer(&outPayload[16])) = statFSOut.BAvail + *(*uint64)(unsafe.Pointer(&outPayload[24])) = statFSOut.Files + *(*uint64)(unsafe.Pointer(&outPayload[32])) = statFSOut.FFree + *(*uint32)(unsafe.Pointer(&outPayload[40])) = statFSOut.BSize + *(*uint32)(unsafe.Pointer(&outPayload[44])) = statFSOut.NameLen + *(*uint32)(unsafe.Pointer(&outPayload[48])) = statFSOut.FRSize + *(*uint32)(unsafe.Pointer(&outPayload[52])) = statFSOut.Padding + *(*uint32)(unsafe.Pointer(&outPayload[56])) = statFSOut.Spare[0] + *(*uint32)(unsafe.Pointer(&outPayload[60])) = statFSOut.Spare[1] + *(*uint32)(unsafe.Pointer(&outPayload[64])) = statFSOut.Spare[2] + *(*uint32)(unsafe.Pointer(&outPayload[68])) = statFSOut.Spare[3] + *(*uint32)(unsafe.Pointer(&outPayload[72])) = statFSOut.Spare[4] + *(*uint32)(unsafe.Pointer(&outPayload[76])) = statFSOut.Spare[5] + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doRelease(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + releaseIn *ReleaseIn + ) + + if len(devFuseFDReadBufPayload) != ReleaseInSize { + volume.logger.Printf("Call to doRelease() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + } + + releaseIn = &ReleaseIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + ReleaseFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + } + + errno = volume.callbacks.DoRelease(inHeader, releaseIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doFSync(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + fSyncIn *FSyncIn + ) + + if len(devFuseFDReadBufPayload) != FSyncInSize { + volume.logger.Printf("Call to doFSync() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + } + + fSyncIn = &FSyncIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + FsyncFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + } + + errno = volume.callbacks.DoFSync(inHeader, fSyncIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doSetXAttr(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + nameDataSplit [][]byte + setXAttrIn *SetXAttrIn + setXAttrInSize int + ) + + if len(devFuseFDReadBufPayload) < SetXAttrInFixedPortionSize { + volume.logger.Printf("Call to doSetXAttr() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + nameDataSplit = bytes.SplitN(devFuseFDReadBufPayload[SetXAttrInFixedPortionSize:], []byte{0}, 2) + if len(nameDataSplit) != 2 { + volume.logger.Printf("Call to doSetXAttr() with bad devFuseFDReadBufPayload") + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + setXAttrIn = &SetXAttrIn{ + Size: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + Name: cloneByteSlice(nameDataSplit[0], false), + Data: cloneByteSlice(nameDataSplit[1], true), + } + + setXAttrInSize = SetXAttrInFixedPortionSize + len(setXAttrIn.Name) + 1 + len(setXAttrIn.Data) + + if len(devFuseFDReadBufPayload) != setXAttrInSize { + volume.logger.Printf("Call to doSetXAttr() with bad Size == %v expected %v", setXAttrIn.Size, setXAttrInSize) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + errno = volume.callbacks.DoSetXAttr(inHeader, setXAttrIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doGetXAttr(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + getXAttrIn *GetXAttrIn + getXAttrOut *GetXAttrOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) < GetXAttrInFixedPortionSize { + volume.logger.Printf("Call to doGetXAttr() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + getXAttrIn = &GetXAttrIn{ + Size: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + Name: cloneByteSlice(devFuseFDReadBufPayload[GetXAttrInFixedPortionSize:], true), + } + + getXAttrOut, errno = volume.callbacks.DoGetXAttr(inHeader, getXAttrIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + if 0 == getXAttrIn.Size { + outPayload = make([]byte, GetXAttrOutSizeOnlySize) + + *(*uint32)(unsafe.Pointer(&outPayload[0])) = getXAttrOut.Size + *(*uint32)(unsafe.Pointer(&outPayload[4])) = getXAttrOut.Padding + } else { + outPayload = getXAttrOut.Data + } + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doListXAttr(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + listXAttrIn *ListXAttrIn + listXAttrOut *ListXAttrOut + nameElement []byte + nameTotalLen uint32 + outPayload []byte + outPayloadOffset uint32 + ) + + if len(devFuseFDReadBufPayload) != ListXAttrInSize { + volume.logger.Printf("Call to doListXAttr() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + listXAttrIn = &ListXAttrIn{ + Size: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + } + + listXAttrOut, errno = volume.callbacks.DoListXAttr(inHeader, listXAttrIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + if 0 == listXAttrIn.Size { + outPayload = make([]byte, ListXAttrOutSizeOnlySize) + + *(*uint32)(unsafe.Pointer(&outPayload[0])) = listXAttrOut.Size + *(*uint32)(unsafe.Pointer(&outPayload[4])) = listXAttrOut.Padding + } else { + nameTotalLen = 0 + + if 0 != len(listXAttrOut.Name) { + for _, nameElement = range listXAttrOut.Name { + nameTotalLen += uint32(len(nameElement) + 1) + } + } + + outPayload = make([]byte, nameTotalLen) + + outPayloadOffset = 0 + + for _, nameElement = range listXAttrOut.Name { + copy(outPayload[outPayloadOffset:], nameElement) + outPayloadOffset += uint32(len(nameElement) + 1) + outPayload[outPayloadOffset-1] = 0 + } + } + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doRemoveXAttr(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + removeXAttrIn *RemoveXAttrIn + ) + + removeXAttrIn = &RemoveXAttrIn{ + Name: cloneByteSlice(devFuseFDReadBufPayload, true), + } + + errno = volume.callbacks.DoRemoveXAttr(inHeader, removeXAttrIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doFlush(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + flushIn *FlushIn + ) + + if len(devFuseFDReadBufPayload) != FlushInSize { + volume.logger.Printf("Call to doFlush() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + flushIn = &FlushIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Unused: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + } + + errno = volume.callbacks.DoFlush(inHeader, flushIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doInit(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + initIn *InitIn + initOut *InitOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) != InitInSize { + volume.logger.Printf("Call to doInit() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + initIn = &InitIn{ + Major: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Minor: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + MaxReadAhead: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + } + + initOut, errno = volume.callbacks.DoInit(inHeader, initIn) + if 0 != errno { + volume.logger.Printf("Call to doInit() returning bad errno == %v", errno) + volume.devFuseFDWriter(inHeader, errno) + return + } + if initOut.MaxWrite != volume.initOutMaxWrite { + volume.logger.Printf("Call to doInit() attempted to modify MaxWrite... ignoring it") + } + + outPayload = make([]byte, InitOutSize) + + *(*uint32)(unsafe.Pointer(&outPayload[0])) = initOut.Major + *(*uint32)(unsafe.Pointer(&outPayload[4])) = initOut.Minor + *(*uint32)(unsafe.Pointer(&outPayload[8])) = initOut.MaxReadAhead + *(*uint32)(unsafe.Pointer(&outPayload[12])) = initOut.Flags + *(*uint16)(unsafe.Pointer(&outPayload[16])) = initOut.MaxBackground + *(*uint16)(unsafe.Pointer(&outPayload[18])) = initOut.CongestionThreshhold + *(*uint32)(unsafe.Pointer(&outPayload[20])) = volume.initOutMaxWrite + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doOpenDir(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + openDirIn *OpenDirIn + openDirOut *OpenDirOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) != OpenDirInSize { + volume.logger.Printf("Call to doOpenDir() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + openDirIn = &OpenDirIn{ + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Unused: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + } + + openDirOut, errno = volume.callbacks.DoOpenDir(inHeader, openDirIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, OpenDirOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = openDirOut.FH + *(*uint32)(unsafe.Pointer(&outPayload[8])) = openDirOut.OpenFlags + *(*uint32)(unsafe.Pointer(&outPayload[12])) = openDirOut.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doReadDir(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + dirEnt *DirEnt + dirEntIndex int + dirEntPayloadSize uint32 + errno syscall.Errno + nameLenAligned uint32 + outPayload []byte + outPayloadOffset uint32 + paddingIndex uint32 + readDirIn *ReadDirIn + readDirOut *ReadDirOut + ) + + if len(devFuseFDReadBufPayload) != ReadDirInSize { + volume.logger.Printf("Call to doReadDir() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + readDirIn = &ReadDirIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Offset: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Size: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + ReadFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[20])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[36])), + } + + readDirOut, errno = volume.callbacks.DoReadDir(inHeader, readDirIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, readDirIn.Size) + + outPayloadOffset = 0 + + for dirEntIndex = 0; dirEntIndex < len(readDirOut.DirEnt); dirEntIndex++ { + dirEnt = &readDirOut.DirEnt[dirEntIndex] + + nameLenAligned = (uint32(len(dirEnt.Name)) + (DirEntAlignment - 1)) & ^uint32(DirEntAlignment-1) + dirEntPayloadSize = DirEntFixedPortionSize + nameLenAligned + + if (outPayloadOffset + dirEntPayloadSize) > readDirIn.Size { + volume.logger.Printf("Return from DoReadDir() had excessive DirEnt's") + + outPayload = outPayload[:outPayloadOffset] + + volume.devFuseFDWriter(inHeader, 0, outPayload) + + return + } + + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+0])) = dirEnt.Ino + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+8])) = dirEnt.Off + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+16])) = uint32(len(dirEnt.Name)) + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+20])) = dirEnt.Type + + outPayloadOffset += DirEntFixedPortionSize + + copy(outPayload[outPayloadOffset:], dirEnt.Name) + + outPayloadOffset += uint32(len(dirEnt.Name)) + + for paddingIndex = uint32(len(dirEnt.Name)); paddingIndex < nameLenAligned; paddingIndex++ { + outPayload[outPayloadOffset] = 0 + outPayloadOffset++ + } + } + + outPayload = outPayload[:outPayloadOffset] + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doReleaseDir(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + releaseDirIn *ReleaseDirIn + ) + + if len(devFuseFDReadBufPayload) != ReleaseDirInSize { + volume.logger.Printf("Call to doReleaseDir() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + releaseDirIn = &ReleaseDirIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + ReleaseFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + } + + errno = volume.callbacks.DoReleaseDir(inHeader, releaseDirIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doFSyncDir(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + fSyncDirIn *FSyncDirIn + ) + + if len(devFuseFDReadBufPayload) != FSyncDirInSize { + volume.logger.Printf("Call to doFSyncDir() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + fSyncDirIn = &FSyncDirIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + FsyncFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + } + + errno = volume.callbacks.DoFSyncDir(inHeader, fSyncDirIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doGetLK(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + getLKIn *GetLKIn + getLKOut *GetLKOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) != GetLKInSize { + volume.logger.Printf("Call to doGetLK() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + getLKIn = &GetLKIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Owner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + FileLock: FileLock{ + Start: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + End: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Type: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + PID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[36])), + }, + LKFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[40])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[44])), + } + + getLKOut, errno = volume.callbacks.DoGetLK(inHeader, getLKIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, GetLKOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = getLKOut.FileLock.Start + *(*uint64)(unsafe.Pointer(&outPayload[8])) = getLKOut.FileLock.End + *(*uint32)(unsafe.Pointer(&outPayload[16])) = getLKOut.FileLock.Type + *(*uint32)(unsafe.Pointer(&outPayload[20])) = getLKOut.FileLock.PID + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doSetLK(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + setLKIn *SetLKIn + ) + + if len(devFuseFDReadBufPayload) != SetLKInSize { + volume.logger.Printf("Call to doSetLK() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + setLKIn = &SetLKIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Owner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + FileLock: FileLock{ + Start: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + End: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Type: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + PID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[36])), + }, + LKFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[40])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[44])), + } + + errno = volume.callbacks.DoSetLK(inHeader, setLKIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doSetLKW(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + setLKWIn *SetLKWIn + ) + + if len(devFuseFDReadBufPayload) != SetLKWInSize { + volume.logger.Printf("Call to doSetLKW() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + setLKWIn = &SetLKWIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Owner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + FileLock: FileLock{ + Start: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + End: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Type: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + PID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[36])), + }, + LKFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[40])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[44])), + } + + errno = volume.callbacks.DoSetLKW(inHeader, setLKWIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doAccess(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + accessIn *AccessIn + ) + + if len(devFuseFDReadBufPayload) != AccessInSize { + volume.logger.Printf("Call to doAccess() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + accessIn = &AccessIn{ + Mask: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + } + + errno = volume.callbacks.DoAccess(inHeader, accessIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doCreate(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + createIn *CreateIn + createOut *CreateOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) < CreateInFixedPortionSize { + volume.logger.Printf("Call to doCreate() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + createIn = &CreateIn{ + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Mode: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + UMask: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + Name: cloneByteSlice(devFuseFDReadBufPayload[CreateInFixedPortionSize:], true), + } + + createOut, errno = volume.callbacks.DoCreate(inHeader, createIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, CreateOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = createOut.EntryOut.NodeID + *(*uint64)(unsafe.Pointer(&outPayload[8])) = createOut.EntryOut.Generation + *(*uint64)(unsafe.Pointer(&outPayload[16])) = createOut.EntryOut.EntryValidSec + *(*uint64)(unsafe.Pointer(&outPayload[24])) = createOut.EntryOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[32])) = createOut.EntryOut.EntryValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[36])) = createOut.EntryOut.AttrValidNSec + + *(*uint64)(unsafe.Pointer(&outPayload[40])) = createOut.EntryOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[48])) = createOut.EntryOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[56])) = createOut.EntryOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[64])) = createOut.EntryOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[72])) = createOut.EntryOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[80])) = createOut.EntryOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[88])) = createOut.EntryOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[92])) = createOut.EntryOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[96])) = createOut.EntryOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[100])) = createOut.EntryOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[104])) = createOut.EntryOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[108])) = createOut.EntryOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[112])) = createOut.EntryOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[116])) = createOut.EntryOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[120])) = createOut.EntryOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[124])) = createOut.EntryOut.Attr.Padding + + *(*uint64)(unsafe.Pointer(&outPayload[128])) = createOut.FH + *(*uint32)(unsafe.Pointer(&outPayload[136])) = createOut.OpenFlags + *(*uint32)(unsafe.Pointer(&outPayload[140])) = createOut.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doInterrupt(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + interruptIn *InterruptIn + ) + + if len(devFuseFDReadBufPayload) != InterruptInSize { + volume.logger.Printf("Call to doInterrupt() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + interruptIn = &InterruptIn{ + Unique: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + } + + volume.callbacks.DoInterrupt(inHeader, interruptIn) +} + +func (volume *volumeStruct) doBMap(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + bMapIn *BMapIn + bMapOut *BMapOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) != BMapInSize { + volume.logger.Printf("Call to doBMap() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + bMapIn = &BMapIn{ + Block: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + BlockSize: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + } + + bMapOut, errno = volume.callbacks.DoBMap(inHeader, bMapIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, BMapOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = bMapOut.Block + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doDestroy(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + if len(devFuseFDReadBufPayload) != 0 { + volume.logger.Printf("Call to doDestroy() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + volume.callbacks.DoDestroy(inHeader) +} + +func (volume *volumeStruct) doPoll(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + outPayload []byte + pollIn *PollIn + pollOut *PollOut + ) + + if len(devFuseFDReadBufPayload) != PollInSize { + volume.logger.Printf("Call to doPoll() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + pollIn = &PollIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + KH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + Events: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[20])), + } + + pollOut, errno = volume.callbacks.DoPoll(inHeader, pollIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, PollOutSize) + + *(*uint32)(unsafe.Pointer(&outPayload[0])) = pollOut.REvents + *(*uint32)(unsafe.Pointer(&outPayload[4])) = pollOut.Padding + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doBatchForget(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + batchForgetIn *BatchForgetIn + batchForgetInForgetIndex uint32 + batchForgetInSize int + batchForgetInOffset uint32 + ) + + if len(devFuseFDReadBufPayload) < BatchForgetInFixedPortionSize { + volume.logger.Printf("Call to doBatchForget() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + batchForgetIn = &BatchForgetIn{ + Count: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Dummy: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[4])), + } + + batchForgetInSize = BatchForgetInFixedPortionSize + int(batchForgetIn.Count*ForgetOneSize) + + if len(devFuseFDReadBufPayload) != batchForgetInSize { + volume.logger.Printf("Call to doBatchForget() with bad len(devFuseFDReadBufPayload) == %v expected %v", len(devFuseFDReadBufPayload), batchForgetInSize) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + batchForgetIn.Forget = make([]ForgetOne, batchForgetIn.Count) + + batchForgetInOffset = BatchForgetInFixedPortionSize + + for batchForgetInForgetIndex = 0; batchForgetInForgetIndex < batchForgetIn.Count; batchForgetInForgetIndex++ { + batchForgetIn.Forget[batchForgetInForgetIndex] = ForgetOne{ + NodeID: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[batchForgetInOffset+0])), + NLookup: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[batchForgetInOffset+8])), + } + } + + volume.callbacks.DoBatchForget(inHeader, batchForgetIn) +} + +func (volume *volumeStruct) doFAllocate(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + fAllocateIn *FAllocateIn + ) + + if len(devFuseFDReadBufPayload) != FAllocateInSize { + volume.logger.Printf("Call to doFAllocate() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + fAllocateIn = &FAllocateIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Offset: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Length: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + Mode: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[28])), + } + + errno = volume.callbacks.DoFAllocate(inHeader, fAllocateIn) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doReadDirPlus(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + dirEntPlus *DirEntPlus + dirEntPlusIndex int + dirEntPlusPayloadSize uint32 + errno syscall.Errno + nameLenAligned uint32 + outPayload []byte + outPayloadOffset uint32 + paddingIndex uint32 + readDirPlusIn *ReadDirPlusIn + readDirPlusOut *ReadDirPlusOut + ) + + if len(devFuseFDReadBufPayload) != ReadDirPlusInSize { + volume.logger.Printf("Call to doReadDirPlus() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + readDirPlusIn = &ReadDirPlusIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Offset: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Size: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + ReadFlags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[20])), + LockOwner: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[24])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[32])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[36])), + } + + readDirPlusOut, errno = volume.callbacks.DoReadDirPlus(inHeader, readDirPlusIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, readDirPlusIn.Size) + + outPayloadOffset = 0 + + for dirEntPlusIndex = 0; dirEntPlusIndex < len(readDirPlusOut.DirEntPlus); dirEntPlusIndex++ { + dirEntPlus = &readDirPlusOut.DirEntPlus[dirEntPlusIndex] + + nameLenAligned = (uint32(len(dirEntPlus.Name)) + (DirEntAlignment - 1)) & ^uint32(DirEntAlignment-1) + dirEntPlusPayloadSize = DirEntPlusFixedPortionSize + nameLenAligned + + if (outPayloadOffset + dirEntPlusPayloadSize) > readDirPlusIn.Size { + volume.logger.Printf("Return from DoReadDirPlus() had excessive DirEntPlus's") + + outPayload = outPayload[:outPayloadOffset] + + volume.devFuseFDWriter(inHeader, 0, outPayload) + + return + } + + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+0])) = dirEntPlus.EntryOut.NodeID + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+8])) = dirEntPlus.EntryOut.Generation + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+16])) = dirEntPlus.EntryOut.EntryValidSec + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+24])) = dirEntPlus.EntryOut.AttrValidSec + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+32])) = dirEntPlus.EntryOut.EntryValidNSec + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+36])) = dirEntPlus.EntryOut.AttrValidNSec + + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+40])) = dirEntPlus.EntryOut.Attr.Ino + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+48])) = dirEntPlus.EntryOut.Attr.Size + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+56])) = dirEntPlus.EntryOut.Attr.Blocks + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+64])) = dirEntPlus.EntryOut.Attr.ATimeSec + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+72])) = dirEntPlus.EntryOut.Attr.MTimeSec + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+80])) = dirEntPlus.EntryOut.Attr.CTimeSec + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+88])) = dirEntPlus.EntryOut.Attr.ATimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+92])) = dirEntPlus.EntryOut.Attr.MTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+96])) = dirEntPlus.EntryOut.Attr.CTimeNSec + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+100])) = dirEntPlus.EntryOut.Attr.Mode + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+104])) = dirEntPlus.EntryOut.Attr.NLink + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+108])) = dirEntPlus.EntryOut.Attr.UID + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+112])) = dirEntPlus.EntryOut.Attr.GID + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+116])) = dirEntPlus.EntryOut.Attr.RDev + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+120])) = dirEntPlus.EntryOut.Attr.BlkSize + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+124])) = dirEntPlus.EntryOut.Attr.Padding + + outPayloadOffset += EntryOutSize + + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+0])) = dirEntPlus.DirEnt.Ino + *(*uint64)(unsafe.Pointer(&outPayload[outPayloadOffset+8])) = dirEntPlus.DirEnt.Off + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+16])) = uint32(len(dirEntPlus.DirEnt.Name)) + *(*uint32)(unsafe.Pointer(&outPayload[outPayloadOffset+20])) = dirEntPlus.DirEnt.Type + + outPayloadOffset += DirEntFixedPortionSize + + copy(outPayload[outPayloadOffset:], dirEntPlus.DirEnt.Name) + + outPayloadOffset += uint32(len(dirEntPlus.DirEnt.Name)) + + for paddingIndex = uint32(len(dirEntPlus.DirEnt.Name)); paddingIndex < nameLenAligned; paddingIndex++ { + outPayload[outPayloadOffset] = 0 + outPayloadOffset++ + } + } + + outPayload = outPayload[:outPayloadOffset] + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} + +func (volume *volumeStruct) doRename2(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + oldNameNewNameSplit [][]byte + rename2In *Rename2In + ) + + if len(devFuseFDReadBufPayload) < Rename2InFixedPortionSize { + volume.logger.Printf("Call to doRename2() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + oldNameNewNameSplit = bytes.SplitN(devFuseFDReadBufPayload[Rename2InFixedPortionSize:], []byte{0}, 2) + if len(oldNameNewNameSplit) != 2 { + volume.logger.Printf("Call to doRename2() with bad devFuseFDReadBufPayload") + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + rename2In = &Rename2In{ + NewDir: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Flags: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[12])), + OldName: cloneByteSlice(oldNameNewNameSplit[0], false), + NewName: cloneByteSlice(oldNameNewNameSplit[1], true), + } + + errno = volume.callbacks.DoRename2(inHeader, rename2In) + + volume.devFuseFDWriter(inHeader, errno) +} + +func (volume *volumeStruct) doLSeek(inHeader *InHeader, devFuseFDReadBufPayload []byte) { + var ( + errno syscall.Errno + lSeekIn *LSeekIn + lSeekOut *LSeekOut + outPayload []byte + ) + + if len(devFuseFDReadBufPayload) != LSeekInSize { + volume.logger.Printf("Call to doLSeek() with bad len(devFuseFDReadBufPayload) == %v", len(devFuseFDReadBufPayload)) + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + return + } + + lSeekIn = &LSeekIn{ + FH: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[0])), + Offset: *(*uint64)(unsafe.Pointer(&devFuseFDReadBufPayload[8])), + Whence: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[16])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBufPayload[20])), + } + + lSeekOut, errno = volume.callbacks.DoLSeek(inHeader, lSeekIn) + if 0 != errno { + volume.devFuseFDWriter(inHeader, errno) + return + } + + outPayload = make([]byte, LSeekOutSize) + + *(*uint64)(unsafe.Pointer(&outPayload[0])) = lSeekOut.Offset + + volume.devFuseFDWriter(inHeader, 0, outPayload) +} diff --git a/vendor/github.com/swiftstack/fission/examples/README.md b/vendor/github.com/swiftstack/fission/examples/README.md new file mode 100644 index 000000000..aa3148092 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/examples/README.md @@ -0,0 +1,3 @@ +# fission/examples + +Examples utilizing package fission. diff --git a/vendor/github.com/swiftstack/fission/examples/fission-ramfs/.gitignore b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/.gitignore new file mode 100644 index 000000000..0e10b231b --- /dev/null +++ b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/.gitignore @@ -0,0 +1 @@ +mount_point/ diff --git a/vendor/github.com/swiftstack/fission/examples/fission-ramfs/Makefile b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/Makefile new file mode 100644 index 000000000..916e0f7cc --- /dev/null +++ b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/Makefile @@ -0,0 +1,12 @@ +all: fmt install + +.PHONY: all clean fmt install + +clean: + go clean -i . + +fmt: + go fmt . + +install: + go install . diff --git a/vendor/github.com/swiftstack/fission/examples/fission-ramfs/README.md b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/README.md new file mode 100644 index 000000000..1e407df38 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/README.md @@ -0,0 +1,7 @@ +# fission/examples/passthrough + +Example fission (FUSE) file system in the style of the low-level `passthrough` file system +presented in the `libfuse` source tree. + +Here, the program must be run with sudo (as package fission doesn't utilize setuid'd `fusermount`). +The first arg is the "source" directory to be presented via the 2nd arg's "mountpoint" directory. diff --git a/vendor/github.com/swiftstack/fission/examples/fission-ramfs/callbacks.go b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/callbacks.go new file mode 100644 index 000000000..384e3634e --- /dev/null +++ b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/callbacks.go @@ -0,0 +1,2684 @@ +package main + +import ( + "os" + "syscall" + + "github.com/swiftstack/sortedmap" + + "github.com/swiftstack/fission" +) + +func (dummy *globalsStruct) DoLookup(inHeader *fission.InHeader, lookupIn *fission.LookupIn) (lookupOut *fission.LookupOut, errno syscall.Errno) { + var ( + dirEntInoAsU64 uint64 + dirEntInoAsValue sortedmap.Value + dirEntInode *inodeStruct + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + grantedLockSet.free(globals.tryLock) + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + dirEntInoAsValue, ok, err = dirInode.dirEntryMap.GetByKey(lookupIn.Name) + if nil != err { + globals.logger.Printf("func DoLookup(NodeID==%v,Name=%s) failed on .dirEntryMap.GetByKey(): %v", inHeader.NodeID, string(lookupIn.Name[:]), err) + os.Exit(1) + } + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + dirEntInoAsU64 = dirEntInoAsValue.(uint64) + + dirEntInode, ok = globals.inodeMap[dirEntInoAsU64] + if !ok { + globals.logger.Printf("func DoLookup(NodeID==%v) failed fetching globals.inodeMap[%v]", inHeader.NodeID, dirEntInoAsU64) + os.Exit(1) + } + + granted = grantedLockSet.try(dirEntInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + lookupOut = &fission.LookupOut{ + EntryOut: fission.EntryOut{ + NodeID: dirEntInode.attr.Ino, + Generation: 0, + EntryValidSec: 0, + AttrValidSec: 0, + EntryValidNSec: 0, + AttrValidNSec: 0, + Attr: fission.Attr{ + Ino: dirEntInode.attr.Ino, + Size: dirEntInode.attr.Size, + Blocks: dirEntInode.attr.Blocks, + ATimeSec: dirEntInode.attr.ATimeSec, + MTimeSec: dirEntInode.attr.MTimeSec, + CTimeSec: dirEntInode.attr.CTimeSec, + ATimeNSec: dirEntInode.attr.ATimeNSec, + MTimeNSec: dirEntInode.attr.MTimeNSec, + CTimeNSec: dirEntInode.attr.CTimeNSec, + Mode: dirEntInode.attr.Mode, + NLink: dirEntInode.attr.NLink, + UID: dirEntInode.attr.UID, + GID: dirEntInode.attr.GID, + RDev: dirEntInode.attr.RDev, + BlkSize: dirEntInode.attr.BlkSize, + Padding: dirEntInode.attr.Padding, + }, + }, + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoForget(inHeader *fission.InHeader, forgetIn *fission.ForgetIn) { + return +} + +func (dummy *globalsStruct) DoGetAttr(inHeader *fission.InHeader, getAttrIn *fission.GetAttrIn) (getAttrOut *fission.GetAttrOut, errno syscall.Errno) { + var ( + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + inode *inodeStruct + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + inode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(inode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + grantedLockSet.free(globals.tryLock) + + getAttrOut = &fission.GetAttrOut{ + AttrValidSec: 0, + AttrValidNSec: 0, + Dummy: 0, + Attr: fission.Attr{ + Ino: inode.attr.Ino, + Size: inode.attr.Size, + Blocks: inode.attr.Blocks, + ATimeSec: inode.attr.ATimeSec, + MTimeSec: inode.attr.MTimeSec, + CTimeSec: inode.attr.CTimeSec, + ATimeNSec: inode.attr.ATimeNSec, + MTimeNSec: inode.attr.MTimeNSec, + CTimeNSec: inode.attr.CTimeNSec, + Mode: inode.attr.Mode, + NLink: inode.attr.NLink, + UID: inode.attr.UID, + GID: inode.attr.GID, + RDev: inode.attr.RDev, + BlkSize: inode.attr.BlkSize, + Padding: inode.attr.Padding, + }, + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoSetAttr(inHeader *fission.InHeader, setAttrIn *fission.SetAttrIn) (setAttrOut *fission.SetAttrOut, errno syscall.Errno) { + var ( + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + inode *inodeStruct + inodeAttrMode uint32 + ok bool + setAttrInMode uint32 + unixTimeNowNSec uint32 + unixTimeNowSec uint64 + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + inode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(inode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + grantedLockSet.free(globals.tryLock) + + unixTimeNowSec, unixTimeNowNSec = unixTimeNow() + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidMode) { + inodeAttrMode = inode.attr.Mode & ^uint32(syscall.S_IRWXU|syscall.S_IRWXG|syscall.S_IRWXO) + setAttrInMode = setAttrIn.Mode & uint32(syscall.S_IRWXU|syscall.S_IRWXG|syscall.S_IRWXO) + inodeAttrMode |= setAttrInMode + + inode.attr.Mode = inodeAttrMode + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidUID) { + inode.attr.UID = setAttrIn.UID + } + if 0 != (setAttrIn.Valid & fission.SetAttrInValidGID) { + inode.attr.GID = setAttrIn.GID + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidSize) { + if syscall.S_IFREG != (inode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + if setAttrIn.Size <= inode.attr.Size { + inode.fileData = inode.fileData[:setAttrIn.Size] + } else { + inode.fileData = append(inode.fileData, make([]byte, (setAttrIn.Size-inode.attr.Size))...) + } + inode.attr.Size = setAttrIn.Size + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidATime) { + inode.attr.ATimeSec = setAttrIn.ATimeSec + inode.attr.ATimeNSec = setAttrIn.ATimeNSec + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidMTime) { + inode.attr.MTimeSec = setAttrIn.MTimeSec + inode.attr.MTimeNSec = setAttrIn.MTimeNSec + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidFH) { + grantedLockSet.freeAll(false) + errno = syscall.ENOSYS + return + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidATimeNow) { + inode.attr.ATimeSec = unixTimeNowSec + inode.attr.ATimeNSec = unixTimeNowNSec + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidMTimeNow) { + inode.attr.MTimeSec = unixTimeNowSec + inode.attr.MTimeNSec = unixTimeNowNSec + } + + if 0 != (setAttrIn.Valid & fission.SetAttrInValidLockOwner) { + grantedLockSet.freeAll(false) + errno = syscall.ENOSYS + } + + setAttrOut = &fission.SetAttrOut{ + AttrValidSec: 0, + AttrValidNSec: 0, + Dummy: 0, + Attr: fission.Attr{ + Ino: inode.attr.Ino, + Size: inode.attr.Size, + Blocks: inode.attr.Blocks, + ATimeSec: inode.attr.ATimeSec, + MTimeSec: inode.attr.MTimeSec, + CTimeSec: inode.attr.CTimeSec, + ATimeNSec: inode.attr.ATimeNSec, + MTimeNSec: inode.attr.MTimeNSec, + CTimeNSec: inode.attr.CTimeNSec, + Mode: inode.attr.Mode, + NLink: inode.attr.NLink, + UID: inode.attr.UID, + GID: inode.attr.GID, + RDev: inode.attr.RDev, + BlkSize: inode.attr.BlkSize, + Padding: inode.attr.Padding, + }, + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoReadLink(inHeader *fission.InHeader) (readLinkOut *fission.ReadLinkOut, errno syscall.Errno) { + var ( + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + symInode *inodeStruct + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + symInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(symInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFLNK != (symInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + readLinkOut = &fission.ReadLinkOut{ + Data: cloneByteSlice(symInode.symlinkData), + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoSymLink(inHeader *fission.InHeader, symLinkIn *fission.SymLinkIn) (symLinkOut *fission.SymLinkOut, errno syscall.Errno) { + var ( + dirEntInode *inodeStruct + dirEntInodeMode uint32 + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + unixTimeNowNSec uint32 + unixTimeNowSec uint64 + ) + + dirEntInodeMode = uint32(syscall.S_IRWXU | syscall.S_IRWXG | syscall.S_IRWXO) + dirEntInodeMode |= syscall.S_IFLNK + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + _, ok, err = dirInode.dirEntryMap.GetByKey(symLinkIn.Name) + if nil != err { + globals.logger.Printf("func DoSymLink(NodeID==%v,Name=%s,Data=%s) failed on .dirEntryMap.GetByKey(): %v", inHeader.NodeID, string(symLinkIn.Name[:]), string(symLinkIn.Data[:]), err) + os.Exit(1) + } + + if ok { + grantedLockSet.freeAll(false) + errno = syscall.EEXIST + return + } + + globals.lastNodeID++ + + unixTimeNowSec, unixTimeNowNSec = unixTimeNow() + + dirEntInode = &inodeStruct{ + tryLock: makeTryLock(), + attr: fission.Attr{ + Ino: globals.lastNodeID, + Size: 0, + Blocks: 0, + ATimeSec: unixTimeNowSec, + MTimeSec: unixTimeNowSec, + CTimeSec: unixTimeNowSec, + ATimeNSec: unixTimeNowNSec, + MTimeNSec: unixTimeNowNSec, + CTimeNSec: unixTimeNowNSec, + Mode: dirEntInodeMode, + NLink: 1, + UID: inHeader.UID, + GID: inHeader.GID, + RDev: 0, + BlkSize: attrBlkSize, + Padding: 0, + }, + xattrMap: sortedmap.NewLLRBTree(sortedmap.CompareByteSlice, globals.xattrMapDummy), + dirEntryMap: nil, + fileData: nil, + symlinkData: symLinkIn.Data, + fhSet: make(map[uint64]struct{}), + } + + ok, err = dirInode.dirEntryMap.Put(symLinkIn.Name, dirEntInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoSymLink(NodeID==%v,Name=%s,Data=%s) failed on .dirEntryMap.Put(): %v", inHeader.NodeID, string(symLinkIn.Name[:]), string(symLinkIn.Data[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoSymLink(NodeID==%v,Name=%s,Data=%s) .dirEntryMap.Put() returned !ok", inHeader.NodeID, string(symLinkIn.Name[:]), string(symLinkIn.Data[:])) + os.Exit(1) + } + + globals.inodeMap[dirEntInode.attr.Ino] = dirEntInode + + symLinkOut = &fission.SymLinkOut{ + EntryOut: fission.EntryOut{ + NodeID: dirEntInode.attr.Ino, + Generation: 0, + EntryValidSec: 0, + AttrValidSec: 0, + EntryValidNSec: 0, + AttrValidNSec: 0, + Attr: fission.Attr{ + Ino: dirEntInode.attr.Ino, + Size: dirEntInode.attr.Size, + Blocks: dirEntInode.attr.Blocks, + ATimeSec: dirEntInode.attr.ATimeSec, + MTimeSec: dirEntInode.attr.MTimeSec, + CTimeSec: dirEntInode.attr.CTimeSec, + ATimeNSec: dirEntInode.attr.ATimeNSec, + MTimeNSec: dirEntInode.attr.MTimeNSec, + CTimeNSec: dirEntInode.attr.CTimeNSec, + Mode: dirEntInode.attr.Mode, + NLink: dirEntInode.attr.NLink, + UID: dirEntInode.attr.UID, + GID: dirEntInode.attr.GID, + RDev: dirEntInode.attr.RDev, + BlkSize: dirEntInode.attr.BlkSize, + Padding: dirEntInode.attr.Padding, + }, + }, + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoMkNod(inHeader *fission.InHeader, mkNodIn *fission.MkNodIn) (mkNodOut *fission.MkNodOut, errno syscall.Errno) { + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoMkDir(inHeader *fission.InHeader, mkDirIn *fission.MkDirIn) (mkDirOut *fission.MkDirOut, errno syscall.Errno) { + var ( + dirEntInode *inodeStruct + dirEntInodeMode uint32 + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + unixTimeNowNSec uint32 + unixTimeNowSec uint64 + ) + + dirEntInodeMode = uint32(syscall.S_IRWXU | syscall.S_IRWXG | syscall.S_IRWXO) + dirEntInodeMode &= mkDirIn.Mode + dirEntInodeMode &= ^mkDirIn.UMask + dirEntInodeMode |= syscall.S_IFDIR + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + _, ok, err = dirInode.dirEntryMap.GetByKey(mkDirIn.Name) + if nil != err { + globals.logger.Printf("func DoMkDir(NodeID==%v,Name=%s) failed on .dirEntryMap.GetByKey(): %v", inHeader.NodeID, string(mkDirIn.Name[:]), err) + os.Exit(1) + } + + if ok { + grantedLockSet.freeAll(false) + errno = syscall.EEXIST + return + } + + globals.lastNodeID++ + + unixTimeNowSec, unixTimeNowNSec = unixTimeNow() + + dirEntInode = &inodeStruct{ + tryLock: makeTryLock(), + attr: fission.Attr{ + Ino: globals.lastNodeID, + Size: 0, + Blocks: 0, + ATimeSec: unixTimeNowSec, + MTimeSec: unixTimeNowSec, + CTimeSec: unixTimeNowSec, + ATimeNSec: unixTimeNowNSec, + MTimeNSec: unixTimeNowNSec, + CTimeNSec: unixTimeNowNSec, + Mode: dirEntInodeMode, + NLink: 2, + UID: inHeader.UID, + GID: inHeader.GID, + RDev: 0, + BlkSize: attrBlkSize, + Padding: 0, + }, + xattrMap: sortedmap.NewLLRBTree(sortedmap.CompareByteSlice, globals.xattrMapDummy), + dirEntryMap: sortedmap.NewLLRBTree(sortedmap.CompareByteSlice, globals.dirEntryMapDummy), + fileData: nil, + symlinkData: nil, + fhSet: make(map[uint64]struct{}), + } + + ok, err = dirEntInode.dirEntryMap.Put([]byte("."), dirEntInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoMkDir(NodeID==%v,Name=%s) failed on dirEntInode.dirEntryMap.Put(\".\"): %v", inHeader.NodeID, string(mkDirIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoMkDir(NodeID==%v,Name=%s) dirEntInode.dirEntryMap.Put(\".\") returned !ok", inHeader.NodeID, string(mkDirIn.Name[:])) + os.Exit(1) + } + ok, err = dirEntInode.dirEntryMap.Put([]byte(".."), dirInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoMkDir(NodeID==%v,Name=%s) failed on dirEntInode.dirEntryMap.Put(\"..\"): %v", inHeader.NodeID, string(mkDirIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoMkDir(NodeID==%v,Name=%s) dirEntInode.dirEntryMap.Put(\"..\") returned !ok", inHeader.NodeID, string(mkDirIn.Name[:])) + os.Exit(1) + } + ok, err = dirInode.dirEntryMap.Put(mkDirIn.Name, dirEntInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoMkDir(NodeID==%v,Name=%s) failed on dirInode.dirEntryMap.Put(): %v", inHeader.NodeID, string(mkDirIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoMkDir(NodeID==%v,Name=%s) dirInode.dirEntryMap.Put() returned !ok", inHeader.NodeID, string(mkDirIn.Name[:])) + os.Exit(1) + } + + dirInode.attr.NLink++ + + globals.inodeMap[dirEntInode.attr.Ino] = dirEntInode + + mkDirOut = &fission.MkDirOut{ + EntryOut: fission.EntryOut{ + NodeID: dirEntInode.attr.Ino, + Generation: 0, + EntryValidSec: 0, + AttrValidSec: 0, + EntryValidNSec: 0, + AttrValidNSec: 0, + Attr: fission.Attr{ + Ino: dirEntInode.attr.Ino, + Size: dirEntInode.attr.Size, + Blocks: dirEntInode.attr.Blocks, + ATimeSec: dirEntInode.attr.ATimeSec, + MTimeSec: dirEntInode.attr.MTimeSec, + CTimeSec: dirEntInode.attr.CTimeSec, + ATimeNSec: dirEntInode.attr.ATimeNSec, + MTimeNSec: dirEntInode.attr.MTimeNSec, + CTimeNSec: dirEntInode.attr.CTimeNSec, + Mode: dirEntInode.attr.Mode, + NLink: dirEntInode.attr.NLink, + UID: dirEntInode.attr.UID, + GID: dirEntInode.attr.GID, + RDev: dirEntInode.attr.RDev, + BlkSize: dirEntInode.attr.BlkSize, + Padding: dirEntInode.attr.Padding, + }, + }, + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoUnlink(inHeader *fission.InHeader, unlinkIn *fission.UnlinkIn) (errno syscall.Errno) { + var ( + dirEntInoAsU64 uint64 + dirEntInoAsValue sortedmap.Value + dirEntInode *inodeStruct + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + dirEntInoAsValue, ok, err = dirInode.dirEntryMap.GetByKey(unlinkIn.Name) + if nil != err { + globals.logger.Printf("func DoUnlink(NodeID==%v,Name=%s) failed on .dirEntryMap.GetByKey(): %v", inHeader.NodeID, string(unlinkIn.Name[:]), err) + os.Exit(1) + } + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + dirEntInoAsU64 = dirEntInoAsValue.(uint64) + + dirEntInode, ok = globals.inodeMap[dirEntInoAsU64] + if !ok { + globals.logger.Printf("func DoUnlink(NodeID==%v,Name==%s) failed fetching globals.inodeMap[%v]", inHeader.NodeID, unlinkIn.Name, dirEntInoAsU64) + os.Exit(1) + } + + granted = grantedLockSet.try(dirEntInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR == (dirEntInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EISDIR + return + } + + ok, err = dirInode.dirEntryMap.DeleteByKey(unlinkIn.Name) + if nil != err { + globals.logger.Printf("func DoUnlink(NodeID==%v,Name=%s) failed on .dirEntryMap.DeleteByKey(): %v", inHeader.NodeID, string(unlinkIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoUnlink(NodeID==%v,Name=%s) .dirEntryMap.DeleteByKey() returned !ok", inHeader.NodeID, string(unlinkIn.Name[:])) + os.Exit(1) + } + + dirEntInode.attr.NLink-- + + if 0 == dirEntInode.attr.NLink { + delete(globals.inodeMap, dirEntInode.attr.Ino) + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRmDir(inHeader *fission.InHeader, rmDirIn *fission.RmDirIn) (errno syscall.Errno) { + var ( + dirEntInoAsU64 uint64 + dirEntInoAsValue sortedmap.Value + dirEntInode *inodeStruct + dirEntInodeDirEntryMapLen int + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + dirEntInoAsValue, ok, err = dirInode.dirEntryMap.GetByKey(rmDirIn.Name) + if nil != err { + globals.logger.Printf("func DoRmDir(NodeID==%v,Name=%s) failed on .dirEntryMap.GetByKey(): %v", inHeader.NodeID, string(rmDirIn.Name[:]), err) + os.Exit(1) + } + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + dirEntInoAsU64 = dirEntInoAsValue.(uint64) + + dirEntInode, ok = globals.inodeMap[dirEntInoAsU64] + if !ok { + globals.logger.Printf("func DoRmDir(NodeID==%v,Name==%s) failed fetching globals.inodeMap[%v]", inHeader.NodeID, rmDirIn.Name, dirEntInoAsU64) + os.Exit(1) + } + + granted = grantedLockSet.try(dirEntInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirEntInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + dirEntInodeDirEntryMapLen, err = dirEntInode.dirEntryMap.Len() + if nil != err { + globals.logger.Printf("func DoRmDir(NodeID==%v,Name=%s) failed on .dirEntryMap.Len(): %v", inHeader.NodeID, string(rmDirIn.Name[:]), err) + os.Exit(1) + } + + if 2 != dirEntInodeDirEntryMapLen { + grantedLockSet.freeAll(false) + errno = syscall.ENOTEMPTY + return + } + + ok, err = dirInode.dirEntryMap.DeleteByKey(rmDirIn.Name) + if nil != err { + globals.logger.Printf("func DoRmDir(NodeID==%v,Name=%s) failed on .dirEntryMap.DeleteByKey(): %v", inHeader.NodeID, string(rmDirIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRmDir(NodeID==%v,Name=%s) .dirEntryMap.DeleteByKey() returned !ok", inHeader.NodeID, string(rmDirIn.Name[:])) + os.Exit(1) + } + + dirEntInode.attr.NLink-- + + delete(globals.inodeMap, dirEntInode.attr.Ino) + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRename(inHeader *fission.InHeader, renameIn *fission.RenameIn) (errno syscall.Errno) { + var ( + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + movedInode *inodeStruct + movedInodeNodeIDAsU64 uint64 + movedInodeNodeIDAsValue sortedmap.Value + newDirInode *inodeStruct + ok bool + oldDirInode *inodeStruct + replacedInode *inodeStruct + replacedInodeDirEntryMapLen int + replacedInodeNodeIDAsU64 uint64 + replacedInodeNodeIDAsValue sortedmap.Value + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + oldDirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(oldDirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (oldDirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + if inHeader.NodeID == renameIn.NewDir { + newDirInode = oldDirInode + } else { + newDirInode, ok = globals.inodeMap[renameIn.NewDir] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(newDirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (newDirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + } + + movedInodeNodeIDAsValue, ok, err = oldDirInode.dirEntryMap.GetByKey(renameIn.OldName) + if nil != err { + globals.logger.Printf("func DoRename(,OldName=%s) failed on .dirEntryMap.GetByKey(): %v", string(renameIn.OldName[:]), err) + os.Exit(1) + } + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + movedInodeNodeIDAsU64 = movedInodeNodeIDAsValue.(uint64) + + movedInode, ok = globals.inodeMap[movedInodeNodeIDAsU64] + if !ok { + globals.logger.Printf("func DoRename(,OldName=%s) globals.inodeMap[movedInodeNodeIDAsU64] returned !ok", string(renameIn.OldName[:])) + os.Exit(1) + } + + granted = grantedLockSet.try(movedInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + replacedInodeNodeIDAsValue, ok, err = newDirInode.dirEntryMap.GetByKey(renameIn.NewName) + if nil != err { + globals.logger.Printf("func DoRename(,NewName=%s) failed on .dirEntryMap.GetByKey(): %v", string(renameIn.NewName[:]), err) + os.Exit(1) + } + + if ok { + replacedInodeNodeIDAsU64 = replacedInodeNodeIDAsValue.(uint64) + + replacedInode, ok = globals.inodeMap[replacedInodeNodeIDAsU64] + if !ok { + globals.logger.Printf("func DoRename(,NewName=%s) globals.inodeMap[replacedInodeNodeIDAsU64] returned !ok", string(renameIn.NewName[:])) + os.Exit(1) + } + + granted = grantedLockSet.try(movedInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + } else { + replacedInode = nil + } + + if syscall.S_IFDIR == (movedInode.attr.Mode & syscall.S_IFMT) { + if nil != replacedInode { + if syscall.S_IFDIR != (movedInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + replacedInodeDirEntryMapLen, err = replacedInode.dirEntryMap.Len() + if nil != err { + globals.logger.Printf("func DoRename(,NewName=%s) failed on .dirEntryMap.Len(): %v", string(renameIn.NewName[:]), err) + os.Exit(1) + } + + if 2 != replacedInodeDirEntryMapLen { + grantedLockSet.freeAll(false) + errno = syscall.EEXIST + return + } + + ok, err = newDirInode.dirEntryMap.DeleteByKey(renameIn.NewName) + if nil != err { + globals.logger.Printf("func DoRename(,[Dir]NewName=%s) failed on .dirEntryMap.DeleteByKey(): %v", string(renameIn.NewName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename(,[Dir]NewName=%s) .dirEntryMap.DeleteByKey() returned !ok", string(renameIn.NewName[:])) + os.Exit(1) + } + + newDirInode.attr.NLink-- + + delete(globals.inodeMap, replacedInode.attr.Ino) + } + + oldDirInode.attr.NLink-- + newDirInode.attr.NLink++ + + ok, err = movedInode.dirEntryMap.PatchByKey([]byte(".."), newDirInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoRename() failed on .dirEntryMap.PatchByKey(): %v", err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename() .dirEntryMap.PatchByKey() returned !ok") + os.Exit(1) + } + } else { + if nil != replacedInode { + if syscall.S_IFDIR == (movedInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EISDIR + return + } + + ok, err = newDirInode.dirEntryMap.DeleteByKey(renameIn.NewName) + if nil != err { + globals.logger.Printf("func DoRename(,[Non-Dir]NewName=%s) failed on .dirEntryMap.DeleteByKey(): %v", string(renameIn.NewName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename(,[Non-Dir]NewName=%s) .dirEntryMap.DeleteByKey() returned !ok", string(renameIn.NewName[:])) + os.Exit(1) + } + + replacedInode.attr.NLink-- + + if 0 == replacedInode.attr.NLink { + delete(globals.inodeMap, replacedInode.attr.Ino) + } + } + } + + ok, err = oldDirInode.dirEntryMap.DeleteByKey(renameIn.OldName) + if nil != err { + globals.logger.Printf("func DoRename(,OldName=%s) failed on .dirEntryMap.DeleteByKey(): %v", string(renameIn.OldName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename() .dirEntryMap.DeleteByKey(,OldName=%s) returned !ok", string(renameIn.OldName[:])) + os.Exit(1) + } + + ok, err = newDirInode.dirEntryMap.Put(renameIn.NewName, movedInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoRename(,OldName=%s) failed on .dirEntryMap.Put(): %v", string(renameIn.NewName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename(,NewName=%s) .dirEntryMap.Put() returned !ok", string(renameIn.NewName[:])) + os.Exit(1) + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoLink(inHeader *fission.InHeader, linkIn *fission.LinkIn) (linkOut *fission.LinkOut, errno syscall.Errno) { + var ( + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + oldInode *inodeStruct + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + _, ok, err = dirInode.dirEntryMap.GetByKey(linkIn.Name) + if nil != err { + globals.logger.Printf("func DoLink(NodeID==%v,Name=%s) failed on .dirEntryMap.GetByKey(): %v", inHeader.NodeID, string(linkIn.Name[:]), err) + os.Exit(1) + } + + if ok { + grantedLockSet.freeAll(false) + errno = syscall.EEXIST + return + } + + oldInode, ok = globals.inodeMap[linkIn.OldNodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(oldInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR == (oldInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EISDIR + return + } + + ok, err = dirInode.dirEntryMap.Put(linkIn.Name, oldInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoLink(NodeID==%v,Name=%s) failed on .dirEntryMap.Put(): %v", inHeader.NodeID, string(linkIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoLink(NodeID==%v,Name=%s) .dirEntryMap.Put() returned !ok", inHeader.NodeID, string(linkIn.Name[:])) + os.Exit(1) + } + + oldInode.attr.NLink++ + + linkOut = &fission.LinkOut{ + EntryOut: fission.EntryOut{ + NodeID: oldInode.attr.Ino, + Generation: 0, + EntryValidSec: 0, + AttrValidSec: 0, + EntryValidNSec: 0, + AttrValidNSec: 0, + Attr: fission.Attr{ + Ino: oldInode.attr.Ino, + Size: oldInode.attr.Size, + Blocks: oldInode.attr.Blocks, + ATimeSec: oldInode.attr.ATimeSec, + MTimeSec: oldInode.attr.MTimeSec, + CTimeSec: oldInode.attr.CTimeSec, + ATimeNSec: oldInode.attr.ATimeNSec, + MTimeNSec: oldInode.attr.MTimeNSec, + CTimeNSec: oldInode.attr.CTimeNSec, + Mode: oldInode.attr.Mode, + NLink: oldInode.attr.NLink, + UID: oldInode.attr.UID, + GID: oldInode.attr.GID, + RDev: oldInode.attr.RDev, + BlkSize: oldInode.attr.BlkSize, + Padding: oldInode.attr.Padding, + }, + }, + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoOpen(inHeader *fission.InHeader, openIn *fission.OpenIn) (openOut *fission.OpenOut, errno syscall.Errno) { + var ( + fileInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fileInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(fileInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFREG != (fileInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + if 0 != (openIn.Flags & fission.FOpenRequestTRUNC) { + fileInode.attr.Size = 0 + fileInode.fileData = make([]byte, 0) + } + + globals.lastFH++ + + openOut = &fission.OpenOut{ + FH: globals.lastFH, + OpenFlags: fission.FOpenResponseDirectIO, + Padding: 0, + } + + globals.fhMap[openOut.FH] = fileInode.attr.Ino + fileInode.fhSet[openOut.FH] = struct{}{} + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRead(inHeader *fission.InHeader, readIn *fission.ReadIn) (readOut *fission.ReadOut, errno syscall.Errno) { + var ( + fhNodeID uint64 + fileInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + readOffsetPlusSize uint64 + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fhNodeID, ok = globals.fhMap[readIn.FH] + if fhNodeID != inHeader.NodeID { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + fileInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(fileInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFREG != (fileInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + readOffsetPlusSize = readIn.Offset + uint64(readIn.Size) + + if readIn.Offset < fileInode.attr.Size { + if readOffsetPlusSize <= fileInode.attr.Size { + readOut = &fission.ReadOut{ + Data: cloneByteSlice(fileInode.fileData[readIn.Offset:readOffsetPlusSize]), + } + } else { + readOut = &fission.ReadOut{ + Data: cloneByteSlice(fileInode.fileData[readIn.Offset:]), + } + } + } else { + readOut = &fission.ReadOut{ + Data: make([]byte, 0), + } + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoWrite(inHeader *fission.InHeader, writeIn *fission.WriteIn) (writeOut *fission.WriteOut, errno syscall.Errno) { + var ( + fhNodeID uint64 + fileInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + overwriteSize uint64 + writeOffsetPlusSize uint64 + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fhNodeID, ok = globals.fhMap[writeIn.FH] + if fhNodeID != inHeader.NodeID { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + fileInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(fileInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFREG != (fileInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + writeOffsetPlusSize = writeIn.Offset + uint64(writeIn.Size) + + if writeIn.Offset < fileInode.attr.Size { + if writeOffsetPlusSize <= fileInode.attr.Size { + _ = copy(fileInode.fileData[writeIn.Offset:writeOffsetPlusSize], writeIn.Data) + } else { + overwriteSize = fileInode.attr.Size - writeIn.Offset + + _ = copy(fileInode.fileData[writeIn.Offset:], writeIn.Data[:overwriteSize]) + fileInode.fileData = append(fileInode.fileData, writeIn.Data[overwriteSize:]...) + + fileInode.attr.Size = writeOffsetPlusSize + } + } else { + if writeIn.Offset > fileInode.attr.Size { + fileInode.fileData = append(fileInode.fileData, make([]byte, (writeIn.Offset-fileInode.attr.Size))...) + } + + fileInode.fileData = append(fileInode.fileData, writeIn.Data...) + + fileInode.attr.Size = writeOffsetPlusSize + } + + grantedLockSet.freeAll(false) + + writeOut = &fission.WriteOut{ + Size: writeIn.Size, + Padding: 0, + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoStatFS(inHeader *fission.InHeader) (statFSOut *fission.StatFSOut, errno syscall.Errno) { + statFSOut = &fission.StatFSOut{ + KStatFS: fission.KStatFS{ + Blocks: 0, + BFree: 0, + BAvail: 0, + Files: 0, + FFree: 0, + BSize: 0, + NameLen: 0, + FRSize: 0, + Padding: 0, + Spare: [6]uint32{0, 0, 0, 0, 0, 0}, + }, + } + + // TODO: Fill in the StatFSOut.KStatFS above correctly + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRelease(inHeader *fission.InHeader, releaseIn *fission.ReleaseIn) (errno syscall.Errno) { + var ( + fhNodeID uint64 + fileInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fhNodeID, ok = globals.fhMap[releaseIn.FH] + if fhNodeID != inHeader.NodeID { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + fileInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(fileInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFREG != (fileInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + delete(globals.fhMap, releaseIn.FH) + delete(fileInode.fhSet, releaseIn.FH) + + if (0 == fileInode.attr.NLink) && (0 == len(fileInode.fhSet)) { + delete(globals.inodeMap, inHeader.NodeID) + // Note: Other threads could still be blocked obtaining a lock on fileInode + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoFSync(inHeader *fission.InHeader, fSyncIn *fission.FSyncIn) (errno syscall.Errno) { + var ( + fhNodeID uint64 + fileInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fhNodeID, ok = globals.fhMap[fSyncIn.FH] + if fhNodeID != inHeader.NodeID { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + fileInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(fileInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFREG != (fileInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoSetXAttr(inHeader *fission.InHeader, setXAttrIn *fission.SetXAttrIn) (errno syscall.Errno) { + var ( + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + inode *inodeStruct + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + inode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(inode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + grantedLockSet.free(globals.tryLock) + + ok, err = inode.xattrMap.PatchByKey(setXAttrIn.Name, setXAttrIn.Data) + if nil != err { + globals.logger.Printf("func DoSetXAttr(NodeID==%v, Name==%s) failed on .xattrMap.PatchByKey(): %v", inHeader.NodeID, string(setXAttrIn.Name[:]), err) + os.Exit(1) + } + + if !ok { + ok, err = inode.xattrMap.Put(setXAttrIn.Name, setXAttrIn.Data) + if nil != err { + globals.logger.Printf("func DoSetXAttr(NodeID==%v, Name==%s) failed on .xattrMap.Put(): %v", inHeader.NodeID, string(setXAttrIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoSetXAttr(NodeID==%v, Name==%s) .xattrMap.Put() returned !ok", inHeader.NodeID, string(setXAttrIn.Name[:])) + os.Exit(1) + } + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoGetXAttr(inHeader *fission.InHeader, getXAttrIn *fission.GetXAttrIn) (getXAttrOut *fission.GetXAttrOut, errno syscall.Errno) { + var ( + dataAsByteSlice []byte + dataAsValue sortedmap.Value + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + inode *inodeStruct + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + inode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(inode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + grantedLockSet.free(globals.tryLock) + + dataAsValue, ok, err = inode.xattrMap.GetByKey(getXAttrIn.Name) + if nil != err { + globals.logger.Printf("func DoGetXAttr(NodeID==%v) failed on .xattrMap.GetByKey(): %v", inHeader.NodeID, err) + os.Exit(1) + } + + grantedLockSet.freeAll(false) + + if !ok { + errno = syscall.ENODATA + return + } + + dataAsByteSlice = dataAsValue.([]byte) + + if 0 == getXAttrIn.Size { + getXAttrOut = &fission.GetXAttrOut{ + Size: uint32(len(dataAsByteSlice)), + Padding: 0, + Data: make([]byte, 0), + } + errno = 0 + return + } + + if uint32(len(dataAsByteSlice)) > getXAttrIn.Size { + errno = syscall.ERANGE + return + } + + getXAttrOut = &fission.GetXAttrOut{ + Size: uint32(len(dataAsByteSlice)), + Padding: 0, + Data: cloneByteSlice(dataAsByteSlice), + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoListXAttr(inHeader *fission.InHeader, listXAttrIn *fission.ListXAttrIn) (listXAttrOut *fission.ListXAttrOut, errno syscall.Errno) { + var ( + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + inode *inodeStruct + ok bool + totalSize uint32 + xattrCount int + xattrIndex int + xattrNameAsByteSlice []byte + xattrNameAsKey sortedmap.Key + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + inode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(inode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + grantedLockSet.free(globals.tryLock) + + listXAttrOut = &fission.ListXAttrOut{ + Size: 0, + Padding: 0, + Name: make([][]byte, 0), + } + + xattrCount, err = inode.xattrMap.Len() + if nil != err { + globals.logger.Printf("func DoListXAttr(NodeID==%v) failed on .dirEntryMap.Len(): %v", inHeader.NodeID, err) + os.Exit(1) + } + + totalSize = 0 + + for xattrIndex = 0; xattrIndex < xattrCount; xattrIndex++ { + xattrNameAsKey, _, ok, err = inode.xattrMap.GetByIndex(xattrIndex) + if nil != err { + globals.logger.Printf("func DoGetXAttr(NodeID==%v) failed on .xattrMap.GetByIndex(%d): %v", inHeader.NodeID, xattrIndex, err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoGetXAttr(NodeID==%v) .xattrMap.GetByIndex(%d) returned !ok", inHeader.NodeID, xattrIndex) + os.Exit(1) + } + + xattrNameAsByteSlice = xattrNameAsKey.([]byte) + + if 0 != listXAttrIn.Size { + if (totalSize + uint32(len(xattrNameAsByteSlice)+1)) > listXAttrIn.Size { + grantedLockSet.freeAll(false) + errno = syscall.ERANGE + return + } + } + + totalSize += uint32(len(xattrNameAsByteSlice) + 1) + + if 0 != listXAttrIn.Size { + listXAttrOut.Name = append(listXAttrOut.Name, xattrNameAsByteSlice) + } + } + + grantedLockSet.freeAll(false) + + listXAttrOut.Size = totalSize + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRemoveXAttr(inHeader *fission.InHeader, removeXAttrIn *fission.RemoveXAttrIn) (errno syscall.Errno) { + var ( + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + inode *inodeStruct + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + inode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(inode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + grantedLockSet.free(globals.tryLock) + + ok, err = inode.xattrMap.DeleteByKey(removeXAttrIn.Name) + if nil != err { + globals.logger.Printf("func DoRemoveXAttr(NodeID==%v, Name==%s) failed on .xattrMap.DeleteByKey(): %v", inHeader.NodeID, string(removeXAttrIn.Name[:]), err) + os.Exit(1) + } + + grantedLockSet.freeAll(false) + + if !ok { + errno = syscall.ENOENT + return + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoFlush(inHeader *fission.InHeader, flushIn *fission.FlushIn) (errno syscall.Errno) { + var ( + fhNodeID uint64 + fileInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fhNodeID, ok = globals.fhMap[flushIn.FH] + if fhNodeID != inHeader.NodeID { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + fileInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(fileInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFREG != (fileInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoInit(inHeader *fission.InHeader, initIn *fission.InitIn) (initOut *fission.InitOut, errno syscall.Errno) { + initOut = &fission.InitOut{ + Major: initIn.Major, + Minor: initIn.Minor, + MaxReadAhead: initIn.MaxReadAhead, + Flags: initIn.Flags & initOutFlagsMask, + MaxBackground: initOutMaxBackgound, + CongestionThreshhold: initOutCongestionThreshhold, + MaxWrite: initOutMaxWrite, + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoOpenDir(inHeader *fission.InHeader, openDirIn *fission.OpenDirIn) (openDirOut *fission.OpenDirOut, errno syscall.Errno) { + var ( + dirInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + globals.lastFH++ + + openDirOut = &fission.OpenDirOut{ + FH: globals.lastFH, + OpenFlags: 0, + Padding: 0, + } + + globals.fhMap[openDirOut.FH] = dirInode.attr.Ino + dirInode.fhSet[openDirOut.FH] = struct{}{} + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoReadDir(inHeader *fission.InHeader, readDirIn *fission.ReadDirIn) (readDirOut *fission.ReadDirOut, errno syscall.Errno) { + var ( + dirEntCount int + dirEntIndex int + dirEntInoAsU64 uint64 + dirEntInoAsValue sortedmap.Value + dirEntInode *inodeStruct + dirEntNameAsByteSlice []byte + dirEntNameAsKey sortedmap.Key + dirEntNameLenAligned uint32 + dirEntSize uint32 + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + totalSize uint32 + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + dirEntCount, err = dirInode.dirEntryMap.Len() + if nil != err { + globals.logger.Printf("func DoReadDir(NodeID==%v) failed on .dirEntryMap.Len(): %v", inHeader.NodeID, err) + os.Exit(1) + } + + if uint64(dirEntCount) < readDirIn.Offset { + // Just return an empty ReadDirOut + + grantedLockSet.freeAll(false) + + readDirOut = &fission.ReadDirOut{ + DirEnt: make([]fission.DirEnt, 0), + } + + errno = 0 + return + } + + // Just compute the maximal ReadDirOut... we'll prune it later + + readDirOut = &fission.ReadDirOut{ + DirEnt: make([]fission.DirEnt, dirEntCount), + } + + for dirEntIndex = 0; dirEntIndex < dirEntCount; dirEntIndex++ { + dirEntNameAsKey, dirEntInoAsValue, ok, err = dirInode.dirEntryMap.GetByIndex(dirEntIndex) + if nil != err { + globals.logger.Printf("func DoReadDir(NodeID==%v) failed on .dirEntryMap.GetByIndex(): %v", inHeader.NodeID, err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoReadDir(NodeID==%v) .dirEntryMap.GetByIndex() returned !ok", inHeader.NodeID) + os.Exit(1) + } + + dirEntNameAsByteSlice = dirEntNameAsKey.([]byte) + dirEntInoAsU64 = dirEntInoAsValue.(uint64) + + dirEntInode, ok = globals.inodeMap[dirEntInoAsU64] + if !ok { + globals.logger.Printf("func DoReadDir(NodeID==%v) failed fetching globals.inodeMap[%v]", inHeader.NodeID, dirEntInoAsU64) + os.Exit(1) + } + + granted = grantedLockSet.try(dirEntInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + readDirOut.DirEnt[dirEntIndex] = fission.DirEnt{ + Ino: dirEntInode.attr.Ino, + Off: uint64(dirEntIndex) + 1, + NameLen: uint32(len(dirEntNameAsByteSlice)), // unnecessary + Type: dirEntInode.attr.Mode & syscall.S_IFMT, + Name: cloneByteSlice(dirEntNameAsByteSlice), + } + } + + grantedLockSet.freeAll(false) + + // Now prune on the left to readDirIn.Offset & on the right anything beyond readDirIn.Size + + readDirOut.DirEnt = readDirOut.DirEnt[readDirIn.Offset:] + + totalSize = 0 + + for dirEntIndex = 0; dirEntIndex < len(readDirOut.DirEnt); dirEntIndex++ { + dirEntNameLenAligned = (uint32(len(readDirOut.DirEnt[dirEntIndex].Name)) + (fission.DirEntAlignment - 1)) & ^uint32(fission.DirEntAlignment-1) + dirEntSize = fission.DirEntFixedPortionSize + dirEntNameLenAligned + + if (totalSize + dirEntSize) > readDirIn.Size { + // Truncate readDirOut here and return + + readDirOut.DirEnt = readDirOut.DirEnt[:dirEntIndex] + + errno = 0 + return + } + + totalSize += dirEntSize + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoReleaseDir(inHeader *fission.InHeader, releaseDirIn *fission.ReleaseDirIn) (errno syscall.Errno) { + var ( + dirInode *inodeStruct + fhNodeID uint64 + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fhNodeID, ok = globals.fhMap[releaseDirIn.FH] + if fhNodeID != inHeader.NodeID { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + delete(globals.fhMap, releaseDirIn.FH) + delete(dirInode.fhSet, releaseDirIn.FH) + + if (0 == dirInode.attr.NLink) && (0 == len(dirInode.fhSet)) { + delete(globals.inodeMap, inHeader.NodeID) + // Note: Other threads could still be blocked obtaining a lock on dirInode + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoFSyncDir(inHeader *fission.InHeader, fSyncDirIn *fission.FSyncDirIn) (errno syscall.Errno) { + var ( + fhNodeID uint64 + fileInode *inodeStruct + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + fhNodeID, ok = globals.fhMap[fSyncDirIn.FH] + if fhNodeID != inHeader.NodeID { + grantedLockSet.freeAll(false) + errno = syscall.EINVAL + return + } + + fileInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(fileInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (fileInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoGetLK(inHeader *fission.InHeader, getLKIn *fission.GetLKIn) (getLKOut *fission.GetLKOut, errno syscall.Errno) { + errno = syscall.ENOSYS + return +} +func (dummy *globalsStruct) DoSetLK(inHeader *fission.InHeader, setLKIn *fission.SetLKIn) (errno syscall.Errno) { + errno = syscall.ENOSYS + return +} +func (dummy *globalsStruct) DoSetLKW(inHeader *fission.InHeader, setLKWIn *fission.SetLKWIn) (errno syscall.Errno) { + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoAccess(inHeader *fission.InHeader, accessIn *fission.AccessIn) (errno syscall.Errno) { + var ( + executeGrantedOrNotRequested bool + executeRequested bool + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + inode *inodeStruct + inodeAttrGID uint32 + inodeAttrMode uint32 + inodeAttrModeGroup uint32 + inodeAttrModeOther uint32 + inodeAttrModeOwner uint32 + inodeAttrUID uint32 + isInodeGroup bool + isInodeOwner bool + isRoot bool + ok bool + readGrantedOrNotRequested bool + readRequested bool + writeGrantedOrNotRequested bool + writeRequested bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + inode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(inode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + inodeAttrUID = inode.attr.UID + inodeAttrGID = inode.attr.GID + + inodeAttrMode = inode.attr.Mode + + inodeAttrModeOwner = inodeAttrMode >> accessOwnerShift + inodeAttrModeGroup = inodeAttrMode >> accessGroupShift + inodeAttrModeOther = inodeAttrMode >> accessOtherShift + + grantedLockSet.freeAll(false) + + isRoot = (inHeader.UID == uint32(0)) + + isInodeOwner = (inHeader.UID == inodeAttrUID) + isInodeGroup = (inHeader.GID == inodeAttrGID) + + readRequested = (0 != (accessIn.Mask & accessROK)) + writeRequested = (0 != (accessIn.Mask & accessWOK)) + executeRequested = (0 != (accessIn.Mask & accessXOK)) + + if readRequested { + if isRoot { + readGrantedOrNotRequested = true + } else { + readGrantedOrNotRequested = false + if isInodeOwner && (0 != (inodeAttrModeOwner & accessROK)) { + readGrantedOrNotRequested = true + } + if isInodeGroup && (0 != (inodeAttrModeGroup & accessROK)) { + readGrantedOrNotRequested = true + } + if 0 != (inodeAttrModeOther & accessROK) { + readGrantedOrNotRequested = true + } + } + } else { + readGrantedOrNotRequested = true + } + + if writeRequested { + if isRoot { + writeGrantedOrNotRequested = true + } else { + writeGrantedOrNotRequested = false + if isInodeOwner && (0 != (inodeAttrModeOwner & accessWOK)) { + writeGrantedOrNotRequested = true + } + if isInodeGroup && (0 != (inodeAttrModeGroup & accessWOK)) { + writeGrantedOrNotRequested = true + } + if 0 != (inodeAttrModeOther & accessWOK) { + writeGrantedOrNotRequested = true + } + } + } else { + writeGrantedOrNotRequested = true + } + + if executeRequested { + if isRoot { + executeGrantedOrNotRequested = false + if 0 != (inodeAttrModeOwner & accessXOK) { + executeGrantedOrNotRequested = true + } + if 0 != (inodeAttrModeGroup & accessXOK) { + executeGrantedOrNotRequested = true + } + if 0 != (inodeAttrModeOther & accessXOK) { + executeGrantedOrNotRequested = true + } + } else { + executeGrantedOrNotRequested = false + if isInodeOwner && (0 != (inodeAttrModeOwner & accessXOK)) { + executeGrantedOrNotRequested = true + } + if isInodeGroup && (0 != (inodeAttrModeGroup & accessXOK)) { + executeGrantedOrNotRequested = true + } + if 0 != (inodeAttrModeOther & accessXOK) { + executeGrantedOrNotRequested = true + } + } + } else { + executeGrantedOrNotRequested = true + } + + if readGrantedOrNotRequested && writeGrantedOrNotRequested && executeGrantedOrNotRequested { + errno = 0 + } else { + errno = syscall.EACCES + } + + return +} + +func (dummy *globalsStruct) DoCreate(inHeader *fission.InHeader, createIn *fission.CreateIn) (createOut *fission.CreateOut, errno syscall.Errno) { + var ( + dirInode *inodeStruct + err error + fileInode *inodeStruct + fileInodeMode uint32 + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + ok bool + unixTimeNowNSec uint32 + unixTimeNowSec uint64 + ) + + fileInodeMode = uint32(syscall.S_IRWXU | syscall.S_IRWXG | syscall.S_IRWXO) + fileInodeMode &= createIn.Mode + fileInodeMode &= ^createIn.UMask + fileInodeMode |= syscall.S_IFREG + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + _, ok, err = dirInode.dirEntryMap.GetByKey(createIn.Name) + if nil != err { + globals.logger.Printf("func DoCreate(NodeID==%v,Name=%s) failed on .dirEntryMap.GetByKey(): %v", inHeader.NodeID, string(createIn.Name[:]), err) + os.Exit(1) + } + + if ok { + grantedLockSet.freeAll(false) + errno = syscall.EEXIST + return + } + + globals.lastNodeID++ + + unixTimeNowSec, unixTimeNowNSec = unixTimeNow() + + fileInode = &inodeStruct{ + tryLock: makeTryLock(), + attr: fission.Attr{ + Ino: globals.lastNodeID, + Size: 0, + Blocks: 0, + ATimeSec: unixTimeNowSec, + MTimeSec: unixTimeNowSec, + CTimeSec: unixTimeNowSec, + ATimeNSec: unixTimeNowNSec, + MTimeNSec: unixTimeNowNSec, + CTimeNSec: unixTimeNowNSec, + Mode: fileInodeMode, + NLink: 1, + UID: inHeader.UID, + GID: inHeader.GID, + RDev: 0, + BlkSize: attrBlkSize, + Padding: 0, + }, + xattrMap: sortedmap.NewLLRBTree(sortedmap.CompareByteSlice, globals.xattrMapDummy), + dirEntryMap: nil, + fileData: make([]byte, 0), + symlinkData: nil, + fhSet: make(map[uint64]struct{}), + } + + ok, err = dirInode.dirEntryMap.Put(createIn.Name, fileInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoCreate(NodeID==%v,Name=%s) failed on .dirEntryMap.Put(): %v", inHeader.NodeID, string(createIn.Name[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoCreate(NodeID==%v,Name=%s) .dirEntryMap.Put() returned !ok", inHeader.NodeID, string(createIn.Name[:])) + os.Exit(1) + } + + globals.inodeMap[fileInode.attr.Ino] = fileInode + + globals.lastFH++ + + createOut = &fission.CreateOut{ + EntryOut: fission.EntryOut{ + NodeID: fileInode.attr.Ino, + Generation: 0, + EntryValidSec: 0, + AttrValidSec: 0, + EntryValidNSec: 0, + AttrValidNSec: 0, + Attr: fission.Attr{ + Ino: fileInode.attr.Ino, + Size: fileInode.attr.Size, + Blocks: fileInode.attr.Blocks, + ATimeSec: fileInode.attr.ATimeSec, + MTimeSec: fileInode.attr.MTimeSec, + CTimeSec: fileInode.attr.CTimeSec, + ATimeNSec: fileInode.attr.ATimeNSec, + MTimeNSec: fileInode.attr.MTimeNSec, + CTimeNSec: fileInode.attr.CTimeNSec, + Mode: fileInode.attr.Mode, + NLink: fileInode.attr.NLink, + UID: fileInode.attr.UID, + GID: fileInode.attr.GID, + RDev: fileInode.attr.RDev, + BlkSize: fileInode.attr.BlkSize, + Padding: fileInode.attr.Padding, + }, + }, + FH: globals.lastFH, + OpenFlags: fission.FOpenResponseDirectIO, + Padding: 0, + } + + globals.fhMap[createOut.FH] = fileInode.attr.Ino + fileInode.fhSet[createOut.FH] = struct{}{} + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoInterrupt(inHeader *fission.InHeader, interruptIn *fission.InterruptIn) { + return +} + +func (dummy *globalsStruct) DoBMap(inHeader *fission.InHeader, bMapIn *fission.BMapIn) (bMapOut *fission.BMapOut, errno syscall.Errno) { + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoDestroy(inHeader *fission.InHeader) { + os.Exit(0) +} + +func (dummy *globalsStruct) DoPoll(inHeader *fission.InHeader, pollIn *fission.PollIn) (pollOut *fission.PollOut, errno syscall.Errno) { + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoBatchForget(inHeader *fission.InHeader, batchForgetIn *fission.BatchForgetIn) { + return +} + +func (dummy *globalsStruct) DoFAllocate(inHeader *fission.InHeader, fAllocateIn *fission.FAllocateIn) (errno syscall.Errno) { + errno = syscall.ENOSYS + return +} + +func (dummy *globalsStruct) DoReadDirPlus(inHeader *fission.InHeader, readDirPlusIn *fission.ReadDirPlusIn) (readDirPlusOut *fission.ReadDirPlusOut, errno syscall.Errno) { + var ( + dirEntInoAsU64 uint64 + dirEntInoAsValue sortedmap.Value + dirEntInode *inodeStruct + dirEntNameAsByteSlice []byte + dirEntNameAsKey sortedmap.Key + dirEntNameLenAligned uint32 + dirEntSize uint32 + dirEntPlusCount int + dirEntPlusIndex int + dirInode *inodeStruct + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + totalSize uint32 + ok bool + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + dirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(dirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (dirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + dirEntPlusCount, err = dirInode.dirEntryMap.Len() + if nil != err { + globals.logger.Printf("func DoReadDirPlus(NodeID==%v) failed on .dirEntryMap.Len(): %v", inHeader.NodeID, err) + os.Exit(1) + } + + if uint64(dirEntPlusCount) < readDirPlusIn.Offset { + // Just return an empty ReadDirPlusOut + + grantedLockSet.freeAll(false) + + readDirPlusOut = &fission.ReadDirPlusOut{ + DirEntPlus: make([]fission.DirEntPlus, 0), + } + + errno = 0 + return + } + + // Just compute the maximal ReadDirPlusOut... we'll prune it later + + readDirPlusOut = &fission.ReadDirPlusOut{ + DirEntPlus: make([]fission.DirEntPlus, dirEntPlusCount), + } + + for dirEntPlusIndex = 0; dirEntPlusIndex < dirEntPlusCount; dirEntPlusIndex++ { + dirEntNameAsKey, dirEntInoAsValue, ok, err = dirInode.dirEntryMap.GetByIndex(dirEntPlusIndex) + if nil != err { + globals.logger.Printf("func DoReadDirPlus(NodeID==%v) failed on .dirEntryMap.GetByIndex(): %v", inHeader.NodeID, err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoReadDirPlus(NodeID==%v) .dirEntryMap.GetByIndex() returned !ok", inHeader.NodeID) + os.Exit(1) + } + + dirEntNameAsByteSlice = dirEntNameAsKey.([]byte) + dirEntInoAsU64 = dirEntInoAsValue.(uint64) + + dirEntInode, ok = globals.inodeMap[dirEntInoAsU64] + if !ok { + globals.logger.Printf("func DoReadDirPlus(NodeID==%v) failed fetching globals.inodeMap[%v]", inHeader.NodeID, dirEntInoAsU64) + os.Exit(1) + } + + granted = grantedLockSet.try(dirEntInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + readDirPlusOut.DirEntPlus[dirEntPlusIndex] = fission.DirEntPlus{ + EntryOut: fission.EntryOut{ + NodeID: dirEntInode.attr.Ino, + Generation: 0, + EntryValidSec: 0, + AttrValidSec: 0, + EntryValidNSec: 0, + AttrValidNSec: 0, + Attr: fission.Attr{ + Ino: dirEntInode.attr.Ino, + Size: dirEntInode.attr.Size, + Blocks: dirEntInode.attr.Blocks, + ATimeSec: dirEntInode.attr.ATimeSec, + MTimeSec: dirEntInode.attr.MTimeSec, + CTimeSec: dirEntInode.attr.CTimeSec, + ATimeNSec: dirEntInode.attr.ATimeNSec, + MTimeNSec: dirEntInode.attr.MTimeNSec, + CTimeNSec: dirEntInode.attr.CTimeNSec, + Mode: dirEntInode.attr.Mode, + NLink: dirEntInode.attr.NLink, + UID: dirEntInode.attr.UID, + GID: dirEntInode.attr.GID, + RDev: dirEntInode.attr.RDev, + BlkSize: dirEntInode.attr.BlkSize, + Padding: dirEntInode.attr.Padding, + }, + }, + DirEnt: fission.DirEnt{ + Ino: dirEntInode.attr.Ino, + Off: uint64(dirEntPlusIndex) + 1, + NameLen: uint32(len(dirEntNameAsByteSlice)), // unnecessary + Type: dirEntInode.attr.Mode & syscall.S_IFMT, + Name: cloneByteSlice(dirEntNameAsByteSlice), + }, + } + } + + grantedLockSet.freeAll(false) + + // Now prune on the left to readDirPlusIn.Offset & on the right anything beyond readDirPlusIn.Size + + readDirPlusOut.DirEntPlus = readDirPlusOut.DirEntPlus[readDirPlusIn.Offset:] + + totalSize = 0 + + for dirEntPlusIndex = 0; dirEntPlusIndex < len(readDirPlusOut.DirEntPlus); dirEntPlusIndex++ { + dirEntNameLenAligned = (uint32(len(readDirPlusOut.DirEntPlus[dirEntPlusIndex].Name)) + (fission.DirEntAlignment - 1)) & ^uint32(fission.DirEntAlignment-1) + dirEntSize = fission.DirEntPlusFixedPortionSize + dirEntNameLenAligned + + if (totalSize + dirEntSize) > readDirPlusIn.Size { + // Truncate readDirPlusOut here and return + + readDirPlusOut.DirEntPlus = readDirPlusOut.DirEntPlus[:dirEntPlusIndex] + + errno = 0 + return + } + + totalSize += dirEntSize + } + + errno = 0 + return +} + +func (dummy *globalsStruct) DoRename2(inHeader *fission.InHeader, rename2In *fission.Rename2In) (errno syscall.Errno) { + var ( + err error + granted bool + grantedLockSet *grantedLockSetStruct = makeGrantedLockSet() + movedInode *inodeStruct + movedInodeNodeIDAsU64 uint64 + movedInodeNodeIDAsValue sortedmap.Value + newDirInode *inodeStruct + ok bool + oldDirInode *inodeStruct + replacedInode *inodeStruct + replacedInodeDirEntryMapLen int + replacedInodeNodeIDAsU64 uint64 + replacedInodeNodeIDAsValue sortedmap.Value + ) + +Restart: + grantedLockSet.get(globals.tryLock) + + oldDirInode, ok = globals.inodeMap[inHeader.NodeID] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(oldDirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (oldDirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + if inHeader.NodeID == rename2In.NewDir { + newDirInode = oldDirInode + } else { + newDirInode, ok = globals.inodeMap[rename2In.NewDir] + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + granted = grantedLockSet.try(newDirInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + if syscall.S_IFDIR != (newDirInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + } + + movedInodeNodeIDAsValue, ok, err = oldDirInode.dirEntryMap.GetByKey(rename2In.OldName) + if nil != err { + globals.logger.Printf("func DoRename2(,OldName=%s) failed on .dirEntryMap.GetByKey(): %v", string(rename2In.OldName[:]), err) + os.Exit(1) + } + if !ok { + grantedLockSet.freeAll(false) + errno = syscall.ENOENT + return + } + + movedInodeNodeIDAsU64 = movedInodeNodeIDAsValue.(uint64) + + movedInode, ok = globals.inodeMap[movedInodeNodeIDAsU64] + if !ok { + globals.logger.Printf("func DoRename2(,OldName=%s) globals.inodeMap[movedInodeNodeIDAsU64] returned !ok", string(rename2In.OldName[:])) + os.Exit(1) + } + + granted = grantedLockSet.try(movedInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + + replacedInodeNodeIDAsValue, ok, err = newDirInode.dirEntryMap.GetByKey(rename2In.NewName) + if nil != err { + globals.logger.Printf("func DoRename2(,NewName=%s) failed on .dirEntryMap.GetByKey(): %v", string(rename2In.NewName[:]), err) + os.Exit(1) + } + + if ok { + replacedInodeNodeIDAsU64 = replacedInodeNodeIDAsValue.(uint64) + + replacedInode, ok = globals.inodeMap[replacedInodeNodeIDAsU64] + if !ok { + globals.logger.Printf("func DoRename2(,NewName=%s) globals.inodeMap[replacedInodeNodeIDAsU64] returned !ok", string(rename2In.NewName[:])) + os.Exit(1) + } + + granted = grantedLockSet.try(movedInode.tryLock) + if !granted { + grantedLockSet.freeAll(true) + goto Restart + } + } else { + replacedInode = nil + } + + if syscall.S_IFDIR == (movedInode.attr.Mode & syscall.S_IFMT) { + if nil != replacedInode { + if syscall.S_IFDIR != (movedInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.ENOTDIR + return + } + + replacedInodeDirEntryMapLen, err = replacedInode.dirEntryMap.Len() + if nil != err { + globals.logger.Printf("func DoRename2(,NewName=%s) failed on .dirEntryMap.Len(): %v", string(rename2In.NewName[:]), err) + os.Exit(1) + } + + if 2 != replacedInodeDirEntryMapLen { + grantedLockSet.freeAll(false) + errno = syscall.EEXIST + return + } + + ok, err = newDirInode.dirEntryMap.DeleteByKey(rename2In.NewName) + if nil != err { + globals.logger.Printf("func DoRename2(,[Dir]NewName=%s) failed on .dirEntryMap.DeleteByKey(): %v", string(rename2In.NewName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename2(,[Dir]NewName=%s) .dirEntryMap.DeleteByKey() returned !ok", string(rename2In.NewName[:])) + os.Exit(1) + } + + newDirInode.attr.NLink-- + + delete(globals.inodeMap, replacedInode.attr.Ino) + } + + oldDirInode.attr.NLink-- + newDirInode.attr.NLink++ + + ok, err = movedInode.dirEntryMap.PatchByKey([]byte(".."), newDirInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoRename2() failed on .dirEntryMap.PatchByKey(): %v", err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename2() .dirEntryMap.PatchByKey() returned !ok") + os.Exit(1) + } + } else { + if nil != replacedInode { + if syscall.S_IFDIR == (movedInode.attr.Mode & syscall.S_IFMT) { + grantedLockSet.freeAll(false) + errno = syscall.EISDIR + return + } + + ok, err = newDirInode.dirEntryMap.DeleteByKey(rename2In.NewName) + if nil != err { + globals.logger.Printf("func DoRename2(,[Non-Dir]NewName=%s) failed on .dirEntryMap.DeleteByKey(): %v", string(rename2In.NewName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename2(,[Non-Dir]NewName=%s) .dirEntryMap.DeleteByKey() returned !ok", string(rename2In.NewName[:])) + os.Exit(1) + } + + replacedInode.attr.NLink-- + + if 0 == replacedInode.attr.NLink { + delete(globals.inodeMap, replacedInode.attr.Ino) + } + } + } + + ok, err = oldDirInode.dirEntryMap.DeleteByKey(rename2In.OldName) + if nil != err { + globals.logger.Printf("func DoRename2(,OldName=%s) failed on .dirEntryMap.DeleteByKey(): %v", string(rename2In.OldName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename2() .dirEntryMap.DeleteByKey(,OldName=%s) returned !ok", string(rename2In.OldName[:])) + os.Exit(1) + } + + ok, err = newDirInode.dirEntryMap.Put(rename2In.NewName, movedInode.attr.Ino) + if nil != err { + globals.logger.Printf("func DoRename2(,OldName=%s) failed on .dirEntryMap.Put(): %v", string(rename2In.NewName[:]), err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("func DoRename2(,NewName=%s) .dirEntryMap.Put() returned !ok", string(rename2In.NewName[:])) + os.Exit(1) + } + + grantedLockSet.freeAll(false) + + errno = 0 + return +} + +func (dummy *globalsStruct) DoLSeek(inHeader *fission.InHeader, lSeekIn *fission.LSeekIn) (lSeekOut *fission.LSeekOut, errno syscall.Errno) { + errno = syscall.ENOSYS + return +} diff --git a/vendor/github.com/swiftstack/fission/examples/fission-ramfs/main.go b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/main.go new file mode 100644 index 000000000..567b250a6 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/examples/fission-ramfs/main.go @@ -0,0 +1,334 @@ +package main + +import ( + "fmt" + "log" + "math/rand" + "os" + "os/signal" + "path" + "syscall" + "time" + + "golang.org/x/sys/unix" + + "github.com/swiftstack/sortedmap" + + "github.com/swiftstack/fission" +) + +const ( + mountFlags = uintptr(0) + + initOutFlagsMask = fission.InitFlagsAsyncRead | fission.InitFlagsBigWrites | fission.InitFlagsDontMask | fission.InitFlagsAutoInvalData | fission.InitFlagsDoReadDirPlus + initOutMaxBackgound = uint16(100) + initOutCongestionThreshhold = uint16(0) + initOutMaxWrite = uint32(128 * 1024) // 128KiB... the max write size in Linux FUSE at this time + + attrBlkSize = 4096 + + tryLockBackoffMin = time.Duration(time.Second) // time.Duration(100 * time.Microsecond) + tryLockBackoffMax = time.Duration(time.Second) // time.Duration(300 * time.Microsecond) + + accessROK = syscall.S_IROTH // surprisingly not defined as syscall.R_OK + accessWOK = syscall.S_IWOTH // surprisingly not defined as syscall.W_OK + accessXOK = syscall.S_IXOTH // surprisingly not defined as syscall.X_OK + + accessMask = syscall.S_IRWXO // used to mask Owner, Group, or Other RWX bits + accessOwnerShift = 6 + accessGroupShift = 3 + accessOtherShift = 0 +) + +type tryLockStruct struct { + lockChan chan struct{} // initialized with make(chan struct{}, 1) to have the lock initially granted +} + +type grantedLockSetStruct struct { + set map[*tryLockStruct]uint64 // Value is # of times it is locked +} + +type inodeStruct struct { + tryLock *tryLockStruct + attr fission.Attr // (attr.Mode&syscall.S_IFMT) must be one of syscall.{S_IFDIR|S_IFREG|S_IFLNK} + xattrMap sortedmap.LLRBTree // key is xattr Name ([]byte); value is xattr Data ([]byte) + dirEntryMap sortedmap.LLRBTree // [S_IFDIR only] key is basename of dirEntry ([]byte); value is attr.Ino (uint64) + fileData []byte // [S_IFREG only] zero-filled up to attr.Size contents of file + symlinkData []byte // [S_IFLNK only] target path of symlink + fhSet map[uint64]struct{} // key is FH +} + +type xattrMapDummyStruct struct{} +type dirEntryMapDummyStruct struct{} + +type globalsStruct struct { + tryLock *tryLockStruct + programPath string + mountPoint string + programName string + volumeName string + logger *log.Logger + errChan chan error + xattrMapDummy *xattrMapDummyStruct + dirEntryMapDummy *dirEntryMapDummyStruct + inodeMap map[uint64]*inodeStruct // key is inodeStruct.atr.Ino + lastNodeID uint64 // valid NodeID's start at 1... but 1 is the RootDir NodeID + fhMap map[uint64]uint64 // key is FH; value is inodeStruct.attr.Ino + lastFH uint64 // valid FH's start at 1 + volume fission.Volume +} + +var globals globalsStruct + +func main() { + var ( + err error + ok bool + rootInode *inodeStruct + signalChan chan os.Signal + unixTimeNowNSec uint32 + unixTimeNowSec uint64 + ) + + if 2 != len(os.Args) { + fmt.Printf("Usage: %s \n", os.Args[0]) + os.Exit(0) + } + + globals.tryLock = makeTryLock() + + globals.programPath = os.Args[0] + globals.mountPoint = os.Args[1] + + globals.programName = path.Base(globals.programPath) + globals.volumeName = path.Base(globals.mountPoint) + + globals.logger = log.New(os.Stdout, "", log.Ldate|log.Ltime) // |log.Lmicroseconds|log.Lshortfile + + globals.errChan = make(chan error, 1) + + globals.xattrMapDummy = nil + globals.dirEntryMapDummy = nil + + unixTimeNowSec, unixTimeNowNSec = unixTimeNow() + + rootInode = &inodeStruct{ + tryLock: makeTryLock(), + attr: fission.Attr{ + Ino: 1, + Size: 0, + Blocks: 0, + ATimeSec: unixTimeNowSec, + MTimeSec: unixTimeNowSec, + CTimeSec: unixTimeNowSec, + ATimeNSec: unixTimeNowNSec, + MTimeNSec: unixTimeNowNSec, + CTimeNSec: unixTimeNowNSec, + Mode: uint32(syscall.S_IFDIR | syscall.S_IRWXU | syscall.S_IRWXG | syscall.S_IRWXO), + NLink: 2, + UID: 0, + GID: 0, + RDev: 0, + BlkSize: attrBlkSize, + Padding: 0, + }, + xattrMap: sortedmap.NewLLRBTree(sortedmap.CompareByteSlice, globals.xattrMapDummy), + dirEntryMap: sortedmap.NewLLRBTree(sortedmap.CompareByteSlice, globals.dirEntryMapDummy), + fileData: nil, + symlinkData: nil, + fhSet: make(map[uint64]struct{}), + } + + ok, err = rootInode.dirEntryMap.Put([]byte("."), uint64(1)) + if nil != err { + globals.logger.Printf("rootInode.dirEntryMap.Put([]byte(\".\"), uint64(1)) failed: %v", err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("rootInode.dirEntryMap.Put([]byte(\".\"), uint64(1)) returned !ok") + os.Exit(1) + } + ok, err = rootInode.dirEntryMap.Put([]byte(".."), uint64(1)) + if nil != err { + globals.logger.Printf("rootInode.dirEntryMap.Put([]byte(\"..\"), uint64(1)) failed: %v", err) + os.Exit(1) + } + if !ok { + globals.logger.Printf("rootInode.dirEntryMap.Put([]byte(\"..\"), uint64(1)) returned !ok") + os.Exit(1) + } + + globals.inodeMap = make(map[uint64]*inodeStruct) + + globals.inodeMap[1] = rootInode + + globals.lastNodeID = uint64(1) // since we used NodeID 1 for the RootDir NodeID + + globals.fhMap = make(map[uint64]uint64) + globals.lastFH = uint64(0) + + globals.volume = fission.NewVolume(globals.volumeName, globals.mountPoint, mountFlags, initOutMaxWrite, &globals, globals.logger, globals.errChan) + + err = globals.volume.DoMount() + if nil != err { + globals.logger.Printf("fission.DoMount() failed: %v", err) + os.Exit(1) + } + + signalChan = make(chan os.Signal, 1) + signal.Notify(signalChan, unix.SIGINT, unix.SIGTERM, unix.SIGHUP) + + select { + case _ = <-signalChan: + // Normal termination due to one of the above registered signals + case err = <-globals.errChan: + // Unexpected exit of /dev/fuse read loop since it's before we call DoUnmount() + globals.logger.Printf("unexpected exit of /dev/fuse read loop: %v", err) + } + + err = globals.volume.DoUnmount() + if nil != err { + globals.logger.Printf("fission.DoUnmount() failed: %v", err) + os.Exit(2) + } +} + +func makeTryLock() (tryLock *tryLockStruct) { + tryLock = &tryLockStruct{lockChan: make(chan struct{}, 1)} + tryLock.lockChan <- struct{}{} + return +} + +func makeGrantedLockSet() (grantedLockSet *grantedLockSetStruct) { + grantedLockSet = &grantedLockSetStruct{set: make(map[*tryLockStruct]uint64)} + return +} + +func (grantedLockSet *grantedLockSetStruct) get(tryLock *tryLockStruct) { + var ( + lockCount uint64 + ok bool + ) + + lockCount, ok = grantedLockSet.set[tryLock] + + if ok { + lockCount++ + grantedLockSet.set[tryLock] = lockCount + return + } + + _ = <-tryLock.lockChan + + grantedLockSet.set[tryLock] = 1 +} + +func (grantedLockSet *grantedLockSetStruct) try(tryLock *tryLockStruct) (granted bool) { + var ( + lockCount uint64 + ok bool + ) + + lockCount, ok = grantedLockSet.set[tryLock] + + if ok { + lockCount++ + grantedLockSet.set[tryLock] = lockCount + granted = true + return + } + + select { + case _ = <-tryLock.lockChan: + granted = true + grantedLockSet.set[tryLock] = 1 + default: + granted = false + } + + return +} + +func (grantedLockSet *grantedLockSetStruct) free(tryLock *tryLockStruct) { + var ( + lockCount uint64 + ) + + lockCount = grantedLockSet.set[tryLock] + + lockCount-- + + if 0 == lockCount { + tryLock.lockChan <- struct{}{} + delete(grantedLockSet.set, tryLock) + } else { + grantedLockSet.set[tryLock] = lockCount + } +} + +func (grantedLockSet *grantedLockSetStruct) freeAll(andDelay bool) { + var ( + tryLock *tryLockStruct + tryLockBackoff time.Duration + ) + + for tryLock = range grantedLockSet.set { + tryLock.lockChan <- struct{}{} + delete(grantedLockSet.set, tryLock) + } + + if andDelay { + tryLockBackoff = tryLockBackoffMin + time.Duration(rand.Int63n(int64(tryLockBackoffMax)-int64(tryLockBackoffMin)+1)) + time.Sleep(tryLockBackoff) + } +} + +func unixTimeToGoTime(unixTimeSec uint64, unixTimeNSec uint32) (goTime time.Time) { + goTime = time.Unix(int64(unixTimeSec), int64(unixTimeNSec)) + return +} +func goTimeToUnixTime(goTime time.Time) (unixTimeSec uint64, unixTimeNSec uint32) { + var ( + unixTime uint64 + ) + unixTime = uint64(goTime.UnixNano()) + unixTimeSec = unixTime / 1e9 + unixTimeNSec = uint32(unixTime - (unixTimeSec * 1e9)) + return +} +func unixTimeNow() (unixTimeNowSec uint64, unixTimeNowNSec uint32) { + unixTimeNowSec, unixTimeNowNSec = goTimeToUnixTime(time.Now()) + return +} + +func cloneByteSlice(inBuf []byte) (outBuf []byte) { + outBuf = make([]byte, len(inBuf)) + if 0 != len(inBuf) { + _ = copy(outBuf, inBuf) + } + return +} + +func (dummy *xattrMapDummyStruct) DumpKey(key sortedmap.Key) (keyAsString string, err error) { + keyAsString = string(key.([]byte)[:]) + err = nil + return +} + +func (dummy *xattrMapDummyStruct) DumpValue(value sortedmap.Value) (valueAsString string, err error) { + valueAsString = fmt.Sprintf("%v", value.([]byte)) + err = nil + return +} + +func (dummy *dirEntryMapDummyStruct) DumpKey(key sortedmap.Key) (keyAsString string, err error) { + keyAsString = string(key.([]byte)[:]) + err = nil + return +} + +func (dummy *dirEntryMapDummyStruct) DumpValue(value sortedmap.Value) (valueAsString string, err error) { + valueAsString = fmt.Sprintf("%d", value.(uint64)) + err = nil + return +} diff --git a/vendor/github.com/swiftstack/fission/volume.go b/vendor/github.com/swiftstack/fission/volume.go new file mode 100644 index 000000000..cf51bde80 --- /dev/null +++ b/vendor/github.com/swiftstack/fission/volume.go @@ -0,0 +1,361 @@ +package fission + +import ( + "fmt" + "log" + "strings" + "sync" + "syscall" + "unsafe" +) + +type volumeStruct struct { + volumeName string + mountpointDirPath string + mountFlags uintptr + initOutMaxWrite uint32 + callbacks Callbacks + logger *log.Logger + errChan chan error + devFuseFDReadSize uint32 // InHeaderSize + WriteInSize + InitOut.MaxWrite + devFuseFDReadPool sync.Pool + devFuseFD int + devFuseFDReaderWG sync.WaitGroup + callbacksWG sync.WaitGroup +} + +func newVolume(volumeName string, mountpointDirPath string, mountFlags uintptr, initOutMaxWrite uint32, callbacks Callbacks, logger *log.Logger, errChan chan error) (volume *volumeStruct) { + volume = &volumeStruct{ + volumeName: volumeName, + mountpointDirPath: mountpointDirPath, + mountFlags: mountFlags, + initOutMaxWrite: initOutMaxWrite, + callbacks: callbacks, + logger: logger, + errChan: errChan, + devFuseFDReadSize: InHeaderSize + WriteInFixedPortionSize + initOutMaxWrite, + } + + volume.devFuseFDReadPool = sync.Pool{ + New: func() interface{} { + return make([]byte, volume.devFuseFDReadSize) // len == cap + }, + } + + return +} + +func (volume *volumeStruct) DoMount() (err error) { + var ( + allowOtherOption string + devFuseFDMountOption string + gid int + gidMountOption string + mountOptions string + rootMode uint32 + rootModeMountOption string + uid int + uidMountOption string + ) + + _ = syscall.Unmount(volume.mountpointDirPath, syscall.MNT_FORCE) + + volume.devFuseFD, err = syscall.Open("/dev/fuse", syscall.O_RDWR|syscall.O_CLOEXEC, 0) + if nil != err { + volume.logger.Printf("Volume %s unable to open /dev/fuse", volume.volumeName) + return + } + + volume.devFuseFDReaderWG.Add(1) + go volume.devFuseFDReader() + + devFuseFDMountOption = fmt.Sprintf("fd=%d", volume.devFuseFD) + + rootMode = syscall.S_IFDIR + rootModeMountOption = fmt.Sprintf("rootmode=%o", rootMode) + + uid = syscall.Geteuid() + gid = syscall.Getegid() + + uidMountOption = fmt.Sprintf("user_id=%d", uid) + gidMountOption = fmt.Sprintf("group_id=%d", gid) + allowOtherOption = "allow_other" + + mountOptions = devFuseFDMountOption + + "," + rootModeMountOption + + "," + uidMountOption + + "," + gidMountOption + + "," + allowOtherOption + + err = syscall.Mount(volume.volumeName, volume.mountpointDirPath, "fuse", volume.mountFlags, mountOptions) + if nil == err { + volume.logger.Printf("Volume %s mounted on mountpoint %s", volume.volumeName, volume.mountpointDirPath) + } else { + volume.logger.Printf("Volume %s mount on mountpoint %s failed: %v", volume.volumeName, volume.mountpointDirPath, err) + _ = syscall.Close(volume.devFuseFD) + volume.devFuseFDReaderWG.Wait() + } + + return +} + +func (volume *volumeStruct) DoUnmount() (err error) { + err = syscall.Unmount(volume.mountpointDirPath, syscall.MNT_FORCE) + if nil != err { + volume.logger.Printf("Unable to unmount %s: %v", volume.mountpointDirPath, err) + return + } + + err = syscall.Close(volume.devFuseFD) + if nil != err { + volume.logger.Printf("Unable to close /dev/fuse: %v", err) + return + } + + volume.devFuseFDReaderWG.Wait() + + volume.logger.Printf("Volume %s unmounted from mountpoint %s", volume.volumeName, volume.mountpointDirPath) + + return +} + +func (volume *volumeStruct) devFuseFDReadPoolGet() (devFuseFDReadBuf []byte) { + devFuseFDReadBuf = volume.devFuseFDReadPool.Get().([]byte) + return +} + +func (volume *volumeStruct) devFuseFDReadPoolPut(devFuseFDReadBuf []byte) { + devFuseFDReadBuf = devFuseFDReadBuf[:cap(devFuseFDReadBuf)] // len == cap + volume.devFuseFDReadPool.Put(devFuseFDReadBuf) +} + +func (volume *volumeStruct) devFuseFDReader() { + var ( + bytesRead int + devFuseFDReadBuf []byte + err error + ) + + for { + devFuseFDReadBuf = volume.devFuseFDReadPoolGet() + + bytesRead, err = syscall.Read(volume.devFuseFD, devFuseFDReadBuf) + if nil != err { + if 0 == strings.Compare("operation not permitted", err.Error()) { + // Special case... simply retry the Read + continue + } + + // Time to exit...but first await outstanding Callbacks + + volume.callbacksWG.Wait() + volume.devFuseFDReaderWG.Done() + + // Signal errChan that we are exiting (passing if due to close of volume.devFuseFD) + + if 0 == strings.Compare("no such device", err.Error()) { + volume.errChan <- nil + } else { + volume.logger.Printf("Exiting due to /dev/fuse Read err: %v", err) + volume.errChan <- err + } + + return + } + + devFuseFDReadBuf = devFuseFDReadBuf[:bytesRead] + + // Dispatch goroutine to process devFuseFDReadBuf + + volume.callbacksWG.Add(1) + go volume.processDevFuseFDReadBuf(devFuseFDReadBuf) + } +} + +func (volume *volumeStruct) processDevFuseFDReadBuf(devFuseFDReadBuf []byte) { + var ( + inHeader *InHeader + ) + + if len(devFuseFDReadBuf) < InHeaderSize { + // All we can do is just drop it + volume.logger.Printf("Read malformed message from /dev/fuse") + volume.devFuseFDReadPoolPut(devFuseFDReadBuf) + volume.callbacksWG.Done() + return + } + + inHeader = &InHeader{ + Len: *(*uint32)(unsafe.Pointer(&devFuseFDReadBuf[0])), + OpCode: *(*uint32)(unsafe.Pointer(&devFuseFDReadBuf[4])), + Unique: *(*uint64)(unsafe.Pointer(&devFuseFDReadBuf[8])), + NodeID: *(*uint64)(unsafe.Pointer(&devFuseFDReadBuf[16])), + UID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBuf[24])), + GID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBuf[28])), + PID: *(*uint32)(unsafe.Pointer(&devFuseFDReadBuf[32])), + Padding: *(*uint32)(unsafe.Pointer(&devFuseFDReadBuf[36])), + } + + switch inHeader.OpCode { + case OpCodeLookup: + volume.doLookup(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeForget: + volume.doForget(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeGetAttr: + volume.doGetAttr(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeSetAttr: + volume.doSetAttr(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeReadLink: + volume.doReadLink(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeSymLink: + volume.doSymLink(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeMkNod: + volume.doMkNod(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeMkDir: + volume.doMkDir(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeUnlink: + volume.doUnlink(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeRmDir: + volume.doRmDir(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeRename: + volume.doRename(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeLink: + volume.doLink(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeOpen: + volume.doOpen(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeRead: + volume.doRead(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeWrite: + volume.doWrite(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeStatFS: + volume.doStatFS(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeRelease: + volume.doRelease(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeFSync: + volume.doFSync(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeSetXAttr: + volume.doSetXAttr(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeGetXAttr: + volume.doGetXAttr(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeListXAttr: + volume.doListXAttr(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeRemoveXAttr: + volume.doRemoveXAttr(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeFlush: + volume.doFlush(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeInit: + volume.doInit(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeOpenDir: + volume.doOpenDir(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeReadDir: + volume.doReadDir(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeReleaseDir: + volume.doReleaseDir(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeFSyncDir: + volume.doFSyncDir(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeGetLK: + volume.doGetLK(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeSetLK: + volume.doSetLK(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeSetLKW: + volume.doSetLKW(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeAccess: + volume.doAccess(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeCreate: + volume.doCreate(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeInterrupt: + volume.doInterrupt(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeBMap: + volume.doBMap(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeDestroy: + volume.doDestroy(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeIoCtl: + volume.devFuseFDWriter(inHeader, syscall.EINVAL) + case OpCodePoll: + volume.doPoll(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeBatchForget: + volume.doBatchForget(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeFAllocate: + volume.doFAllocate(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeReadDirPlus: + volume.doReadDirPlus(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeRename2: + volume.doRename2(inHeader, devFuseFDReadBuf[InHeaderSize:]) + case OpCodeLSeek: + volume.doLSeek(inHeader, devFuseFDReadBuf[InHeaderSize:]) + default: + volume.devFuseFDWriter(inHeader, syscall.ENOSYS) + } + + volume.devFuseFDReadPoolPut(devFuseFDReadBuf) + volume.callbacksWG.Done() +} + +func (volume *volumeStruct) devFuseFDWriter(inHeader *InHeader, errno syscall.Errno, bufs ...[]byte) { + var ( + buf []byte + bytesWritten uintptr + iovec []syscall.Iovec + iovecSpan uintptr + outHeader []byte + ) + + // First, log any syscall.ENOSYS responses + + if syscall.ENOSYS == errno { + volume.logger.Printf("Read unsupported/unrecognized message OpCode == %v", inHeader.OpCode) + } + + // Construct outHeader w/out knowing iovecSpan and put it in iovec[0] + + outHeader = make([]byte, OutHeaderSize) + + iovecSpan = uintptr(OutHeaderSize) + + *(*uint32)(unsafe.Pointer(&outHeader[0])) = uint32(iovecSpan) // Updated later + *(*int32)(unsafe.Pointer(&outHeader[4])) = -int32(errno) + *(*uint64)(unsafe.Pointer(&outHeader[8])) = inHeader.Unique + + iovec = make([]syscall.Iovec, 1, len(bufs)+1) + + iovec[0] = syscall.Iovec{Base: &outHeader[0], Len: uint64(OutHeaderSize)} + + // Construct iovec elements for supplied bufs (if any) + + for _, buf = range bufs { + if 0 != len(buf) { + iovec = append(iovec, syscall.Iovec{Base: &buf[0], Len: uint64(len(buf))}) + iovecSpan += uintptr(len(buf)) + } + } + + // Now go back and update outHeader + + *(*uint32)(unsafe.Pointer(&outHeader[0])) = uint32(iovecSpan) + + // Finally, send iovec to /dev/fuse + + bytesWritten, _, errno = syscall.Syscall( + syscall.SYS_WRITEV, + uintptr(volume.devFuseFD), + uintptr(unsafe.Pointer(&iovec[0])), + uintptr(len(iovec))) + if 0 == errno { + if bytesWritten != iovecSpan { + volume.logger.Printf("Write to /dev/fuse returned bad bytesWritten: %v", bytesWritten) + } + } else { + volume.logger.Printf("Write to /dev/fuse returned bad errno: %v", errno) + } +} + +func cloneByteSlice(inBuf []byte, andTrimTrailingNullByte bool) (outBuf []byte) { + outBuf = make([]byte, len(inBuf)) + if 0 != len(inBuf) { + _ = copy(outBuf, inBuf) + if andTrimTrailingNullByte && (0 == outBuf[len(outBuf)-1]) { + outBuf = outBuf[:len(outBuf)-1] + } + } + return +}