📄 dir.c
字号:
/* * linux/fs/fat/dir.c * * directory handling functions for fat-based filesystems * * Written 1992,1993 by Werner Almesberger * * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> * * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu> * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk> * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de> */#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>static inline loff_t fat_make_i_pos(struct super_block *sb, struct buffer_head *bh, struct msdos_dir_entry *de){ return ((loff_t)bh->b_blocknr << MSDOS_SB(sb)->dir_per_block_bits) | (de - (struct msdos_dir_entry *)bh->b_data);}static inline void fat_dir_readahead(struct inode *dir, sector_t iblock, sector_t phys){ struct super_block *sb = dir->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); struct buffer_head *bh; int sec; /* This is not a first sector of cluster, or sec_per_clus == 1 */ if ((iblock & (sbi->sec_per_clus - 1)) || sbi->sec_per_clus == 1) return; /* root dir of FAT12/FAT16 */ if ((sbi->fat_bits != 32) && (dir->i_ino == MSDOS_ROOT_INO)) return; bh = sb_find_get_block(sb, phys); if (bh == NULL || !buffer_uptodate(bh)) { for (sec = 0; sec < sbi->sec_per_clus; sec++) sb_breadahead(sb, phys + sec); } brelse(bh);}/* Returns the inode number of the directory entry at offset pos. If bh is non-NULL, it is brelse'd before. Pos is incremented. The buffer header is returned in bh. AV. Most often we do it item-by-item. Makes sense to optimize. AV. OK, there we go: if both bh and de are non-NULL we assume that we just AV. want the next entry (took one explicit de=NULL in vfat/namei.c). AV. It's done in fat_get_entry() (inlined), here the slow case lives. AV. Additionally, when we return -1 (i.e. reached the end of directory) AV. we make bh NULL. */static int fat__get_entry(struct inode *dir, loff_t *pos, struct buffer_head **bh, struct msdos_dir_entry **de){ struct super_block *sb = dir->i_sb; sector_t phys, iblock; unsigned long mapped_blocks; int err, offset;next: if (*bh) brelse(*bh); *bh = NULL; iblock = *pos >> sb->s_blocksize_bits; err = fat_bmap(dir, iblock, &phys, &mapped_blocks); if (err || !phys) return -1; /* beyond EOF or error */ fat_dir_readahead(dir, iblock, phys); *bh = sb_bread(sb, phys); if (*bh == NULL) { printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n", (unsigned long long)phys); /* skip this block */ *pos = (iblock + 1) << sb->s_blocksize_bits; goto next; } offset = *pos & (sb->s_blocksize - 1); *pos += sizeof(struct msdos_dir_entry); *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); return 0;}static inline int fat_get_entry(struct inode *dir, loff_t *pos, struct buffer_head **bh, struct msdos_dir_entry **de){ /* Fast stuff first */ if (*bh && *de && (*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; } return fat__get_entry(dir, pos, bh, de);}/* * Convert Unicode 16 to UTF-8, translated Unicode, or ASCII. * If uni_xlate is enabled and we can't get a 1:1 conversion, use a * colon as an escape character since it is normally invalid on the vfat * filesystem. The following four characters are the hexadecimal digits * of Unicode value. This lets us do a full dump and restore of Unicode * filenames. We could get into some trouble with long Unicode names, * 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){ wchar_t *ip, ec; unsigned char *op, nc; int charlen; int k; ip = uni; op = ascii; while (*ip) { ec = *ip++; if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) { op += 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; } else { *op++ = '?'; } } /* We have some slack there, so it's OK */ if (op>ascii+256) { op = ascii + 256; break; } } *op = 0; return (op - ascii);}static inline intfat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni){ int charlen; charlen = t->char2uni(c, clen, uni); if (charlen < 0) { *uni = 0x003f; /* a question mark */ charlen = 1; } return charlen;}static inline intfat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni){ int charlen; wchar_t wc; charlen = t->char2uni(c, clen, &wc); if (charlen < 0) { *uni = 0x003f; /* a question mark */ charlen = 1; } else if (charlen <= 1) { unsigned char nc = t->charset2lower[*c]; if (!nc) nc = *c; if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) { *uni = 0x003f; /* a question mark */ charlen = 1; } } else *uni = wc; return charlen;}static inline intfat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, wchar_t *uni_buf, unsigned short opt, int lower){ int len = 0; if (opt & VFAT_SFN_DISPLAY_LOWER) len = fat_short2lower_uni(nls, buf, buf_size, uni_buf); else if (opt & VFAT_SFN_DISPLAY_WIN95) len = fat_short2uni(nls, buf, buf_size, uni_buf); else if (opt & VFAT_SFN_DISPLAY_WINNT) { if (lower) len = fat_short2lower_uni(nls, buf, buf_size, uni_buf); else len = fat_short2uni(nls, buf, buf_size, uni_buf); } else len = fat_short2uni(nls, buf, buf_size, uni_buf); return len;}enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, };/** * fat_parse_long - Parse extended directory entry. * * This function returns zero on success, negative value on error, or one of * the following: * * %PARSE_INVALID - Directory entry is invalid. * %PARSE_NOT_LONGNAME - Directory entry does not contain longname. * %PARSE_EOF - Directory has no more entries. */static int fat_parse_long(struct inode *dir, loff_t *pos, struct buffer_head **bh, struct msdos_dir_entry **de, wchar_t **unicode, unsigned char *nr_slots){ struct msdos_dir_slot *ds; unsigned char id, slot, slots, alias_checksum; if (!*unicode) { *unicode = (wchar_t *)__get_free_page(GFP_KERNEL); if (!*unicode) { brelse(*bh); return -ENOMEM; } }parse_long: slots = 0; ds = (struct msdos_dir_slot *)*de; id = ds->id; if (!(id & 0x40)) return PARSE_INVALID; slots = id & ~0x40; if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ return PARSE_INVALID; *nr_slots = slots; alias_checksum = ds->alias_checksum; slot = slots; while (1) { int offset; slot--; offset = slot * 13; fat16_towchar(*unicode + offset, ds->name0_4, 5); fat16_towchar(*unicode + offset + 5, ds->name5_10, 6); fat16_towchar(*unicode + offset + 11, ds->name11_12, 2); if (ds->id & 0x40) (*unicode)[offset + 13] = 0; if (fat_get_entry(dir, pos, bh, de) < 0) return PARSE_EOF; if (slot == 0) break; ds = (struct msdos_dir_slot *)*de; if (ds->attr != ATTR_EXT) return PARSE_NOT_LONGNAME; if ((ds->id & ~0x40) != slot) goto parse_long; if (ds->alias_checksum != alias_checksum) goto parse_long; } if ((*de)->name[0] == DELETED_FLAG) return PARSE_INVALID; if ((*de)->attr == ATTR_EXT) goto parse_long; if (IS_FREE((*de)->name) || ((*de)->attr & ATTR_VOLUME)) return PARSE_INVALID; if (fat_checksum((*de)->name) != alias_checksum) *nr_slots = 0; return 0;}/* * Return values: negative -> error, 0 -> not found, positive -> found, * value is the total amount of slots, including the shortname entry. */int fat_search_long(struct inode *inode, const unsigned char *name, int name_len, struct fat_slot_info *sinfo){ struct super_block *sb = inode->i_sb; 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; 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; loff_t cpos = 0; int chl, i, j, last_u, err; err = -ENOENT; while(1) { if (fat_get_entry(inode, &cpos, &bh, &de) == -1) goto EODir;parse_record: nr_slots = 0; if (de->name[0] == DELETED_FLAG) continue; if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) continue; if (de->attr != ATTR_EXT && IS_FREE(de->name)) continue; 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) continue; else if (status == PARSE_NOT_LONGNAME) goto parse_record; else if (status == PARSE_EOF) goto EODir; } 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) 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; 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; } }Found: nr_slots++; /* include the de */ sinfo->slot_off = cpos - nr_slots * sizeof(*de); sinfo->nr_slots = nr_slots; sinfo->de = de; sinfo->bh = bh; sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); err = 0;EODir: if (unicode) free_page((unsigned long)unicode); return err;}EXPORT_SYMBOL_GPL(fat_search_long);struct fat_ioctl_filldir_callback { void __user *dirent; int result; /* for dir ioctl */ const char *longname; int long_len; const char *shortname; int short_len;};static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir, int short_only, int 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -