* [REVIEW 1/2] Case insensitive support for XFS - kernel patch
@ 2008-01-18 4:43 Barry Naujok
2008-01-19 5:22 ` Eric Sandeen
2008-01-19 5:30 ` Eric Sandeen
0 siblings, 2 replies; 5+ messages in thread
From: Barry Naujok @ 2008-01-18 4:43 UTC (permalink / raw)
To: xfs@oss.sgi.com; +Cc: xfs-dev
[-- Attachment #1: Type: text/plain, Size: 1319 bytes --]
This patch should apply to 2.6.24-rc6.
Makefile | 1
linux-2.6/xfs_export.c | 2
linux-2.6/xfs_iops.c | 130 ++++++++++++
linux-2.6/xfs_iops.h | 1
linux-2.6/xfs_ksyms.c | 1
linux-2.6/xfs_linux.h | 8
linux-2.6/xfs_super.c | 31 ++
xfs_attr.c | 47 +++-
xfs_attr_leaf.c | 217 +++++++++++---------
xfs_attr_leaf.h | 12 +
xfs_clnt.h | 5
xfs_da_btree.c | 95 ++++++++
xfs_da_btree.h | 35 +++
xfs_dir2.c | 141 ++++++++++---
xfs_dir2.h | 10
xfs_dir2_block.c | 63 ++++-
xfs_dir2_data.c | 3
xfs_dir2_leaf.c | 48 +++-
xfs_dir2_node.c | 36 ++-
xfs_dir2_sf.c | 66 ++++--
xfs_itable.c | 2
xfs_mount.c | 26 ++
xfs_mount.h | 12 +
xfs_rename.c | 5
xfs_sb.h | 33 ++-
xfs_types.h | 5
xfs_unicode.c | 523
+++++++++++++++++++++++++++++++++++++++++++++++++
xfs_unicode.h | 76 +++++++
xfs_utils.c | 10
xfs_utils.h | 2
xfs_vfsops.c | 40 +++
xfs_vnodeops.c | 7
xfs_vnodeops.h | 2
33 files changed, 1495 insertions(+), 200 deletions(-)
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ci_kernel.patch --]
[-- Type: text/x-patch; name=ci_kernel.patch, Size: 96466 bytes --]
===========================================================================
fs/xfs/Makefile
===========================================================================
--- a/fs/xfs/Makefile 2008-01-18 15:31:23.000000000 +1100
+++ b/fs/xfs/Makefile 2007-10-23 16:17:22.173903950 +1000
@@ -74,6 +74,7 @@ xfs-y += xfs_alloc.o \
xfs_trans_extfree.o \
xfs_trans_inode.o \
xfs_trans_item.o \
+ xfs_unicode.o \
xfs_utils.o \
xfs_vfsops.o \
xfs_vnodeops.o \
===========================================================================
fs/xfs/linux-2.6/xfs_export.c
===========================================================================
--- a/fs/xfs/linux-2.6/xfs_export.c 2008-01-18 15:31:23.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_export.c 2008-01-11 14:20:10.000000000 +1100
@@ -217,7 +217,7 @@ xfs_fs_get_parent(
struct dentry *parent;
cvp = NULL;
- error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp);
+ error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp, NULL, NULL);
if (unlikely(error))
return ERR_PTR(-error);
===========================================================================
fs/xfs/linux-2.6/xfs_iops.c
===========================================================================
--- a/fs/xfs/linux-2.6/xfs_iops.c 2008-01-18 15:31:23.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_iops.c 2008-01-17 12:26:26.905427167 +1100
@@ -47,6 +47,8 @@
#include "xfs_buf_item.h"
#include "xfs_utils.h"
#include "xfs_vnodeops.h"
+#include "xfs_da_btree.h"
+#include "xfs_unicode.h"
#include <linux/capability.h>
#include <linux/xattr.h>
@@ -388,7 +390,7 @@ xfs_vn_lookup(
if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG);
- error = xfs_lookup(XFS_I(dir), dentry, &cvp);
+ error = xfs_lookup(XFS_I(dir), dentry, &cvp, NULL, NULL);
if (unlikely(error)) {
if (unlikely(error != ENOENT))
return ERR_PTR(-error);
@@ -399,6 +401,113 @@ xfs_vn_lookup(
return d_splice_alias(vn_to_inode(cvp), dentry);
}
+STATIC struct dentry *
+xfs_vn_ci_lookup(
+ struct inode *dir,
+ struct dentry *dentry,
+ struct nameidata *nd)
+{
+ bhv_vnode_t *cvp;
+ int error;
+ struct dentry *result;
+ struct qstr actual_name;
+ struct inode *inode;
+
+ if (dentry->d_name.len >= MAXNAMELEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ error = xfs_lookup(XFS_I(dir), dentry, &cvp, (char **)&actual_name.name,
+ &actual_name.len);
+ if (unlikely(error)) {
+ if (unlikely(error != ENOENT))
+ return ERR_PTR(-error);
+ d_add(dentry, NULL);
+ return NULL;
+ }
+ inode = vn_to_inode(cvp);
+
+ /* if exact match, just splice and exit */
+ if (!actual_name.name) {
+ result = d_splice_alias(inode, dentry);
+ return result;
+ }
+
+ /*
+ * case-insensitive match, create a dentry to return and fill it
+ * in with the correctly cased name. Parameter "dentry" is not
+ * used anymore and the caller will free it.
+ * Derived from fs/ntfs/namei.c
+ */
+
+ actual_name.hash = full_name_hash(actual_name.name, actual_name.len);
+
+ /* Does an existing dentry match? */
+ result = d_lookup(dentry->d_parent, &actual_name);
+ if (!result) {
+ /* if not, create one */
+ result = d_alloc(dentry->d_parent, &actual_name);
+ xfs_free_unicode_nls_name((char *)actual_name.name);
+ if (!result)
+ return ERR_PTR(-ENOMEM);
+ dentry = d_splice_alias(inode, result);
+ if (dentry) {
+ dput(result);
+ return dentry;
+ }
+ return result;
+ }
+ xfs_free_unicode_nls_name((char *)actual_name.name);
+
+ /* an existing dentry matches, use it */
+
+ if (result->d_inode) {
+ /*
+ * already an inode attached, deref the inode that was
+ * refcounted with xfs_lookup and return the dentry.
+ */
+ if (unlikely(result->d_inode != inode)) {
+ /* This can happen because bad inodes are unhashed. */
+ BUG_ON(!is_bad_inode(inode));
+ BUG_ON(!is_bad_inode(result->d_inode));
+ }
+ iput(inode);
+ return result;
+ }
+
+ if (!S_ISDIR(inode->i_mode)) {
+ /* not a directory, easy to handle */
+ d_instantiate(result, inode);
+ return result;
+ }
+
+ spin_lock(&dcache_lock);
+ if (list_empty(&inode->i_dentry)) {
+ /*
+ * Directory without a 'disconnected' dentry; we need to do
+ * d_instantiate() by hand because it takes dcache_lock which
+ * we already hold.
+ */
+ list_add(&result->d_alias, &inode->i_dentry);
+ result->d_inode = inode;
+ spin_unlock(&dcache_lock);
+ security_d_instantiate(result, inode);
+ return result;
+ }
+ /*
+ * Directory with a 'disconnected' dentry; get a reference to the
+ * 'disconnected' dentry.
+ */
+ dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+ dget_locked(dentry);
+ spin_unlock(&dcache_lock);
+ security_d_instantiate(result, inode);
+ d_move(dentry, result);
+ iput(inode);
+ dput(result);
+ return dentry;
+}
+
+
STATIC int
xfs_vn_link(
struct dentry *old_dentry,
@@ -868,6 +977,25 @@ const struct inode_operations xfs_dir_in
.removexattr = xfs_vn_removexattr,
};
+const struct inode_operations xfs_dir_ci_inode_operations = {
+ .create = xfs_vn_create,
+ .lookup = xfs_vn_ci_lookup,
+ .link = xfs_vn_link,
+ .unlink = xfs_vn_unlink,
+ .symlink = xfs_vn_symlink,
+ .mkdir = xfs_vn_mkdir,
+ .rmdir = xfs_vn_rmdir,
+ .mknod = xfs_vn_mknod,
+ .rename = xfs_vn_rename,
+ .permission = xfs_vn_permission,
+ .getattr = xfs_vn_getattr,
+ .setattr = xfs_vn_setattr,
+ .setxattr = xfs_vn_setxattr,
+ .getxattr = xfs_vn_getxattr,
+ .listxattr = xfs_vn_listxattr,
+ .removexattr = xfs_vn_removexattr,
+};
+
const struct inode_operations xfs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = xfs_vn_follow_link,
===========================================================================
fs/xfs/linux-2.6/xfs_iops.h
===========================================================================
--- a/fs/xfs/linux-2.6/xfs_iops.h 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_iops.h 2007-10-26 13:19:11.702517171 +1000
@@ -20,6 +20,7 @@
extern const struct inode_operations xfs_inode_operations;
extern const struct inode_operations xfs_dir_inode_operations;
+extern const struct inode_operations xfs_dir_ci_inode_operations;
extern const struct inode_operations xfs_symlink_inode_operations;
extern const struct file_operations xfs_file_operations;
===========================================================================
fs/xfs/linux-2.6/xfs_ksyms.c
===========================================================================
--- a/fs/xfs/linux-2.6/xfs_ksyms.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_ksyms.c 2008-01-11 14:20:11.000000000 +1100
@@ -157,6 +157,7 @@ EXPORT_SYMBOL(kmem_zone_init);
EXPORT_SYMBOL(kmem_zone_zalloc);
EXPORT_SYMBOL(xfs_address_space_operations);
EXPORT_SYMBOL(xfs_dir_inode_operations);
+EXPORT_SYMBOL(xfs_dir_ci_inode_operations);
EXPORT_SYMBOL(xfs_dir_file_operations);
EXPORT_SYMBOL(xfs_inode_operations);
EXPORT_SYMBOL(xfs_file_operations);
===========================================================================
fs/xfs/linux-2.6/xfs_linux.h
===========================================================================
--- a/fs/xfs/linux-2.6/xfs_linux.h 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_linux.h 2008-01-11 14:49:16.537591564 +1100
@@ -75,6 +75,8 @@
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/nls.h>
#include <asm/page.h>
#include <asm/div64.h>
@@ -180,6 +182,12 @@
#define howmany(x, y) (((x)+((y)-1))/(y))
/*
+ * NLS UTF-8 character set
+ */
+
+#define XFS_NLS_UTF8 "utf8"
+
+/*
* Various platform dependent calls that don't fit anywhere else
*/
#define xfs_sort(a,n,s,fn) sort(a,n,s,fn,NULL)
===========================================================================
fs/xfs/linux-2.6/xfs_super.c
===========================================================================
--- a/fs/xfs/linux-2.6/xfs_super.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/linux-2.6/xfs_super.c 2008-01-11 14:46:25.067566854 +1100
@@ -50,6 +50,7 @@
#include "xfs_vnodeops.h"
#include "xfs_vfsops.h"
#include "xfs_version.h"
+#include "xfs_unicode.h"
#include "xfs_log_priv.h"
#include <linux/namei.h>
@@ -121,6 +122,9 @@ xfs_args_allocate(
#define MNTOPT_ATTR2 "attr2" /* do use attr2 attribute format */
#define MNTOPT_NOATTR2 "noattr2" /* do not use attr2 attribute format */
#define MNTOPT_FILESTREAM "filestreams" /* use filestreams allocator */
+#define MNTOPT_NLS "nls" /* NLS code page to use */
+#define MNTOPT_CILOOKUP "ci" /* case-insensitive dir names */
+#define MNTOPT_CIATTR "ciattr" /* case-insensitive attr names */
#define MNTOPT_QUOTA "quota" /* disk quotas (user) */
#define MNTOPT_NOQUOTA "noquota" /* no quotas */
#define MNTOPT_USRQUOTA "usrquota" /* user quota enabled */
@@ -315,6 +319,18 @@ xfs_parseargs(
args->flags &= ~XFSMNT_ATTR2;
} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
args->flags2 |= XFSMNT2_FILESTREAMS;
+ } else if (!strcmp(this_char, MNTOPT_NLS)) {
+ if (!value || !*value) {
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
+ this_char);
+ return EINVAL;
+ }
+ strncpy(args->nls, value, MAXNAMELEN);
+ } else if (!strcmp(this_char, MNTOPT_CILOOKUP)) {
+ args->flags2 |= XFSMNT2_CILOOKUP;
+ } else if (!strcmp(this_char, MNTOPT_CIATTR)) {
+ args->flags2 |= XFSMNT2_CIATTR;
} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
@@ -454,6 +470,8 @@ xfs_showargs(
{ XFS_MOUNT_OSYNCISOSYNC, "," MNTOPT_OSYNCISOSYNC },
{ XFS_MOUNT_ATTR2, "," MNTOPT_ATTR2 },
{ XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM },
+ { XFS_MOUNT_CI_LOOKUP, "," MNTOPT_CILOOKUP },
+ { XFS_MOUNT_CI_ATTR, "," MNTOPT_CIATTR },
{ XFS_MOUNT_DMAPI, "," MNTOPT_DMAPI },
{ XFS_MOUNT_GRPID, "," MNTOPT_GRPID },
{ 0, NULL }
@@ -516,6 +534,13 @@ xfs_showargs(
if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT))
seq_puts(m, "," MNTOPT_NOQUOTA);
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ if (mp->m_nls)
+ seq_printf(m, "," MNTOPT_NLS "=%s", mp->m_nls->charset);
+ else
+ seq_puts(m, "," MNTOPT_NLS "=" XFS_NLS_UTF8);
+ }
+
return 0;
}
__uint64_t
@@ -563,7 +588,11 @@ xfs_set_inodeops(
inode->i_mapping->a_ops = &xfs_address_space_operations;
break;
case S_IFDIR:
- inode->i_op = &xfs_dir_inode_operations;
+ inode->i_op =
+ xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) ||
+ (XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CI_LOOKUP) ?
+ &xfs_dir_ci_inode_operations :
+ &xfs_dir_inode_operations;
inode->i_fop = &xfs_dir_file_operations;
break;
case S_IFLNK:
===========================================================================
fs/xfs/xfs_attr.c
===========================================================================
--- a/fs/xfs/xfs_attr.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_attr.c 2008-01-18 13:25:20.068339942 +1100
@@ -106,6 +106,17 @@ ktrace_t *xfs_attr_trace_buf;
* Overall external interface routines.
*========================================================================*/
+void
+xfs_attr_mount(struct xfs_mount *mp)
+{
+ mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ mp->m_attrnameops = (mp->m_flags & XFS_MOUNT_CI_ATTR) ?
+ &xfs_unicode_ci_nameops : &xfs_unicode_nameops;
+ } else
+ mp->m_attrnameops = &xfs_default_nameops;
+}
+
int
xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen,
char *value, int *valuelenp, int flags, struct cred *cred)
@@ -122,14 +133,14 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
* Fill in the arg structure for this request.
*/
memset((char *)&args, 0, sizeof(args));
- args.name = name;
- args.namelen = namelen;
args.value = value;
args.valuelen = *valuelenp;
args.flags = flags;
- args.hashval = xfs_da_hashname(args.name, args.namelen);
args.dp = ip;
args.whichfork = XFS_ATTR_FORK;
+ error = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (error)
+ return error;
/*
* Decide on what work routines to call based on the inode size.
@@ -153,6 +164,7 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch
if (error == EEXIST)
error = 0;
+ xfs_da_cleanup_name(&args, name);
return(error);
}
@@ -181,6 +193,7 @@ xfs_attr_get(
xfs_ilock(ip, XFS_ILOCK_SHARED);
error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred);
xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
return(error);
}
@@ -219,18 +232,18 @@ xfs_attr_set_int(xfs_inode_t *dp, const
* Fill in the arg structure for this request.
*/
memset((char *)&args, 0, sizeof(args));
- args.name = name;
- args.namelen = namelen;
args.value = value;
args.valuelen = valuelen;
args.flags = flags;
- args.hashval = xfs_da_hashname(args.name, args.namelen);
args.dp = dp;
args.firstblock = &firstblock;
args.flist = &flist;
args.whichfork = XFS_ATTR_FORK;
args.addname = 1;
args.oknoent = 1;
+ error = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (error)
+ return error;
/*
* Determine space new attribute will use, and if it would be
@@ -282,6 +295,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const
0, XFS_TRANS_PERM_LOG_RES,
XFS_ATTRSET_LOG_COUNT))) {
xfs_trans_cancel(args.trans, 0);
+ xfs_da_cleanup_name(&args, name);
return(error);
}
xfs_ilock(dp, XFS_ILOCK_EXCL);
@@ -292,6 +306,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const
if (error) {
xfs_iunlock(dp, XFS_ILOCK_EXCL);
xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES);
+ xfs_da_cleanup_name(&args, name);
return (error);
}
@@ -342,6 +357,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const
if (!error && (flags & ATTR_KERNOTIME) == 0) {
xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
}
+ xfs_da_cleanup_name(&args, name);
return(error == 0 ? err2 : error);
}
@@ -411,6 +427,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const
xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
}
+ xfs_da_cleanup_name(&args, name);
return(error);
out:
@@ -418,6 +435,7 @@ out:
xfs_trans_cancel(args.trans,
XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ xfs_da_cleanup_name(&args, name);
return(error);
}
@@ -460,21 +478,23 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
* Fill in the arg structure for this request.
*/
memset((char *)&args, 0, sizeof(args));
- args.name = name;
- args.namelen = namelen;
args.flags = flags;
- args.hashval = xfs_da_hashname(args.name, args.namelen);
args.dp = dp;
args.firstblock = &firstblock;
args.flist = &flist;
args.total = 0;
args.whichfork = XFS_ATTR_FORK;
+ error = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (error)
+ return error;
/*
* Attach the dquots to the inode.
*/
- if ((error = XFS_QM_DQATTACH(mp, dp, 0)))
+ if ((error = XFS_QM_DQATTACH(mp, dp, 0))) {
+ xfs_da_cleanup_name(&args, name);
return (error);
+ }
/*
* Start our first transaction of the day.
@@ -502,6 +522,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
0, XFS_TRANS_PERM_LOG_RES,
XFS_ATTRRM_LOG_COUNT))) {
xfs_trans_cancel(args.trans, 0);
+ xfs_da_cleanup_name(&args, name);
return(error);
}
@@ -559,6 +580,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con
xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
}
+ xfs_da_cleanup_name(&args, name);
return(error);
out:
@@ -566,6 +588,7 @@ out:
xfs_trans_cancel(args.trans,
XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ xfs_da_cleanup_name(&args, name);
return(error);
}
@@ -634,7 +657,7 @@ xfs_attr_list_int(xfs_attr_list_context_
*/
/*ARGSUSED*/
STATIC int
-xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
+xfs_attr_user_list(xfs_attr_list_context_t *context, attrnames_t *namesp,
char *name, int namelen,
int valuelen, char *value)
{
@@ -765,7 +788,7 @@ xfs_attr_list(
context.alist->al_count = 0;
context.alist->al_more = 0;
context.alist->al_offset[0] = context.bufsize;
- context.put_listent = xfs_attr_put_listent;
+ context.put_listent = xfs_attr_user_list;
}
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
===========================================================================
fs/xfs/xfs_attr_leaf.c
===========================================================================
--- a/fs/xfs/xfs_attr_leaf.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_attr_leaf.c 2008-01-18 13:25:11.873394723 +1100
@@ -42,6 +42,7 @@
#include "xfs_attr.h"
#include "xfs_attr_leaf.h"
#include "xfs_error.h"
+#include "xfs_unicode.h"
/*
* xfs_attr_leaf.c
@@ -90,6 +91,10 @@ STATIC void xfs_attr_leaf_moveents(xfs_a
xfs_mount_t *mp);
STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index);
+STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context,
+ attrnames_t *namesp, char *name,
+ int namelen, int valuelen, char *value);
+
/*========================================================================
* Namespace helper routines
*========================================================================*/
@@ -135,6 +140,38 @@ xfs_attr_namesp_match_overrides(int arg_
* External routines when attribute fork size < XFS_LITINO(mp).
*========================================================================*/
+STATIC xfs_attr_sf_entry_t *
+xfs_attr_shortform_find_ent(xfs_da_args_t *args)
+{
+ xfs_attr_shortform_t *sf;
+ xfs_attr_sf_entry_t *sfe;
+ int i;
+ xfs_attr_sf_entry_t *ci_sfe = NULL;
+
+ ASSERT(args->dp->i_afp->if_flags & XFS_IFINLINE);
+ sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
+ sfe = &sf->list[0];
+
+ args->cmpresult = XFS_CMP_DIFFERENT;
+ for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+ if (!xfs_attr_namesp_match(args->flags, sfe->flags))
+ continue;
+ switch (xfs_attr_compname(args->dp, sfe->nameval, sfe->namelen,
+ args->name, args->namelen)) {
+ case XFS_CMP_EXACT:
+ args->cmpresult = XFS_CMP_EXACT;
+ return sfe;
+ case XFS_CMP_CASE:
+ if (!ci_sfe) {
+ args->cmpresult = XFS_CMP_CASE;
+ ci_sfe = sfe;
+ }
+ default:;
+ }
+ }
+ return ci_sfe;
+}
+
/*
* Query whether the requested number of additional bytes of extended
* attribute space will be able to fit inline.
@@ -295,13 +332,10 @@ xfs_attr_shortform_add(xfs_da_args_t *ar
sfe = &sf->list[0];
for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
#ifdef DEBUG
- if (sfe->namelen != args->namelen)
- continue;
- if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
- continue;
if (!xfs_attr_namesp_match(args->flags, sfe->flags))
continue;
- ASSERT(0);
+ ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+ sfe->nameval, sfe->namelen) == XFS_CMP_DIFFERENT);
#endif
}
@@ -331,29 +365,19 @@ xfs_attr_shortform_remove(xfs_da_args_t
{
xfs_attr_shortform_t *sf;
xfs_attr_sf_entry_t *sfe;
- int base, size=0, end, totsize, i;
+ int base, size, end, totsize;
xfs_mount_t *mp;
xfs_inode_t *dp;
+ sfe = xfs_attr_shortform_find_ent(args);
+ if (!sfe)
+ return XFS_ERROR(ENOATTR);
+
dp = args->dp;
mp = dp->i_mount;
- base = sizeof(xfs_attr_sf_hdr_t);
sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
- sfe = &sf->list[0];
- end = sf->hdr.count;
- for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
- base += size, i++) {
size = XFS_ATTR_SF_ENTSIZE(sfe);
- if (sfe->namelen != args->namelen)
- continue;
- if (memcmp(sfe->nameval, args->name, args->namelen) != 0)
- continue;
- if (!xfs_attr_namesp_match(args->flags, sfe->flags))
- continue;
- break;
- }
- if (i == end)
- return(XFS_ERROR(ENOATTR));
+ base = (int)((char *)sfe - (char *)sf);
/*
* Fix up the attribute fork data, covering the hole
@@ -412,26 +436,7 @@ xfs_attr_shortform_remove(xfs_da_args_t
int
xfs_attr_shortform_lookup(xfs_da_args_t *args)
{
- xfs_attr_shortform_t *sf;
- xfs_attr_sf_entry_t *sfe;
- int i;
- xfs_ifork_t *ifp;
-
- ifp = args->dp->i_afp;
- ASSERT(ifp->if_flags & XFS_IFINLINE);
- sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
- sfe = &sf->list[0];
- for (i = 0; i < sf->hdr.count;
- sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
- if (sfe->namelen != args->namelen)
- continue;
- if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
- continue;
- if (!xfs_attr_namesp_match(args->flags, sfe->flags))
- continue;
- return(XFS_ERROR(EEXIST));
- }
- return(XFS_ERROR(ENOATTR));
+ return XFS_ERROR(xfs_attr_shortform_find_ent(args) ? EEXIST : ENOATTR);
}
/*
@@ -441,35 +446,24 @@ xfs_attr_shortform_lookup(xfs_da_args_t
int
xfs_attr_shortform_getvalue(xfs_da_args_t *args)
{
- xfs_attr_shortform_t *sf;
xfs_attr_sf_entry_t *sfe;
- int i;
- ASSERT(args->dp->i_d.di_aformat == XFS_IFINLINE);
- sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
- sfe = &sf->list[0];
- for (i = 0; i < sf->hdr.count;
- sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
- if (sfe->namelen != args->namelen)
- continue;
- if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
- continue;
- if (!xfs_attr_namesp_match(args->flags, sfe->flags))
- continue;
+ sfe = xfs_attr_shortform_find_ent(args);
+ if (!sfe)
+ return XFS_ERROR(ENOATTR);
+
if (args->flags & ATTR_KERNOVAL) {
args->valuelen = sfe->valuelen;
- return(XFS_ERROR(EEXIST));
+ return XFS_ERROR(EEXIST);
}
if (args->valuelen < sfe->valuelen) {
args->valuelen = sfe->valuelen;
- return(XFS_ERROR(ERANGE));
+ return XFS_ERROR(ERANGE);
}
args->valuelen = sfe->valuelen;
- memcpy(args->value, &sfe->nameval[args->namelen],
- args->valuelen);
- return(XFS_ERROR(EEXIST));
- }
- return(XFS_ERROR(ENOATTR));
+ memcpy(args->value, &sfe->nameval[args->namelen], args->valuelen);
+
+ return XFS_ERROR(EEXIST);
}
/*
@@ -535,17 +529,18 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t
sfe = &sf->list[0];
for (i = 0; i < sf->hdr.count; i++) {
- nargs.name = (char *)sfe->nameval;
- nargs.namelen = sfe->namelen;
- nargs.value = (char *)&sfe->nameval[nargs.namelen];
+ nargs.value = (char *)&sfe->nameval[sfe->namelen];
nargs.valuelen = sfe->valuelen;
- nargs.hashval = xfs_da_hashname((char *)sfe->nameval,
+ error = xfs_da_setup_name_and_hash(&nargs, sfe->nameval,
sfe->namelen);
+ if (error)
+ goto out;
nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(sfe->flags);
error = xfs_attr_leaf_lookup_int(bp, &nargs); /* set a->index */
ASSERT(error == ENOATTR);
error = xfs_attr_leaf_add(bp, &nargs);
ASSERT(error != ENOSPC);
+ xfs_da_cleanup_name(&nargs, sfe->nameval);
if (error)
goto out;
sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
@@ -631,7 +626,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
continue;
}
namesp = xfs_attr_flags_namesp(sfe->flags);
- error = context->put_listent(context,
+ error = xfs_attr_put_listent(context,
namesp,
(char *)sfe->nameval,
(int)sfe->namelen,
@@ -734,7 +729,7 @@ xfs_attr_shortform_list(xfs_attr_list_co
cursor->hashval = sbp->hash;
cursor->offset = 0;
}
- error = context->put_listent(context,
+ error = xfs_attr_put_listent(context,
namesp,
sbp->name,
sbp->namelen,
@@ -1960,6 +1955,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
xfs_attr_leaf_name_remote_t *name_rmt;
int probe, span;
xfs_dahash_t hashval;
+ xfs_dacmp_t cmp;
leaf = bp->data;
ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_ATTR_LEAF_MAGIC);
@@ -2008,6 +2004,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
/*
* Duplicate keys may be present, so search all of them for a match.
*/
+ args->cmpresult = XFS_CMP_DIFFERENT;
for ( ; (probe < be16_to_cpu(leaf->hdr.count)) &&
(be32_to_cpu(entry->hashval) == hashval);
entry++, probe++) {
@@ -2019,35 +2016,40 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp
* If we are looking for complete entries, show only those.
*/
if ((args->flags & XFS_ATTR_INCOMPLETE) !=
- (entry->flags & XFS_ATTR_INCOMPLETE)) {
- continue;
- }
- if (entry->flags & XFS_ATTR_LOCAL) {
- name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
- if (name_loc->namelen != args->namelen)
- continue;
- if (memcmp(args->name, (char *)name_loc->nameval, args->namelen) != 0)
+ (entry->flags & XFS_ATTR_INCOMPLETE))
continue;
if (!xfs_attr_namesp_match(args->flags, entry->flags))
continue;
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
+ cmp = xfs_attr_compname(args->dp, args->name, args->namelen,
+ name_loc->nameval, name_loc->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
args->index = probe;
- return(XFS_ERROR(EEXIST));
+ args->rmtblkno = 0;
+ args->rmtblkcnt = 0;
+ if (cmp == XFS_CMP_EXACT)
+ return XFS_ERROR(EEXIST);
+ }
} else {
name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe);
- if (name_rmt->namelen != args->namelen)
- continue;
- if (memcmp(args->name, (char *)name_rmt->name,
- args->namelen) != 0)
- continue;
- if (!xfs_attr_namesp_match(args->flags, entry->flags))
- continue;
+ cmp = xfs_attr_compname(args->dp, args->name, args->namelen,
+ name_rmt->name, name_rmt->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
args->index = probe;
args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount,
be32_to_cpu(name_rmt->valuelen));
- return(XFS_ERROR(EEXIST));
+ if (cmp == XFS_CMP_EXACT)
+ return XFS_ERROR(EEXIST);
+ }
}
}
+ if (args->cmpresult == XFS_CMP_CASE)
+ return XFS_ERROR(EEXIST);
+
args->index = probe;
return(XFS_ERROR(ENOATTR));
}
@@ -2074,8 +2076,8 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp,
entry = &leaf->entries[args->index];
if (entry->flags & XFS_ATTR_LOCAL) {
name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index);
- ASSERT(name_loc->namelen == args->namelen);
- ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0);
+ ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+ name_loc->nameval, name_loc->namelen) != XFS_CMP_DIFFERENT);
valuelen = be16_to_cpu(name_loc->valuelen);
if (args->flags & ATTR_KERNOVAL) {
args->valuelen = valuelen;
@@ -2089,8 +2091,8 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp,
memcpy(args->value, &name_loc->nameval[args->namelen], valuelen);
} else {
name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
- ASSERT(name_rmt->namelen == args->namelen);
- ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0);
+ ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+ name_rmt->nameval, name_rmt->namelen) != XFS_CMP_DIFFERENT);
valuelen = be32_to_cpu(name_rmt->valuelen);
args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, valuelen);
@@ -2418,7 +2420,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp,
xfs_attr_leaf_name_local_t *name_loc =
XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
- retval = context->put_listent(context,
+ retval = xfs_attr_put_listent(context,
namesp,
(char *)name_loc->nameval,
(int)name_loc->namelen,
@@ -2445,7 +2447,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp,
retval = xfs_attr_rmtval_get(&args);
if (retval)
return retval;
- retval = context->put_listent(context,
+ retval = xfs_attr_put_listent(context,
namesp,
(char *)name_rmt->name,
(int)name_rmt->namelen,
@@ -2454,7 +2456,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp,
kmem_free(args.value, valuelen);
}
else {
- retval = context->put_listent(context,
+ retval = xfs_attr_put_listent(context,
namesp,
(char *)name_rmt->name,
(int)name_rmt->namelen,
@@ -2472,6 +2474,31 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp,
return(retval);
}
+/*
+ * Do NLS name conversion if required for attribute name and call
+ * context's put_listent routine
+ */
+
+STATIC int
+xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp,
+ char *name, int namelen, int valuelen, char *value)
+{
+ xfs_mount_t *mp = context->dp->i_mount;
+ char *nls_name = NULL;
+ int rval;
+
+ if (!mp->m_nls)
+ return context->put_listent(context, namesp, name, namelen,
+ valuelen, value);
+
+ rval = xfs_unicode_to_nls(mp->m_nls, name, namelen, &nls_name);
+ if (rval < 0)
+ return -rval;
+ rval = context->put_listent(context, namesp, nls_name, rval,
+ valuelen, value);
+ xfs_free_unicode_nls_name(nls_name);
+ return rval;
+}
/*========================================================================
* Manage the INCOMPLETE flag in a leaf entry
@@ -2522,8 +2549,8 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *a
name = (char *)name_rmt->name;
}
ASSERT(be32_to_cpu(entry->hashval) == args->hashval);
- ASSERT(namelen == args->namelen);
- ASSERT(memcmp(name, args->name, namelen) == 0);
+ ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen,
+ name, namelen) != XFS_CMP_DIFFERENT);
#endif /* DEBUG */
entry->flags &= ~XFS_ATTR_INCOMPLETE;
@@ -2674,8 +2701,8 @@ xfs_attr_leaf_flipflags(xfs_da_args_t *a
name2 = (char *)name_rmt->name;
}
ASSERT(be32_to_cpu(entry1->hashval) == be32_to_cpu(entry2->hashval));
- ASSERT(namelen1 == namelen2);
- ASSERT(memcmp(name1, name2, namelen1) == 0);
+ ASSERT(xfs_attr_compname(args->dp, name1, namelen1, name2, namelen2) !=
+ XFS_CMP_DIFFERENT);
#endif /* DEBUG */
ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE);
===========================================================================
fs/xfs/xfs_attr_leaf.h
===========================================================================
--- a/fs/xfs/xfs_attr_leaf.h 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_attr_leaf.h 2008-01-11 14:16:44.268796245 +1100
@@ -36,6 +36,7 @@ struct xfs_da_args;
struct xfs_da_state;
struct xfs_da_state_blk;
struct xfs_inode;
+struct xfs_mount;
struct xfs_trans;
/*========================================================================
@@ -204,6 +205,16 @@ static inline int xfs_attr_leaf_entsize_
return (((bsize) >> 1) + ((bsize) >> 2));
}
+/*
+ * Do hash and name compare based on nameops
+ */
+#define xfs_attr_hashname(dp, n, l) \
+ ((dp)->i_mount->m_attrnameops->hashname((dp), (n), (l)))
+
+#define xfs_attr_compname(dp, n1, l1, n2, l2) \
+ ((dp)->i_mount->m_attrnameops->compname((dp), (n1), (l1), \
+ (n2), (l2)))
+
/*========================================================================
* Structure used to pass context around among the routines.
@@ -296,6 +307,7 @@ int xfs_attr_root_inactive(struct xfs_tr
/*
* Utility routines.
*/
+void xfs_attr_mount(struct xfs_mount *mp);
xfs_dahash_t xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count);
int xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp,
struct xfs_dabuf *leaf2_bp);
===========================================================================
fs/xfs/xfs_clnt.h
===========================================================================
--- a/fs/xfs/xfs_clnt.h 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_clnt.h 2007-11-01 13:16:55.000383139 +1100
@@ -48,6 +48,7 @@ struct xfs_mount_args {
char rtname[MAXNAMELEN+1]; /* realtime device filename */
char logname[MAXNAMELEN+1]; /* journal device filename */
char mtpt[MAXNAMELEN+1]; /* filesystem mount point */
+ char nls[MAXNAMELEN+1]; /* NLS code page to use */
int sunit; /* stripe unit (BBs) */
int swidth; /* stripe width (BBs), multiple of sunit */
uchar_t iosizelog; /* log2 of the preferred I/O size */
@@ -100,5 +101,9 @@ struct xfs_mount_args {
* I/O size in stat(2) */
#define XFSMNT2_FILESTREAMS 0x00000002 /* enable the filestreams
* allocator */
+#define XFSMNT2_CILOOKUP 0x00000004 /* enable case-insensitive
+ * filename lookup */
+#define XFSMNT2_CIATTR 0x00000008 /* enable case-insensitive
+ * extended attr names */
#endif /* __XFS_CLNT_H__ */
===========================================================================
fs/xfs/xfs_da_btree.c
===========================================================================
--- a/fs/xfs/xfs_da_btree.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_da_btree.c 2007-10-31 16:04:16.463309546 +1100
@@ -46,6 +46,7 @@
#include "xfs_dir2_block.h"
#include "xfs_dir2_node.h"
#include "xfs_error.h"
+#include "xfs_unicode.h"
/*
* xfs_da_btree.c
@@ -1530,6 +1531,100 @@ xfs_da_hashname(const uchar_t *name, int
}
}
+
+static xfs_dahash_t
+xfs_default_hashname(xfs_inode_t *inode, const uchar_t *name, int namelen)
+{
+ return xfs_da_hashname(name, namelen);
+}
+
+xfs_dacmp_t
+xfs_default_compname(xfs_inode_t *inode, const uchar_t *name1, int len1,
+ const uchar_t *name2, int len2)
+{
+ return (len1 == len2 && memcmp(name1, name2, len1) == 0) ?
+ XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
+}
+
+static xfs_dahash_t
+xfs_unicode_ci_hashname(
+ xfs_inode_t *inode,
+ const uchar_t *name,
+ int namelen)
+{
+ return xfs_unicode_hash(inode->i_mount->m_cft, name, namelen);
+}
+
+static xfs_dacmp_t
+xfs_unicode_ci_compname(
+ xfs_inode_t *inode,
+ const uchar_t *name1,
+ int len1,
+ const uchar_t *name2,
+ int len2)
+{
+ if (len1 == len2 && memcmp(name1, name2, len1) == 0)
+ return XFS_CMP_EXACT;
+
+ return xfs_unicode_casecmp(inode->i_mount->m_cft, name1, len1,
+ name2, len2) == 0 ? XFS_CMP_CASE : XFS_CMP_DIFFERENT;
+}
+
+const struct xfs_nameops xfs_default_nameops = {
+ .hashname = xfs_default_hashname,
+ .compname = xfs_default_compname
+};
+
+const struct xfs_nameops xfs_unicode_nameops = {
+ .hashname = xfs_unicode_ci_hashname,
+ .compname = xfs_default_compname,
+};
+
+const struct xfs_nameops xfs_unicode_ci_nameops = {
+ .hashname = xfs_unicode_ci_hashname,
+ .compname = xfs_unicode_ci_compname,
+};
+
+int
+xfs_da_setup_name_and_hash(
+ xfs_da_args_t *args,
+ const char *name,
+ int namelen)
+{
+ xfs_mount_t *mp = args->dp->i_mount;
+
+ if (mp->m_nls) {
+ args->name = NULL;
+ args->namelen = xfs_nls_to_unicode(mp->m_nls, name, namelen,
+ (char **)&args->name);
+ if (args->namelen < 0)
+ return -args->namelen;
+ } else {
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ int rval;
+ rval = xfs_unicode_validate(name, namelen);
+ if (rval < 0)
+ return -rval;
+ }
+ args->name = name;
+ args->namelen = namelen;
+ }
+ args->hashval = (args->whichfork == XFS_ATTR_FORK) ?
+ xfs_attr_hashname(args->dp, args->name, args->namelen) :
+ xfs_dir_hashname(args->dp, args->name, args->namelen);
+ return 0;
+}
+
+void
+xfs_da_cleanup_name(
+ xfs_da_args_t *args,
+ const char *name)
+{
+ if ((char *)args->name != name)
+ xfs_free_unicode_nls_name((char *)args->name);
+}
+
+
/*
* Add a block to the btree ahead of the file.
* Return the new block number to the caller.
===========================================================================
fs/xfs/xfs_da_btree.h
===========================================================================
--- a/fs/xfs/xfs_da_btree.h 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_da_btree.h 2008-01-14 12:12:32.917055949 +1100
@@ -99,6 +99,15 @@ typedef struct xfs_da_node_entry xfs_da_
*========================================================================*/
/*
+ * Search comparison results
+ */
+typedef enum {
+ XFS_CMP_DIFFERENT, /* names are completely different */
+ XFS_CMP_EXACT, /* names are exactly the same */
+ XFS_CMP_CASE /* names are same but differ in case */
+} xfs_dacmp_t;
+
+/*
* Structure to ease passing around component names.
*/
typedef struct xfs_da_args {
@@ -127,6 +136,7 @@ typedef struct xfs_da_args {
unsigned char rename; /* T/F: this is an atomic rename op */
unsigned char addname; /* T/F: this is an add operation */
unsigned char oknoent; /* T/F: ok to return ENOENT, else die */
+ xfs_dacmp_t cmpresult; /* name compare result for lookups */
} xfs_da_args_t;
/*
@@ -201,12 +211,37 @@ typedef struct xfs_da_state {
(uint)(XFS_DA_LOGOFF(BASE, ADDR)), \
(uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1)
+/*
+ * Name ops for directory and/or attr name operations
+ */
+
+typedef xfs_dahash_t (*xfs_hashname_t)(struct xfs_inode *, const uchar_t *,
+ int);
+typedef xfs_dacmp_t (*xfs_compname_t)(struct xfs_inode *, const uchar_t *,
+ int, const uchar_t *, int);
+
+typedef struct xfs_nameops {
+ xfs_hashname_t hashname;
+ xfs_compname_t compname;
+} xfs_nameops_t;
+
#ifdef __KERNEL__
/*========================================================================
* Function prototypes for the kernel.
*========================================================================*/
+extern const struct xfs_nameops xfs_default_nameops;
+extern const struct xfs_nameops xfs_unicode_nameops;
+extern const struct xfs_nameops xfs_unicode_ci_nameops;
+
+xfs_dacmp_t xfs_default_compname(struct xfs_inode *inode, const uchar_t *name1,
+ int len1, const uchar_t *name2, int len2);
+
+int xfs_da_setup_name_and_hash(xfs_da_args_t *args, const char *name,
+ int namelen);
+void xfs_da_cleanup_name(xfs_da_args_t *args, const char *name);
+
/*
* Routines used for growing the Btree.
*/
===========================================================================
fs/xfs/xfs_dir2.c
===========================================================================
--- a/fs/xfs/xfs_dir2.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2.c 2008-01-11 14:24:51.701973714 +1100
@@ -42,8 +42,56 @@
#include "xfs_dir2_node.h"
#include "xfs_dir2_trace.h"
#include "xfs_error.h"
+#include "xfs_unicode.h"
#include "xfs_vnodeops.h"
+/*
+ * V1 case-insensitive support for directories
+ */
+static xfs_dahash_t
+xfs_ascii_ci_hashname(
+ xfs_inode_t *inode,
+ const uchar_t *name,
+ int namelen)
+{
+ xfs_dahash_t hash;
+ int i;
+
+ for (i = 0, hash = 0; i < namelen; i++)
+ hash = tolower(name[i]) ^ rol32(hash, 7);
+
+ return hash;
+}
+
+static xfs_dacmp_t
+xfs_ascii_ci_compname(
+ xfs_inode_t *inode,
+ const uchar_t *name1,
+ int len1,
+ const uchar_t *name2,
+ int len2)
+{
+ xfs_dacmp_t result = XFS_CMP_EXACT;
+ int i;
+
+ if (len1 != len2)
+ return XFS_CMP_DIFFERENT;
+
+ for (i = 0; i < len1; i++) {
+ if (name1[i] == name2[i])
+ continue;
+ if (tolower(name1[i]) != tolower(name2[i]))
+ return XFS_CMP_DIFFERENT;
+ result = XFS_CMP_CASE;
+ }
+
+ return result;
+}
+
+static const struct xfs_nameops xfs_ascii_ci_nameops = {
+ .hashname = xfs_ascii_ci_hashname,
+ .compname = xfs_ascii_ci_compname,
+};
void
xfs_dir_mount(
@@ -64,6 +112,13 @@ 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;
+
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CI_LOOKUP) ?
+ &xfs_unicode_ci_nameops : &xfs_unicode_nameops;
+ } else
+ mp->m_dirnameops = (xfs_sb_version_hasoldci(&mp->m_sb)) ?
+ &xfs_ascii_ci_nameops : &xfs_default_nameops;
}
/*
@@ -140,7 +195,7 @@ xfs_dir_init(
}
/*
- Enter a name in a directory.
+ * Enter a name in a directory.
*/
int
xfs_dir_createname(
@@ -162,9 +217,6 @@ xfs_dir_createname(
return rval;
XFS_STATS_INC(xs_dir_create);
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -174,32 +226,41 @@ xfs_dir_createname(
args.trans = tp;
args.justcheck = 0;
args.addname = args.oknoent = 1;
+ rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (rval)
+ return rval;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_addname(&args);
else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_block_addname(&args);
else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_leaf_addname(&args);
else
rval = xfs_dir2_node_addname(&args);
+out:
+ xfs_da_cleanup_name(&args, name);
return rval;
}
/*
- * Lookup a name in a directory, give back the inode number.
+ * Lookup a name in a directory, give back the inode number and also
+ * actual name if case-insensitive match.
*/
+
int
xfs_dir_lookup(
xfs_trans_t *tp,
xfs_inode_t *dp,
char *name,
int namelen,
- xfs_ino_t *inum) /* out: inode number */
+ xfs_ino_t *inum, /* out: inode number */
+ char **actual_name, /* out: actual name if different */
+ int *actual_namelen)
{
xfs_da_args_t args;
int rval;
@@ -208,9 +269,9 @@ xfs_dir_lookup(
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
XFS_STATS_INC(xs_dir_lookup);
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
+ if (actual_name)
+ *actual_name = NULL;
+
args.inumber = 0;
args.dp = dp;
args.firstblock = NULL;
@@ -220,23 +281,39 @@ xfs_dir_lookup(
args.trans = tp;
args.justcheck = args.addname = 0;
args.oknoent = 1;
+ args.value = NULL; /* value may contain actual name on return */
+ args.valuelen = 0;
+ rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (rval)
+ return rval;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_lookup(&args);
else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_block_lookup(&args);
else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_leaf_lookup(&args);
else
rval = xfs_dir2_node_lookup(&args);
if (rval == EEXIST)
rval = 0;
- if (rval == 0)
+ if (rval == 0) {
*inum = args.inumber;
+ if (args.value) {
+ ASSERT(args->cmpresult == XFS_CMP_CASE);
+ if (actual_name) {
+ *actual_namelen = args.valuelen,
+ *actual_name = args.value;
+ } else
+ xfs_free_unicode_nls_name(args.value);
+ }
+ }
+out:
+ xfs_da_cleanup_name(&args, name);
return rval;
}
@@ -261,9 +338,6 @@ xfs_dir_removename(
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
XFS_STATS_INC(xs_dir_remove);
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
args.inumber = ino;
args.dp = dp;
args.firstblock = first;
@@ -272,19 +346,24 @@ xfs_dir_removename(
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
args.justcheck = args.addname = args.oknoent = 0;
+ rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (rval)
+ return rval;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_removename(&args);
else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_block_removename(&args);
else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_leaf_removename(&args);
else
rval = xfs_dir2_node_removename(&args);
+out:
+ xfs_da_cleanup_name(&args, name);
return rval;
}
@@ -345,9 +424,6 @@ xfs_dir_replace(
if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
return rval;
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -356,19 +432,24 @@ xfs_dir_replace(
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
args.justcheck = args.addname = args.oknoent = 0;
+ rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (rval)
+ return rval;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_replace(&args);
else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_block_replace(&args);
else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_leaf_replace(&args);
else
rval = xfs_dir2_node_replace(&args);
+out:
+ xfs_da_cleanup_name(&args, name);
return rval;
}
@@ -388,9 +469,6 @@ xfs_dir_canenter(
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
args.inumber = 0;
args.dp = dp;
args.firstblock = NULL;
@@ -399,19 +477,24 @@ xfs_dir_canenter(
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
args.justcheck = args.addname = args.oknoent = 1;
+ rval = xfs_da_setup_name_and_hash(&args, name, namelen);
+ if (rval)
+ return rval;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_addname(&args);
else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_block_addname(&args);
else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
+ goto out;
else if (v)
rval = xfs_dir2_leaf_addname(&args);
else
rval = xfs_dir2_node_addname(&args);
+out:
+ xfs_da_cleanup_name(&args, name);
return rval;
}
===========================================================================
fs/xfs/xfs_dir2.h
===========================================================================
--- a/fs/xfs/xfs_dir2.h 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2.h 2007-11-01 13:11:00.206583735 +1100
@@ -72,7 +72,8 @@ extern int xfs_dir_createname(struct xfs
xfs_fsblock_t *first,
struct xfs_bmap_free *flist, xfs_extlen_t tot);
extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
- char *name, int namelen, xfs_ino_t *inum);
+ char *name, int namelen, xfs_ino_t *inum,
+ char **actual_name, int *actual_namelen);
extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
char *name, int namelen, xfs_ino_t ino,
xfs_fsblock_t *first,
@@ -85,6 +86,13 @@ extern int xfs_dir_canenter(struct xfs_t
char *name, int namelen);
extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
+#define xfs_dir_hashname(dp, n, l) \
+ ((dp)->i_mount->m_dirnameops->hashname((dp), (n), (l)))
+
+#define xfs_dir_compname(dp, n1, l1, n2, l2) \
+ ((dp)->i_mount->m_dirnameops->compname((dp), (n1), (l1), \
+ (n2), (l2)))
+
/*
* Utility routines for v2 directories.
*/
===========================================================================
fs/xfs/xfs_dir2_block.c
===========================================================================
--- a/fs/xfs/xfs_dir2_block.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2_block.c 2008-01-11 14:28:44.763934272 +1100
@@ -38,6 +38,7 @@
#include "xfs_dir2_block.h"
#include "xfs_dir2_trace.h"
#include "xfs_error.h"
+#include "xfs_unicode.h"
/*
* Local function prototypes.
@@ -450,6 +451,8 @@ xfs_dir2_block_getdents(
int wantoff; /* starting block offset */
xfs_ino_t ino;
xfs_off_t cook;
+ char *nls_name = NULL; /* NLS name buffer */
+ int nls_namelen = 0;
mp = dp->i_mount;
/*
@@ -481,6 +484,9 @@ xfs_dir2_block_getdents(
ptr = (char *)block->u;
endptr = (char *)xfs_dir2_block_leaf_p(btp);
+ if (mp->m_nls)
+ nls_name = xfs_alloc_unicode_nls_name();
+
/*
* Loop over the data portion of the block.
* Each object is a real entry (dep) or an unused one (dup).
@@ -513,15 +519,19 @@ xfs_dir2_block_getdents(
#if XFS_BIG_INUMS
ino += mp->m_inoadd;
#endif
+ if (mp->m_nls)
+ nls_namelen = xfs_unicode_to_nls(mp->m_nls, dep->name,
+ dep->namelen, &nls_name);
/*
* If it didn't fit, set the final offset to here & return.
*/
- if (filldir(dirent, dep->name, dep->namelen, cook,
- ino, DT_UNKNOWN)) {
+ if (filldir(dirent,
+ nls_namelen > 0 ? nls_name : (char *)dep->name,
+ nls_namelen > 0 ? nls_namelen : dep->namelen,
+ cook, ino, DT_UNKNOWN)) {
*offset = cook;
- xfs_da_brelse(NULL, bp);
- return 0;
+ goto out;
}
}
@@ -530,6 +540,9 @@ xfs_dir2_block_getdents(
* Set the offset to a non-existent block 1 and return.
*/
*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
+out:
+ if (mp->m_nls)
+ xfs_free_unicode_nls_name(nls_name);
xfs_da_brelse(NULL, bp);
return 0;
}
@@ -616,6 +629,14 @@ xfs_dir2_block_lookup(
* Fill in inode number, release the block.
*/
args->inumber = be64_to_cpu(dep->inumber);
+ if (args->cmpresult == XFS_CMP_CASE) {
+ args->valuelen = xfs_unicode_to_nls(mp->m_nls, dep->name,
+ dep->namelen, (char **)&args->value);
+ if (args->valuelen < 0) {
+ xfs_da_brelse(args->trans, bp);
+ return XFS_ERROR(-args->valuelen);
+ }
+ }
xfs_da_brelse(args->trans, bp);
return XFS_ERROR(EEXIST);
}
@@ -643,6 +664,7 @@ xfs_dir2_block_lookup_int(
int mid; /* binary search current idx */
xfs_mount_t *mp; /* filesystem mount point */
xfs_trans_t *tp; /* transaction pointer */
+ xfs_dacmp_t cmp; /* comparison result */
dp = args->dp;
tp = args->trans;
@@ -688,6 +710,7 @@ xfs_dir2_block_lookup_int(
* Now loop forward through all the entries with the
* right hash value looking for our name.
*/
+ args->cmpresult = XFS_CMP_DIFFERENT;
do {
if ((addr = be32_to_cpu(blp[mid].address)) == XFS_DIR2_NULL_DATAPTR)
continue;
@@ -697,20 +720,34 @@ xfs_dir2_block_lookup_int(
dep = (xfs_dir2_data_entry_t *)
((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
/*
- * Compare, if it's right give back buffer & entry number.
- */
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
+ * Compare, if it's right give back buffer & entry number:
+ *
+ * lookup case - use nameops;
+ *
+ * replace/remove case - as lookup has been already been
+ * performed, look for an exact match using the fast method
+ */
+ cmp = args->oknoent ?
+ xfs_dir_compname(dp, dep->name, dep->namelen,
+ args->name, args->namelen) :
+ xfs_default_compname(dp, dep->name, dep->namelen,
+ args->name, args->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
*bpp = bp;
*entno = mid;
+ if (cmp == XFS_CMP_EXACT)
return 0;
}
- } while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash);
+ } while (++mid < be32_to_cpu(btp->count) &&
+ be32_to_cpu(blp[mid].hashval) == hash);
+
+ ASSERT(args->oknoent);
+ if (args->cmpresult == XFS_CMP_CASE)
+ return 0;
/*
* No match, release the buffer and return ENOENT.
*/
- ASSERT(args->oknoent);
xfs_da_brelse(tp, bp);
return XFS_ERROR(ENOENT);
}
@@ -1187,8 +1224,8 @@ xfs_dir2_sf_to_block(
tagp = xfs_dir2_data_entry_tag_p(dep);
*tagp = cpu_to_be16((char *)dep - (char *)block);
xfs_dir2_data_log_entry(tp, bp, dep);
- blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname(
- (char *)sfep->name, sfep->namelen));
+ blp[2 + i].hashval = cpu_to_be32(xfs_dir_hashname(dp,
+ sfep->name, sfep->namelen));
blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
(char *)dep - (char *)block));
offset = (int)((char *)(tagp + 1) - (char *)block);
===========================================================================
fs/xfs/xfs_dir2_data.c
===========================================================================
--- a/fs/xfs/xfs_dir2_data.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2_data.c 2007-10-10 15:10:39.019079916 +1000
@@ -140,7 +140,8 @@ xfs_dir2_data_check(
addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
(xfs_dir2_data_aoff_t)
((char *)dep - (char *)d));
- hash = xfs_da_hashname((char *)dep->name, dep->namelen);
+ hash = xfs_dir_hashname(dp, (char *)dep->name,
+ dep->namelen);
for (i = 0; i < be32_to_cpu(btp->count); i++) {
if (be32_to_cpu(lep[i].address) == addr &&
be32_to_cpu(lep[i].hashval) == hash)
===========================================================================
fs/xfs/xfs_dir2_leaf.c
===========================================================================
--- a/fs/xfs/xfs_dir2_leaf.c 2008-01-18 15:31:24.000000000 +1100
+++ b/fs/xfs/xfs_dir2_leaf.c 2008-01-17 17:06:07.795121260 +1100
@@ -40,6 +40,7 @@
#include "xfs_dir2_node.h"
#include "xfs_dir2_trace.h"
#include "xfs_error.h"
+#include "xfs_unicode.h"
/*
* Local function declarations.
@@ -780,6 +781,8 @@ xfs_dir2_leaf_getdents(
int ra_offset; /* map entry offset for ra */
int ra_want; /* readahead count wanted */
xfs_ino_t ino;
+ char *nls_name = NULL; /* NLS name buffer */
+ int nls_namelen = 0;
/*
* If the offset is at or past the largest allowed value,
@@ -800,6 +803,9 @@ xfs_dir2_leaf_getdents(
map_valid = ra_index = ra_offset = ra_current = map_blocks = 0;
bp = NULL;
+ if (mp->m_nls)
+ nls_name = xfs_alloc_unicode_nls_name();
+
/*
* Inside the loop we keep the main offset value as a byte offset
* in the directory file.
@@ -1086,11 +1092,16 @@ xfs_dir2_leaf_getdents(
#if XFS_BIG_INUMS
ino += mp->m_inoadd;
#endif
+ if (mp->m_nls)
+ nls_namelen = xfs_unicode_to_nls(mp->m_nls, dep->name,
+ dep->namelen, &nls_name);
/*
* Won't fit. Return to caller.
*/
- if (filldir(dirent, dep->name, dep->namelen,
+ if (filldir(dirent,
+ nls_namelen > 0 ? nls_name : (char *)dep->name,
+ nls_namelen > 0 ? nls_namelen : dep->namelen,
xfs_dir2_byte_to_dataptr(mp, curoff),
ino, DT_UNKNOWN))
break;
@@ -1113,6 +1124,8 @@ xfs_dir2_leaf_getdents(
kmem_free(map, map_size * sizeof(*map));
if (bp)
xfs_da_brelse(NULL, bp);
+ if (mp->m_nls)
+ xfs_free_unicode_nls_name(nls_name);
return error;
}
@@ -1300,10 +1313,17 @@ xfs_dir2_leaf_lookup(
/*
* Return the found inode number.
*/
+ error = EEXIST;
args->inumber = be64_to_cpu(dep->inumber);
+ if (args->cmpresult == XFS_CMP_CASE) {
+ args->valuelen = xfs_unicode_to_nls(args->dp->i_mount->m_nls,
+ dep->name, dep->namelen, (char **)&args->value);
+ if (args->valuelen < 0)
+ error = -args->valuelen;
+ }
xfs_da_brelse(tp, dbp);
xfs_da_brelse(tp, lbp);
- return XFS_ERROR(EEXIST);
+ return XFS_ERROR(error);
}
/*
@@ -1331,6 +1351,7 @@ xfs_dir2_leaf_lookup_int(
xfs_mount_t *mp; /* filesystem mount point */
xfs_dir2_db_t newdb; /* new data block number */
xfs_trans_t *tp; /* transaction pointer */
+ xfs_dacmp_t cmp; /* name compare result */
dp = args->dp;
tp = args->trans;
@@ -1354,6 +1375,7 @@ xfs_dir2_leaf_lookup_int(
* Loop over all the entries with the right hash value
* looking to match the name.
*/
+ args->cmpresult = XFS_CMP_DIFFERENT;
for (lep = &leaf->ents[index], dbp = NULL, curdb = -1;
index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
lep++, index++) {
@@ -1391,19 +1413,31 @@ xfs_dir2_leaf_lookup_int(
xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
/*
* If it matches then return it.
- */
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
+ *
+ * lookup case - use nameops;
+ *
+ * replace/remove case - as lookup has been already been
+ * performed, look for an exact match using the fast method
+ */
+ cmp = args->oknoent ?
+ xfs_dir_compname(dp, dep->name, dep->namelen,
+ args->name, args->namelen) :
+ xfs_default_compname(dp, dep->name, dep->namelen,
+ args->name, args->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
*dbpp = dbp;
*indexp = index;
+ if (cmp == XFS_CMP_EXACT)
return 0;
}
}
+ ASSERT(args->oknoent);
+ if (args->cmpresult == XFS_CMP_CASE)
+ return 0;
/*
* No match found, return ENOENT.
*/
- ASSERT(args->oknoent);
if (dbp)
xfs_da_brelse(tp, dbp);
xfs_da_brelse(tp, lbp);
===========================================================================
fs/xfs/xfs_dir2_node.c
===========================================================================
--- a/fs/xfs/xfs_dir2_node.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_dir2_node.c 2007-10-31 12:32:04.060201390 +1100
@@ -39,6 +39,7 @@
#include "xfs_dir2_node.h"
#include "xfs_dir2_trace.h"
#include "xfs_error.h"
+#include "xfs_unicode.h"
/*
* Function declarations.
@@ -414,6 +415,7 @@ xfs_dir2_leafn_lookup_int(
xfs_dir2_db_t newdb; /* new data block number */
xfs_dir2_db_t newfdb; /* new free block number */
xfs_trans_t *tp; /* transaction pointer */
+ xfs_dacmp_t cmp; /* comparison result */
dp = args->dp;
tp = args->trans;
@@ -455,6 +457,7 @@ xfs_dir2_leafn_lookup_int(
/*
* Loop over leaf entries with the right hash value.
*/
+ args->cmpresult = XFS_CMP_DIFFERENT;
for (lep = &leaf->ents[index];
index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
lep++, index++) {
@@ -572,28 +575,34 @@ xfs_dir2_leafn_lookup_int(
/*
* Point to the data entry.
*/
- dep = (xfs_dir2_data_entry_t *)
- ((char *)curbp->data +
+ dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
/*
* Compare the entry, return it if it matches.
*/
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
+ cmp = args->oknoent ?
+ xfs_dir_compname(dp, dep->name, dep->namelen,
+ args->name, args->namelen):
+ xfs_default_compname(dp, dep->name, dep->namelen,
+ args->name, args->namelen);
+ if (cmp != XFS_CMP_DIFFERENT &&
+ cmp != args->cmpresult) {
+ args->cmpresult = cmp;
args->inumber = be64_to_cpu(dep->inumber);
*indexp = index;
state->extravalid = 1;
state->extrablk.bp = curbp;
state->extrablk.blkno = curdb;
- state->extrablk.index =
- (int)((char *)dep -
+ state->extrablk.index = (int)((char *)dep -
(char *)curbp->data);
state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+ if (cmp == XFS_CMP_EXACT)
return XFS_ERROR(EEXIST);
}
}
}
+ if (args->cmpresult == XFS_CMP_CASE)
+ return XFS_ERROR(EEXIST);
/*
* Didn't find a match.
* If we are holding a buffer, give it back in case our caller
@@ -1768,6 +1777,19 @@ xfs_dir2_node_lookup(
if (error)
rval = error;
/*
+ * If case-insens match, copy name out
+ */
+ if (args->cmpresult == XFS_CMP_CASE) {
+ xfs_dir2_data_entry_t *dep =
+ (xfs_dir2_data_entry_t *)
+ ((char *)state->extrablk.bp->data +
+ state->extrablk.index);
+ args->valuelen = xfs_unicode_to_nls(args->dp->i_mount->m_nls,
+ dep->name, dep->namelen, (char **)&args->value);
+ if (args->valuelen < 0)
+ rval = -args->valuelen;
+ }
+ /*
* Release the btree blocks and leaf block.
*/
for (i = 0; i < state->path.active; i++) {
===========================================================================
fs/xfs/xfs_dir2_sf.c
===========================================================================
--- a/fs/xfs/xfs_dir2_sf.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_dir2_sf.c 2008-01-17 12:25:01.552398622 +1100
@@ -38,6 +38,7 @@
#include "xfs_dir2_leaf.h"
#include "xfs_dir2_block.h"
#include "xfs_dir2_trace.h"
+#include "xfs_unicode.h"
/*
* Prototypes for internal functions.
@@ -708,6 +709,8 @@ xfs_dir2_sf_getdents(
xfs_dir2_dataptr_t dot_offset;
xfs_dir2_dataptr_t dotdot_offset;
xfs_ino_t ino;
+ char *nls_name = NULL; /* NLS name buffer */
+ int nls_namelen = 0;
mp = dp->i_mount;
@@ -772,6 +775,9 @@ xfs_dir2_sf_getdents(
}
}
+ if (mp->m_nls)
+ nls_name = xfs_alloc_unicode_nls_name();
+
/*
* Loop while there are more entries and put'ing works.
*/
@@ -789,16 +795,22 @@ xfs_dir2_sf_getdents(
#if XFS_BIG_INUMS
ino += mp->m_inoadd;
#endif
-
- if (filldir(dirent, sfep->name, sfep->namelen,
+ if (mp->m_nls)
+ nls_namelen = xfs_unicode_to_nls(mp->m_nls, sfep->name,
+ sfep->namelen, &nls_name);
+ if (filldir(dirent,
+ nls_namelen > 0 ? nls_name : (char *)sfep->name,
+ nls_namelen > 0 ? nls_namelen : sfep->namelen,
off, ino, DT_UNKNOWN)) {
*offset = off;
- return 0;
+ goto out;
}
sfep = xfs_dir2_sf_nextentry(sfp, sfep);
}
-
*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
+out:
+ if (mp->m_nls)
+ xfs_free_unicode_nls_name(nls_name);
return 0;
}
@@ -836,6 +848,7 @@ xfs_dir2_sf_lookup(
*/
if (args->namelen == 1 && args->name[0] == '.') {
args->inumber = dp->i_ino;
+ args->cmpresult = XFS_CMP_EXACT;
return XFS_ERROR(EEXIST);
}
/*
@@ -844,23 +857,43 @@ xfs_dir2_sf_lookup(
if (args->namelen == 2 &&
args->name[0] == '.' && args->name[1] == '.') {
args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
+ args->cmpresult = XFS_CMP_EXACT;
return XFS_ERROR(EEXIST);
}
/*
* Loop over all the entries trying to match ours.
*/
- for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
- i < sfp->hdr.count;
+ args->cmpresult = XFS_CMP_DIFFERENT;
+ for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
- if (sfep->namelen == args->namelen &&
- sfep->name[0] == args->name[0] &&
- memcmp(args->name, sfep->name, args->namelen) == 0) {
- args->inumber =
- xfs_dir2_sf_get_inumber(sfp,
+ switch (xfs_dir_compname(dp, sfep->name, sfep->namelen,
+ args->name, args->namelen)) {
+ case XFS_CMP_EXACT:
+ args->cmpresult = XFS_CMP_EXACT;
+ args->inumber = xfs_dir2_sf_get_inumber(sfp,
xfs_dir2_sf_inumberp(sfep));
+ if (args->value) {
+ xfs_free_unicode_nls_name(args->value);
+ args->value = NULL;
+ }
return XFS_ERROR(EEXIST);
+
+ case XFS_CMP_CASE:
+ if (!args->value) {
+ args->valuelen = xfs_unicode_to_nls(
+ args->dp->i_mount->m_nls, sfep->name,
+ sfep->namelen, (char **)&args->value);
+ if (args->valuelen < 0)
+ return XFS_ERROR(-args->valuelen);
+ args->cmpresult = XFS_CMP_CASE;
+ args->inumber = xfs_dir2_sf_get_inumber(sfp,
+ xfs_dir2_sf_inumberp(sfep));
+ }
+ default: ;
}
}
+ if (args->cmpresult == XFS_CMP_CASE)
+ return XFS_ERROR(EEXIST);
/*
* Didn't find it.
*/
@@ -907,9 +940,8 @@ xfs_dir2_sf_removename(
for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
i < sfp->hdr.count;
i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
- if (sfep->namelen == args->namelen &&
- sfep->name[0] == args->name[0] &&
- memcmp(sfep->name, args->name, args->namelen) == 0) {
+ if (xfs_default_compname(dp, sfep->name, sfep->namelen,
+ args->name, args->namelen) == XFS_CMP_EXACT) {
ASSERT(xfs_dir2_sf_get_inumber(sfp,
xfs_dir2_sf_inumberp(sfep)) ==
args->inumber);
@@ -1044,9 +1076,9 @@ xfs_dir2_sf_replace(
for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
i < sfp->hdr.count;
i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
- if (sfep->namelen == args->namelen &&
- sfep->name[0] == args->name[0] &&
- memcmp(args->name, sfep->name, args->namelen) == 0) {
+ if (xfs_default_compname(dp, sfep->name,
+ sfep->namelen, args->name,
+ args->namelen) == XFS_CMP_EXACT) {
#if XFS_BIG_INUMS || defined(DEBUG)
ino = xfs_dir2_sf_get_inumber(sfp,
xfs_dir2_sf_inumberp(sfep));
===========================================================================
fs/xfs/xfs_itable.c
===========================================================================
--- a/fs/xfs/xfs_itable.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_itable.c 2008-01-18 13:45:10.740061749 +1100
@@ -45,6 +45,8 @@ xfs_internal_inum(
xfs_ino_t ino)
{
return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||
+ (xfs_sb_version_hasunicode(&mp->m_sb) &&
+ ino == mp->m_sb.sb_cftino) ||
(XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&
(ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino)));
}
===========================================================================
fs/xfs/xfs_mount.c
===========================================================================
--- a/fs/xfs/xfs_mount.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_mount.c 2008-01-17 17:10:29.777728874 +1100
@@ -25,6 +25,7 @@
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir2.h"
+#include "xfs_attr.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
@@ -43,6 +44,9 @@
#include "xfs_rw.h"
#include "xfs_quota.h"
#include "xfs_fsops.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_unicode.h"
STATIC void xfs_mount_log_sbunit(xfs_mount_t *, __int64_t);
STATIC int xfs_uuid_mount(xfs_mount_t *);
@@ -119,6 +123,8 @@ static const struct {
{ offsetof(xfs_sb_t, sb_logsectsize),0 },
{ offsetof(xfs_sb_t, sb_logsunit), 0 },
{ offsetof(xfs_sb_t, sb_features2), 0 },
+ { offsetof(xfs_sb_t, sb_bad_features2), 0 },
+ { offsetof(xfs_sb_t, sb_cftino), 0 },
{ sizeof(xfs_sb_t), 0 }
};
@@ -171,6 +177,9 @@ xfs_mount_free(
sizeof(xfs_perag_t) * mp->m_sb.sb_agcount);
}
+ if (mp->m_cft)
+ xfs_unicode_free_cft(mp->m_cft);
+
spinlock_destroy(&mp->m_ail_lock);
spinlock_destroy(&mp->m_sb_lock);
mutex_destroy(&mp->m_ilock);
@@ -455,6 +464,8 @@ xfs_sb_from_disk(
to->sb_logsectsize = be16_to_cpu(from->sb_logsectsize);
to->sb_logsunit = be32_to_cpu(from->sb_logsunit);
to->sb_features2 = be32_to_cpu(from->sb_features2);
+ to->sb_bad_features2 = be32_to_cpu(from->sb_bad_features2);
+ to->sb_cftino = be64_to_cpu(from->sb_cftino);
}
/*
@@ -1062,7 +1073,7 @@ xfs_mountfs(
/*
* Initialize the attribute manager's entries.
*/
- mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+ xfs_attr_mount(mp);
/*
* Initialize the precomputed transaction reservations values.
@@ -1165,6 +1176,17 @@ xfs_mountfs(
}
/*
+ * Load in unicode case folding table from disk
+ */
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ error = xfs_unicode_read_cft(mp);
+ if (error) {
+ cmn_err(CE_WARN, "XFS: failed to read case folding table");
+ goto error4;
+ }
+ }
+
+ /*
* If fs is not mounted readonly, then update the superblock
* unit and width changes.
*/
@@ -1220,6 +1242,8 @@ xfs_mountfs(
* Free up the root inode.
*/
VN_RELE(rvp);
+ if (mp->m_cft)
+ xfs_unicode_free_cft(mp->m_cft);
error3:
xfs_log_unmount_dealloc(mp);
error2:
===========================================================================
fs/xfs/xfs_mount.h
===========================================================================
--- a/fs/xfs/xfs_mount.h 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_mount.h 2007-11-01 13:13:36.858098082 +1100
@@ -54,6 +54,7 @@ typedef struct xfs_trans_reservations {
#else
struct cred;
struct log;
+struct nls_table;
struct xfs_mount_args;
struct xfs_inode;
struct xfs_bmbt_irec;
@@ -61,6 +62,8 @@ struct xfs_bmap_free;
struct xfs_extdelta;
struct xfs_swapext;
struct xfs_mru_cache;
+struct xfs_nameops;
+struct xfs_cft;
/*
* Prototypes and functions for the Data Migration subsystem.
@@ -306,6 +309,10 @@ typedef struct xfs_mount {
__uint8_t m_inode_quiesce;/* call quiesce on new inodes.
field governed by m_ilock */
__uint8_t m_sectbb_log; /* sectlog - BBSHIFT */
+ const struct xfs_nameops *m_dirnameops; /* vector of dir name ops */
+ const struct xfs_nameops *m_attrnameops; /* vector of attr name ops */
+ const struct xfs_cft *m_cft; /* unicode case fold table */
+ struct nls_table *m_nls; /* active NLS table */
int m_dirblksize; /* directory block sz--bytes */
int m_dirblkfsbs; /* directory block sz--fsbs */
xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */
@@ -371,7 +378,10 @@ typedef struct xfs_mount {
counters */
#define XFS_MOUNT_FILESTREAMS (1ULL << 24) /* enable the filestreams
allocator */
-
+#define XFS_MOUNT_CI_LOOKUP (1ULL << 25) /* enable case-insensitive
+ * file lookup */
+#define XFS_MOUNT_CI_ATTR (1ULL << 26) /* enable case-insensitive
+ attribute names */
/*
* Default minimum read and write sizes.
===========================================================================
fs/xfs/xfs_rename.c
===========================================================================
--- a/fs/xfs/xfs_rename.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_rename.c 2008-01-17 12:25:31.652529581 +1100
@@ -130,7 +130,9 @@ xfs_lock_for_rename(
lock_mode = xfs_ilock_map_shared(dp2);
}
- error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2);
+ error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2,
+ NULL, NULL);
+
if (error == ENOENT) { /* target does not need to exist. */
inum2 = 0;
} else if (error) {
@@ -214,6 +216,7 @@ xfs_lock_for_rename(
for (;i < 4; i++) {
i_tab[i] = NULL;
}
+
return 0;
}
===========================================================================
fs/xfs/xfs_sb.h
===========================================================================
--- a/fs/xfs/xfs_sb.h 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_sb.h 2007-10-23 16:55:47.440178601 +1000
@@ -46,10 +46,12 @@ struct xfs_mount;
#define XFS_SB_VERSION_SECTORBIT 0x0800
#define XFS_SB_VERSION_EXTFLGBIT 0x1000
#define XFS_SB_VERSION_DIRV2BIT 0x2000
+#define XFS_SB_VERSION_OLDCIBIT 0x4000
#define XFS_SB_VERSION_MOREBITSBIT 0x8000
#define XFS_SB_VERSION_OKSASHFBITS \
(XFS_SB_VERSION_EXTFLGBIT | \
- XFS_SB_VERSION_DIRV2BIT)
+ XFS_SB_VERSION_DIRV2BIT | \
+ XFS_SB_VERSION_OLDCIBIT)
#define XFS_SB_VERSION_OKREALFBITS \
(XFS_SB_VERSION_ATTRBIT | \
XFS_SB_VERSION_NLINKBIT | \
@@ -77,10 +79,12 @@ struct xfs_mount;
#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
#define XFS_SB_VERSION2_RESERVED4BIT 0x00000004
#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
+#define XFS_SB_VERSION2_UNICODEBIT 0x00000020 /* Unicode names */
#define XFS_SB_VERSION2_OKREALFBITS \
(XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
- XFS_SB_VERSION2_ATTR2BIT)
+ XFS_SB_VERSION2_ATTR2BIT | \
+ XFS_SB_VERSION2_UNICODEBIT)
#define XFS_SB_VERSION2_OKSASHFBITS \
(0)
#define XFS_SB_VERSION2_OKREALBITS \
@@ -145,6 +149,9 @@ typedef struct xfs_sb {
__uint16_t sb_logsectsize; /* sector size for the log, bytes */
__uint32_t sb_logsunit; /* stripe unit size for the log */
__uint32_t sb_features2; /* additional feature bits */
+ __uint32_t sb_bad_features2; /* features2 could be here */
+ xfs_ino_t sb_cftino; /* unicode case folding table inode */
+ /* must be padded to 64 bit alignment */
} xfs_sb_t;
/*
@@ -205,6 +212,9 @@ typedef struct xfs_dsb {
__be16 sb_logsectsize; /* sector size for the log, bytes */
__be32 sb_logsunit; /* stripe unit size for the log */
__be32 sb_features2; /* additional feature bits */
+ __be32 sb_bad_features2; /* features2 could be here */
+ __be64 sb_cftino; /* unicode case folding table inode */
+ /* must be padded to 64 bit alignment */
} xfs_dsb_t;
/*
@@ -223,7 +233,7 @@ typedef enum {
XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN,
XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG,
XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT,
- XFS_SBS_FEATURES2,
+ XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2, XFS_SBS_CFTINO,
XFS_SBS_FIELDCOUNT
} xfs_sb_field_t;
@@ -248,13 +258,15 @@ typedef enum {
#define XFS_SB_IFREE XFS_SB_MVAL(IFREE)
#define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS)
#define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2)
+#define XFS_SB_CASEFOLDINO XFS_SB_MVAL(CASEFOLDINO)
#define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT)
#define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1)
#define XFS_SB_MOD_BITS \
(XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
- XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2)
+ XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \
+ XFS_SB_CFTINO)
/*
@@ -463,6 +475,12 @@ static inline int xfs_sb_version_hassect
((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
}
+static inline int xfs_sb_version_hasoldci(xfs_sb_t *sbp)
+{
+ return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_OLDCIBIT);
+}
+
#define XFS_SB_VERSION_HASMOREBITS(sbp) xfs_sb_version_hasmorebits(sbp)
static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
{
@@ -502,6 +520,13 @@ static inline void xfs_sb_version_addatt
((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)));
}
+static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp)
+{
+ return (xfs_sb_version_hasmorebits(sbp) && \
+ ((sbp)->sb_features2 & XFS_SB_VERSION2_UNICODEBIT));
+}
+
+
/*
* end of superblock version macros
*/
===========================================================================
fs/xfs/xfs_types.h
===========================================================================
--- a/fs/xfs/xfs_types.h 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_types.h 2007-10-25 17:49:01.982895520 +1000
@@ -160,4 +160,9 @@ typedef enum {
XFS_BTNUM_MAX
} xfs_btnum_t;
+typedef struct xfs_string {
+ const uchar_t *name;
+ int len;
+} xfs_string_t;
+
#endif /* __XFS_TYPES_H__ */
===========================================================================
fs/xfs/xfs_unicode.c
===========================================================================
--- a/fs/xfs/xfs_unicode.c 2006-06-17 00:58:24.000000000 +1000
+++ b/fs/xfs/xfs_unicode.c 2008-01-17 17:11:29.734083909 +1100
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_clnt.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_itable.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_bmap.h"
+#include "xfs_rw.h"
+#include "xfs_unicode.h"
+
+#define MAX_FOLD_CHARS 4
+
+static kmem_zone_t *xfs_nls_uni_zone;
+
+static inline int
+xfs_casefold(
+ const xfs_cft_t *cft,
+ __uint16_t c,
+ __uint16_t *fc)
+{
+ __uint16_t *table = XFS_CFT_PTR(cft, 0);
+ __uint16_t tmp = table[c >> 8];
+ int i;
+
+ if (!tmp) {
+ *fc = c;
+ return 1;
+ }
+ tmp = table[tmp + (c & 0xff)];
+ if ((tmp & 0xf000) != 0xe000) {
+ *fc = tmp;
+ return 1;
+ }
+ i = ((tmp >> 10) & 0x3) + 2;
+ ASSERT(i < cft->num_tables);
+ table = XFS_CFT_PTR(cft, i - 1) + ((tmp & 0x3ff) * i);
+
+ memcpy(fc, table, sizeof(__uint16_t) * i);
+
+ return i;
+}
+
+static inline int
+xfs_utf8_casefold(
+ const xfs_cft_t *cft,
+ const uchar_t **name,
+ int *namelen,
+ __uint16_t *fc)
+{
+ wchar_t uc;
+
+ if (*namelen == 0)
+ return 0;
+
+ if (**name & 0x80) {
+ int n = utf8_mbtowc(&uc, *name, *namelen);
+ if (n < 0) {
+ (*namelen)--;
+ *fc = *(*name)++;
+ return 1;
+ }
+ *name += n;
+ *namelen -= n;
+ } else {
+ uc = *(*name)++;
+ (*namelen)--;
+ }
+ return xfs_casefold(cft, uc, fc);
+}
+
+__uint32_t
+xfs_unicode_hash(
+ const xfs_cft_t *cft,
+ const uchar_t *name,
+ int namelen)
+{
+ __uint32_t hash = 0;
+ __uint16_t fc[MAX_FOLD_CHARS];
+ int nfc;
+ int i;
+
+ while (namelen > 0) {
+ nfc = xfs_utf8_casefold(cft, &name, &namelen, fc);
+ for (i = 0; i < nfc; i++)
+ hash = fc[i] ^ rol32(hash, 7);
+ }
+ return hash;
+}
+
+int
+xfs_unicode_casecmp(
+ const xfs_cft_t *cft,
+ const uchar_t *name1,
+ int len1,
+ const uchar_t *name2,
+ int len2)
+{
+ __uint16_t fc1[MAX_FOLD_CHARS], fc2[MAX_FOLD_CHARS];
+ __uint16_t *pfc1, *pfc2;
+ int nfc1, nfc2;
+
+ nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+ pfc1 = fc1;
+ nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+ pfc2 = fc2;
+
+ while (nfc1 > 0 && nfc2 > 0) {
+ if (*pfc1 != *pfc2)
+ return (*pfc1 < *pfc2) ? -1 : 1;
+ if (!--nfc1) {
+ nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1);
+ pfc1 = fc1;
+ } else
+ pfc1++;
+ if (!--nfc2) {
+ nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2);
+ pfc2 = fc2;
+ } else
+ pfc2++;
+ }
+ if (nfc1 != nfc2)
+ return (nfc1 < nfc2) ? -1 : 1;
+ return 0;
+}
+
+
+char *
+xfs_alloc_unicode_nls_name(void)
+{
+ return kmem_zone_alloc(xfs_nls_uni_zone, KM_SLEEP);
+}
+
+
+void
+xfs_free_unicode_nls_name(
+ char *name)
+{
+ kmem_zone_free(xfs_nls_uni_zone, name);
+}
+
+int
+xfs_nls_to_unicode(
+ struct nls_table *nls,
+ const char *nls_name,
+ int nls_namelen,
+ char **uni_name)
+{
+ char *n;
+ int i, o;
+ wchar_t uc;
+ int nlen;
+ int u8len;
+ int rval;
+
+ n = *uni_name ? *uni_name : xfs_alloc_unicode_nls_name();
+
+ if (!nls) {
+ if (nls_namelen > MAXNAMELEN) {
+ rval = -ENAMETOOLONG;
+ goto err_out;
+ }
+ memcpy(n, nls_name, nls_namelen);
+ *uni_name = n;
+ return nls_namelen;
+ }
+
+ for (i = 0, o = 0; i < nls_namelen; i += nlen, o += u8len) {
+ nlen = nls->char2uni(nls_name + i, nls_namelen - i, &uc);
+ if (nlen < 0) {
+ rval = nlen;
+ goto err_out;
+ }
+ if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xdfff)) {
+ rval = -EINVAL; /* don't support chars outside BMP */
+ goto err_out;
+ }
+ u8len = utf8_wctomb(n + o, uc, MAXNAMELEN - o);
+ if (u8len <= 0) {
+ rval = (MAXNAMELEN - o < 3) ? -ENAMETOOLONG : -EINVAL;
+ goto err_out;
+ }
+ }
+ *uni_name = n;
+ return o;
+err_out:
+ if (*uni_name == NULL)
+ xfs_free_unicode_nls_name(n);
+ return rval;
+}
+
+int
+xfs_unicode_to_nls(
+ struct nls_table *nls,
+ const char *uni_name,
+ int uni_namelen,
+ char **nls_name)
+{
+ char *n;
+ int i, o;
+ wchar_t uc;
+ int nlen;
+ int u8len;
+ int rval;
+
+ n = *nls_name ? *nls_name : xfs_alloc_unicode_nls_name();
+
+ if (!nls) {
+ if (uni_namelen > MAXNAMELEN) {
+ rval = -ENAMETOOLONG;
+ goto err_out;
+ }
+ memcpy(n, uni_name, uni_namelen);
+ *nls_name = n;
+ return uni_namelen;
+ }
+
+ for (i = 0, o = 0; i < uni_namelen && o < MAXNAMELEN;
+ i += u8len, o += nlen) {
+ u8len = utf8_mbtowc(&uc, uni_name + i, uni_namelen - i);
+ if (u8len < 0) {
+ rval = -EINVAL;
+ goto err_out;
+ }
+ nlen = nls->uni2char(uc, n + o, MAXNAMELEN - o);
+ if (nlen == -EINVAL) {
+ n[o] = '?';
+ nlen = 1;
+ } else if (nlen < 0) {
+ rval = nlen;
+ goto err_out;
+ }
+ }
+ if (i == uni_namelen) {
+ *nls_name = n;
+ return o;
+ }
+
+ rval = -ENAMETOOLONG;
+err_out:
+ if (*nls_name == NULL)
+ xfs_free_unicode_nls_name(n);
+ return rval;
+}
+
+int
+xfs_unicode_validate(
+ const uchar_t *name,
+ int namelen)
+{
+ wchar_t uc;
+ int i, nlen;
+
+ for (i = 0; i < namelen; i += nlen) {
+ if (*name >= 0xf0) {
+ cmn_err(CE_WARN, "xfs_unicode_validate: "
+ "UTF-8 char beyond U+FFFF\n");
+ return -EINVAL;
+ }
+ /* utf8_mbtowc must fail on overlong sequences too */
+ nlen = utf8_mbtowc(&uc, name + i, namelen - i);
+ if (nlen < 0) {
+ cmn_err(CE_WARN, "xfs_unicode_validate: "
+ "invalid UTF-8 sequence\n");
+ return -EILSEQ;
+ }
+ /* check for invalid/surrogate/private unicode chars */
+ if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xf8ff)) {
+ cmn_err(CE_WARN, "xfs_unicode_validate: "
+ "unsupported UTF-8 char\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Unicode Case Fold Table management
+ */
+
+struct cft_item {
+ xfs_cft_t *table;
+ int size;
+ int refcount;
+};
+
+static mutex_t cft_lock;
+static int cft_size;
+static struct cft_item *cft_list;
+
+static xfs_cft_t *
+add_cft(
+ xfs_dcft_t *dcft,
+ int size)
+{
+ int found = 0;
+ int i, j;
+ xfs_cft_t *cft;
+ __be16 *duc;
+ __uint16_t *uc;
+
+ mutex_lock(&cft_lock);
+
+ for (i = 0; i < cft_size; i++) {
+ if (cft_list[i].size != size)
+ continue;
+ cft = cft_list[i].table;
+ if (cft->num_tables != be32_to_cpu(dcft->num_tables) ||
+ cft->flags != be32_to_cpu(dcft->flags))
+ continue;
+ found = 1;
+ for (j = 0; j < cft->num_tables; j++) {
+ if (cft->table_offset[j] !=
+ be32_to_cpu(dcft->table_offset[j])) {
+ found = 0;
+ break;
+ }
+ }
+ if (found) {
+ cft_list[i].refcount++;
+ mutex_unlock(&cft_lock);
+ return cft;
+ }
+ }
+
+ cft = vmalloc(size);
+ if (!cft) {
+ mutex_unlock(&cft_lock);
+ return NULL;
+ }
+ cft->magic = be32_to_cpu(dcft->magic);
+ cft->flags = be32_to_cpu(dcft->flags);
+ cft->num_tables = be32_to_cpu(dcft->num_tables);
+ ASSERT(cft->num_tables <= MAX_FOLD_CHARS);
+ for (i = 0; i < cft->num_tables; i++)
+ cft->table_offset[i] = be32_to_cpu(dcft->table_offset[i]);
+ j = (size - cft->table_offset[0]) / sizeof(__uint16_t);
+ uc = XFS_CFT_PTR(cft, 0);
+ duc = XFS_DCFT_PTR(dcft, 0);
+ for (i = 0; i < j; i++)
+ uc[i] = be16_to_cpu(duc[i]);
+
+ cft_list = kmem_realloc(cft_list,
+ (cft_size + 1) * sizeof(struct cft_item),
+ cft_size * sizeof(struct cft_item), KM_SLEEP);
+ cft_list[cft_size].table = cft;
+ cft_list[cft_size].size = size;
+ cft_list[cft_size].refcount = 1;
+ cft_size++;
+
+ mutex_unlock(&cft_lock);
+
+ return cft;
+}
+
+static void
+remove_cft(
+ const xfs_cft_t *cft)
+{
+ int i;
+
+ mutex_lock(&cft_lock);
+
+ for (i = 0; i < cft_size; i++) {
+ if (cft_list[i].table == cft) {
+ ASSERT(cft_list[i].refcount > 0);
+ cft_list[i].refcount--;
+ break;
+ }
+ }
+
+ mutex_unlock(&cft_lock);
+}
+
+
+int
+xfs_unicode_read_cft(
+ xfs_mount_t *mp)
+{
+ int error;
+ xfs_inode_t *cftip;
+ int size;
+ int nfsb;
+ int nmap;
+ xfs_bmbt_irec_t *mapp;
+ int n;
+ int byte_cnt;
+ xfs_buf_t *bp;
+ char *table;
+ xfs_dcft_t *dcft;
+
+ if (mp->m_sb.sb_cftino == NULLFSINO || mp->m_sb.sb_cftino == 0)
+ return EINVAL;
+ error = xfs_iget(mp, NULL, mp->m_sb.sb_cftino, 0, 0, &cftip, 0);
+ if (error)
+ return error;
+ ASSERT(cftip != NULL);
+
+ size = cftip->i_d.di_size;
+ nfsb = cftip->i_d.di_nblocks;
+
+ table = vmalloc(size);
+ if (!table) {
+ xfs_iput(cftip, 0);
+ return ENOMEM;
+ }
+ dcft = (xfs_dcft_t *)table;
+
+ nmap = nfsb;
+ mapp = kmem_alloc(nfsb * sizeof(xfs_bmbt_irec_t), KM_SLEEP);
+
+ error = xfs_bmapi(NULL, cftip, 0, nfsb, 0, NULL, 0, mapp, &nmap,
+ NULL, NULL);
+ if (error)
+ goto out;
+
+ for (n = 0; n < nmap; n++) {
+ byte_cnt = XFS_FSB_TO_B(mp, mapp[n].br_blockcount);
+
+ error = xfs_read_buf(mp, mp->m_ddev_targp,
+ XFS_FSB_TO_DADDR(mp, mapp[n].br_startblock),
+ BTOBB(byte_cnt), 0, &bp);
+ if (error)
+ goto out;
+
+ if (size < byte_cnt)
+ byte_cnt = size;
+ size -= byte_cnt;
+ memcpy(table, XFS_BUF_PTR(bp), byte_cnt);
+ table += byte_cnt;
+ xfs_buf_relse(bp);
+ }
+
+ /* verify case table read off disk */
+ if (!uuid_equal(&dcft->uuid, &mp->m_sb.sb_uuid)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* clear UUID for in-memory copy/compare */
+ memset(&dcft->uuid, 0, sizeof(dcft->uuid));
+
+ mp->m_cft = add_cft(dcft, cftip->i_d.di_size);
+ if (mp->m_cft == NULL)
+ error = ENOMEM;
+
+out:
+ xfs_iput(cftip, 0);
+ kmem_free(mapp, nfsb * sizeof(xfs_bmbt_irec_t));
+ vfree(dcft);
+
+ return error;
+}
+
+void
+xfs_unicode_free_cft(
+ const xfs_cft_t *cft)
+{
+ remove_cft(cft);
+}
+
+void
+xfs_unicode_init(void)
+{
+ mutex_init(&cft_lock);
+ xfs_nls_uni_zone = kmem_zone_init(MAXNAMELEN, "xfs_nls_uni");
+}
+
+void
+xfs_unicode_uninit(void)
+{
+ int i;
+
+ mutex_lock(&cft_lock);
+
+ for (i = 0; i < cft_size; i++) {
+ ASSERT(cft_list[i].refcount == 0);
+ vfree(cft_list[i].table);
+ }
+ kmem_free(cft_list, cft_size * sizeof(struct cft_item));
+ cft_size = 0;
+ cft_list = NULL;
+
+ mutex_unlock(&cft_lock);
+
+ kmem_zone_destroy(xfs_nls_uni_zone);
+}
===========================================================================
fs/xfs/xfs_unicode.h
===========================================================================
--- a/fs/xfs/xfs_unicode.h 2006-06-17 00:58:24.000000000 +1000
+++ b/fs/xfs/xfs_unicode.h 2008-01-11 11:59:50.325468145 +1100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __XFS_UNICODE_H__
+#define __XFS_UNICODE_H__
+
+#define XFS_CFT_MAGIC 0x58434654 /* 'XCFT' */
+#define XFS_CFT_FLAG_TURKIC 0x00000001
+#define XFS_CFT_FLAG_MAX 0x00000001
+
+/*
+ * Case Fold Table - on disk version. Must match the incore version below.
+ */
+typedef struct xfs_dcft {
+ __be32 magic; /* validity check */
+ __be32 flags;
+ uuid_t uuid; /* UUID of the filesystem */
+ __be32 crc; /* for future support */
+ __be32 num_tables; /* single, double, etc */
+ __be32 table_offset[1];
+} xfs_dcft_t;
+
+/*
+ * Case Fold Table - in core version. Must match the ondisk version above.
+ */
+typedef struct xfs_cft {
+ __uint32_t magic;
+ __uint32_t flags;
+ uuid_t uuid; /* UUID of the filesystem */
+ __uint32_t crc;
+ __uint32_t num_tables; /* single, double, etc */
+ __uint32_t table_offset[1];/* num_tables sized */
+ /* 16-bit array tables immediately follow */
+} xfs_cft_t;
+
+#define XFS_CFT_PTR(t,n) (__uint16_t *)(((char *)(t)) + \
+ (t)->table_offset[n])
+#define XFS_DCFT_PTR(t,n) (__be16 *)(((char *)(t)) + \
+ be32_to_cpu((t)->table_offset[n]))
+
+void xfs_unicode_init(void);
+void xfs_unicode_uninit(void);
+
+__uint32_t xfs_unicode_hash(const xfs_cft_t *cft,
+ const uchar_t *name, int namelen);
+
+int xfs_unicode_casecmp(const xfs_cft_t *cft, const uchar_t *name1,
+ int len1, const uchar_t *name2, int len2);
+
+char *xfs_alloc_unicode_nls_name(void);
+void xfs_free_unicode_nls_name(char *name);
+int xfs_nls_to_unicode(struct nls_table *nls, const char *nls_name,
+ int nls_namelen, char **uni_name);
+int xfs_unicode_to_nls(struct nls_table *nls, const char *uni_name,
+ int uni_namelen, char **nls_name);
+
+int xfs_unicode_validate(const uchar_t *name, int namelen);
+
+int xfs_unicode_read_cft(struct xfs_mount *mp);
+void xfs_unicode_free_cft(const xfs_cft_t *cft);
+
+#endif /* __XFS_UNICODE_H__ */
===========================================================================
fs/xfs/xfs_utils.c
===========================================================================
--- a/fs/xfs/xfs_utils.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_utils.c 2007-10-26 15:42:04.856766756 +1000
@@ -39,6 +39,7 @@
#include "xfs_rw.h"
#include "xfs_itable.h"
#include "xfs_utils.h"
+#include "xfs_unicode.h"
/*
* xfs_get_dir_entry is used to get a reference to an inode given
@@ -69,13 +70,16 @@ xfs_dir_lookup_int(
uint lock_mode,
bhv_vname_t *dentry,
xfs_ino_t *inum,
- xfs_inode_t **ipp)
+ xfs_inode_t **ipp,
+ char **actual_name,
+ int *actual_namelen)
{
int error;
xfs_itrace_entry(dp);
- error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum);
+ error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum,
+ actual_name, actual_namelen);
if (!error) {
/*
* Unlock the directory. We do this because we can't
@@ -102,6 +106,8 @@ xfs_dir_lookup_int(
xfs_ilock(dp, lock_mode);
error = XFS_ERROR(ENOENT);
}
+ if (error && actual_name)
+ xfs_free_unicode_nls_name(*actual_name);
}
return error;
}
===========================================================================
fs/xfs/xfs_utils.h
===========================================================================
--- a/fs/xfs/xfs_utils.h 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_utils.h 2007-10-26 15:35:54.052564595 +1000
@@ -23,7 +23,7 @@
extern int xfs_get_dir_entry (bhv_vname_t *, xfs_inode_t **);
extern int xfs_dir_lookup_int (xfs_inode_t *, uint, bhv_vname_t *, xfs_ino_t *,
- xfs_inode_t **);
+ xfs_inode_t **, char **, int *);
extern int xfs_truncate_file (xfs_mount_t *, xfs_inode_t *);
extern int xfs_dir_ialloc (xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t,
xfs_dev_t, cred_t *, prid_t, int,
===========================================================================
fs/xfs/xfs_vfsops.c
===========================================================================
--- a/fs/xfs/xfs_vfsops.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_vfsops.c 2008-01-11 14:47:36.214448806 +1100
@@ -56,7 +56,7 @@
#include "xfs_fsops.h"
#include "xfs_vnodeops.h"
#include "xfs_vfsops.h"
-
+#include "xfs_unicode.h"
int
xfs_init(void)
@@ -81,6 +81,7 @@ xfs_init(void)
xfs_acl_zone_init(xfs_acl_zone, "xfs_acl");
xfs_mru_cache_init();
xfs_filestream_init();
+ xfs_unicode_init();
/*
* The size of the zone allocated buf log item is the maximum
@@ -158,6 +159,7 @@ xfs_cleanup(void)
xfs_cleanup_procfs();
xfs_sysctl_unregister();
xfs_refcache_destroy();
+ xfs_unicode_uninit();
xfs_filestream_uninit();
xfs_mru_cache_uninit();
xfs_acl_zone_destroy(xfs_acl_zone);
@@ -247,7 +249,6 @@ xfs_start_flags(
mp->m_logname = kmem_alloc(strlen(ap->logname) + 1, KM_SLEEP);
strcpy(mp->m_logname, ap->logname);
}
-
if (ap->flags & XFSMNT_WSYNC)
mp->m_flags |= XFS_MOUNT_WSYNC;
#if XFS_BIG_INUMS
@@ -404,6 +405,39 @@ xfs_finish_flags(
mp->m_qflags |= XFS_OQUOTA_ENFD;
}
+ if (xfs_sb_version_hasunicode(&mp->m_sb)) {
+ if (ap->flags2 & XFSMNT2_CILOOKUP)
+ mp->m_flags |= XFS_MOUNT_CI_LOOKUP;
+ if (ap->flags2 & XFSMNT2_CIATTR)
+ mp->m_flags |= XFS_MOUNT_CI_ATTR;
+
+ mp->m_nls = ap->nls[0] ? load_nls(ap->nls) : load_nls_default();
+ if (!mp->m_nls) {
+ cmn_err(CE_WARN,
+ "XFS: unable to load nls mapping \"%s\"\n", ap->nls);
+ return XFS_ERROR(EINVAL);
+ }
+ if (strcmp(mp->m_nls->charset, XFS_NLS_UTF8) == 0) {
+ /* special case utf8 - no translation required */
+ unload_nls(mp->m_nls);
+ mp->m_nls = NULL;
+ }
+ } else {
+ /*
+ * Check for mount options which require a Unicode FS
+ */
+ if (ap->flags2 & (XFSMNT2_CILOOKUP | XFSMNT2_CIATTR)) {
+ cmn_err(CE_WARN,
+ "XFS: can't do case-insensitive mount on non-utf8 filesystem");
+ return XFS_ERROR(EINVAL);
+
+ }
+ if (ap->nls[0]) {
+ cmn_err(CE_WARN,
+ "XFS: can't use nls mount option on non-utf8 filesystem");
+ return XFS_ERROR(EINVAL);
+ }
+ }
return 0;
}
@@ -641,6 +675,8 @@ out:
xfs_unmountfs(mp, credp);
xfs_qmops_put(mp);
xfs_dmops_put(mp);
+ if (mp->m_nls)
+ unload_nls(mp->m_nls);
kmem_free(mp, sizeof(xfs_mount_t));
}
===========================================================================
fs/xfs/xfs_vnodeops.c
===========================================================================
--- a/fs/xfs/xfs_vnodeops.c 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_vnodeops.c 2008-01-11 14:20:12.000000000 +1100
@@ -1793,7 +1793,9 @@ int
xfs_lookup(
xfs_inode_t *dp,
bhv_vname_t *dentry,
- bhv_vnode_t **vpp)
+ bhv_vnode_t **vpp,
+ char **actual_name,
+ int *actual_namelen)
{
xfs_inode_t *ip;
xfs_ino_t e_inum;
@@ -1806,7 +1808,8 @@ xfs_lookup(
return XFS_ERROR(EIO);
lock_mode = xfs_ilock_map_shared(dp);
- error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip);
+ error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip,
+ actual_name, actual_namelen);
if (!error) {
*vpp = XFS_ITOV(ip);
xfs_itrace_ref(ip);
===========================================================================
fs/xfs/xfs_vnodeops.h
===========================================================================
--- a/fs/xfs/xfs_vnodeops.h 2008-01-18 15:31:25.000000000 +1100
+++ b/fs/xfs/xfs_vnodeops.h 2008-01-11 14:20:12.000000000 +1100
@@ -25,7 +25,7 @@ int xfs_fsync(struct xfs_inode *ip, int
int xfs_release(struct xfs_inode *ip);
int xfs_inactive(struct xfs_inode *ip);
int xfs_lookup(struct xfs_inode *dp, bhv_vname_t *dentry,
- bhv_vnode_t **vpp);
+ bhv_vnode_t **vpp, char **actual_name, int *actual_namelen);
int xfs_create(struct xfs_inode *dp, bhv_vname_t *dentry, mode_t mode,
xfs_dev_t rdev, bhv_vnode_t **vpp, struct cred *credp);
int xfs_remove(struct xfs_inode *dp, bhv_vname_t *dentry);
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch 2008-01-18 4:43 [REVIEW 1/2] Case insensitive support for XFS - kernel patch Barry Naujok @ 2008-01-19 5:22 ` Eric Sandeen 2008-01-21 3:53 ` Barry Naujok 2008-01-19 5:30 ` Eric Sandeen 1 sibling, 1 reply; 5+ messages in thread From: Eric Sandeen @ 2008-01-19 5:22 UTC (permalink / raw) To: Barry Naujok; +Cc: xfs@oss.sgi.com, xfs-dev Barry Naujok wrote: > This patch should apply to 2.6.24-rc6. actually doesn't seem to; at least doesn't apply to rc8... mount handling has moved, etc - well, easy enough to fix up. The samba guys are excited about this, I bet :) Lots of comments below; keep an open mind and a sense of humor, most aren't demands but ideas, musings etc... > =========================================================================== > fs/xfs/Makefile > =========================================================================== > > --- a/fs/xfs/Makefile 2008-01-18 15:31:23.000000000 +1100 > +++ b/fs/xfs/Makefile 2007-10-23 16:17:22.173903950 +1000 > @@ -74,6 +74,7 @@ xfs-y += xfs_alloc.o \ > xfs_trans_extfree.o \ > xfs_trans_inode.o \ > xfs_trans_item.o \ > + xfs_unicode.o \ obj-$(CONFIG_XFS_UNICODE) perhaps? It's a lot of new code that not everyone needs? > xfs_utils.o \ > xfs_vfsops.o \ > xfs_vnodeops.o \ > ... > =========================================================================== > fs/xfs/linux-2.6/xfs_iops.c > =========================================================================== > > --- a/fs/xfs/linux-2.6/xfs_iops.c 2008-01-18 15:31:23.000000000 +1100 > +++ b/fs/xfs/linux-2.6/xfs_iops.c 2008-01-17 12:26:26.905427167 +1100 > @@ -47,6 +47,8 @@ > #include "xfs_buf_item.h" > #include "xfs_utils.h" > #include "xfs_vnodeops.h" > +#include "xfs_da_btree.h" > +#include "xfs_unicode.h" > > #include <linux/capability.h> > #include <linux/xattr.h> > @@ -388,7 +390,7 @@ xfs_vn_lookup( > if (dentry->d_name.len >= MAXNAMELEN) > return ERR_PTR(-ENAMETOOLONG); > > - error = xfs_lookup(XFS_I(dir), dentry, &cvp); > + error = xfs_lookup(XFS_I(dir), dentry, &cvp, NULL, NULL); > if (unlikely(error)) { > if (unlikely(error != ENOENT)) > return ERR_PTR(-error); > @@ -399,6 +401,113 @@ xfs_vn_lookup( > return d_splice_alias(vn_to_inode(cvp), dentry); > } > > +STATIC struct dentry * > +xfs_vn_ci_lookup( > + struct inode *dir, > + struct dentry *dentry, > + struct nameidata *nd) > +{ > + bhv_vnode_t *cvp; > + int error; > + struct dentry *result; > + struct qstr actual_name; > + struct inode *inode; > + > + if (dentry->d_name.len >= MAXNAMELEN) > + return ERR_PTR(-ENAMETOOLONG); > + > + error = xfs_lookup(XFS_I(dir), dentry, &cvp, (char **)&actual_name.name, > + &actual_name.len); > + if (unlikely(error)) { > + if (unlikely(error != ENOENT)) > + return ERR_PTR(-error); > + d_add(dentry, NULL); > + return NULL; > + } > + inode = vn_to_inode(cvp); > + > + /* if exact match, just splice and exit */ > + if (!actual_name.name) { > + result = d_splice_alias(inode, dentry); > + return result; > + } > + > + /* > + * case-insensitive match, create a dentry to return and fill it > + * in with the correctly cased name. Parameter "dentry" is not > + * used anymore and the caller will free it. > + * Derived from fs/ntfs/namei.c > + */ > + > + actual_name.hash = full_name_hash(actual_name.name, actual_name.len); > + > + /* Does an existing dentry match? */ > + result = d_lookup(dentry->d_parent, &actual_name); > + if (!result) { > + /* if not, create one */ > + result = d_alloc(dentry->d_parent, &actual_name); > + xfs_free_unicode_nls_name((char *)actual_name.name); > + if (!result) > + return ERR_PTR(-ENOMEM); > + dentry = d_splice_alias(inode, result); > + if (dentry) { > + dput(result); > + return dentry; > + } > + return result; > + } > + xfs_free_unicode_nls_name((char *)actual_name.name); > + > + /* an existing dentry matches, use it */ > + > + if (result->d_inode) { > + /* > + * already an inode attached, deref the inode that was > + * refcounted with xfs_lookup and return the dentry. > + */ > + if (unlikely(result->d_inode != inode)) { > + /* This can happen because bad inodes are unhashed. */ > + BUG_ON(!is_bad_inode(inode)); > + BUG_ON(!is_bad_inode(result->d_inode)); > + } > + iput(inode); > + return result; > + } > + > + if (!S_ISDIR(inode->i_mode)) { > + /* not a directory, easy to handle */ > + d_instantiate(result, inode); > + return result; > + } > + > + spin_lock(&dcache_lock); > + if (list_empty(&inode->i_dentry)) { > + /* > + * Directory without a 'disconnected' dentry; we need to do > + * d_instantiate() by hand because it takes dcache_lock which > + * we already hold. > + */ > + list_add(&result->d_alias, &inode->i_dentry); > + result->d_inode = inode; > + spin_unlock(&dcache_lock); > + security_d_instantiate(result, inode); > + return result; > + } > + /* > + * Directory with a 'disconnected' dentry; get a reference to the > + * 'disconnected' dentry. > + */ > + dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias); > + dget_locked(dentry); > + spin_unlock(&dcache_lock); > + security_d_instantiate(result, inode); > + d_move(dentry, result); > + iput(inode); > + dput(result); > + return dentry; > +} > + > + It seems like it might be nice to make the CI code a compile-time option? Fairly big new chunks... if it could be done cleanly, might be nice... > =========================================================================== > fs/xfs/linux-2.6/xfs_linux.h > =========================================================================== > > --- a/fs/xfs/linux-2.6/xfs_linux.h 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/linux-2.6/xfs_linux.h 2008-01-11 14:49:16.537591564 +1100 > @@ -75,6 +75,8 @@ > #include <linux/delay.h> > #include <linux/log2.h> > #include <linux/spinlock.h> > +#include <linux/ctype.h> > +#include <linux/nls.h> > > #include <asm/page.h> > #include <asm/div64.h> > @@ -180,6 +182,12 @@ > #define howmany(x, y) (((x)+((y)-1))/(y)) > > /* > + * NLS UTF-8 character set > + */ > + > +#define XFS_NLS_UTF8 "utf8" I guess that had to go somewhere? :) > + > +/* > * Various platform dependent calls that don't fit anywhere else > */ > #define xfs_sort(a,n,s,fn) sort(a,n,s,fn,NULL) > > =========================================================================== > fs/xfs/linux-2.6/xfs_super.c > =========================================================================== > > --- a/fs/xfs/linux-2.6/xfs_super.c 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/linux-2.6/xfs_super.c 2008-01-11 14:46:25.067566854 +1100 > @@ -50,6 +50,7 @@ > #include "xfs_vnodeops.h" > #include "xfs_vfsops.h" > #include "xfs_version.h" > +#include "xfs_unicode.h" > #include "xfs_log_priv.h" > > #include <linux/namei.h> > @@ -121,6 +122,9 @@ xfs_args_allocate( > #define MNTOPT_ATTR2 "attr2" /* do use attr2 attribute format */ > #define MNTOPT_NOATTR2 "noattr2" /* do not use attr2 attribute format */ > #define MNTOPT_FILESTREAM "filestreams" /* use filestreams allocator */ > +#define MNTOPT_NLS "nls" /* NLS code page to use */ > +#define MNTOPT_CILOOKUP "ci" /* case-insensitive dir names */ > +#define MNTOPT_CIATTR "ciattr" /* case-insensitive attr names */ > #define MNTOPT_QUOTA "quota" /* disk quotas (user) */ > #define MNTOPT_NOQUOTA "noquota" /* no quotas */ > #define MNTOPT_USRQUOTA "usrquota" /* user quota enabled */ Please document in Documentation/filesystems/xfs.txt too... > @@ -315,6 +319,18 @@ xfs_parseargs( > args->flags &= ~XFSMNT_ATTR2; > } else if (!strcmp(this_char, MNTOPT_FILESTREAM)) { > args->flags2 |= XFSMNT2_FILESTREAMS; > + } else if (!strcmp(this_char, MNTOPT_NLS)) { > + if (!value || !*value) { > + cmn_err(CE_WARN, > + "XFS: %s option requires an argument", > + this_char); > + return EINVAL; > + } > + strncpy(args->nls, value, MAXNAMELEN); > + } else if (!strcmp(this_char, MNTOPT_CILOOKUP)) { > + args->flags2 |= XFSMNT2_CILOOKUP; > + } else if (!strcmp(this_char, MNTOPT_CIATTR)) { > + args->flags2 |= XFSMNT2_CIATTR; > } else if (!strcmp(this_char, MNTOPT_NOQUOTA)) { > args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA); > args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA); > @@ -454,6 +470,8 @@ xfs_showargs( > { XFS_MOUNT_OSYNCISOSYNC, "," MNTOPT_OSYNCISOSYNC }, > { XFS_MOUNT_ATTR2, "," MNTOPT_ATTR2 }, > { XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM }, > + { XFS_MOUNT_CI_LOOKUP, "," MNTOPT_CILOOKUP }, > + { XFS_MOUNT_CI_ATTR, "," MNTOPT_CIATTR }, > { XFS_MOUNT_DMAPI, "," MNTOPT_DMAPI }, > { XFS_MOUNT_GRPID, "," MNTOPT_GRPID }, > { 0, NULL } > @@ -516,6 +534,13 @@ xfs_showargs( > if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT)) > seq_puts(m, "," MNTOPT_NOQUOTA); > > + if (xfs_sb_version_hasunicode(&mp->m_sb)) { > + if (mp->m_nls) > + seq_printf(m, "," MNTOPT_NLS "=%s", mp->m_nls->charset); > + else > + seq_puts(m, "," MNTOPT_NLS "=" XFS_NLS_UTF8); > + } > + > return 0; > } > __uint64_t > @@ -563,7 +588,11 @@ xfs_set_inodeops( > inode->i_mapping->a_ops = &xfs_address_space_operations; > break; > case S_IFDIR: > - inode->i_op = &xfs_dir_inode_operations; > + inode->i_op = > + xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) || > + (XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CI_LOOKUP) ? > + &xfs_dir_ci_inode_operations : > + &xfs_dir_inode_operations; Do any linux filesystems in existence actually have XFS_SB_VERSION_OLDCIBIT? I'd think not - this patch is *adding* that macro - what's this for? > inode->i_fop = &xfs_dir_file_operations; > break; > case S_IFLNK: > > =========================================================================== > fs/xfs/xfs_attr.c > =========================================================================== > > --- a/fs/xfs/xfs_attr.c 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/xfs_attr.c 2008-01-18 13:25:20.068339942 +1100 > @@ -106,6 +106,17 @@ ktrace_t *xfs_attr_trace_buf; > * Overall external interface routines. > *========================================================================*/ > > +void > +xfs_attr_mount(struct xfs_mount *mp) > +{ > + mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100; > + if (xfs_sb_version_hasunicode(&mp->m_sb)) { > + mp->m_attrnameops = (mp->m_flags & XFS_MOUNT_CI_ATTR) ? > + &xfs_unicode_ci_nameops : &xfs_unicode_nameops; > + } else > + mp->m_attrnameops = &xfs_default_nameops; > +} Hm, I thought most little mount-helper subroutines went right in next to xfs_mountfs() in xfs_mount.c; just a thought. Then you wouldn't need the prototype in the header either... > + > int > xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen, > char *value, int *valuelenp, int flags, struct cred *cred) > @@ -122,14 +133,14 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch > * Fill in the arg structure for this request. > */ > memset((char *)&args, 0, sizeof(args)); > - args.name = name; > - args.namelen = namelen; > args.value = value; > args.valuelen = *valuelenp; > args.flags = flags; > - args.hashval = xfs_da_hashname(args.name, args.namelen); > args.dp = ip; > args.whichfork = XFS_ATTR_FORK; > + error = xfs_da_setup_name_and_hash(&args, name, namelen); > + if (error) > + return error; In the spirit of incremental-but-functional patches, which helps to break down & review big patchsets like this, I think this addition of xfs_da_setup_name_and_hash() could stand on its own, and add the nls stuff in a subsequent patch? > /* > * Decide on what work routines to call based on the inode size. > @@ -153,6 +164,7 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch > > if (error == EEXIST) > error = 0; > + xfs_da_cleanup_name(&args, name); I'm being a little lazy here; under which circumstances would args->name != name, and need to be freed? ... > =========================================================================== > fs/xfs/xfs_attr_leaf.c > =========================================================================== > > --- a/fs/xfs/xfs_attr_leaf.c 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/xfs_attr_leaf.c 2008-01-18 13:25:11.873394723 +1100 > @@ -42,6 +42,7 @@ > #include "xfs_attr.h" > #include "xfs_attr_leaf.h" > #include "xfs_error.h" > +#include "xfs_unicode.h" > > /* > * xfs_attr_leaf.c > @@ -90,6 +91,10 @@ STATIC void xfs_attr_leaf_moveents(xfs_a > xfs_mount_t *mp); > STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index); > > +STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context, > + attrnames_t *namesp, char *name, > + int namelen, int valuelen, char *value); > + > /*======================================================================== > * Namespace helper routines > *========================================================================*/ > @@ -135,6 +140,38 @@ xfs_attr_namesp_match_overrides(int arg_ > * External routines when attribute fork size < XFS_LITINO(mp). > *========================================================================*/ > > +STATIC xfs_attr_sf_entry_t * > +xfs_attr_shortform_find_ent(xfs_da_args_t *args) > +{ > + xfs_attr_shortform_t *sf; > + xfs_attr_sf_entry_t *sfe; > + int i; > + xfs_attr_sf_entry_t *ci_sfe = NULL; > + > + ASSERT(args->dp->i_afp->if_flags & XFS_IFINLINE); > + sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data; > + sfe = &sf->list[0]; > + > + args->cmpresult = XFS_CMP_DIFFERENT; > + for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { > + if (!xfs_attr_namesp_match(args->flags, sfe->flags)) > + continue; > + switch (xfs_attr_compname(args->dp, sfe->nameval, sfe->namelen, > + args->name, args->namelen)) { > + case XFS_CMP_EXACT: > + args->cmpresult = XFS_CMP_EXACT; > + return sfe; > + case XFS_CMP_CASE: > + if (!ci_sfe) { > + args->cmpresult = XFS_CMP_CASE; > + ci_sfe = sfe; > + } > + default:; > + } > + } > + return ci_sfe; > +} Perhaps this helper could be a sub-patch too? Just a thought. > /* > * Query whether the requested number of additional bytes of extended > * attribute space will be able to fit inline. > @@ -295,13 +332,10 @@ xfs_attr_shortform_add(xfs_da_args_t *ar > sfe = &sf->list[0]; > for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { > #ifdef DEBUG > - if (sfe->namelen != args->namelen) > - continue; > - if (memcmp(args->name, sfe->nameval, args->namelen) != 0) > - continue; > if (!xfs_attr_namesp_match(args->flags, sfe->flags)) > continue; > - ASSERT(0); > + ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen, > + sfe->nameval, sfe->namelen) == XFS_CMP_DIFFERENT); Perhaps this helper too could come as a subpatch. ... > + if (!sfe) > + return XFS_ERROR(ENOATTR); > + Indentation from here on looks busted. Are you missing braces? > if (args->flags & ATTR_KERNOVAL) { > args->valuelen = sfe->valuelen; > - return(XFS_ERROR(EEXIST)); > + return XFS_ERROR(EEXIST); need another tab here? > } > if (args->valuelen < sfe->valuelen) { > args->valuelen = sfe->valuelen; > - return(XFS_ERROR(ERANGE)); > + return XFS_ERROR(ERANGE); and here? > } > args->valuelen = sfe->valuelen; > - memcpy(args->value, &sfe->nameval[args->namelen], > - args->valuelen); > - return(XFS_ERROR(EEXIST)); > - } > - return(XFS_ERROR(ENOATTR)); > + memcpy(args->value, &sfe->nameval[args->namelen], args->valuelen); > + > + return XFS_ERROR(EEXIST); > } > > /* ... > @@ -631,7 +626,7 @@ xfs_attr_shortform_list(xfs_attr_list_co > continue; > } > namesp = xfs_attr_flags_namesp(sfe->flags); > - error = context->put_listent(context, > + error = xfs_attr_put_listent(context, > namesp, > (char *)sfe->nameval, > (int)sfe->namelen, > @@ -734,7 +729,7 @@ xfs_attr_shortform_list(xfs_attr_list_co > cursor->hashval = sbp->hash; > cursor->offset = 0; > } > - error = context->put_listent(context, > + error = xfs_attr_put_listent(context, > namesp, > sbp->name, > sbp->namelen, maybe the introduction of xfs_attr_put_listent could be a small prep-work patch too. Or is this getting old ;) > @@ -1960,6 +1955,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp > xfs_attr_leaf_name_remote_t *name_rmt; > int probe, span; > xfs_dahash_t hashval; > + xfs_dacmp_t cmp; > > leaf = bp->data; > ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_ATTR_LEAF_MAGIC); > @@ -2008,6 +2004,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp > /* > * Duplicate keys may be present, so search all of them for a match. > */ > + args->cmpresult = XFS_CMP_DIFFERENT; > for ( ; (probe < be16_to_cpu(leaf->hdr.count)) && > (be32_to_cpu(entry->hashval) == hashval); > entry++, probe++) { > @@ -2019,35 +2016,40 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp > * If we are looking for complete entries, show only those. > */ > if ((args->flags & XFS_ATTR_INCOMPLETE) != > - (entry->flags & XFS_ATTR_INCOMPLETE)) { > - continue; > - } > - if (entry->flags & XFS_ATTR_LOCAL) { > - name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe); > - if (name_loc->namelen != args->namelen) > - continue; > - if (memcmp(args->name, (char *)name_loc->nameval, args->namelen) != 0) > + (entry->flags & XFS_ATTR_INCOMPLETE)) > continue; > if (!xfs_attr_namesp_match(args->flags, entry->flags)) > continue; > + if (entry->flags & XFS_ATTR_LOCAL) { > + name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe); > + cmp = xfs_attr_compname(args->dp, args->name, args->namelen, > + name_loc->nameval, name_loc->namelen); > + if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { > + args->cmpresult = cmp; > args->index = probe; weird indentation here too... > - return(XFS_ERROR(EEXIST)); > + args->rmtblkno = 0; > + args->rmtblkcnt = 0; > + if (cmp == XFS_CMP_EXACT) > + return XFS_ERROR(EEXIST); > + } > } else { > name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe); > - if (name_rmt->namelen != args->namelen) > - continue; > - if (memcmp(args->name, (char *)name_rmt->name, > - args->namelen) != 0) > - continue; > - if (!xfs_attr_namesp_match(args->flags, entry->flags)) > - continue; > + cmp = xfs_attr_compname(args->dp, args->name, args->namelen, > + name_rmt->name, name_rmt->namelen); > + if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { > + args->cmpresult = cmp; > args->index = probe; > args->rmtblkno = be32_to_cpu(name_rmt->valueblk); > args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, > be32_to_cpu(name_rmt->valuelen)); > - return(XFS_ERROR(EEXIST)); > + if (cmp == XFS_CMP_EXACT) > + return XFS_ERROR(EEXIST); and here > + } > } > } > + if (args->cmpresult == XFS_CMP_CASE) > + return XFS_ERROR(EEXIST); > + > args->index = probe; > return(XFS_ERROR(ENOATTR)); > } > @@ -2418,7 +2420,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, > xfs_attr_leaf_name_local_t *name_loc = > XFS_ATTR_LEAF_NAME_LOCAL(leaf, i); > > - retval = context->put_listent(context, > + retval = xfs_attr_put_listent(context, sub-patch? :) ... (int)name_rmt->namelen, > @@ -2472,6 +2474,31 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, > return(retval); > } > > +/* > + * Do NLS name conversion if required for attribute name and call > + * context's put_listent routine > + */ > + > +STATIC int > +xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp, > + char *name, int namelen, int valuelen, char *value) > +{ > + xfs_mount_t *mp = context->dp->i_mount; > + char *nls_name = NULL; > + int rval; > + > + if (!mp->m_nls) > + return context->put_listent(context, namesp, name, namelen, > + valuelen, value); > + > + rval = xfs_unicode_to_nls(mp->m_nls, name, namelen, &nls_name); > + if (rval < 0) > + return -rval; > + rval = context->put_listent(context, namesp, nls_name, rval, > + valuelen, value); > + xfs_free_unicode_nls_name(nls_name); > + return rval; > +} > > /*======================================================================== > * Manage the INCOMPLETE flag in a leaf entry > =========================================================================== > fs/xfs/xfs_attr_leaf.h > =========================================================================== > > --- a/fs/xfs/xfs_attr_leaf.h 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/xfs_attr_leaf.h 2008-01-11 14:16:44.268796245 +1100 > @@ -36,6 +36,7 @@ struct xfs_da_args; > struct xfs_da_state; > struct xfs_da_state_blk; > struct xfs_inode; > +struct xfs_mount; > struct xfs_trans; > > /*======================================================================== > @@ -204,6 +205,16 @@ static inline int xfs_attr_leaf_entsize_ > return (((bsize) >> 1) + ((bsize) >> 2)); > } > > +/* > + * Do hash and name compare based on nameops > + */ > +#define xfs_attr_hashname(dp, n, l) \ > + ((dp)->i_mount->m_attrnameops->hashname((dp), (n), (l))) > + > +#define xfs_attr_compname(dp, n1, l1, n2, l2) \ > + ((dp)->i_mount->m_attrnameops->compname((dp), (n1), (l1), \ > + (n2), (l2))) > + > > /*======================================================================== > * Structure used to pass context around among the routines. > @@ -296,6 +307,7 @@ int xfs_attr_root_inactive(struct xfs_tr > /* > * Utility routines. > */ > +void xfs_attr_mount(struct xfs_mount *mp); this could just go in xfs_mount.c and be static. > xfs_dahash_t xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count); > int xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp, > struct xfs_dabuf *leaf2_bp); > > =========================================================================== > fs/xfs/xfs_da_btree.c > =========================================================================== > > --- a/fs/xfs/xfs_da_btree.c 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/xfs_da_btree.c 2007-10-31 16:04:16.463309546 +1100 > @@ -46,6 +46,7 @@ > #include "xfs_dir2_block.h" > #include "xfs_dir2_node.h" > #include "xfs_error.h" > +#include "xfs_unicode.h" > > /* > * xfs_da_btree.c > @@ -1530,6 +1531,100 @@ xfs_da_hashname(const uchar_t *name, int > } > } > > + > +static xfs_dahash_t > +xfs_default_hashname(xfs_inode_t *inode, const uchar_t *name, int namelen) > +{ > + return xfs_da_hashname(name, namelen); > +} Could these be rolled into one instead of the wrapper? I see 3 other direct callers of xfs_da_hashname... and does xfs_attr_shortform_list need to use xfs_attr_hashname instead of xfs_da_hashname? Do "." and ".." ever have different answers in different codepages? Does that matter? > +xfs_dacmp_t > +xfs_default_compname(xfs_inode_t *inode, const uchar_t *name1, int len1, > + const uchar_t *name2, int len2) > +{ > + return (len1 == len2 && memcmp(name1, name2, len1) == 0) ? > + XFS_CMP_EXACT : XFS_CMP_DIFFERENT; > +} > + > +static xfs_dahash_t > +xfs_unicode_ci_hashname( > + xfs_inode_t *inode, > + const uchar_t *name, > + int namelen) > +{ > + return xfs_unicode_hash(inode->i_mount->m_cft, name, namelen); > +} this wrapper is the only caller of xfs_unicode_hash? Is it just to conveniently get from inode to m_cft? strikes me as a little interesting that while an inode is passed into a xfs_hashname_t, it's never actually used directly - same for compname - would it make any sense to just pass the xfs_cft in from the start? ... > =========================================================================== > fs/xfs/xfs_dir2.c > =========================================================================== > > --- a/fs/xfs/xfs_dir2.c 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/xfs_dir2.c 2008-01-11 14:24:51.701973714 +1100 > @@ -42,8 +42,56 @@ > #include "xfs_dir2_node.h" > #include "xfs_dir2_trace.h" > #include "xfs_error.h" > +#include "xfs_unicode.h" > #include "xfs_vnodeops.h" > > +/* > + * V1 case-insensitive support for directories > + */ remind me, what's "V1?" > +static xfs_dahash_t > +xfs_ascii_ci_hashname( > + xfs_inode_t *inode, > + const uchar_t *name, > + int namelen) > +{ > + xfs_dahash_t hash; > + int i; > + > + for (i = 0, hash = 0; i < namelen; i++) > + hash = tolower(name[i]) ^ rol32(hash, 7); > + > + return hash; > +} > + > +static xfs_dacmp_t > +xfs_ascii_ci_compname( > + xfs_inode_t *inode, > + const uchar_t *name1, > + int len1, > + const uchar_t *name2, > + int len2) > +{ > + xfs_dacmp_t result = XFS_CMP_EXACT; > + int i; > + > + if (len1 != len2) > + return XFS_CMP_DIFFERENT; > + > + for (i = 0; i < len1; i++) { > + if (name1[i] == name2[i]) > + continue; > + if (tolower(name1[i]) != tolower(name2[i])) > + return XFS_CMP_DIFFERENT; > + result = XFS_CMP_CASE; > + } > + > + return result; > +} > + > +static const struct xfs_nameops xfs_ascii_ci_nameops = { > + .hashname = xfs_ascii_ci_hashname, > + .compname = xfs_ascii_ci_compname, > +}; > > void > xfs_dir_mount( > @@ -64,6 +112,13 @@ 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; > + > + if (xfs_sb_version_hasunicode(&mp->m_sb)) { > + mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CI_LOOKUP) ? > + &xfs_unicode_ci_nameops : &xfs_unicode_nameops; > + } else > + mp->m_dirnameops = (xfs_sb_version_hasoldci(&mp->m_sb)) ? > + &xfs_ascii_ci_nameops : &xfs_default_nameops; Oh, "V1" is oldci - can you document it that way in the comment? And if that hasn't been seen in the wild on linux can this go away? ... > =========================================================================== > fs/xfs/xfs_dir2_block.c > =========================================================================== > > --- a/fs/xfs/xfs_dir2_block.c 2008-01-18 15:31:24.000000000 +1100 > +++ b/fs/xfs/xfs_dir2_block.c 2008-01-11 14:28:44.763934272 +1100 ... > @@ -697,20 +720,34 @@ xfs_dir2_block_lookup_int( > dep = (xfs_dir2_data_entry_t *) > ((char *)block + xfs_dir2_dataptr_to_off(mp, addr)); > /* > - * Compare, if it's right give back buffer & entry number. > - */ > - if (dep->namelen == args->namelen && > - dep->name[0] == args->name[0] && > - memcmp(dep->name, args->name, args->namelen) == 0) { > + * Compare, if it's right give back buffer & entry number: > + * > + * lookup case - use nameops; > + * > + * replace/remove case - as lookup has been already been > + * performed, look for an exact match using the fast method > + */ > + cmp = args->oknoent ? > + xfs_dir_compname(dp, dep->name, dep->namelen, > + args->name, args->namelen) : > + xfs_default_compname(dp, dep->name, dep->namelen, > + args->name, args->namelen); > + if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { > + args->cmpresult = cmp; > *bpp = bp; > *entno = mid; > + if (cmp == XFS_CMP_EXACT) > return 0; > } > - } while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash); > + } while (++mid < be32_to_cpu(btp->count) && > + be32_to_cpu(blp[mid].hashval) == hash); thanks... can you trim down the other > 80 char lines you've added, too? :) > @@ -1300,10 +1313,17 @@ xfs_dir2_leaf_lookup( > /* > * Return the found inode number. > */ > + error = EEXIST; > args->inumber = be64_to_cpu(dep->inumber); > + if (args->cmpresult == XFS_CMP_CASE) { > + args->valuelen = xfs_unicode_to_nls(args->dp->i_mount->m_nls, > + dep->name, dep->namelen, (char **)&args->value); > + if (args->valuelen < 0) > + error = -args->valuelen; hm, error signs... new functions return negative, whereas old xfs code returns positive errors? ... > @@ -1391,19 +1413,31 @@ xfs_dir2_leaf_lookup_int( > xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); > /* > * If it matches then return it. > - */ > - if (dep->namelen == args->namelen && > - dep->name[0] == args->name[0] && > - memcmp(dep->name, args->name, args->namelen) == 0) { > + * > + * lookup case - use nameops; > + * > + * replace/remove case - as lookup has been already been > + * performed, look for an exact match using the fast method > + */ > + cmp = args->oknoent ? > + xfs_dir_compname(dp, dep->name, dep->namelen, > + args->name, args->namelen) : > + xfs_default_compname(dp, dep->name, dep->namelen, > + args->name, args->namelen); > + if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { > + args->cmpresult = cmp; > *dbpp = dbp; > *indexp = index; > + if (cmp == XFS_CMP_EXACT) > return 0; tab missing? > =========================================================================== > fs/xfs/xfs_dir2_node.c > =========================================================================== > > --- a/fs/xfs/xfs_dir2_node.c 2008-01-18 15:31:25.000000000 +1100 > +++ b/fs/xfs/xfs_dir2_node.c 2007-10-31 12:32:04.060201390 +1100 ... > @@ -572,28 +575,34 @@ xfs_dir2_leafn_lookup_int( > /* > * Point to the data entry. > */ > - dep = (xfs_dir2_data_entry_t *) > - ((char *)curbp->data + > + dep = (xfs_dir2_data_entry_t *)((char *)curbp->data + > xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); > /* > * Compare the entry, return it if it matches. > */ > - if (dep->namelen == args->namelen && > - dep->name[0] == args->name[0] && > - memcmp(dep->name, args->name, args->namelen) == 0) { > + cmp = args->oknoent ? > + xfs_dir_compname(dp, dep->name, dep->namelen, > + args->name, args->namelen): > + xfs_default_compname(dp, dep->name, dep->namelen, > + args->name, args->namelen); > + if (cmp != XFS_CMP_DIFFERENT && > + cmp != args->cmpresult) { > + args->cmpresult = cmp; > args->inumber = be64_to_cpu(dep->inumber); > *indexp = index; > state->extravalid = 1; > state->extrablk.bp = curbp; > state->extrablk.blkno = curdb; > - state->extrablk.index = > - (int)((char *)dep - > + state->extrablk.index = (int)((char *)dep - > (char *)curbp->data); > state->extrablk.magic = XFS_DIR2_DATA_MAGIC; > + if (cmp == XFS_CMP_EXACT) > return XFS_ERROR(EEXIST); tab... Hmm wonder if I caught all of these... > =========================================================================== > fs/xfs/xfs_dir2_sf.c > =========================================================================== > > --- a/fs/xfs/xfs_dir2_sf.c 2008-01-18 15:31:25.000000000 +1100 > +++ b/fs/xfs/xfs_dir2_sf.c 2008-01-17 12:25:01.552398622 +1100 > @@ -38,6 +38,7 @@ > #include "xfs_dir2_leaf.h" > #include "xfs_dir2_block.h" > #include "xfs_dir2_trace.h" > +#include "xfs_unicode.h" > > /* > * Prototypes for internal functions. > @@ -708,6 +709,8 @@ xfs_dir2_sf_getdents( > xfs_dir2_dataptr_t dot_offset; > xfs_dir2_dataptr_t dotdot_offset; > xfs_ino_t ino; > + char *nls_name = NULL; /* NLS name buffer */ > + int nls_namelen = 0; > > mp = dp->i_mount; > > @@ -772,6 +775,9 @@ xfs_dir2_sf_getdents( > } > } > > + if (mp->m_nls) > + nls_name = xfs_alloc_unicode_nls_name(); > + > /* > * Loop while there are more entries and put'ing works. > */ > @@ -789,16 +795,22 @@ xfs_dir2_sf_getdents( > #if XFS_BIG_INUMS > ino += mp->m_inoadd; > #endif > - > - if (filldir(dirent, sfep->name, sfep->namelen, > + if (mp->m_nls) > + nls_namelen = xfs_unicode_to_nls(mp->m_nls, sfep->name, > + sfep->namelen, &nls_name); > + if (filldir(dirent, > + nls_namelen > 0 ? nls_name : (char *)sfep->name, > + nls_namelen > 0 ? nls_namelen : sfep->namelen, > off, ino, DT_UNKNOWN)) { Hmm we've seen the foo > 0 ? bar : baz stuff a few times, should this get a helper? ... > @@ -844,23 +857,43 @@ xfs_dir2_sf_lookup( > if (args->namelen == 2 && > args->name[0] == '.' && args->name[1] == '.') { > args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent); > + args->cmpresult = XFS_CMP_EXACT; > return XFS_ERROR(EEXIST); > } > /* > * Loop over all the entries trying to match ours. > */ > - for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); > - i < sfp->hdr.count; > + args->cmpresult = XFS_CMP_DIFFERENT; > + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; > i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { > - if (sfep->namelen == args->namelen && > - sfep->name[0] == args->name[0] && > - memcmp(args->name, sfep->name, args->namelen) == 0) { > - args->inumber = > - xfs_dir2_sf_get_inumber(sfp, > + switch (xfs_dir_compname(dp, sfep->name, sfep->namelen, > + args->name, args->namelen)) { > + case XFS_CMP_EXACT: > + args->cmpresult = XFS_CMP_EXACT; > + args->inumber = xfs_dir2_sf_get_inumber(sfp, > xfs_dir2_sf_inumberp(sfep)); > + if (args->value) { > + xfs_free_unicode_nls_name(args->value); > + args->value = NULL; > + } > return XFS_ERROR(EEXIST); > + > + case XFS_CMP_CASE: > + if (!args->value) { > + args->valuelen = xfs_unicode_to_nls( > + args->dp->i_mount->m_nls, sfep->name, > + sfep->namelen, (char **)&args->value); > + if (args->valuelen < 0) > + return XFS_ERROR(-args->valuelen); > + args->cmpresult = XFS_CMP_CASE; > + args->inumber = xfs_dir2_sf_get_inumber(sfp, > + xfs_dir2_sf_inumberp(sfep)); > + } > + default: ; Hmm that's a little funky, to my eyes anyway. ... > =========================================================================== > fs/xfs/xfs_mount.c > =========================================================================== > > --- a/fs/xfs/xfs_mount.c 2008-01-18 15:31:25.000000000 +1100 > +++ b/fs/xfs/xfs_mount.c 2008-01-17 17:10:29.777728874 +1100 > @@ -25,6 +25,7 @@ > #include "xfs_sb.h" > #include "xfs_ag.h" > #include "xfs_dir2.h" > +#include "xfs_attr.h" > #include "xfs_dmapi.h" > #include "xfs_mount.h" > #include "xfs_bmap_btree.h" > @@ -43,6 +44,9 @@ > #include "xfs_rw.h" > #include "xfs_quota.h" > #include "xfs_fsops.h" > +#include "xfs_da_btree.h" > +#include "xfs_attr_leaf.h" > +#include "xfs_unicode.h" > > STATIC void xfs_mount_log_sbunit(xfs_mount_t *, __int64_t); > STATIC int xfs_uuid_mount(xfs_mount_t *); > @@ -119,6 +123,8 @@ static const struct { > { offsetof(xfs_sb_t, sb_logsectsize),0 }, > { offsetof(xfs_sb_t, sb_logsunit), 0 }, > { offsetof(xfs_sb_t, sb_features2), 0 }, > + { offsetof(xfs_sb_t, sb_bad_features2), 0 }, bad_features2? what's this and what does it have to do w/ CI, and why is it set but never used in xfs_sb_from_disk? if features2 "could be here" shouldn't we be doing something with that? This could do with comments at least, somewhere. ... > @@ -1165,6 +1176,17 @@ xfs_mountfs( > } > > /* > + * Load in unicode case folding table from disk > + */ > + if (xfs_sb_version_hasunicode(&mp->m_sb)) { > + error = xfs_unicode_read_cft(mp); > + if (error) { > + cmn_err(CE_WARN, "XFS: failed to read case folding table"); 80+ chars... .. > =========================================================================== > fs/xfs/xfs_sb.h > =========================================================================== > > --- a/fs/xfs/xfs_sb.h 2008-01-18 15:31:25.000000000 +1100 > +++ b/fs/xfs/xfs_sb.h 2007-10-23 16:55:47.440178601 +1000 > @@ -46,10 +46,12 @@ struct xfs_mount; > #define XFS_SB_VERSION_SECTORBIT 0x0800 > #define XFS_SB_VERSION_EXTFLGBIT 0x1000 > #define XFS_SB_VERSION_DIRV2BIT 0x2000 > +#define XFS_SB_VERSION_OLDCIBIT 0x4000 > #define XFS_SB_VERSION_MOREBITSBIT 0x8000 > #define XFS_SB_VERSION_OKSASHFBITS \ > (XFS_SB_VERSION_EXTFLGBIT | \ > - XFS_SB_VERSION_DIRV2BIT) > + XFS_SB_VERSION_DIRV2BIT | \ > + XFS_SB_VERSION_OLDCIBIT) there's that oldcibit again... > #define XFS_SB_VERSION_OKREALFBITS \ > (XFS_SB_VERSION_ATTRBIT | \ > XFS_SB_VERSION_NLINKBIT | \ > @@ -77,10 +79,12 @@ struct xfs_mount; > #define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */ > #define XFS_SB_VERSION2_RESERVED4BIT 0x00000004 > #define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */ > +#define XFS_SB_VERSION2_UNICODEBIT 0x00000020 /* Unicode names */ > > #define XFS_SB_VERSION2_OKREALFBITS \ > (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \ > - XFS_SB_VERSION2_ATTR2BIT) > + XFS_SB_VERSION2_ATTR2BIT | \ > + XFS_SB_VERSION2_UNICODEBIT) > #define XFS_SB_VERSION2_OKSASHFBITS \ > (0) > #define XFS_SB_VERSION2_OKREALBITS \ > @@ -145,6 +149,9 @@ typedef struct xfs_sb { > __uint16_t sb_logsectsize; /* sector size for the log, bytes */ > __uint32_t sb_logsunit; /* stripe unit size for the log */ > __uint32_t sb_features2; /* additional feature bits */ > + __uint32_t sb_bad_features2; /* features2 could be here */ Ok, so...? ... > @@ -463,6 +475,12 @@ static inline int xfs_sb_version_hassect > ((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT); > } > > +static inline int xfs_sb_version_hasoldci(xfs_sb_t *sbp) > +{ > + return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ > + ((sbp)->sb_versionnum & XFS_SB_VERSION_OLDCIBIT); > +} You forgot the uppercase indirection macro! ;) ... > =========================================================================== > fs/xfs/xfs_unicode.c > =========================================================================== ... > + > +char * > +xfs_alloc_unicode_nls_name(void) > +{ > + return kmem_zone_alloc(xfs_nls_uni_zone, KM_SLEEP); > +} Why wrap/hide this? > + > + > +void > +xfs_free_unicode_nls_name( > + char *name) > +{ > + kmem_zone_free(xfs_nls_uni_zone, name); > +} Or this? Eh, maybe it's handy. > +int > +xfs_nls_to_unicode( > + struct nls_table *nls, > + const char *nls_name, > + int nls_namelen, > + char **uni_name) > +{ > + char *n; > + int i, o; > + wchar_t uc; > + int nlen; > + int u8len; > + int rval; > + > + n = *uni_name ? *uni_name : xfs_alloc_unicode_nls_name(); > + > + if (!nls) { > + if (nls_namelen > MAXNAMELEN) { > + rval = -ENAMETOOLONG; The rest of core xfs code returns positive errors; why the shift in this file? Well, I guess because you want to return a length, but this strikes me as a bit inconsistent... we've been burned by getting error signs wrong in the past, this looks like an exception to the existing sign conventions ... > +int > +xfs_unicode_validate( > + const uchar_t *name, > + int namelen) > +{ > + wchar_t uc; > + int i, nlen; > + > + for (i = 0; i < namelen; i += nlen) { > + if (*name >= 0xf0) { > + cmn_err(CE_WARN, "xfs_unicode_validate: " > + "UTF-8 char beyond U+FFFF\n"); > + return -EINVAL; > + } > + /* utf8_mbtowc must fail on overlong sequences too */ > + nlen = utf8_mbtowc(&uc, name + i, namelen - i); > + if (nlen < 0) { > + cmn_err(CE_WARN, "xfs_unicode_validate: " > + "invalid UTF-8 sequence\n"); > + return -EILSEQ; > + } > + /* check for invalid/surrogate/private unicode chars */ > + if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xf8ff)) { > + cmn_err(CE_WARN, "xfs_unicode_validate: " > + "unsupported UTF-8 char\n"); > + return -EINVAL; > + } > + } > + return 0; > +} and now this leads to stuff like: rval = xfs_unicode_validate(name, namelen); if (rval < 0) return -rval; ... this looks odd to me. ... > +xfs_unicode_read_cft( > + xfs_mount_t *mp) > +{ > + int error; > + xfs_inode_t *cftip; > + int size; > + int nfsb; > + int nmap; > + xfs_bmbt_irec_t *mapp; > + int n; > + int byte_cnt; > + xfs_buf_t *bp; > + char *table; > + xfs_dcft_t *dcft; > + > + if (mp->m_sb.sb_cftino == NULLFSINO || mp->m_sb.sb_cftino == 0) > + return EINVAL; oh, here it's positive? :) ... > +void > +xfs_unicode_free_cft( > + const xfs_cft_t *cft) > +{ > + remove_cft(cft); > +} why the wrapper? > +void > +xfs_unicode_init(void) > +{ > + mutex_init(&cft_lock); > + xfs_nls_uni_zone = kmem_zone_init(MAXNAMELEN, "xfs_nls_uni"); > +} Hm, no corresponding mutex_destroy > +void > +xfs_unicode_uninit(void) > +{ > + int i; > + > + mutex_lock(&cft_lock); > + > + for (i = 0; i < cft_size; i++) { > + ASSERT(cft_list[i].refcount == 0); > + vfree(cft_list[i].table); > + } > + kmem_free(cft_list, cft_size * sizeof(struct cft_item)); > + cft_size = 0; > + cft_list = NULL; > + > + mutex_unlock(&cft_lock); destroy mutex here too just for tidiness > + kmem_zone_destroy(xfs_nls_uni_zone); > +} > ... ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch 2008-01-19 5:22 ` Eric Sandeen @ 2008-01-21 3:53 ` Barry Naujok 2008-01-23 6:55 ` Christoph Hellwig 0 siblings, 1 reply; 5+ messages in thread From: Barry Naujok @ 2008-01-21 3:53 UTC (permalink / raw) To: Eric Sandeen; +Cc: xfs@oss.sgi.com, xfs-dev [-- Attachment #1: Type: text/plain, Size: 392 bytes --] On Sat, 19 Jan 2008 16:22:47 +1100, Eric Sandeen <sandeen@sandeen.net> wrote: > Lots of comments below; I have addressed the "real" ones :) - xfs_unicode.c functions do not return negative error codes anymore. - Fixed up the indenting in the patch. - Added a few comments for things that weren't clear. - Made sure my changes didn't exceed 80 char lines. No sub-patches at this stage. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: ci_kernel.patch --] [-- Type: text/x-patch; name=ci_kernel.patch, Size: 101860 bytes --] =========================================================================== fs/xfs/Makefile =========================================================================== --- a/fs/xfs/Makefile 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/Makefile 2007-10-23 16:17:22.173903950 +1000 @@ -74,6 +74,7 @@ xfs-y += xfs_alloc.o \ xfs_trans_extfree.o \ xfs_trans_inode.o \ xfs_trans_item.o \ + xfs_unicode.o \ xfs_utils.o \ xfs_vfsops.o \ xfs_vnodeops.o \ @@ -97,7 +98,7 @@ xfs-y += $(addprefix $(XFS_LINUX)/, \ xfs_lrw.o \ xfs_super.o \ xfs_vnode.o \ - xfs_ksyms.o) + xfs_ksyms.o) # Objects in support/ xfs-y += $(addprefix support/, \ =========================================================================== fs/xfs/linux-2.6/xfs_export.c =========================================================================== --- a/fs/xfs/linux-2.6/xfs_export.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/linux-2.6/xfs_export.c 2008-01-11 14:20:10.000000000 +1100 @@ -217,7 +217,7 @@ xfs_fs_get_parent( struct dentry *parent; cvp = NULL; - error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp); + error = xfs_lookup(XFS_I(child->d_inode), &dotdot, &cvp, NULL, NULL); if (unlikely(error)) return ERR_PTR(-error); =========================================================================== fs/xfs/linux-2.6/xfs_iops.c =========================================================================== --- a/fs/xfs/linux-2.6/xfs_iops.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/linux-2.6/xfs_iops.c 2008-01-21 14:27:33.000000000 +1100 @@ -47,6 +47,8 @@ #include "xfs_buf_item.h" #include "xfs_utils.h" #include "xfs_vnodeops.h" +#include "xfs_da_btree.h" +#include "xfs_unicode.h" #include <linux/capability.h> #include <linux/xattr.h> @@ -388,7 +390,7 @@ xfs_vn_lookup( if (dentry->d_name.len >= MAXNAMELEN) return ERR_PTR(-ENAMETOOLONG); - error = xfs_lookup(XFS_I(dir), dentry, &cvp); + error = xfs_lookup(XFS_I(dir), dentry, &cvp, NULL, NULL); if (unlikely(error)) { if (unlikely(error != ENOENT)) return ERR_PTR(-error); @@ -399,6 +401,113 @@ xfs_vn_lookup( return d_splice_alias(vn_to_inode(cvp), dentry); } +STATIC struct dentry * +xfs_vn_ci_lookup( + struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + bhv_vnode_t *cvp; + int error; + struct dentry *result; + struct qstr actual_name; + struct inode *inode; + + if (dentry->d_name.len >= MAXNAMELEN) + return ERR_PTR(-ENAMETOOLONG); + + error = xfs_lookup(XFS_I(dir), dentry, &cvp, (char **)&actual_name.name, + &actual_name.len); + if (unlikely(error)) { + if (unlikely(error != ENOENT)) + return ERR_PTR(-error); + d_add(dentry, NULL); + return NULL; + } + inode = vn_to_inode(cvp); + + /* if exact match, just splice and exit */ + if (!actual_name.name) { + result = d_splice_alias(inode, dentry); + return result; + } + + /* + * case-insensitive match, create a dentry to return and fill it + * in with the correctly cased name. Parameter "dentry" is not + * used anymore and the caller will free it. + * Derived from fs/ntfs/namei.c + */ + + actual_name.hash = full_name_hash(actual_name.name, actual_name.len); + + /* Does an existing dentry match? */ + result = d_lookup(dentry->d_parent, &actual_name); + if (!result) { + /* if not, create one */ + result = d_alloc(dentry->d_parent, &actual_name); + xfs_free_unicode_nls_name((char *)actual_name.name); + if (!result) + return ERR_PTR(-ENOMEM); + dentry = d_splice_alias(inode, result); + if (dentry) { + dput(result); + return dentry; + } + return result; + } + xfs_free_unicode_nls_name((char *)actual_name.name); + + /* an existing dentry matches, use it */ + + if (result->d_inode) { + /* + * already an inode attached, deref the inode that was + * refcounted with xfs_lookup and return the dentry. + */ + if (unlikely(result->d_inode != inode)) { + /* This can happen because bad inodes are unhashed. */ + BUG_ON(!is_bad_inode(inode)); + BUG_ON(!is_bad_inode(result->d_inode)); + } + iput(inode); + return result; + } + + if (!S_ISDIR(inode->i_mode)) { + /* not a directory, easy to handle */ + d_instantiate(result, inode); + return result; + } + + spin_lock(&dcache_lock); + if (list_empty(&inode->i_dentry)) { + /* + * Directory without a 'disconnected' dentry; we need to do + * d_instantiate() by hand because it takes dcache_lock which + * we already hold. + */ + list_add(&result->d_alias, &inode->i_dentry); + result->d_inode = inode; + spin_unlock(&dcache_lock); + security_d_instantiate(result, inode); + return result; + } + /* + * Directory with a 'disconnected' dentry; get a reference to the + * 'disconnected' dentry. + */ + dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias); + dget_locked(dentry); + spin_unlock(&dcache_lock); + security_d_instantiate(result, inode); + d_move(dentry, result); + iput(inode); + dput(result); + return dentry; +} + + STATIC int xfs_vn_link( struct dentry *old_dentry, @@ -887,6 +996,25 @@ const struct inode_operations xfs_dir_in .removexattr = xfs_vn_removexattr, }; +const struct inode_operations xfs_dir_ci_inode_operations = { + .create = xfs_vn_create, + .lookup = xfs_vn_ci_lookup, + .link = xfs_vn_link, + .unlink = xfs_vn_unlink, + .symlink = xfs_vn_symlink, + .mkdir = xfs_vn_mkdir, + .rmdir = xfs_vn_rmdir, + .mknod = xfs_vn_mknod, + .rename = xfs_vn_rename, + .permission = xfs_vn_permission, + .getattr = xfs_vn_getattr, + .setattr = xfs_vn_setattr, + .setxattr = xfs_vn_setxattr, + .getxattr = xfs_vn_getxattr, + .listxattr = xfs_vn_listxattr, + .removexattr = xfs_vn_removexattr, +}; + const struct inode_operations xfs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = xfs_vn_follow_link, =========================================================================== fs/xfs/linux-2.6/xfs_iops.h =========================================================================== --- a/fs/xfs/linux-2.6/xfs_iops.h 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/linux-2.6/xfs_iops.h 2007-10-26 13:19:11.702517171 +1000 @@ -20,6 +20,7 @@ extern const struct inode_operations xfs_inode_operations; extern const struct inode_operations xfs_dir_inode_operations; +extern const struct inode_operations xfs_dir_ci_inode_operations; extern const struct inode_operations xfs_symlink_inode_operations; extern const struct file_operations xfs_file_operations; =========================================================================== fs/xfs/linux-2.6/xfs_ksyms.c =========================================================================== --- a/fs/xfs/linux-2.6/xfs_ksyms.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/linux-2.6/xfs_ksyms.c 2008-01-21 14:27:33.000000000 +1100 @@ -157,6 +157,7 @@ EXPORT_SYMBOL(kmem_zone_init); EXPORT_SYMBOL(kmem_zone_zalloc); EXPORT_SYMBOL(xfs_address_space_operations); EXPORT_SYMBOL(xfs_dir_inode_operations); +EXPORT_SYMBOL(xfs_dir_ci_inode_operations); EXPORT_SYMBOL(xfs_dir_file_operations); EXPORT_SYMBOL(xfs_inode_operations); EXPORT_SYMBOL(xfs_file_operations); =========================================================================== fs/xfs/linux-2.6/xfs_linux.h =========================================================================== --- a/fs/xfs/linux-2.6/xfs_linux.h 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/linux-2.6/xfs_linux.h 2008-01-11 14:49:16.537591564 +1100 @@ -75,6 +75,8 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/spinlock.h> +#include <linux/ctype.h> +#include <linux/nls.h> #include <asm/page.h> #include <asm/div64.h> @@ -180,6 +182,12 @@ #define howmany(x, y) (((x)+((y)-1))/(y)) /* + * NLS UTF-8 character set + */ + +#define XFS_NLS_UTF8 "utf8" + +/* * Various platform dependent calls that don't fit anywhere else */ #define xfs_sort(a,n,s,fn) sort(a,n,s,fn,NULL) =========================================================================== fs/xfs/linux-2.6/xfs_super.c =========================================================================== --- a/fs/xfs/linux-2.6/xfs_super.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/linux-2.6/xfs_super.c 2008-01-21 14:27:33.000000000 +1100 @@ -50,6 +50,7 @@ #include "xfs_vnodeops.h" #include "xfs_vfsops.h" #include "xfs_version.h" +#include "xfs_unicode.h" #include "xfs_log_priv.h" #include "xfs_trans_priv.h" @@ -122,6 +123,9 @@ xfs_args_allocate( #define MNTOPT_ATTR2 "attr2" /* do use attr2 attribute format */ #define MNTOPT_NOATTR2 "noattr2" /* do not use attr2 attribute format */ #define MNTOPT_FILESTREAM "filestreams" /* use filestreams allocator */ +#define MNTOPT_NLS "nls" /* NLS code page to use */ +#define MNTOPT_CILOOKUP "ci" /* case-insensitive dir names */ +#define MNTOPT_CIATTR "ciattr" /* case-insensitive attr names */ #define MNTOPT_QUOTA "quota" /* disk quotas (user) */ #define MNTOPT_NOQUOTA "noquota" /* no quotas */ #define MNTOPT_USRQUOTA "usrquota" /* user quota enabled */ @@ -316,6 +320,18 @@ xfs_parseargs( args->flags &= ~XFSMNT_ATTR2; } else if (!strcmp(this_char, MNTOPT_FILESTREAM)) { args->flags2 |= XFSMNT2_FILESTREAMS; + } else if (!strcmp(this_char, MNTOPT_NLS)) { + if (!value || !*value) { + cmn_err(CE_WARN, + "XFS: %s option requires an argument", + this_char); + return EINVAL; + } + strncpy(args->nls, value, MAXNAMELEN); + } else if (!strcmp(this_char, MNTOPT_CILOOKUP)) { + args->flags2 |= XFSMNT2_CILOOKUP; + } else if (!strcmp(this_char, MNTOPT_CIATTR)) { + args->flags2 |= XFSMNT2_CIATTR; } else if (!strcmp(this_char, MNTOPT_NOQUOTA)) { args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA); args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA); @@ -455,6 +471,8 @@ xfs_showargs( { XFS_MOUNT_OSYNCISOSYNC, "," MNTOPT_OSYNCISOSYNC }, { XFS_MOUNT_ATTR2, "," MNTOPT_ATTR2 }, { XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM }, + { XFS_MOUNT_CI_LOOKUP, "," MNTOPT_CILOOKUP }, + { XFS_MOUNT_CI_ATTR, "," MNTOPT_CIATTR }, { XFS_MOUNT_DMAPI, "," MNTOPT_DMAPI }, { XFS_MOUNT_GRPID, "," MNTOPT_GRPID }, { 0, NULL } @@ -517,6 +535,13 @@ xfs_showargs( if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT)) seq_puts(m, "," MNTOPT_NOQUOTA); + if (xfs_sb_version_hasunicode(&mp->m_sb)) { + if (mp->m_nls) + seq_printf(m, "," MNTOPT_NLS "=%s", mp->m_nls->charset); + else + seq_puts(m, "," MNTOPT_NLS "=" XFS_NLS_UTF8); + } + return 0; } __uint64_t @@ -564,7 +589,11 @@ xfs_set_inodeops( inode->i_mapping->a_ops = &xfs_address_space_operations; break; case S_IFDIR: - inode->i_op = &xfs_dir_inode_operations; + inode->i_op = + xfs_sb_version_hasoldci(&XFS_I(inode)->i_mount->m_sb) || + (XFS_I(inode)->i_mount->m_flags & XFS_MOUNT_CI_LOOKUP) ? + &xfs_dir_ci_inode_operations : + &xfs_dir_inode_operations; inode->i_fop = &xfs_dir_file_operations; break; case S_IFLNK: =========================================================================== fs/xfs/xfs_attr.c =========================================================================== --- a/fs/xfs/xfs_attr.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_attr.c 2008-01-18 13:25:20.068339942 +1100 @@ -106,6 +106,17 @@ ktrace_t *xfs_attr_trace_buf; * Overall external interface routines. *========================================================================*/ +void +xfs_attr_mount(struct xfs_mount *mp) +{ + mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100; + if (xfs_sb_version_hasunicode(&mp->m_sb)) { + mp->m_attrnameops = (mp->m_flags & XFS_MOUNT_CI_ATTR) ? + &xfs_unicode_ci_nameops : &xfs_unicode_nameops; + } else + mp->m_attrnameops = &xfs_default_nameops; +} + int xfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen, char *value, int *valuelenp, int flags, struct cred *cred) @@ -122,14 +133,14 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch * Fill in the arg structure for this request. */ memset((char *)&args, 0, sizeof(args)); - args.name = name; - args.namelen = namelen; args.value = value; args.valuelen = *valuelenp; args.flags = flags; - args.hashval = xfs_da_hashname(args.name, args.namelen); args.dp = ip; args.whichfork = XFS_ATTR_FORK; + error = xfs_da_setup_name_and_hash(&args, name, namelen); + if (error) + return error; /* * Decide on what work routines to call based on the inode size. @@ -153,6 +164,7 @@ xfs_attr_fetch(xfs_inode_t *ip, const ch if (error == EEXIST) error = 0; + xfs_da_cleanup_name(&args, name); return(error); } @@ -181,6 +193,7 @@ xfs_attr_get( xfs_ilock(ip, XFS_ILOCK_SHARED); error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred); xfs_iunlock(ip, XFS_ILOCK_SHARED); + return(error); } @@ -219,18 +232,18 @@ xfs_attr_set_int(xfs_inode_t *dp, const * Fill in the arg structure for this request. */ memset((char *)&args, 0, sizeof(args)); - args.name = name; - args.namelen = namelen; args.value = value; args.valuelen = valuelen; args.flags = flags; - args.hashval = xfs_da_hashname(args.name, args.namelen); args.dp = dp; args.firstblock = &firstblock; args.flist = &flist; args.whichfork = XFS_ATTR_FORK; args.addname = 1; args.oknoent = 1; + error = xfs_da_setup_name_and_hash(&args, name, namelen); + if (error) + return error; /* * Determine space new attribute will use, and if it would be @@ -282,6 +295,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const 0, XFS_TRANS_PERM_LOG_RES, XFS_ATTRSET_LOG_COUNT))) { xfs_trans_cancel(args.trans, 0); + xfs_da_cleanup_name(&args, name); return(error); } xfs_ilock(dp, XFS_ILOCK_EXCL); @@ -292,6 +306,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const if (error) { xfs_iunlock(dp, XFS_ILOCK_EXCL); xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES); + xfs_da_cleanup_name(&args, name); return (error); } @@ -342,6 +357,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const if (!error && (flags & ATTR_KERNOTIME) == 0) { xfs_ichgtime(dp, XFS_ICHGTIME_CHG); } + xfs_da_cleanup_name(&args, name); return(error == 0 ? err2 : error); } @@ -411,6 +427,7 @@ xfs_attr_set_int(xfs_inode_t *dp, const xfs_ichgtime(dp, XFS_ICHGTIME_CHG); } + xfs_da_cleanup_name(&args, name); return(error); out: @@ -418,6 +435,7 @@ out: xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); xfs_iunlock(dp, XFS_ILOCK_EXCL); + xfs_da_cleanup_name(&args, name); return(error); } @@ -460,21 +478,23 @@ xfs_attr_remove_int(xfs_inode_t *dp, con * Fill in the arg structure for this request. */ memset((char *)&args, 0, sizeof(args)); - args.name = name; - args.namelen = namelen; args.flags = flags; - args.hashval = xfs_da_hashname(args.name, args.namelen); args.dp = dp; args.firstblock = &firstblock; args.flist = &flist; args.total = 0; args.whichfork = XFS_ATTR_FORK; + error = xfs_da_setup_name_and_hash(&args, name, namelen); + if (error) + return error; /* * Attach the dquots to the inode. */ - if ((error = XFS_QM_DQATTACH(mp, dp, 0))) + if ((error = XFS_QM_DQATTACH(mp, dp, 0))) { + xfs_da_cleanup_name(&args, name); return (error); + } /* * Start our first transaction of the day. @@ -502,6 +522,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con 0, XFS_TRANS_PERM_LOG_RES, XFS_ATTRRM_LOG_COUNT))) { xfs_trans_cancel(args.trans, 0); + xfs_da_cleanup_name(&args, name); return(error); } @@ -559,6 +580,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, con xfs_ichgtime(dp, XFS_ICHGTIME_CHG); } + xfs_da_cleanup_name(&args, name); return(error); out: @@ -566,6 +588,7 @@ out: xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); xfs_iunlock(dp, XFS_ILOCK_EXCL); + xfs_da_cleanup_name(&args, name); return(error); } @@ -634,7 +657,7 @@ xfs_attr_list_int(xfs_attr_list_context_ */ /*ARGSUSED*/ STATIC int -xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp, +xfs_attr_user_list(xfs_attr_list_context_t *context, attrnames_t *namesp, char *name, int namelen, int valuelen, char *value) { @@ -765,7 +788,7 @@ xfs_attr_list( context.alist->al_count = 0; context.alist->al_more = 0; context.alist->al_offset[0] = context.bufsize; - context.put_listent = xfs_attr_put_listent; + context.put_listent = xfs_attr_user_list; } if (XFS_FORCED_SHUTDOWN(dp->i_mount)) =========================================================================== fs/xfs/xfs_attr_leaf.c =========================================================================== --- a/fs/xfs/xfs_attr_leaf.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_attr_leaf.c 2008-01-21 14:07:01.982908019 +1100 @@ -42,6 +42,7 @@ #include "xfs_attr.h" #include "xfs_attr_leaf.h" #include "xfs_error.h" +#include "xfs_unicode.h" /* * xfs_attr_leaf.c @@ -90,6 +91,10 @@ STATIC void xfs_attr_leaf_moveents(xfs_a xfs_mount_t *mp); STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index); +STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context, + attrnames_t *namesp, char *name, + int namelen, int valuelen, char *value); + /*======================================================================== * Namespace helper routines *========================================================================*/ @@ -135,6 +140,38 @@ xfs_attr_namesp_match_overrides(int arg_ * External routines when attribute fork size < XFS_LITINO(mp). *========================================================================*/ +STATIC xfs_attr_sf_entry_t * +xfs_attr_shortform_find_ent(xfs_da_args_t *args) +{ + xfs_attr_shortform_t *sf; + xfs_attr_sf_entry_t *sfe; + int i; + xfs_attr_sf_entry_t *ci_sfe = NULL; + + ASSERT(args->dp->i_afp->if_flags & XFS_IFINLINE); + sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data; + sfe = &sf->list[0]; + + args->cmpresult = XFS_CMP_DIFFERENT; + for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { + if (!xfs_attr_namesp_match(args->flags, sfe->flags)) + continue; + switch (xfs_attr_compname(args->dp, sfe->nameval, sfe->namelen, + args->name, args->namelen)) { + case XFS_CMP_EXACT: + args->cmpresult = XFS_CMP_EXACT; + return sfe; + case XFS_CMP_CASE: + if (!ci_sfe) { + args->cmpresult = XFS_CMP_CASE; + ci_sfe = sfe; + } + default:; + } + } + return ci_sfe; +} + /* * Query whether the requested number of additional bytes of extended * attribute space will be able to fit inline. @@ -295,13 +332,10 @@ xfs_attr_shortform_add(xfs_da_args_t *ar sfe = &sf->list[0]; for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { #ifdef DEBUG - if (sfe->namelen != args->namelen) - continue; - if (memcmp(args->name, sfe->nameval, args->namelen) != 0) - continue; if (!xfs_attr_namesp_match(args->flags, sfe->flags)) continue; - ASSERT(0); + ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen, + sfe->nameval, sfe->namelen) == XFS_CMP_DIFFERENT); #endif } @@ -331,29 +365,19 @@ xfs_attr_shortform_remove(xfs_da_args_t { xfs_attr_shortform_t *sf; xfs_attr_sf_entry_t *sfe; - int base, size=0, end, totsize, i; + int base, size, end, totsize; xfs_mount_t *mp; xfs_inode_t *dp; + sfe = xfs_attr_shortform_find_ent(args); + if (!sfe) + return XFS_ERROR(ENOATTR); + dp = args->dp; mp = dp->i_mount; - base = sizeof(xfs_attr_sf_hdr_t); sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data; - sfe = &sf->list[0]; - end = sf->hdr.count; - for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), - base += size, i++) { - size = XFS_ATTR_SF_ENTSIZE(sfe); - if (sfe->namelen != args->namelen) - continue; - if (memcmp(sfe->nameval, args->name, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, sfe->flags)) - continue; - break; - } - if (i == end) - return(XFS_ERROR(ENOATTR)); + size = XFS_ATTR_SF_ENTSIZE(sfe); + base = (int)((char *)sfe - (char *)sf); /* * Fix up the attribute fork data, covering the hole @@ -412,26 +436,7 @@ xfs_attr_shortform_remove(xfs_da_args_t int xfs_attr_shortform_lookup(xfs_da_args_t *args) { - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - int i; - xfs_ifork_t *ifp; - - ifp = args->dp->i_afp; - ASSERT(ifp->if_flags & XFS_IFINLINE); - sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; - sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { - if (sfe->namelen != args->namelen) - continue; - if (memcmp(args->name, sfe->nameval, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, sfe->flags)) - continue; - return(XFS_ERROR(EEXIST)); - } - return(XFS_ERROR(ENOATTR)); + return XFS_ERROR(xfs_attr_shortform_find_ent(args) ? EEXIST : ENOATTR); } /* @@ -441,35 +446,24 @@ xfs_attr_shortform_lookup(xfs_da_args_t int xfs_attr_shortform_getvalue(xfs_da_args_t *args) { - xfs_attr_shortform_t *sf; xfs_attr_sf_entry_t *sfe; - int i; - ASSERT(args->dp->i_d.di_aformat == XFS_IFINLINE); - sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data; - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; - sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { - if (sfe->namelen != args->namelen) - continue; - if (memcmp(args->name, sfe->nameval, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, sfe->flags)) - continue; - if (args->flags & ATTR_KERNOVAL) { - args->valuelen = sfe->valuelen; - return(XFS_ERROR(EEXIST)); - } - if (args->valuelen < sfe->valuelen) { - args->valuelen = sfe->valuelen; - return(XFS_ERROR(ERANGE)); - } + sfe = xfs_attr_shortform_find_ent(args); + if (!sfe) + return XFS_ERROR(ENOATTR); + + if (args->flags & ATTR_KERNOVAL) { args->valuelen = sfe->valuelen; - memcpy(args->value, &sfe->nameval[args->namelen], - args->valuelen); - return(XFS_ERROR(EEXIST)); + return XFS_ERROR(EEXIST); } - return(XFS_ERROR(ENOATTR)); + if (args->valuelen < sfe->valuelen) { + args->valuelen = sfe->valuelen; + return XFS_ERROR(ERANGE); + } + args->valuelen = sfe->valuelen; + memcpy(args->value, &sfe->nameval[args->namelen], args->valuelen); + + return XFS_ERROR(EEXIST); } /* @@ -535,17 +529,18 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t sfe = &sf->list[0]; for (i = 0; i < sf->hdr.count; i++) { - nargs.name = (char *)sfe->nameval; - nargs.namelen = sfe->namelen; - nargs.value = (char *)&sfe->nameval[nargs.namelen]; + nargs.value = (char *)&sfe->nameval[sfe->namelen]; nargs.valuelen = sfe->valuelen; - nargs.hashval = xfs_da_hashname((char *)sfe->nameval, - sfe->namelen); + error = xfs_da_setup_name_and_hash(&nargs, sfe->nameval, + sfe->namelen); + if (error) + goto out; nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(sfe->flags); error = xfs_attr_leaf_lookup_int(bp, &nargs); /* set a->index */ ASSERT(error == ENOATTR); error = xfs_attr_leaf_add(bp, &nargs); ASSERT(error != ENOSPC); + xfs_da_cleanup_name(&nargs, sfe->nameval); if (error) goto out; sfe = XFS_ATTR_SF_NEXTENTRY(sfe); @@ -631,7 +626,7 @@ xfs_attr_shortform_list(xfs_attr_list_co continue; } namesp = xfs_attr_flags_namesp(sfe->flags); - error = context->put_listent(context, + error = xfs_attr_put_listent(context, namesp, (char *)sfe->nameval, (int)sfe->namelen, @@ -734,7 +729,7 @@ xfs_attr_shortform_list(xfs_attr_list_co cursor->hashval = sbp->hash; cursor->offset = 0; } - error = context->put_listent(context, + error = xfs_attr_put_listent(context, namesp, sbp->name, sbp->namelen, @@ -1960,6 +1955,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp xfs_attr_leaf_name_remote_t *name_rmt; int probe, span; xfs_dahash_t hashval; + xfs_dacmp_t cmp; leaf = bp->data; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_ATTR_LEAF_MAGIC); @@ -2008,6 +2004,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp /* * Duplicate keys may be present, so search all of them for a match. */ + args->cmpresult = XFS_CMP_DIFFERENT; for ( ; (probe < be16_to_cpu(leaf->hdr.count)) && (be32_to_cpu(entry->hashval) == hashval); entry++, probe++) { @@ -2019,35 +2016,46 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp * If we are looking for complete entries, show only those. */ if ((args->flags & XFS_ATTR_INCOMPLETE) != - (entry->flags & XFS_ATTR_INCOMPLETE)) { + (entry->flags & XFS_ATTR_INCOMPLETE)) + continue; + if (!xfs_attr_namesp_match(args->flags, entry->flags)) continue; - } if (entry->flags & XFS_ATTR_LOCAL) { name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe); - if (name_loc->namelen != args->namelen) - continue; - if (memcmp(args->name, (char *)name_loc->nameval, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, entry->flags)) - continue; - args->index = probe; - return(XFS_ERROR(EEXIST)); + cmp = xfs_attr_compname(args->dp, args->name, + args->namelen, name_loc->nameval, + name_loc->namelen); + if (cmp != XFS_CMP_DIFFERENT && + cmp != args->cmpresult) { + args->cmpresult = cmp; + args->index = probe; + args->rmtblkno = 0; + args->rmtblkcnt = 0; + if (cmp == XFS_CMP_EXACT) + return XFS_ERROR(EEXIST); + } } else { name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe); - if (name_rmt->namelen != args->namelen) - continue; - if (memcmp(args->name, (char *)name_rmt->name, - args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, entry->flags)) - continue; - args->index = probe; - args->rmtblkno = be32_to_cpu(name_rmt->valueblk); - args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, - be32_to_cpu(name_rmt->valuelen)); - return(XFS_ERROR(EEXIST)); + cmp = xfs_attr_compname(args->dp, args->name, + args->namelen, name_rmt->name, + name_rmt->namelen); + if (cmp != XFS_CMP_DIFFERENT && + cmp != args->cmpresult) { + args->cmpresult = cmp; + args->index = probe; + args->rmtblkno = + be32_to_cpu(name_rmt->valueblk); + args->rmtblkcnt = + XFS_B_TO_FSB(args->dp->i_mount, + be32_to_cpu(name_rmt->valuelen)); + if (cmp == XFS_CMP_EXACT) + return XFS_ERROR(EEXIST); + } } } + if (args->cmpresult == XFS_CMP_CASE) + return XFS_ERROR(EEXIST); + args->index = probe; return(XFS_ERROR(ENOATTR)); } @@ -2074,8 +2082,9 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, entry = &leaf->entries[args->index]; if (entry->flags & XFS_ATTR_LOCAL) { name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index); - ASSERT(name_loc->namelen == args->namelen); - ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0); + ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen, + name_loc->nameval, name_loc->namelen) != + XFS_CMP_DIFFERENT); valuelen = be16_to_cpu(name_loc->valuelen); if (args->flags & ATTR_KERNOVAL) { args->valuelen = valuelen; @@ -2086,11 +2095,13 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, return(XFS_ERROR(ERANGE)); } args->valuelen = valuelen; - memcpy(args->value, &name_loc->nameval[args->namelen], valuelen); + memcpy(args->value, &name_loc->nameval[args->namelen], + valuelen); } else { name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index); - ASSERT(name_rmt->namelen == args->namelen); - ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0); + ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen, + name_rmt->nameval, name_rmt->namelen) != + XFS_CMP_DIFFERENT); valuelen = be32_to_cpu(name_rmt->valuelen); args->rmtblkno = be32_to_cpu(name_rmt->valueblk); args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, valuelen); @@ -2298,7 +2309,8 @@ xfs_attr_leaf_lasthash(xfs_dabuf_t *bp, *count = be16_to_cpu(leaf->hdr.count); if (!leaf->hdr.count) return(0); - return be32_to_cpu(leaf->entries[be16_to_cpu(leaf->hdr.count)-1].hashval); + return be32_to_cpu( + leaf->entries[be16_to_cpu(leaf->hdr.count)-1].hashval); } /* @@ -2316,7 +2328,7 @@ xfs_attr_leaf_entsize(xfs_attr_leafblock if (leaf->entries[index].flags & XFS_ATTR_LOCAL) { name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, index); size = XFS_ATTR_LEAF_ENTSIZE_LOCAL(name_loc->namelen, - be16_to_cpu(name_loc->valuelen)); + be16_to_cpu(name_loc->valuelen)); } else { name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, index); size = XFS_ATTR_LEAF_ENTSIZE_REMOTE(name_rmt->namelen); @@ -2409,7 +2421,8 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, if (entry->flags & XFS_ATTR_INCOMPLETE) continue; /* skip incomplete entries */ - if (!xfs_attr_namesp_match_overrides(context->flags, entry->flags)) + if (!xfs_attr_namesp_match_overrides(context->flags, + entry->flags)) continue; namesp = xfs_attr_flags_namesp(entry->flags); @@ -2418,12 +2431,13 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, xfs_attr_leaf_name_local_t *name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, i); - retval = context->put_listent(context, + retval = xfs_attr_put_listent(context, namesp, (char *)name_loc->nameval, (int)name_loc->namelen, be16_to_cpu(name_loc->valuelen), - (char *)&name_loc->nameval[name_loc->namelen]); + (char *)&name_loc-> + nameval[name_loc->namelen]); if (retval) return retval; } else { @@ -2441,11 +2455,12 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, args.valuelen = valuelen; args.value = kmem_alloc(valuelen, KM_SLEEP); args.rmtblkno = be32_to_cpu(name_rmt->valueblk); - args.rmtblkcnt = XFS_B_TO_FSB(args.dp->i_mount, valuelen); + args.rmtblkcnt = XFS_B_TO_FSB(args.dp->i_mount, + valuelen); retval = xfs_attr_rmtval_get(&args); if (retval) return retval; - retval = context->put_listent(context, + retval = xfs_attr_put_listent(context, namesp, (char *)name_rmt->name, (int)name_rmt->namelen, @@ -2454,7 +2469,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, kmem_free(args.value, valuelen); } else { - retval = context->put_listent(context, + retval = xfs_attr_put_listent(context, namesp, (char *)name_rmt->name, (int)name_rmt->namelen, @@ -2472,6 +2487,33 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, return(retval); } +/* + * Do NLS name conversion if required for attribute name and call + * context's put_listent routine + */ + +STATIC int +xfs_attr_put_listent(xfs_attr_list_context_t *context, attrnames_t *namesp, + char *name, int namelen, int valuelen, char *value) +{ + xfs_mount_t *mp = context->dp->i_mount; + char *nls_name = NULL; + int nls_namelen; + int rval; + + if (!mp->m_nls) + return context->put_listent(context, namesp, name, namelen, + valuelen, value); + + rval = xfs_unicode_to_nls(mp->m_nls, name, namelen, &nls_name, + &nls_namelen); + if (rval != 0) + return rval; + rval = context->put_listent(context, namesp, nls_name, nls_namelen, + valuelen, value); + xfs_free_unicode_nls_name(nls_name); + return rval; +} /*======================================================================== * Manage the INCOMPLETE flag in a leaf entry @@ -2522,8 +2564,8 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *a name = (char *)name_rmt->name; } ASSERT(be32_to_cpu(entry->hashval) == args->hashval); - ASSERT(namelen == args->namelen); - ASSERT(memcmp(name, args->name, namelen) == 0); + ASSERT(xfs_attr_compname(args->dp, args->name, args->namelen, + name, namelen) != XFS_CMP_DIFFERENT); #endif /* DEBUG */ entry->flags &= ~XFS_ATTR_INCOMPLETE; @@ -2674,8 +2716,8 @@ xfs_attr_leaf_flipflags(xfs_da_args_t *a name2 = (char *)name_rmt->name; } ASSERT(be32_to_cpu(entry1->hashval) == be32_to_cpu(entry2->hashval)); - ASSERT(namelen1 == namelen2); - ASSERT(memcmp(name1, name2, namelen1) == 0); + ASSERT(xfs_attr_compname(args->dp, name1, namelen1, name2, namelen2) != + XFS_CMP_DIFFERENT); #endif /* DEBUG */ ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE); @@ -2834,7 +2876,8 @@ xfs_attr_node_inactive(xfs_trans_t **tra if (be16_to_cpu(info->magic) == XFS_DA_NODE_MAGIC) { error = xfs_attr_node_inactive(trans, dp, child_bp, level+1); - } else if (be16_to_cpu(info->magic) == XFS_ATTR_LEAF_MAGIC) { + } else if (be16_to_cpu(info->magic) == + XFS_ATTR_LEAF_MAGIC) { error = xfs_attr_leaf_inactive(trans, dp, child_bp); } else { @@ -2935,7 +2978,7 @@ xfs_attr_leaf_inactive(xfs_trans_t **tra if (name_rmt->valueblk) { lp->valueblk = be32_to_cpu(name_rmt->valueblk); lp->valuelen = XFS_B_TO_FSB(dp->i_mount, - be32_to_cpu(name_rmt->valuelen)); + be32_to_cpu(name_rmt->valuelen)); lp++; } } =========================================================================== fs/xfs/xfs_attr_leaf.h =========================================================================== --- a/fs/xfs/xfs_attr_leaf.h 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_attr_leaf.h 2008-01-11 14:16:44.268796245 +1100 @@ -36,6 +36,7 @@ struct xfs_da_args; struct xfs_da_state; struct xfs_da_state_blk; struct xfs_inode; +struct xfs_mount; struct xfs_trans; /*======================================================================== @@ -204,6 +205,16 @@ static inline int xfs_attr_leaf_entsize_ return (((bsize) >> 1) + ((bsize) >> 2)); } +/* + * Do hash and name compare based on nameops + */ +#define xfs_attr_hashname(dp, n, l) \ + ((dp)->i_mount->m_attrnameops->hashname((dp), (n), (l))) + +#define xfs_attr_compname(dp, n1, l1, n2, l2) \ + ((dp)->i_mount->m_attrnameops->compname((dp), (n1), (l1), \ + (n2), (l2))) + /*======================================================================== * Structure used to pass context around among the routines. @@ -296,6 +307,7 @@ int xfs_attr_root_inactive(struct xfs_tr /* * Utility routines. */ +void xfs_attr_mount(struct xfs_mount *mp); xfs_dahash_t xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count); int xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp, struct xfs_dabuf *leaf2_bp); =========================================================================== fs/xfs/xfs_clnt.h =========================================================================== --- a/fs/xfs/xfs_clnt.h 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_clnt.h 2007-11-01 13:16:55.000383139 +1100 @@ -48,6 +48,7 @@ struct xfs_mount_args { char rtname[MAXNAMELEN+1]; /* realtime device filename */ char logname[MAXNAMELEN+1]; /* journal device filename */ char mtpt[MAXNAMELEN+1]; /* filesystem mount point */ + char nls[MAXNAMELEN+1]; /* NLS code page to use */ int sunit; /* stripe unit (BBs) */ int swidth; /* stripe width (BBs), multiple of sunit */ uchar_t iosizelog; /* log2 of the preferred I/O size */ @@ -100,5 +101,9 @@ struct xfs_mount_args { * I/O size in stat(2) */ #define XFSMNT2_FILESTREAMS 0x00000002 /* enable the filestreams * allocator */ +#define XFSMNT2_CILOOKUP 0x00000004 /* enable case-insensitive + * filename lookup */ +#define XFSMNT2_CIATTR 0x00000008 /* enable case-insensitive + * extended attr names */ #endif /* __XFS_CLNT_H__ */ =========================================================================== fs/xfs/xfs_da_btree.c =========================================================================== --- a/fs/xfs/xfs_da_btree.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_da_btree.c 2008-01-21 14:23:59.544205024 +1100 @@ -46,6 +46,7 @@ #include "xfs_dir2_block.h" #include "xfs_dir2_node.h" #include "xfs_error.h" +#include "xfs_unicode.h" /* * xfs_da_btree.c @@ -1530,6 +1531,106 @@ xfs_da_hashname(const uchar_t *name, int } } + +static xfs_dahash_t +xfs_default_hashname(xfs_inode_t *inode, const uchar_t *name, int namelen) +{ + return xfs_da_hashname(name, namelen); +} + +xfs_dacmp_t +xfs_default_compname(xfs_inode_t *inode, const uchar_t *name1, int len1, + const uchar_t *name2, int len2) +{ + return (len1 == len2 && memcmp(name1, name2, len1) == 0) ? + XFS_CMP_EXACT : XFS_CMP_DIFFERENT; +} + +static xfs_dahash_t +xfs_unicode_ci_hashname( + xfs_inode_t *inode, + const uchar_t *name, + int namelen) +{ + return xfs_unicode_hash(inode->i_mount->m_cft, name, namelen); +} + +static xfs_dacmp_t +xfs_unicode_ci_compname( + xfs_inode_t *inode, + const uchar_t *name1, + int len1, + const uchar_t *name2, + int len2) +{ + if (len1 == len2 && memcmp(name1, name2, len1) == 0) + return XFS_CMP_EXACT; + + return xfs_unicode_casecmp(inode->i_mount->m_cft, name1, len1, + name2, len2) == 0 ? XFS_CMP_CASE : XFS_CMP_DIFFERENT; +} + +const struct xfs_nameops xfs_default_nameops = { + .hashname = xfs_default_hashname, + .compname = xfs_default_compname +}; + +const struct xfs_nameops xfs_unicode_nameops = { + .hashname = xfs_unicode_ci_hashname, + .compname = xfs_default_compname, +}; + +const struct xfs_nameops xfs_unicode_ci_nameops = { + .hashname = xfs_unicode_ci_hashname, + .compname = xfs_unicode_ci_compname, +}; + +int +xfs_da_setup_name_and_hash( + xfs_da_args_t *args, + const char *name, + int namelen) +{ + xfs_mount_t *mp = args->dp->i_mount; + int error; + + /* + * xfs_nls_to_unicode will return an allocated string in + * args->name. Therefore, all calls to xfs_da_setup_name_and_hash + * needs a corresponding call to xfs_da_cleanup_name. + */ + + if (mp->m_nls) { + args->name = NULL; + error = xfs_nls_to_unicode(mp->m_nls, name, namelen, + (char **)&args->name, &args->namelen); + if (error) + return error; + } else { + if (xfs_sb_version_hasunicode(&mp->m_sb)) { + error = xfs_unicode_validate(name, namelen); + if (error) + return error; + } + args->name = name; + args->namelen = namelen; + } + args->hashval = (args->whichfork == XFS_ATTR_FORK) ? + xfs_attr_hashname(args->dp, args->name, args->namelen) : + xfs_dir_hashname(args->dp, args->name, args->namelen); + return 0; +} + +void +xfs_da_cleanup_name( + xfs_da_args_t *args, + const char *name) +{ + if ((char *)args->name != name) + xfs_free_unicode_nls_name((char *)args->name); +} + + /* * Add a block to the btree ahead of the file. * Return the new block number to the caller. =========================================================================== fs/xfs/xfs_da_btree.h =========================================================================== --- a/fs/xfs/xfs_da_btree.h 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_da_btree.h 2008-01-14 12:12:32.917055949 +1100 @@ -99,12 +99,21 @@ typedef struct xfs_da_node_entry xfs_da_ *========================================================================*/ /* + * Search comparison results + */ +typedef enum { + XFS_CMP_DIFFERENT, /* names are completely different */ + XFS_CMP_EXACT, /* names are exactly the same */ + XFS_CMP_CASE /* names are same but differ in case */ +} xfs_dacmp_t; + +/* * Structure to ease passing around component names. */ typedef struct xfs_da_args { const uchar_t *name; /* string (maybe not NULL terminated) */ int namelen; /* length of string (maybe no NULL) */ - uchar_t *value; /* set of bytes (maybe contain NULLs) */ + uchar_t *value; /* set of bytes (maybe contain NULLs) */ int valuelen; /* length of value */ int flags; /* argument flags (eg: ATTR_NOCREATE) */ xfs_dahash_t hashval; /* hash value of name */ @@ -127,6 +136,7 @@ typedef struct xfs_da_args { unsigned char rename; /* T/F: this is an atomic rename op */ unsigned char addname; /* T/F: this is an add operation */ unsigned char oknoent; /* T/F: ok to return ENOENT, else die */ + xfs_dacmp_t cmpresult; /* name compare result for lookups */ } xfs_da_args_t; /* @@ -201,12 +211,37 @@ typedef struct xfs_da_state { (uint)(XFS_DA_LOGOFF(BASE, ADDR)), \ (uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1) +/* + * Name ops for directory and/or attr name operations + */ + +typedef xfs_dahash_t (*xfs_hashname_t)(struct xfs_inode *, const uchar_t *, + int); +typedef xfs_dacmp_t (*xfs_compname_t)(struct xfs_inode *, const uchar_t *, + int, const uchar_t *, int); + +typedef struct xfs_nameops { + xfs_hashname_t hashname; + xfs_compname_t compname; +} xfs_nameops_t; + #ifdef __KERNEL__ /*======================================================================== * Function prototypes for the kernel. *========================================================================*/ +extern const struct xfs_nameops xfs_default_nameops; +extern const struct xfs_nameops xfs_unicode_nameops; +extern const struct xfs_nameops xfs_unicode_ci_nameops; + +xfs_dacmp_t xfs_default_compname(struct xfs_inode *inode, const uchar_t *name1, + int len1, const uchar_t *name2, int len2); + +int xfs_da_setup_name_and_hash(xfs_da_args_t *args, const char *name, + int namelen); +void xfs_da_cleanup_name(xfs_da_args_t *args, const char *name); + /* * Routines used for growing the Btree. */ =========================================================================== fs/xfs/xfs_dir2.c =========================================================================== --- a/fs/xfs/xfs_dir2.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_dir2.c 2008-01-21 13:21:20.375779062 +1100 @@ -42,8 +42,58 @@ #include "xfs_dir2_node.h" #include "xfs_dir2_trace.h" #include "xfs_error.h" +#include "xfs_unicode.h" #include "xfs_vnodeops.h" +/* + * V1/OLDCI case-insensitive support for directories + * + * This is ASCII only case support, ie. A-Z. + */ +static xfs_dahash_t +xfs_ascii_ci_hashname( + xfs_inode_t *inode, + const uchar_t *name, + int namelen) +{ + xfs_dahash_t hash; + int i; + + for (i = 0, hash = 0; i < namelen; i++) + hash = tolower(name[i]) ^ rol32(hash, 7); + + return hash; +} + +static xfs_dacmp_t +xfs_ascii_ci_compname( + xfs_inode_t *inode, + const uchar_t *name1, + int len1, + const uchar_t *name2, + int len2) +{ + xfs_dacmp_t result = XFS_CMP_EXACT; + int i; + + if (len1 != len2) + return XFS_CMP_DIFFERENT; + + for (i = 0; i < len1; i++) { + if (name1[i] == name2[i]) + continue; + if (tolower(name1[i]) != tolower(name2[i])) + return XFS_CMP_DIFFERENT; + result = XFS_CMP_CASE; + } + + return result; +} + +static const struct xfs_nameops xfs_ascii_ci_nameops = { + .hashname = xfs_ascii_ci_hashname, + .compname = xfs_ascii_ci_compname, +}; void xfs_dir_mount( @@ -64,6 +114,13 @@ 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; + + if (xfs_sb_version_hasunicode(&mp->m_sb)) { + mp->m_dirnameops = (mp->m_flags & XFS_MOUNT_CI_LOOKUP) ? + &xfs_unicode_ci_nameops : &xfs_unicode_nameops; + } else + mp->m_dirnameops = (xfs_sb_version_hasoldci(&mp->m_sb)) ? + &xfs_ascii_ci_nameops : &xfs_default_nameops; } /* @@ -140,7 +197,7 @@ xfs_dir_init( } /* - Enter a name in a directory. + * Enter a name in a directory. */ int xfs_dir_createname( @@ -162,9 +219,6 @@ xfs_dir_createname( return rval; XFS_STATS_INC(xs_dir_create); - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); args.inumber = inum; args.dp = dp; args.firstblock = first; @@ -174,32 +228,41 @@ xfs_dir_createname( args.trans = tp; args.justcheck = 0; args.addname = args.oknoent = 1; + rval = xfs_da_setup_name_and_hash(&args, name, namelen); + if (rval) + return rval; if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_addname(&args); else if ((rval = xfs_dir2_isblock(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_block_addname(&args); else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_leaf_addname(&args); else rval = xfs_dir2_node_addname(&args); +out: + xfs_da_cleanup_name(&args, name); return rval; } /* - * Lookup a name in a directory, give back the inode number. + * Lookup a name in a directory, give back the inode number and also + * actual name if case-insensitive match. */ + int xfs_dir_lookup( xfs_trans_t *tp, xfs_inode_t *dp, char *name, int namelen, - xfs_ino_t *inum) /* out: inode number */ + xfs_ino_t *inum, /* out: inode number */ + char **actual_name, /* out: actual name if different */ + int *actual_namelen) { xfs_da_args_t args; int rval; @@ -208,9 +271,9 @@ xfs_dir_lookup( ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); XFS_STATS_INC(xs_dir_lookup); - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); + if (actual_name) + *actual_name = NULL; + args.inumber = 0; args.dp = dp; args.firstblock = NULL; @@ -220,23 +283,39 @@ xfs_dir_lookup( args.trans = tp; args.justcheck = args.addname = 0; args.oknoent = 1; + args.value = NULL; /* value may contain actual name on return */ + args.valuelen = 0; + rval = xfs_da_setup_name_and_hash(&args, name, namelen); + if (rval) + return rval; if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_lookup(&args); else if ((rval = xfs_dir2_isblock(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_block_lookup(&args); else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_leaf_lookup(&args); else rval = xfs_dir2_node_lookup(&args); if (rval == EEXIST) rval = 0; - if (rval == 0) + if (rval == 0) { *inum = args.inumber; + if (args.value) { + ASSERT(args->cmpresult == XFS_CMP_CASE); + if (actual_name) { + *actual_namelen = args.valuelen, + *actual_name = args.value; + } else + xfs_free_unicode_nls_name(args.value); + } + } +out: + xfs_da_cleanup_name(&args, name); return rval; } @@ -261,9 +340,6 @@ xfs_dir_removename( ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); XFS_STATS_INC(xs_dir_remove); - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); args.inumber = ino; args.dp = dp; args.firstblock = first; @@ -272,19 +348,24 @@ xfs_dir_removename( args.whichfork = XFS_DATA_FORK; args.trans = tp; args.justcheck = args.addname = args.oknoent = 0; + rval = xfs_da_setup_name_and_hash(&args, name, namelen); + if (rval) + return rval; if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_removename(&args); else if ((rval = xfs_dir2_isblock(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_block_removename(&args); else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_leaf_removename(&args); else rval = xfs_dir2_node_removename(&args); +out: + xfs_da_cleanup_name(&args, name); return rval; } @@ -345,9 +426,6 @@ xfs_dir_replace( if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) return rval; - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); args.inumber = inum; args.dp = dp; args.firstblock = first; @@ -356,19 +434,24 @@ xfs_dir_replace( args.whichfork = XFS_DATA_FORK; args.trans = tp; args.justcheck = args.addname = args.oknoent = 0; + rval = xfs_da_setup_name_and_hash(&args, name, namelen); + if (rval) + return rval; if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_replace(&args); else if ((rval = xfs_dir2_isblock(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_block_replace(&args); else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_leaf_replace(&args); else rval = xfs_dir2_node_replace(&args); +out: + xfs_da_cleanup_name(&args, name); return rval; } @@ -388,9 +471,6 @@ xfs_dir_canenter( ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); args.inumber = 0; args.dp = dp; args.firstblock = NULL; @@ -399,19 +479,24 @@ xfs_dir_canenter( args.whichfork = XFS_DATA_FORK; args.trans = tp; args.justcheck = args.addname = args.oknoent = 1; + rval = xfs_da_setup_name_and_hash(&args, name, namelen); + if (rval) + return rval; if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_addname(&args); else if ((rval = xfs_dir2_isblock(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_block_addname(&args); else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) - return rval; + goto out; else if (v) rval = xfs_dir2_leaf_addname(&args); else rval = xfs_dir2_node_addname(&args); +out: + xfs_da_cleanup_name(&args, name); return rval; } =========================================================================== fs/xfs/xfs_dir2.h =========================================================================== --- a/fs/xfs/xfs_dir2.h 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_dir2.h 2007-11-01 13:11:00.206583735 +1100 @@ -72,7 +72,8 @@ extern int xfs_dir_createname(struct xfs xfs_fsblock_t *first, struct xfs_bmap_free *flist, xfs_extlen_t tot); extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp, - char *name, int namelen, xfs_ino_t *inum); + char *name, int namelen, xfs_ino_t *inum, + char **actual_name, int *actual_namelen); extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp, char *name, int namelen, xfs_ino_t ino, xfs_fsblock_t *first, @@ -85,6 +86,13 @@ extern int xfs_dir_canenter(struct xfs_t char *name, int namelen); extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino); +#define xfs_dir_hashname(dp, n, l) \ + ((dp)->i_mount->m_dirnameops->hashname((dp), (n), (l))) + +#define xfs_dir_compname(dp, n1, l1, n2, l2) \ + ((dp)->i_mount->m_dirnameops->compname((dp), (n1), (l1), \ + (n2), (l2))) + /* * Utility routines for v2 directories. */ =========================================================================== fs/xfs/xfs_dir2_block.c =========================================================================== --- a/fs/xfs/xfs_dir2_block.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_dir2_block.c 2008-01-21 13:50:21.175545312 +1100 @@ -38,6 +38,7 @@ #include "xfs_dir2_block.h" #include "xfs_dir2_trace.h" #include "xfs_error.h" +#include "xfs_unicode.h" /* * Local function prototypes. @@ -450,6 +451,8 @@ xfs_dir2_block_getdents( int wantoff; /* starting block offset */ xfs_ino_t ino; xfs_off_t cook; + char *nls_name = NULL; /* NLS name buffer */ + int nls_namelen = 0; mp = dp->i_mount; /* @@ -481,6 +484,9 @@ xfs_dir2_block_getdents( ptr = (char *)block->u; endptr = (char *)xfs_dir2_block_leaf_p(btp); + if (mp->m_nls) + nls_name = xfs_alloc_unicode_nls_name(); + /* * Loop over the data portion of the block. * Each object is a real entry (dep) or an unused one (dup). @@ -513,15 +519,24 @@ xfs_dir2_block_getdents( #if XFS_BIG_INUMS ino += mp->m_inoadd; #endif + if (mp->m_nls) { + error = xfs_unicode_to_nls(mp->m_nls, dep->name, + dep->namelen, &nls_name, &nls_namelen); + if (error) { + xfs_da_brelse(NULL, bp); + return error; + } + } /* * If it didn't fit, set the final offset to here & return. */ - if (filldir(dirent, dep->name, dep->namelen, cook, - ino, DT_UNKNOWN)) { + if (filldir(dirent, + nls_namelen ? nls_name : (char *)dep->name, + nls_namelen ? nls_namelen : dep->namelen, + cook, ino, DT_UNKNOWN)) { *offset = cook; - xfs_da_brelse(NULL, bp); - return 0; + goto out; } } @@ -530,6 +545,9 @@ xfs_dir2_block_getdents( * Set the offset to a non-existent block 1 and return. */ *offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0); +out: + if (mp->m_nls) + xfs_free_unicode_nls_name(nls_name); xfs_da_brelse(NULL, bp); return 0; } @@ -616,6 +634,14 @@ xfs_dir2_block_lookup( * Fill in inode number, release the block. */ args->inumber = be64_to_cpu(dep->inumber); + if (args->cmpresult == XFS_CMP_CASE) { + error = xfs_unicode_to_nls(mp->m_nls, dep->name, dep->namelen, + (char **)&args->value, &args->valuelen); + if (error) { + xfs_da_brelse(args->trans, bp); + return XFS_ERROR(error); + } + } xfs_da_brelse(args->trans, bp); return XFS_ERROR(EEXIST); } @@ -643,6 +669,7 @@ xfs_dir2_block_lookup_int( int mid; /* binary search current idx */ xfs_mount_t *mp; /* filesystem mount point */ xfs_trans_t *tp; /* transaction pointer */ + xfs_dacmp_t cmp; /* comparison result */ dp = args->dp; tp = args->trans; @@ -688,6 +715,7 @@ xfs_dir2_block_lookup_int( * Now loop forward through all the entries with the * right hash value looking for our name. */ + args->cmpresult = XFS_CMP_DIFFERENT; do { if ((addr = be32_to_cpu(blp[mid].address)) == XFS_DIR2_NULL_DATAPTR) continue; @@ -697,20 +725,34 @@ xfs_dir2_block_lookup_int( dep = (xfs_dir2_data_entry_t *) ((char *)block + xfs_dir2_dataptr_to_off(mp, addr)); /* - * Compare, if it's right give back buffer & entry number. - */ - if (dep->namelen == args->namelen && - dep->name[0] == args->name[0] && - memcmp(dep->name, args->name, args->namelen) == 0) { + * Compare, if it's right give back buffer & entry number: + * + * lookup case - use nameops; + * + * replace/remove case - as lookup has been already been + * performed, look for an exact match using the fast method + */ + cmp = args->oknoent ? + xfs_dir_compname(dp, dep->name, dep->namelen, + args->name, args->namelen) : + xfs_default_compname(dp, dep->name, dep->namelen, + args->name, args->namelen); + if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { + args->cmpresult = cmp; *bpp = bp; *entno = mid; - return 0; + if (cmp == XFS_CMP_EXACT) + return 0; } - } while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash); + } while (++mid < be32_to_cpu(btp->count) && + be32_to_cpu(blp[mid].hashval) == hash); + + ASSERT(args->oknoent); + if (args->cmpresult == XFS_CMP_CASE) + return 0; /* * No match, release the buffer and return ENOENT. */ - ASSERT(args->oknoent); xfs_da_brelse(tp, bp); return XFS_ERROR(ENOENT); } @@ -1187,8 +1229,8 @@ xfs_dir2_sf_to_block( tagp = xfs_dir2_data_entry_tag_p(dep); *tagp = cpu_to_be16((char *)dep - (char *)block); xfs_dir2_data_log_entry(tp, bp, dep); - blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname( - (char *)sfep->name, sfep->namelen)); + blp[2 + i].hashval = cpu_to_be32(xfs_dir_hashname(dp, + sfep->name, sfep->namelen)); blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp, (char *)dep - (char *)block)); offset = (int)((char *)(tagp + 1) - (char *)block); =========================================================================== fs/xfs/xfs_dir2_data.c =========================================================================== --- a/fs/xfs/xfs_dir2_data.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_dir2_data.c 2008-01-21 14:33:22.540095423 +1100 @@ -140,7 +140,8 @@ xfs_dir2_data_check( addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, (xfs_dir2_data_aoff_t) ((char *)dep - (char *)d)); - hash = xfs_da_hashname((char *)dep->name, dep->namelen); + hash = xfs_dir_hashname(dp, (char *)dep->name, + dep->namelen); for (i = 0; i < be32_to_cpu(btp->count); i++) { if (be32_to_cpu(lep[i].address) == addr && be32_to_cpu(lep[i].hashval) == hash) =========================================================================== fs/xfs/xfs_dir2_leaf.c =========================================================================== --- a/fs/xfs/xfs_dir2_leaf.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_dir2_leaf.c 2008-01-21 14:16:01.621460279 +1100 @@ -40,6 +40,7 @@ #include "xfs_dir2_node.h" #include "xfs_dir2_trace.h" #include "xfs_error.h" +#include "xfs_unicode.h" /* * Local function declarations. @@ -780,6 +781,8 @@ xfs_dir2_leaf_getdents( int ra_offset; /* map entry offset for ra */ int ra_want; /* readahead count wanted */ xfs_ino_t ino; + char *nls_name = NULL; /* NLS name buffer */ + int nls_namelen = 0; /* * If the offset is at or past the largest allowed value, @@ -800,6 +803,9 @@ xfs_dir2_leaf_getdents( map_valid = ra_index = ra_offset = ra_current = map_blocks = 0; bp = NULL; + if (mp->m_nls) + nls_name = xfs_alloc_unicode_nls_name(); + /* * Inside the loop we keep the main offset value as a byte offset * in the directory file. @@ -1086,13 +1092,21 @@ xfs_dir2_leaf_getdents( #if XFS_BIG_INUMS ino += mp->m_inoadd; #endif + if (mp->m_nls) { + error = xfs_unicode_to_nls(mp->m_nls, dep->name, + dep->namelen, &nls_name, &nls_namelen); + if (error) + break; + } /* * Won't fit. Return to caller. */ - if (filldir(dirent, dep->name, dep->namelen, - xfs_dir2_byte_to_dataptr(mp, curoff), - ino, DT_UNKNOWN)) + if (filldir(dirent, + nls_namelen ? nls_name : (char *)dep->name, + nls_namelen ? nls_namelen : dep->namelen, + xfs_dir2_byte_to_dataptr(mp, curoff), + ino, DT_UNKNOWN)) break; /* @@ -1113,6 +1127,8 @@ xfs_dir2_leaf_getdents( kmem_free(map, map_size * sizeof(*map)); if (bp) xfs_da_brelse(NULL, bp); + if (mp->m_nls) + xfs_free_unicode_nls_name(nls_name); return error; } @@ -1300,10 +1316,18 @@ xfs_dir2_leaf_lookup( /* * Return the found inode number. */ + error = EEXIST; args->inumber = be64_to_cpu(dep->inumber); + if (args->cmpresult == XFS_CMP_CASE) { + error = xfs_unicode_to_nls(args->dp->i_mount->m_nls, + dep->name, dep->namelen, + (char **)&args->value, &args->valuelen); + if (!error) + error = EEXIST; + } xfs_da_brelse(tp, dbp); xfs_da_brelse(tp, lbp); - return XFS_ERROR(EEXIST); + return XFS_ERROR(error); } /* @@ -1331,6 +1355,7 @@ xfs_dir2_leaf_lookup_int( xfs_mount_t *mp; /* filesystem mount point */ xfs_dir2_db_t newdb; /* new data block number */ xfs_trans_t *tp; /* transaction pointer */ + xfs_dacmp_t cmp; /* name compare result */ dp = args->dp; tp = args->trans; @@ -1354,6 +1379,7 @@ xfs_dir2_leaf_lookup_int( * Loop over all the entries with the right hash value * looking to match the name. */ + args->cmpresult = XFS_CMP_DIFFERENT; for (lep = &leaf->ents[index], dbp = NULL, curdb = -1; index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval; lep++, index++) { @@ -1391,19 +1417,31 @@ xfs_dir2_leaf_lookup_int( xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); /* * If it matches then return it. - */ - if (dep->namelen == args->namelen && - dep->name[0] == args->name[0] && - memcmp(dep->name, args->name, args->namelen) == 0) { + * + * lookup case - use nameops; + * + * replace/remove case - as lookup has been already been + * performed, look for an exact match using the fast method + */ + cmp = args->oknoent ? + xfs_dir_compname(dp, dep->name, dep->namelen, + args->name, args->namelen) : + xfs_default_compname(dp, dep->name, dep->namelen, + args->name, args->namelen); + if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { + args->cmpresult = cmp; *dbpp = dbp; *indexp = index; - return 0; + if (cmp == XFS_CMP_EXACT) + return 0; } } + ASSERT(args->oknoent); + if (args->cmpresult == XFS_CMP_CASE) + return 0; /* * No match found, return ENOENT. */ - ASSERT(args->oknoent); if (dbp) xfs_da_brelse(tp, dbp); xfs_da_brelse(tp, lbp); =========================================================================== fs/xfs/xfs_dir2_node.c =========================================================================== --- a/fs/xfs/xfs_dir2_node.c 2008-01-21 14:42:50.000000000 +1100 +++ b/fs/xfs/xfs_dir2_node.c 2008-01-21 14:35:07.954595095 +1100 @@ -39,6 +39,7 @@ #include "xfs_dir2_node.h" #include "xfs_dir2_trace.h" #include "xfs_error.h" +#include "xfs_unicode.h" /* * Function declarations. @@ -414,6 +415,7 @@ xfs_dir2_leafn_lookup_int( xfs_dir2_db_t newdb; /* new data block number */ xfs_dir2_db_t newfdb; /* new free block number */ xfs_trans_t *tp; /* transaction pointer */ + xfs_dacmp_t cmp; /* comparison result */ dp = args->dp; tp = args->trans; @@ -455,6 +457,7 @@ xfs_dir2_leafn_lookup_int( /* * Loop over leaf entries with the right hash value. */ + args->cmpresult = XFS_CMP_DIFFERENT; for (lep = &leaf->ents[index]; index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval; lep++, index++) { @@ -572,28 +575,36 @@ xfs_dir2_leafn_lookup_int( /* * Point to the data entry. */ - dep = (xfs_dir2_data_entry_t *) - ((char *)curbp->data + - xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); + dep = (xfs_dir2_data_entry_t *)((char *)curbp->data + + xfs_dir2_dataptr_to_off(mp, + be32_to_cpu(lep->address))); /* * Compare the entry, return it if it matches. */ - if (dep->namelen == args->namelen && - dep->name[0] == args->name[0] && - memcmp(dep->name, args->name, args->namelen) == 0) { + cmp = args->oknoent ? + xfs_dir_compname(dp, dep->name, dep->namelen, + args->name, args->namelen): + xfs_default_compname(dp, dep->name, + dep->namelen, args->name, + args->namelen); + if (cmp != XFS_CMP_DIFFERENT && + cmp != args->cmpresult) { + args->cmpresult = cmp; args->inumber = be64_to_cpu(dep->inumber); *indexp = index; state->extravalid = 1; state->extrablk.bp = curbp; state->extrablk.blkno = curdb; - state->extrablk.index = - (int)((char *)dep - - (char *)curbp->data); + state->extrablk.index = (int)((char *)dep - + (char *)curbp->data); state->extrablk.magic = XFS_DIR2_DATA_MAGIC; - return XFS_ERROR(EEXIST); + if (cmp == XFS_CMP_EXACT) + return XFS_ERROR(EEXIST); } } } + if (args->cmpresult == XFS_CMP_CASE) + return XFS_ERROR(EEXIST); /* * Didn't find a match. * If we are holding a buffer, give it back in case our caller @@ -823,16 +834,17 @@ xfs_dir2_leafn_rebalance( */ if (!state->inleaf) blk2->index = blk1->index - be16_to_cpu(leaf1->hdr.count); - - /* - * Finally sanity check just to make sure we are not returning a negative index + + /* + * Finally sanity check just to make sure we are not returning a + * negative index */ if(blk2->index < 0) { state->inleaf = 1; blk2->index = 0; cmn_err(CE_ALERT, - "xfs_dir2_leafn_rebalance: picked the wrong leaf? reverting original leaf: " - "blk1->index %d\n", + "xfs_dir2_leafn_rebalance: picked the wrong leaf? " + "reverting original leaf: blk1->index %d\n", blk1->index); } } @@ -1768,6 +1780,20 @@ xfs_dir2_node_lookup( if (error) rval = error; /* + * If case-insens match, copy name out + */ + if (args->cmpresult == XFS_CMP_CASE) { + xfs_dir2_data_entry_t *dep = + (xfs_dir2_data_entry_t *) + ((char *)state->extrablk.bp->data + + state->extrablk.index); + error = xfs_unicode_to_nls(args->dp->i_mount->m_nls, + dep->name, dep->namelen, + (char **)&args->value, &args->valuelen); + if (error) + rval = error; + } + /* * Release the btree blocks and leaf block. */ for (i = 0; i < state->path.active; i++) { =========================================================================== fs/xfs/xfs_dir2_sf.c =========================================================================== --- a/fs/xfs/xfs_dir2_sf.c 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_dir2_sf.c 2008-01-21 14:21:42.201792024 +1100 @@ -38,6 +38,7 @@ #include "xfs_dir2_leaf.h" #include "xfs_dir2_block.h" #include "xfs_dir2_trace.h" +#include "xfs_unicode.h" /* * Prototypes for internal functions. @@ -701,6 +702,7 @@ xfs_dir2_sf_getdents( filldir_t filldir) { int i; /* shortform entry number */ + int error = 0; xfs_mount_t *mp; /* filesystem mount point */ xfs_dir2_dataptr_t off; /* current entry's offset */ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ @@ -708,6 +710,8 @@ xfs_dir2_sf_getdents( xfs_dir2_dataptr_t dot_offset; xfs_dir2_dataptr_t dotdot_offset; xfs_ino_t ino; + char *nls_name = NULL; /* NLS name buffer */ + int nls_namelen = 0; mp = dp->i_mount; @@ -772,6 +776,9 @@ xfs_dir2_sf_getdents( } } + if (mp->m_nls) + nls_name = xfs_alloc_unicode_nls_name(); + /* * Loop while there are more entries and put'ing works. */ @@ -789,17 +796,26 @@ xfs_dir2_sf_getdents( #if XFS_BIG_INUMS ino += mp->m_inoadd; #endif - - if (filldir(dirent, sfep->name, sfep->namelen, - off, ino, DT_UNKNOWN)) { + if (mp->m_nls) { + error = xfs_unicode_to_nls(mp->m_nls, sfep->name, + sfep->namelen, &nls_name, &nls_namelen); + if (error) + goto out; + } + if (filldir(dirent, + nls_namelen ? nls_name : (char *)sfep->name, + nls_namelen ? nls_namelen : sfep->namelen, + off, ino, DT_UNKNOWN)) { *offset = off; - return 0; + goto out; } sfep = xfs_dir2_sf_nextentry(sfp, sfep); } - *offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0); - return 0; +out: + if (mp->m_nls) + xfs_free_unicode_nls_name(nls_name); + return error; } /* @@ -812,6 +828,7 @@ xfs_dir2_sf_lookup( { xfs_inode_t *dp; /* incore directory inode */ int i; /* entry index */ + int error; xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ @@ -836,6 +853,7 @@ xfs_dir2_sf_lookup( */ if (args->namelen == 1 && args->name[0] == '.') { args->inumber = dp->i_ino; + args->cmpresult = XFS_CMP_EXACT; return XFS_ERROR(EEXIST); } /* @@ -844,23 +862,44 @@ xfs_dir2_sf_lookup( if (args->namelen == 2 && args->name[0] == '.' && args->name[1] == '.') { args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent); + args->cmpresult = XFS_CMP_EXACT; return XFS_ERROR(EEXIST); } /* * Loop over all the entries trying to match ours. */ - for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); - i < sfp->hdr.count; - i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { - if (sfep->namelen == args->namelen && - sfep->name[0] == args->name[0] && - memcmp(args->name, sfep->name, args->namelen) == 0) { - args->inumber = - xfs_dir2_sf_get_inumber(sfp, - xfs_dir2_sf_inumberp(sfep)); + args->cmpresult = XFS_CMP_DIFFERENT; + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { + switch (xfs_dir_compname(dp, sfep->name, sfep->namelen, + args->name, args->namelen)) { + case XFS_CMP_EXACT: + args->cmpresult = XFS_CMP_EXACT; + args->inumber = xfs_dir2_sf_get_inumber(sfp, + xfs_dir2_sf_inumberp(sfep)); + if (args->value) { + xfs_free_unicode_nls_name(args->value); + args->value = NULL; + } return XFS_ERROR(EEXIST); + + case XFS_CMP_CASE: + if (!args->value) { + error = xfs_unicode_to_nls( + args->dp->i_mount->m_nls, sfep->name, + sfep->namelen, (char **)&args->value, + &args->valuelen); + if (error) + return XFS_ERROR(error); + args->cmpresult = XFS_CMP_CASE; + args->inumber = xfs_dir2_sf_get_inumber(sfp, + xfs_dir2_sf_inumberp(sfep)); + } + default: ; } } + if (args->cmpresult == XFS_CMP_CASE) + return XFS_ERROR(EEXIST); /* * Didn't find it. */ @@ -907,9 +946,8 @@ xfs_dir2_sf_removename( for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { - if (sfep->namelen == args->namelen && - sfep->name[0] == args->name[0] && - memcmp(sfep->name, args->name, args->namelen) == 0) { + if (xfs_default_compname(dp, sfep->name, sfep->namelen, + args->name, args->namelen) == XFS_CMP_EXACT) { ASSERT(xfs_dir2_sf_get_inumber(sfp, xfs_dir2_sf_inumberp(sfep)) == args->inumber); @@ -1044,9 +1082,9 @@ xfs_dir2_sf_replace( for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { - if (sfep->namelen == args->namelen && - sfep->name[0] == args->name[0] && - memcmp(args->name, sfep->name, args->namelen) == 0) { + if (xfs_default_compname(dp, sfep->name, + sfep->namelen, args->name, + args->namelen) == XFS_CMP_EXACT) { #if XFS_BIG_INUMS || defined(DEBUG) ino = xfs_dir2_sf_get_inumber(sfp, xfs_dir2_sf_inumberp(sfep)); =========================================================================== fs/xfs/xfs_itable.c =========================================================================== --- a/fs/xfs/xfs_itable.c 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_itable.c 2008-01-18 13:45:10.740061749 +1100 @@ -45,6 +45,8 @@ xfs_internal_inum( xfs_ino_t ino) { return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || + (xfs_sb_version_hasunicode(&mp->m_sb) && + ino == mp->m_sb.sb_cftino) || (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) && (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino))); } =========================================================================== fs/xfs/xfs_mount.c =========================================================================== --- a/fs/xfs/xfs_mount.c 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_mount.c 2008-01-21 14:27:33.000000000 +1100 @@ -25,6 +25,7 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" +#include "xfs_attr.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" @@ -43,6 +44,9 @@ #include "xfs_rw.h" #include "xfs_quota.h" #include "xfs_fsops.h" +#include "xfs_da_btree.h" +#include "xfs_attr_leaf.h" +#include "xfs_unicode.h" STATIC void xfs_mount_log_sbunit(xfs_mount_t *, __int64_t); STATIC int xfs_uuid_mount(xfs_mount_t *); @@ -119,6 +123,8 @@ static const struct { { offsetof(xfs_sb_t, sb_logsectsize),0 }, { offsetof(xfs_sb_t, sb_logsunit), 0 }, { offsetof(xfs_sb_t, sb_features2), 0 }, + { offsetof(xfs_sb_t, sb_bad_features2), 0 }, + { offsetof(xfs_sb_t, sb_cftino), 0 }, { sizeof(xfs_sb_t), 0 } }; @@ -165,6 +171,9 @@ xfs_mount_free( sizeof(xfs_perag_t) * mp->m_sb.sb_agcount); } + if (mp->m_cft) + xfs_unicode_free_cft(mp->m_cft); + spinlock_destroy(&mp->m_ail_lock); spinlock_destroy(&mp->m_sb_lock); mutex_destroy(&mp->m_ilock); @@ -449,6 +458,8 @@ xfs_sb_from_disk( to->sb_logsectsize = be16_to_cpu(from->sb_logsectsize); to->sb_logsunit = be32_to_cpu(from->sb_logsunit); to->sb_features2 = be32_to_cpu(from->sb_features2); + to->sb_bad_features2 = be32_to_cpu(from->sb_bad_features2); + to->sb_cftino = be64_to_cpu(from->sb_cftino); } /* @@ -1056,7 +1067,7 @@ xfs_mountfs( /* * Initialize the attribute manager's entries. */ - mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100; + xfs_attr_mount(mp); /* * Initialize the precomputed transaction reservations values. @@ -1159,6 +1170,18 @@ xfs_mountfs( } /* + * Load in unicode case folding table from disk + */ + if (xfs_sb_version_hasunicode(&mp->m_sb)) { + error = xfs_unicode_read_cft(mp); + if (error) { + cmn_err(CE_WARN, + "XFS: failed to read case folding table"); + goto error4; + } + } + + /* * If fs is not mounted readonly, then update the superblock * unit and width changes. */ @@ -1214,6 +1237,8 @@ xfs_mountfs( * Free up the root inode. */ VN_RELE(rvp); + if (mp->m_cft) + xfs_unicode_free_cft(mp->m_cft); error3: xfs_log_unmount_dealloc(mp); error2: =========================================================================== fs/xfs/xfs_mount.h =========================================================================== --- a/fs/xfs/xfs_mount.h 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_mount.h 2008-01-21 14:27:33.000000000 +1100 @@ -54,6 +54,7 @@ typedef struct xfs_trans_reservations { #else struct cred; struct log; +struct nls_table; struct xfs_mount_args; struct xfs_inode; struct xfs_bmbt_irec; @@ -61,6 +62,8 @@ struct xfs_bmap_free; struct xfs_extdelta; struct xfs_swapext; struct xfs_mru_cache; +struct xfs_nameops; +struct xfs_cft; /* * Prototypes and functions for the Data Migration subsystem. @@ -312,6 +315,10 @@ typedef struct xfs_mount { __uint8_t m_inode_quiesce;/* call quiesce on new inodes. field governed by m_ilock */ __uint8_t m_sectbb_log; /* sectlog - BBSHIFT */ + const struct xfs_nameops *m_dirnameops; /* vector of dir name ops */ + const struct xfs_nameops *m_attrnameops; /* vector of attr name ops */ + const struct xfs_cft *m_cft; /* unicode case fold table */ + struct nls_table *m_nls; /* active NLS table */ int m_dirblksize; /* directory block sz--bytes */ int m_dirblkfsbs; /* directory block sz--fsbs */ xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */ @@ -377,7 +384,10 @@ typedef struct xfs_mount { counters */ #define XFS_MOUNT_FILESTREAMS (1ULL << 24) /* enable the filestreams allocator */ - +#define XFS_MOUNT_CI_LOOKUP (1ULL << 25) /* enable case-insensitive + * file lookup */ +#define XFS_MOUNT_CI_ATTR (1ULL << 26) /* enable case-insensitive + attribute names */ /* * Default minimum read and write sizes. =========================================================================== fs/xfs/xfs_rename.c =========================================================================== --- a/fs/xfs/xfs_rename.c 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_rename.c 2008-01-17 12:25:31.652529581 +1100 @@ -130,7 +130,9 @@ xfs_lock_for_rename( lock_mode = xfs_ilock_map_shared(dp2); } - error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2); + error = xfs_dir_lookup_int(dp2, lock_mode, vname2, &inum2, &ip2, + NULL, NULL); + if (error == ENOENT) { /* target does not need to exist. */ inum2 = 0; } else if (error) { @@ -214,6 +216,7 @@ xfs_lock_for_rename( for (;i < 4; i++) { i_tab[i] = NULL; } + return 0; } =========================================================================== fs/xfs/xfs_sb.h =========================================================================== --- a/fs/xfs/xfs_sb.h 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_sb.h 2008-01-21 14:41:29.721741469 +1100 @@ -46,10 +46,12 @@ struct xfs_mount; #define XFS_SB_VERSION_SECTORBIT 0x0800 #define XFS_SB_VERSION_EXTFLGBIT 0x1000 #define XFS_SB_VERSION_DIRV2BIT 0x2000 +#define XFS_SB_VERSION_OLDCIBIT 0x4000 /* ASCII only case-insens. */ #define XFS_SB_VERSION_MOREBITSBIT 0x8000 #define XFS_SB_VERSION_OKSASHFBITS \ (XFS_SB_VERSION_EXTFLGBIT | \ - XFS_SB_VERSION_DIRV2BIT) + XFS_SB_VERSION_DIRV2BIT | \ + XFS_SB_VERSION_OLDCIBIT) #define XFS_SB_VERSION_OKREALFBITS \ (XFS_SB_VERSION_ATTRBIT | \ XFS_SB_VERSION_NLINKBIT | \ @@ -77,10 +79,12 @@ struct xfs_mount; #define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */ #define XFS_SB_VERSION2_RESERVED4BIT 0x00000004 #define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */ +#define XFS_SB_VERSION2_UNICODEBIT 0x00000020 /* Unicode names */ #define XFS_SB_VERSION2_OKREALFBITS \ (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \ - XFS_SB_VERSION2_ATTR2BIT) + XFS_SB_VERSION2_ATTR2BIT | \ + XFS_SB_VERSION2_UNICODEBIT) #define XFS_SB_VERSION2_OKSASHFBITS \ (0) #define XFS_SB_VERSION2_OKREALBITS \ @@ -145,6 +149,9 @@ typedef struct xfs_sb { __uint16_t sb_logsectsize; /* sector size for the log, bytes */ __uint32_t sb_logsunit; /* stripe unit size for the log */ __uint32_t sb_features2; /* additional feature bits */ + __uint32_t sb_bad_features2; /* bad mkfs set features2 here */ + xfs_ino_t sb_cftino; /* unicode case folding table inode */ + /* must be padded to 64 bit alignment */ } xfs_sb_t; /* @@ -205,6 +212,9 @@ typedef struct xfs_dsb { __be16 sb_logsectsize; /* sector size for the log, bytes */ __be32 sb_logsunit; /* stripe unit size for the log */ __be32 sb_features2; /* additional feature bits */ + __be32 sb_bad_features2; /* bad mkfs set features2 here */ + __be64 sb_cftino; /* unicode case folding table inode */ + /* must be padded to 64 bit alignment */ } xfs_dsb_t; /* @@ -223,7 +233,7 @@ typedef enum { XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN, XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG, XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT, - XFS_SBS_FEATURES2, + XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2, XFS_SBS_CFTINO, XFS_SBS_FIELDCOUNT } xfs_sb_field_t; @@ -248,13 +258,15 @@ typedef enum { #define XFS_SB_IFREE XFS_SB_MVAL(IFREE) #define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS) #define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2) +#define XFS_SB_CASEFOLDINO XFS_SB_MVAL(CASEFOLDINO) #define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT) #define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1) #define XFS_SB_MOD_BITS \ (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \ XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \ XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \ - XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2) + XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \ + XFS_SB_CFTINO) /* @@ -463,6 +475,12 @@ static inline int xfs_sb_version_hassect ((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT); } +static inline int xfs_sb_version_hasoldci(xfs_sb_t *sbp) +{ + return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_OLDCIBIT); +} + #define XFS_SB_VERSION_HASMOREBITS(sbp) xfs_sb_version_hasmorebits(sbp) static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp) { @@ -502,6 +520,13 @@ static inline void xfs_sb_version_addatt ((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT))); } +static inline int xfs_sb_version_hasunicode(xfs_sb_t *sbp) +{ + return (xfs_sb_version_hasmorebits(sbp) && \ + ((sbp)->sb_features2 & XFS_SB_VERSION2_UNICODEBIT)); +} + + /* * end of superblock version macros */ =========================================================================== fs/xfs/xfs_unicode.c =========================================================================== --- a/fs/xfs/xfs_unicode.c 2006-06-17 00:58:24.000000000 +1000 +++ b/fs/xfs/xfs_unicode.c 2008-01-21 14:10:29.048257871 +1100 @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2007 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_bit.h" +#include "xfs_log.h" +#include "xfs_inum.h" +#include "xfs_clnt.h" +#include "xfs_trans.h" +#include "xfs_sb.h" +#include "xfs_ag.h" +#include "xfs_dir2.h" +#include "xfs_alloc.h" +#include "xfs_dmapi.h" +#include "xfs_mount.h" +#include "xfs_bmap_btree.h" +#include "xfs_alloc_btree.h" +#include "xfs_ialloc_btree.h" +#include "xfs_dir2_sf.h" +#include "xfs_attr_sf.h" +#include "xfs_dinode.h" +#include "xfs_inode.h" +#include "xfs_btree.h" +#include "xfs_ialloc.h" +#include "xfs_itable.h" +#include "xfs_rtalloc.h" +#include "xfs_error.h" +#include "xfs_bmap.h" +#include "xfs_rw.h" +#include "xfs_unicode.h" + +#define MAX_FOLD_CHARS 4 + +static kmem_zone_t *xfs_nls_uni_zone; + +static inline int +xfs_casefold( + const xfs_cft_t *cft, + __uint16_t c, + __uint16_t *fc) +{ + __uint16_t *table = XFS_CFT_PTR(cft, 0); + __uint16_t tmp = table[c >> 8]; + int i; + + if (!tmp) { + *fc = c; + return 1; + } + tmp = table[tmp + (c & 0xff)]; + if ((tmp & 0xf000) != 0xe000) { + *fc = tmp; + return 1; + } + i = ((tmp >> 10) & 0x3) + 2; + ASSERT(i < cft->num_tables); + table = XFS_CFT_PTR(cft, i - 1) + ((tmp & 0x3ff) * i); + + memcpy(fc, table, sizeof(__uint16_t) * i); + + return i; +} + +static inline int +xfs_utf8_casefold( + const xfs_cft_t *cft, + const uchar_t **name, + int *namelen, + __uint16_t *fc) +{ + wchar_t uc; + + if (*namelen == 0) + return 0; + + if (**name & 0x80) { + int n = utf8_mbtowc(&uc, *name, *namelen); + if (n < 0) { + (*namelen)--; + *fc = *(*name)++; + return 1; + } + *name += n; + *namelen -= n; + } else { + uc = *(*name)++; + (*namelen)--; + } + return xfs_casefold(cft, uc, fc); +} + +__uint32_t +xfs_unicode_hash( + const xfs_cft_t *cft, + const uchar_t *name, + int namelen) +{ + __uint32_t hash = 0; + __uint16_t fc[MAX_FOLD_CHARS]; + int nfc; + int i; + + while (namelen > 0) { + nfc = xfs_utf8_casefold(cft, &name, &namelen, fc); + for (i = 0; i < nfc; i++) + hash = fc[i] ^ rol32(hash, 7); + } + return hash; +} + +int +xfs_unicode_casecmp( + const xfs_cft_t *cft, + const uchar_t *name1, + int len1, + const uchar_t *name2, + int len2) +{ + __uint16_t fc1[MAX_FOLD_CHARS], fc2[MAX_FOLD_CHARS]; + __uint16_t *pfc1, *pfc2; + int nfc1, nfc2; + + nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1); + pfc1 = fc1; + nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2); + pfc2 = fc2; + + while (nfc1 > 0 && nfc2 > 0) { + if (*pfc1 != *pfc2) + return (*pfc1 < *pfc2) ? -1 : 1; + if (!--nfc1) { + nfc1 = xfs_utf8_casefold(cft, &name1, &len1, fc1); + pfc1 = fc1; + } else + pfc1++; + if (!--nfc2) { + nfc2 = xfs_utf8_casefold(cft, &name2, &len2, fc2); + pfc2 = fc2; + } else + pfc2++; + } + if (nfc1 != nfc2) + return (nfc1 < nfc2) ? -1 : 1; + return 0; +} + + +char * +xfs_alloc_unicode_nls_name(void) +{ + return kmem_zone_alloc(xfs_nls_uni_zone, KM_SLEEP); +} + + +void +xfs_free_unicode_nls_name( + char *name) +{ + kmem_zone_free(xfs_nls_uni_zone, name); +} + +int +xfs_nls_to_unicode( + struct nls_table *nls, + const char *nls_name, + int nls_namelen, + char **uni_name, + int *uni_namelen) +{ + char *n; + int i, o; + wchar_t uc; + int nlen; + int u8len; + int error = 0; + + n = *uni_name ? *uni_name : xfs_alloc_unicode_nls_name(); + + if (!nls) { + if (nls_namelen > MAXNAMELEN) { + error = ENAMETOOLONG; + goto err_out; + } + memcpy(n, nls_name, nls_namelen); + *uni_name = n; + *uni_namelen = nls_namelen; + return 0; + } + + for (i = 0, o = 0; i < nls_namelen; i += nlen, o += u8len) { + nlen = nls->char2uni(nls_name + i, nls_namelen - i, &uc); + if (nlen < 0) { + error = -nlen; + goto err_out; + } + if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xdfff)) { + error = EINVAL; /* don't support chars outside BMP */ + goto err_out; + } + u8len = utf8_wctomb(n + o, uc, MAXNAMELEN - o); + if (u8len <= 0) { + error = (MAXNAMELEN - o < 3) ? ENAMETOOLONG : EINVAL; + goto err_out; + } + } + *uni_name = n; + *uni_namelen = o; + return 0; +err_out: + if (*uni_name == NULL) + xfs_free_unicode_nls_name(n); + return error; +} + +int +xfs_unicode_to_nls( + struct nls_table *nls, + const char *uni_name, + int uni_namelen, + char **nls_name, + int *nls_namelen) +{ + char *n; + int i, o; + wchar_t uc; + int nlen; + int u8len; + int error = 0; + + n = *nls_name ? *nls_name : xfs_alloc_unicode_nls_name(); + + if (!nls) { + if (uni_namelen > MAXNAMELEN) { + error = ENAMETOOLONG; + goto err_out; + } + memcpy(n, uni_name, uni_namelen); + *nls_name = n; + *nls_namelen = uni_namelen; + return 0; + } + + for (i = 0, o = 0; i < uni_namelen && o < MAXNAMELEN; + i += u8len, o += nlen) { + u8len = utf8_mbtowc(&uc, uni_name + i, uni_namelen - i); + if (u8len < 0) { + error = EINVAL; + goto err_out; + } + nlen = nls->uni2char(uc, n + o, MAXNAMELEN - o); + if (nlen == -EINVAL) { + n[o] = '?'; + nlen = 1; + } else if (nlen < 0) { + error = -nlen; + goto err_out; + } + } + if (i == uni_namelen) { + *nls_name = n; + *nls_namelen = o; + return 0; + } + + error = ENAMETOOLONG; +err_out: + if (*nls_name == NULL) + xfs_free_unicode_nls_name(n); + return error; +} + +int +xfs_unicode_validate( + const uchar_t *name, + int namelen) +{ + wchar_t uc; + int i, nlen; + + for (i = 0; i < namelen; i += nlen) { + if (*name >= 0xf0) { + cmn_err(CE_WARN, "xfs_unicode_validate: " + "UTF-8 char beyond U+FFFF\n"); + return EINVAL; + } + /* utf8_mbtowc must fail on overlong sequences too */ + nlen = utf8_mbtowc(&uc, name + i, namelen - i); + if (nlen < 0) { + cmn_err(CE_WARN, "xfs_unicode_validate: " + "invalid UTF-8 sequence\n"); + return EILSEQ; + } + /* check for invalid/surrogate/private unicode chars */ + if (uc >= 0xfffe || (uc >= 0xd800 && uc <= 0xf8ff)) { + cmn_err(CE_WARN, "xfs_unicode_validate: " + "unsupported UTF-8 char\n"); + return EINVAL; + } + } + return 0; +} + +/* + * Unicode Case Fold Table management + */ + +struct cft_item { + xfs_cft_t *table; + int size; + int refcount; +}; + +static mutex_t cft_lock; +static int cft_size; +static struct cft_item *cft_list; + +static xfs_cft_t * +add_cft( + xfs_dcft_t *dcft, + int size) +{ + int found = 0; + int i, j; + xfs_cft_t *cft; + __be16 *duc; + __uint16_t *uc; + + mutex_lock(&cft_lock); + + for (i = 0; i < cft_size; i++) { + if (cft_list[i].size != size) + continue; + cft = cft_list[i].table; + if (cft->num_tables != be32_to_cpu(dcft->num_tables) || + cft->flags != be32_to_cpu(dcft->flags)) + continue; + found = 1; + for (j = 0; j < cft->num_tables; j++) { + if (cft->table_offset[j] != + be32_to_cpu(dcft->table_offset[j])) { + found = 0; + break; + } + } + if (found) { + cft_list[i].refcount++; + mutex_unlock(&cft_lock); + return cft; + } + } + + cft = vmalloc(size); + if (!cft) { + mutex_unlock(&cft_lock); + return NULL; + } + cft->magic = be32_to_cpu(dcft->magic); + cft->flags = be32_to_cpu(dcft->flags); + cft->num_tables = be32_to_cpu(dcft->num_tables); + ASSERT(cft->num_tables <= MAX_FOLD_CHARS); + for (i = 0; i < cft->num_tables; i++) + cft->table_offset[i] = be32_to_cpu(dcft->table_offset[i]); + j = (size - cft->table_offset[0]) / sizeof(__uint16_t); + uc = XFS_CFT_PTR(cft, 0); + duc = XFS_DCFT_PTR(dcft, 0); + for (i = 0; i < j; i++) + uc[i] = be16_to_cpu(duc[i]); + + cft_list = kmem_realloc(cft_list, + (cft_size + 1) * sizeof(struct cft_item), + cft_size * sizeof(struct cft_item), KM_SLEEP); + cft_list[cft_size].table = cft; + cft_list[cft_size].size = size; + cft_list[cft_size].refcount = 1; + cft_size++; + + mutex_unlock(&cft_lock); + + return cft; +} + +static void +remove_cft( + const xfs_cft_t *cft) +{ + int i; + + mutex_lock(&cft_lock); + + for (i = 0; i < cft_size; i++) { + if (cft_list[i].table == cft) { + ASSERT(cft_list[i].refcount > 0); + cft_list[i].refcount--; + break; + } + } + + mutex_unlock(&cft_lock); +} + + +int +xfs_unicode_read_cft( + xfs_mount_t *mp) +{ + int error; + xfs_inode_t *cftip; + int size; + int nfsb; + int nmap; + xfs_bmbt_irec_t *mapp; + int n; + int byte_cnt; + xfs_buf_t *bp; + char *table; + xfs_dcft_t *dcft; + + if (mp->m_sb.sb_cftino == NULLFSINO || mp->m_sb.sb_cftino == 0) + return EINVAL; + error = xfs_iget(mp, NULL, mp->m_sb.sb_cftino, 0, 0, &cftip, 0); + if (error) + return error; + ASSERT(cftip != NULL); + + size = cftip->i_d.di_size; + nfsb = cftip->i_d.di_nblocks; + + table = vmalloc(size); + if (!table) { + xfs_iput(cftip, 0); + return ENOMEM; + } + dcft = (xfs_dcft_t *)table; + + nmap = nfsb; + mapp = kmem_alloc(nfsb * sizeof(xfs_bmbt_irec_t), KM_SLEEP); + + error = xfs_bmapi(NULL, cftip, 0, nfsb, 0, NULL, 0, mapp, &nmap, + NULL, NULL); + if (error) + goto out; + + for (n = 0; n < nmap; n++) { + byte_cnt = XFS_FSB_TO_B(mp, mapp[n].br_blockcount); + + error = xfs_read_buf(mp, mp->m_ddev_targp, + XFS_FSB_TO_DADDR(mp, mapp[n].br_startblock), + BTOBB(byte_cnt), 0, &bp); + if (error) + goto out; + + if (size < byte_cnt) + byte_cnt = size; + size -= byte_cnt; + memcpy(table, XFS_BUF_PTR(bp), byte_cnt); + table += byte_cnt; + xfs_buf_relse(bp); + } + + /* verify case table read off disk */ + if (!uuid_equal(&dcft->uuid, &mp->m_sb.sb_uuid)) { + error = EINVAL; + goto out; + } + + /* clear UUID for in-memory copy/compare */ + memset(&dcft->uuid, 0, sizeof(dcft->uuid)); + + mp->m_cft = add_cft(dcft, cftip->i_d.di_size); + if (mp->m_cft == NULL) + error = ENOMEM; + +out: + xfs_iput(cftip, 0); + kmem_free(mapp, nfsb * sizeof(xfs_bmbt_irec_t)); + vfree(dcft); + + return error; +} + +void +xfs_unicode_free_cft( + const xfs_cft_t *cft) +{ + remove_cft(cft); +} + +void +xfs_unicode_init(void) +{ + mutex_init(&cft_lock); + xfs_nls_uni_zone = kmem_zone_init(MAXNAMELEN, "xfs_nls_uni"); +} + +void +xfs_unicode_uninit(void) +{ + int i; + + mutex_lock(&cft_lock); + + for (i = 0; i < cft_size; i++) { + ASSERT(cft_list[i].refcount == 0); + vfree(cft_list[i].table); + } + kmem_free(cft_list, cft_size * sizeof(struct cft_item)); + cft_size = 0; + cft_list = NULL; + + mutex_unlock(&cft_lock); + mutex_destroy(&cft_lock); + + kmem_zone_destroy(xfs_nls_uni_zone); +} =========================================================================== fs/xfs/xfs_unicode.h =========================================================================== --- a/fs/xfs/xfs_unicode.h 2006-06-17 00:58:24.000000000 +1000 +++ b/fs/xfs/xfs_unicode.h 2008-01-21 14:23:07.266899447 +1100 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2007 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __XFS_UNICODE_H__ +#define __XFS_UNICODE_H__ + +#define XFS_CFT_MAGIC 0x58434654 /* 'XCFT' */ +#define XFS_CFT_FLAG_TURKIC 0x00000001 +#define XFS_CFT_FLAG_MAX 0x00000001 + +/* + * Case Fold Table - on disk version. Must match the incore version below. + */ +typedef struct xfs_dcft { + __be32 magic; /* validity check */ + __be32 flags; + uuid_t uuid; /* UUID of the filesystem */ + __be32 crc; /* for future support */ + __be32 num_tables; /* single, double, etc */ + __be32 table_offset[1]; +} xfs_dcft_t; + +/* + * Case Fold Table - in core version. Must match the ondisk version above. + */ +typedef struct xfs_cft { + __uint32_t magic; + __uint32_t flags; + uuid_t uuid; /* UUID of the filesystem */ + __uint32_t crc; + __uint32_t num_tables; /* single, double, etc */ + __uint32_t table_offset[1];/* num_tables sized */ + /* 16-bit array tables immediately follow */ +} xfs_cft_t; + +#define XFS_CFT_PTR(t,n) (__uint16_t *)(((char *)(t)) + \ + (t)->table_offset[n]) +#define XFS_DCFT_PTR(t,n) (__be16 *)(((char *)(t)) + \ + be32_to_cpu((t)->table_offset[n])) + +void xfs_unicode_init(void); +void xfs_unicode_uninit(void); + +__uint32_t xfs_unicode_hash(const xfs_cft_t *cft, + const uchar_t *name, int namelen); + +int xfs_unicode_casecmp(const xfs_cft_t *cft, const uchar_t *name1, + int len1, const uchar_t *name2, int len2); + +char *xfs_alloc_unicode_nls_name(void); +void xfs_free_unicode_nls_name(char *name); +int xfs_nls_to_unicode(struct nls_table *nls, const char *nls_name, + int nls_namelen, char **uni_name, int *uni_namelen); +int xfs_unicode_to_nls(struct nls_table *nls, const char *uni_name, + int uni_namelen, char **nls_name, int *nls_namelen); + +int xfs_unicode_validate(const uchar_t *name, int namelen); + +int xfs_unicode_read_cft(struct xfs_mount *mp); +void xfs_unicode_free_cft(const xfs_cft_t *cft); + +#endif /* __XFS_UNICODE_H__ */ =========================================================================== fs/xfs/xfs_utils.c =========================================================================== --- a/fs/xfs/xfs_utils.c 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_utils.c 2007-10-26 15:42:04.856766756 +1000 @@ -39,6 +39,7 @@ #include "xfs_rw.h" #include "xfs_itable.h" #include "xfs_utils.h" +#include "xfs_unicode.h" /* * xfs_get_dir_entry is used to get a reference to an inode given @@ -69,13 +70,16 @@ xfs_dir_lookup_int( uint lock_mode, bhv_vname_t *dentry, xfs_ino_t *inum, - xfs_inode_t **ipp) + xfs_inode_t **ipp, + char **actual_name, + int *actual_namelen) { int error; xfs_itrace_entry(dp); - error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum); + error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum, + actual_name, actual_namelen); if (!error) { /* * Unlock the directory. We do this because we can't @@ -102,6 +106,8 @@ xfs_dir_lookup_int( xfs_ilock(dp, lock_mode); error = XFS_ERROR(ENOENT); } + if (error && actual_name) + xfs_free_unicode_nls_name(*actual_name); } return error; } =========================================================================== fs/xfs/xfs_utils.h =========================================================================== --- a/fs/xfs/xfs_utils.h 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_utils.h 2007-10-26 15:35:54.052564595 +1000 @@ -23,7 +23,7 @@ extern int xfs_get_dir_entry (bhv_vname_t *, xfs_inode_t **); extern int xfs_dir_lookup_int (xfs_inode_t *, uint, bhv_vname_t *, xfs_ino_t *, - xfs_inode_t **); + xfs_inode_t **, char **, int *); extern int xfs_truncate_file (xfs_mount_t *, xfs_inode_t *); extern int xfs_dir_ialloc (xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t, xfs_dev_t, cred_t *, prid_t, int, =========================================================================== fs/xfs/xfs_vfsops.c =========================================================================== --- a/fs/xfs/xfs_vfsops.c 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_vfsops.c 2008-01-11 14:47:36.214448806 +1100 @@ -56,7 +56,7 @@ #include "xfs_fsops.h" #include "xfs_vnodeops.h" #include "xfs_vfsops.h" - +#include "xfs_unicode.h" int xfs_init(void) @@ -81,6 +81,7 @@ xfs_init(void) xfs_acl_zone_init(xfs_acl_zone, "xfs_acl"); xfs_mru_cache_init(); xfs_filestream_init(); + xfs_unicode_init(); /* * The size of the zone allocated buf log item is the maximum @@ -158,6 +159,7 @@ xfs_cleanup(void) xfs_cleanup_procfs(); xfs_sysctl_unregister(); xfs_refcache_destroy(); + xfs_unicode_uninit(); xfs_filestream_uninit(); xfs_mru_cache_uninit(); xfs_acl_zone_destroy(xfs_acl_zone); @@ -247,7 +249,6 @@ xfs_start_flags( mp->m_logname = kmem_alloc(strlen(ap->logname) + 1, KM_SLEEP); strcpy(mp->m_logname, ap->logname); } - if (ap->flags & XFSMNT_WSYNC) mp->m_flags |= XFS_MOUNT_WSYNC; #if XFS_BIG_INUMS @@ -404,6 +405,39 @@ xfs_finish_flags( mp->m_qflags |= XFS_OQUOTA_ENFD; } + if (xfs_sb_version_hasunicode(&mp->m_sb)) { + if (ap->flags2 & XFSMNT2_CILOOKUP) + mp->m_flags |= XFS_MOUNT_CI_LOOKUP; + if (ap->flags2 & XFSMNT2_CIATTR) + mp->m_flags |= XFS_MOUNT_CI_ATTR; + + mp->m_nls = ap->nls[0] ? load_nls(ap->nls) : load_nls_default(); + if (!mp->m_nls) { + cmn_err(CE_WARN, + "XFS: unable to load nls mapping \"%s\"\n", ap->nls); + return XFS_ERROR(EINVAL); + } + if (strcmp(mp->m_nls->charset, XFS_NLS_UTF8) == 0) { + /* special case utf8 - no translation required */ + unload_nls(mp->m_nls); + mp->m_nls = NULL; + } + } else { + /* + * Check for mount options which require a Unicode FS + */ + if (ap->flags2 & (XFSMNT2_CILOOKUP | XFSMNT2_CIATTR)) { + cmn_err(CE_WARN, + "XFS: can't do case-insensitive mount on non-utf8 filesystem"); + return XFS_ERROR(EINVAL); + + } + if (ap->nls[0]) { + cmn_err(CE_WARN, + "XFS: can't use nls mount option on non-utf8 filesystem"); + return XFS_ERROR(EINVAL); + } + } return 0; } @@ -641,6 +675,8 @@ out: xfs_unmountfs(mp, credp); xfs_qmops_put(mp); xfs_dmops_put(mp); + if (mp->m_nls) + unload_nls(mp->m_nls); kmem_free(mp, sizeof(xfs_mount_t)); } =========================================================================== fs/xfs/xfs_vnodeops.c =========================================================================== --- a/fs/xfs/xfs_vnodeops.c 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_vnodeops.c 2008-01-21 14:27:33.000000000 +1100 @@ -1772,7 +1772,9 @@ int xfs_lookup( xfs_inode_t *dp, bhv_vname_t *dentry, - bhv_vnode_t **vpp) + bhv_vnode_t **vpp, + char **actual_name, + int *actual_namelen) { xfs_inode_t *ip; xfs_ino_t e_inum; @@ -1785,7 +1787,8 @@ xfs_lookup( return XFS_ERROR(EIO); lock_mode = xfs_ilock_map_shared(dp); - error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip); + error = xfs_dir_lookup_int(dp, lock_mode, dentry, &e_inum, &ip, + actual_name, actual_namelen); if (!error) { *vpp = XFS_ITOV(ip); xfs_itrace_ref(ip); =========================================================================== fs/xfs/xfs_vnodeops.h =========================================================================== --- a/fs/xfs/xfs_vnodeops.h 2008-01-21 14:42:51.000000000 +1100 +++ b/fs/xfs/xfs_vnodeops.h 2008-01-21 14:27:33.000000000 +1100 @@ -24,7 +24,7 @@ int xfs_fsync(struct xfs_inode *ip, int int xfs_release(struct xfs_inode *ip); int xfs_inactive(struct xfs_inode *ip); int xfs_lookup(struct xfs_inode *dp, bhv_vname_t *dentry, - bhv_vnode_t **vpp); + bhv_vnode_t **vpp, char **actual_name, int *actual_namelen); int xfs_create(struct xfs_inode *dp, bhv_vname_t *dentry, mode_t mode, xfs_dev_t rdev, bhv_vnode_t **vpp, struct cred *credp); int xfs_remove(struct xfs_inode *dp, bhv_vname_t *dentry); ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch 2008-01-21 3:53 ` Barry Naujok @ 2008-01-23 6:55 ` Christoph Hellwig 0 siblings, 0 replies; 5+ messages in thread From: Christoph Hellwig @ 2008-01-23 6:55 UTC (permalink / raw) To: Barry Naujok; +Cc: Eric Sandeen, xfs@oss.sgi.com, xfs-dev I'm currently sick in my bed, so just a very quick glance over the patch and a few comments: - no need to export the new inode ops, they're not used by the quota or dmapi methods. - any chance you could rebase this ontop of my various vnode removals, this should make some things a little cleaner. - there's a lot of hair dcache internals in here, so please run this patch past linux-fsdevel@vger.kernel.org to get some broader review. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [REVIEW 1/2] Case insensitive support for XFS - kernel patch 2008-01-18 4:43 [REVIEW 1/2] Case insensitive support for XFS - kernel patch Barry Naujok 2008-01-19 5:22 ` Eric Sandeen @ 2008-01-19 5:30 ` Eric Sandeen 1 sibling, 0 replies; 5+ messages in thread From: Eric Sandeen @ 2008-01-19 5:30 UTC (permalink / raw) To: Barry Naujok; +Cc: xfs@oss.sgi.com, xfs-dev Barry Naujok wrote: > This patch should apply to 2.6.24-rc6. Just FYI, on the stack picture, of the bigger stack users these are the deltas on my x86_64 box: -xfs_bulkstat 280 +xfs_bulkstat 296 :( -xfs_dir2_leaf_getdents 136 +xfs_dir2_leaf_getdents 200 -xfs_dir2_sf_to_block 120 +xfs_dir2_sf_to_block 136 -xfs_dir_canenter 144 +xfs_dir_canenter 152 -xfs_dir_createname 152 +xfs_dir_createname 168 -xfs_dir_lookup 152 +xfs_dir_lookup 168 -xfs_dir_replace 152 +xfs_dir_replace 168 -Eric ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2008-01-23 6:55 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-01-18 4:43 [REVIEW 1/2] Case insensitive support for XFS - kernel patch Barry Naujok 2008-01-19 5:22 ` Eric Sandeen 2008-01-21 3:53 ` Barry Naujok 2008-01-23 6:55 ` Christoph Hellwig 2008-01-19 5:30 ` Eric Sandeen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox