patch-2.1.63 linux/fs/fat/inode.c
Next file: linux/fs/fat/mmap.c
Previous file: linux/fs/fat/dir.c
Back to the patch index
Back to the overall index
- Lines: 493
- Date:
Thu Nov 6 17:42:44 1997
- Orig file:
v2.1.62/linux/fs/fat/inode.c
- Orig date:
Sat Oct 25 02:44:17 1997
diff -u --recursive --new-file v2.1.62/linux/fs/fat/inode.c linux/fs/fat/inode.c
@@ -5,7 +5,6 @@
* VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
*/
-#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
#include <linux/version.h>
#define __NO_VERSION__
#include <linux/module.h>
@@ -25,79 +24,111 @@
#include "msbuffer.h"
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
#include <asm/uaccess.h>
-#define FAT_COPY_TO_USER(uaddr, kaddr, len) copy_to_user(uaddr, kaddr, len)
-#else
-#include <asm/segment.h>
-#define FAT_COPY_TO_USER(uaddr, kaddr, len) memcpy_tofs(uaddr, kaddr, len)
-#endif
#include <asm/unaligned.h>
-#if 0
+/* #define FAT_PARANOIA 1 */
+#ifdef FAT_DEBUG
# define PRINTK(x) printk x
#else
# define PRINTK(x)
#endif
-void fat_put_inode(struct inode *inode)
-{
- struct inode *depend, *linked;
- struct super_block *sb;
-
- depend = MSDOS_I(inode)->i_depend;
- linked = MSDOS_I(inode)->i_linked;
- sb = inode->i_sb;
- if (inode->i_nlink) {
- if (depend) {
- iput(depend);
- }
- if (linked) {
- iput(linked);
- MSDOS_I(inode)->i_linked = NULL;
- }
- if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
- }
-}
-
-void fat_delete_inode(struct inode *inode)
+/*
+ * Free any dependent inodes at the (effective) last use.
+ */
+static int fat_free_links(struct inode *inode)
{
- struct inode *depend, *linked;
- struct super_block *sb;
+ struct inode *depend, *linked, *old_inode;
+ int success = 0;
+ /*
+ * Clear the fields first to avoid races
+ */
depend = MSDOS_I(inode)->i_depend;
+ MSDOS_I(inode)->i_depend = NULL;
linked = MSDOS_I(inode)->i_linked;
- sb = inode->i_sb;
+ MSDOS_I(inode)->i_linked = NULL;
- inode->i_size = 0;
- fat_truncate(inode);
if (depend) {
- if (MSDOS_I(depend)->i_old != inode) {
- printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
- depend, inode, MSDOS_I(depend)->i_old);
- fat_fs_panic(sb,"...");
- goto done;
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: depend inode is %ld, i_count=%d\n",
+depend->i_ino, depend->i_count);
+#endif
+ old_inode = MSDOS_I(depend)->i_old;
+ if (old_inode != inode) {
+ printk("fat_free_link: Invalid depend for inode %ld: "
+ "expected 0x%p, got 0x%p\n",
+ depend->i_ino, inode, old_inode);
+ goto out;
}
MSDOS_I(depend)->i_old = NULL;
iput(depend);
}
+
if (linked) {
- if (MSDOS_I(linked)->i_oldlink != inode) {
- printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
- linked, inode, MSDOS_I(linked)->i_oldlink);
- fat_fs_panic(sb,"...");
- goto done;
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: linked inode is %ld, i_count=%d\n",
+linked->i_ino, linked->i_count);
+#endif
+ old_inode = MSDOS_I(linked)->i_oldlink;
+ if (old_inode != inode) {
+ printk("fat_free_link: Invalid link for inode %ld: "
+ "expected 0x%p, got 0x%p\n",
+ linked->i_ino, inode, old_inode);
+ goto out;
}
MSDOS_I(linked)->i_oldlink = NULL;
iput(linked);
}
-done:
+ success = 1;
+out:
+ return success;
+}
+
+/*
+ * This is a little tricky, as we may have links and may be linked
+ * by other inodes. Also, we're subject to race conditions ...
+ */
+void fat_put_inode(struct inode *inode)
+{
+ int last_use = 1;
+
+ /*
+ * Check whether we're a dependent of other inodes ...
+ */
+ if (MSDOS_I(inode)->i_oldlink)
+ last_use++;
+ if (MSDOS_I(inode)->i_old)
+ last_use++;
+
+ if (inode->i_count <= last_use) {
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: last use for %ld, i_count=%d\n",
+inode->i_ino, inode->i_count);
+#endif
+ if (inode->i_nlink) {
+ if (MSDOS_I(inode)->i_busy)
+ fat_cache_inval_inode(inode);
+ fat_free_links(inode);
+ }
+ }
+}
+
+void fat_delete_inode(struct inode *inode)
+{
+ fat_cache_inval_inode(inode);
+ inode->i_size = 0;
+ fat_truncate(inode);
+ if (!fat_free_links(inode))
+ fat_fs_panic(inode->i_sb,"..."); /* is this necessary? */
clear_inode(inode);
}
void fat_put_super(struct super_block *sb)
{
+ lock_super(sb);
if (MSDOS_SB(sb)->fat_bits == 32) {
fat_clusters_flush(sb);
}
@@ -111,12 +142,15 @@
if (MSDOS_SB(sb)->nls_io) {
unload_nls(MSDOS_SB(sb)->nls_io);
MSDOS_SB(sb)->nls_io = NULL;
- if (MSDOS_SB(sb)->options.iocharset) {
- kfree(MSDOS_SB(sb)->options.iocharset);
- MSDOS_SB(sb)->options.iocharset = NULL;
- }
}
- lock_super(sb);
+ /*
+ * Note: the iocharset option might have been specified
+ * without enabling nls_io, so check for it here.
+ */
+ if (MSDOS_SB(sb)->options.iocharset) {
+ kfree(MSDOS_SB(sb)->options.iocharset);
+ MSDOS_SB(sb)->options.iocharset = NULL;
+ }
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
@@ -129,7 +163,7 @@
{
char *this_char,*value,save,*savep;
char *p;
- int ret, len;
+ int ret = 1, len;
opts->name_check = 'n';
opts->conversion = 'b';
@@ -142,11 +176,12 @@
opts->iocharset = NULL;
*debug = *fat = 0;
- if (!options) return 1;
+ if (!options)
+ goto out;
save = 0;
savep = NULL;
- ret = 1;
- for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ for (this_char = strtok(options,","); this_char;
+ this_char = strtok(NULL,",")) {
if ((value = strchr(this_char,'=')) != NULL) {
save = *value;
savep = value;
@@ -155,17 +190,23 @@
if (!strcmp(this_char,"check") && value) {
if (value[0] && !value[1] && strchr("rns",*value))
opts->name_check = *value;
- else if (!strcmp(value,"relaxed")) opts->name_check = 'r';
- else if (!strcmp(value,"normal")) opts->name_check = 'n';
- else if (!strcmp(value,"strict")) opts->name_check = 's';
+ else if (!strcmp(value,"relaxed"))
+ opts->name_check = 'r';
+ else if (!strcmp(value,"normal"))
+ opts->name_check = 'n';
+ else if (!strcmp(value,"strict"))
+ opts->name_check = 's';
else ret = 0;
}
else if (!strcmp(this_char,"conv") && value) {
if (value[0] && !value[1] && strchr("bta",*value))
opts->conversion = *value;
- else if (!strcmp(value,"binary")) opts->conversion = 'b';
- else if (!strcmp(value,"text")) opts->conversion = 't';
- else if (!strcmp(value,"auto")) opts->conversion = 'a';
+ else if (!strcmp(value,"binary"))
+ opts->conversion = 'b';
+ else if (!strcmp(value,"text"))
+ opts->conversion = 't';
+ else if (!strcmp(value,"auto"))
+ opts->conversion = 'a';
else ret = 0;
}
else if (!strcmp(this_char,"dots")) {
@@ -222,8 +263,11 @@
}
else if (!strcmp(this_char,"blocksize")) {
if (*value) ret = 0;
- else if (*blksize != 512 && *blksize != 1024 && *blksize != 2048){
- printk ("MSDOS FS: Invalid blocksize (512, 1024, or 2048)\n");
+ else if (*blksize != 512 &&
+ *blksize != 1024 &&
+ *blksize != 2048) {
+ printk ("MSDOS FS: Invalid blocksize "
+ "(512, 1024, or 2048)\n");
}
}
else if (!strcmp(this_char,"sys_immutable")) {
@@ -233,46 +277,54 @@
else if (!strcmp(this_char,"codepage")) {
opts->codepage = simple_strtoul(value,&value,0);
if (*value) ret = 0;
- else printk ("MSDOS FS: Using codepage %d\n", opts->codepage);
+ else printk ("MSDOS FS: Using codepage %d\n",
+ opts->codepage);
}
else if (!strcmp(this_char,"iocharset")) {
p = value;
while (*value && *value != ',') value++;
len = value - p;
- if (len) {
- opts->iocharset = kmalloc(len+1, GFP_KERNEL);
- memcpy(opts->iocharset, p, len);
- opts->iocharset[len] = 0;
- printk ("MSDOS FS: Using IO charset %s\n",
- opts->iocharset);
- } else {
- opts->iocharset = NULL;
- ret = 0;
+ if (len) {
+ char * buffer = kmalloc(len+1, GFP_KERNEL);
+ if (buffer) {
+ opts->iocharset = buffer;
+ memcpy(buffer, p, len);
+ buffer[len] = 0;
+ printk("MSDOS FS: IO charset %s\n",
+ buffer);
+ } else
+ ret = 0;
}
}
if (this_char != options) *(this_char-1) = ',';
if (value) *savep = save;
- if (ret == 0) return 0;
+ if (ret == 0)
+ break;
}
- return 1;
+out:
+ return ret;
}
-
-/* Read the super block of an MS-DOS FS. */
-
-struct super_block *fat_read_super(struct super_block *sb,void *data, int silent)
+/*
+ * Read the super block of an MS-DOS FS.
+ *
+ * Note that this may be called from vfat_read_super
+ * with some fields already initialized.
+ */
+struct super_block *
+fat_read_super(struct super_block *sb, void *data, int silent)
{
+ struct inode *root_inode;
struct buffer_head *bh;
struct fat_boot_sector *b;
+ char *p;
int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
int debug,error,fat,cp;
int blksize = 512;
int fat32;
struct fat_mount_options opts;
char buf[50];
- char *p;
- struct inode *root_inode;
MOD_INC_USE_COUNT;
if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
@@ -281,14 +333,14 @@
printk ("MSDOS: Hardware sector size is %d\n",blksize);
}
}
+
opts.isvfat = MSDOS_SB(sb)->options.isvfat;
if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
- || (blksize != 512 && blksize != 1024 && blksize != 2048))
- {
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ || (blksize != 512 && blksize != 1024 && blksize != 2048))
+ goto out_fail;
+ /* N.B. we should parse directly into the sb structure */
+ memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
+
cache_init();
lock_super(sb);
if( blksize > 1024 )
@@ -307,10 +359,7 @@
unlock_super(sb);
if (bh == NULL || !fat_is_uptodate(sb,bh)) {
fat_brelse (sb, bh);
- sb->s_dev = 0;
- printk("FAT bread failed\n");
- MOD_DEC_USE_COUNT;
- return NULL;
+ goto out_no_bread;
}
set_blocksize(sb->s_dev, blksize);
@@ -351,7 +400,9 @@
fsinfo = (struct fat_boot_fsinfo *)
&bh->b_data[MSDOS_SB(sb)->fsinfo_offset];
if (CF_LE_L(fsinfo->signature) != 0x61417272) {
- printk("fat_read_super: Did not find valid FSINFO signature. Found 0x%x\n", CF_LE_L(fsinfo->signature));
+ printk("fat_read_super: Did not find valid FSINFO "
+ "signature. Found 0x%x\n",
+ CF_LE_L(fsinfo->signature));
} else {
MSDOS_SB(sb)->free_clusters = CF_LE_L(fsinfo->free_clusters);
}
@@ -405,8 +456,10 @@
opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
MSDOS_CAN_BMAP(MSDOS_SB(sb)) ? ",bmap" : "");
printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
- "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\n",b->media,MSDOS_SB(sb)->cluster_size,
- MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length,
+ "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\n",
+ b->media,MSDOS_SB(sb)->cluster_size,
+ MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,
+ MSDOS_SB(sb)->fat_length,
MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
MSDOS_SB(sb)->data_start,
CF_LE_W(*(unsigned short *) &b->sectors),
@@ -416,67 +469,70 @@
}
if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
MSDOS_SB(sb)->clusters = fat_clusters-2;
- if (error) {
- if (!silent)
- printk("VFS: Can't find a valid MSDOS filesystem on dev "
- "%s.\n", kdevname(sb->s_dev));
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (error)
+ goto out_invalid;
+
sb->s_magic = MSDOS_SUPER_MAGIC;
/* set up enough so that it can read an inode */
MSDOS_SB(sb)->fat_wait = NULL;
MSDOS_SB(sb)->fat_lock = 0;
MSDOS_SB(sb)->prev_free = 0;
- memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
cp = opts.codepage ? opts.codepage : 437;
sprintf(buf, "cp%d", cp);
MSDOS_SB(sb)->nls_disk = load_nls(buf);
if (! MSDOS_SB(sb)->nls_disk) {
/* Fail only if explicit charset specified */
- if (opts.codepage == 0) {
- MSDOS_SB(sb)->options.codepage = 0;
- MSDOS_SB(sb)->nls_disk = load_nls_default();
- } else {
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (opts.codepage != 0)
+ goto out_fail;
+ MSDOS_SB(sb)->options.codepage = 0; /* already 0?? */
+ MSDOS_SB(sb)->nls_disk = load_nls_default();
}
- p = opts.iocharset ? opts.iocharset : "iso8859-1";
+ MSDOS_SB(sb)->nls_io = NULL;
if (MSDOS_SB(sb)->options.isvfat && !opts.utf8) {
+ p = opts.iocharset ? opts.iocharset : "iso8859-1";
MSDOS_SB(sb)->nls_io = load_nls(p);
if (! MSDOS_SB(sb)->nls_io) {
/* Fail only if explicit charset specified */
- if (opts.iocharset) {
- kfree(opts.iocharset);
- unload_nls(MSDOS_SB(sb)->nls_disk);
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- } else {
- MSDOS_SB(sb)->nls_io = load_nls_default();
- }
+ if (opts.iocharset)
+ goto out_unload_nls;
+ MSDOS_SB(sb)->nls_io = load_nls_default();
}
}
- root_inode = iget(sb,MSDOS_ROOT_INO);
+ root_inode = iget(sb, MSDOS_ROOT_INO);
+ if (!root_inode)
+ goto out_no_root;
sb->s_root = d_alloc_root(root_inode, NULL);
- if (!sb->s_root) {
- sb->s_dev = 0;
- printk("get root inode failed\n");
- unload_nls(MSDOS_SB(sb)->nls_disk);
- if (MSDOS_SB(sb)->nls_io) unload_nls(MSDOS_SB(sb)->nls_io);
- if (opts.iocharset) kfree(opts.iocharset);
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (!sb->s_root)
+ goto out_no_root;
return sb;
-}
+out_no_root:
+ printk("get root inode failed\n");
+ iput(root_inode);
+ if (MSDOS_SB(sb)->nls_io)
+ unload_nls(MSDOS_SB(sb)->nls_io);
+out_unload_nls:
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ goto out_fail;
+out_invalid:
+ if (!silent)
+ printk("VFS: Can't find a valid MSDOS filesystem on dev %s.\n",
+ kdevname(sb->s_dev));
+ goto out_fail;
+out_no_bread:
+ printk("FAT bread failed\n");
+out_fail:
+ if (opts.iocharset) {
+ printk("VFS: freeing iocharset=%s\n", opts.iocharset);
+ kfree(opts.iocharset);
+ }
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
{
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov