]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/fat/dir.c
Merge branch 'linux-2.6' into merge
[linux-2.6-omap-h63xx.git] / fs / fat / dir.c
index c16af246d245ddd45daf90a59ef0612fe5904dac..486725ee99ae68a977bc9ca2d8e8cd47990ed579 100644 (file)
@@ -124,8 +124,8 @@ 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 uni_xlate,
-                      struct nls_table *nls)
+static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len,
+                      int uni_xlate, struct nls_table *nls)
 {
        wchar_t *ip, ec;
        unsigned char *op, nc;
@@ -135,10 +135,11 @@ static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
        ip = uni;
        op = ascii;
 
-       while (*ip) {
+       while (*ip && ((len - NLS_MAX_CHARSET_SIZE) > 0)) {
                ec = *ip++;
                if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) {
                        op += charlen;
+                       len -= charlen;
                } else {
                        if (uni_xlate == 1) {
                                *op = ':';
@@ -149,16 +150,19 @@ static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
                                        ec >>= 4;
                                }
                                op += 5;
+                               len -= 5;
                        } else {
                                *op++ = '?';
+                               len--;
                        }
                }
-               /* We have some slack there, so it's OK */
-               if (op>ascii+256) {
-                       op = ascii + 256;
-                       break;
-               }
        }
+
+       if (unlikely(*ip)) {
+               printk(KERN_WARNING "FAT: filename was truncated while "
+                      "converting.");
+       }
+
        *op = 0;
        return (op - ascii);
 }
@@ -243,7 +247,7 @@ static int fat_parse_long(struct inode *dir, loff_t *pos,
        unsigned char id, slot, slots, alias_checksum;
 
        if (!*unicode) {
-               *unicode = (wchar_t *)__get_free_page(GFP_KERNEL);
+               *unicode = __getname();
                if (!*unicode) {
                        brelse(*bh);
                        return -ENOMEM;
@@ -311,9 +315,11 @@ int fat_search_long(struct inode *inode, const unsigned char *name,
        struct nls_table *nls_io = sbi->nls_io;
        struct nls_table *nls_disk = sbi->nls_disk;
        wchar_t bufuname[14];
-       unsigned char xlate_len, nr_slots;
+       unsigned char nr_slots;
+       int xlate_len;
        wchar_t *unicode = NULL;
-       unsigned char work[8], bufname[260];    /* 256 + 4 */
+       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');
@@ -321,6 +327,10 @@ int fat_search_long(struct inode *inode, const unsigned char *name,
        loff_t cpos = 0;
        int chl, i, j, last_u, err;
 
+       bufname = __getname();
+       if (!bufname)
+               return -ENOMEM;
+
        err = -ENOENT;
        while(1) {
                if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
@@ -351,7 +361,8 @@ parse_record:
                if (work[0] == 0x05)
                        work[0] = 0xE5;
                for (i = 0, j = 0, last_u = 0; i < 8;) {
-                       if (!work[i]) break;
+                       if (!work[i])
+                               break;
                        chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
                                                &bufuname[j++], opt_shortname,
                                                de->lcase & CASE_LOWER_BASE);
@@ -365,13 +376,15 @@ parse_record:
                }
                j = last_u;
                fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
-               for (i = 0; i < 3;) {
-                       if (!de->ext[i]) break;
-                       chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i,
+               for (i = 8; i < MSDOS_NAME;) {
+                       if (!work[i])
+                               break;
+                       chl = fat_shortname2uni(nls_disk, &work[i],
+                                               MSDOS_NAME - i,
                                                &bufuname[j++], opt_shortname,
                                                de->lcase & CASE_LOWER_EXT);
                        if (chl <= 1) {
-                               if (de->ext[i] != ' ')
+                               if (work[i] != ' ')
                                        last_u = j;
                        } else {
                                last_u = j;
@@ -383,8 +396,8 @@ parse_record:
 
                bufuname[last_u] = 0x0000;
                xlate_len = utf8
-                       ?utf8_wcstombs(bufname, bufuname, sizeof(bufname))
-                       :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
+                       ?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,
@@ -393,8 +406,8 @@ parse_record:
 
                if (nr_slots) {
                        xlate_len = utf8
-                               ?utf8_wcstombs(bufname, unicode, sizeof(bufname))
-                               :uni16_to_x8(bufname, unicode, uni_xlate, nls_io);
+                               ?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)) ||
@@ -413,8 +426,10 @@ Found:
        sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
        err = 0;
 EODir:
+       if (bufname)
+               __putname(bufname);
        if (unicode)
-               free_page((unsigned long)unicode);
+               __putname(unicode);
 
        return err;
 }
@@ -422,7 +437,7 @@ EODir:
 EXPORT_SYMBOL_GPL(fat_search_long);
 
 struct fat_ioctl_filldir_callback {
-       struct dirent __user *dirent;
+       void __user *dirent;
        int result;
        /* for dir ioctl */
        const char *longname;
@@ -445,7 +460,7 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,
        int fill_len;
        wchar_t bufuname[14];
        wchar_t *unicode = NULL;
-       unsigned char c, work[8], bufname[56], *ptname = bufname;
+       unsigned char c, work[MSDOS_NAME], bufname[56], *ptname = bufname;
        unsigned long lpos, dummy, *furrfu = &lpos;
        int uni_xlate = sbi->options.unicode_xlate;
        int isvfat = sbi->options.isvfat;
@@ -527,7 +542,8 @@ parse_record:
        if (work[0] == 0x05)
                work[0] = 0xE5;
        for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) {
-               if (!(c = work[i])) break;
+               if (!(c = work[i]))
+                       break;
                chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
                                        &bufuname[j++], opt_shortname,
                                        de->lcase & CASE_LOWER_BASE);
@@ -549,9 +565,10 @@ parse_record:
        j = last_u;
        fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
        ptname[i++] = '.';
-       for (i2 = 0; i2 < 3;) {
-               if (!(c = de->ext[i2])) break;
-               chl = fat_shortname2uni(nls_disk, &de->ext[i2], 3 - i2,
+       for (i2 = 8; i2 < MSDOS_NAME;) {
+               if (!(c = work[i2]))
+                       break;
+               chl = fat_shortname2uni(nls_disk, &work[i2], MSDOS_NAME - i2,
                                        &bufuname[j++], opt_shortname,
                                        de->lcase & CASE_LOWER_EXT);
                if (chl <= 1) {
@@ -563,8 +580,8 @@ parse_record:
                        }
                } else {
                        last_u = j;
-                       for (chi = 0; chi < chl && i2 < 3; chi++) {
-                               ptname[i++] = de->ext[i2++];
+                       for (chi = 0; chi < chl && i2 < MSDOS_NAME; chi++) {
+                               ptname[i++] = work[i2++];
                                last = i;
                        }
                }
@@ -593,7 +610,7 @@ parse_record:
        if (isvfat) {
                bufuname[j] = 0x0000;
                i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname))
-                        : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
+                        : uni16_to_x8(bufname, bufuname, sizeof(bufname), uni_xlate, nls_io);
        }
 
        fill_name = bufname;
@@ -602,10 +619,10 @@ parse_record:
                /* convert the unicode long name. 261 is maximum size
                 * of unicode buffer. (13 * slots + nul) */
                void *longname = unicode + 261;
-               int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0]));
+               int buf_size = PATH_MAX - (261 * sizeof(unicode[0]));
                int long_len = utf8
                        ? utf8_wcstombs(longname, unicode, buf_size)
-                       : uni16_to_x8(longname, unicode, uni_xlate, nls_io);
+                       : uni16_to_x8(longname, unicode, buf_size, uni_xlate, nls_io);
 
                if (!both) {
                        fill_name = longname;
@@ -635,7 +652,7 @@ EODir:
 FillFailed:
        brelse(bh);
        if (unicode)
-               free_page((unsigned long)unicode);
+               __putname(unicode);
 out:
        unlock_kernel();
        return ret;
@@ -647,62 +664,85 @@ static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
        return __fat_readdir(inode, filp, dirent, filldir, 0, 0);
 }
 
-static int fat_ioctl_filldir(void *__buf, const char *name, int name_len,
-                            loff_t offset, u64 ino, unsigned int d_type)
+#define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type)                         \
+static int func(void *__buf, const char *name, int name_len,              \
+                            loff_t offset, u64 ino, unsigned int d_type)  \
+{                                                                         \
+       struct fat_ioctl_filldir_callback *buf = __buf;                    \
+       struct dirent_type __user *d1 = buf->dirent;                       \
+       struct dirent_type __user *d2 = d1 + 1;                            \
+                                                                          \
+       if (buf->result)                                                   \
+               return -EINVAL;                                            \
+       buf->result++;                                                     \
+                                                                          \
+       if (name != NULL) {                                                \
+               /* dirent has only short name */                           \
+               if (name_len >= sizeof(d1->d_name))                        \
+                       name_len = sizeof(d1->d_name) - 1;                 \
+                                                                          \
+               if (put_user(0, d2->d_name)                     ||         \
+                   put_user(0, &d2->d_reclen)                  ||         \
+                   copy_to_user(d1->d_name, name, name_len)    ||         \
+                   put_user(0, d1->d_name + name_len)          ||         \
+                   put_user(name_len, &d1->d_reclen))                     \
+                       goto efault;                                       \
+       } else {                                                           \
+               /* dirent has short and long name */                       \
+               const char *longname = buf->longname;                      \
+               int long_len = buf->long_len;                              \
+               const char *shortname = buf->shortname;                    \
+               int short_len = buf->short_len;                            \
+                                                                          \
+               if (long_len >= sizeof(d1->d_name))                        \
+                       long_len = sizeof(d1->d_name) - 1;                 \
+               if (short_len >= sizeof(d1->d_name))                       \
+                       short_len = sizeof(d1->d_name) - 1;                \
+                                                                          \
+               if (copy_to_user(d2->d_name, longname, long_len)        || \
+                   put_user(0, d2->d_name + long_len)                  || \
+                   put_user(long_len, &d2->d_reclen)                   || \
+                   put_user(ino, &d2->d_ino)                           || \
+                   put_user(offset, &d2->d_off)                        || \
+                   copy_to_user(d1->d_name, shortname, short_len)      || \
+                   put_user(0, d1->d_name + short_len)                 || \
+                   put_user(short_len, &d1->d_reclen))                    \
+                       goto efault;                                       \
+       }                                                                  \
+       return 0;                                                          \
+efault:                                                                           \
+       buf->result = -EFAULT;                                             \
+       return -EFAULT;                                                    \
+}
+
+FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, dirent)
+
+static int fat_ioctl_readdir(struct inode *inode, struct file *filp,
+                            void __user *dirent, filldir_t filldir,
+                            int short_only, int both)
 {
-       struct fat_ioctl_filldir_callback *buf = __buf;
-       struct dirent __user *d1 = buf->dirent;
-       struct dirent __user *d2 = d1 + 1;
-
-       if (buf->result)
-               return -EINVAL;
-       buf->result++;
-
-       if (name != NULL) {
-               /* dirent has only short name */
-               if (name_len >= sizeof(d1->d_name))
-                       name_len = sizeof(d1->d_name) - 1;
-
-               if (put_user(0, d2->d_name)                     ||
-                   put_user(0, &d2->d_reclen)                  ||
-                   copy_to_user(d1->d_name, name, name_len)    ||
-                   put_user(0, d1->d_name + name_len)          ||
-                   put_user(name_len, &d1->d_reclen))
-                       goto efault;
-       } else {
-               /* dirent has short and long name */
-               const char *longname = buf->longname;
-               int long_len = buf->long_len;
-               const char *shortname = buf->shortname;
-               int short_len = buf->short_len;
-
-               if (long_len >= sizeof(d1->d_name))
-                       long_len = sizeof(d1->d_name) - 1;
-               if (short_len >= sizeof(d1->d_name))
-                       short_len = sizeof(d1->d_name) - 1;
-
-               if (copy_to_user(d2->d_name, longname, long_len)        ||
-                   put_user(0, d2->d_name + long_len)                  ||
-                   put_user(long_len, &d2->d_reclen)                   ||
-                   put_user(ino, &d2->d_ino)                           ||
-                   put_user(offset, &d2->d_off)                        ||
-                   copy_to_user(d1->d_name, shortname, short_len)      ||
-                   put_user(0, d1->d_name + short_len)                 ||
-                   put_user(short_len, &d1->d_reclen))
-                       goto efault;
+       struct fat_ioctl_filldir_callback buf;
+       int ret;
+
+       buf.dirent = dirent;
+       buf.result = 0;
+       mutex_lock(&inode->i_mutex);
+       ret = -ENOENT;
+       if (!IS_DEADDIR(inode)) {
+               ret = __fat_readdir(inode, filp, &buf, filldir,
+                                   short_only, both);
        }
-       return 0;
-efault:
-       buf->result = -EFAULT;
-       return -EFAULT;
+       mutex_unlock(&inode->i_mutex);
+       if (ret >= 0)
+               ret = buf.result;
+       return ret;
 }
 
-static int fat_dir_ioctl(struct inode * inode, struct file * filp,
-                 unsigned int cmd, unsigned long arg)
+static int fat_dir_ioctl(struct inode *inode, struct file *filp,
+                        unsigned int cmd, unsigned long arg)
 {
-       struct fat_ioctl_filldir_callback buf;
-       struct dirent __user *d1;
-       int ret, short_only, both;
+       struct dirent __user *d1 = (struct dirent __user *)arg;
+       int short_only, both;
 
        switch (cmd) {
        case VFAT_IOCTL_READDIR_SHORT:
@@ -717,7 +757,6 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp,
                return fat_generic_ioctl(inode, filp, cmd, arg);
        }
 
-       d1 = (struct dirent __user *)arg;
        if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2])))
                return -EFAULT;
        /*
@@ -728,69 +767,48 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp,
        if (put_user(0, &d1->d_reclen))
                return -EFAULT;
 
-       buf.dirent = d1;
-       buf.result = 0;
-       mutex_lock(&inode->i_mutex);
-       ret = -ENOENT;
-       if (!IS_DEADDIR(inode)) {
-               ret = __fat_readdir(inode, filp, &buf, fat_ioctl_filldir,
-                                   short_only, both);
-       }
-       mutex_unlock(&inode->i_mutex);
-       if (ret >= 0)
-               ret = buf.result;
-       return ret;
+       return fat_ioctl_readdir(inode, filp, d1, fat_ioctl_filldir,
+                                short_only, both);
 }
 
 #ifdef CONFIG_COMPAT
 #define        VFAT_IOCTL_READDIR_BOTH32       _IOR('r', 1, struct compat_dirent[2])
 #define        VFAT_IOCTL_READDIR_SHORT32      _IOR('r', 2, struct compat_dirent[2])
 
-static long fat_compat_put_dirent32(struct dirent *d,
-                                   struct compat_dirent __user *d32)
-{
-        if (!access_ok(VERIFY_WRITE, d32, sizeof(struct compat_dirent)))
-                return -EFAULT;
-
-        __put_user(d->d_ino, &d32->d_ino);
-        __put_user(d->d_off, &d32->d_off);
-        __put_user(d->d_reclen, &d32->d_reclen);
-        if (__copy_to_user(d32->d_name, d->d_name, d->d_reclen))
-               return -EFAULT;
+FAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent)
 
-        return 0;
-}
-
-static long fat_compat_dir_ioctl(struct file *file, unsigned cmd,
+static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd,
                                 unsigned long arg)
 {
-       struct compat_dirent __user *p = compat_ptr(arg);
-       int ret;
-       mm_segment_t oldfs = get_fs();
-       struct dirent d[2];
+       struct inode *inode = filp->f_path.dentry->d_inode;
+       struct compat_dirent __user *d1 = compat_ptr(arg);
+       int short_only, both;
 
        switch (cmd) {
-       case VFAT_IOCTL_READDIR_BOTH32:
-               cmd = VFAT_IOCTL_READDIR_BOTH;
-               break;
        case VFAT_IOCTL_READDIR_SHORT32:
-               cmd = VFAT_IOCTL_READDIR_SHORT;
+               short_only = 1;
+               both = 0;
+               break;
+       case VFAT_IOCTL_READDIR_BOTH32:
+               short_only = 0;
+               both = 1;
                break;
        default:
                return -ENOIOCTLCMD;
        }
 
-       set_fs(KERNEL_DS);
-       lock_kernel();
-       ret = fat_dir_ioctl(file->f_path.dentry->d_inode, file,
-                           cmd, (unsigned long) &d);
-       unlock_kernel();
-       set_fs(oldfs);
-       if (ret >= 0) {
-               ret |= fat_compat_put_dirent32(&d[0], p);
-               ret |= fat_compat_put_dirent32(&d[1], p + 1);
-       }
-       return ret;
+       if (!access_ok(VERIFY_WRITE, d1, sizeof(struct compat_dirent[2])))
+               return -EFAULT;
+       /*
+        * Yes, we don't need this put_user() absolutely. However old
+        * code didn't return the right value. So, app use this value,
+        * in order to check whether it is EOF.
+        */
+       if (put_user(0, &d1->d_reclen))
+               return -EFAULT;
+
+       return fat_ioctl_readdir(inode, filp, d1, fat_compat_ioctl_filldir,
+                                short_only, both);
 }
 #endif /* CONFIG_COMPAT */