diff options
Diffstat (limited to 'fs/fat/dir.c')
| -rw-r--r-- | fs/fat/dir.c | 698 |
1 files changed, 387 insertions, 311 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 72cbcd61bd9..3963ede84eb 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -16,12 +16,29 @@ #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 <linux/uaccess.h> +#include <linux/kernel.h> +#include "fat.h" + +/* + * 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)) + +static inline unsigned char fat_tolower(unsigned char c) +{ + return ((c >= 'A') && (c <= 'Z')) ? c+32 : c; +} static inline loff_t fat_make_i_pos(struct super_block *sb, struct buffer_head *bh, @@ -78,7 +95,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 */ @@ -86,8 +103,8 @@ next: *bh = sb_bread(sb, phys); if (*bh == NULL) { - printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n", - (unsigned long long)phys); + fat_msg_ratelimit(sb, KERN_ERR, + "Directory bread(block %llu) failed", (llu)phys); /* skip this block */ *pos = (iblock + 1) << sb->s_blocksize_bits; goto next; @@ -106,7 +123,8 @@ static inline int fat_get_entry(struct inode *dir, loff_t *pos, { /* Fast stuff first */ if (*bh && *de && - (*de - (struct msdos_dir_entry *)(*bh)->b_data) < MSDOS_SB(dir->i_sb)->dir_per_block - 1) { + (*de - (struct msdos_dir_entry *)(*bh)->b_data) < + MSDOS_SB(dir->i_sb)->dir_per_block - 1) { *pos += sizeof(struct msdos_dir_entry); (*de)++; return 0; @@ -124,43 +142,55 @@ 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(struct super_block *sb, unsigned char *ascii, + const wchar_t *uni, int len, struct nls_table *nls) { - wchar_t *ip, ec; - unsigned char *op, nc; + int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; + const wchar_t *ip; + wchar_t ec; + unsigned char *op; int charlen; - int k; 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) { + charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE); + if (charlen > 0) { op += charlen; + len -= charlen; } else { if (uni_xlate == 1) { - *op = ':'; - for (k = 4; k > 0; k--) { - nc = ec & 0xF; - op[k] = nc > 9 ? nc + ('a' - 10) - : nc + '0'; - ec >>= 4; - } - op += 5; + *op++ = ':'; + op = hex_byte_pack(op, ec >> 8); + op = hex_byte_pack(op, ec); + 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)) { + fat_msg(sb, KERN_WARNING, + "filename was truncated while converting."); + } + *op = 0; - return (op - ascii); + return op - ascii; +} + +static inline int fat_uni_to_x8(struct super_block *sb, const wchar_t *uni, + unsigned char *buf, int size) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); + if (sbi->options.utf8) + return utf16s_to_utf8s(uni, FAT_MAX_UNI_CHARS, + UTF16_HOST_ENDIAN, buf, size); + else + return uni16_to_x8(sb, buf, uni, size, sbi->nls_io); } static inline int @@ -177,7 +207,8 @@ fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) } static inline int -fat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) +fat_short2lower_uni(struct nls_table *t, unsigned char *c, + int clen, wchar_t *uni) { int charlen; wchar_t wc; @@ -192,7 +223,8 @@ fat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *un if (!nc) nc = *c; - if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) { + charlen = t->char2uni(&nc, 1, uni); + if (charlen < 0) { *uni = 0x003f; /* a question mark */ charlen = 1; } @@ -223,6 +255,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 +288,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; @@ -297,9 +342,126 @@ parse_long: return 0; } +/** + * fat_parse_short - Parse MS-DOS (short) directory entry. + * @sb: superblock + * @de: directory entry to parse + * @name: FAT_MAX_SHORT_SIZE array in which to place extracted name + * @dot_hidden: Nonzero == prepend '.' to names with ATTR_HIDDEN + * + * Returns the number of characters extracted into 'name'. + */ +static int fat_parse_short(struct super_block *sb, + const struct msdos_dir_entry *de, + unsigned char *name, int dot_hidden) +{ + const struct msdos_sb_info *sbi = MSDOS_SB(sb); + int isvfat = sbi->options.isvfat; + int nocase = sbi->options.nocase; + unsigned short opt_shortname = sbi->options.shortname; + struct nls_table *nls_disk = sbi->nls_disk; + wchar_t uni_name[14]; + unsigned char c, work[MSDOS_NAME]; + unsigned char *ptname = name; + int chi, chl, i, j, k; + int dotoffset = 0; + int name_len = 0, uni_len = 0; + + if (!isvfat && dot_hidden && (de->attr & ATTR_HIDDEN)) { + *ptname++ = '.'; + dotoffset = 1; + } + + memcpy(work, de->name, sizeof(work)); + /* see namei.c, msdos_format_name */ + if (work[0] == 0x05) + work[0] = 0xE5; + + /* Filename */ + for (i = 0, j = 0; i < 8;) { + c = work[i]; + if (!c) + break; + chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, + &uni_name[j++], opt_shortname, + de->lcase & CASE_LOWER_BASE); + if (chl <= 1) { + if (!isvfat) + ptname[i] = nocase ? c : fat_tolower(c); + i++; + if (c != ' ') { + name_len = i; + uni_len = j; + } + } else { + uni_len = j; + if (isvfat) + i += min(chl, 8-i); + else { + for (chi = 0; chi < chl && i < 8; chi++, i++) + ptname[i] = work[i]; + } + if (chl) + name_len = i; + } + } + + i = name_len; + j = uni_len; + fat_short2uni(nls_disk, ".", 1, &uni_name[j++]); + if (!isvfat) + ptname[i] = '.'; + i++; + + /* Extension */ + for (k = 8; k < MSDOS_NAME;) { + c = work[k]; + if (!c) + break; + chl = fat_shortname2uni(nls_disk, &work[k], MSDOS_NAME - k, + &uni_name[j++], opt_shortname, + de->lcase & CASE_LOWER_EXT); + if (chl <= 1) { + k++; + if (!isvfat) + ptname[i] = nocase ? c : fat_tolower(c); + i++; + if (c != ' ') { + name_len = i; + uni_len = j; + } + } else { + uni_len = j; + if (isvfat) { + int offset = min(chl, MSDOS_NAME-k); + k += offset; + i += offset; + } else { + for (chi = 0; chi < chl && k < MSDOS_NAME; + chi++, i++, k++) { + ptname[i] = work[k]; + } + } + if (chl) + name_len = i; + } + } + + if (name_len > 0) { + name_len += dotoffset; + + if (sbi->options.isvfat) { + uni_name[uni_len] = 0x0000; + name_len = fat_uni_to_x8(sb, uni_name, name, + FAT_MAX_SHORT_SIZE); + } + } + + return name_len; +} + /* - * Return values: negative -> error, 0 -> not found, positive -> found, - * value is the total amount of slots, including the shortname entry. + * Return values: negative -> error/not found, 0 -> found. */ int fat_search_long(struct inode *inode, const unsigned char *name, int name_len, struct fat_slot_info *sinfo) @@ -308,23 +470,16 @@ 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 xlate_len, nr_slots; + unsigned char nr_slots; wchar_t *unicode = NULL; - unsigned char work[MSDOS_NAME], bufname[260]; /* 256 + 4 */ - int uni_xlate = sbi->options.unicode_xlate; - int utf8 = sbi->options.utf8; - int anycase = (sbi->options.name_check != 's'); - unsigned short opt_shortname = sbi->options.shortname; + unsigned char bufname[FAT_MAX_SHORT_SIZE]; loff_t cpos = 0; - int chl, i, j, last_u, err; + int 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,78 +491,42 @@ 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)); - /* see namei.c, msdos_format_name */ - if (work[0] == 0x05) - work[0] = 0xE5; - for (i = 0, j = 0, last_u = 0; i < 8;) { - if (!work[i]) - break; - chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, - &bufuname[j++], opt_shortname, - de->lcase & CASE_LOWER_BASE); - if (chl <= 1) { - if (work[i] != ' ') - last_u = j; - } else { - last_u = j; - } - i += chl; - } - j = last_u; - fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); - 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 (work[i] != ' ') - last_u = j; - } else { - last_u = j; - } - i += chl; - } - if (!last_u) + /* Never prepend '.' to hidden files here. + * That is done only for msdos mounts (and only when + * 'dotsOK=yes'); if we are executing here, it is in the + * context of a vfat mount. + */ + len = fat_parse_short(sb, de, bufname, 0); + if (len == 0) continue; - 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; + /* Compare shortname */ + 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(sb, 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; @@ -415,16 +534,16 @@ 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; } - EXPORT_SYMBOL_GPL(fat_search_long); struct fat_ioctl_filldir_callback { + struct dir_context ctx; void __user *dirent; int result; /* for dir ioctl */ @@ -434,158 +553,122 @@ struct fat_ioctl_filldir_callback { int short_len; }; -static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, - filldir_t filldir, int short_only, int both) +static int __fat_readdir(struct inode *inode, struct file *file, + struct dir_context *ctx, int short_only, + struct fat_ioctl_filldir_callback *both) { struct super_block *sb = inode->i_sb; 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; - wchar_t bufuname[14]; + unsigned char nr_slots; 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 bufname[FAT_MAX_SHORT_SIZE]; int isvfat = sbi->options.isvfat; - int utf8 = sbi->options.utf8; - int nocase = sbi->options.nocase; - unsigned short opt_shortname = sbi->options.shortname; - unsigned long inum; - int chi, chl, i, i2, j, last, last_u, dotoffset = 0; + const char *fill_name = NULL; + int fake_offset = 0; loff_t cpos; + int short_len = 0, fill_len = 0; int ret = 0; - lock_kernel(); + mutex_lock(&sbi->s_lock); - cpos = filp->f_pos; + cpos = ctx->pos; /* Fake . and .. for the root directory. */ if (inode->i_ino == MSDOS_ROOT_INO) { - while (cpos < 2) { - if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0) - goto out; - cpos++; - filp->f_pos++; - } - if (cpos == 2) { - dummy = 2; - furrfu = &dummy; + if (!dir_emit_dots(file, ctx)) + goto out; + if (ctx->pos == 2) { + fake_offset = 1; 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; + ctx->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 (sbi->options.dotsOK) { - ptname = bufname; - dotoffset = 0; - if (de->attr & ATTR_HIDDEN) { - *ptname++ = '.'; - dotoffset = 1; - } - } + if (nr_slots) { + void *longname = unicode + FAT_MAX_UNI_CHARS; + int size = PATH_MAX - FAT_MAX_UNI_SIZE; + int len = fat_uni_to_x8(sb, unicode, longname, size); - memcpy(work, de->name, sizeof(de->name)); - /* see namei.c, msdos_format_name */ - if (work[0] == 0x05) - work[0] = 0xE5; - for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) { - if (!(c = work[i])) - break; - chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, - &bufuname[j++], opt_shortname, - de->lcase & CASE_LOWER_BASE); - if (chl <= 1) { - ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c; - if (c != ' ') { - last = i; - last_u = j; - } - } else { - last_u = j; - for (chi = 0; chi < chl && i < 8; chi++) { - ptname[i] = work[i]; - i++; last = i; - } - } - } - i = last; - j = last_u; - fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); - ptname[i++] = '.'; - 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) { - i2++; - ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c; - if (c != ' ') { - last = i; - last_u = j; - } - } else { - last_u = j; - for (chi = 0; chi < chl && i2 < MSDOS_NAME; chi++) { - ptname[i++] = work[i2++]; - last = i; - } + fill_name = longname; + fill_len = len; + /* !both && !short_only, so we don't need shortname. */ + if (!both) + goto start_filldir; + + short_len = fat_parse_short(sb, de, bufname, + sbi->options.dotsOK); + if (short_len == 0) + goto record_end; + /* hack for fat_ioctl_filldir() */ + both->longname = fill_name; + both->long_len = fill_len; + both->shortname = bufname; + both->short_len = short_len; + fill_name = NULL; + fill_len = 0; + goto start_filldir; } } - if (!last) - goto RecEnd; - i = last + dotoffset; - j = last_u; + short_len = fat_parse_short(sb, de, bufname, sbi->options.dotsOK); + if (short_len == 0) + goto record_end; - lpos = cpos - (long_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_path.dentry); + fill_name = bufname; + fill_len = short_len; + +start_filldir: + if (!fake_offset) + ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry); + + if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) { + if (!dir_emit_dot(file, ctx)) + goto fill_failed; + } else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { + if (!dir_emit_dotdot(file, ctx)) + goto fill_failed; } else { + unsigned long inum; loff_t i_pos = fat_make_i_pos(sb, bh, de); struct inode *tmp = fat_iget(sb, i_pos); if (tmp) { @@ -593,63 +676,29 @@ parse_record: iput(tmp); } else inum = iunique(sb, MSDOS_ROOT_INO); + if (!dir_emit(ctx, fill_name, fill_len, inum, + (de->attr & ATTR_DIR) ? DT_DIR : DT_REG)) + goto fill_failed; } - 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; - -RecEnd: - furrfu = &lpos; - filp->f_pos = cpos; - goto GetNew; -EODir: - filp->f_pos = cpos; -FillFailed: +record_end: + fake_offset = 0; + ctx->pos = cpos; + goto get_new; +end_of_dir: + ctx->pos = cpos; +fill_failed: brelse(bh); if (unicode) - free_page((unsigned long)unicode); + __putname(unicode); out: - unlock_kernel(); + mutex_unlock(&sbi->s_lock); return ret; } -static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) +static int fat_readdir(struct file *file, struct dir_context *ctx) { - struct inode *inode = filp->f_path.dentry->d_inode; - return __fat_readdir(inode, filp, dirent, filldir, 0, 0); + return __fat_readdir(file_inode(file), file, ctx, 0, NULL); } #define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type) \ @@ -703,22 +752,27 @@ 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, +static int fat_ioctl_readdir(struct inode *inode, struct file *file, void __user *dirent, filldir_t filldir, int short_only, int both) { - struct fat_ioctl_filldir_callback buf; + struct fat_ioctl_filldir_callback buf = { + .ctx.actor = filldir, + .dirent = dirent + }; int ret; buf.dirent = dirent; buf.result = 0; mutex_lock(&inode->i_mutex); + buf.ctx.pos = file->f_pos; ret = -ENOENT; if (!IS_DEADDIR(inode)) { - ret = __fat_readdir(inode, filp, &buf, filldir, - short_only, both); + ret = __fat_readdir(inode, file, &buf.ctx, + short_only, both ? &buf : NULL); + file->f_pos = buf.ctx.pos; } mutex_unlock(&inode->i_mutex); if (ret >= 0) @@ -726,10 +780,11 @@ static int fat_ioctl_readdir(struct inode *inode, struct file *filp, return ret; } -static int fat_dir_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +static long fat_dir_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) { - struct dirent __user *d1 = (struct dirent __user *)arg; + struct inode *inode = file_inode(filp); + struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg; int short_only, both; switch (cmd) { @@ -742,10 +797,10 @@ static int fat_dir_ioctl(struct inode *inode, struct file *filp, both = 1; break; default: - return fat_generic_ioctl(inode, filp, cmd, arg); + return fat_generic_ioctl(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 @@ -768,7 +823,7 @@ FAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent) static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd, unsigned long arg) { - struct inode *inode = filp->f_path.dentry->d_inode; + struct inode *inode = file_inode(filp); struct compat_dirent __user *d1 = compat_ptr(arg); int short_only, both; @@ -782,7 +837,7 @@ static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd, both = 1; break; default: - return -ENOIOCTLCMD; + return fat_generic_ioctl(filp, cmd, (unsigned long)arg); } if (!access_ok(VERIFY_WRITE, d1, sizeof(struct compat_dirent[2]))) @@ -801,13 +856,14 @@ 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, + .iterate = fat_readdir, + .unlocked_ioctl = fat_dir_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = fat_compat_dir_ioctl, #endif - .fsync = file_fsync, + .fsync = fat_file_fsync, }; static int fat_get_short_entry(struct inode *dir, loff_t *pos, @@ -823,25 +879,26 @@ static int fat_get_short_entry(struct inode *dir, loff_t *pos, } /* - * The ".." entry can not provide the "struct fat_slot_info" informations - * for inode. So, this function provide the some informations only. + * The ".." entry can not provide the "struct fat_slot_info" information + * for inode, nor a usable i_pos. So, this function provides some information + * only. + * + * Since this function walks through the on-disk inodes within a directory, + * callers are responsible for taking any locks necessary to prevent the + * directory from changing. */ int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, - struct msdos_dir_entry **de, loff_t *i_pos) + struct msdos_dir_entry **de) { - loff_t offset; + loff_t offset = 0; - offset = 0; - *bh = NULL; + *de = NULL; while (fat_get_short_entry(dir, &offset, bh, de) >= 0) { - if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) { - *i_pos = fat_make_i_pos(dir->i_sb, *bh, *de); + if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) return 0; - } } return -ENOENT; } - EXPORT_SYMBOL_GPL(fat_get_dotdot_entry); /* See if directory is empty */ @@ -864,7 +921,6 @@ int fat_dir_empty(struct inode *dir) brelse(bh); return result; } - EXPORT_SYMBOL_GPL(fat_dir_empty); /* @@ -910,9 +966,31 @@ int fat_scan(struct inode *dir, const unsigned char *name, } return -ENOENT; } - EXPORT_SYMBOL_GPL(fat_scan); +/* + * Scans a directory for a given logstart. + * Returns an error code or zero. + */ +int fat_scan_logstart(struct inode *dir, int i_logstart, + struct fat_slot_info *sinfo) +{ + struct super_block *sb = dir->i_sb; + + sinfo->slot_off = 0; + sinfo->bh = NULL; + while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh, + &sinfo->de) >= 0) { + if (fat_get_start(MSDOS_SB(sb), sinfo->de) == i_logstart) { + sinfo->slot_off -= sizeof(*sinfo->de); + sinfo->nr_slots = 1; + sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); + return 0; + } + } + return -ENOENT; +} + static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) { struct super_block *sb = dir->i_sb; @@ -934,7 +1012,7 @@ static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) de++; nr_slots--; } - mark_buffer_dirty(bh); + mark_buffer_dirty_inode(bh, dir); if (IS_DIRSYNC(dir)) err = sync_dirty_buffer(bh); brelse(bh); @@ -950,6 +1028,7 @@ static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) { + struct super_block *sb = dir->i_sb; struct msdos_dir_entry *de; struct buffer_head *bh; int err = 0, nr_slots; @@ -968,7 +1047,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) de--; nr_slots--; } - mark_buffer_dirty(bh); + mark_buffer_dirty_inode(bh, dir); if (IS_DIRSYNC(dir)) err = sync_dirty_buffer(bh); brelse(bh); @@ -984,8 +1063,8 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) */ err = __fat_remove_entries(dir, sinfo->slot_off, nr_slots); if (err) { - printk(KERN_WARNING - "FAT: Couldn't remove the long name slots\n"); + fat_msg(sb, KERN_WARNING, + "Couldn't remove the long name slots"); } } @@ -997,7 +1076,6 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) return 0; } - EXPORT_SYMBOL_GPL(fat_remove_entries); static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, @@ -1018,7 +1096,7 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, } memset(bhs[n]->b_data, 0, sb->s_blocksize); set_buffer_uptodate(bhs[n]); - mark_buffer_dirty(bhs[n]); + mark_buffer_dirty_inode(bhs[n], dir); n++; blknr++; @@ -1057,6 +1135,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); @@ -1070,7 +1149,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) */ @@ -1080,23 +1159,22 @@ 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); - de[0].starthi = cpu_to_le16(cluster >> 16); - de[1].start = cpu_to_le16(MSDOS_I(dir)->i_logstart); - de[1].starthi = cpu_to_le16(MSDOS_I(dir)->i_logstart >> 16); + fat_set_start(&de[0], cluster); + fat_set_start(&de[1], MSDOS_I(dir)->i_logstart); de[0].size = de[1].size = 0; memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de)); set_buffer_uptodate(bhs[0]); - mark_buffer_dirty(bhs[0]); + mark_buffer_dirty_inode(bhs[0], dir); err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE); if (err) @@ -1109,7 +1187,6 @@ error_free: error: return err; } - EXPORT_SYMBOL_GPL(fat_alloc_new_dir); static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, @@ -1158,7 +1235,7 @@ static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, slots += copy; size -= copy; set_buffer_uptodate(bhs[n]); - mark_buffer_dirty(bhs[n]); + mark_buffer_dirty_inode(bhs[n], dir); if (!size) break; n++; @@ -1198,13 +1275,13 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, struct super_block *sb = dir->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */ - struct msdos_dir_entry *de; + struct msdos_dir_entry *uninitialized_var(de); int err, free_slots, i, nr_bhs; loff_t pos, i_pos; sinfo->nr_slots = nr_slots; - /* First stage: search free direcotry entries */ + /* First stage: search free directory entries */ free_slots = nr_bhs = 0; bh = prev = NULL; pos = 0; @@ -1234,7 +1311,7 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, if (sbi->fat_bits != 32) goto error; } else if (MSDOS_I(dir)->i_start == 0) { - printk(KERN_ERR "FAT: Corrupted directory (i_pos %lld)\n", + fat_msg(sb, KERN_ERR, "Corrupted directory (i_pos %lld)", MSDOS_I(dir)->i_pos); err = -EIO; goto error; @@ -1258,7 +1335,7 @@ found: for (i = 0; i < long_bhs; i++) { int copy = min_t(int, sb->s_blocksize - offset, size); memcpy(bhs[i]->b_data + offset, slots, copy); - mark_buffer_dirty(bhs[i]); + mark_buffer_dirty_inode(bhs[i], dir); offset = 0; slots += copy; size -= copy; @@ -1269,7 +1346,7 @@ found: /* Fill the short name slot. */ int copy = min_t(int, sb->s_blocksize - offset, size); memcpy(bhs[i]->b_data + offset, slots, copy); - mark_buffer_dirty(bhs[i]); + mark_buffer_dirty_inode(bhs[i], dir); if (IS_DIRSYNC(dir)) err = sync_dirty_buffer(bhs[i]); } @@ -1299,7 +1376,7 @@ found: goto error_remove; } if (dir->i_size & (sbi->cluster_size - 1)) { - fat_fs_panic(sb, "Odd directory size"); + fat_fs_error(sb, "Odd directory size"); dir->i_size = (dir->i_size + sbi->cluster_size - 1) & ~((loff_t)sbi->cluster_size - 1); } @@ -1325,5 +1402,4 @@ error_remove: __fat_remove_entries(dir, pos, free_slots); return err; } - EXPORT_SYMBOL_GPL(fat_add_entries); |
