bool nfs_inode::update_nolock()

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;
}