summaryrefslogtreecommitdiffstats
path: root/fs/fat
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
commit59223edaa18759982db0a8aced0e77457d10c68e (patch)
tree89354903b01fa0a447bffeefe00df3044495db2e /fs/fat
parentdb7d4daea91e105e3859cf461d7e53b9b77454b2 (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.c37
-rw-r--r--fs/fat/dir.c571
-rw-r--r--fs/fat/fatfs_syms.c15
-rw-r--r--fs/fat/inode.c307
-rw-r--r--fs/fat/misc.c101
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;
}