void rpc_task::run_setattr()

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