diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
commit | 59223edaa18759982db0a8aced0e77457d10c68e (patch) | |
tree | 89354903b01fa0a447bffeefe00df3044495db2e /fs/fat | |
parent | db7d4daea91e105e3859cf461d7e53b9b77454b2 (diff) |
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/cache.c | 37 | ||||
-rw-r--r-- | fs/fat/dir.c | 571 | ||||
-rw-r--r-- | fs/fat/fatfs_syms.c | 15 | ||||
-rw-r--r-- | fs/fat/inode.c | 307 | ||||
-rw-r--r-- | fs/fat/misc.c | 101 |
5 files changed, 666 insertions, 365 deletions
diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 9083d6b31..b984fe759 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -2,6 +2,10 @@ * linux/fs/fat/cache.c * * Written 1992,1993 by Werner Almesberger + * + * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead + * of inode number. + * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. */ #include <linux/msdos_fs.h> @@ -62,6 +66,8 @@ int fat_access(struct super_block *sb,int nr,int new_value) p_first = p_last = NULL; /* GCC needs that stuff */ next = CF_LE_L(((unsigned long *) bh->b_data)[(first & (SECTOR_SIZE-1)) >> 2]); + /* Fscking Microsoft marketing department. Their "32" is 28. */ + next &= 0xfffffff; if (next >= 0xffffff7) next = -1; PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%d\n", b, nr, first, next)); @@ -141,14 +147,13 @@ void fat_cache_init(void) void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu) { struct fat_cache *walk; + int first = MSDOS_I(inode)->i_start; -#ifdef DEBUG -printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev), - inode->i_ino, cluster, *f_clu, *d_clu); -#endif + if (!first) + return; for (walk = fat_cache; walk; walk = walk->next) if (inode->i_dev == walk->device - && walk->ino == inode->i_ino + && walk->start_cluster == first && walk->file_cluster <= cluster && walk->file_cluster > *f_clu) { *d_clu = walk->disk_cluster; @@ -171,7 +176,8 @@ static void list_cache(void) for (walk = fat_cache; walk; walk = walk->next) { if (walk->device) printk("<%s,%d>(%d,%d) ", kdevname(walk->device), - walk->ino, walk->file_cluster, walk->disk_cluster); + walk->start_cluster, walk->file_cluster, + walk->disk_cluster); else printk("-- "); } printk("\n"); @@ -182,15 +188,12 @@ static void list_cache(void) void fat_cache_add(struct inode *inode,int f_clu,int d_clu) { struct fat_cache *walk,*last; + int first = MSDOS_I(inode)->i_start; -#ifdef DEBUG -printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev), - inode->i_ino, f_clu, d_clu); -#endif last = NULL; for (walk = fat_cache; walk->next; walk = (last = walk)->next) if (inode->i_dev == walk->device - && walk->ino == inode->i_ino + && walk->start_cluster == first && walk->file_cluster == f_clu) { if (walk->disk_cluster != d_clu) { printk("FAT cache corruption inode=%ld\n", @@ -209,7 +212,7 @@ list_cache(); return; } walk->device = inode->i_dev; - walk->ino = inode->i_ino; + walk->start_cluster = first; walk->file_cluster = f_clu; walk->disk_cluster = d_clu; last->next = NULL; @@ -227,10 +230,11 @@ list_cache(); void fat_cache_inval_inode(struct inode *inode) { struct fat_cache *walk; + int first = MSDOS_I(inode)->i_start; for (walk = fat_cache; walk; walk = walk->next) if (walk->device == inode->i_dev - && walk->ino == inode->i_ino) + && walk->start_cluster == first) walk->device = 0; } @@ -300,9 +304,11 @@ int fat_free(struct inode *inode,int skip) return -EIO; } } - if (last) + if (last) { fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb)); - else { + fat_cache_inval_inode(inode); + } else { + fat_cache_inval_inode(inode); MSDOS_I(inode)->i_start = 0; MSDOS_I(inode)->i_logstart = 0; mark_inode_dirty(inode); @@ -322,6 +328,5 @@ int fat_free(struct inode *inode,int skip) inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size; } unlock_fat(inode->i_sb); - fat_cache_inval_inode(inode); return 0; } diff --git a/fs/fat/dir.c b/fs/fat/dir.c index b6d6fb405..594baa0e2 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -9,7 +9,7 @@ * * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu> * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk> - * Plugged buffer overrun in readdir(). AV + * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV */ #define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) @@ -25,6 +25,7 @@ #include <linux/ioctl.h> #include <linux/dirent.h> #include <linux/mm.h> +#include <linux/ctype.h> #include <asm/uaccess.h> @@ -118,224 +119,348 @@ static void dump_de(struct msdos_dir_entry *de) printk("]\n"); } #endif -int fat_readdirx( - struct inode *inode, - struct file *filp, - void *dirent, - fat_filldir_t fat_filldir, - filldir_t filldir, - int shortnames, - int longnames, - int both) +static int memicmp(const char *s1, const char *s2, int len) { + while(len--) if (tolower(*s1++)!=tolower(*s2++)) return 1; + 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 char *name, int name_len, int anycase, + loff_t *spos, loff_t *lpos) { struct super_block *sb = inode->i_sb; int ino,i,i2,last; char c; - struct buffer_head *bh; + struct buffer_head *bh = NULL; struct msdos_dir_entry *de; - unsigned long oldpos = filp->f_pos; - unsigned long spos; - int is_long; - char longname[275]; - unsigned char long_len = 0; /* Make compiler warning go away */ - unsigned char alias_checksum = 0; /* Make compiler warning go away */ - unsigned char long_slots = 0; + loff_t cpos = 0; + char bufname[14]; + unsigned char long_slots; int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; int utf8 = MSDOS_SB(sb)->options.utf8; unsigned char *unicode = NULL; struct nls_table *nls = MSDOS_SB(sb)->nls_io; - -/* Fake . and .. for the root directory. */ - if (inode->i_ino == MSDOS_ROOT_INO) { - while (oldpos < 2) { - if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0) - return 0; - oldpos++; - filp->f_pos++; - } - if (oldpos == 2) - filp->f_pos = 0; - } - if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) - return -ENOENT; - - bh = NULL; - longname[0] = longname[1] = 0; - is_long = 0; - ino = fat_get_entry(inode,&filp->f_pos,&bh,&de); - while (ino > -1) { -#if 0 - dump_de(de); -#endif - /* Check for long filename entry */ - if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) { - is_long = 0; - oldpos = filp->f_pos; - } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) { - int get_new_entry; + int res = 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; int offset; unsigned char id; unsigned char slot; - unsigned char slots = 0; + unsigned char slots; + unsigned char sum; + unsigned char alias_checksum; if (!unicode) { unicode = (unsigned char *) __get_free_page(GFP_KERNEL); - if (!unicode) + if (!unicode) { + fat_brelse(sb, bh); return -ENOMEM; + } } - +parse_long: + slots = 0; offset = 0; ds = (struct msdos_dir_slot *) de; id = ds->id; - if (id & 0x40) { - slots = id & ~0x40; - /* - * Dirty, but not dirtier than the original, - * and plugs the hole. - */ - if (slots > 20) - slots = 0; - else { - long_slots = slots; - is_long = 1; - alias_checksum = ds->alias_checksum; - } - } + if (!(id & 0x40)) + continue; + slots = id & ~0x40; + if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ + continue; + long_slots = slots; + alias_checksum = ds->alias_checksum; - get_new_entry = 1; slot = slots; - while (slot > 0) { - PRINTK(("1. get_new_entry: %d\n", get_new_entry)); - if (ds->attr != ATTR_EXT) { - is_long = 0; - get_new_entry = 0; - break; - } - if ((ds->id & ~0x40) != slot) { - is_long = 0; - break; - } - if (ds->alias_checksum != alias_checksum) { - is_long = 0; - break; - } + while (1) { slot--; offset = slot * 26; - PRINTK(("2. get_new_entry: %d\n", get_new_entry)); memcpy(&unicode[offset], ds->name0_4, 10); - offset += 10; - memcpy(&unicode[offset], ds->name5_10, 12); - offset += 12; - memcpy(&unicode[offset], ds->name11_12, 4); - offset += 4; + memcpy(&unicode[offset+10], ds->name5_10, 12); + memcpy(&unicode[offset+22], ds->name11_12, 4); + offset += 26; if (ds->id & 0x40) { unicode[offset] = 0; unicode[offset+1] = 0; } - if (slot > 0) { - ino = fat_get_entry(inode,&filp->f_pos,&bh,&de); - PRINTK(("4. get_new_entry: %d\n", get_new_entry)); - if (ino == -1) { - is_long = 0; - get_new_entry = 0; - break; - } - ds = (struct msdos_dir_slot *) de; - } - PRINTK(("5. get_new_entry: %d\n", get_new_entry)); + 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; } - } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) { - char bufname[14]; - char *ptname = bufname; - int dotoffset = 0; - int was_long = is_long; - - if (is_long) { - unsigned char sum; - for (sum = 0, i = 0; i < 11; i++) { - sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; - } + 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; + } - if (sum != alias_checksum) { - PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum)); - is_long = 0; - long_slots = 0; - } - if (utf8) { - long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname)); - } else { - long_len = uni16_to_x8(longname, unicode, uni_xlate, nls); - } - } + for (i = 0, last = 0; i < 8;) { + if (!(c = de->name[i])) break; + if (c >= 'A' && c <= 'Z') c += 32; + if (c == 0x05) c = 0xE5; + if ((bufname[i++] = c) != ' ') + last = i; + } + i = last; + bufname[i++] = '.'; + for (i2 = 0; i2 < 3; i2++) { + if (!(c = de->ext[i2])) break; + if (c >= 'A' && c <= 'Z') c += 32; + if ((bufname[i++] = c) != ' ') + last = i; + } + if (!last) + continue; + + if (last==name_len) + if ((!anycase && !memcmp(name, bufname, last)) || + (anycase && !memicmp(name, bufname, last))) + goto Found; + if (long_slots) { + char longname[260]; /* 256 + 4 */ + unsigned char long_len; + long_len = utf8 + ?utf8_wcstombs(longname, (__u16 *) unicode, 260) + :uni16_to_x8(longname, unicode, uni_xlate, nls); + if (long_len != name_len) + continue; + if ((!anycase && !memcmp(name, longname, long_len)) || + (anycase && !memicmp(name, longname, long_len))) + goto Found; + } + } - if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) { - bufname[0] = '.'; - dotoffset = 1; - ptname = bufname+1; - } - for (i = 0, last = 0; i < 8; i++) { - if (!(c = de->name[i])) break; - if (c >= 'A' && c <= 'Z') c += 32; - /* see namei.c, msdos_format_name */ - if (c == 0x05) c = 0xE5; - if (c != ' ') - last = i+1; - ptname[i] = c; - } - i = last; - ptname[i] = '.'; - i++; - for (i2 = 0; i2 < 3; i2++) { - if (!(c = de->ext[i2])) break; - if (c >= 'A' && c <= 'Z') c += 32; - if (c != ' ') - last = i+1; - ptname[i] = c; - i++; +Found: + fat_brelse(sb, bh); + res = long_slots + 1; + *spos = cpos - sizeof(struct msdos_dir_entry); + *lpos = cpos - res*sizeof(struct msdos_dir_entry); +EODir: + 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; + int ino,inum,i,i2,last; + char c; + struct buffer_head *bh; + struct msdos_dir_entry *de; + unsigned long lpos; + loff_t cpos; + unsigned char long_slots; + int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; + int utf8 = MSDOS_SB(sb)->options.utf8; + unsigned char *unicode = NULL; + struct nls_table *nls = MSDOS_SB(sb)->nls_io; + char bufname[14]; + char *ptname = bufname; + int dotoffset = 0; + + 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) < 0) + return 0; + cpos++; + filp->f_pos++; + } + if (cpos == 2) + 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 (MSDOS_SB(sb)->options.isvfat) { + if (de->name[0] == (__s8) DELETED_FLAG) + goto RecEnd; + if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) + goto RecEnd; + if (de->attr != ATTR_EXT && IS_FREE(de->name)) + goto RecEnd; + } else { + if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) + goto RecEnd; + } + + if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) { + struct msdos_dir_slot *ds; + int offset; + unsigned char id; + unsigned char slot; + unsigned char slots; + unsigned char sum; + unsigned char alias_checksum; + + if (!unicode) { + unicode = (unsigned char *) + __get_free_page(GFP_KERNEL); + if (!unicode) { + filp->f_pos = cpos; + fat_brelse(sb, bh); + return -ENOMEM; } - if ((i = last) != 0) { - if (!strcmp(de->name,MSDOS_DOT)) - ino = inode->i_ino; - else if (!strcmp(de->name,MSDOS_DOTDOT)) - ino = fat_parent_ino(inode,0); - - if (shortnames || !is_long) { - if (both) - bufname[i+dotoffset] = '\0'; - spos = oldpos; - if (was_long) { - spos = filp->f_pos - sizeof(struct msdos_dir_entry); - } else { - long_slots = 0; - } - if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) { - filp->f_pos = oldpos; - break; - } - } - if (is_long && longnames) { - if (both) { - memcpy(&longname[long_len+1], bufname, i+dotoffset); - long_len += i+dotoffset; - } - spos = filp->f_pos - sizeof(struct msdos_dir_entry); - if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) { - filp->f_pos = oldpos; - break; - } - } - oldpos = filp->f_pos; + } +ParseLong: + slots = 0; + offset = 0; + ds = (struct msdos_dir_slot *) de; + id = ds->id; + if (!(id & 0x40)) + goto RecEnd; + slots = id & ~0x40; + if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ + goto RecEnd; + long_slots = slots; + alias_checksum = ds->alias_checksum; + + slot = slots; + while (1) { + slot--; + offset = slot * 26; + memcpy(&unicode[offset], ds->name0_4, 10); + memcpy(&unicode[offset+10], ds->name5_10, 12); + memcpy(&unicode[offset+22], ds->name11_12, 4); + offset += 26; + + if (ds->id & 0x40) { + unicode[offset] = 0; + unicode[offset+1] = 0; } - is_long = 0; - } else { - is_long = 0; - oldpos = filp->f_pos; + if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) + goto EODir; + if (slot == 0) + break; + ds = (struct msdos_dir_slot *) de; + if (ds->attr != ATTR_EXT) + goto RecEnd; /* XXX */ + if ((ds->id & ~0x40) != slot) + goto ParseLong; + if (ds->alias_checksum != alias_checksum) + goto ParseLong; + } + if (de->name[0] == (__s8) DELETED_FLAG) + goto RecEnd; + if (de->attr == ATTR_EXT) + goto ParseLong; + if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) + goto RecEnd; + 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; + } + + if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) { + *ptname++ = '.'; + dotoffset = 1; + } + for (i = 0, last = 0; i < 8;) { + if (!(c = de->name[i])) break; + if (c >= 'A' && c <= 'Z') c += 32; + /* see namei.c, msdos_format_name */ + if (c == 0x05) c = 0xE5; + if ((ptname[i++] = c) != ' ') + last = i; + } + i = last; + ptname[i++] = '.'; + for (i2 = 0; i2 < 3; i2++) { + if (!(c = de->ext[i2])) break; + if (c >= 'A' && c <= 'Z') c += 32; + if ((ptname[i++] = c) != ' ') + last = i; + } + if (!last) + goto RecEnd; + + i = last + dotoffset; + + lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry); + if (!memcmp(de->name,MSDOS_DOT,11)) + inum = inode->i_ino; + else if (!memcmp(de->name,MSDOS_DOTDOT,11)) { +/* inum = fat_parent_ino(inode,0); */ + inum = filp->f_dentry->d_parent->d_inode->i_ino; + } else { + struct inode *tmp = fat_iget(sb, ino); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else + inum = iunique(sb, MSDOS_ROOT_INO); + } + + if (!long_slots||shortnames) { + if (both) + bufname[i] = '\0'; + if (filldir(dirent, bufname, i, lpos, inum) < 0) + goto FillFailed; + } else { + char longname[275]; + unsigned char long_len = utf8 + ? utf8_wcstombs(longname, (__u16 *) unicode, 275) + : uni16_to_x8(longname, unicode, uni_xlate, nls); + if (both) { + memcpy(&longname[long_len+1], bufname, i); + long_len += i; } - ino = fat_get_entry(inode,&filp->f_pos,&bh,&de); + if (filldir(dirent, longname, long_len, lpos, inum) < 0) + goto FillFailed; } + +RecEnd: + filp->f_pos = cpos; + goto GetNew; +EODir: + filp->f_pos = cpos; +FillFailed: if (bh) fat_brelse(sb, bh); if (unicode) { @@ -344,39 +469,17 @@ int fat_readdirx( return 0; } -static int fat_filldir( - filldir_t filldir, - void * buf, - const char * name, - int name_len, - int is_long, - off_t offset, - off_t short_offset, - int long_slots, - ino_t ino) -{ - return filldir(buf, name, name_len, offset, ino); -} - -int fat_readdir( - struct file *filp, - void *dirent, - filldir_t filldir) +int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; - return fat_readdirx(inode, filp, dirent, fat_filldir, filldir, - 0, 1, 0); + return fat_readdirx(inode, filp, dirent, filldir, 0, 0); } static int vfat_ioctl_fill( - filldir_t filldir, void * buf, const char * name, int name_len, - int is_long, off_t offset, - off_t short_offset, - int long_slots, ino_t ino) { struct dirent *d1 = (struct dirent *)buf; @@ -438,7 +541,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, return err; put_user(0, &d1->d_reclen); return fat_readdirx(inode,filp,(void *)arg, - vfat_ioctl_fill, NULL, 0, 1, 1); + vfat_ioctl_fill, 0, 1); } case VFAT_IOCTL_READDIR_SHORT: { struct dirent *d1 = (struct dirent *)arg; @@ -447,7 +550,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, if (err) return err; return fat_readdirx(inode,filp,(void *)arg, - vfat_ioctl_fill, NULL, 1, 0, 1); + vfat_ioctl_fill, 1, 1); } default: /* forward ioctl to CVF extension */ @@ -461,6 +564,62 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, return 0; } +/***** See if directory is empty */ +int fat_dir_empty(struct inode *dir) +{ + loff_t pos; + struct buffer_head *bh; + struct msdos_dir_entry *de; + int ino,result = 0; + + pos = 0; + bh = NULL; + while (fat_get_entry(dir,&pos,&bh,&de,&ino) > -1) { + /* Ignore vfat longname entries */ + if (de->attr == ATTR_EXT) + continue; + if (!IS_FREE(de->name) && + strncmp(de->name,MSDOS_DOT , MSDOS_NAME) && + strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) { + result = -ENOTEMPTY; + break; + } + } + if (bh) + fat_brelse(dir->i_sb, bh); + + return result; +} + +/* This assumes that size of cluster is above the 32*slots */ + +int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh, + struct msdos_dir_entry **de, int *ino) +{ + struct super_block *sb = dir->i_sb; + loff_t offset, curr; + int row; + int res; + + offset = curr = 0; + *bh = NULL; + row = 0; + while (fat_get_entry(dir,&curr,bh,de,ino) > -1) { + if (IS_FREE((*de)->name)) { + if (++row == slots) + return offset; + } else { + row = 0; + offset = curr; + } + } + if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) + return -ENOSPC; + if ((res = fat_add_cluster(dir)) < 0) return res; + do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots); + return offset; +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c index 838c679d4..a8cd88cac 100644 --- a/fs/fat/fatfs_syms.c +++ b/fs/fat/fatfs_syms.c @@ -18,9 +18,11 @@ extern struct file_operations fat_dir_operations; EXPORT_SYMBOL(fat_add_cluster); +EXPORT_SYMBOL(fat_add_cluster1); EXPORT_SYMBOL(fat_bmap); EXPORT_SYMBOL(fat_brelse); EXPORT_SYMBOL(fat_cache_inval_inode); +EXPORT_SYMBOL(fat_clear_inode); EXPORT_SYMBOL(fat_date_unix2dos); EXPORT_SYMBOL(fat_delete_inode); EXPORT_SYMBOL(fat_dir_operations); @@ -28,17 +30,18 @@ EXPORT_SYMBOL(fat_esc2uni); EXPORT_SYMBOL(fat_file_read); EXPORT_SYMBOL(fat_file_write); EXPORT_SYMBOL(fat_fs_panic); -EXPORT_SYMBOL(fat_get_entry); +EXPORT_SYMBOL(fat__get_entry); EXPORT_SYMBOL(fat_lock_creation); EXPORT_SYMBOL(fat_mark_buffer_dirty); EXPORT_SYMBOL(fat_mmap); EXPORT_SYMBOL(fat_notify_change); EXPORT_SYMBOL(fat_parent_ino); -EXPORT_SYMBOL(fat_put_inode); EXPORT_SYMBOL(fat_put_super); -EXPORT_SYMBOL(fat_read_inode); +EXPORT_SYMBOL(fat_attach); +EXPORT_SYMBOL(fat_detach); +EXPORT_SYMBOL(fat_build_inode); EXPORT_SYMBOL(fat_read_super); -EXPORT_SYMBOL(fat_readdirx); +EXPORT_SYMBOL(fat_search_long); EXPORT_SYMBOL(fat_readdir); EXPORT_SYMBOL(fat_scan); EXPORT_SYMBOL(fat_smap); @@ -54,9 +57,11 @@ EXPORT_SYMBOL(lock_fat); EXPORT_SYMBOL(unlock_fat); EXPORT_SYMBOL(fat_dir_ioctl); EXPORT_SYMBOL(fat_readpage); -EXPORT_SYMBOL(fat_is_binary); +EXPORT_SYMBOL(fat_add_entries); +EXPORT_SYMBOL(fat_dir_empty); int init_fat_fs(void) { + fat_hash_init(); return 0; } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 339bcb6f6..b55bfd712 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -3,6 +3,7 @@ * * Written 1992,1993 by Werner Almesberger * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner + * Rewritten for the constant inumbers support by Al Viro * * Fixes: * @@ -45,34 +46,123 @@ # define PRINTK1(x) #endif -void fat_put_inode(struct inode *inode) +/* + * New FAT inode stuff. We do the following: + * a) i_ino is constant and has nothing with on-disk location. + * b) FAT manages its own cache of directory entries. + * c) *This* cache is indexed by on-disk location. + * d) inode has an associated directory entry, all right, but + * it may be unhashed. + * e) currently entries are stored within struct inode. That should + * change. + * f) we deal with races in the following way: + * 1. readdir() and lookup() do FAT-dir-cache lookup. + * 2. rename() unhashes the F-d-c entry and rehashes it in + * a new place. + * 3. unlink() and rmdir() unhash F-d-c entry. + * 4. fat_write_inode() checks whether the thing is unhashed. + * If it is we silently return. If it isn't we do bread(), + * check if the location is still valid and retry if it + * isn't. Otherwise we do changes. + * 5. Spinlock is used to protect hash/unhash/location check/lookup + * 6. fat_clear_inode() unhashes the F-d-c entry. + * 7. lookup() and readdir() do igrab() if they find a F-d-c entry + * and consider negative result as cache miss. + */ + +#define FAT_HASH_BITS 8 +#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS) +#define FAT_HASH_MASK (FAT_HASH_SIZE-1) +static struct list_head fat_inode_hashtable[FAT_HASH_SIZE]; +spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED; + +void fat_hash_init(void) { + int i; + for(i=0;i<FAT_HASH_SIZE;i++) { + INIT_LIST_HEAD(&fat_inode_hashtable[i]); + } +} + +static inline unsigned long fat_hash(struct super_block *sb, int i_pos) { - /* - * Check whether we're a dependent of other inodes ... - */ - if (inode->i_count <= 1) { -#ifdef FAT_PARANOIA -printk("fat_put_inode: last use for (%p,%ld), i_count=%d\n", -inode, inode->i_ino, inode->i_count); -#endif - if (inode->i_nlink) { - if (MSDOS_I(inode)->i_busy) - fat_cache_inval_inode(inode); - } + unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb; + tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS*2); + return tmp & FAT_HASH_MASK; +} + +void fat_attach(struct inode *inode, int i_pos) { + spin_lock(&fat_inode_lock); + MSDOS_I(inode)->i_location = i_pos; + list_add(&MSDOS_I(inode)->i_fat_hash, + fat_inode_hashtable+fat_hash(inode->i_sb, i_pos)); + spin_unlock(&fat_inode_lock); +} + +void fat_detach(struct inode *inode) { + spin_lock(&fat_inode_lock); + MSDOS_I(inode)->i_location = 0; + list_del(&MSDOS_I(inode)->i_fat_hash); + INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); + spin_unlock(&fat_inode_lock); +} + +struct inode *fat_iget(struct super_block *sb, int i_pos) { + struct list_head *p = fat_inode_hashtable + fat_hash(sb, i_pos); + struct list_head *walk; + struct msdos_inode_info *i; + struct inode *inode = NULL; + spin_lock(&fat_inode_lock); + for(walk=p->next;walk!=p;walk=walk->next) { + i = list_entry(walk, struct msdos_inode_info, i_fat_hash); + if (i->i_fat_inode->i_sb != sb) + continue; + if (i->i_location != i_pos) + continue; + inode = igrab(i->i_fat_inode); } + spin_unlock(&fat_inode_lock); + return inode; +} + +static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de); + +struct inode *fat_build_inode(struct super_block *sb, + struct msdos_dir_entry *de, int ino, int *res) +{ + struct inode *inode; + *res = 0; + inode = fat_iget(sb, ino); + if (inode) + goto out; + inode = get_empty_inode(); + *res = -ENOMEM; + if (!inode) + goto out; + *res = 0; + inode->i_sb = sb; + inode->i_dev = sb->s_dev; + inode->i_ino = iunique(sb, MSDOS_ROOT_INO); + fat_fill_inode(inode, de); + fat_attach(inode, ino); + insert_inode_hash(inode); +out: + return inode; } void fat_delete_inode(struct inode *inode) { - /* - * Make sure there are no active dependencies ... - */ - fat_cache_inval_inode(inode); inode->i_size = 0; fat_truncate(inode); clear_inode(inode); } +void fat_clear_inode(struct inode *inode) +{ + spin_lock(&fat_inode_lock); + fat_cache_inval_inode(inode); + list_del(&MSDOS_I(inode)->i_fat_hash); + spin_unlock(&fat_inode_lock); +} void fat_put_super(struct super_block *sb) { @@ -103,6 +193,8 @@ void fat_put_super(struct super_block *sb) MSDOS_SB(sb)->options.iocharset = NULL; } + if (MSDOS_SB(sb)->put_super_callback) + MSDOS_SB(sb)->put_super_callback(sb); MOD_DEC_USE_COUNT; return; } @@ -267,6 +359,61 @@ out: return ret; } +static void fat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + int nr; + + MSDOS_I(inode)->i_binary = 1; + INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); + MSDOS_I(inode)->i_location = 0; + MSDOS_I(inode)->i_fat_inode = inode; + inode->i_uid = MSDOS_SB(sb)->options.fs_uid; + inode->i_gid = MSDOS_SB(sb)->options.fs_gid; + inode->i_version = ++event; + inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR; + inode->i_op = MSDOS_SB(sb)->dir_ops; + if (MSDOS_SB(sb)->fat_bits == 32) { + MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster; + if ((nr = MSDOS_I(inode)->i_start) != 0) { + while (nr != -1) { + inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size; + if (!(nr = fat_access(sb,nr,-1))) { + printk("Directory %ld: bad FAT\n", + inode->i_ino); + break; + } + } + } + } else { + MSDOS_I(inode)->i_start = 0; + inode->i_size = MSDOS_SB(sb)->dir_entries* + sizeof(struct msdos_dir_entry); + } + inode->i_blksize = MSDOS_SB(sb)->cluster_size* SECTOR_SIZE; + inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ + inode->i_blksize*MSDOS_SB(sb)->cluster_size; + MSDOS_I(inode)->i_logstart = 0; + + MSDOS_I(inode)->i_attrs = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = 0; + MSDOS_I(inode)->i_ctime_ms = 0; + inode->i_nlink = fat_subdirs(inode)+2; +} + +static struct super_operations fat_sops = { + NULL, + fat_write_inode, + NULL, + fat_delete_inode, + fat_notify_change, + fat_put_super, + NULL, /* write_super */ + fat_statfs, + NULL, /* remount */ + fat_clear_inode +}; + /* * Read the super block of an MS-DOS FS. * @@ -274,7 +421,8 @@ out: * with some fields already initialized. */ struct super_block * -fat_read_super(struct super_block *sb, void *data, int silent) +fat_read_super(struct super_block *sb, void *data, int silent, + struct inode_operations *fs_dir_inode_ops) { struct inode *root_inode; struct buffer_head *bh; @@ -296,6 +444,9 @@ fat_read_super(struct super_block *sb, void *data, int silent) MSDOS_SB(sb)->private_data = NULL; MOD_INC_USE_COUNT; + MSDOS_SB(sb)->dir_ops = fs_dir_inode_ops; + MSDOS_SB(sb)->put_super_callback = NULL; + sb->s_op = &fat_sops; if (hardsect_size[MAJOR(sb->s_dev)] != NULL){ blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]; if (blksize != 512){ @@ -464,7 +615,7 @@ fat_read_super(struct super_block *sb, void *data, int silent) sb->s_magic = MSDOS_SUPER_MAGIC; /* set up enough so that it can read an inode */ - MSDOS_SB(sb)->fat_wait = NULL; + init_waitqueue_head(&MSDOS_SB(sb)->fat_wait); MSDOS_SB(sb)->fat_lock = 0; MSDOS_SB(sb)->prev_free = 0; @@ -491,10 +642,15 @@ fat_read_super(struct super_block *sb, void *data, int silent) } } - root_inode = iget(sb, MSDOS_ROOT_INO); + root_inode=get_empty_inode(); if (!root_inode) - goto out_no_root; - sb->s_root = d_alloc_root(root_inode, NULL); + goto out_unload_nls; + root_inode->i_sb = sb; + root_inode->i_dev = sb->s_dev; + root_inode->i_ino = MSDOS_ROOT_INO; + fat_read_root(root_inode); + insert_inode_hash(root_inode); + sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto out_no_root; if(i>=0) { @@ -590,71 +746,28 @@ static int is_exec(char *extension) return 0; } -void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_ops) +/* doesn't deal with root inode */ +static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) { struct super_block *sb = inode->i_sb; - struct buffer_head *bh; - struct msdos_dir_entry *raw_entry; int nr; - PRINTK1(("fat_read_inode: inode=%p, ino=%ld, sb->dir_start=0x%x\n", - inode, inode->i_ino, MSDOS_SB(sb)->dir_start)); - MSDOS_I(inode)->i_busy = 0; MSDOS_I(inode)->i_binary = 1; + INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); + MSDOS_I(inode)->i_location = 0; + MSDOS_I(inode)->i_fat_inode = inode; inode->i_uid = MSDOS_SB(sb)->options.fs_uid; inode->i_gid = MSDOS_SB(sb)->options.fs_gid; inode->i_version = ++event; - if (inode->i_ino == MSDOS_ROOT_INO) { - inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | - S_IFDIR; - inode->i_op = fs_dir_inode_ops; - if (MSDOS_SB(sb)->fat_bits == 32) { - MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster; - if ((nr = MSDOS_I(inode)->i_start) != 0) { - while (nr != -1) { - inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size; - if (!(nr = fat_access(sb,nr,-1))) { - printk("Directory %ld: bad FAT\n", - inode->i_ino); - break; - } - } - } - } else { - MSDOS_I(inode)->i_start = 0; - inode->i_size = MSDOS_SB(sb)->dir_entries* - sizeof(struct msdos_dir_entry); - } - inode->i_blksize = MSDOS_SB(sb)->cluster_size* - SECTOR_SIZE; - inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ - inode->i_blksize*MSDOS_SB(sb)->cluster_size; - MSDOS_I(inode)->i_logstart = 0; - - MSDOS_I(inode)->i_attrs = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = 0; - MSDOS_I(inode)->i_ctime_ms = 0; - inode->i_nlink = fat_subdirs(inode)+2; - /* subdirs (neither . nor ..) plus . and "self" */ - return; - } - if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { - printk("dev = %s, ino = %ld\n", - kdevname(inode->i_dev), inode->i_ino); - fat_fs_panic(sb, "fat_read_inode: unable to read i-node block"); - return; - } - raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) - [inode->i_ino & (MSDOS_DPB-1)]; - if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) { - inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO & + if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) { + inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR; - inode->i_op = fs_dir_inode_ops; + inode->i_op = MSDOS_SB(inode->i_sb)->dir_ops; - MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); + MSDOS_I(inode)->i_start = CF_LE_W(de->start); if (MSDOS_SB(sb)->fat_bits == 32) { MSDOS_I(inode)->i_start |= - (CF_LE_W(raw_entry->starthi) << 16); + (CF_LE_W(de->starthi) << 16); } MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start; inode->i_nlink = fat_subdirs(inode); @@ -677,10 +790,10 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o } } } else { /* not a directory */ - inode->i_mode = MSDOS_MKMODE(raw_entry->attr, + inode->i_mode = MSDOS_MKMODE(de->attr, ((IS_NOEXEC(inode) || (MSDOS_SB(sb)->options.showexec && - !is_exec(raw_entry->ext))) + !is_exec(de->ext))) ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG; if (MSDOS_SB(sb)->cvf_format) @@ -691,51 +804,58 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048) ? &fat_file_inode_operations_1024 : &fat_file_inode_operations; - MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); + MSDOS_I(inode)->i_start = CF_LE_W(de->start); if (MSDOS_SB(sb)->fat_bits == 32) { MSDOS_I(inode)->i_start |= - (CF_LE_W(raw_entry->starthi) << 16); + (CF_LE_W(de->starthi) << 16); } MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start; inode->i_nlink = 1; - inode->i_size = CF_LE_L(raw_entry->size); + inode->i_size = CF_LE_L(de->size); } - if(raw_entry->attr & ATTR_SYS) + if(de->attr & ATTR_SYS) if (MSDOS_SB(sb)->options.sys_immutable) inode->i_flags |= S_IMMUTABLE; MSDOS_I(inode)->i_binary = - fat_is_binary(MSDOS_SB(sb)->options.conversion, raw_entry->ext); - MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED; + fat_is_binary(MSDOS_SB(sb)->options.conversion, de->ext); + MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; /* this is as close to the truth as we can get ... */ inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE; inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ inode->i_blksize*MSDOS_SB(sb)->cluster_size; inode->i_mtime = inode->i_atime = - date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date)); + date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date)); inode->i_ctime = MSDOS_SB(sb)->options.isvfat - ? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate)) + ? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate)) : inode->i_mtime; - MSDOS_I(inode)->i_ctime_ms = raw_entry->ctime_ms; - fat_brelse(sb, bh); + MSDOS_I(inode)->i_ctime_ms = de->ctime_ms; } - void fat_write_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct buffer_head *bh; struct msdos_dir_entry *raw_entry; + int i_pos; - if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return; - if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { - printk("dev = %s, ino = %ld\n", - kdevname(inode->i_dev), inode->i_ino); +retry: + i_pos = MSDOS_I(inode)->i_location; + if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) return; + if (!(bh = fat_bread(sb, i_pos >> MSDOS_DPB_BITS))) { + printk("dev = %s, ino = %d\n", kdevname(inode->i_dev), i_pos); fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block"); return; } + spin_lock(&fat_inode_lock); + if (i_pos != MSDOS_I(inode)->i_location) { + spin_unlock(&fat_inode_lock); + fat_brelse(sb, bh); + goto retry; + } + raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) - [inode->i_ino & (MSDOS_DPB-1)]; + [i_pos & (MSDOS_DPB-1)]; if (S_ISDIR(inode->i_mode)) { raw_entry->attr = ATTR_DIR; raw_entry->size = 0; @@ -757,6 +877,7 @@ void fat_write_inode(struct inode *inode) raw_entry->ctime = CT_LE_W(raw_entry->ctime); raw_entry->cdate = CT_LE_W(raw_entry->cdate); } + spin_unlock(&fat_inode_lock); fat_mark_buffer_dirty(sb, bh, 1); fat_brelse(sb, bh); } diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 1dfddd3c7..4cd218d58 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -79,7 +79,7 @@ int fat_is_binary(char conversion,char *extension) /* File creation lock. This is system-wide to avoid deadlocks in rename. */ /* (rename might deadlock before detecting cross-FS moves.) */ -static struct wait_queue *creation_wait = NULL; +static DECLARE_WAIT_QUEUE_HEAD(creation_wait); static int creation_lock = 0; @@ -144,17 +144,17 @@ void fat_clusters_flush(struct super_block *sb) * represented by inode. The cluster is zero-initialized. */ -int fat_add_cluster(struct inode *inode) +struct buffer_head *fat_add_cluster1(struct inode *inode) { struct super_block *sb = inode->i_sb; int count,nr,limit,last,curr,sector,last_sector,file_cluster; - struct buffer_head *bh; + struct buffer_head *bh, *res=NULL; int cluster_size = MSDOS_SB(sb)->cluster_size; if (MSDOS_SB(sb)->fat_bits != 32) { - if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC; + if (inode->i_ino == MSDOS_ROOT_INO) return res; } - if (!MSDOS_SB(sb)->free_clusters) return -ENOSPC; + if (!MSDOS_SB(sb)->free_clusters) return res; lock_fat(sb); limit = MSDOS_SB(sb)->clusters; nr = limit; /* to keep GCC happy */ @@ -170,7 +170,7 @@ printk("free cluster: %d\n",nr); if (count >= limit) { MSDOS_SB(sb)->free_clusters = 0; unlock_fat(sb); - return -ENOSPC; + return res; } fat_access(sb,nr,EOF_FAT(sb)); if (MSDOS_SB(sb)->free_clusters != -1) @@ -202,7 +202,7 @@ printk("set to %x\n",fat_access(sb,nr,-1)); if (!(curr = fat_access(sb, last = curr,-1))) { fat_fs_panic(sb,"File without EOF"); - return -ENOSPC; + return res; } } PRINTK ((" -- ")); @@ -235,7 +235,10 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1)); memset(bh->b_data,0,SECTOR_SIZE); fat_set_uptodate(sb, bh, 1); fat_mark_buffer_dirty(sb, bh, 1); - fat_brelse(sb, bh); + if (!res) + res=bh; + else + fat_brelse(sb, bh); } } if (file_cluster != inode->i_blocks/cluster_size){ @@ -257,9 +260,17 @@ printk("size is %d now (%x)\n",inode->i_size,inode); #endif mark_inode_dirty(inode); } - return 0; + return res; } +int fat_add_cluster(struct inode *inode) +{ + struct buffer_head *bh = fat_add_cluster1(inode); + if (!bh) + return -ENOSPC; + fat_brelse(inode->i_sb, bh); + return 0; +} /* Linear day numbers of the respective 1sts in non-leap years. */ @@ -319,10 +330,17 @@ void fat_date_unix2dos(int unix_date,unsigned short *time, /* 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. */ + 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. + */ -int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, - struct msdos_dir_entry **de) +int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, + struct msdos_dir_entry **de, int *ino) { struct super_block *sb = dir->i_sb; int sector, offset; @@ -330,15 +348,16 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, while (1) { offset = *pos; PRINTK (("get_entry offset %d\n",offset)); + if (*bh) + fat_brelse(sb, *bh); + *bh = NULL; if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1) return -1; PRINTK (("get_entry sector %d %p\n",sector,*bh)); + PRINTK (("get_entry sector apres brelse\n")); if (!sector) return -1; /* beyond EOF */ *pos += sizeof(struct msdos_dir_entry); - if (*bh) - fat_brelse(sb, *bh); - PRINTK (("get_entry sector apres brelse\n")); if (!(*bh = fat_bread(sb, sector))) { printk("Directory sread (sector 0x%x) failed\n",sector); continue; @@ -346,8 +365,9 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, PRINTK (("get_entry apres sread\n")); *de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset & (SECTOR_SIZE-1))); - return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >> + *ino = (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >> MSDOS_DIR_BITS); + return 0; } } @@ -393,14 +413,6 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, #define RSS_FREE /* search for free entry */ \ { \ done = IS_FREE(data[entry].name); \ - if (done) { \ - inode = iget(sb,sector*MSDOS_DPS+entry); \ - if (inode) { \ - /* Directory slots of busy deleted files aren't available yet. */ \ - done = !MSDOS_I(inode)->i_busy; \ - iput(inode); \ - } \ - } \ } #define RSS_COUNT /* count subdirectories */ \ @@ -412,11 +424,10 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, static int raw_scan_sector(struct super_block *sb,int sector,const char *name, int *number,int *ino,struct buffer_head **res_bh, - struct msdos_dir_entry **res_de,char scantype) + struct msdos_dir_entry **res_de) { struct buffer_head *bh; struct msdos_dir_entry *data; - struct inode *inode; int entry,start,done; if (!(bh = fat_bread(sb,sector))) @@ -426,11 +437,6 @@ static int raw_scan_sector(struct super_block *sb,int sector,const char *name, /* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */ if (name) { RSS_NAME - if (done && scantype) { /* scantype != SCAN_ANY */ - done = (data[entry].attr & ATTR_HIDDEN) - ? (scantype==SCAN_HID) - : (scantype==SCAN_NOTHID); - } } else { if (!ino) RSS_COUNT else { @@ -464,13 +470,13 @@ static int raw_scan_sector(struct super_block *sb,int sector,const char *name, */ static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino, - struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype) + struct buffer_head **res_bh,struct msdos_dir_entry **res_de) { int count,cluster; for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) { if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count, - name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster; + name,number,ino,res_bh,res_de)) >= 0) return cluster; } return -ENOENT; } @@ -483,7 +489,7 @@ static int raw_scan_root(struct super_block *sb,const char *name,int *number,int static int raw_scan_nonroot(struct super_block *sb,int start,const char *name, int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry - **res_de,char scantype) + **res_de) { int count,cluster; @@ -494,7 +500,7 @@ static int raw_scan_nonroot(struct super_block *sb,int start,const char *name, for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) { if ((cluster = raw_scan_sector(sb,(start-2)* MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+ - count,name,number,ino,res_bh,res_de,scantype)) >= 0) + count,name,number,ino,res_bh,res_de)) >= 0) return cluster; } if (!(start = fat_access(sb,start,-1))) { @@ -519,12 +525,12 @@ static int raw_scan_nonroot(struct super_block *sb,int start,const char *name, static int raw_scan(struct super_block *sb, int start, const char *name, int *number, int *ino, struct buffer_head **res_bh, - struct msdos_dir_entry **res_de, char scantype) + struct msdos_dir_entry **res_de) { if (start) return raw_scan_nonroot - (sb,start,name,number,ino,res_bh,res_de,scantype); + (sb,start,name,number,ino,res_bh,res_de); else return raw_scan_root - (sb,name,number,ino,res_bh,res_de,scantype); + (sb,name,number,ino,res_bh,res_de); } @@ -532,6 +538,11 @@ static int raw_scan(struct super_block *sb, int start, const char *name, * fat_parent_ino returns the inode number of the parent directory of dir. * File creation has to be deferred while fat_parent_ino is running to * prevent renames. + * + * AV. Bad, bad, bad... We need a mapping that would give us inode by + * first cluster. Sheeeeit... OK, we can do it on fat_fill_inode() and + * update on fat_add_cluster(). When will we remove it? fat_clear_inode() + * and fat_truncate() to zero? */ int fat_parent_ino(struct inode *dir,int locked) @@ -544,7 +555,7 @@ int fat_parent_ino(struct inode *dir,int locked) if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino; if (!locked) fat_lock_creation(); /* prevent renames */ if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT, - &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) { + &zero,NULL,NULL,NULL)) < 0) { if (!locked) fat_unlock_creation(); return curr; } @@ -553,7 +564,7 @@ int fat_parent_ino(struct inode *dir,int locked) else { PRINTK(("fat_parent_ino: Debug 2\n")); if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL, - NULL,NULL,SCAN_ANY)) < 0) { + NULL,NULL)) < 0) { PRINTK(("fat_parent_ino: Debug 3 prev=%d\n", prev)); if (!locked) fat_unlock_creation(); return prev; @@ -563,7 +574,7 @@ int fat_parent_ino(struct inode *dir,int locked) prev = MSDOS_SB(dir->i_sb)->root_cluster; } if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL, - NULL,SCAN_ANY)) < 0) { + NULL)) < 0) { PRINTK(("fat_parent_ino: Debug 5 error=%d\n", error)); if (!locked) fat_unlock_creation(); return error; @@ -587,12 +598,12 @@ int fat_subdirs(struct inode *dir) count = 0; if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(dir->i_sb)->fat_bits != 32)) { - (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY); + (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL); } else { if ((dir->i_ino != MSDOS_ROOT_INO) && !MSDOS_I(dir)->i_start) return 0; /* in mkdir */ else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start, - NULL,&count,NULL,NULL,NULL,SCAN_ANY); + NULL,&count,NULL,NULL,NULL); } return count; } @@ -604,11 +615,11 @@ int fat_subdirs(struct inode *dir) */ int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh, - struct msdos_dir_entry **res_de,int *ino, char scantype) + struct msdos_dir_entry **res_de,int *ino) { int res; res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start, - name, NULL, ino, res_bh, res_de, scantype); + name, NULL, ino, res_bh, res_de); return res<0 ? res : 0; } |