X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Ffat%2Fdir.c;h=3a7f603b698246fde1952ff8d464f50d21126885;hb=7eef4091a653c243a87e5375c54504cc03bec4d8;hp=3e50a41662834834538886e5cd0aa4d48d0c3b08;hpb=188f83dfe0eeecd1427d0d255cc97dbf7ef6b4b7;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 3e50a41..3a7f603 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -16,12 +16,11 @@ #include #include #include -#include -#include #include #include #include #include +#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 uni_xlate, - struct nls_table *nls) +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; @@ -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,20 +150,33 @@ 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); } +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) { @@ -223,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, }; /** @@ -243,7 +270,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; @@ -298,6 +325,19 @@ parse_long: } /* + * 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. */ @@ -308,23 +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; + unsigned char nr_slots; wchar_t bufuname[14]; - unsigned char xlate_len, nr_slots; wchar_t *unicode = NULL; - unsigned char work[8], bufname[260]; /* 256 + 4 */ - int uni_xlate = sbi->options.unicode_xlate; - int utf8 = sbi->options.utf8; - int anycase = (sbi->options.name_check != 's'); + unsigned char work[MSDOS_NAME]; + 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; + 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) @@ -336,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)); @@ -351,7 +389,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 +404,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; @@ -381,30 +422,24 @@ parse_record: if (!last_u) continue; + /* Compare shortname */ bufuname[last_u] = 0x0000; - xlate_len = utf8 - ?utf8_wcstombs(bufname, bufuname, sizeof(bufname)) - :uni16_to_x8(bufname, bufuname, 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, sizeof(bufname)) - :uni16_to_x8(bufname, unicode, 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; @@ -412,9 +447,9 @@ Found: sinfo->bh = bh; sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); err = 0; -EODir: +end_of_dir: if (unicode) - free_page((unsigned long)unicode); + __putname(unicode); return err; } @@ -422,7 +457,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; @@ -438,26 +473,23 @@ 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[8], 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_kernel(); + lock_super(sb); cpos = filp->f_pos; /* Fake . and .. for the root directory. */ @@ -474,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) { @@ -527,7 +574,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 +597,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,23 +612,43 @@ 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; } } } 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)) { - inum = parent_ino(filp->f_dentry); + inum = parent_ino(filp->f_path.dentry); } else { loff_t i_pos = fat_make_i_pos(sb, bh, de); struct inode *tmp = fat_iget(sb, i_pos); @@ -590,119 +659,110 @@ 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, 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 = PAGE_SIZE - (261 * sizeof(unicode[0])); - int long_len = utf8 - ? utf8_wcstombs(longname, unicode, buf_size) - : uni16_to_x8(longname, unicode, 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) - free_page((unsigned long)unicode); + __putname(unicode); out: - unlock_kernel(); + unlock_super(sb); return ret; } static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct inode *inode = filp->f_dentry->d_inode; + struct inode *inode = filp->f_path.dentry->d_inode; 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, ino_t 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, __fat_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 __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg; + int short_only, both; switch (cmd) { case VFAT_IOCTL_READDIR_SHORT: @@ -717,8 +777,7 @@ 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]))) + if (!access_ok(VERIFY_WRITE, d1, sizeof(struct __fat_dirent[2]))) return -EFAULT; /* * Yes, we don't need this put_user() absolutely. However old @@ -728,73 +787,53 @@ 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; - - return 0; -} +FAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent) -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_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 */ const struct file_operations fat_dir_operations = { + .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = fat_readdir, .ioctl = fat_dir_ioctl, @@ -1051,6 +1090,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); @@ -1064,7 +1104,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) */ @@ -1074,13 +1114,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);