Whamcloud - gitweb
A mostly-fix for "mknod /mnt/lustre/foofo p". It doesn't fail outright
[fs/lustre-release.git] / lustre / llite / namei.c
index e6e1307..39214e4 100644 (file)
@@ -1,4 +1,5 @@
-/*
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
  *
  * This code is issued under the GNU General Public License.
  * See the file COPYING in this distribution
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  *  Directory entry file type support and forward compatibility hooks
  *      for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
- * 
+ *
  *  Changes for use in OBDFS
  *  Copyright (c) 1999, Seagate Technology Inc.
  *  Copyright (C) 2001, Cluster File Systems, Inc.
  *                       Rewritten based on recent ext2 page cache use.
- * 
+ *
  */
 
 #include <linux/fs.h>
@@ -34,6 +35,7 @@
 
 #include <linux/obd_support.h>
 #include <linux/lustre_lite.h>
+#include <linux/lustre_dlm.h>
 extern struct address_space_operations ll_aops;
 
 /* from super.c */
@@ -42,7 +44,7 @@ extern int ll_setattr(struct dentry *de, struct iattr *attr);
 
 /* from dir.c */
 extern int ll_add_link (struct dentry *dentry, struct inode *inode);
-ino_t ll_inode_by_name(struct inode * dir, struct dentry *dentry, int *typ);
+obd_id ll_inode_by_name(struct inode * dir, struct dentry *dentry, int *typ);
 int ext2_make_empty(struct inode *inode, struct inode *parent);
 struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
                    struct dentry *dentry, struct page ** res_page);
@@ -60,7 +62,7 @@ static inline void ext2_inc_count(struct inode *inode)
         inode->i_nlink++;
 }
 
-/* postpone the disk update until the inode really goes away */ 
+/* postpone the disk update until the inode really goes away */
 static inline void ext2_dec_count(struct inode *inode)
 {
         inode->i_nlink--;
@@ -90,85 +92,198 @@ static int ll_find_inode(struct inode *inode, unsigned long ino, void *opaque)
         return 1;
 }
 
-static struct dentry *ll_lookup(struct inode * dir, struct dentry *dentry)
+extern struct dentry_operations ll_d_ops;
+
+int ll_lock(struct inode *dir, struct dentry *dentry,
+            struct lookup_intent *it, struct lustre_handle *lockh)
+{
+        struct ll_sb_info *sbi = ll_i2sbi(dir);
+        int err, lock_mode;
+
+        if ((it->it_op & (IT_CREAT | IT_MKDIR | IT_SYMLINK | IT_SETATTR |
+                          IT_MKNOD)))
+                lock_mode = LCK_PW;
+        else if (it->it_op & (IT_READDIR | IT_GETATTR | IT_OPEN | IT_UNLINK |
+                              IT_RMDIR | IT_RENAME | IT_RENAME2))
+                lock_mode = LCK_PR;
+        else if (it->it_op & IT_LOOKUP)
+                lock_mode = LCK_CR;
+        else {
+                LBUG();
+                RETURN(-1);
+        }
+
+        err = mdc_enqueue(&sbi->ll_mdc_conn, LDLM_MDSINTENT, it, lock_mode, dir,
+                          dentry, lockh, 0, NULL, 0, dir, sizeof(*dir));
+
+        RETURN(err);
+}
+
+int ll_unlock(__u32 mode, struct lustre_handle *lockh)
+{
+        ENTRY;
+
+        ldlm_lock_decref(lockh, mode);
+
+        RETURN(0);
+}
+
+static struct dentry *ll_lookup2(struct inode * dir, struct dentry *dentry,
+                                 struct lookup_intent *it)
 {
         struct ptlrpc_request *request = NULL;
         struct inode * inode = NULL;
         struct ll_sb_info *sbi = ll_i2sbi(dir);
-        int err;
-        int type;
-        ino_t ino;
-        
+        struct ll_inode_md md;
+        struct lustre_handle lockh;
+        int err, type, offset;
+        struct lookup_intent lookup_it = { IT_LOOKUP };
+        obd_id ino;
+
         ENTRY;
+
+        if (it == NULL) {
+                it = &lookup_it;
+                dentry->d_it = it;
+        }
+
+        CDEBUG(D_INFO, "name: %*s, intent op: %d\n", dentry->d_name.len,
+               dentry->d_name.name, it->it_op);
+
         if (dentry->d_name.len > EXT2_NAME_LEN)
                 RETURN(ERR_PTR(-ENAMETOOLONG));
 
-        ino = ll_inode_by_name(dir, dentry, &type);
-        if (!ino)
+        err = ll_lock(dir, dentry, it, &lockh);
+        if (err < 0) {
+                /* FIXME: Mike LBUG() can disappear the moment that 
+                 *   ll_lock has sane interrupt behavior 
+                 */
+                LBUG();
+                RETURN(ERR_PTR(err));
+        }
+        memcpy(it->it_lock_handle, &lockh, sizeof(lockh));
+
+        if ((it->it_op & (IT_CREAT | IT_MKDIR | IT_SYMLINK | IT_MKNOD)) &&
+            it->it_disposition && !it->it_status)
                 GOTO(negative, NULL);
 
-        err = mdc_getattr(&sbi->ll_mds_client, sbi->ll_mds_conn, ino, type,
-                          OBD_MD_FLNOTOBD|OBD_MD_FLBLOCKS, &request);
-        if (err) {
-                CERROR("failure %d inode %ld\n", err, (long)ino);
-                ptlrpc_free_req(request);
-                RETURN(ERR_PTR(-abs(err)));
+        if ((it->it_op & (IT_RENAME | IT_GETATTR | IT_UNLINK | IT_RMDIR |
+                          IT_SETATTR | IT_LOOKUP)) && 
+            it->it_disposition && it->it_status)
+                GOTO(negative, NULL);
+
+        request = (struct ptlrpc_request *)it->it_data;
+        if (!it->it_disposition) {
+                struct ll_inode_info *lli = ll_i2info(dir);
+                memcpy(&lli->lli_intent_lock_handle, &lockh, sizeof(lockh));
+
+                ino = ll_inode_by_name(dir, dentry, &type);
+#warning FIXME: handle negative inode case (see old ll_lookup)
+
+                err = mdc_getattr(&sbi->ll_mdc_conn, ino, type,
+                                  OBD_MD_FLNOTOBD|OBD_MD_FLBLOCKS, 0, &request);
+                if (err) {
+                        CERROR("failure %d inode %Ld\n", err, (long long)ino);
+                        ptlrpc_free_req(request);
+                        RETURN(ERR_PTR(-abs(err)));
+                }
+                offset = 0;
+        } else if (it->it_op == IT_RENAME2) {
+                inode = ((struct dentry *)(it->it_data))->d_inode;
+                GOTO(out_req, NULL); 
+        } else {
+                offset = 1;
         }
 
-        inode = iget4(dir->i_sb, ino, ll_find_inode,
-                      lustre_msg_buf(request->rq_repmsg, 0));
+        md.body = lustre_msg_buf(request->rq_repmsg, offset);
+        if (S_ISREG(md.body->mode)) {
+                if (request->rq_repmsg->bufcount < offset + 1)
+                        LBUG();
+                md.md = lustre_msg_buf(request->rq_repmsg, offset + 1);
+        } else
+                md.md = NULL;
+
+        /* No rpc's happen during iget4, -ENOMEM's are possible */
+        inode = iget4(dir->i_sb, ino, ll_find_inode, &md);
+        if (it->it_op & IT_RENAME)
+                it->it_data = dentry;
 
+ out_req:
         ptlrpc_free_req(request);
-        if (!inode) 
+        if (!inode || IS_ERR(inode)) { 
+                ll_intent_release(dentry); 
                 RETURN(ERR_PTR(-ENOMEM));
-
+        }
         EXIT;
  negative:
+        dentry->d_op = &ll_d_ops;
         d_add(dentry, inode);
+        if (it->it_op == IT_LOOKUP)
+                ll_intent_release(dentry);
+
         return NULL;
 }
 
-static struct inode *ll_create_node(struct inode *dir, const char *name, 
-                                    int namelen, const char *tgt, int tgtlen, 
-                                    int mode, __u64 id)
+static struct inode *ll_create_node(struct inode *dir, const char *name,
+                                    int namelen, const char *tgt, int tgtlen,
+                                    int mode, __u64 extra,
+                                    struct lookup_intent *it,
+                                    struct lov_stripe_md *smd)
 {
         struct inode *inode;
         struct ptlrpc_request *request = NULL;
         struct mds_body *body;
-        int err;
+        int rc;
         time_t time = CURRENT_TIME;
         struct ll_sb_info *sbi = ll_i2sbi(dir);
+        int gid = current->fsgid;
+        struct ll_inode_md md;
 
         ENTRY;
 
-        err = mdc_create(&sbi->ll_mds_client, sbi->ll_mds_conn, dir, name,
-                         namelen, tgt, tgtlen, mode, id,  current->uid,
-                         current->gid, time, &request);
-        if (err) { 
-                inode = ERR_PTR(err);
-                GOTO(out, err);
+        if (dir->i_mode & S_ISGID) {
+                gid = dir->i_gid;
+                if (S_ISDIR(mode))
+                        mode |= S_ISGID;
+        }
+
+        if (!it->it_disposition) {
+                rc = mdc_create(&sbi->ll_mdc_conn, dir, name, namelen, tgt,
+                                 tgtlen, mode, current->fsuid,
+                                 gid, time, extra, smd, &request);
+                if (rc) {
+                        inode = ERR_PTR(rc);
+                        GOTO(out, rc);
+                }
+                body = lustre_msg_buf(request->rq_repmsg, 0);
+                md.md = smd;
+        } else {
+                request = it->it_data;
+                body = lustre_msg_buf(request->rq_repmsg, 1);
+                md.md = NULL;
         }
-        body = lustre_msg_buf(request->rq_repmsg, 0);
-        body->valid = (__u32)OBD_MD_FLNOTOBD;
 
-        body->objid = id; 
+        body->valid = OBD_MD_FLNOTOBD;
+
         body->nlink = 1;
         body->atime = body->ctime = body->mtime = time;
+        body->uid = current->fsuid;
+        body->gid = gid;
         body->mode = mode;
-        CDEBUG(D_INODE, "-- new_inode: objid %lld, ino %d, mode %o\n",
-               (unsigned long long)body->objid, body->ino, body->mode); 
 
-        inode = iget4(dir->i_sb, body->ino, ll_find_inode, body);
+        md.body = body;
+
+        inode = iget4(dir->i_sb, body->ino, ll_find_inode, &md);
         if (IS_ERR(inode)) {
-                CERROR("new_inode -fatal:  %ld\n", PTR_ERR(inode));
-                inode = ERR_PTR(-EIO);
+                rc = PTR_ERR(inode);
+                CERROR("new_inode -fatal: rc %d\n", rc);
                 LBUG();
-                GOTO(out, -EIO);
+                GOTO(out, rc);
         }
 
         if (!list_empty(&inode->i_dentry)) {
-                CERROR("new_inode -fatal: inode %d, ct %d lnk %d\n", 
-                       body->ino, atomic_read(&inode->i_count), 
+                CERROR("new_inode -fatal: inode %d, ct %d lnk %d\n",
+                       body->ino, atomic_read(&inode->i_count),
                        inode->i_nlink);
                 iput(inode);
                 LBUG();
@@ -191,15 +306,14 @@ int ll_mdc_unlink(struct inode *dir, struct inode *child,
 
         ENTRY;
 
-        err = mdc_unlink(&sbi->ll_mds_client, sbi->ll_mds_conn, dir, child,
+        err = mdc_unlink(&sbi->ll_mdc_conn, dir, child,
                          name, len, &request);
         ptlrpc_free_req(request);
 
-        EXIT;
-        return err;
+        RETURN(err);
 }
 
-int ll_mdc_link(struct dentry *src, struct inode *dir, 
+int ll_mdc_link(struct dentry *src, struct inode *dir,
                 const char *name, int len)
 {
         struct ptlrpc_request *request = NULL;
@@ -208,30 +322,28 @@ int ll_mdc_link(struct dentry *src, struct inode *dir,
 
         ENTRY;
 
-        err = mdc_link(&sbi->ll_mds_client, sbi->ll_mds_conn, src, dir, name,
+        err = mdc_link(&sbi->ll_mdc_conn, src, dir, name,
                        len, &request);
         ptlrpc_free_req(request);
 
-        EXIT;
-        return err;
+        RETURN(err);
 }
 
-int ll_mdc_rename(struct inode *src, struct inode *tgt, 
+int ll_mdc_rename(struct inode *src, struct inode *tgt,
                   struct dentry *old, struct dentry *new)
 {
         struct ptlrpc_request *request = NULL;
-        int err;
         struct ll_sb_info *sbi = ll_i2sbi(src);
+        int err;
 
         ENTRY;
 
-        err = mdc_rename(&sbi->ll_mds_client, sbi->ll_mds_conn, src, tgt, 
-                         old->d_name.name, old->d_name.len, 
+        err = mdc_rename(&sbi->ll_mdc_conn, src, tgt,
+                         old->d_name.name, old->d_name.len,
                          new->d_name.name, new->d_name.len, &request);
         ptlrpc_free_req(request);
 
-        EXIT;
-        return err;
+        RETURN(err);
 }
 
 /*
@@ -240,88 +352,106 @@ int ll_mdc_rename(struct inode *src, struct inode *tgt,
  * is so far negative - it has no inode.
  *
  * If the create succeeds, we fill in the inode information
- * with d_instantiate(). 
+ * with d_instantiate().
  */
 
-static int ll_create (struct inode * dir, struct dentry * dentry, int mode)
+static int ll_create(struct inode * dir, struct dentry * dentry, int mode)
 {
-        int err
+        int err, rc = 0;
         struct obdo oa;
-        struct inode * inode;
-
-        memset(&oa, 0, sizeof(oa)); 
-        oa.o_valid = OBD_MD_FLMODE; 
-        oa.o_mode = S_IFREG | 0600;
-        err = obd_create(ll_i2obdconn(dir), &oa);  
-        if (err) { 
-                RETURN(err);
+        struct inode *inode;
+        struct lov_stripe_md *smd;
+        struct ll_inode_info *ii;
+
+        if (dentry->d_it->it_disposition == 0) {
+                memset(&oa, 0, sizeof(oa));
+                oa.o_valid = OBD_MD_FLMODE;
+                oa.o_mode = S_IFREG | 0600;
+                rc = obd_create(ll_i2obdconn(dir), &oa, &smd);
+                if (rc)
+                        RETURN(rc);
         }
 
-        mode = mode | S_IFREG;
         CDEBUG(D_DENTRY, "name %s mode %o o_id %lld\n",
                dentry->d_name.name, mode, (unsigned long long)oa.o_id);
-        inode = ll_create_node(dir, dentry->d_name.name, dentry->d_name.len, 
-                               NULL, 0, mode, oa.o_id);
-        err = PTR_ERR(inode);
-        if (!IS_ERR(inode)) {
-                // XXX clean up the object
-                inode->i_op = &ll_file_inode_operations;
-                inode->i_fop = &ll_file_operations;
-                inode->i_mapping->a_ops = &ll_aops;
-                err = ext2_add_nondir(dentry, inode);
+        inode = ll_create_node(dir, dentry->d_name.name, dentry->d_name.len,
+                               NULL, 0, mode, 0, dentry->d_it, smd);
+
+        if (IS_ERR(inode)) {
+                rc = PTR_ERR(inode);
+                CERROR("error creating MDS object for id %Ld: rc = %d\n",
+                       (unsigned long long)oa.o_id, rc);
+                GOTO(out_destroy, rc);
         }
-        RETURN(err);
-} /* ll_create */
 
+        if (dentry->d_it->it_disposition) {
+                struct ll_inode_info *ii = ll_i2info(inode);
+                ii->lli_flags |= OBD_FL_CREATEONOPEN;
+                memcpy(&ii->lli_intent_lock_handle,
+                       dentry->d_it->it_lock_handle,
+                       sizeof(struct lustre_handle));
+        }
+
+        /* no directory data updates when intents rule */
+        if (dentry->d_it->it_disposition == 0)
+                rc = ext2_add_nondir(dentry, inode);
+        else
+                d_instantiate(dentry, inode);
+        RETURN(rc);
+
+out_destroy:
+        oa.o_easize = ii->lli_smd->lmd_size;
+        err = obd_destroy(ll_i2obdconn(dir), &oa, ii->lli_smd);
+        if (err)
+                CERROR("error destroying object %Ld in error path: err = %d\n",
+                       (unsigned long long)oa.o_id, err);
+        return rc;
+}
 
-static int ll_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
+static int ll_mknod(struct inode *dir, struct dentry *dentry, int mode,
+                    int rdev)
 {
-        struct inode * inode = ll_create_node(dir, dentry->d_name.name, 
+        struct inode * inode = ll_create_node(dir, dentry->d_name.name,
                                               dentry->d_name.len, NULL, 0,
-                                              mode, 0);
+                                              mode, rdev, dentry->d_it, NULL);
         int err = PTR_ERR(inode);
-        if (!IS_ERR(inode)) {
-                init_special_inode(inode, mode, rdev);
+        if (!IS_ERR(inode))
                 err = ext2_add_nondir(dentry, inode);
-        }
         return err;
 }
 
-static int ll_symlink (struct inode * dir, struct dentry * dentry,
-        const char * symname)
+static int ll_symlink(struct inode *dir, struct dentry *dentry,
+                      const char *symname)
 {
         int err = -ENAMETOOLONG;
         unsigned l = strlen(symname);
         struct inode * inode;
         struct ll_inode_info *oinfo;
 
-        if (l > LL_INLINESZ)
-                return err;
-
-        inode = ll_create_node(dir, dentry->d_name.name, 
+        inode = ll_create_node(dir, dentry->d_name.name,
                                dentry->d_name.len, symname, l,
-                               S_IFLNK | S_IRWXUGO, 0);
+                               S_IFLNK | S_IRWXUGO, 0, dentry->d_it, NULL);
         err = PTR_ERR(inode);
         if (IS_ERR(inode))
                 return err;
 
         oinfo = ll_i2info(inode);
-        
-        inode->i_op = &ll_fast_symlink_inode_operations;
-        memcpy(oinfo->lli_inline, symname, l);
-        inode->i_size = l-1;
+
+        OBD_ALLOC(oinfo->lli_symlink_name, l + 1);
+        memcpy(oinfo->lli_symlink_name, symname, l + 1);
+        inode->i_size = l;
 
         err = ext2_add_nondir(dentry, inode);
 
-        if (err) { 
+        if (err) {
                 ext2_dec_count(inode);
                 iput (inode);
         }
         return err;
 }
 
-static int ll_link (struct dentry * old_dentry, struct inode * dir,
-        struct dentry *dentry)
+static int ll_link(struct dentry * old_dentry, struct inode * dir,
+                   struct dentry *dentry)
 {
         int err;
         struct inode *inode = old_dentry->d_inode;
@@ -332,9 +462,9 @@ static int ll_link (struct dentry * old_dentry, struct inode * dir,
         if (inode->i_nlink >= EXT2_LINK_MAX)
                 return -EMLINK;
 
-        err = ll_mdc_link(old_dentry, dir, 
+        err = ll_mdc_link(old_dentry, dir,
                           dentry->d_name.name, dentry->d_name.len);
-        if (err) { 
+        if (err) {
                 EXIT;
                 return err;
         }
@@ -346,7 +476,6 @@ static int ll_link (struct dentry * old_dentry, struct inode * dir,
         return ext2_add_nondir(dentry, inode);
 }
 
-
 static int ll_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 {
         struct inode * inode;
@@ -358,16 +487,13 @@ static int ll_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 
         ext2_inc_count(dir);
 
-        inode = ll_create_node (dir, dentry->d_name.name, 
-                                dentry->d_name.len, NULL, 0, 
-                                S_IFDIR | mode, 0);
+        inode = ll_create_node (dir, dentry->d_name.name,
+                                dentry->d_name.len, NULL, 0,
+                                S_IFDIR | mode, 0, dentry->d_it, NULL);
         err = PTR_ERR(inode);
         if (IS_ERR(inode))
                 goto out_dir;
 
-        inode->i_op = &ll_dir_inode_operations;
-        inode->i_fop = &ll_dir_operations;
-        inode->i_mapping->a_ops = &ll_aops;
         inode->i_nlink = 1;
         ext2_inc_count(inode);
 
@@ -375,9 +501,12 @@ static int ll_mkdir(struct inode * dir, struct dentry * dentry, int mode)
         if (err)
                 goto out_fail;
 
-        err = ll_add_link(dentry, inode);
-        if (err)
-                goto out_fail;
+        /* no directory data updates when intents rule */
+        if (dentry->d_it->it_disposition == 0) {
+                err = ll_add_link(dentry, inode);
+                if (err)
+                        goto out_fail;
+        }
 
         d_instantiate(dentry, inode);
 out:
@@ -402,13 +531,18 @@ static int ll_unlink(struct inode * dir, struct dentry *dentry)
         struct page * page;
         int err = -ENOENT;
 
+        if (dentry->d_it && dentry->d_it->it_disposition) {
+                inode->i_nlink = 0;
+                GOTO(out, err = dentry->d_it->it_status);
+        }
+
         de = ext2_find_entry (dir, dentry, &page);
         if (!de)
                 goto out;
-        
+
         err = ll_mdc_unlink(dir, dentry->d_inode,
                             dentry->d_name.name, dentry->d_name.len);
-        if (err) 
+        if (err)
                 goto out;
 
         err = ext2_delete_entry (de, page);
@@ -417,7 +551,6 @@ static int ll_unlink(struct inode * dir, struct dentry *dentry)
 
         inode->i_ctime = dir->i_ctime;
         ext2_dec_count(inode);
-        err = 0;
 out:
         return err;
 }
@@ -425,21 +558,26 @@ out:
 static int ll_rmdir(struct inode * dir, struct dentry *dentry)
 {
         struct inode * inode = dentry->d_inode;
-        int err = -ENOTEMPTY;
+        int err = 0;
+        int intent_did = dentry->d_it && dentry->d_it->it_disposition;
+
+        if (!intent_did) {
+                if (!ext2_empty_dir(inode))
+                LBUG();
 
-        if (ext2_empty_dir(inode)) {
                 err = ll_unlink(dir, dentry);
-                if (!err) {
-                        inode->i_size = 0;
-                        ext2_dec_count(inode);
-                        ext2_dec_count(dir);
-                }
-        }
-        return err;
+                if (err)
+                        RETURN(err);
+        } else
+                err = dentry->d_it->it_status;
+        inode->i_size = 0;
+        ext2_dec_count(inode);
+        ext2_dec_count(dir);
+        RETURN(err);
 }
 
-static int ll_rename (struct inode * old_dir, struct dentry * old_dentry,
-        struct inode * new_dir, struct dentry * new_dentry )
+static int ll_rename(struct inode * old_dir, struct dentry * old_dentry,
+                     struct inode * new_dir, struct dentry * new_dentry)
 {
         struct inode * old_inode = old_dentry->d_inode;
         struct inode * new_inode = new_dentry->d_inode;
@@ -449,8 +587,11 @@ static int ll_rename (struct inode * old_dir, struct dentry * old_dentry,
         struct ext2_dir_entry_2 * old_de;
         int err = -ENOENT;
 
-        err = ll_mdc_rename(old_dir, new_dir, old_dentry, new_dentry); 
-        if (err) 
+        if (new_dentry->d_it && new_dentry->d_it->it_disposition)
+                GOTO(out, err = new_dentry->d_it->it_status);
+
+        err = ll_mdc_rename(old_dir, new_dir, old_dentry, new_dentry);
+        if (err)
                 goto out;
 
         old_de = ext2_find_entry (old_dir, old_dentry, &old_page);
@@ -507,7 +648,6 @@ static int ll_rename (struct inode * old_dir, struct dentry * old_dentry,
         }
         return 0;
 
-
 out_dir:
         if (dir_de) {
                 kunmap(dir_page);
@@ -522,7 +662,7 @@ out:
 
 struct inode_operations ll_dir_inode_operations = {
         create:         ll_create,
-        lookup:         ll_lookup,
+        lookup2:        ll_lookup2,
         link:           ll_link,
         unlink:         ll_unlink,
         symlink:        ll_symlink,