/* * namei.c - NTFS kernel directory inode operations. Part of the Linux-NTFS * project. * * Copyright (c) 2001,2002 Anton Altaparmakov. * * This program/include file 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; either version 2 of the License, or * (at your option) any later version. * * This program/include file is distributed in the hope that it will 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 (in the main directory of the Linux-NTFS * distribution in the file COPYING); if not, write to the Free Software * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/dcache.h> #include "ntfs.h" #include "dir.h" /** * ntfs_lookup - find the inode represented by a dentry in a directory inode * @dir_ino: directory inode in which to look for the inode * @dent: dentry representing the inode to look for * * In short, ntfs_lookup() looks for the inode represented by the dentry @dent * in the directory inode @dir_ino and if found attaches the inode to the * dentry @dent. * * In more detail, the dentry @dent specifies which inode to look for by * supplying the name of the inode in @dent->d_name.name. ntfs_lookup() * converts the name to Unicode and walks the contents of the directory inode * @dir_ino looking for the converted Unicode name. If the name is found in the * directory, the corresponding inode is loaded by calling iget() on its inode * number and the inode is associated with the dentry @dent via a call to * d_add(). * * If the name is not found in the directory, a NULL inode is inserted into the * dentry @dent. The dentry is then termed a negative dentry. * * Only if an actual error occurs, do we return an error via ERR_PTR(). * * 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. * * There are three cases we need to distinguish here: * * 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. */ 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; int uname_len; ntfs_debug("Looking up %s in directory inode 0x%lx.", dent->d_name.name, dir_ino->i_ino); /* Convert the name of the dentry to Unicode. */ uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len, &uname); if (uname_len < 0) { ntfs_error(vol->sb, "Failed to convert name to Unicode."); return ERR_PTR(uname_len); } mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len, &name); kmem_cache_free(ntfs_name_cache, uname); if (!IS_ERR_MREF(mref)) { dent_ino = (unsigned long)MREF(mref); ntfs_debug("Found inode 0x%lx. Calling iget.", dent_ino); dent_inode = iget(vol->sb, dent_ino); if (dent_inode) { /* Consistency check. */ if (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || dent_ino == FILE_MFT) { /* 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 = " "0x%x, inode sequence number = 0x%x, " "returning -EACCES. Run chkdsk.", (unsigned long long)MREF(mref), MSEQNO(mref), NTFS_I(dent_inode)->seq_no); iput(dent_inode); } else 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. */ d_add(dent, NULL); ntfs_debug("Done."); return NULL; } 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); /* * Note: No need for dparent_lock as i_sem is held on the parent inode. */ /* 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); } } /* * Inode operations for directories. */ struct inode_operations ntfs_dir_inode_ops = { lookup: ntfs_lookup, /* VFS: Lookup directory. */ };