From 230e5ab6a084ed50470f101934782dbf54b0d06b Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 6 Dec 1997 23:51:34 +0000 Subject: Merge with Linux 2.1.67. --- fs/vfat/namei.c | 360 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 206 insertions(+), 154 deletions(-) (limited to 'fs/vfat/namei.c') diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index 587291669..c1ff4176c 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -11,21 +11,20 @@ */ #include +#define __NO_VERSION__ #include #include #include +#include #include #include #include #include #include -#include - -#include +#include #include "../fat/msbuffer.h" -#include "../fat/tables.h" #if 0 # define PRINTK(x) printk x @@ -79,13 +78,30 @@ static struct super_operations vfat_sops = { NULL /* remount */ }; +static int simple_getbool(char *s, int *setval) +{ + if (s) { + if (!strcmp(s,"1") || !strcmp(s,"yes") || !strcmp(s,"true")) { + *setval = 1; + } else if (!strcmp(s,"0") || !strcmp(s,"no") || !strcmp(s,"false")) { + *setval = 0; + } else { + return 0; + } + } else { + *setval = 1; + } + return 1; +} + static int parse_options(char *options, struct fat_mount_options *opts) { char *this_char,*value,save,*savep; - int ret; + int ret, val; opts->unicode_xlate = opts->posixfs = 0; opts->numtail = 1; + opts->utf8 = 0; if (!options) return 1; save = 0; @@ -97,25 +113,19 @@ static int parse_options(char *options, struct fat_mount_options *opts) savep = value; *value++ = 0; } - 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 (!strcmp(this_char,"utf8")) { + ret = simple_getbool(value, &val); + if (ret) opts->utf8 = val; + } else if (!strcmp(this_char,"uni_xlate")) { + ret = simple_getbool(value, &val); + if (ret) opts->unicode_xlate = val; + } else if (!strcmp(this_char,"posix")) { + ret = simple_getbool(value, &val); + if (ret) opts->posixfs = val; + } else if (!strcmp(this_char,"nonumtail")) { + ret = simple_getbool(value, &val); + if (ret) { + opts->numtail = !val; } } if (this_char != options) @@ -127,6 +137,9 @@ static int parse_options(char *options, struct fat_mount_options *opts) return 0; } } + if (opts->unicode_xlate) { + opts->utf8 = 0; + } return 1; } @@ -137,6 +150,8 @@ struct super_block *vfat_read_super(struct super_block *sb,void *data, 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) { @@ -148,7 +163,6 @@ struct super_block *vfat_read_super(struct super_block *sb,void *data, if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) { MOD_DEC_USE_COUNT; } else { - MSDOS_SB(sb)->options.isvfat = 1; MSDOS_SB(sb)->options.dotsOK = 0; } @@ -225,7 +239,6 @@ static const char *reserved_names[] = { /* Characters that are undesirable in an MS-DOS file name */ static char bad_chars[] = "*?<>|\":/\\"; -static char bad_if_strict[] = "+=,; []"; static char replace_chars[] = "[];,+="; static int vfat_find(struct inode *dir,struct qstr* name, @@ -265,8 +278,8 @@ static int vfat_valid_longname(const char *name, int len, int dot_dirs, return 0; } -static int vfat_valid_shortname(char conv,const char *name,int len, - int dot_dirs) +static int vfat_valid_shortname(const char *name,int len, + int dot_dirs, int utf8) { const char *walk, **reserved; unsigned char c; @@ -283,17 +296,17 @@ static int vfat_valid_shortname(char conv,const char *name,int len, for (walk = name; len && walk-name < 8;) { c = *walk++; len--; - if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; - if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL; - if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; - if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL; + 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 ((conv == 's' || conv == 'x') && len && c != '.') { + if (len && c != '.') { c = *walk++; len--; if (c != '.') return -EINVAL; @@ -304,18 +317,17 @@ static int vfat_valid_shortname(char conv,const char *name,int len, while (len > 0 && walk-name < (MSDOS_NAME+1)) { c = *walk++; len--; - if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; - if (conv == 's' && strchr(bad_if_strict,c)) - return -EINVAL; - if (conv == 'x' && strchr(replace_chars,c)) + 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' && (conv == 's' || conv == 'x')) return -EINVAL; + if (c >= 'A' && c <= 'Z') return -EINVAL; space = c == ' '; } if (space) return -EINVAL; - if ((conv == 's' || conv == 'x') && len) return -EINVAL; + if (len) return -EINVAL; } for (reserved = reserved_names; *reserved; reserved++) if (!strncmp(name,*reserved,8)) return -EINVAL; @@ -328,8 +340,8 @@ static int vfat_valid_shortname(char conv,const char *name,int len, * returned. The formatted short filename is returned in 'res'. */ -static int vfat_format_name(char conv,const char *name,int len,char *res, - int dot_dirs) +static int vfat_format_name(const char *name,int len,char *res, + int dot_dirs,int utf8) { char *walk; const char **reserved; @@ -349,17 +361,17 @@ static int vfat_format_name(char conv,const char *name,int len,char *res, for (walk = res; len && walk-res < 8; walk++) { c = *name++; len--; - if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; - if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; - if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL; - if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL; + 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 ((conv == 's' || conv == 'x') && len && c != '.') { + if (len && c != '.') { c = *name++; len--; if (c != '.') return -EINVAL; @@ -370,19 +382,18 @@ static int vfat_format_name(char conv,const char *name,int len,char *res, while (len > 0 && walk-res < MSDOS_NAME) { c = *name++; len--; - if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; - if (conv == 's' && strchr(bad_if_strict,c)) - return -EINVAL; - if (conv == 'x' && strchr(replace_chars,c)) + 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' && (conv == 's' || conv == 'x')) return -EINVAL; + if (c >= 'A' && c <= 'Z') return -EINVAL; space = c == ' '; *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; } if (space) return -EINVAL; - if ((conv == 's' || conv == 'x') && len) return -EINVAL; + if (len) return -EINVAL; } while (walk-res < MSDOS_NAME) *walk++ = ' '; for (reserved = reserved_names; *reserved; reserved++) @@ -397,7 +408,7 @@ static char skip_chars[] = ".:\"?<>| "; * shortname does not exist */ static int vfat_create_shortname(struct inode *dir, const char *name, - int len, char *name_res) + int len, char *name_res, int utf8) { const char *ip, *ext_start, *end; char *p; @@ -417,16 +428,25 @@ static int vfat_create_shortname(struct inode *dir, const char *name, 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 */ + * 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 = -1; + break; + } if (*ip >= 'A' && *ip <= 'Z') { *p = *ip + 32; } else { *p = *ip; } } - res = vfat_format_name('x', msdos_name, len, name_res, 1); + if (res == 0) { + res = vfat_format_name(msdos_name, len, name_res, 1, utf8); + } if (res > -1) { PRINTK(("vfat_create_shortname 1\n")); qname.name=msdos_name; @@ -476,7 +496,10 @@ static int vfat_create_shortname(struct inode *dir, const char *name, for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++) { - if (!strchr(skip_chars, *ip)) { + if (utf8 && (*ip & 0x80)) { + *p++ = '_'; + baselen++; + } else if (!strchr(skip_chars, *ip)) { if (*ip >= 'A' && *ip <= 'Z') { *p = *ip + 32; } else { @@ -496,7 +519,10 @@ static int vfat_create_shortname(struct inode *dir, const char *name, if (ext_start) { extlen = 0; for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) { - if (!strchr(skip_chars, *ip)) { + if (utf8 && (*ip & 0x80)) { + *p++ = '_'; + extlen++; + } else if (!strchr(skip_chars, *ip)) { if (*ip >= 'A' && *ip <= 'Z') { *p = *ip + 32; } else { @@ -547,7 +573,7 @@ static int vfat_create_shortname(struct inode *dir, const char *name, qname.len=totlen; res = vfat_find(dir, &qname, 0, 0, 0, &sinfo); } - res = vfat_format_name('x', msdos_name, totlen, name_res, 1); + res = vfat_format_name(msdos_name, totlen, name_res, 1, utf8); return res; } @@ -596,7 +622,9 @@ static loff_t vfat_find_free_slots(struct inode *dir,int slots) ino = fat_get_entry(dir,&curr,&bh,&de); } - if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC; + if ((dir->i_ino == MSDOS_ROOT_INO) && + (MSDOS_SB(sb)->fat_bits != 32)) + return -ENOSPC; if ((res = fat_add_cluster(dir)) < 0) return res; ino = fat_get_entry(dir,&curr,&bh,&de); } @@ -605,7 +633,8 @@ static loff_t vfat_find_free_slots(struct inode *dir,int slots) /* Translate a string, including coded sequences into Unicode */ static int -xlate_to_uni(const char *name, int len, char *outname, int *outlen, int escape) +xlate_to_uni(const char *name, int len, char *outname, int *outlen, + int escape, int utf8, struct nls_table *nls) { int i; const unsigned char *ip; @@ -613,22 +642,43 @@ xlate_to_uni(const char *name, int len, char *outname, int *outlen, int escape) int fill; unsigned char c1, c2, c3; - op = outname; - for (i = 0, ip = name, op = outname, *outlen = 0; - i < len && *outlen <= 260; i++, *outlen += 1) - { - if (escape && (i < len - 4) && - (*ip == ':') && - ((c1 = fat_code2uni[ip[1]]) != 255) && - ((c2 = fat_code2uni[ip[2]]) != 255) && - ((c3 = fat_code2uni[ip[3]]) != 255)) { - *op++ = (c1 << 4) + (c2 >> 2); - *op++ = ((c2 & 0x3) << 6) + c3; - ip += 4; + if (utf8) { + *outlen = utf8_mbstowcs((__u16 *) outname, name, PAGE_SIZE); + if (name[len-1] == '.') + *outlen-=2; + op = &outname[*outlen * sizeof(__u16)]; + } else { + if (name[len-1] == '.') + len--; + op = outname; + if (nls) { + /* XXX: i is incorrectly computed. */ + for (i = 0, ip = name, op = outname, *outlen = 0; + i < len && *outlen <= 260; i++, *outlen += 1) + { + if (escape && (*ip == ':')) { + if (i > len - 4) return -EINVAL; + c1 = fat_esc2uni[ip[1]]; + c2 = fat_esc2uni[ip[2]]; + c3 = fat_esc2uni[ip[3]]; + if (c1 == 255 || c2 == 255 || c3 == 255) + return -EINVAL; + *op++ = (c1 << 4) + (c2 >> 2); + *op++ = ((c2 & 0x3) << 6) + c3; + ip += 4; + } else { + *op++ = nls->charset2uni[*ip].uni1; + *op++ = nls->charset2uni[*ip].uni2; + ip++; + } + } } else { - *op++ = fat_a2uni[*ip].uni1; - *op++ = fat_a2uni[*ip].uni2; - ip++; + for (i = 0, ip = name, op = outname, *outlen = 0; + i < len && *outlen <= 260; i++, *outlen += 1) + { + *op++ = *ip++; + *op++ = 0; + } } } if (*outlen > 260) @@ -653,7 +703,8 @@ xlate_to_uni(const char *name, int len, char *outname, int *outlen, int escape) static int vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len, - char *msdos_name, int *slots, int uni_xlate) + char *msdos_name, int *slots, + int uni_xlate, int utf8, struct nls_table *nls) { struct msdos_dir_slot *ps; struct msdos_dir_entry *de; @@ -667,10 +718,11 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len, int i; loff_t offset; + if (name[len-1] == '.') len--; if(!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; uniname = (char *) page; - res = xlate_to_uni(name, len, uniname, &unilen, uni_xlate); + res = xlate_to_uni(name, len, uniname, &unilen, uni_xlate, utf8, nls); if (res < 0) { free_page(page); return res; @@ -690,8 +742,7 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len, ps->attr = ATTR_EXT; ps->reserved = 0; ps->alias_checksum = cksum; - ps->start[0] = 0; - ps->start[1] = 0; + ps->start = 0; PRINTK(("vfat_fill_long_slots 5: uniname=%s\n",uniname)); offset = (slot - 1) * 26; ip = &uniname[offset]; @@ -728,11 +779,14 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len, { struct msdos_dir_entry *de; char msdos_name[MSDOS_NAME]; - int res, xlate; + int res, xlate, utf8; + struct nls_table *nls; PRINTK(("Entering vfat_build_slots: name=%s, len=%d\n", name, len)); de = (struct msdos_dir_entry *) ds; xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate; + utf8 = MSDOS_SB(dir->i_sb)->options.utf8; + nls = MSDOS_SB(dir->i_sb)->nls_io; *slots = 1; *is_long = 0; @@ -742,13 +796,13 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len, strncpy(de->name, MSDOS_DOT, MSDOS_NAME); } else { PRINTK(("vfat_build_slots 4\n")); - res = vfat_valid_shortname('x', name, len, 1); + res = vfat_valid_shortname(name, len, 1, utf8); if (res > -1) { PRINTK(("vfat_build_slots 5a\n")); - res = vfat_format_name('x', name, len, de->name, 1); + res = vfat_format_name(name, len, de->name, 1, utf8); PRINTK(("vfat_build_slots 5b\n")); } else { - res = vfat_create_shortname(dir, name, len, msdos_name); + res = vfat_create_shortname(dir, name, len, msdos_name, utf8); if (res < 0) { return res; } @@ -761,7 +815,7 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len, *is_long = 1; return vfat_fill_long_slots(ds, name, len, msdos_name, - slots, xlate); + slots, xlate, utf8, nls); } } return 0; @@ -787,8 +841,11 @@ static int vfat_readdir_cb( vf->name, vf->len, name, name_len); #endif + /* Filenames cannot end in '.' or we treat like it has none */ if (vf->len != name_len) { - return 0; + if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) { + return 0; + } } s1 = name; s2 = vf->name; @@ -821,12 +878,17 @@ static int vfat_find(struct inode *dir,struct qstr* qname, struct msdos_dir_entry *de; struct msdos_dir_slot *ps; loff_t offset; - struct msdos_dir_slot ds[MSDOS_SLOTS]; + struct msdos_dir_slot *ds; int is_long; int slots, slot; int res; PRINTK(("Entering vfat_find\n")); + + ds = (struct msdos_dir_slot *) + kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL); + if (ds == NULL) return -ENOMEM; + fil.f_pos = 0; vf.name = qname->name; vf.len = qname->len; @@ -835,10 +897,11 @@ static int vfat_find(struct inode *dir,struct qstr* qname, vf.posix = MSDOS_SB(sb)->options.posixfs; res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0); PRINTK(("vfat_find: Debug 1\n")); - if (res < 0) return res; + if (res < 0) goto cleanup; if (vf.found) { if (new_filename) { - return -EEXIST; + res = -EEXIST; + goto cleanup; } sinfo_out->longname_offset = vf.offset; sinfo_out->shortname_offset = vf.short_offset; @@ -848,16 +911,19 @@ static int vfat_find(struct inode *dir,struct qstr* qname, sinfo_out->ino = vf.ino; PRINTK(("vfat_find: Debug 2\n")); - return 0; + res = 0; + goto cleanup; } PRINTK(("vfat_find: Debug 3\n")); - if (!vf.found && !new_filename) - return -ENOENT; + if (!vf.found && !new_filename) { + res = -ENOENT; + goto cleanup; + } res = vfat_build_slots(dir, qname->name, qname->len, ds, &slots, &is_long); - if (res < 0) return res; + if (res < 0) goto cleanup; de = (struct msdos_dir_entry *) ds; @@ -867,7 +933,8 @@ static int vfat_find(struct inode *dir,struct qstr* qname, if (is_long) slots++; offset = vfat_find_free_slots(dir, slots); if (offset < 0) { - return offset; + res = offset; + goto cleanup; } PRINTK(("vfat_find: create file 2\n")); @@ -878,7 +945,8 @@ static int vfat_find(struct inode *dir,struct qstr* qname, sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de); if (sinfo_out->ino < 0) { PRINTK(("vfat_find: problem\n")); - return sinfo_out->ino; + res = sinfo_out->ino; + goto cleanup; } memcpy(de, ps, sizeof(struct msdos_dir_slot)); fat_mark_buffer_dirty(sb, bh, 1); @@ -890,12 +958,12 @@ static int vfat_find(struct inode *dir,struct qstr* qname, PRINTK(("vfat_find: create file 5\n")); - memset(de->unused, 0, sizeof(de->unused)); fat_date_unix2dos(dir->i_mtime,&de->time,&de->date); de->ctime_ms = 0; de->ctime = de->time; de->adate = de->cdate = de->date; de->start = 0; + de->starthi = 0; de->size = 0; de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT; @@ -913,10 +981,15 @@ static int vfat_find(struct inode *dir,struct qstr* qname, sinfo_out->total_slots = slots; sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot); sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots; + res = 0; return 0; + } else { + res = -ENOENT; } - return -ENOENT; +cleanup: + kfree(ds); + return res; } int vfat_lookup(struct inode *dir,struct dentry *dentry) @@ -1040,10 +1113,10 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); memcpy(de->name,name,MSDOS_NAME); - memset(de->unused, 0, sizeof(de->unused)); de->lcase = 0; de->attr = ATTR_DIR; de->start = 0; + de->starthi = 0; fat_date_unix2dos(dir->i_mtime,&de->time,&de->date); de->ctime_ms = 0; de->ctime = de->time; @@ -1058,10 +1131,12 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, if (isdot) { dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; + MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart; dot->i_nlink = dir->i_nlink; } else { dot->i_size = parent->i_size; MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start; + MSDOS_I(dot)->i_logstart = MSDOS_I(parent)->i_logstart; dot->i_nlink = parent->i_nlink; } @@ -1244,10 +1319,8 @@ static int vfat_rmdirx(struct inode *dir,struct dentry* dentry) if (res > 0) { res = 0; } - } else { - printk("Problem in vfat_rmdirx\n"); + dir->i_version = ++event; } - dir->i_version = ++event; rmdir_done: fat_brelse(sb, bh); @@ -1275,19 +1348,15 @@ static int vfat_unlinkx( struct slot_info sinfo; bh = NULL; - if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0) - goto unlink_done; + res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo); if (res >= 0 && sinfo.total_slots > 0) { res = vfat_remove_entry(dir,&sinfo,&bh,dentry,0,nospc); if (res > 0) { res = 0; } - } else { - printk("Problem in vfat_unlinkx: res=%d, total_slots=%d\n",res, sinfo.total_slots); } -unlink_done: fat_brelse(sb, bh); return res; } @@ -1329,7 +1398,16 @@ int vfat_unlink(struct inode *dir,struct dentry* dentry) return res; } +/***** Unlink, as called for uvfatfs */ +int vfat_unlink_uvfat(struct inode *dir,struct dentry *dentry) +{ + int res; + res = vfat_unlinkx (dir,dentry,0); + iput(dir); + return res; +} + int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, struct inode *new_dir,struct dentry *new_dentry) { @@ -1337,8 +1415,9 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, struct buffer_head *old_bh,*new_bh,*dotdot_bh; struct msdos_dir_entry *old_de,*new_de,*dotdot_de; loff_t old_offset,new_offset,old_longname_offset; - int old_slots,old_ino,new_ino,dotdot_ino,ino; - struct inode *old_inode, *new_inode, *dotdot_inode, *walk; + int old_slots,old_ino,new_ino,dotdot_ino; + struct inode *old_inode, *new_inode, *dotdot_inode; + struct dentry *walk; int res, is_dir, i; int locked = 0; struct slot_info sinfo; @@ -1373,25 +1452,13 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, res = -EINVAL; goto rename_done; } - if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO; + walk = new_dentry; /* prevent moving directory below itself */ - while (walk->i_ino != MSDOS_ROOT_INO) { - ino = fat_parent_ino(walk,1); - iput(walk); - if (ino < 0) { - res = ino; - goto rename_done; - } - if (ino == old_ino) { - res = -EINVAL; - goto rename_done; - } - if (!(walk = iget(new_dir->i_sb,ino))) { - res = -EIO; - goto rename_done; - } + for (;;) { + if (walk == old_dentry) return -EINVAL; + if (walk == walk->d_parent) break; + walk = walk->d_parent; } - iput(walk); } res = vfat_find(new_dir,&new_dentry->d_name,1,0,is_dir,&sinfo); @@ -1417,10 +1484,13 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, PRINTK(("vfat_rename 8\n")); if (res < 0) goto rename_done; } else { - PRINTK(("vfat_rename 9\n")); - res = vfat_unlinkx(new_dir,new_dentry,1); - PRINTK(("vfat_rename 10\n")); - if (res < 0) goto rename_done; + /* Is this the same file, different case? */ + if (new_inode != old_inode) { + PRINTK(("vfat_rename 9\n")); + res = vfat_unlinkx(new_dir,new_dentry,1); + PRINTK(("vfat_rename 10\n")); + if (res < 0) goto rename_done; + } } } @@ -1444,6 +1514,7 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, new_de->cdate = old_de->cdate; new_de->adate = old_de->adate; new_de->start = old_de->start; + new_de->starthi = old_de->starthi; new_de->size = old_de->size; if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done; @@ -1492,8 +1563,10 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, res = -EIO; goto rename_done; } - dotdot_de->start = MSDOS_I(dotdot_inode)->i_start = - MSDOS_I(new_dir)->i_start; + MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; + MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart; + dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); + dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); mark_inode_dirty(dotdot_inode); fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; @@ -1505,8 +1578,9 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, } if (res > 0) res = 0; - d_instantiate(new_dentry,new_inode); - d_delete(old_dentry); + if (res == 0) { + d_move(old_dentry, new_dentry); + } rename_done: if (locked) @@ -1547,28 +1621,6 @@ void vfat_read_inode(struct inode *inode) fat_read_inode(inode, &vfat_dir_inode_operations); } -static struct file_system_type vfat_fs_type = { - "vfat", - FS_REQUIRES_DEV, - vfat_read_super, - NULL -}; - -EXPORT_SYMBOL(vfat_create); -EXPORT_SYMBOL(vfat_unlink); -EXPORT_SYMBOL(vfat_mkdir); -EXPORT_SYMBOL(vfat_rmdir); -EXPORT_SYMBOL(vfat_rename); -EXPORT_SYMBOL(vfat_put_super); -EXPORT_SYMBOL(vfat_read_super); -EXPORT_SYMBOL(vfat_read_inode); -EXPORT_SYMBOL(vfat_lookup); - -__initfunc(int init_vfat_fs(void)) -{ - return register_filesystem(&vfat_fs_type); -} - #ifdef MODULE int init_module(void) { -- cgit v1.2.3