Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
[linux-2.6-block.git] / fs / ceph / dir.c
index 83e9976f718983ccfb5d95c56893e81c7d01caa9..4248307fea909c6f1555758e536a3f733ad5f95d 100644 (file)
@@ -49,9 +49,9 @@ int ceph_init_dentry(struct dentry *dentry)
                goto out_unlock;
        }
 
-       if (ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
+       if (ceph_snap(d_inode(dentry->d_parent)) == CEPH_NOSNAP)
                d_set_d_op(dentry, &ceph_dentry_ops);
-       else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
+       else if (ceph_snap(d_inode(dentry->d_parent)) == CEPH_SNAPDIR)
                d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
        else
                d_set_d_op(dentry, &ceph_snap_dentry_ops);
@@ -77,7 +77,7 @@ struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry)
 
        spin_lock(&dentry->d_lock);
        if (!IS_ROOT(dentry)) {
-               inode = dentry->d_parent->d_inode;
+               inode = d_inode(dentry->d_parent);
                ihold(inode);
        }
        spin_unlock(&dentry->d_lock);
@@ -122,7 +122,7 @@ static int __dcache_readdir(struct file *file,  struct dir_context *ctx,
 {
        struct ceph_file_info *fi = file->private_data;
        struct dentry *parent = file->f_path.dentry;
-       struct inode *dir = parent->d_inode;
+       struct inode *dir = d_inode(parent);
        struct list_head *p;
        struct dentry *dentry, *last;
        struct ceph_dentry_info *di;
@@ -161,15 +161,15 @@ more:
                }
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
                if (di->lease_shared_gen == shared_gen &&
-                   !d_unhashed(dentry) && dentry->d_inode &&
-                   ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
-                   ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
+                   !d_unhashed(dentry) && d_really_is_positive(dentry) &&
+                   ceph_snap(d_inode(dentry)) != CEPH_SNAPDIR &&
+                   ceph_ino(d_inode(dentry)) != CEPH_INO_CEPH &&
                    fpos_cmp(ctx->pos, di->offset) <= 0)
                        break;
                dout(" skipping %p %pd at %llu (%llu)%s%s\n", dentry,
                     dentry, di->offset,
                     ctx->pos, d_unhashed(dentry) ? " unhashed" : "",
-                    !dentry->d_inode ? " null" : "");
+                    !d_inode(dentry) ? " null" : "");
                spin_unlock(&dentry->d_lock);
                p = p->prev;
                dentry = list_entry(p, struct dentry, d_child);
@@ -189,11 +189,11 @@ more:
        }
 
        dout(" %llu (%llu) dentry %p %pd %p\n", di->offset, ctx->pos,
-            dentry, dentry, dentry->d_inode);
+            dentry, dentry, d_inode(dentry));
        if (!dir_emit(ctx, dentry->d_name.name,
                      dentry->d_name.len,
-                     ceph_translate_ino(dentry->d_sb, dentry->d_inode->i_ino),
-                     dentry->d_inode->i_mode >> 12)) {
+                     ceph_translate_ino(dentry->d_sb, d_inode(dentry)->i_ino),
+                     d_inode(dentry)->i_mode >> 12)) {
                if (last) {
                        /* remember our position */
                        fi->dentry = last;
@@ -281,6 +281,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
        /* can we use the dcache? */
        spin_lock(&ci->i_ceph_lock);
        if ((ctx->pos == 2 || fi->dentry) &&
+           ceph_test_mount_opt(fsc, DCACHE) &&
            !ceph_test_mount_opt(fsc, NOASYNCREADDIR) &&
            ceph_snap(inode) != CEPH_SNAPDIR &&
            __ceph_dir_is_complete_ordered(ci) &&
@@ -336,16 +337,23 @@ more:
                        ceph_mdsc_put_request(req);
                        return err;
                }
-               req->r_inode = inode;
-               ihold(inode);
-               req->r_dentry = dget(file->f_path.dentry);
                /* hints to request -> mds selection code */
                req->r_direct_mode = USE_AUTH_MDS;
                req->r_direct_hash = ceph_frag_value(frag);
                req->r_direct_is_hash = true;
-               req->r_path2 = kstrdup(fi->last_name, GFP_NOFS);
+               if (fi->last_name) {
+                       req->r_path2 = kstrdup(fi->last_name, GFP_NOFS);
+                       if (!req->r_path2) {
+                               ceph_mdsc_put_request(req);
+                               return -ENOMEM;
+                       }
+               }
                req->r_readdir_offset = fi->next_offset;
                req->r_args.readdir.frag = cpu_to_le32(frag);
+
+               req->r_inode = inode;
+               ihold(inode);
+               req->r_dentry = dget(file->f_path.dentry);
                err = ceph_mdsc_do_request(mdsc, NULL, req);
                if (err < 0) {
                        ceph_mdsc_put_request(req);
@@ -535,7 +543,7 @@ int ceph_handle_snapdir(struct ceph_mds_request *req,
                        struct dentry *dentry, int err)
 {
        struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
-       struct inode *parent = dentry->d_parent->d_inode; /* we hold i_mutex */
+       struct inode *parent = d_inode(dentry->d_parent); /* we hold i_mutex */
 
        /* .snap dir? */
        if (err == -ENOENT &&
@@ -571,8 +579,8 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
                err = 0;
                if (!req->r_reply_info.head->is_dentry) {
                        dout("ENOENT and no trace, dentry %p inode %p\n",
-                            dentry, dentry->d_inode);
-                       if (dentry->d_inode) {
+                            dentry, d_inode(dentry));
+                       if (d_really_is_positive(dentry)) {
                                d_drop(dentry);
                                err = -ENOENT;
                        } else {
@@ -619,7 +627,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
                return ERR_PTR(err);
 
        /* can we conclude ENOENT locally? */
-       if (dentry->d_inode == NULL) {
+       if (d_really_is_negative(dentry)) {
                struct ceph_inode_info *ci = ceph_inode(dir);
                struct ceph_dentry_info *di = ceph_dentry(dentry);
 
@@ -629,6 +637,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
                            fsc->mount_options->snapdir_name,
                            dentry->d_name.len) &&
                    !is_root_ceph_dentry(dir, dentry) &&
+                   ceph_test_mount_opt(fsc, DCACHE) &&
                    __ceph_dir_is_complete(ci) &&
                    (__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) {
                        spin_unlock(&ci->i_ceph_lock);
@@ -725,7 +734,7 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry,
        ceph_mdsc_put_request(req);
 out:
        if (!err)
-               ceph_init_inode_acls(dentry->d_inode, &acls);
+               ceph_init_inode_acls(d_inode(dentry), &acls);
        else
                d_drop(dentry);
        ceph_release_acls_info(&acls);
@@ -755,10 +764,15 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry,
                err = PTR_ERR(req);
                goto out;
        }
-       req->r_dentry = dget(dentry);
-       req->r_num_caps = 2;
        req->r_path2 = kstrdup(dest, GFP_NOFS);
+       if (!req->r_path2) {
+               err = -ENOMEM;
+               ceph_mdsc_put_request(req);
+               goto out;
+       }
        req->r_locked_dir = dir;
+       req->r_dentry = dget(dentry);
+       req->r_num_caps = 2;
        req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
        err = ceph_mdsc_do_request(mdsc, dir, req);
@@ -821,7 +835,7 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        ceph_mdsc_put_request(req);
 out:
        if (!err)
-               ceph_init_inode_acls(dentry->d_inode, &acls);
+               ceph_init_inode_acls(d_inode(dentry), &acls);
        else
                d_drop(dentry);
        ceph_release_acls_info(&acls);
@@ -858,8 +872,8 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
        if (err) {
                d_drop(dentry);
        } else if (!req->r_reply_info.head->is_dentry) {
-               ihold(old_dentry->d_inode);
-               d_instantiate(dentry, old_dentry->d_inode);
+               ihold(d_inode(old_dentry));
+               d_instantiate(dentry, d_inode(old_dentry));
        }
        ceph_mdsc_put_request(req);
        return err;
@@ -892,7 +906,7 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
 {
        struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
        struct ceph_mds_client *mdsc = fsc->mdsc;
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = d_inode(dentry);
        struct ceph_mds_request *req;
        int err = -EROFS;
        int op;
@@ -933,16 +947,20 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct ceph_fs_client *fsc = ceph_sb_to_client(old_dir->i_sb);
        struct ceph_mds_client *mdsc = fsc->mdsc;
        struct ceph_mds_request *req;
+       int op = CEPH_MDS_OP_RENAME;
        int err;
 
        if (ceph_snap(old_dir) != ceph_snap(new_dir))
                return -EXDEV;
-       if (ceph_snap(old_dir) != CEPH_NOSNAP ||
-           ceph_snap(new_dir) != CEPH_NOSNAP)
-               return -EROFS;
+       if (ceph_snap(old_dir) != CEPH_NOSNAP) {
+               if (old_dir == new_dir && ceph_snap(old_dir) == CEPH_SNAPDIR)
+                       op = CEPH_MDS_OP_RENAMESNAP;
+               else
+                       return -EROFS;
+       }
        dout("rename dir %p dentry %p to dir %p dentry %p\n",
             old_dir, old_dentry, new_dir, new_dentry);
-       req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_RENAME, USE_AUTH_MDS);
+       req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
        if (IS_ERR(req))
                return PTR_ERR(req);
        ihold(old_dir);
@@ -957,8 +975,8 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
        /* release LINK_RDCACHE on source inode (mds will lock it) */
        req->r_old_inode_drop = CEPH_CAP_LINK_SHARED;
-       if (new_dentry->d_inode)
-               req->r_inode_drop = drop_caps_for_unlink(new_dentry->d_inode);
+       if (d_really_is_positive(new_dentry))
+               req->r_inode_drop = drop_caps_for_unlink(d_inode(new_dentry));
        err = ceph_mdsc_do_request(mdsc, old_dir, req);
        if (!err && !req->r_reply_info.head->is_dentry) {
                /*
@@ -1024,7 +1042,7 @@ static int dentry_lease_is_valid(struct dentry *dentry)
                        if (di->lease_renew_after &&
                            time_after(jiffies, di->lease_renew_after)) {
                                /* we should renew */
-                               dir = dentry->d_parent->d_inode;
+                               dir = d_inode(dentry->d_parent);
                                session = ceph_get_mds_session(s);
                                seq = di->lease_seq;
                                di->lease_renew_after = 0;
@@ -1074,22 +1092,22 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
                return -ECHILD;
 
        dout("d_revalidate %p '%pd' inode %p offset %lld\n", dentry,
-            dentry, dentry->d_inode, ceph_dentry(dentry)->offset);
+            dentry, d_inode(dentry), ceph_dentry(dentry)->offset);
 
        dir = ceph_get_dentry_parent_inode(dentry);
 
        /* always trust cached snapped dentries, snapdir dentry */
        if (ceph_snap(dir) != CEPH_NOSNAP) {
                dout("d_revalidate %p '%pd' inode %p is SNAPPED\n", dentry,
-                    dentry, dentry->d_inode);
+                    dentry, d_inode(dentry));
                valid = 1;
-       } else if (dentry->d_inode &&
-                  ceph_snap(dentry->d_inode) == CEPH_SNAPDIR) {
+       } else if (d_really_is_positive(dentry) &&
+                  ceph_snap(d_inode(dentry)) == CEPH_SNAPDIR) {
                valid = 1;
        } else if (dentry_lease_is_valid(dentry) ||
                   dir_lease_is_valid(dir, dentry)) {
-               if (dentry->d_inode)
-                       valid = ceph_is_any_caps(dentry->d_inode);
+               if (d_really_is_positive(dentry))
+                       valid = ceph_is_any_caps(d_inode(dentry));
                else
                        valid = 1;
        }
@@ -1151,7 +1169,7 @@ static void ceph_d_prune(struct dentry *dentry)
         * we hold d_lock, so d_parent is stable, and d_fsdata is never
         * cleared until d_release
         */
-       ceph_dir_clear_complete(dentry->d_parent->d_inode);
+       ceph_dir_clear_complete(d_inode(dentry->d_parent));
 }
 
 /*
@@ -1240,11 +1258,12 @@ static int ceph_dir_fsync(struct file *file, loff_t start, loff_t end,
                dout("dir_fsync %p wait on tid %llu (until %llu)\n",
                     inode, req->r_tid, last_tid);
                if (req->r_timeout) {
-                       ret = wait_for_completion_timeout(
-                               &req->r_safe_completion, req->r_timeout);
-                       if (ret > 0)
+                       unsigned long time_left = wait_for_completion_timeout(
+                                                       &req->r_safe_completion,
+                                                       req->r_timeout);
+                       if (time_left > 0)
                                ret = 0;
-                       else if (ret == 0)
+                       else
                                ret = -EIO;  /* timed out */
                } else {
                        wait_for_completion(&req->r_safe_completion);
@@ -1372,6 +1391,7 @@ const struct inode_operations ceph_snapdir_iops = {
        .getattr = ceph_getattr,
        .mkdir = ceph_mkdir,
        .rmdir = ceph_unlink,
+       .rename = ceph_rename,
 };
 
 const struct dentry_operations ceph_dentry_ops = {