]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/fat/dir.c
fat: i_blocks warning fix
[linux-2.6-omap-h63xx.git] / fs / fat / dir.c
index 34541d06e6263b1817d96ee4d640f2cc8aa35eb7..67e0583570983ccb7683dcbf10dcdb6d67288909 100644 (file)
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/time.h>
-#include <linux/msdos_fs.h>
-#include <linux/dirent.h>
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/compat.h>
 #include <asm/uaccess.h>
+#include "fat.h"
 
 static inline loff_t fat_make_i_pos(struct super_block *sb,
                                    struct buffer_head *bh,
@@ -78,7 +77,7 @@ next:
 
        *bh = NULL;
        iblock = *pos >> sb->s_blocksize_bits;
-       err = fat_bmap(dir, iblock, &phys, &mapped_blocks);
+       err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0);
        if (err || !phys)
                return -1;      /* beyond EOF or error */
 
@@ -87,7 +86,7 @@ next:
        *bh = sb_bread(sb, phys);
        if (*bh == NULL) {
                printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n",
-                      (unsigned long long)phys);
+                      (llu)phys);
                /* skip this block */
                *pos = (iblock + 1) << sb->s_blocksize_bits;
                goto next;
@@ -124,10 +123,11 @@ static inline int fat_get_entry(struct inode *dir, loff_t *pos,
  * but ignore that right now.
  * Ahem... Stack smashing in ring 0 isn't fun. Fixed.
  */
-static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len,
+static int uni16_to_x8(unsigned char *ascii, const wchar_t *uni, int len,
                       int uni_xlate, struct nls_table *nls)
 {
-       wchar_t *ip, ec;
+       const wchar_t *ip;
+       wchar_t ec;
        unsigned char *op, nc;
        int charlen;
        int k;
@@ -167,6 +167,16 @@ static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len,
        return (op - ascii);
 }
 
+static inline int fat_uni_to_x8(struct msdos_sb_info *sbi, const wchar_t *uni,
+                               unsigned char *buf, int size)
+{
+       if (sbi->options.utf8)
+               return utf8_wcstombs(buf, uni, size);
+       else
+               return uni16_to_x8(buf, uni, size, sbi->options.unicode_xlate,
+                                  sbi->nls_io);
+}
+
 static inline int
 fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
 {
@@ -227,6 +237,19 @@ fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size,
        return len;
 }
 
+static inline int fat_name_match(struct msdos_sb_info *sbi,
+                                const unsigned char *a, int a_len,
+                                const unsigned char *b, int b_len)
+{
+       if (a_len != b_len)
+               return 0;
+
+       if (sbi->options.name_check != 's')
+               return !nls_strnicmp(sbi->nls_io, a, b, a_len);
+       else
+               return !memcmp(a, b, a_len);
+}
+
 enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, };
 
 /**
@@ -301,6 +324,19 @@ parse_long:
        return 0;
 }
 
+/*
+ * Maximum buffer size of short name.
+ * [(MSDOS_NAME + '.') * max one char + nul]
+ * For msdos style, ['.' (hidden) + MSDOS_NAME + '.' + nul]
+ */
+#define FAT_MAX_SHORT_SIZE     ((MSDOS_NAME + 1) * NLS_MAX_CHARSET_SIZE + 1)
+/*
+ * Maximum buffer size of unicode chars from slots.
+ * [(max longname slots * 13 (size in a slot) + nul) * sizeof(wchar_t)]
+ */
+#define FAT_MAX_UNI_CHARS      ((MSDOS_SLOTS - 1) * 13 + 1)
+#define FAT_MAX_UNI_SIZE       (FAT_MAX_UNI_CHARS * sizeof(wchar_t))
+
 /*
  * Return values: negative -> error, 0 -> not found, positive -> found,
  * value is the total amount of slots, including the shortname entry.
@@ -312,29 +348,20 @@ int fat_search_long(struct inode *inode, const unsigned char *name,
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        struct buffer_head *bh = NULL;
        struct msdos_dir_entry *de;
-       struct nls_table *nls_io = sbi->nls_io;
        struct nls_table *nls_disk = sbi->nls_disk;
-       wchar_t bufuname[14];
        unsigned char nr_slots;
-       int xlate_len;
+       wchar_t bufuname[14];
        wchar_t *unicode = NULL;
        unsigned char work[MSDOS_NAME];
-       unsigned char *bufname = NULL;
-       int uni_xlate = sbi->options.unicode_xlate;
-       int utf8 = sbi->options.utf8;
-       int anycase = (sbi->options.name_check != 's');
+       unsigned char bufname[FAT_MAX_SHORT_SIZE];
        unsigned short opt_shortname = sbi->options.shortname;
        loff_t cpos = 0;
-       int chl, i, j, last_u, err;
-
-       bufname = __getname();
-       if (!bufname)
-               return -ENOMEM;
+       int chl, i, j, last_u, err, len;
 
        err = -ENOENT;
-       while(1) {
+       while (1) {
                if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
-                       goto EODir;
+                       goto end_of_dir;
 parse_record:
                nr_slots = 0;
                if (de->name[0] == DELETED_FLAG)
@@ -346,14 +373,15 @@ parse_record:
                if (de->attr == ATTR_EXT) {
                        int status = fat_parse_long(inode, &cpos, &bh, &de,
                                                    &unicode, &nr_slots);
-                       if (status < 0)
-                               return status;
-                       else if (status == PARSE_INVALID)
+                       if (status < 0) {
+                               err = status;
+                               goto end_of_dir;
+                       } else if (status == PARSE_INVALID)
                                continue;
                        else if (status == PARSE_NOT_LONGNAME)
                                goto parse_record;
                        else if (status == PARSE_EOF)
-                               goto EODir;
+                               goto end_of_dir;
                }
 
                memcpy(work, de->name, sizeof(de->name));
@@ -394,30 +422,24 @@ parse_record:
                if (!last_u)
                        continue;
 
+               /* Compare shortname */
                bufuname[last_u] = 0x0000;
-               xlate_len = utf8
-                       ?utf8_wcstombs(bufname, bufuname, PATH_MAX)
-                       :uni16_to_x8(bufname, bufuname, PATH_MAX, uni_xlate, nls_io);
-               if (xlate_len == name_len)
-                       if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
-                           (anycase && !nls_strnicmp(nls_io, name, bufname,
-                                                               xlate_len)))
-                               goto Found;
+               len = fat_uni_to_x8(sbi, bufuname, bufname, sizeof(bufname));
+               if (fat_name_match(sbi, name, name_len, bufname, len))
+                       goto found;
 
                if (nr_slots) {
-                       xlate_len = utf8
-                               ?utf8_wcstombs(bufname, unicode, PATH_MAX)
-                               :uni16_to_x8(bufname, unicode, PATH_MAX, uni_xlate, nls_io);
-                       if (xlate_len != name_len)
-                               continue;
-                       if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
-                           (anycase && !nls_strnicmp(nls_io, name, bufname,
-                                                               xlate_len)))
-                               goto Found;
+                       void *longname = unicode + FAT_MAX_UNI_CHARS;
+                       int size = PATH_MAX - FAT_MAX_UNI_SIZE;
+
+                       /* Compare longname */
+                       len = fat_uni_to_x8(sbi, unicode, longname, size);
+                       if (fat_name_match(sbi, name, name_len, longname, len))
+                               goto found;
                }
        }
 
-Found:
+found:
        nr_slots++;     /* include the de */
        sinfo->slot_off = cpos - nr_slots * sizeof(*de);
        sinfo->nr_slots = nr_slots;
@@ -425,9 +447,7 @@ Found:
        sinfo->bh = bh;
        sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
        err = 0;
-EODir:
-       if (bufname)
-               __putname(bufname);
+end_of_dir:
        if (unicode)
                __putname(unicode);
 
@@ -453,23 +473,20 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        struct buffer_head *bh;
        struct msdos_dir_entry *de;
-       struct nls_table *nls_io = sbi->nls_io;
        struct nls_table *nls_disk = sbi->nls_disk;
-       unsigned char long_slots;
-       const char *fill_name;
-       int fill_len;
+       unsigned char nr_slots;
        wchar_t bufuname[14];
        wchar_t *unicode = NULL;
-       unsigned char c, work[MSDOS_NAME], bufname[56], *ptname = bufname;
-       unsigned long lpos, dummy, *furrfu = &lpos;
-       int uni_xlate = sbi->options.unicode_xlate;
+       unsigned char c, work[MSDOS_NAME];
+       unsigned char bufname[FAT_MAX_SHORT_SIZE], *ptname = bufname;
+       unsigned short opt_shortname = sbi->options.shortname;
        int isvfat = sbi->options.isvfat;
-       int utf8 = sbi->options.utf8;
        int nocase = sbi->options.nocase;
-       unsigned short opt_shortname = sbi->options.shortname;
+       const char *fill_name = NULL;
        unsigned long inum;
-       int chi, chl, i, i2, j, last, last_u, dotoffset = 0;
+       unsigned long lpos, dummy, *furrfu = &lpos;
        loff_t cpos;
+       int chi, chl, i, i2, j, last, last_u, dotoffset = 0, fill_len = 0;
        int ret = 0;
 
        lock_super(sb);
@@ -489,43 +506,58 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,
                        cpos = 0;
                }
        }
-       if (cpos & (sizeof(struct msdos_dir_entry)-1)) {
+       if (cpos & (sizeof(struct msdos_dir_entry) - 1)) {
                ret = -ENOENT;
                goto out;
        }
 
        bh = NULL;
-GetNew:
+get_new:
        if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
-               goto EODir;
+               goto end_of_dir;
 parse_record:
-       long_slots = 0;
-       /* Check for long filename entry */
-       if (isvfat) {
+       nr_slots = 0;
+       /*
+        * Check for long filename entry, but if short_only, we don't
+        * need to parse long filename.
+        */
+       if (isvfat && !short_only) {
                if (de->name[0] == DELETED_FLAG)
-                       goto RecEnd;
+                       goto record_end;
                if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
-                       goto RecEnd;
+                       goto record_end;
                if (de->attr != ATTR_EXT && IS_FREE(de->name))
-                       goto RecEnd;
+                       goto record_end;
        } else {
                if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
-                       goto RecEnd;
+                       goto record_end;
        }
 
        if (isvfat && de->attr == ATTR_EXT) {
                int status = fat_parse_long(inode, &cpos, &bh, &de,
-                                           &unicode, &long_slots);
+                                           &unicode, &nr_slots);
                if (status < 0) {
                        filp->f_pos = cpos;
                        ret = status;
                        goto out;
                } else if (status == PARSE_INVALID)
-                       goto RecEnd;
+                       goto record_end;
                else if (status == PARSE_NOT_LONGNAME)
                        goto parse_record;
                else if (status == PARSE_EOF)
-                       goto EODir;
+                       goto end_of_dir;
+
+               if (nr_slots) {
+                       void *longname = unicode + FAT_MAX_UNI_CHARS;
+                       int size = PATH_MAX - FAT_MAX_UNI_SIZE;
+                       int len = fat_uni_to_x8(sbi, unicode, longname, size);
+
+                       fill_name = longname;
+                       fill_len = len;
+                       /* !both && !short_only, so we don't need shortname. */
+                       if (!both)
+                               goto start_filldir;
+               }
        }
 
        if (sbi->options.dotsOK) {
@@ -587,12 +619,32 @@ parse_record:
                }
        }
        if (!last)
-               goto RecEnd;
+               goto record_end;
 
        i = last + dotoffset;
        j = last_u;
 
-       lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
+       if (isvfat) {
+               bufuname[j] = 0x0000;
+               i = fat_uni_to_x8(sbi, bufuname, bufname, sizeof(bufname));
+       }
+       if (nr_slots) {
+               /* hack for fat_ioctl_filldir() */
+               struct fat_ioctl_filldir_callback *p = dirent;
+
+               p->longname = fill_name;
+               p->long_len = fill_len;
+               p->shortname = bufname;
+               p->short_len = i;
+               fill_name = NULL;
+               fill_len = 0;
+       } else {
+               fill_name = bufname;
+               fill_len = i;
+       }
+
+start_filldir:
+       lpos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry);
        if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME))
                inum = inode->i_ino;
        else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) {
@@ -607,49 +659,17 @@ parse_record:
                        inum = iunique(sb, MSDOS_ROOT_INO);
        }
 
-       if (isvfat) {
-               bufuname[j] = 0x0000;
-               i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname))
-                        : uni16_to_x8(bufname, bufuname, sizeof(bufname), uni_xlate, nls_io);
-       }
-
-       fill_name = bufname;
-       fill_len = i;
-       if (!short_only && long_slots) {
-               /* convert the unicode long name. 261 is maximum size
-                * of unicode buffer. (13 * slots + nul) */
-               void *longname = unicode + 261;
-               int buf_size = PATH_MAX - (261 * sizeof(unicode[0]));
-               int long_len = utf8
-                       ? utf8_wcstombs(longname, unicode, buf_size)
-                       : uni16_to_x8(longname, unicode, buf_size, uni_xlate, nls_io);
-
-               if (!both) {
-                       fill_name = longname;
-                       fill_len = long_len;
-               } else {
-                       /* hack for fat_ioctl_filldir() */
-                       struct fat_ioctl_filldir_callback *p = dirent;
-
-                       p->longname = longname;
-                       p->long_len = long_len;
-                       p->shortname = bufname;
-                       p->short_len = i;
-                       fill_name = NULL;
-                       fill_len = 0;
-               }
-       }
        if (filldir(dirent, fill_name, fill_len, *furrfu, inum,
                    (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
-               goto FillFailed;
+               goto fill_failed;
 
-RecEnd:
+record_end:
        furrfu = &lpos;
        filp->f_pos = cpos;
-       goto GetNew;
-EODir:
+       goto get_new;
+end_of_dir:
        filp->f_pos = cpos;
-FillFailed:
+fill_failed:
        brelse(bh);
        if (unicode)
                __putname(unicode);
@@ -715,7 +735,7 @@ efault:                                                                        \
        return -EFAULT;                                                    \
 }
 
-FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, dirent)
+FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, __fat_dirent)
 
 static int fat_ioctl_readdir(struct inode *inode, struct file *filp,
                             void __user *dirent, filldir_t filldir,
@@ -741,7 +761,7 @@ static int fat_ioctl_readdir(struct inode *inode, struct file *filp,
 static int fat_dir_ioctl(struct inode *inode, struct file *filp,
                         unsigned int cmd, unsigned long arg)
 {
-       struct dirent __user *d1 = (struct dirent __user *)arg;
+       struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg;
        int short_only, both;
 
        switch (cmd) {
@@ -757,7 +777,7 @@ static int fat_dir_ioctl(struct inode *inode, struct file *filp,
                return fat_generic_ioctl(inode, filp, cmd, arg);
        }
 
-       if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2])))
+       if (!access_ok(VERIFY_WRITE, d1, sizeof(struct __fat_dirent[2])))
                return -EFAULT;
        /*
         * Yes, we don't need this put_user() absolutely. However old
@@ -813,6 +833,7 @@ static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd,
 #endif /* CONFIG_COMPAT */
 
 const struct file_operations fat_dir_operations = {
+       .llseek         = generic_file_llseek,
        .read           = generic_read_dir,
        .readdir        = fat_readdir,
        .ioctl          = fat_dir_ioctl,
@@ -820,6 +841,7 @@ const struct file_operations fat_dir_operations = {
        .compat_ioctl   = fat_compat_dir_ioctl,
 #endif
        .fsync          = file_fsync,
+       .llseek         = generic_file_llseek,
 };
 
 static int fat_get_short_entry(struct inode *dir, loff_t *pos,
@@ -1069,6 +1091,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
        struct msdos_dir_entry *de;
        sector_t blknr;
        __le16 date, time;
+       u8 time_cs;
        int err, cluster;
 
        err = fat_alloc_clusters(dir, &cluster, 1);
@@ -1082,7 +1105,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
                goto error_free;
        }
 
-       fat_date_unix2dos(ts->tv_sec, &time, &date);
+       fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
 
        de = (struct msdos_dir_entry *)bhs[0]->b_data;
        /* filling the new directory slots ("." and ".." entries) */
@@ -1092,13 +1115,14 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
        de[0].lcase = de[1].lcase = 0;
        de[0].time = de[1].time = time;
        de[0].date = de[1].date = date;
-       de[0].ctime_cs = de[1].ctime_cs = 0;
        if (sbi->options.isvfat) {
                /* extra timestamps */
                de[0].ctime = de[1].ctime = time;
+               de[0].ctime_cs = de[1].ctime_cs = time_cs;
                de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date;
        } else {
                de[0].ctime = de[1].ctime = 0;
+               de[0].ctime_cs = de[1].ctime_cs = 0;
                de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0;
        }
        de[0].start = cpu_to_le16(cluster);