Exemple #1
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.
 */
int fuse_do_setattr(struct inode *inode, struct iattr *attr,
		    struct file *file)
{
	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 (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
		attr->ia_valid |= ATTR_FORCE;

	err = inode_change_ok(inode, attr);
	if (err)
		return err;

	if (attr->ia_valid & ATTR_OPEN) {
		if (fc->atomic_o_trunc)
			return 0;
		file = NULL;
	}

	if (attr->ia_valid & ATTR_SIZE)
		is_truncate = true;

	req = fuse_get_req_nopages(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;
	fuse_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) {
		truncate_pagecache(inode, oldsize, outarg.attr.size);
		invalidate_inode_pages2(inode->i_mapping);
	}

	return 0;

error:
	if (is_truncate)
		fuse_release_nowrite(inode);

	return err;
}
Exemple #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.
 */
int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
		    struct file *file)
{
	struct inode *inode = d_inode(dentry);
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_inode *fi = get_fuse_inode(inode);
	FUSE_ARGS(args);
	struct fuse_setattr_in inarg;
	struct fuse_attr_out outarg;
	bool is_truncate = false;
	bool is_wb = fc->writeback_cache;
	loff_t oldsize;
	int err;
	bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode);

	if (!fc->default_permissions)
		attr->ia_valid |= ATTR_FORCE;

	err = setattr_prepare(dentry, attr);
	if (err)
		return err;

	if (attr->ia_valid & ATTR_OPEN) {
		if (fc->atomic_o_trunc)
			return 0;
		file = NULL;
	}

	if (attr->ia_valid & ATTR_SIZE)
		is_truncate = true;

	if (is_truncate) {
		fuse_set_nowrite(inode);
		set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
		if (trust_local_cmtime && attr->ia_size != inode->i_size)
			attr->ia_valid |= ATTR_MTIME | ATTR_CTIME;
	}

	memset(&inarg, 0, sizeof(inarg));
	memset(&outarg, 0, sizeof(outarg));
	iattr_to_fattr(attr, &inarg, trust_local_cmtime);
	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);
	}
	fuse_setattr_fill(fc, &args, inode, &inarg, &outarg);
	err = fuse_simple_request(fc, &args);
	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);
	/* the kernel maintains i_mtime locally */
	if (trust_local_cmtime) {
		if (attr->ia_valid & ATTR_MTIME)
			inode->i_mtime = attr->ia_mtime;
		if (attr->ia_valid & ATTR_CTIME)
			inode->i_ctime = attr->ia_ctime;
		/* FIXME: clear I_DIRTY_SYNC? */
	}

	fuse_change_attributes_common(inode, &outarg.attr,
				      attr_timeout(&outarg));
	oldsize = inode->i_size;
	/* see the comment in fuse_change_attributes() */
	if (!is_wb || is_truncate || !S_ISREG(inode->i_mode))
		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 ((is_truncate || !is_wb) &&
	    S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
		truncate_pagecache(inode, outarg.attr.size);
		invalidate_inode_pages2(inode->i_mapping);
	}

	clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
	return 0;

error:
	if (is_truncate)
		fuse_release_nowrite(inode);

	clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
	return err;
}
/*
 * 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.
 */
int fuse_do_setattr(struct inode *inode, struct iattr *attr,
		    struct file *file)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_inode *fi = get_fuse_inode(inode);
	struct fuse_req *req;
	struct fuse_setattr_in inarg;
	struct fuse_attr_out outarg;
	bool is_truncate = false;
	int is_wb = fc->writeback_cache;
	loff_t oldsize;
	int err;
	bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode);

	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) {
		err = inode_newsize_ok(inode, attr->ia_size);
		if (err)
			return err;
		is_truncate = true;
	}

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

	if (is_truncate) {
		fuse_set_nowrite(inode);
		set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
	}

	memset(&inarg, 0, sizeof(inarg));
	memset(&outarg, 0, sizeof(outarg));
	iattr_to_fattr(attr, &inarg, trust_local_mtime);
	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);
	}
	fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
	fuse_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);
	/* the kernel maintains i_mtime locally */
	if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) {
		inode->i_mtime = attr->ia_mtime;
		clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
	}

	fuse_change_attributes_common(inode, &outarg.attr,
				      attr_timeout(&outarg));
	oldsize = inode->i_size;
	if (!is_wb || is_truncate || !S_ISREG(inode->i_mode))
		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 ((is_truncate || !is_wb) &&
	    S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
		truncate_pagecache(inode, oldsize, outarg.attr.size);
		invalidate_inode_pages2(inode->i_mapping);
	}

	clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
	return 0;

error:
	if (is_truncate)
		fuse_release_nowrite(inode);

	clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
	return err;
}
Exemple #4
0
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_inode *fi = get_fuse_inode(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))
        attr->ia_valid |= ATTR_FORCE;

    err = inode_change_ok(inode, attr);
    if (err)
        return err;

    if (attr->ia_valid & ATTR_OPEN) {
        if (fc->atomic_o_trunc)
            return 0;
        file = NULL;
    }

    if (attr->ia_valid & ATTR_SIZE)
        is_truncate = true;

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

    if (is_truncate) {
        fuse_set_nowrite(inode);
        set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
    }

    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) {

        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;
    fuse_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) {

        __fuse_release_nowrite(inode);
    }
    spin_unlock(&fc->lock);

    if (S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
        truncate_pagecache(inode, oldsize, outarg.attr.size);
        invalidate_inode_pages2(inode->i_mapping);
    }

    clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
    return 0;

error:
    if (is_truncate)
        fuse_release_nowrite(inode);

    clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
    return err;
}
Exemple #5
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;
}