xfs: merge xfs_mkdir into xfs_create
[safe/jmp/linux-2.6] / fs / xfs / xfs_dir2.c
index 3387acd..1afb122 100644 (file)
@@ -48,6 +48,52 @@ struct xfs_name xfs_name_dotdot = {"..", 2};
 
 extern const struct xfs_nameops xfs_default_nameops;
 
+/*
+ * ASCII case-insensitive (ie. A-Z) support for directories that was
+ * used in IRIX.
+ */
+STATIC xfs_dahash_t
+xfs_ascii_ci_hashname(
+       struct xfs_name *name)
+{
+       xfs_dahash_t    hash;
+       int             i;
+
+       for (i = 0, hash = 0; i < name->len; i++)
+               hash = tolower(name->name[i]) ^ rol32(hash, 7);
+
+       return hash;
+}
+
+STATIC enum xfs_dacmp
+xfs_ascii_ci_compname(
+       struct xfs_da_args *args,
+       const char      *name,
+       int             len)
+{
+       enum xfs_dacmp  result;
+       int             i;
+
+       if (args->namelen != len)
+               return XFS_CMP_DIFFERENT;
+
+       result = XFS_CMP_EXACT;
+       for (i = 0; i < len; i++) {
+               if (args->name[i] == name[i])
+                       continue;
+               if (tolower(args->name[i]) != tolower(name[i]))
+                       return XFS_CMP_DIFFERENT;
+               result = XFS_CMP_CASE;
+       }
+
+       return result;
+}
+
+static struct xfs_nameops xfs_ascii_ci_nameops = {
+       .hashname       = xfs_ascii_ci_hashname,
+       .compname       = xfs_ascii_ci_compname,
+};
+
 void
 xfs_dir_mount(
        xfs_mount_t     *mp)
@@ -67,7 +113,10 @@ xfs_dir_mount(
                (mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
                (uint)sizeof(xfs_da_node_entry_t);
        mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
-       mp->m_dirnameops = &xfs_default_nameops;
+       if (xfs_sb_version_hasasciici(&mp->m_sb))
+               mp->m_dirnameops = &xfs_ascii_ci_nameops;
+       else
+               mp->m_dirnameops = &xfs_default_nameops;
 }
 
 /*
@@ -165,6 +214,7 @@ xfs_dir_createname(
                return rval;
        XFS_STATS_INC(xs_dir_create);
 
+       memset(&args, 0, sizeof(xfs_da_args_t));
        args.name = name->name;
        args.namelen = name->len;
        args.hashval = dp->i_mount->m_dirnameops->hashname(name);
@@ -193,14 +243,43 @@ xfs_dir_createname(
 }
 
 /*
+ * If doing a CI lookup and case-insensitive match, dup actual name into
+ * args.value. Return EEXIST for success (ie. name found) or an error.
+ */
+int
+xfs_dir_cilookup_result(
+       struct xfs_da_args *args,
+       const char      *name,
+       int             len)
+{
+       if (args->cmpresult == XFS_CMP_DIFFERENT)
+               return ENOENT;
+       if (args->cmpresult != XFS_CMP_CASE ||
+                                       !(args->op_flags & XFS_DA_OP_CILOOKUP))
+               return EEXIST;
+
+       args->value = kmem_alloc(len, KM_MAYFAIL);
+       if (!args->value)
+               return ENOMEM;
+
+       memcpy(args->value, name, len);
+       args->valuelen = len;
+       return EEXIST;
+}
+
+/*
  * Lookup a name in a directory, give back the inode number.
+ * If ci_name is not NULL, returns the actual name in ci_name if it differs
+ * to name, or ci_name->name is set to NULL for an exact match.
  */
+
 int
 xfs_dir_lookup(
        xfs_trans_t     *tp,
        xfs_inode_t     *dp,
        struct xfs_name *name,
-       xfs_ino_t       *inum)          /* out: inode number */
+       xfs_ino_t       *inum,          /* out: inode number */
+       struct xfs_name *ci_name)       /* out: actual name if CI match */
 {
        xfs_da_args_t   args;
        int             rval;
@@ -208,8 +287,8 @@ xfs_dir_lookup(
 
        ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
        XFS_STATS_INC(xs_dir_lookup);
-       memset(&args, 0, sizeof(xfs_da_args_t));
 
+       memset(&args, 0, sizeof(xfs_da_args_t));
        args.name = name->name;
        args.namelen = name->len;
        args.hashval = dp->i_mount->m_dirnameops->hashname(name);
@@ -217,7 +296,8 @@ xfs_dir_lookup(
        args.whichfork = XFS_DATA_FORK;
        args.trans = tp;
        args.op_flags = XFS_DA_OP_OKNOENT;
-       args.cmpresult = XFS_CMP_DIFFERENT;
+       if (ci_name)
+               args.op_flags |= XFS_DA_OP_CILOOKUP;
 
        if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
                rval = xfs_dir2_sf_lookup(&args);
@@ -233,8 +313,13 @@ xfs_dir_lookup(
                rval = xfs_dir2_node_lookup(&args);
        if (rval == EEXIST)
                rval = 0;
-       if (rval == 0)
+       if (!rval) {
                *inum = args.inumber;
+               if (ci_name) {
+                       ci_name->name = args.value;
+                       ci_name->len = args.valuelen;
+               }
+       }
        return rval;
 }
 
@@ -258,6 +343,7 @@ xfs_dir_removename(
        ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
        XFS_STATS_INC(xs_dir_remove);
 
+       memset(&args, 0, sizeof(xfs_da_args_t));
        args.name = name->name;
        args.namelen = name->len;
        args.hashval = dp->i_mount->m_dirnameops->hashname(name);
@@ -268,7 +354,6 @@ xfs_dir_removename(
        args.total = total;
        args.whichfork = XFS_DATA_FORK;
        args.trans = tp;
-       args.op_flags = 0;
 
        if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
                rval = xfs_dir2_sf_removename(&args);
@@ -341,6 +426,7 @@ xfs_dir_replace(
        if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
                return rval;
 
+       memset(&args, 0, sizeof(xfs_da_args_t));
        args.name = name->name;
        args.namelen = name->len;
        args.hashval = dp->i_mount->m_dirnameops->hashname(name);
@@ -351,7 +437,6 @@ xfs_dir_replace(
        args.total = total;
        args.whichfork = XFS_DATA_FORK;
        args.trans = tp;
-       args.op_flags = 0;
 
        if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
                rval = xfs_dir2_sf_replace(&args);
@@ -387,8 +472,8 @@ xfs_dir_canenter(
                return 0;
 
        ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
-       memset(&args, 0, sizeof(xfs_da_args_t));
 
+       memset(&args, 0, sizeof(xfs_da_args_t));
        args.name = name->name;
        args.namelen = name->len;
        args.hashval = dp->i_mount->m_dirnameops->hashname(name);
@@ -440,11 +525,13 @@ xfs_dir2_grow_inode(
        xfs_mount_t     *mp;
        int             nmap;           /* number of bmap entries */
        xfs_trans_t     *tp;
+       xfs_drfsbno_t   nblks;
 
        xfs_dir2_trace_args_s("grow_inode", args, space);
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
+       nblks = dp->i_d.di_nblocks;
        /*
         * Set lowest possible block in the space requested.
         */
@@ -537,7 +624,11 @@ xfs_dir2_grow_inode(
         */
        if (mapp != &map)
                kmem_free(mapp);
+
+       /* account for newly allocated blocks in reserved blocks total */
+       args->total -= dp->i_d.di_nblocks - nblks;
        *dbp = xfs_dir2_da_to_db(mp, (xfs_dablk_t)bno);
+
        /*
         * Update file's size if this is the data space and it grew.
         */