in turbonfs/src/rpc_task.cpp [3250:3380]
void rpc_task::run_setattr()
{
auto ino = rpc_api->setattr_task.get_ino();
struct nfs_inode *inode = get_client()->get_nfs_inode_from_ino(ino);
const struct stat *attr = rpc_api->setattr_task.get_attr();
const int valid = rpc_api->setattr_task.get_attr_flags_to_set();
bool rpc_retry;
/*
* If this is a setattr(mtime) call called for updating mtime of a file
* under write in writeback mode, skip the call and return cached
* attributes. Note that write requests sent to NFS server will correctly
* update the mtime so we don't need to do that.
* Since fuse doesn't provide us a way to turn off these setattr(mtime)
* calls, we have this hack.
*/
if ((valid && !(valid & ~FUSE_SET_ATTR_MTIME)) &&
inode->skip_mtime_update(attr->st_mtim)) {
/*
* Set fuse kernel attribute cache timeout to the current attribute
* cache timeout for this inode, as per the recent revalidation
* experience.
*/
AZLogDebug("[{}] Skipping mtime update", ino);
reply_attr(inode->get_attr(), inode->get_actimeo());
return;
}
do {
SETATTR3args args;
::memset(&args, 0, sizeof(args));
args.object = inode->get_fh();
if (valid & FUSE_SET_ATTR_MODE) {
AZLogDebug("Setting mode to 0{:o}", attr->st_mode);
args.new_attributes.mode.set_it = 1;
args.new_attributes.mode.set_mode3_u.mode = attr->st_mode;
}
if (valid & FUSE_SET_ATTR_UID) {
AZLogDebug("Setting uid to {}", attr->st_uid);
args.new_attributes.uid.set_it = 1;
args.new_attributes.uid.set_uid3_u.uid = attr->st_uid;
}
if (valid & FUSE_SET_ATTR_GID) {
AZLogDebug("Setting gid to {}", attr->st_gid);
args.new_attributes.gid.set_it = 1;
args.new_attributes.gid.set_gid3_u.gid = attr->st_gid;
}
if (valid & FUSE_SET_ATTR_SIZE) {
// Truncate the cache to reflect the size.
if (inode->has_filecache()) {
AZLogDebug("[{}]: Truncating file size to {}",
ino, attr->st_size);
/*
* Note: This can block for a long time, as it waits for all
* ongoing IOs to finish.
*/
inode->truncate_start(attr->st_size);
}
AZLogDebug("Setting size to {}", attr->st_size);
args.new_attributes.size.set_it = 1;
args.new_attributes.size.set_size3_u.size = attr->st_size;
}
if (valid & FUSE_SET_ATTR_ATIME) {
// TODO: These log are causing crash, look at it later.
// AZLogDebug("Setting atime to {}", attr->st_atim.tv_sec);
args.new_attributes.atime.set_it = SET_TO_CLIENT_TIME;
args.new_attributes.atime.set_atime_u.atime.seconds =
attr->st_atim.tv_sec;
args.new_attributes.atime.set_atime_u.atime.nseconds =
attr->st_atim.tv_nsec;
}
if (valid & FUSE_SET_ATTR_MTIME) {
// TODO: These log are causing crash, look at it later.
// AZLogDebug("Setting mtime to {}", attr->st_mtim.tv_sec);
args.new_attributes.mtime.set_it = SET_TO_CLIENT_TIME;
args.new_attributes.mtime.set_mtime_u.mtime.seconds =
attr->st_mtim.tv_sec;
args.new_attributes.mtime.set_mtime_u.mtime.nseconds =
attr->st_mtim.tv_nsec;
}
if (valid & FUSE_SET_ATTR_ATIME_NOW) {
args.new_attributes.atime.set_it = SET_TO_SERVER_TIME;
}
if (valid & FUSE_SET_ATTR_MTIME_NOW) {
args.new_attributes.mtime.set_it = SET_TO_SERVER_TIME;
}
rpc_retry = false;
stats.on_rpc_issue();
if (rpc_nfs3_setattr_task(get_rpc_ctx(), setattr_callback, &args,
this) == NULL) {
stats.on_rpc_cancel();
/*
* Most common reason for this is memory allocation failure,
* hence wait for some time before retrying. Also block the
* current thread as we really want to slow down things.
*
* TODO: For soft mount should we fail this?
*/
rpc_retry = true;
AZLogWarn("rpc_nfs3_setattr_task failed to issue, retrying "
"after 5 secs!");
/*
* Undo truncate_start(), before retrying.
*/
if (valid & FUSE_SET_ATTR_SIZE) {
if (inode->has_filecache()) {
AZLogDebug("[{}]: Releasing flush_lock", ino);
inode->truncate_end(attr->st_size);
}
}
::sleep(5);
}
} while (rpc_retry);
}