in turbonfs/src/rpc_task.cpp [2031:2142]
void unlink_callback(
struct rpc_context *rpc,
int rpc_status,
void *data,
void *private_data)
{
rpc_task *task = (rpc_task*) private_data;
assert(task->magic == RPC_TASK_MAGIC);
auto res = (REMOVE3res*)data;
#if 0
/*
* Don't inject jukebox for non-idempotent requests.
*/
INJECT_JUKEBOX(res, task);
#endif
const fuse_ino_t parent_ino =
task->rpc_api->unlink_task.get_parent_ino();
struct nfs_inode *parent_inode =
task->get_client()->get_nfs_inode_from_ino(parent_ino);
// Are we unlinking a silly-renamed file?
const bool for_silly_rename =
task->rpc_api->unlink_task.get_for_silly_rename();
int status = task->status(rpc_status, NFS_STATUS(res));
const bool noent_is_success =
(rpc_pdu_is_retransmitted(rpc_get_pdu(rpc)) &&
aznfsc_cfg.sys.nodrc.remove_noent_as_success);
/*
* Now that the request has completed, we can query libnfs for the
* dispatch time.
*/
task->get_stats().on_rpc_complete(rpc_get_pdu(rpc), NFS_STATUSX(rpc_status, res));
if (NFS_STATUS(res) == NFS3ERR_JUKEBOX) {
task->get_client()->jukebox_retry(task);
} else if (NFS_STATUS(res) == NFS3ERR_NOENT && noent_is_success) {
AZLogWarn("[{}/{}] unlink_callback{}: Treating NFS3ERR_NOENT as "
"success for a retransmitted REMOVE RPC",
parent_ino, task->rpc_api->unlink_task.get_file_name(),
for_silly_rename ? "(silly_rename)" : "");
status = 0;
goto handle_success;
} else {
handle_success:
/*
* REMOVE3res_u.resok and REMOVE3res_u.resfail have the same layout,
* so we can safely access REMOVE3res_u.resok for both success and
* failure cases.
*/
static_assert(sizeof(res->REMOVE3res_u.resok) ==
sizeof(res->REMOVE3res_u.resfail));
if (status == 0) {
if (!res->REMOVE3res_u.resok.dir_wcc.after.attributes_follow) {
AZLogDebug("[{}] Postop attributes not received for unlink, "
"invalidating parent attribute cache", parent_ino);
/*
* Since the post-op attributes are not populated for the parent
* directory, invalidate the cache as the attributes may no longer
* be valid since unlink() would have changed the parent directory
* attributes.
*/
parent_inode->invalidate_attribute_cache();
}
/*
* See comment above readdirectory_cache::lookuponly, why we don't
* need to call UPDATE_INODE_ATTR() to invalidate the
* readdirectory_cache.
*/
if (aznfsc_cfg.cache.attr.user.enable) {
UPDATE_INODE_WCC(parent_inode, res->REMOVE3res_u.resok.dir_wcc);
} else {
UPDATE_INODE_ATTR(parent_inode, res->REMOVE3res_u.resok.dir_wcc.after);
}
} else {
AZLogError("[{}/{}] unlink_callback{}: failed with status {}",
parent_ino, task->rpc_api->unlink_task.get_file_name(),
for_silly_rename ? "(silly_rename)" : "", status);
}
if (task->get_fuse_req()) {
task->reply_error(status);
} else {
/*
* Only when rename_callback() calls inode->release() for silly
* renamed file, do we pass fuse req as nullptr, as that's not
* in response to a user request.
*/
assert(for_silly_rename);
task->free_rpc_task();
}
/*
* Drop parent directory refcnt taken in rename_callback().
* Note that we drop the refcnt irrespective of the unlink status.
* This is done as fuse ignores any error returns from release()
* which means the inode will be forgotten and hence we must drop
* the parent directory inode ref which was taken to have a valid
* parent directory inode till the child inode is present.
* For jukebox we will retry the rename and drop the parent dir
* ref when the unlink completes.
*/
if (for_silly_rename) {
parent_inode->decref();
}
}
}