fat: Fix stat->f_namelen
[linux-2.6.git] / fs / fat / namei_vfat.c
index bf326d4..c1ef501 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/jiffies.h>
 #include <linux/ctype.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/namei.h>
 #include "fat.h"
@@ -78,7 +77,7 @@ static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
         * for creation.
         */
        if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
-               if (nd->flags & LOOKUP_CREATE)
+               if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
                        return 0;
        }
 
@@ -166,13 +165,13 @@ static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
        return 1;
 }
 
-static struct dentry_operations vfat_ci_dentry_ops = {
+static const struct dentry_operations vfat_ci_dentry_ops = {
        .d_revalidate   = vfat_revalidate_ci,
        .d_hash         = vfat_hashi,
        .d_compare      = vfat_cmpi,
 };
 
-static struct dentry_operations vfat_dentry_ops = {
+static const struct dentry_operations vfat_dentry_ops = {
        .d_revalidate   = vfat_revalidate,
        .d_hash         = vfat_hash,
        .d_compare      = vfat_cmp,
@@ -500,24 +499,17 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
        int charlen;
 
        if (utf8) {
-               int name_len = strlen(name);
-
-               *outlen = utf8_mbstowcs((wchar_t *)outname, name, PATH_MAX);
-
-               /*
-                * We stripped '.'s before and set len appropriately,
-                * but utf8_mbstowcs doesn't care about len
-                */
-               *outlen -= (name_len - len);
-
-               if (*outlen > 255)
+               *outlen = utf8s_to_utf16s(name, len, (wchar_t *)outname);
+               if (*outlen < 0)
+                       return *outlen;
+               else if (*outlen > FAT_LFN_LEN)
                        return -ENAMETOOLONG;
 
                op = &outname[*outlen * sizeof(wchar_t)];
        } else {
                if (nls) {
                        for (i = 0, ip = name, op = outname, *outlen = 0;
-                            i < len && *outlen <= 255;
+                            i < len && *outlen <= FAT_LFN_LEN;
                             *outlen += 1)
                        {
                                if (escape && (*ip == ':')) {
@@ -557,7 +549,7 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
                                return -ENAMETOOLONG;
                } else {
                        for (i = 0, ip = name, op = outname, *outlen = 0;
-                            i < len && *outlen <= 255;
+                            i < len && *outlen <= FAT_LFN_LEN;
                             i++, *outlen += 1)
                        {
                                *op++ = *ip++;
@@ -709,6 +701,15 @@ static int vfat_find(struct inode *dir, struct qstr *qname,
        return fat_search_long(dir, qname->name, len, sinfo);
 }
 
+/*
+ * (nfsd's) anonymous disconnected dentry?
+ * NOTE: !IS_ROOT() is not anonymous (I.e. d_splice_alias() did the job).
+ */
+static int vfat_d_anon_disconn(struct dentry *dentry)
+{
+       return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED);
+}
+
 static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
                                  struct nameidata *nd)
 {
@@ -737,11 +738,11 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
        }
 
        alias = d_find_alias(inode);
-       if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
+       if (alias && !vfat_d_anon_disconn(alias)) {
                /*
-                * This inode has non DCACHE_DISCONNECTED dentry. This
-                * means, the user did ->lookup() by an another name
-                * (longname vs 8.3 alias of it) in past.
+                * This inode has non anonymous-DCACHE_DISCONNECTED
+                * dentry. This means, the user did ->lookup() by an
+                * another name (longname vs 8.3 alias of it) in past.
                 *
                 * Switch to new one for reason of locality if possible.
                 */
@@ -751,7 +752,9 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
                iput(inode);
                unlock_super(sb);
                return alias;
-       }
+       } else
+               dput(alias);
+
 out:
        unlock_super(sb);
        dentry->d_op = sb->s_root->d_op;
@@ -965,7 +968,7 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
                int start = MSDOS_I(new_dir)->i_logstart;
                dotdot_de->start = cpu_to_le16(start);
                dotdot_de->starthi = cpu_to_le16(start >> 16);
-               mark_buffer_dirty(dotdot_bh);
+               mark_buffer_dirty_inode(dotdot_bh, old_inode);
                if (IS_DIRSYNC(new_dir)) {
                        err = sync_dirty_buffer(dotdot_bh);
                        if (err)
@@ -1009,7 +1012,7 @@ error_dotdot:
                int start = MSDOS_I(old_dir)->i_logstart;
                dotdot_de->start = cpu_to_le16(start);
                dotdot_de->starthi = cpu_to_le16(start >> 16);
-               mark_buffer_dirty(dotdot_bh);
+               mark_buffer_dirty_inode(dotdot_bh, old_inode);
                corrupt |= sync_dirty_buffer(dotdot_bh);
        }
 error_inode:
@@ -1030,7 +1033,7 @@ error_inode:
                sinfo.bh = NULL;
        }
        if (corrupt < 0) {
-               fat_fs_panic(new_dir->i_sb,
+               fat_fs_error(new_dir->i_sb,
                             "%s: Filesystem corrupted (i_pos %lld)",
                             __func__, sinfo.i_pos);
        }