* [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-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
* 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
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