From 6455d351354a4fb033c2798edd6c6fff09b0806b Mon Sep 17 00:00:00 2001 From: Ed McClanahan Date: Tue, 7 Jul 2020 12:41:43 -0700 Subject: [PATCH 1/2] Honor O_TRUNC option on open() calls --- pfsagentd/fission.go | 380 +++++++++++++++++++++++++------------------ 1 file changed, 226 insertions(+), 154 deletions(-) diff --git a/pfsagentd/fission.go b/pfsagentd/fission.go index 8e0eaf996..6002a89b4 100644 --- a/pfsagentd/fission.go +++ b/pfsagentd/fission.go @@ -344,218 +344,283 @@ func (dummy *globalsStruct) DoGetAttr(inHeader *fission.InHeader, getAttrIn *fis return } -func (dummy *globalsStruct) DoSetAttr(inHeader *fission.InHeader, setAttrIn *fission.SetAttrIn) (setAttrOut *fission.SetAttrOut, errno syscall.Errno) { +func setMode(nodeID uint64, mode uint32) (errno syscall.Errno) { + var ( + chmodReply *jrpcfs.Reply + chmodRequest *jrpcfs.ChmodRequest + err error + ) + + chmodRequest = &jrpcfs.ChmodRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(nodeID), + }, + FileMode: mode & uint32(os.ModePerm), + } + + chmodReply = &jrpcfs.Reply{} + + err = globals.retryRPCClient.Send("RpcChmod", chmodRequest, chmodReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func setUIDAndOrGID(nodeID uint64, settingUID bool, uid uint32, settingGID bool, gid uint32) (errno syscall.Errno) { + var ( + chownReply *jrpcfs.Reply + chownRequest *jrpcfs.ChownRequest + err error + ) + + chownRequest = &jrpcfs.ChownRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(nodeID), + }, + } + + if settingUID { + chownRequest.UserID = int32(uid) + } else { + chownRequest.UserID = -1 + } + + if settingGID { + chownRequest.GroupID = int32(gid) + } else { + chownRequest.GroupID = -1 + } + + chownReply = &jrpcfs.Reply{} + + err = globals.retryRPCClient.Send("RpcChown", chownRequest, chownReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } + + errno = 0 + return +} + +func setSize(nodeID uint64, size uint64) (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 chunkedPutContextLast *chunkedPutContextStruct 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) + fileInode = referenceFileInode(inode.InodeNumber(nodeID)) - // Since we are modifying it, invalidate cached stat if present + fileInode.doFlushIfNecessary() - globals.Lock() - fileInode, ok = globals.fileInodeMap[inode.InodeNumber(inHeader.NodeID)] - if ok { - fileInode.cachedStat = nil - } - globals.Unlock() + grantedLock = fileInode.getExclusiveLock() - 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 + if fileInode.extentMapFileSize > size { + fileInode.extentMapFileSize = size } - settingMode = (0 != (setAttrIn.Valid & fission.SetAttrInValidMode)) - - settingUID = (0 != (setAttrIn.Valid & fission.SetAttrInValidUID)) - settingGID = (0 != (setAttrIn.Valid & fission.SetAttrInValidGID)) + pruneExtentMap(fileInode.extentMap, size) - settingSize = (0 != (setAttrIn.Valid & fission.SetAttrInValidSize)) + chunkedPutContextLast = nil - settingAtime = (0 != (setAttrIn.Valid & fission.SetAttrInValidATime)) || (0 != (setAttrIn.Valid & fission.SetAttrInValidATimeNow)) - settingMtime = (0 != (setAttrIn.Valid & fission.SetAttrInValidMTime)) || (0 != (setAttrIn.Valid & fission.SetAttrInValidMTimeNow)) + chunkedPutContextElement = fileInode.chunkedPutList.Front() - settingAtimeAndOrMtime = settingAtime || settingMtime + for nil != chunkedPutContextElement { + chunkedPutContext, ok = chunkedPutContextElement.Value.(*chunkedPutContextStruct) + if !ok { + logFatalf("chunkedPutContextElement.Value.(*chunkedPutContextStruct) returned !ok") + } - // 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 + chunkedPutContextLast = chunkedPutContext - if settingMode { - chmodRequest = &jrpcfs.ChmodRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(inHeader.NodeID), - }, - FileMode: setAttrIn.Mode & uint32(os.ModePerm), + if chunkedPutContext.fileSize > size { + chunkedPutContext.fileSize = size } - chmodReply = &jrpcfs.Reply{} + pruneExtentMap(chunkedPutContext.extentMap, size) - err = globals.retryRPCClient.Send("RpcChmod", chmodRequest, chmodReply) - if nil != err { - errno = convertErrToErrno(err, syscall.EIO) - return - } + chunkedPutContextElement = chunkedPutContextElement.Next() } - if settingUID || settingGID { - chownRequest = &jrpcfs.ChownRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(inHeader.NodeID), - }, + if nil == chunkedPutContextLast { + if fileInode.extentMapFileSize < size { + fileInode.extentMapFileSize = size } - - if settingUID { - chownRequest.UserID = int32(setAttrIn.UID) - } else { - chownRequest.UserID = -1 + } else { + if chunkedPutContext.fileSize < size { + chunkedPutContext.fileSize = size } + } - if settingGID { - chownRequest.GroupID = int32(setAttrIn.GID) - } else { - chownRequest.GroupID = -1 - } + resizeRequest = &jrpcfs.ResizeRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(nodeID), + }, + NewSize: size, + } - chownReply = &jrpcfs.Reply{} + resizeReply = &jrpcfs.Reply{} - err = globals.retryRPCClient.Send("RpcChown", chownRequest, chownReply) - if nil != err { - errno = convertErrToErrno(err, syscall.EIO) - return - } + err = globals.retryRPCClient.Send("RpcResize", resizeRequest, resizeReply) + if nil != err { + grantedLock.release() + fileInode.dereference() + errno = convertErrToErrno(err, syscall.EIO) + return } - if settingSize { - fileInode = referenceFileInode(inode.InodeNumber(inHeader.NodeID)) + grantedLock.release() - fileInode.doFlushIfNecessary() + fileInode.dereference() - grantedLock = fileInode.getExclusiveLock() + errno = 0 + return +} - if fileInode.extentMapFileSize > setAttrIn.Size { - fileInode.extentMapFileSize = setAttrIn.Size - } +func setMTimeAndOrATime(nodeID uint64, settingMTime bool, settingMTimeNow bool, mTimeSec uint64, mTimeNSec uint32, settingATime bool, settingATimeNow bool, aTimeSec uint64, aTimeNSec uint32) (errno syscall.Errno) { + var ( + err error + setTimeReply *jrpcfs.Reply + setTimeRequest *jrpcfs.SetTimeRequest + timeNow time.Time + ) - pruneExtentMap(fileInode.extentMap, setAttrIn.Size) + setTimeRequest = &jrpcfs.SetTimeRequest{ + InodeHandle: jrpcfs.InodeHandle{ + MountID: globals.mountID, + InodeNumber: int64(nodeID), + }, + StatStruct: jrpcfs.StatStruct{ + MTimeNs: uint64(0), // Updated below if settingMTime + ATimeNs: uint64(0), // Updated below if settingATime + }, + } - chunkedPutContextLast = nil + timeNow = time.Now() - chunkedPutContextElement = fileInode.chunkedPutList.Front() + if settingMTime { + if settingMTimeNow { + setTimeRequest.MTimeNs = uint64(timeNow.UnixNano()) + } else { + setTimeRequest.MTimeNs = unixTimeToNs(mTimeSec, mTimeNSec) + } + } + if settingATime { + if settingATimeNow { + setTimeRequest.ATimeNs = uint64(timeNow.UnixNano()) + } else { + setTimeRequest.ATimeNs = unixTimeToNs(aTimeSec, aTimeNSec) + } + } - for nil != chunkedPutContextElement { - chunkedPutContext, ok = chunkedPutContextElement.Value.(*chunkedPutContextStruct) - if !ok { - logFatalf("chunkedPutContextElement.Value.(*chunkedPutContextStruct) returned !ok") - } + setTimeReply = &jrpcfs.Reply{} - chunkedPutContextLast = chunkedPutContext + err = globals.retryRPCClient.Send("RpcSetTime", setTimeRequest, setTimeReply) + if nil != err { + errno = convertErrToErrno(err, syscall.EIO) + return + } - if chunkedPutContext.fileSize > setAttrIn.Size { - chunkedPutContext.fileSize = setAttrIn.Size - } + errno = 0 + return +} - pruneExtentMap(chunkedPutContext.extentMap, setAttrIn.Size) +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 + err error + fileInode *fileInodeStruct + getStatReply *jrpcfs.StatStruct + getStatRequest *jrpcfs.GetStatRequest + mTimeNSec uint32 + mTimeSec uint64 + ok bool + settingATime bool + settingATimeNow bool + settingGID bool + settingMode bool + settingMTime bool + settingMTimeAndOrATime bool + settingMTimeNow bool + settingSize bool + settingUID bool + ) - chunkedPutContextElement = chunkedPutContextElement.Next() - } + _ = atomic.AddUint64(&globals.metrics.FUSE_DoSetAttr_calls, 1) - if nil == chunkedPutContextLast { - if fileInode.extentMapFileSize < setAttrIn.Size { - fileInode.extentMapFileSize = setAttrIn.Size - } - } else { - if chunkedPutContext.fileSize < setAttrIn.Size { - chunkedPutContext.fileSize = setAttrIn.Size - } - } + // Since we are modifying it, invalidate cached stat if present - resizeRequest = &jrpcfs.ResizeRequest{ - InodeHandle: jrpcfs.InodeHandle{ - MountID: globals.mountID, - InodeNumber: int64(inHeader.NodeID), - }, - NewSize: setAttrIn.Size, - } + globals.Lock() + fileInode, ok = globals.fileInodeMap[inode.InodeNumber(inHeader.NodeID)] + if ok { + fileInode.cachedStat = nil + } + globals.Unlock() - resizeReply = &jrpcfs.Reply{} + 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 + } - err = globals.retryRPCClient.Send("RpcResize", resizeRequest, resizeReply) - if nil != err { - errno = convertErrToErrno(err, syscall.EIO) - return - } + settingMode = (0 != (setAttrIn.Valid & fission.SetAttrInValidMode)) - grantedLock.release() + settingUID = (0 != (setAttrIn.Valid & fission.SetAttrInValidUID)) + settingGID = (0 != (setAttrIn.Valid & fission.SetAttrInValidGID)) - fileInode.dereference() - } + settingSize = (0 != (setAttrIn.Valid & fission.SetAttrInValidSize)) - 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 - }, - } + settingMTime = (0 != (setAttrIn.Valid & fission.SetAttrInValidMTime)) || (0 != (setAttrIn.Valid & fission.SetAttrInValidMTimeNow)) + settingATime = (0 != (setAttrIn.Valid & fission.SetAttrInValidATime)) || (0 != (setAttrIn.Valid & fission.SetAttrInValidATimeNow)) - timeNow = time.Now() + settingMTimeNow = settingMTime && (0 != (setAttrIn.Valid & fission.SetAttrInValidMTimeNow)) + settingATimeNow = settingATime && (0 != (setAttrIn.Valid & fission.SetAttrInValidATimeNow)) - if settingMtime { - if 0 != (setAttrIn.Valid & fission.SetAttrInValidMTimeNow) { - setTimeRequest.MTimeNs = uint64(timeNow.UnixNano()) - } else { - setTimeRequest.MTimeNs = unixTimeToNs(setAttrIn.MTimeSec, setAttrIn.MTimeNSec) - } + settingMTimeAndOrATime = 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 { + errno = setMode(inHeader.NodeID, setAttrIn.Mode) + if 0 != errno { + return } - if settingAtime { - if 0 != (setAttrIn.Valid & fission.SetAttrInValidATimeNow) { - setTimeRequest.ATimeNs = uint64(timeNow.UnixNano()) - } else { - setTimeRequest.ATimeNs = unixTimeToNs(setAttrIn.ATimeSec, setAttrIn.ATimeNSec) - } + } + + if settingUID || settingGID { + errno = setUIDAndOrGID(inHeader.NodeID, settingUID, setAttrIn.UID, settingGID, setAttrIn.GID) + if 0 != errno { + return } + } - setTimeReply = &jrpcfs.Reply{} + if settingSize { + errno = setSize(inHeader.NodeID, setAttrIn.Size) + if 0 != errno { + return + } + } - err = globals.retryRPCClient.Send("RpcSetTime", setTimeRequest, setTimeReply) - if nil != err { - errno = convertErrToErrno(err, syscall.EIO) + if settingMTimeAndOrATime { + errno = setMTimeAndOrATime(inHeader.NodeID, settingMTime, settingMTimeNow, setAttrIn.MTimeSec, setAttrIn.MTimeNSec, settingATime, settingATimeNow, setAttrIn.ATimeSec, setAttrIn.ATimeNSec) + if 0 != errno { return } } @@ -1056,6 +1121,13 @@ func (dummy *globalsStruct) DoOpen(inHeader *fission.InHeader, openIn *fission.O globals.Unlock() + if 0 != (openIn.Flags & fission.FOpenRequestTRUNC) { + errno = setSize(inHeader.NodeID, 0) + if 0 != errno { + return + } + } + errno = 0 return } From 7a17648585eba516c84c5a847b2ee7e50ab84983 Mon Sep 17 00:00:00 2001 From: Ed McClanahan Date: Tue, 7 Jul 2020 16:34:20 -0700 Subject: [PATCH 2/2] Update release_notes.md for 1.16.4 --- release_notes.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/release_notes.md b/release_notes.md index efc45a968..57c12b83b 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,5 +1,12 @@ # ProxyFS Release Notes +## 1.16.4 (July 7, 2020) + +### Bug Fixes: + +Opening a file with the O_TRUNC option in a PFSAgent-mounted file +system would fail to truncate a pre-existing file. + ## 1.16.3 (July 2, 2020) ### Bug Fixes: