stop passing nameidata * to ->d_revalidate()
[linux-3.10.git] / fs / fuse / dir.c
index b4c09c5..eba30bd 100644 (file)
@@ -154,7 +154,7 @@ u64 fuse_get_attr_version(struct fuse_conn *fc)
  * 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)
+static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
 {
        struct inode *inode;
 
@@ -174,7 +174,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
                if (!inode)
                        return 0;
 
-               if (nd && (nd->flags & LOOKUP_RCU))
+               if (flags & LOOKUP_RCU)
                        return -ECHILD;
 
                fc = get_fuse_conn(inode);
@@ -249,7 +249,7 @@ static struct dentry *fuse_d_add_directory(struct dentry *entry,
                /* This tries to shrink the subtree below alias */
                fuse_invalidate_entry(alias);
                dput(alias);
-               if (!list_empty(&inode->i_dentry))
+               if (!hlist_empty(&inode->i_dentry))
                        return ERR_PTR(-EBUSY);
        } else {
                dput(alias);
@@ -369,8 +369,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
  * 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, int mode,
-                           struct nameidata *nd)
+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;
@@ -381,18 +382,11 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
        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;
-
-       if (flags & O_DIRECT)
-               return -EINVAL;
 
        forget = fuse_alloc_forget();
+       err = -ENOMEM;
        if (!forget)
-               return -ENOMEM;
+               goto out_err;
 
        req = fuse_get_req(fc);
        err = PTR_ERR(req);
@@ -431,11 +425,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
        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;
+       if (err)
                goto out_free_ff;
-       }
 
        err = -EIO;
        if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
@@ -451,28 +442,74 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
                flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
                fuse_sync_release(ff, flags);
                fuse_queue_forget(fc, forget, outentry.nodeid, 1);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto out_err;
        }
        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)) {
+       err = finish_open(file, entry, generic_file_open, opened);
+       if (err) {
                fuse_sync_release(ff, flags);
-               return PTR_ERR(file);
+       } else {
+               file->private_data = fuse_file_get(ff);
+               fuse_finish_open(inode, file);
        }
-       file->private_data = fuse_file_get(ff);
-       fuse_finish_open(inode, file);
-       return 0;
+       return err;
 
- out_free_ff:
+out_free_ff:
        fuse_file_free(ff);
- out_put_request:
+out_put_request:
        fuse_put_request(fc, req);
- out_put_forget_req:
+out_put_forget_req:
        kfree(forget);
+out_err:
+       return err;
+}
+
+static int fuse_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
+                           struct file *file, unsigned flags,
+                           umode_t mode, int *opened)
+{
+       int err;
+       struct fuse_conn *fc = get_fuse_conn(dir);
+       struct dentry *res = NULL;
+
+       if (d_unhashed(entry)) {
+               res = fuse_lookup(dir, entry, NULL);
+               if (IS_ERR(res))
+                       return PTR_ERR(res);
+
+               if (res)
+                       entry = res;
+       }
+
+       if (!(flags & O_CREAT) || entry->d_inode)
+               goto no_open;
+
+       /* Only creates */
+       *opened |= FILE_CREATED;
+
+       if (fc->no_create)
+               goto mknod;
+
+       err = fuse_create_open(dir, entry, file, flags, mode, opened);
+       if (err == -ENOSYS) {
+               fc->no_create = 1;
+               goto mknod;
+       }
+out_dput:
+       dput(res);
        return err;
+
+mknod:
+       err = fuse_mknod(dir, entry, mode, 0);
+       if (err)
+               goto out_dput;
+no_open:
+       return finish_no_open(file, res);
 }
 
 /*
@@ -480,7 +517,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
  */
 static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
                            struct inode *dir, struct dentry *entry,
-                           int mode)
+                           umode_t mode)
 {
        struct fuse_entry_out outarg;
        struct inode *inode;
@@ -576,12 +613,6 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode,
 static int fuse_create(struct inode *dir, struct dentry *entry, umode_t mode,
                       struct nameidata *nd)
 {
-       if (nd) {
-               int err = fuse_create_open(dir, entry, mode, nd);
-               if (err != -ENOSYS)
-                       return err;
-               /* Fall back on mknod */
-       }
        return fuse_mknod(dir, entry, mode, 0);
 }
 
@@ -644,13 +675,12 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
        fuse_put_request(fc, req);
        if (!err) {
                struct inode *inode = entry->d_inode;
+               struct fuse_inode *fi = get_fuse_inode(inode);
 
-               /*
-                * 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.
-                */
-               clear_nlink(inode);
+               spin_lock(&fc->lock);
+               fi->attr_version = ++fc->attr_version;
+               drop_nlink(inode);
+               spin_unlock(&fc->lock);
                fuse_invalidate_attr(inode);
                fuse_invalidate_attr(dir);
                fuse_invalidate_entry_cache(entry);
@@ -762,14 +792,25 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
           will reflect changes in the backing inode (link count,
           etc.)
        */
-       if (!err || err == -EINTR)
+       if (!err) {
+               struct fuse_inode *fi = get_fuse_inode(inode);
+
+               spin_lock(&fc->lock);
+               fi->attr_version = ++fc->attr_version;
+               inc_nlink(inode);
+               spin_unlock(&fc->lock);
                fuse_invalidate_attr(inode);
+       } else if (err == -EINTR) {
+               fuse_invalidate_attr(inode);
+       }
        return err;
 }
 
 static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
                          struct kstat *stat)
 {
+       unsigned int blkbits;
+
        stat->dev = inode->i_sb->s_dev;
        stat->ino = attr->ino;
        stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
@@ -785,7 +826,13 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
        stat->ctime.tv_nsec = attr->ctimensec;
        stat->size = attr->size;
        stat->blocks = attr->blocks;
-       stat->blksize = (1 << inode->i_blkbits);
+
+       if (attr->blksize != 0)
+               blkbits = ilog2(attr->blksize);
+       else
+               blkbits = inode->i_sb->s_blocksize_bits;
+
+       stat->blksize = 1 << blkbits;
 }
 
 static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
@@ -858,6 +905,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
                if (stat) {
                        generic_fillattr(inode, stat);
                        stat->mode = fi->orig_i_mode;
+                       stat->ino = fi->orig_ino;
                }
        }
 
@@ -868,7 +916,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
 }
 
 int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
-                            struct qstr *name)
+                            u64 child_nodeid, struct qstr *name)
 {
        int err = -ENOTDIR;
        struct inode *parent;
@@ -895,8 +943,36 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
 
        fuse_invalidate_attr(parent);
        fuse_invalidate_entry(entry);
+
+       if (child_nodeid != 0 && entry->d_inode) {
+               mutex_lock(&entry->d_inode->i_mutex);
+               if (get_node_id(entry->d_inode) != child_nodeid) {
+                       err = -ENOENT;
+                       goto badentry;
+               }
+               if (d_mountpoint(entry)) {
+                       err = -EBUSY;
+                       goto badentry;
+               }
+               if (S_ISDIR(entry->d_inode->i_mode)) {
+                       shrink_dcache_parent(entry);
+                       if (!simple_empty(entry)) {
+                               err = -ENOTEMPTY;
+                               goto badentry;
+                       }
+                       entry->d_inode->i_flags |= S_DEAD;
+               }
+               dont_mount(entry);
+               clear_nlink(entry->d_inode);
+               err = 0;
+ badentry:
+               mutex_unlock(&entry->d_inode->i_mutex);
+               if (!err)
+                       d_delete(entry);
+       } else {
+               err = 0;
+       }
        dput(entry);
-       err = 0;
 
  unlock:
        mutex_unlock(&parent->i_mutex);
@@ -1182,6 +1258,30 @@ static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end,
        return fuse_fsync_common(file, start, end, datasync, 1);
 }
 
+static long fuse_dir_ioctl(struct file *file, unsigned int cmd,
+                           unsigned long arg)
+{
+       struct fuse_conn *fc = get_fuse_conn(file->f_mapping->host);
+
+       /* FUSE_IOCTL_DIR only supported for API version >= 7.18 */
+       if (fc->minor < 18)
+               return -ENOTTY;
+
+       return fuse_ioctl_common(file, cmd, arg, FUSE_IOCTL_DIR);
+}
+
+static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd,
+                                  unsigned long arg)
+{
+       struct fuse_conn *fc = get_fuse_conn(file->f_mapping->host);
+
+       if (fc->minor < 18)
+               return -ENOTTY;
+
+       return fuse_ioctl_common(file, cmd, arg,
+                                FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR);
+}
+
 static bool update_mtime(unsigned ivalid)
 {
        /* Always update if mtime is explicitly set  */
@@ -1580,6 +1680,7 @@ static const struct inode_operations fuse_dir_inode_operations = {
        .link           = fuse_link,
        .setattr        = fuse_setattr,
        .create         = fuse_create,
+       .atomic_open    = fuse_atomic_open,
        .mknod          = fuse_mknod,
        .permission     = fuse_permission,
        .getattr        = fuse_getattr,
@@ -1596,6 +1697,8 @@ static const struct file_operations fuse_dir_operations = {
        .open           = fuse_dir_open,
        .release        = fuse_dir_release,
        .fsync          = fuse_dir_fsync,
+       .unlocked_ioctl = fuse_dir_ioctl,
+       .compat_ioctl   = fuse_dir_compat_ioctl,
 };
 
 static const struct inode_operations fuse_common_inode_operations = {