struct fuse_conn *fc;
struct fuse_req *req;
struct fuse_req *forget_req;
-
- /* Doesn't hurt to "reset" the validity timeout */
- fuse_invalidate_entry_cache(entry);
+ struct dentry *parent;
/* For negative dentries, always do a fresh lookup */
if (!inode)
return 0;
}
- fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
+ parent = dget_parent(entry);
+ fuse_lookup_init(req, parent->d_inode, entry, &outarg);
request_send(fc, req);
+ dput(parent);
err = req->out.h.error;
fuse_put_request(fc, req);
/* Zero nodeid is same as -ENOENT */
outarg.nodeid, 1);
return 0;
}
+ spin_lock(&fc->lock);
fi->nlookup ++;
+ spin_unlock(&fc->lock);
}
fuse_put_request(fc, forget_req);
if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
return 1;
}
-/*
- * Check if there's already a hashed alias of this directory inode.
- * If yes, then lookup and mkdir must not create a new alias.
- */
-static int dir_alias(struct inode *inode)
-{
- if (S_ISDIR(inode->i_mode)) {
- struct dentry *alias = d_find_alias(inode);
- if (alias) {
- dput(alias);
- return 1;
- }
- }
- return 0;
-}
-
static int invalid_nodeid(u64 nodeid)
{
return !nodeid || nodeid == FUSE_ROOT_ID;
S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m);
}
+/*
+ * Add a directory inode to a dentry, ensuring that no other dentry
+ * refers to this inode. Called with fc->inst_mutex.
+ */
+static int fuse_d_add_directory(struct dentry *entry, struct inode *inode)
+{
+ struct dentry *alias = d_find_alias(inode);
+ if (alias) {
+ /* This tries to shrink the subtree below alias */
+ fuse_invalidate_entry(alias);
+ dput(alias);
+ if (!list_empty(&inode->i_dentry))
+ return -EBUSY;
+ }
+ d_add(entry, inode);
+ return 0;
+}
+
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
struct nameidata *nd)
{
if (err && err != -ENOENT)
return ERR_PTR(err);
- if (inode && dir_alias(inode)) {
- iput(inode);
- return ERR_PTR(-EIO);
- }
- d_add(entry, inode);
+ if (inode && S_ISDIR(inode->i_mode)) {
+ mutex_lock(&fc->inst_mutex);
+ err = fuse_d_add_directory(entry, inode);
+ mutex_unlock(&fc->inst_mutex);
+ if (err) {
+ iput(inode);
+ return ERR_PTR(err);
+ }
+ } else
+ d_add(entry, inode);
+
entry->d_op = &fuse_dentry_operations;
if (!err)
fuse_change_timeout(entry, &outarg);
}
fuse_put_request(fc, forget_req);
- if (dir_alias(inode)) {
- iput(inode);
- return -EIO;
- }
+ if (S_ISDIR(inode->i_mode)) {
+ struct dentry *alias;
+ mutex_lock(&fc->inst_mutex);
+ alias = d_find_alias(inode);
+ if (alias) {
+ /* New directory must have moved since mkdir */
+ mutex_unlock(&fc->inst_mutex);
+ dput(alias);
+ iput(inode);
+ return -EBUSY;
+ }
+ d_instantiate(entry, inode);
+ mutex_unlock(&fc->inst_mutex);
+ } else
+ d_instantiate(entry, inode);
- d_instantiate(entry, inode);
fuse_change_timeout(entry, &outarg);
fuse_invalidate_attr(dir);
return 0;
/* Set nlink to zero so the inode can be cleared, if
the inode does have more links this will be
discovered at the next lookup/getattr */
- inode->i_nlink = 0;
+ clear_nlink(inode);
fuse_invalidate_attr(inode);
fuse_invalidate_attr(dir);
fuse_invalidate_entry_cache(entry);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
- entry->d_inode->i_nlink = 0;
+ clear_nlink(entry->d_inode);
fuse_invalidate_attr(dir);
fuse_invalidate_entry_cache(entry);
} else if (err == -EINTR)
if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO))
return -EACCES;
- if (nd && (nd->flags & LOOKUP_ACCESS))
+ if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR)))
return fuse_access(inode, mask);
return 0;
}
int err;
size_t nbytes;
struct page *page;
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req;
if (attr->ia_valid & ATTR_SIZE) {
unsigned long limit;
is_truncate = 1;
+ 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);