diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
commit | 27cfca1ec98e91261b1a5355d10a8996464b63af (patch) | |
tree | 8e895a53e372fa682b4c0a585b9377d67ed70d0e /fs/vfat/namei.c | |
parent | 6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff) |
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too
o Upgrade to 2.1.89.
Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'fs/vfat/namei.c')
-rw-r--r-- | fs/vfat/namei.c | 536 |
1 files changed, 372 insertions, 164 deletions
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index 8aacb6dfd..bdd37df13 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -10,7 +10,6 @@ * the problem, send a script that demonstrates it. */ -#include <linux/config.h> #define __NO_VERSION__ #include <linux/module.h> @@ -27,10 +26,21 @@ #include "../fat/msbuffer.h" -#if 0 -# define PRINTK(x) printk x +#define DEBUG_LEVEL 0 +#if (DEBUG_LEVEL >= 1) +# define PRINTK1(x) printk x +#else +# define PRINTK1(x) +#endif +#if (DEBUG_LEVEL >= 2) +# define PRINTK2(x) printk x +#else +# define PRINTK2(x) +#endif +#if (DEBUG_LEVEL >= 3) +# define PRINTK3(x) printk x #else -# define PRINTK(x) +# define PRINTK3(x) #endif #ifndef DEBUG @@ -50,9 +60,61 @@ struct vfat_find_info { int long_slots; ino_t ino; int posix; + int anycase; }; void vfat_read_inode(struct inode *inode); +static int vfat_valid_shortname(const char *,int, int, int); +static int vfat_format_name(const char *, int, char *, int, int); +static int vfat_valid_longname(const char *, int, int, int); +static int vfat_hashi(struct dentry *parent, struct qstr *qstr); +static int vfat_hash(struct dentry *parent, struct qstr *qstr); +static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int vfat_revalidate(struct dentry *dentry); + +static struct dentry_operations vfat_dentry_ops[4] = { + { + NULL, /* d_revalidate */ + vfat_hashi, + vfat_cmpi, + NULL /* d_delete */ + }, + { + vfat_revalidate, + vfat_hashi, + vfat_cmpi, + NULL /* d_delete */ + }, + { + NULL, /* d_revalidate */ + vfat_hash, + vfat_cmp, + NULL /* d_delete */ + }, + { + vfat_revalidate, + vfat_hash, + vfat_cmp, + NULL /* d_delete */ + } +}; + +static int strnicmp(const char *s1, const char *s2, int len) +{ + int n = 0; + while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) { + s1++; s2++; n++; + if (n == len) return 0; + } + if (*s1 == 0 && *s2 == 0) return 0; + if (*s1 && *s2) { + if (*s1 > *s2) return 1; + return -1; + } + if (*s1) return 1; + return -1; +} void vfat_put_super(struct super_block *sb) { @@ -60,6 +122,14 @@ void vfat_put_super(struct super_block *sb) MOD_DEC_USE_COUNT; } +static int vfat_revalidate(struct dentry *dentry) +{ + PRINTK1(("vfat_revalidate: %s\n", dentry->d_name.name)); + if (dentry->d_time == dentry->d_parent->d_inode->i_version) { + return 1; + } + return 0; +} static struct super_operations vfat_sops = { vfat_read_inode, @@ -138,6 +208,97 @@ static int parse_options(char *options, struct fat_mount_options *opts) return 1; } +/* + * Compute the hash for the vfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The vfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int vfat_hash(struct dentry *dentry, struct qstr *qstr) +{ + const char *name; + int len; + + len = qstr->len; + name = qstr->name; + while (len && name[len-1] == '.') + len--; + + qstr->hash = full_name_hash(name, len); + + return 0; +} + +/* + * Compute the hash for the vfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The vfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int vfat_hashi(struct dentry *dentry, struct qstr *qstr) +{ + const char *name; + int len; + char c; + unsigned long hash; + + len = qstr->len; + name = qstr->name; + while (len && name[len-1] == '.') + len--; + + hash = init_name_hash(); + while (len--) { + c = tolower(*name++); + hash = partial_name_hash(tolower(c), hash); + } + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Case insensitive compare of two vfat names. + */ +static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b) +{ + int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = a->len; + blen = b->len; + while (alen && a->name[alen-1] == '.') + alen--; + while (blen && b->name[blen-1] == '.') + blen--; + if (alen == blen) { + if (strnicmp(a->name, b->name, alen) == 0) + return 0; + } + return 1; +} + +/* + * Case sensitive compare of two vfat names. + */ +static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) +{ + int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = a->len; + blen = b->len; + while (alen && a->name[alen-1] == '.') + alen--; + while (blen && b->name[blen-1] == '.') + blen--; + if (alen == blen) { + if (strncmp(a->name, b->name, alen) == 0) + return 0; + } + return 1; +} + struct super_block *vfat_read_super(struct super_block *sb,void *data, int silent) { @@ -159,6 +320,14 @@ struct super_block *vfat_read_super(struct super_block *sb,void *data, MOD_DEC_USE_COUNT; } else { MSDOS_SB(sb)->options.dotsOK = 0; + if (MSDOS_SB(sb)->options.posixfs) { + MSDOS_SB(sb)->options.name_check = 's'; + } + if (MSDOS_SB(sb)->options.name_check != 's') { + sb->s_root->d_op = &vfat_dentry_ops[0]; + } else { + sb->s_root->d_op = &vfat_dentry_ops[2]; + } } return res; @@ -243,23 +412,7 @@ static char replace_chars[] = "[];,+="; static int vfat_find(struct inode *dir,struct qstr* name, int find_long,int new_filename,int is_dir, - struct slot_info *sinfo_out); - -static int strnicmp(const char *s1, const char *s2, int len) -{ - int n = 0; - while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) { - s1++; s2++; n++; - if (n == len) return 0; - } - if (*s1 == 0 && *s2 == 0) return 0; - if (*s1 && *s2) { - if (*s1 > *s2) return 1; - return -1; - } - if (*s1) return 1; - return -1; -} + struct vfat_slot_info *sinfo_out); /* Checks the validity of a long MS-DOS filename */ /* Returns negative number on error, 0 for a normal @@ -457,11 +610,11 @@ static int vfat_create_shortname(struct inode *dir, const char *name, int res; int spaces; char buf[8]; - struct slot_info sinfo; + struct vfat_slot_info sinfo; const char *name_start; struct qstr qname; - PRINTK(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len)); + PRINTK2(("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) { @@ -486,17 +639,17 @@ static int vfat_create_shortname(struct inode *dir, const char *name, res = vfat_format_name(msdos_name, len, name_res, 1, utf8); } if (res > -1) { - PRINTK(("vfat_create_shortname 1\n")); + PRINTK3(("vfat_create_shortname 1\n")); qname.name=msdos_name; qname.len=len; res = vfat_find(dir, &qname, 0, 0, 0, &sinfo); - PRINTK(("vfat_create_shortname 2\n")); + PRINTK3(("vfat_create_shortname 2\n")); if (res > -1) return -EEXIST; return 0; } } - PRINTK(("vfat_create_shortname 3\n")); + PRINTK3(("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) { @@ -588,29 +741,55 @@ static int vfat_create_shortname(struct inode *dir, const char *name, qname.len=totlen; res = vfat_find(dir, &qname, 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); + if (res > -1) { + /* + * Try to find a unique extension. This used to + * iterate through all possibilities sequentially, + * but that gave extremely bad performance. Windows + * only tries a few cases before using random + * values for part of the base. + */ - totlen = baselen + sz + 1 + extlen + (extlen > 0); + if (2 > spaces) { + baselen = baselen - (2 - spaces); + spaces = 2; + } + msdos_name[baselen] = '~'; + msdos_name[baselen+2] = '.'; + strcpy(&msdos_name[baselen+3], ext); + totlen = baselen + 2 + extlen + (extlen > 0); qname.name=msdos_name; qname.len=totlen; - res = vfat_find(dir, &qname, 0, 0, 0, &sinfo); + for (i = 1; res > -1 && i < 10; i++) { + strncpy(msdos_name, base, baselen); + msdos_name[baselen+1] = i + '0'; + res = vfat_find(dir, &qname, 0, 0, 0, &sinfo); + } + } + if (res > -1) { + i = jiffies & 0xffff; + sz = (jiffies >> 16) & 0x7; + if (6 > spaces) { + baselen = baselen - (6 - spaces); + spaces = 6; + } + msdos_name[baselen+4] = '~'; + msdos_name[baselen+5] = '1' + sz; + msdos_name[baselen+6] = '.'; + strcpy(&msdos_name[baselen+7], ext); + totlen = baselen + 6 + extlen + (extlen > 0); + qname.name=msdos_name; + qname.len=totlen; + while (res > -1) { + sprintf(buf, "%04x", i); + memcpy(&msdos_name[baselen], buf, 4); + msdos_name[12] = 0; + res = vfat_find(dir, &qname, 0, 0, 0, &sinfo); + i -= 11; + } } + res = vfat_format_name(msdos_name, totlen, name_res, 1, utf8); return res; } @@ -628,7 +807,7 @@ static loff_t vfat_find_free_slots(struct inode *dir,int slots) int res; int added; - PRINTK(("vfat_find_free_slots: find %d free slots\n", slots)); + PRINTK2(("vfat_find_free_slots: find %d free slots\n", slots)); offset = curr = 0; bh = NULL; row = 0; @@ -642,7 +821,7 @@ static loff_t vfat_find_free_slots(struct inode *dir,int slots) if (inode) { /* Directory slots of busy deleted files aren't available yet. */ done = !MSDOS_I(inode)->i_busy; - /* PRINTK(("inode %d still busy\n", ino)); */ + /* PRINTK3(("inode %d still busy\n", ino)); */ iput(inode); } } @@ -690,7 +869,6 @@ xlate_to_uni(const char *name, int len, char *outname, int *outlen, 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) { @@ -770,18 +948,18 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len, for (cksum = i = 0; i < 11; i++) { cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i]; } - PRINTK(("vfat_fill_long_slots 3: slots=%d\n",*slots)); + PRINTK3(("vfat_fill_long_slots 3: slots=%d\n",*slots)); for (ps = ds, slot = *slots; slot > 0; slot--, ps++) { int end, j; - PRINTK(("vfat_fill_long_slots 4\n")); + PRINTK3(("vfat_fill_long_slots 4\n")); ps->id = slot; ps->attr = ATTR_EXT; ps->reserved = 0; ps->alias_checksum = cksum; ps->start = 0; - PRINTK(("vfat_fill_long_slots 5: uniname=%s\n",uniname)); + PRINTK3(("vfat_fill_long_slots 5: uniname=%s\n",uniname)); offset = (slot - 1) * 26; ip = &uniname[offset]; j = offset; @@ -790,22 +968,22 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len, ps->name0_4[i] = *ip++; ps->name0_4[i+1] = *ip++; } - PRINTK(("vfat_fill_long_slots 6\n")); + PRINTK3(("vfat_fill_long_slots 6\n")); for (i = 0; i < 12; i += 2) { ps->name5_10[i] = *ip++; ps->name5_10[i+1] = *ip++; } - PRINTK(("vfat_fill_long_slots 7\n")); + PRINTK3(("vfat_fill_long_slots 7\n")); for (i = 0; i < 4; i += 2) { ps->name11_12[i] = *ip++; ps->name11_12[i+1] = *ip++; } } - PRINTK(("vfat_fill_long_slots 8\n")); + PRINTK3(("vfat_fill_long_slots 8\n")); ds[0].id |= 0x40; de = (struct msdos_dir_entry *) ps; - PRINTK(("vfat_fill_long_slots 9\n")); + PRINTK3(("vfat_fill_long_slots 9\n")); strncpy(de->name, msdos_name, MSDOS_NAME); free_page(page); @@ -820,7 +998,7 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len, int res, xlate, utf8; struct nls_table *nls; - PRINTK(("Entering vfat_build_slots: name=%s, len=%d\n", name, len)); + PRINTK2(("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; @@ -833,12 +1011,12 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len, } else if (len == 2 && name[0] == '.' && name[1] == '.') { strncpy(de->name, MSDOS_DOT, MSDOS_NAME); } else { - PRINTK(("vfat_build_slots 4\n")); + PRINTK3(("vfat_build_slots 4\n")); res = vfat_valid_shortname(name, len, 1, utf8); if (res > -1) { - PRINTK(("vfat_build_slots 5a\n")); + PRINTK3(("vfat_build_slots 5a\n")); res = vfat_format_name(name, len, de->name, 1, utf8); - PRINTK(("vfat_build_slots 5b\n")); + PRINTK3(("vfat_build_slots 5b\n")); } else { res = vfat_create_shortname(dir, name, len, msdos_name, utf8); if (res < 0) { @@ -879,16 +1057,13 @@ 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) { - if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) { - return 0; - } + return 0; } s1 = name; s2 = vf->name; for (i = 0; i < name_len; i++) { - if (vf->new_filename && !vf->posix) { + if (vf->anycase || (vf->new_filename && !vf->posix)) { if (tolower(*s1) != tolower(*s2)) return 0; } else { @@ -907,7 +1082,7 @@ static int vfat_readdir_cb( } static int vfat_find(struct inode *dir,struct qstr* qname, - int find_long, int new_filename,int is_dir,struct slot_info *sinfo_out) + int find_long, int new_filename,int is_dir,struct vfat_slot_info *sinfo_out) { struct super_block *sb = dir->i_sb; struct vfat_find_info vf; @@ -921,7 +1096,7 @@ static int vfat_find(struct inode *dir,struct qstr* qname, int slots, slot; int res; - PRINTK(("Entering vfat_find\n")); + PRINTK2(("Entering vfat_find\n")); ds = (struct msdos_dir_slot *) kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL); @@ -933,8 +1108,9 @@ static int vfat_find(struct inode *dir,struct qstr* qname, vf.new_filename = new_filename; vf.found = 0; vf.posix = MSDOS_SB(sb)->options.posixfs; + vf.anycase = (MSDOS_SB(sb)->options.name_check != 's'); res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0); - PRINTK(("vfat_find: Debug 1\n")); + PRINTK3(("vfat_find: Debug 1\n")); if (res < 0) goto cleanup; if (vf.found) { if (new_filename) { @@ -948,12 +1124,12 @@ static int vfat_find(struct inode *dir,struct qstr* qname, sinfo_out->total_slots = vf.long_slots + 1; sinfo_out->ino = vf.ino; - PRINTK(("vfat_find: Debug 2\n")); + PRINTK3(("vfat_find: Debug 2\n")); res = 0; goto cleanup; } - PRINTK(("vfat_find: Debug 3\n")); + PRINTK3(("vfat_find: Debug 3\n")); if (!vf.found && !new_filename) { res = -ENOENT; goto cleanup; @@ -967,7 +1143,7 @@ static int vfat_find(struct inode *dir,struct qstr* qname, bh = NULL; if (new_filename) { - PRINTK(("vfat_find: create file 1\n")); + PRINTK3(("vfat_find: create file 1\n")); if (is_long) slots++; offset = vfat_find_free_slots(dir, slots); if (offset < 0) { @@ -975,14 +1151,14 @@ static int vfat_find(struct inode *dir,struct qstr* qname, goto cleanup; } - PRINTK(("vfat_find: create file 2\n")); + PRINTK3(("vfat_find: create file 2\n")); /* Now create the new entry */ bh = NULL; for (slot = 0, ps = ds; slot < slots; slot++, ps++) { - PRINTK(("vfat_find: create file 3, slot=%d\n",slot)); + PRINTK3(("vfat_find: create file 3, slot=%d\n",slot)); sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de); if (sinfo_out->ino < 0) { - PRINTK(("vfat_find: problem\n")); + PRINTK3(("vfat_find: problem\n")); res = sinfo_out->ino; goto cleanup; } @@ -990,11 +1166,11 @@ static int vfat_find(struct inode *dir,struct qstr* qname, fat_mark_buffer_dirty(sb, bh, 1); } - PRINTK(("vfat_find: create file 4\n")); + PRINTK3(("vfat_find: create file 4\n")); dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; mark_inode_dirty(dir); - PRINTK(("vfat_find: create file 5\n")); + PRINTK3(("vfat_find: create file 5\n")); fat_date_unix2dos(dir->i_mtime,&de->time,&de->date); de->ctime_ms = 0; @@ -1020,7 +1196,6 @@ static int vfat_find(struct inode *dir,struct qstr* qname, 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; } @@ -1033,33 +1208,36 @@ cleanup: int vfat_lookup(struct inode *dir,struct dentry *dentry) { int res; - struct slot_info sinfo; + struct vfat_slot_info sinfo; struct inode *result; + int table; - PRINTK (("vfat_lookup: name=%s, len=%d\n", + PRINTK2(("vfat_lookup: name=%s, len=%d\n", dentry->d_name.name, dentry->d_name.len)); + table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0; + dentry->d_op = &vfat_dentry_ops[table]; + result = NULL; if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0) { - d_add(dentry,NULL); - return 0; + result = NULL; + table++; + goto error; } - PRINTK (("vfat_lookup 4.5\n")); + PRINTK3(("vfat_lookup 4.5\n")); if (!(result = iget(dir->i_sb,sinfo.ino))) return -EACCES; - PRINTK (("vfat_lookup 5\n")); - if (!result->i_sb || - (result->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { - /* crossed a mount point into a non-msdos fs */ - d_add(dentry,NULL); - return 0; - } + PRINTK3(("vfat_lookup 5\n")); if (MSDOS_I(result)->i_busy) { /* mkdir in progress */ iput(result); - d_add(dentry,NULL); - return 0; - } - PRINTK (("vfat_lookup 6\n")); + result = NULL; + table++; + goto error; + } + PRINTK3(("vfat_lookup 6\n")); +error: + dentry->d_op = &vfat_dentry_ops[table]; + dentry->d_time = dentry->d_parent->d_inode->i_version; d_add(dentry,result); return 0; } @@ -1073,10 +1251,10 @@ static int vfat_create_entry(struct inode *dir,struct qstr* qname, loff_t offset; struct buffer_head *bh; struct msdos_dir_entry *de; - struct slot_info sinfo; + struct vfat_slot_info sinfo; *result=0; - PRINTK(("vfat_create_entry 1\n")); + PRINTK1(("vfat_create_entry: Entering\n")); res = vfat_find(dir, qname, 1, 1, is_dir, &sinfo); if (res < 0) { return res; @@ -1084,16 +1262,16 @@ static int vfat_create_entry(struct inode *dir,struct qstr* qname, offset = sinfo.shortname_offset; - PRINTK(("vfat_create_entry 2\n")); + PRINTK3(("vfat_create_entry 2\n")); bh = NULL; ino = fat_get_entry(dir, &offset, &bh, &de); if (ino < 0) { - PRINTK(("vfat_mkdir problem\n")); + PRINTK3(("vfat_mkdir problem\n")); if (bh) fat_brelse(sb, bh); return ino; } - PRINTK(("vfat_create_entry 3\n")); + PRINTK3(("vfat_create_entry 3\n")); if ((*result = iget(dir->i_sb,ino)) != NULL) vfat_read_inode(*result); @@ -1119,8 +1297,9 @@ int vfat_create(struct inode *dir,struct dentry* dentry,int mode) res = vfat_create_entry(dir,&dentry->d_name,0,&result); fat_unlock_creation(); if (res < 0) { - PRINTK(("vfat_create: unable to get new entry\n")); + PRINTK3(("vfat_create: unable to get new entry\n")); } else { + dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry,result); } return res; @@ -1133,7 +1312,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, struct super_block *sb = dir->i_sb; struct inode *dot; - PRINTK(("vfat_create_a_dotdir 1\n")); + PRINTK2(("vfat_create_a_dotdir: Entering\n")); /* * XXX all times should be set by caller upon successful completion. @@ -1171,7 +1350,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, iput(dot); - PRINTK(("vfat_create_a_dotdir 2\n")); + PRINTK3(("vfat_create_a_dotdir 2\n")); return 0; } @@ -1183,31 +1362,31 @@ static int vfat_create_dotdirs(struct inode *dir, struct inode *parent) struct msdos_dir_entry *de; loff_t offset; - PRINTK(("vfat_create_dotdirs 1\n")); + PRINTK2(("vfat_create_dotdirs: Entering\n")); if ((res = fat_add_cluster(dir)) < 0) return res; - PRINTK(("vfat_create_dotdirs 2\n")); + PRINTK3(("vfat_create_dotdirs 2\n")); offset = 0; bh = NULL; if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res; - PRINTK(("vfat_create_dotdirs 3\n")); + PRINTK3(("vfat_create_dotdirs 3\n")); res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1); - PRINTK(("vfat_create_dotdirs 4\n")); + PRINTK3(("vfat_create_dotdirs 4\n")); if (res < 0) { fat_brelse(sb, bh); return res; } - PRINTK(("vfat_create_dotdirs 5\n")); + PRINTK3(("vfat_create_dotdirs 5\n")); if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) { fat_brelse(sb, bh); return res; } - PRINTK(("vfat_create_dotdirs 6\n")); + PRINTK3(("vfat_create_dotdirs 6\n")); res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0); - PRINTK(("vfat_create_dotdirs 7\n")); + PRINTK3(("vfat_create_dotdirs 7\n")); fat_brelse(sb, bh); return res; @@ -1221,12 +1400,6 @@ static int vfat_empty(struct inode *dir) struct buffer_head *bh; struct msdos_dir_entry *de; - /* - * Prune any child dentries, then verify that - * the directory is empty and not in use. - */ - shrink_dcache_sb(sb); /* should be child prune */ - if (dir->i_count > 1) { return -EBUSY; } @@ -1262,6 +1435,7 @@ static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh, if (dir->i_dev != dentry->d_inode->i_dev || dir == dentry->d_inode) { return -EBUSY; } + res = vfat_empty(dentry->d_inode); if (res) { return res; @@ -1299,7 +1473,7 @@ static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh, return 0; } -static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo, +static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, struct buffer_head **bh,struct dentry* dentry, int is_dir,int nospc) { @@ -1333,14 +1507,44 @@ static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo, return 0; } +/* Replace inodes in alias dentries and drop all but the initial dentry */ +static void drop_replace_inodes(struct dentry *dentry, struct inode *inode) +{ + struct list_head *head, *next, *tmp; + struct dentry *alias; + + PRINTK1(("drop_replace_inodes: dentry=%p, inode=%p\n", dentry, inode)); + head = &dentry->d_inode->i_dentry; + if (dentry->d_inode) { + next = dentry->d_inode->i_dentry.next; + while (next != head) { + tmp = next; + next = tmp->next; + alias = list_entry(tmp, struct dentry, d_alias); + if (inode) { + list_del(&alias->d_alias); + iput(alias->d_inode); + d_instantiate(alias, inode); + /* dentry is already accounted for */ + if (alias != dentry) { + inode->i_count++; + } + } + if (alias != dentry) { + d_drop(alias); + } + } + } +} static int vfat_rmdirx(struct inode *dir,struct dentry* dentry) { struct super_block *sb = dir->i_sb; int res; struct buffer_head *bh; - struct slot_info sinfo; + struct vfat_slot_info sinfo; + PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry)); res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo); if (res >= 0 && sinfo.total_slots > 0) { @@ -1359,8 +1563,10 @@ static int vfat_rmdirx(struct inode *dir,struct dentry* dentry) int vfat_rmdir(struct inode *dir,struct dentry* dentry) { int res; + PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); res = vfat_rmdirx(dir, dentry); if (res >= 0) { + drop_replace_inodes(dentry, NULL); d_delete(dentry); } return res; @@ -1374,8 +1580,9 @@ static int vfat_unlinkx( struct super_block *sb = dir->i_sb; int res; struct buffer_head *bh; - struct slot_info sinfo; + struct vfat_slot_info sinfo; + PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); bh = NULL; res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo); @@ -1396,6 +1603,7 @@ int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode) struct inode *inode; int res; + PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); fat_lock_creation(); if ((res = vfat_create_entry(dir,&dentry->d_name,1,&inode)) < 0) { fat_unlock_creation(); @@ -1409,6 +1617,7 @@ int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode) res = vfat_create_dotdirs(inode, dir); fat_unlock_creation(); MSDOS_I(inode)->i_busy = 0; + dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry,inode); if (res < 0) { if (vfat_rmdir(dir,dentry) < 0) @@ -1422,8 +1631,10 @@ int vfat_unlink(struct inode *dir,struct dentry* dentry) { int res; + PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); res = vfat_unlinkx (dir,dentry,1); if (res >= 0) { + drop_replace_inodes(dentry, NULL); d_delete(dentry); } return res; @@ -1451,9 +1662,13 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, struct dentry *walk; int res, is_dir, i; int locked = 0; - struct slot_info sinfo; + struct vfat_slot_info sinfo; + int put_new_inode = 0; - PRINTK(("vfat_rename 1\n")); + PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n", + old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino, + new_dentry, new_dentry->d_inode, + new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0)); if (old_dir == new_dir && old_dentry->d_name.len == new_dentry->d_name.len && strncmp(old_dentry->d_name.name, new_dentry->d_name.name, @@ -1463,7 +1678,7 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, old_bh = new_bh = NULL; old_inode = new_inode = NULL; res = vfat_find(old_dir,&old_dentry->d_name,1,0,0,&sinfo); - PRINTK(("vfat_rename 2\n")); + PRINTK3(("vfat_rename 2\n")); if (res < 0) goto rename_done; old_slots = sinfo.total_slots; @@ -1471,7 +1686,7 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, old_offset = sinfo.shortname_offset; old_ino = sinfo.ino; res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de); - PRINTK(("vfat_rename 3\n")); + PRINTK3(("vfat_rename 3\n")); if (res < 0) goto rename_done; res = -ENOENT; @@ -1494,15 +1709,15 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, res = vfat_find(new_dir,&new_dentry->d_name,1,0,is_dir,&sinfo); - PRINTK(("vfat_rename 4\n")); + PRINTK3(("vfat_rename 4\n")); if (res > -1) { int new_is_dir; - PRINTK(("vfat_rename 5\n")); + PRINTK3(("vfat_rename 5\n")); /* Filename currently exists. Need to delete it */ new_offset = sinfo.shortname_offset; res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de); - PRINTK(("vfat_rename 6\n")); + PRINTK3(("vfat_rename 6\n")); if (res < 0) goto rename_done; if (!(new_inode = iget(new_dir->i_sb,res))) @@ -1510,82 +1725,70 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, new_is_dir = S_ISDIR(new_inode->i_mode); iput(new_inode); if (new_is_dir) { - PRINTK(("vfat_rename 7\n")); + PRINTK3(("vfat_rename 7\n")); res = vfat_rmdirx(new_dir,new_dentry); - PRINTK(("vfat_rename 8\n")); + PRINTK3(("vfat_rename 8\n")); if (res < 0) goto rename_done; } else { /* 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")); + PRINTK3(("vfat_rename 9\n")); + res = vfat_unlink(new_dir,new_dentry); + PRINTK3(("vfat_rename 10\n")); if (res < 0) goto rename_done; } } } - PRINTK(("vfat_rename 11\n")); + PRINTK3(("vfat_rename 11\n")); fat_lock_creation(); locked = 1; res = vfat_find(new_dir,&new_dentry->d_name,1,1,is_dir,&sinfo); - PRINTK(("vfat_rename 12\n")); + PRINTK3(("vfat_rename 12\n")); if (res < 0) goto rename_done; new_offset = sinfo.shortname_offset; new_ino = sinfo.ino; - res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de); - PRINTK(("vfat_rename 13\n")); - if (res < 0) goto rename_done; - - new_de->attr = old_de->attr; - new_de->time = old_de->time; - new_de->date = old_de->date; - new_de->ctime_ms = old_de->ctime_ms; - 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; + PRINTK3(("vfat_rename 13: new_ino=%d\n", new_ino)); if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done; - PRINTK(("vfat_rename 14\n")); + put_new_inode = 1; + + new_inode->i_mode = old_inode->i_mode; + new_inode->i_size = old_inode->i_size; + new_inode->i_blocks = old_inode->i_blocks; + new_inode->i_mtime = old_inode->i_mtime; + new_inode->i_atime = old_inode->i_atime; + new_inode->i_ctime = old_inode->i_ctime; + new_inode->i_nlink = old_inode->i_nlink; + new_inode->i_op = old_inode->i_op; + MSDOS_I(new_inode)->i_ctime_ms = MSDOS_I(old_inode)->i_ctime_ms; + + MSDOS_I(new_inode)->i_start = MSDOS_I(old_inode)->i_start; + MSDOS_I(new_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart; + MSDOS_I(new_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs; - /* At this point, we have the inodes of the old file and the - * new file. We need to transfer all information from the old - * inode to the new inode and then delete the slots of the old - * entry - */ - - vfat_read_inode(new_inode); - MSDOS_I(old_inode)->i_busy = 1; - MSDOS_I(old_inode)->i_linked = new_inode; - MSDOS_I(new_inode)->i_oldlink = old_inode; fat_cache_inval_inode(old_inode); - PRINTK(("vfat_rename 15: old_slots=%d\n",old_slots)); - mark_inode_dirty(old_inode); + mark_inode_dirty(new_inode); + old_dir->i_version = ++event; + new_dir->i_version = ++event; + + PRINTK3(("vfat_rename 14: old_slots=%d\n",old_slots)); /* remove the old entry */ for (i = old_slots; i > 0; --i) { res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de); if (res < 0) { - printk("vfat_unlinkx: problem 1\n"); + printk("vfat_rename: problem 1\n"); continue; } old_de->name[0] = DELETED_FLAG; old_de->attr = 0; fat_mark_buffer_dirty(sb, old_bh, 1); } - PRINTK(("vfat_rename 15b\n")); - - fat_mark_buffer_dirty(sb, new_bh, 1); + PRINTK3(("vfat_rename 15b\n")); - /* XXX: There is some code in the original MSDOS rename that - * is not duplicated here and it might cause a problem in - * certain circumstances. - */ - if (S_ISDIR(old_inode->i_mode)) { if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh, &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done; @@ -1609,8 +1812,11 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, } if (res > 0) res = 0; + if (res == 0) { + drop_replace_inodes(old_dentry, new_inode); d_move(old_dentry, new_dentry); + put_new_inode = 0; } rename_done: @@ -1620,6 +1826,8 @@ rename_done: fat_brelse(sb, old_bh); if (new_bh) fat_brelse(sb, new_bh); + if (put_new_inode) + iput(new_inode); return res; } |