Example #1
0
static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
                            struct inode *dir, struct dentry *entry,
                            umode_t mode)
{
    struct fuse_entry_out outarg;
    struct inode *inode;
    int err;
    struct fuse_forget_link *forget;

    forget = fuse_alloc_forget();
    if (!forget) {
        fuse_put_request(fc, req);
        return -ENOMEM;
    }

    memset(&outarg, 0, sizeof(outarg));
    req->in.h.nodeid = get_node_id(dir);
    req->out.numargs = 1;
    if (fc->minor < 9)
        req->out.args[0].size = FUSE_COMPAT_ENTRY_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)
        goto out_put_forget_req;

    err = -EIO;
    if (invalid_nodeid(outarg.nodeid))
        goto out_put_forget_req;

    if ((outarg.attr.mode ^ mode) & S_IFMT)
        goto out_put_forget_req;

    inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
                      &outarg.attr, entry_attr_timeout(&outarg), 0);
    if (!inode) {
        fuse_queue_forget(fc, forget, outarg.nodeid, 1);
        return -ENOMEM;
    }
    kfree(forget);

    if (S_ISDIR(inode->i_mode)) {
        struct dentry *alias;
        mutex_lock(&fc->inst_mutex);
        alias = d_find_alias(inode);
        if (alias) {

            mutex_unlock(&fc->inst_mutex);
            dput(alias);
            iput(inode);
            pr_info("fuse: %s -EBUSY (%s)\n", __func__,
                    entry->d_name.name);
            return -EBUSY;
        }
        d_instantiate(entry, inode);
        mutex_unlock(&fc->inst_mutex);
    } else
        d_instantiate(entry, inode);

    fuse_change_entry_timeout(entry, &outarg);
    fuse_invalidate_attr(dir);
    return 0;

out_put_forget_req:
    kfree(forget);
    return err;
}
Example #2
0
static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
{
	struct inode *inode;

	inode = ACCESS_ONCE(entry->d_inode);
	if (inode && is_bad_inode(inode))
		return 0;
	else if (fuse_dentry_time(entry) < get_jiffies_64()) {
		int err;
		struct fuse_entry_out outarg;
		struct fuse_conn *fc;
		struct fuse_req *req;
		struct fuse_forget_link *forget;
		struct dentry *parent;
		u64 attr_version;

		
		if (!inode)
			return 0;

		if (nd && (nd->flags & LOOKUP_RCU))
			return -ECHILD;

		fc = get_fuse_conn(inode);
		req = fuse_get_req(fc);
		if (IS_ERR(req))
			return 0;

		forget = fuse_alloc_forget();
		if (!forget) {
			fuse_put_request(fc, req);
			return 0;
		}

		attr_version = fuse_get_attr_version(fc);

		parent = dget_parent(entry);
		fuse_lookup_init(fc, req, get_node_id(parent->d_inode),
				 &entry->d_name, &outarg);
		fuse_request_send(fc, req);
		dput(parent);
		err = req->out.h.error;
		fuse_put_request(fc, req);
		
		if (!err && !outarg.nodeid)
			err = -ENOENT;
		if (!err) {
			struct fuse_inode *fi = get_fuse_inode(inode);
			if (outarg.nodeid != get_node_id(inode)) {
				fuse_queue_forget(fc, forget, outarg.nodeid, 1);
				return 0;
			}
			spin_lock(&fc->lock);
			fi->nlookup++;
			spin_unlock(&fc->lock);
		}
		kfree(forget);
		if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
			return 0;

		fuse_change_attributes(inode, &outarg.attr,
				       entry_attr_timeout(&outarg),
				       attr_version);
		fuse_change_entry_timeout(entry, &outarg);
	}
	return 1;
}
Example #3
0
static int fuse_create_open(struct inode *dir, struct dentry *entry,
                            umode_t mode, struct nameidata *nd)
{
    int err;
    struct inode *inode;
    struct fuse_conn *fc = get_fuse_conn(dir);
    struct fuse_req *req;
    struct fuse_forget_link *forget;
    struct fuse_create_in inarg;
    struct fuse_open_out outopen;
    struct fuse_entry_out outentry;
    struct fuse_file *ff;
    struct file *file;
    int flags = nd->intent.open.flags;

    if (fc->no_create)
        return -ENOSYS;

    forget = fuse_alloc_forget();
    if (!forget)
        return -ENOMEM;

    req = fuse_get_req(fc);
    err = PTR_ERR(req);
    if (IS_ERR(req))
        goto out_put_forget_req;

    err = -ENOMEM;
    ff = fuse_file_alloc(fc);
    if (!ff)
        goto out_put_request;

    if (!fc->dont_mask)
        mode &= ~current_umask();

    flags &= ~O_NOCTTY;
    memset(&inarg, 0, sizeof(inarg));
    memset(&outentry, 0, sizeof(outentry));
    inarg.flags = flags;
    inarg.mode = mode;
    inarg.umask = current_umask();
    req->in.h.opcode = FUSE_CREATE;
    req->in.h.nodeid = get_node_id(dir);
    req->in.numargs = 2;
    req->in.args[0].size = fc->minor < 12 ? sizeof(struct fuse_open_in) :
                           sizeof(inarg);
    req->in.args[0].value = &inarg;
    req->in.args[1].size = entry->d_name.len + 1;
    req->in.args[1].value = entry->d_name.name;
    req->out.numargs = 2;
    if (fc->minor < 9)
        req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
    else
        req->out.args[0].size = sizeof(outentry);
    req->out.args[0].value = &outentry;
    req->out.args[1].size = sizeof(outopen);
    req->out.args[1].value = &outopen;
    fuse_request_send(fc, req);
    err = req->out.h.error;
    if (err) {
        if (err == -ENOSYS)
            fc->no_create = 1;
        goto out_free_ff;
    }

    err = -EIO;
    if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
        goto out_free_ff;

    fuse_put_request(fc, req);
    ff->fh = outopen.fh;
    ff->nodeid = outentry.nodeid;
    ff->open_flags = outopen.open_flags;
    inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
                      &outentry.attr, entry_attr_timeout(&outentry), 0);
    if (!inode) {
        flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
        fuse_sync_release(ff, flags);
        fuse_queue_forget(fc, forget, outentry.nodeid, 1);
        return -ENOMEM;
    }
    kfree(forget);
    d_instantiate(entry, inode);
    fuse_change_entry_timeout(entry, &outentry);
    fuse_invalidate_attr(dir);
    file = lookup_instantiate_filp(nd, entry, generic_file_open);
    if (IS_ERR(file)) {
        fuse_sync_release(ff, flags);
        return PTR_ERR(file);
    }
    file->private_data = fuse_file_get(ff);
    fuse_finish_open(inode, file);
    return 0;

out_free_ff:
    fuse_file_free(ff);
out_put_request:
    fuse_put_request(fc, req);
out_put_forget_req:
    kfree(forget);
    return err;
}
Example #4
0
/*
 * Atomic create+open operation
 *
 * If the filesystem doesn't support this, then fall back to separate
 * 'mknod' + 'open' requests.
 */
static int fuse_create_open(struct inode *dir, struct dentry *entry,
			    struct file *file, unsigned flags,
			    umode_t mode, int *opened)
{
	int err;
	struct inode *inode;
	struct fuse_conn *fc = get_fuse_conn(dir);
	FUSE_ARGS(args);
	struct fuse_forget_link *forget;
	struct fuse_create_in inarg;
	struct fuse_open_out outopen;
	struct fuse_entry_out outentry;
	struct fuse_file *ff;

	/* Userspace expects S_IFREG in create mode */
	BUG_ON((mode & S_IFMT) != S_IFREG);

	forget = fuse_alloc_forget();
	err = -ENOMEM;
	if (!forget)
		goto out_err;

	err = -ENOMEM;
	ff = fuse_file_alloc(fc);
	if (!ff)
		goto out_put_forget_req;

	if (!fc->dont_mask)
		mode &= ~current_umask();

	flags &= ~O_NOCTTY;
	memset(&inarg, 0, sizeof(inarg));
	memset(&outentry, 0, sizeof(outentry));
	inarg.flags = flags;
	inarg.mode = mode;
	inarg.umask = current_umask();
	args.in.h.opcode = FUSE_CREATE;
	args.in.h.nodeid = get_node_id(dir);
	args.in.numargs = 2;
	args.in.args[0].size = sizeof(inarg);
	args.in.args[0].value = &inarg;
	args.in.args[1].size = entry->d_name.len + 1;
	args.in.args[1].value = entry->d_name.name;
	args.out.numargs = 2;
	args.out.args[0].size = sizeof(outentry);
	args.out.args[0].value = &outentry;
	args.out.args[1].size = sizeof(outopen);
	args.out.args[1].value = &outopen;
	err = fuse_simple_request(fc, &args);
	if (err)
		goto out_free_ff;

	err = -EIO;
	if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
		goto out_free_ff;

	ff->fh = outopen.fh;
	ff->nodeid = outentry.nodeid;
	ff->open_flags = outopen.open_flags;
	inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
			  &outentry.attr, entry_attr_timeout(&outentry), 0);
	if (!inode) {
		flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
		fuse_sync_release(ff, flags);
		fuse_queue_forget(fc, forget, outentry.nodeid, 1);
		err = -ENOMEM;
		goto out_err;
	}
	kfree(forget);
	d_instantiate(entry, inode);
	fuse_change_entry_timeout(entry, &outentry);
	fuse_invalidate_attr(dir);
	err = finish_open(file, entry, generic_file_open, opened);
	if (err) {
		fuse_sync_release(ff, flags);
	} else {
		file->private_data = fuse_file_get(ff);
		fuse_finish_open(inode, file);
	}
	return err;

out_free_ff:
	fuse_file_free(ff);
out_put_forget_req:
	kfree(forget);
out_err:
	return err;
}
Example #5
0
/*
 * Check whether the dentry is still valid
 *
 * If the entry validity timeout has expired and the dentry is
 * positive, try to redo the lookup.  If the lookup results in a
 * different inode, then let the VFS invalidate the dentry and redo
 * the lookup once more.  If the lookup results in the same inode,
 * then refresh the attributes, timeouts and mark the dentry valid.
 */
static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
{
	struct inode *inode;
	struct dentry *parent;
	struct fuse_conn *fc;
	struct fuse_inode *fi;
	int ret;

	inode = d_inode_rcu(entry);
	if (inode && is_bad_inode(inode))
		goto invalid;
	else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
		 (flags & LOOKUP_REVAL)) {
		struct fuse_entry_out outarg;
		FUSE_ARGS(args);
		struct fuse_forget_link *forget;
		u64 attr_version;

		/* For negative dentries, always do a fresh lookup */
		if (!inode)
			goto invalid;

		ret = -ECHILD;
		if (flags & LOOKUP_RCU)
			goto out;

		fc = get_fuse_conn(inode);

		forget = fuse_alloc_forget();
		ret = -ENOMEM;
		if (!forget)
			goto out;

		attr_version = fuse_get_attr_version(fc);

		parent = dget_parent(entry);
		fuse_lookup_init(fc, &args, get_node_id(d_inode(parent)),
				 &entry->d_name, &outarg);
		ret = fuse_simple_request(fc, &args);
		dput(parent);
		/* Zero nodeid is same as -ENOENT */
		if (!ret && !outarg.nodeid)
			ret = -ENOENT;
		if (!ret) {
			fi = get_fuse_inode(inode);
			if (outarg.nodeid != get_node_id(inode)) {
				fuse_queue_forget(fc, forget, outarg.nodeid, 1);
				goto invalid;
			}
			spin_lock(&fc->lock);
			fi->nlookup++;
			spin_unlock(&fc->lock);
		}
		kfree(forget);
		if (ret == -ENOMEM)
			goto out;
		if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
			goto invalid;

		fuse_change_attributes(inode, &outarg.attr,
				       entry_attr_timeout(&outarg),
				       attr_version);
		fuse_change_entry_timeout(entry, &outarg);
	} else if (inode) {
		fi = get_fuse_inode(inode);
		if (flags & LOOKUP_RCU) {
			if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
				return -ECHILD;
		} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
			parent = dget_parent(entry);
			fuse_advise_use_readdirplus(d_inode(parent));
			dput(parent);
		}
	}
	ret = 1;
out:
	return ret;

invalid:
	ret = 0;
	goto out;
}
/*
 * Check whether the dentry is still valid
 *
 * If the entry validity timeout has expired and the dentry is
 * positive, try to redo the lookup.  If the lookup results in a
 * different inode, then let the VFS invalidate the dentry and redo
 * the lookup once more.  If the lookup results in the same inode,
 * then refresh the attributes, timeouts and mark the dentry valid.
 */
static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
{
	struct inode *inode = entry->d_inode;
	struct dentry *parent;
	struct fuse_conn *fc;
	int ret;

	if (inode && is_bad_inode(inode))
		goto invalid;
	else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
		 (nd->flags & LOOKUP_REVAL)) {
		int err;
		struct fuse_entry_out outarg;
		struct fuse_req *req;
		struct fuse_forget_link *forget;
		u64 attr_version;

		/* For negative dentries, always do a fresh lookup */
		if (!inode)
			goto invalid;

		fc = get_fuse_conn(inode);
		req = fuse_get_req_nopages(fc);
		ret = PTR_ERR(req);
		if (IS_ERR(req))
			goto out;

		forget = fuse_alloc_forget();
		if (!forget) {
			fuse_put_request(fc, req);
			ret = -ENOMEM;
			goto out;
		}

		attr_version = fuse_get_attr_version(fc);

		parent = dget_parent(entry);
		fuse_lookup_init(fc, req, get_node_id(parent->d_inode),
				 &entry->d_name, &outarg);
		fuse_request_send(fc, req);
		dput(parent);
		err = req->out.h.error;
		fuse_put_request(fc, req);
		/* Zero nodeid is same as -ENOENT */
		if (!err && !outarg.nodeid)
			err = -ENOENT;
		if (!err) {
			struct fuse_inode *fi = get_fuse_inode(inode);
			if (outarg.nodeid != get_node_id(inode)) {
				fuse_queue_forget(fc, forget, outarg.nodeid, 1);
				goto invalid;
			}
			spin_lock(&fc->lock);
			fi->nlookup++;
			spin_unlock(&fc->lock);
		}
		kfree(forget);
		if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
			goto invalid;

		fuse_change_attributes(inode, &outarg.attr,
				       entry_attr_timeout(&outarg),
				       attr_version);
		fuse_change_entry_timeout(entry, &outarg);
	} else if (inode) {
		fc = get_fuse_conn(inode);
		if (fc->readdirplus_auto) {
			parent = dget_parent(entry);
			fuse_advise_use_readdirplus(parent->d_inode);
			dput(parent);
		}
	}
	ret = 1;
out:
	return ret;

invalid:
	ret = 0;
	if (inode) {
		if (S_ISDIR(inode->i_mode)) {
			if (have_submounts(entry)) {
				ret = 1;
				goto out;
			}
			shrink_dcache_parent(entry);
		} else {
			if (d_mountpoint(entry)) {
				ret = 1;
				goto out;
			}
		}
	}
	d_drop(entry);
	goto out;
}
Example #7
0
/*
 * Check whether the dentry is still valid
 *
 * If the entry validity timeout has expired and the dentry is
 * positive, try to redo the lookup.  If the lookup results in a
 * different inode, then let the VFS invalidate the dentry and redo
 * the lookup once more.  If the lookup results in the same inode,
 * then refresh the attributes, timeouts and mark the dentry valid.
 */
static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
{
	struct inode *inode;
	struct dentry *parent;
	struct fuse_conn *fc;
	struct fuse_inode *fi;
	int ret;

	inode = ACCESS_ONCE(entry->d_inode);
	if (inode && is_bad_inode(inode))
		goto invalid;
	else if (fuse_dentry_time(entry) < get_jiffies_64()) {
		int err;
		struct fuse_entry_out outarg;
		struct fuse_req *req;
		struct fuse_forget_link *forget;
		u64 attr_version;

		/* For negative dentries, always do a fresh lookup */
		if (!inode)
			goto invalid;

		ret = -ECHILD;
		if (flags & LOOKUP_RCU)
			goto out;

		fc = get_fuse_conn(inode);
		req = fuse_get_req_nopages(fc);
		ret = PTR_ERR(req);
		if (IS_ERR(req))
			goto out;

		forget = fuse_alloc_forget();
		if (!forget) {
			fuse_put_request(fc, req);
			ret = -ENOMEM;
			goto out;
		}

		attr_version = fuse_get_attr_version(fc);

		parent = dget_parent(entry);
		fuse_lookup_init(fc, req, get_node_id(parent->d_inode),
				 &entry->d_name, &outarg);
		fuse_request_send(fc, req);
		dput(parent);
		err = req->out.h.error;
		fuse_put_request(fc, req);
		/* Zero nodeid is same as -ENOENT */
		if (!err && !outarg.nodeid)
			err = -ENOENT;
		if (!err) {
			fi = get_fuse_inode(inode);
			if (outarg.nodeid != get_node_id(inode)) {
				fuse_queue_forget(fc, forget, outarg.nodeid, 1);
				goto invalid;
			}
			spin_lock(&fc->lock);
			fi->nlookup++;
			spin_unlock(&fc->lock);
		}
		kfree(forget);
		if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
			goto invalid;

		fuse_change_attributes(inode, &outarg.attr,
				       entry_attr_timeout(&outarg),
				       attr_version);
		fuse_change_entry_timeout(entry, &outarg);
	} else if (inode) {
		fi = get_fuse_inode(inode);
		if (flags & LOOKUP_RCU) {
			if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
				return -ECHILD;
		} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
			parent = dget_parent(entry);
			fuse_advise_use_readdirplus(parent->d_inode);
			dput(parent);
		}
	}
	ret = 1;
out:
	return ret;

invalid:
	ret = 0;

	if (!(flags & LOOKUP_RCU) && check_submounts_and_drop(entry) != 0)
		ret = 1;
	goto out;
}