in turbonfs/src/nfs_inode.cpp [1865:2019]
bool nfs_inode::update_nolock(const struct fattr3 *postattr,
const struct wcc_attr *preattr)
{
/*
* We must be called with at least one of preop or postop attributes.
* Operations that do not change file/dir, they will only get postop
* attributes from the server.
* Update operations that change file/dir, they will get both postop and
* preop attributes for success case and for failure cases they may not
* get the postop attributes.
*/
assert(preattr || postattr);
#ifdef ENABLE_PARANOID
/*
* XXX This assert has been seen to fail (for unlink).
*/
#if 0
if (preattr && postattr) {
/*
* ctime cannot go back.
*/
assert(compare_nfstime(postattr->ctime, preattr->ctime) >= 0);
}
#endif
#endif
/*
* If postattr are present and they do not have a newer ctime than the
* cached attributes, then our cache (both attributes and data if any) is
* uptodate.
*/
if (postattr) {
const bool postattr_is_newer =
(compare_timespec_and_nfstime(attr.st_ctim, postattr->ctime) == -1);
if (!postattr_is_newer) {
/*
* Attributes haven't changed from the cached ones, refresh the
* attribute cache timeout.
*/
assert(attr_timeout_timestamp != -1);
assert(attr_timeout_secs != -1);
attr_timeout_timestamp =
std::max(get_current_msecs() + attr_timeout_secs*1000,
attr_timeout_timestamp.load());
return false;
}
}
/*
* Either postattr is not provided (rare) or postattr has a newer ctime
* than the cached attributes. Latter could mean either file/dir data has
* changed (in which case we need to invalidate our cached data) or just
* the file/dir metadata has changed (in which case we don't invalidate the
* cached data and just update the inode attributes).
* For the "has file/dir data changed" check we use the preop attributes if
* provided, else we use the postop attributes. Note that requests which
* change file/dir will receive both preop and postop attributes from the
* server and for such requests we need to check cached attributes against
* the preop attributes to ignore changes done by the request itself. Other
* requests which do not change file/dir only have the postop attributes for
* this check.
* Note that we consider file/dir data as changed when either the mtime or
* the size changes.
*/
const nfstime3 *pmtime = preattr ? &preattr->mtime : &postattr->mtime;
const nfstime3 *pctime = preattr ? &preattr->ctime : &postattr->ctime;
const size3 *psize = preattr ? &preattr->size : &postattr->size;
const bool file_data_changed =
((compare_timespec_and_nfstime(attr.st_mtim, *pmtime) != 0) ||
(attr.st_size != (off_t) *psize));
/*
* Update cached attributes and also reset the attr_timeout_secs and
* attr_timeout_timestamp since the attributes have changed.
*/
if (postattr) {
AZLogDebug("[{}:{}] Got attributes newer than cached attributes, "
"ctime: {}.{} -> {}.{}, mtime: {}.{} -> {}.{}, "
"size: {} -> {}",
get_filetype_coding(), get_fuse_ino(),
attr.st_ctim.tv_sec, attr.st_ctim.tv_nsec,
postattr->ctime.seconds, postattr->ctime.nseconds,
attr.st_mtim.tv_sec, attr.st_mtim.tv_nsec,
postattr->mtime.seconds, postattr->mtime.nseconds,
attr.st_size, postattr->size);
/*
* TODO: Nitin to uncomment this along with his change that defines
* cached file size.
*/
#if 0
/*
* If the file has been truncated in the server (mostly by some other
* client), we need to drop the extra cached data that we may have, else
* a subsequent reader may be incorrectly returned that extra data which
* is no longer part of the file.
*/
if (has_filecache() &&
(postattr->size < (uint64_t) attr.st_size)) {
get_filecache()->truncate(postattr->size);
}
#endif
nfs_client::stat_from_fattr3(attr, *postattr);
attr_timeout_secs = get_actimeo_min();
attr_timeout_timestamp = get_current_msecs() + attr_timeout_secs*1000;
// file type should not change.
assert((attr.st_mode & S_IFMT) == file_type);
}
/*
* Invalidate cache iff file data has changed.
*
* Note: This does not flush the dirty membufs, those will be flushed
* later when we decide to flush the cache. This means if some
* other client has written to the same parts of the file as
* this node, those will be overwritten when we flush our cache.
* This is not something unexpected as multiple writers updating
* a file w/o coordinating using file locks is expected to result
* in undefined results.
* This also means that if another client has truncated the file
* we will reduce the file size in our saved nfs_inode::attr.
* Later when we flush the dirty membufs the size will be updated
* if some of those membufs write past the file.
*
* Note: For the rare case where server doesn't provide postop attributes
* but only preop attributes, we might invalidate the cached data
* and not update the cached attributes. This would cause the next
* wcc data to also cause cache invalidation, untill we update the
* cached attributes. This should not be common case and in case
* it happens we will effectively run w/o attribute and data cache,
* which is safe.
* XXX We don't update ctime/mtime/size from preop attr even if they
* are more recent.
*/
if (file_data_changed) {
AZLogDebug("[{}:{}] {} changed at server, "
"ctime: {}.{} -> {}.{}, mtime: {}.{} -> {}.{}, "
"size: {} -> {}",
get_filetype_coding(), get_fuse_ino(),
is_dir() ? "Directory" : "File",
pctime->seconds, pctime->nseconds,
attr.st_ctim.tv_sec, attr.st_ctim.tv_nsec,
pmtime->seconds, pmtime->nseconds,
attr.st_mtim.tv_sec, attr.st_mtim.tv_nsec,
*psize, attr.st_size);
invalidate_cache();
}
return true;
}