Commit ccc55c48 authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: 2.0.8 release. Major updates for dcache aliasing issues wrt short/long file names.

In order to handle the case insensitivity issues of NTFS with regards to the
dcache and the dcache requiring only one dentry per directory, we deal with
dentry aliases that only differ in case in ->ntfs_lookup() while maintining
a case sensitive dcache. This means that we get the full benefit of dcache
speed when the file/directory is looked up with the same case as returned by
->ntfs_readdir() but that a lookup for any other case (or for the short file
name) will not find anything in dcache and will enter ->ntfs_lookup()
instead, where we search the directory for a fully matching file name
(including case) and if that is not found, we search for a file name that
matches with different case and if that has non-POSIX semantics we return
that. We actually do only one search (case sensitive) and keep tabs on
whether we have found a case insensitive match in the process.

To simplify matters for us, we do not treat the short vs long filenames as
two hard links but instead if the lookup matches a short filename, we
return the dentry for the corresponding long filename instead.

In ->ntfs_lookup() we distinguish three cases:

1) @dent perfectly matches (i.e. including case) a directory entry with a
   file name in the WIN32 or POSIX namespaces. In this case
   ntfs_lookup_inode_by_name() will return with name set to NULL and we
   just d_add() @dent.
2) @dent matches (not including case) a directory entry with a file name in
   the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return
   with name set to point to a kmalloc()ed ntfs_name structure containing
   the properly cased little endian Unicode name. We convert the name to the
   current NLS code page, search if a dentry with this name already exists
   and if so return that instead of @dent. The VFS will then destroy the old
   @dent and use the one we returned. If a dentry is not found, we allocate
   a new one, d_add() it, and return it as above.
3) @dent matches either perfectly or not (i.e. we don't care about case) a
   directory entry with a file name in the DOS namespace. In this case
   ntfs_lookup_inode_by_name() will return with name set to point to a
   kmalloc()ed ntfs_name structure containing the mft reference (cpu endian)
   of the inode. We use the mft reference to read the inode and to find the
   file name in the WIN32 namespace corresponding to the matched short file
   name. We then convert the name to the current NLS code page, and proceed
   searching for a dentry with this name, etc, as in case 2), above.
parent 6a98297e
......@@ -25,6 +25,9 @@ ToDo:
2.0.8 - Major updates for handling of case sensitivity and dcache aliasing.
Big thanks go to Al Viro and other inhabitants of #kernel for investing
their time to discuss the case sensitivity and dcache aliasing issues.
- Remove unused source file fs/ntfs/attraops.c.
- Remove show_inodes mount option(s), thus dropping support for
displaying of short file names.
......@@ -38,13 +41,18 @@ ToDo:
- Remove really dumb logic bug in boot sector recovery code.
- Fix dcache aliasing issues wrt short/long file names via changes
to fs/ntfs/dir.c::ntfs_lookup_inode_by_name() and
fs/ntfs/namei.c::ntfs_lookup(). (The latter is still TODO:)
- Add additional argument to ntfs_lookup_inode_by_name() in which we
return information about the matching file name if the case is not
matching or the match is a short file name. See comments above the
function definition for details.
fs/ntfs/namei.c::ntfs_lookup():
- Add additional argument to ntfs_lookup_inode_by_name() in which we
return information about the matching file name if the case is not
matching or the match is a short file name. See comments above the
function definition for details.
- Change ntfs_lookup() to only create dcache entries for the correctly
cased file name and only for the WIN32 namespace counterpart of DOS
namespace file names. This ensures we have only one dentry per
directory and also removes all dcache aliasing issues between short
and long file names once we add write support. See comments above
function for details.
- Fix potential 1 byte overflow in fs/ntfs/unistr.c::ntfs_ucstonls().
- TODO: (AIA) Change ntfs_lookup()...
2.0.7 - Minor cleanups and updates for changes in core kernel code.
......
......@@ -20,6 +20,8 @@
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/dcache.h>
#include "ntfs.h"
#include "dir.h"
......@@ -45,8 +47,6 @@
*
* Only if an actual error occurs, do we return an error via ERR_PTR().
*
* TODO: Implement the below! (AIA)
*
* In order to handle the case insensitivity issues of NTFS with regards to the
* dcache and the dcache requiring only one dentry per directory, we deal with
* dentry aliases that only differ in case in ->ntfs_lookup() while maintining
......@@ -71,13 +71,13 @@
* ntfs_lookup_inode_by_name() will return with name set to NULL and we
* just d_add() @dent.
* 2) @dent matches (not including case) a directory entry with a file name in
* the WIN32 or POSIX namespaces. In this case ntfs_lookup_inode_by_name()
* will return with name set to point to a kmalloc()ed ntfs_name structure
* containing the properly cased little endian Unicode name. We convert the
* name to the current NLS code page, search if a dentry with this name
* already exists and if so return that instead of @dent. The VFS will then
* destroy the old @dent and use the one we returned. If a dentry is not
* found, we allocate a new one, d_add() it, and return it as above.
* the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return
* with name set to point to a kmalloc()ed ntfs_name structure containing
* the properly cased little endian Unicode name. We convert the name to the
* current NLS code page, search if a dentry with this name already exists
* and if so return that instead of @dent. The VFS will then destroy the old
* @dent and use the one we returned. If a dentry is not found, we allocate
* a new one, d_add() it, and return it as above.
* 3) @dent matches either perfectly or not (i.e. we don't care about case) a
* directory entry with a file name in the DOS namespace. In this case
* ntfs_lookup_inode_by_name() will return with name set to point to a
......@@ -91,11 +91,11 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
{
ntfs_volume *vol = NTFS_SB(dir_ino->i_sb);
struct inode *dent_inode;
uchar_t *uname;
ntfs_name *name = NULL;
u64 mref;
unsigned long dent_ino;
uchar_t *uname;
int uname_len;
ntfs_name *name = NULL;
ntfs_debug("Looking up %s in directory inode 0x%lx.",
dent->d_name.name, dir_ino->i_ino);
......@@ -109,9 +109,6 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len,
&name);
kmem_cache_free(ntfs_name_cache, uname);
// TODO: Handle name. (AIA)
if (name)
kfree(name);
if (!IS_ERR_MREF(mref)) {
dent_ino = (unsigned long)MREF(mref);
ntfs_debug("Found inode 0x%lx. Calling iget.", dent_ino);
......@@ -120,9 +117,17 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
/* Consistency check. */
if (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no ||
dent_ino == FILE_MFT) {
d_add(dent, dent_inode);
ntfs_debug("Done.");
return NULL;
/* Perfect WIN32/POSIX match. -- Case 1. */
if (!name) {
d_add(dent, dent_inode);
ntfs_debug("Done.");
return NULL;
}
/*
* We are too indented. Handle imperfect
* matches and short file names further below.
*/
goto handle_name;
}
ntfs_error(vol->sb, "Found stale reference to inode "
"0x%Lx (reference sequence number = "
......@@ -136,8 +141,11 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
ntfs_error(vol->sb, "iget(0x%Lx) failed, returning "
"-EACCES.",
(unsigned long long)MREF(mref));
if (name)
kfree(name);
return ERR_PTR(-EACCES);
}
/* It is guaranteed that name is no longer allocated at this point. */
if (MREF_ERR(mref) == -ENOENT) {
ntfs_debug("Entry was not found, adding negative dentry.");
/* The dcache will handle negative entries. */
......@@ -148,6 +156,127 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error "
"code %i.", -MREF_ERR(mref));
return ERR_PTR(MREF_ERR(mref));
// TODO: Consider moving this lot to a separate function! (AIA)
handle_name:
{
struct dentry *real_dent;
attr_search_context *ctx;
ntfs_inode *ni = NTFS_I(dent_inode);
int err;
struct qstr nls_name;
nls_name.name = NULL;
if (name->type != FILE_NAME_DOS) { /* Case 2. */
nls_name.len = (unsigned)ntfs_ucstonls(vol,
(uchar_t*)&name->name, name->len,
(unsigned char**)&nls_name.name,
name->len * 3 + 1);
kfree(name);
} else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */
MFT_RECORD *m;
FILE_NAME_ATTR *fn;
kfree(name);
/* Find the WIN32 name corresponding to the matched DOS name. */
ni = NTFS_I(dent_inode);
m = map_mft_record(READ, ni);
if (IS_ERR(m)) {
err = PTR_ERR(m);
goto name_err_out;
}
ctx = get_attr_search_ctx(ni, m);
if (!ctx) {
err = -ENOMEM;
goto unm_err_out;
}
do {
ATTR_RECORD *a;
u32 val_len;
if (!lookup_attr(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0,
ctx)) {
ntfs_error(vol->sb, "Inode corrupt: No WIN32 "
"namespace counterpart to DOS "
"file name. Run chkdsk.");
err = -EIO;
goto put_unm_err_out;
}
/* Consistency checks. */
a = ctx->attr;
if (a->non_resident || a->flags)
goto eio_put_unm_err_out;
val_len = le32_to_cpu(a->_ARA(value_length));
if (le16_to_cpu(a->_ARA(value_offset)) + val_len >
le32_to_cpu(a->length))
goto eio_put_unm_err_out;
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(
ctx->attr->_ARA(value_offset)));
if ((u32)(fn->file_name_length * sizeof(uchar_t) +
sizeof(FILE_NAME_ATTR)) > val_len)
goto eio_put_unm_err_out;
} while (fn->file_name_type != FILE_NAME_WIN32);
/* Convert the found WIN32 name to current NLS code page. */
nls_name.len = (unsigned)ntfs_ucstonls(vol,
(uchar_t*)&fn->file_name, fn->file_name_length,
(unsigned char**)&nls_name.name,
fn->file_name_length * 3 + 1);
put_attr_search_ctx(ctx);
unmap_mft_record(READ, ni);
}
/* Check if a conversion error occured. */
if ((signed)nls_name.len < 0) {
err = (signed)nls_name.len;
goto name_err_out;
}
nls_name.hash = full_name_hash(nls_name.name, nls_name.len);
// FIXME: Do we need dcache_lock or dparent_lock here or is the
// fact that i_sem is held on the parent inode sufficient? (AIA)
/* Does a dentry matching the nls_name exist already? */
real_dent = d_lookup(dent->d_parent, &nls_name);
/* If not, create it now. */
if (!real_dent) {
real_dent = d_alloc(dent->d_parent, &nls_name);
kfree(nls_name.name);
if (!real_dent) {
err = -ENOMEM;
goto name_err_out;
}
d_add(real_dent, dent_inode);
return real_dent;
}
kfree(nls_name.name);
/* Matching dentry exists, check if it is negative. */
if (real_dent->d_inode) {
BUG_ON(real_dent->d_inode != dent_inode);
/*
* Already have the inode and the dentry attached, decrement
* the reference count to balance the iget() we did earlier on.
*/
iput(dent_inode);
return real_dent;
}
/* Negative dentry: instantiate it. */
d_instantiate(real_dent, dent_inode);
return real_dent;
eio_put_unm_err_out:
ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk.");
err = -EIO;
put_unm_err_out:
put_attr_search_ctx(ctx);
unm_err_out:
unmap_mft_record(READ, ni);
name_err_out:
iput(dent_inode);
return ERR_PTR(err);
}
}
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment