📄 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/fs.h>#include <linux/msdos_fs.h>#include <linux/nls.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/stat.h>#include <linux/string.h>#include <linux/ioctl.h>#include <linux/dirent.h>#include <linux/mm.h>#include <linux/ctype.h>#include <asm/uaccess.h>#define PRINTK(X)struct file_operations fat_dir_operations = { read: generic_read_dir, readdir: fat_readdir, ioctl: fat_dir_ioctl, fsync: file_fsync,};/* * Convert Unicode 16 to UTF8, 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 intuni16_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);}#if 0static void dump_de(struct msdos_dir_entry *de){ int i; unsigned char *p = (unsigned char *) de; printk("["); for (i = 0; i < 32; i++, p++) { printk("%02x ", *p); } printk("]\n");}#endifstatic inline unsigned charfat_tolower(struct nls_table *t, unsigned char c){ unsigned char nc = t->charset2lower[c]; return nc ? nc : c;}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 intfat_strnicmp(struct nls_table *t, const unsigned char *s1, const unsigned char *s2, int len){ while(len--) if (fat_tolower(t, *s1++) != fat_tolower(t, *s2++)) return 1; return 0;}static inline intfat_shortname2uni(struct nls_table *nls, 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;}/* * 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 char *name, int name_len, int anycase, loff_t *spos, loff_t *lpos){ struct super_block *sb = inode->i_sb; struct buffer_head *bh = NULL; struct msdos_dir_entry *de; struct nls_table *nls_io = MSDOS_SB(sb)->nls_io; struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk; wchar_t bufuname[14]; unsigned char xlate_len, long_slots; wchar_t *unicode = NULL; char work[8], bufname[260]; /* 256 + 4 */ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; int utf8 = MSDOS_SB(sb)->options.utf8; unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname; int ino, chl, i, j, last_u, res = 0; loff_t cpos = 0; while(1) { if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) goto EODir;parse_record: long_slots = 0; if (de->name[0] == (__s8) 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) { struct msdos_dir_slot *ds; unsigned char id; unsigned char slot; unsigned char slots; unsigned char sum; unsigned char alias_checksum; if (!unicode) { unicode = (wchar_t *) __get_free_page(GFP_KERNEL); if (!unicode) { fat_brelse(sb, bh); return -ENOMEM; } }parse_long: slots = 0; ds = (struct msdos_dir_slot *) de; id = ds->id; if (!(id & 0x40)) continue; slots = id & ~0x40; if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ continue; long_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(inode,&cpos,&bh,&de,&ino)<0) goto EODir; if (slot == 0) break; ds = (struct msdos_dir_slot *) de; if (ds->attr != ATTR_EXT) goto parse_record; if ((ds->id & ~0x40) != slot) goto parse_long; if (ds->alias_checksum != alias_checksum) goto parse_long; } if (de->name[0] == (__s8) DELETED_FLAG) continue; if (de->attr == ATTR_EXT) goto parse_long; if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) continue; for (sum = 0, i = 0; i < 11; i++) sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; if (sum != alias_checksum) long_slots = 0; } for (i = 0; i < 8; i++) { /* see namei.c, msdos_format_name */ if (de->name[i] == 0x05) work[i] = 0xE5; else work[i] = de->name[i]; } 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 = 0; i < 3;) { if (!de->ext[i]) break; chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_EXT); if (chl <= 1) { if (de->ext[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 && !fat_strnicmp(nls_io, name, bufname, xlate_len))) goto Found; if (long_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 && !fat_strnicmp(nls_io, name, bufname, xlate_len))) goto Found; } }Found: res = long_slots + 1; *spos = cpos - sizeof(struct msdos_dir_entry); *lpos = cpos - res*sizeof(struct msdos_dir_entry);EODir: fat_brelse(sb, bh); if (unicode) { free_page((unsigned long) unicode); } return res;}static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir, int shortnames, int both){ struct super_block *sb = inode->i_sb; struct buffer_head *bh; struct msdos_dir_entry *de; struct nls_table *nls_io = MSDOS_SB(sb)->nls_io; struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk; wchar_t bufuname[14]; unsigned char long_slots; wchar_t *unicode = NULL; char c, work[8], bufname[56], *ptname = bufname; unsigned long lpos, dummy, *furrfu = &lpos; int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; int isvfat = MSDOS_SB(sb)->options.isvfat; int utf8 = MSDOS_SB(sb)->options.utf8; int nocase = MSDOS_SB(sb)->options.nocase; unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname; int ino, inum, chi, chl, i, i2, j, last, last_u, dotoffset = 0; loff_t cpos; cpos = filp->f_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) return 0; cpos++; filp->f_pos++; } if (cpos == 2) { dummy = 2; furrfu = &dummy; cpos = 0; } } if (cpos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT; bh = NULL;GetNew: long_slots = 0; if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) goto EODir; /* Check for long filename entry */ if (isvfat) { if (de->name[0] == (__s8) DELETED_FLAG)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -