📄 namei.c
字号:
/* * linux/fs/vfat/namei.c * * Written 1992,1993 by Werner Almesberger * * Windows95/Windows NT compatible extended MSDOS filesystem * by Gordon Chaffee Copyright (C) 1995. Send bug reports for the * VFAT filesystem to <chaffee@plateau.cs.berkeley.edu>. Specify * what file operation caused you trouble and if you can duplicate * the problem, send a script that demonstrates it. */#include <linux/config.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/sched.h>#include <linux/msdos_fs.h>#include <linux/nls.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/stat.h>#include <linux/mm.h>#include <linux/malloc.h>#include "../fat/msbuffer.h"#if 0# define PRINTK(x) printk x#else# define PRINTK(x)#endif#ifndef DEBUG# define CHECK_STACK#else# define CHECK_STACK check_stack(__FILE__, __LINE__)#endif/* * XXX: It would be better to use the tolower from linux/ctype.h, * but _ctype is needed and it is not exported. */#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))struct vfat_find_info { const char *name; int len; int new_filename; int found; int is_long; off_t offset; off_t short_offset; int long_slots; ino_t ino; int posix;};void vfat_read_inode(struct inode *inode);void vfat_put_super(struct super_block *sb){ fat_put_super(sb); MOD_DEC_USE_COUNT;}static struct super_operations vfat_sops = { vfat_read_inode, fat_notify_change, fat_write_inode, fat_put_inode, vfat_put_super, NULL, /* added in 0.96c */ fat_statfs, NULL};static int parse_options(char *options, struct fat_mount_options *opts){ char *this_char,*value,save,*savep; int ret; opts->unicode_xlate = opts->posixfs = 0; opts->numtail = 1; opts->utf8 = 0; if (!options) return 1; save = 0; savep = NULL; ret = 1; for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) { save = *value; savep = value; *value++ = 0; } if (!strcmp(this_char,"utf8")) { if (value) { ret = 0; } else { opts->utf8 = 1; } } else if (!strcmp(this_char,"uni_xlate")) { if (value) { ret = 0; } else { opts->unicode_xlate = 1; } } else if (!strcmp(this_char,"posix")) { if (value) { ret = 0; } else { opts->posixfs = 1; } } else if (!strcmp(this_char,"nonumtail")) { if (value) { ret = 0; } else { opts->numtail = 0; } } if (this_char != options) *(this_char-1) = ','; if (value) { *savep = save; } if (ret == 0) { return 0; } } if (opts->unicode_xlate) { opts->utf8 = 0; } return 1;}struct super_block *vfat_read_super(struct super_block *sb,void *data, int silent){ struct super_block *res; MOD_INC_USE_COUNT; MSDOS_SB(sb)->options.isvfat = 1; sb->s_op = &vfat_sops; res = fat_read_super(sb, data, silent); if (res == NULL) { sb->s_dev = 0; MOD_DEC_USE_COUNT; return NULL; } if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) { MOD_DEC_USE_COUNT; } else { MSDOS_SB(sb)->options.dotsOK = 0; } return res;}#ifdef DEBUGstatic voidcheck_stack(const char *fname, int lineno){ int stack_level; char *pg_dir; stack_level = (long)(&pg_dir)-current->kernel_stack_page; if (stack_level < 0) printk("*-*-*-* vfat kstack overflow in %s line %d: SL=%d\n", fname, lineno, stack_level); else if (stack_level < 500) printk("*-*-*-* vfat kstack low in %s line %d: SL=%d\n", fname, lineno, stack_level);#if 0 else printk("------- vfat kstack ok in %s line %d: SL=%d\n", fname, lineno, stack_level);#endif if (*(unsigned long *) current->kernel_stack_page != STACK_MAGIC) { printk("******* vfat stack corruption detected in %s at line %d\n", fname, lineno); }}static int debug = 0;static void dump_fat(struct super_block *sb,int start){ printk("["); while (start) { printk("%d ",start); start = fat_access(sb,start,-1); if (!start) { printk("ERROR"); break; } if (start == -1) break; } printk("]\n");}static 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");}#endif/* MS-DOS "device special files" */static const char *reserved_names[] = { "CON ","PRN ","NUL ","AUX ", "LPT1 ","LPT2 ","LPT3 ","LPT4 ", "COM1 ","COM2 ","COM3 ","COM4 ", NULL };/* Characters that are undesirable in an MS-DOS file name */static char bad_chars[] = "*?<>|\":/\\";static char replace_chars[] = "[];,+=";static int vfat_find(struct inode *dir,const char *name,int len, int find_long,int new_filename,int is_dir, struct slot_info *sinfo_out);/* Checks the validity of an long MS-DOS filename *//* Returns negative number on error, 0 for a normal * return, and 1 for . or .. */static int vfat_valid_longname(const char *name, int len, int dot_dirs, int xlate){ const char **reserved; unsigned char c; int i; if (IS_FREE(name)) return -EINVAL; if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { if (!dot_dirs) return -EEXIST; return 1; } if (len && name[len-1] == ' ') return -EINVAL; if (len >= 256) return -EINVAL; for (i = 0; i < len; i++) { c = name[i]; if (xlate && c == ':') continue; if (strchr(bad_chars,c)) { return -EINVAL; } } if (len == 3 || len == 4) { for (reserved = reserved_names; *reserved; reserved++) if (!strncmp(name,*reserved,8)) return -EINVAL; } return 0;}static int vfat_valid_shortname(const char *name,int len, int dot_dirs, int utf8){ const char *walk, **reserved; unsigned char c; int space; if (IS_FREE(name)) return -EINVAL; if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { if (!dot_dirs) return -EEXIST; return 1; } space = 1; /* disallow names starting with a dot */ c = 0; for (walk = name; len && walk-name < 8;) { c = *walk++; len--; if (utf8 && (c & 0x80)) return -EINVAL; if (strchr(bad_chars,c)) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; if (c >= 'A' && c <= 'Z') return -EINVAL; if (c < ' ' || c == ':' || c == '\\') return -EINVAL; if ((walk == name) && (c == 0xE5)) c = 0x05; if (c == '.') break; space = c == ' '; } if (space) return -EINVAL; if (len && c != '.') { c = *walk++; len--; if (c != '.') return -EINVAL; } while (c != '.' && len--) c = *walk++; if (c == '.') { if (len >= 4) return -EINVAL; while (len > 0 && walk-name < (MSDOS_NAME+1)) { c = *walk++; len--; if (utf8 && (c & 0x80)) return -EINVAL; if (strchr(bad_chars,c)) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; if (c < ' ' || c == ':' || c == '\\' || c == '.') return -EINVAL; if (c >= 'A' && c <= 'Z') return -EINVAL; space = c == ' '; } if (space) return -EINVAL; if (len) return -EINVAL; } for (reserved = reserved_names; *reserved; reserved++) if (!strncmp(name,*reserved,8)) return -EINVAL; return 0;}/* Takes a short filename and converts it to a formatted MS-DOS filename. * If the short filename is not a valid MS-DOS filename, an error is * returned. The formatted short filename is returned in 'res'. */static int vfat_format_name(const char *name,int len,char *res, int dot_dirs,int utf8){ char *walk; const char **reserved; unsigned char c; int space; if (IS_FREE(name)) return -EINVAL; if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { if (!dot_dirs) return -EEXIST; memset(res+1,' ',10); while (len--) *res++ = '.'; return 0; } space = 1; /* disallow names starting with a dot */ c = 0; for (walk = res; len && walk-res < 8; walk++) { c = *name++; len--; if (utf8 && (c & 0x80)) return -EINVAL; if (strchr(bad_chars,c)) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; if (c >= 'A' && c <= 'Z') return -EINVAL; if (c < ' ' || c == ':' || c == '\\') return -EINVAL; if (c == '.') break; space = c == ' '; *walk = c >= 'a' && c <= 'z' ? c-32 : c; } if (space) return -EINVAL; if (len && c != '.') { c = *name++; len--; if (c != '.') return -EINVAL; } while (c != '.' && len--) c = *name++; if (c == '.') { while (walk-res < 8) *walk++ = ' '; while (len > 0 && walk-res < MSDOS_NAME) { c = *name++; len--; if (utf8 && (c & 0x80)) return -EINVAL; if (strchr(bad_chars,c)) return -EINVAL; if (strchr(replace_chars,c)) return -EINVAL; if (c < ' ' || c == ':' || c == '\\' || c == '.') return -EINVAL; if (c >= 'A' && c <= 'Z') return -EINVAL; space = c == ' '; *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; } if (space) return -EINVAL; if (len) return -EINVAL; } while (walk-res < MSDOS_NAME) *walk++ = ' '; for (reserved = reserved_names; *reserved; reserved++) if (!strncmp(res,*reserved,8)) return -EINVAL; return 0;}static char skip_chars[] = ".:\"?<>| ";/* Given a valid longname, create a unique shortname. Make sure the * shortname does not exist */static int vfat_create_shortname(struct inode *dir, const char *name, int len, char *name_res, int utf8){ const char *ip, *ext_start, *end; char *p; int sz, extlen, baselen, totlen; char msdos_name[13]; char base[9], ext[4]; int i; int res; int spaces; char buf[8]; struct slot_info sinfo; const char *name_start; PRINTK(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len)); sz = 0; /* Make compiler happy */ if (len && name[len-1]==' ') return -EINVAL; if (len <= 12) { /* Do a case insensitive search if the name would be a valid * shortname if is were all capitalized. However, do not * allow spaces in short names because Win95 scandisk does * not like that */ res = 0; for (i = 0, p = msdos_name, ip = name; i < len; i++, p++, ip++) { if (*ip == ' ') { res = -EINVAL; break; } if (*ip >= 'A' && *ip <= 'Z') { *p = *ip + 32; } else { *p = *ip; } } if (res == 0) { res = vfat_format_name(msdos_name, len, name_res, 1, utf8); } if (res > -1) { PRINTK(("vfat_create_shortname 1\n")); res = vfat_find(dir, msdos_name, len, 0, 0, 0, &sinfo); PRINTK(("vfat_create_shortname 2\n")); if (res > -1) return -EEXIST; return 0; } } PRINTK(("vfat_create_shortname 3\n")); /* Now, we need to create a shortname from the long name */ ext_start = end = &name[len]; while (--ext_start >= name) { if (*ext_start == '.') { if (ext_start == end - 1) { sz = len; ext_start = NULL; } break; } } if (ext_start == name - 1) { sz = len; ext_start = NULL; } else if (ext_start) { /* * Names which start with a dot could be just * an extension eg. "...test". In this case Win95 * uses the extension as the name and sets no extension. */ name_start = &name[0]; while (name_start < ext_start) { if (!strchr(skip_chars,*name_start)) break; name_start++; } if (name_start != ext_start) { sz = ext_start - name; ext_start++; } else { sz = len; ext_start=NULL; } } for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++) { if (utf8 && (*ip & 0x80)) { *p++ = '_'; baselen++; } else if (!strchr(skip_chars, *ip)) { if (*ip >= 'A' && *ip <= 'Z') { *p = *ip + 32; } else { *p = *ip; } if (strchr(replace_chars, *p)) *p='_'; p++; baselen++; } ip++; } if (baselen == 0) { return -EINVAL; } spaces = 8 - baselen; if (ext_start) { extlen = 0; for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) { if (utf8 && (*ip & 0x80)) { *p++ = '_'; extlen++; } else if (!strchr(skip_chars, *ip)) { if (*ip >= 'A' && *ip <= 'Z') { *p = *ip + 32; } else { *p = *ip; } if (strchr(replace_chars, *p)) *p='_'; extlen++; p++; } } } else { extlen = 0; } ext[extlen] = '\0'; base[baselen] = '\0'; strcpy(msdos_name, base); msdos_name[baselen] = '.'; strcpy(&msdos_name[baselen+1], ext); totlen = baselen + extlen + (extlen > 0); res = 0; if (MSDOS_SB(dir->i_sb)->options.numtail == 0) { res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo); } i = 0; while (res > -1) { /* Create the next shortname to try */ i++; if (i == 10000000) return -EEXIST; sprintf(buf, "%d", i); sz = strlen(buf); if (sz + 1 > spaces) { baselen = baselen - (sz + 1 - spaces); spaces = sz + 1; } strncpy(msdos_name, base, baselen); msdos_name[baselen] = '~'; strcpy(&msdos_name[baselen+1], buf); msdos_name[baselen+sz+1] = '.'; strcpy(&msdos_name[baselen+sz+2], ext); totlen = baselen + sz + 1 + extlen + (extlen > 0); res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo); } res = vfat_format_name(msdos_name, totlen, name_res, 1, utf8); return res;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -