Example #1
0
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
			    u64 attr_valid, u64 attr_version)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_inode *fi = get_fuse_inode(inode);
	loff_t oldsize;

	spin_lock(&fc->lock);
	if (attr_version != 0 && fi->attr_version > attr_version) {
		spin_unlock(&fc->lock);
		return;
	}

	fuse_change_attributes_common(inode, attr, attr_valid);

	oldsize = inode->i_size;
	i_size_write(inode, attr->size);
	spin_unlock(&fc->lock);

	if (S_ISREG(inode->i_mode) && oldsize != attr->size) {
		if (attr->size < oldsize)
			fuse_truncate(inode->i_mapping, attr->size);
		invalidate_inode_pages2(inode->i_mapping);
	}
}
Example #2
0
/*
 * Set attributes, and at the same time refresh them.
 *
 * Truncation is slightly complicated, because the 'truncate' request
 * may fail, in which case we don't want to touch the mapping.
 * vmtruncate() doesn't allow for this case, so do the rlimit checking
 * and the actual truncation by hand.
 */
static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
                           struct file *file)
{
    struct inode *inode = entry->d_inode;
    struct fuse_conn *fc = get_fuse_conn(inode);
    struct fuse_req *req;
    struct fuse_setattr_in inarg;
    struct fuse_attr_out outarg;
    bool is_truncate = false;
    loff_t oldsize;
    int err;

    if (!fuse_allow_task(fc, current))
        return -EACCES;

    if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
        err = inode_change_ok(inode, attr);
        if (err)
            return err;
    }

    if ((attr->ia_valid & ATTR_OPEN) && fc->atomic_o_trunc)
        return 0;

    if (attr->ia_valid & ATTR_SIZE) {
        unsigned long limit;
        if (IS_SWAPFILE(inode))
            return -ETXTBSY;
        limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
        if (limit != RLIM_INFINITY && attr->ia_size > (loff_t) limit) {
            send_sig(SIGXFSZ, current, 0);
            return -EFBIG;
        }
        is_truncate = true;
    }

    req = fuse_get_req(fc);
    if (IS_ERR(req))
        return PTR_ERR(req);

    if (is_truncate)
        fuse_set_nowrite(inode);

    memset(&inarg, 0, sizeof(inarg));
    memset(&outarg, 0, sizeof(outarg));
    iattr_to_fattr(attr, &inarg);
    if (file) {
        struct fuse_file *ff = file->private_data;
        inarg.valid |= FATTR_FH;
        inarg.fh = ff->fh;
    }
    if (attr->ia_valid & ATTR_SIZE) {
        /* For mandatory locking in truncate */
        inarg.valid |= FATTR_LOCKOWNER;
        inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
    }
    req->in.h.opcode = FUSE_SETATTR;
    req->in.h.nodeid = get_node_id(inode);
    req->in.numargs = 1;
    req->in.args[0].size = sizeof(inarg);
    req->in.args[0].value = &inarg;
    req->out.numargs = 1;
    if (fc->minor < 9)
        req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
    else
        req->out.args[0].size = sizeof(outarg);
    req->out.args[0].value = &outarg;
    request_send(fc, req);
    err = req->out.h.error;
    fuse_put_request(fc, req);
    if (err) {
        if (err == -EINTR)
            fuse_invalidate_attr(inode);
        goto error;
    }

    if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
        make_bad_inode(inode);
        err = -EIO;
        goto error;
    }

    spin_lock(&fc->lock);
    fuse_change_attributes_common(inode, &outarg.attr,
                                  attr_timeout(&outarg));
    oldsize = inode->i_size;
    i_size_write(inode, outarg.attr.size);

    if (is_truncate) {
        /* NOTE: this may release/reacquire fc->lock */
        __fuse_release_nowrite(inode);
    }
    spin_unlock(&fc->lock);

    /*
     * Only call invalidate_inode_pages2() after removing
     * FUSE_NOWRITE, otherwise fuse_launder_page() would deadlock.
     */
    if (S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
        if (outarg.attr.size < oldsize)
            fuse_truncate(inode->i_mapping, outarg.attr.size);
        invalidate_inode_pages2(inode->i_mapping);
    }

    return 0;

error:
    if (is_truncate)
        fuse_release_nowrite(inode);

    return err;
}