summaryrefslogtreecommitdiffstats
path: root/fs/affs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/affs')
-rw-r--r--fs/affs/.cvsignore1
-rw-r--r--fs/affs/Changes63
-rw-r--r--fs/affs/bitmap.c1
-rw-r--r--fs/affs/file.c249
-rw-r--r--fs/affs/inode.c39
-rw-r--r--fs/affs/namei.c292
-rw-r--r--fs/affs/super.c484
-rw-r--r--fs/affs/symlink.c10
8 files changed, 649 insertions, 490 deletions
diff --git a/fs/affs/.cvsignore b/fs/affs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/affs/.cvsignore
+++ b/fs/affs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/affs/Changes b/fs/affs/Changes
index 06736aa3a..851b0514a 100644
--- a/fs/affs/Changes
+++ b/fs/affs/Changes
@@ -21,11 +21,70 @@ Known bugs:
might leave a trashed file system with the
bitmap flag set valid.
-- The blocks from deleted directories are
- sometimes reclaimed only at umount time.
+- When a file is truncated to a size that is not
+ a multiple of the blocksize, the rest of the
+ last allocated block is not cleared. Well,
+ this fs never claimed to be Posix conformant.
Please direct bug reports to: hjw@zvw.de
+Version 3.8
+-----------
+Bill Hawes kindly reviewed the affs and sent me the
+patches he did. They're marked (BH). Thanks, Bill!
+
+- Cleanup of error handling in read_super().
+ Didn't release all ressources in case of an
+ error. (BH)
+
+- put_inode() releases the ext cache only if it's
+ no longer needed. (BH)
+
+- One set of dentry callbacks is enough. (BH)
+
+- Cleanup of error handling in namei.c. (BH)
+
+- Cleanup of error handling in file.c. (BH)
+
+- The original blocksize of the device is
+ restored when the fs is unmounted. (BH)
+
+- getblock() did not invalidate the key cache
+ when it allocated a new block.
+
+- Removed some unneccessary locks as Bill
+ suggested.
+
+- Simplified match_name(), changed all hashing
+ and case insensitive name comparisons to use
+ uppercase. This makes the tolower() routines
+ obsolete.
+
+- Added mount option 'mufs' to force muFS
+ uid/gid interpretation.
+
+- File mode changes were not updated on disk.
+ This was fixed before, but somehow got lost.
+
+Version 3.7
+-----------
+
+- Added dentry callbacks to allow the dcache to
+ operate case insensitive and length ignorant
+ like the affs itself.
+
+- getblock() didn't update the lastblock field in the
+ inode if the fs was not an OFS. This bug only shows
+ up if a file was enlarged via truncate() and there
+ was not enough space.
+
+- Remove some more superfluous code left over from
+ the old link days ...
+
+- Fixed some oversights which were in patch 2.1.78.
+
+- Fixed a few typos.
+
Version 3.6
-----------
diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c
index 832214e79..26098dd19 100644
--- a/fs/affs/bitmap.c
+++ b/fs/affs/bitmap.c
@@ -7,6 +7,7 @@
* block allocation, deallocation, calculation of free space.
*/
+#define DEBUG 0
#include <linux/sched.h>
#include <linux/affs_fs.h>
#include <linux/stat.h>
diff --git a/fs/affs/file.c b/fs/affs/file.c
index df130db03..d0d4bd7bc 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -12,6 +12,7 @@
* affs regular file handling primitives
*/
+#define DEBUG 0
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/sched.h>
@@ -308,7 +309,7 @@ affs_bmap(struct inode *inode, int block)
for (;;) {
bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
- if (!bh)
+ if (!bh)
return 0;
index = seqnum_to_index(ext);
if (index > inode->u.affs_i.i_ec->max_ext &&
@@ -359,57 +360,57 @@ affs_bmap(struct inode *inode, int block)
static struct buffer_head *
affs_getblock(struct inode *inode, s32 block)
{
- struct buffer_head *bh;
- struct buffer_head *ebh;
- struct buffer_head *pbh;
+ struct super_block *sb = inode->i_sb;
+ int ofs = sb->u.affs_sb.s_flags & SF_OFS;
+ int ext = block / AFFS_I2HSIZE(inode);
+ struct buffer_head *bh, *ebh, *pbh = NULL;
struct key_cache *kc;
s32 key, nkey;
- int ext;
int cf, j, pt;
int index;
- int ofs;
+ int err;
pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
if (block < 0)
- return NULL;
+ goto out_fail;
- /* Writers always use cache line 3. In almost all cases, files
- * will be written by only one process at the same time, and
- * they also will be written in strict sequential order. Thus
- * there is not much sense in looking whether the key of the
- * requested block is available - it won't be there.
- */
- kc = &inode->u.affs_i.i_ec->kc[3];
- ofs = inode->i_sb->u.affs_sb.s_flags & SF_OFS;
- ext = block / AFFS_I2HSIZE(inode);
key = calc_key(inode,&ext);
block -= ext * AFFS_I2HSIZE(inode);
pt = ext ? T_LIST : T_SHORT;
- pbh = NULL;
+ /* Key refers now to the last known extension block,
+ * ext is its sequence number (if 0, key refers to the
+ * header block), and block is the block number relative
+ * to the first block stored in that extension block.
+ */
for (;;) { /* Loop over header block and extension blocks */
+ struct file_front *fdp;
+
bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
if (!bh)
- return NULL;
- if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j) ||
- cf != pt || j != ST_FILE) {
- affs_error(inode->i_sb,"getblock","Inode %d is not a valid %s",key,
- pt == T_SHORT ? "file header" : "extension block");
- affs_brelse(bh);
- return NULL;
+ goto out_fail;
+ fdp = (struct file_front *) bh->b_data;
+ err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j);
+ if (err || cf != pt || j != ST_FILE) {
+ affs_error(sb, "getblock",
+ "Block %d is not a valid %s", key,
+ pt == T_SHORT ? "file header" : "ext block");
+ goto out_free_bh;
}
j = be32_to_cpu(((struct file_front *)bh->b_data)->block_count);
- cf = 0;
- while (j < AFFS_I2HSIZE(inode) && j <= block) {
+ for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) {
if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
- if (j > 0)
- pbh = affs_bread(inode->i_dev,cpu_to_be32(AFFS_BLOCK(bh->b_data,inode,j - 1)),
- AFFS_I2BSIZE(inode));
- else
+ if (j > 0) {
+ s32 k = AFFS_BLOCK(bh->b_data, inode,
+ j - 1);
+ pbh = affs_bread(inode->i_dev,
+ be32_to_cpu(k),
+ AFFS_I2BSIZE(inode));
+ } else
pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
if (!pbh) {
- affs_error(inode->i_sb,"getblock",
+ affs_error(sb,"getblock",
"Cannot get last block in file");
break;
}
@@ -417,47 +418,45 @@ affs_getblock(struct inode *inode, s32 block)
nkey = affs_new_data(inode);
if (!nkey)
break;
- lock_super(inode->i_sb);
+ inode->u.affs_i.i_lastblock++;
if (AFFS_BLOCK(bh->b_data,inode,j)) {
- unlock_super(inode->i_sb);
- affs_warning(inode->i_sb,"getblock","Block already allocated");
- affs_free_block(inode->i_sb,nkey);
- j++;
+ affs_warning(sb,"getblock","Block already allocated");
+ affs_free_block(sb,nkey);
continue;
}
- unlock_super(inode->i_sb);
AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey);
if (ofs) {
ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
if (!ebh) {
- affs_error(inode->i_sb,"getblock",
+ affs_error(sb,"getblock",
"Cannot get block %d",nkey);
- affs_free_block(inode->i_sb,nkey);
+ affs_free_block(sb,nkey);
AFFS_BLOCK(bh->b_data,inode,j) = 0;
break;
}
- inode->u.affs_i.i_lastblock++;
DATA_FRONT(ebh)->primary_type = cpu_to_be32(T_DATA);
DATA_FRONT(ebh)->header_key = cpu_to_be32(inode->i_ino);
DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),
+ ebh->b_data, 5);
+ mark_buffer_dirty(ebh, 0);
if (pbh) {
DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24);
DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey);
affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
mark_buffer_dirty(pbh,0);
- mark_buffer_dirty(ebh,0);
affs_brelse(pbh);
}
pbh = ebh;
}
- j++;
cf = 1;
}
+ /* N.B. May need to release pbh after here */
+
if (cf) {
if (pt == T_SHORT)
- ((struct file_front *)bh->b_data)->first_data =
- AFFS_BLOCK(bh->b_data,inode,0);
- ((struct file_front *)bh->b_data)->block_count = cpu_to_be32(j);
+ fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0);
+ fdp->block_count = cpu_to_be32(j);
affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
mark_buffer_dirty(bh,1);
}
@@ -468,52 +467,63 @@ affs_getblock(struct inode *inode, s32 block)
break;
}
if (j < AFFS_I2HSIZE(inode)) {
- affs_brelse(bh);
- return NULL;
+ /* N.B. What about pbh here? */
+ goto out_free_bh;
}
block -= AFFS_I2HSIZE(inode);
key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
if (!key) {
key = affs_new_header(inode);
- if (!key) {
- affs_brelse(bh);
- return NULL;
- }
+ if (!key)
+ goto out_free_bh;
ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
if (!ebh) {
- affs_free_block(inode->i_sb,key);
- return NULL;
+ /* N.B. must free bh here */
+ goto out_free_block;
}
((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST);
((struct file_front *)ebh->b_data)->own_key = cpu_to_be32(key);
FILE_END(ebh->b_data,inode)->secondary_type = cpu_to_be32(ST_FILE);
FILE_END(ebh->b_data,inode)->parent = cpu_to_be32(inode->i_ino);
affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+ mark_buffer_dirty(ebh, 1);
FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key);
affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
mark_buffer_dirty(bh,1);
affs_brelse(bh);
bh = ebh;
}
- affs_brelse(bh);
pt = T_LIST;
ext++;
- if ((index = seqnum_to_index(ext)) > inode->u.affs_i.i_ec->max_ext &&
- AFFS_ISINDEX(ext) && inode->u.affs_i.i_ec) {
+ index = seqnum_to_index(ext);
+ if (index > inode->u.affs_i.i_ec->max_ext &&
+ AFFS_ISINDEX(ext)) {
inode->u.affs_i.i_ec->ec[index] = key;
inode->u.affs_i.i_ec->max_ext = index;
}
+ affs_brelse(bh);
+ }
+
+ /* Invalidate key cache */
+ for (j = 0; j < 4; j++) {
+ kc = &inode->u.affs_i.i_ec->kc[j];
+ kc->kc_last = -1;
}
- kc->kc_this_key = key;
- kc->kc_this_seq = ext;
- kc->kc_next_key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
affs_brelse(bh);
if (!key)
- return NULL;
-
- return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+ goto out_fail;
+
+ bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode));
+ return bh;
+
+out_free_block:
+ affs_free_block(sb, key);
+out_free_bh:
+ affs_brelse(bh);
+out_fail:
+ return NULL;
}
static ssize_t
@@ -591,14 +601,11 @@ affs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
inode->i_mode);
return -EINVAL;
}
- if (!inode->u.affs_i.i_ec) {
- if (alloc_ext_cache(inode)) {
- return -ENOMEM;
- }
- }
- if (filp->f_flags & O_APPEND) {
+ if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode))
+ return -ENOMEM;
+ if (filp->f_flags & O_APPEND)
pos = inode->i_size;
- } else
+ else
pos = *ppos;
written = 0;
blocksize = AFFS_I2BSIZE(inode);
@@ -733,6 +740,23 @@ affs_file_write_ofs(struct file *filp, const char *buf, size_t count, loff_t *pp
return written;
}
+/* Free any preallocated blocks */
+void
+affs_free_prealloc(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ int block;
+
+ pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
+
+ while (inode->u.affs_i.i_pa_cnt) {
+ block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
+ inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
+ inode->u.affs_i.i_pa_cnt--;
+ affs_free_block(sb, block);
+ }
+}
+
void
affs_truncate(struct inode *inode)
{
@@ -750,7 +774,7 @@ affs_truncate(struct inode *inode)
int rem;
int ext;
- pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+ pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
first = (inode->i_size + blocksize - 1) / blocksize;
@@ -763,12 +787,7 @@ affs_truncate(struct inode *inode)
}
bh = affs_getblock(inode,first - 1);
- while (inode->u.affs_i.i_pa_cnt) { /* Free any preallocated blocks */
- affs_free_block(inode->i_sb,
- inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
- inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
- inode->u.affs_i.i_pa_cnt--;
- }
+ affs_free_prealloc(inode);
if (inode->u.affs_i.i_zone) {
lock_super(inode->i_sb);
zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
@@ -777,7 +796,7 @@ affs_truncate(struct inode *inode)
unlock_super(inode->i_sb);
}
if (!bh) {
- affs_error(inode->i_sb,"truncate","Cannot extend file");
+ affs_warning(inode->i_sb,"truncate","Cannot extend file");
inode->i_size = blocksize * (inode->u.affs_i.i_lastblock + 1);
} else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
rem = inode->i_size % blocksize;
@@ -798,12 +817,6 @@ affs_truncate(struct inode *inode)
}
ptype = be32_to_cpu(((struct file_front *)bh->b_data)->primary_type);
stype = be32_to_cpu(FILE_END(bh->b_data,inode)->secondary_type);
- if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
- LINK_END(bh->b_data,inode)->original == 0) {
- pr_debug("AFFS: truncate(): dumping link\n");
- affs_brelse(bh);
- break;
- }
if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
affs_error(inode->i_sb,"truncate","Bad block (ptype=%d, stype=%d)",
ptype,stype);
@@ -873,56 +886,66 @@ affs_truncate(struct inode *inode)
static int
affs_release_file(struct inode *inode, struct file *filp)
{
+ struct super_block *sb = inode->i_sb;
struct affs_zone *zone;
pr_debug("AFFS: release_file(ino=%lu)\n",inode->i_ino);
if (filp->f_mode & 2) { /* Free preallocated blocks */
- while (inode->u.affs_i.i_pa_cnt) {
- affs_free_block(inode->i_sb,
- inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
- inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
- inode->u.affs_i.i_pa_cnt--;
- }
+ affs_free_prealloc(inode);
if (inode->u.affs_i.i_zone) {
- lock_super(inode->i_sb);
- zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+ zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
if (zone->z_ino == inode->i_ino)
zone->z_ino = 0;
- unlock_super(inode->i_sb);
}
}
return 0;
}
+/*
+ * Called only when we need to allocate the extension cache.
+ */
static int
alloc_ext_cache(struct inode *inode)
{
s32 key;
int i;
+ unsigned long cache_page;
+ int error = 0;
pr_debug("AFFS: alloc_ext_cache(ino=%lu)\n",inode->i_ino);
- lock_super(inode->i_sb);
- if (!inode->u.affs_i.i_ec) {
- inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
- if (inode->u.affs_i.i_ec) {
- /* We only have to initialize non-zero values.
- * get_free_page() zeroed the page already.
- */
- key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
- inode->u.affs_i.i_ec->ec[0] = key;
- for (i = 0; i < 4; i++) {
- inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
- inode->u.affs_i.i_ec->kc[i].kc_last = -1;
- }
- }
- }
- unlock_super(inode->i_sb);
-
- if (!inode->u.affs_i.i_ec) {
- affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
- return -ENOMEM;
+ cache_page = get_free_page(GFP_KERNEL);
+ /*
+ * Check whether somebody else allocated it for us ...
+ */
+ if (inode->u.affs_i.i_ec)
+ goto out_free;
+ if (!cache_page)
+ goto out_error;
+
+ inode->u.affs_i.i_ec = (struct ext_cache *) cache_page;
+ /* We only have to initialize non-zero values.
+ * get_free_page() zeroed the page already.
+ */
+ key = inode->u.affs_i.i_original;
+ if (!inode->u.affs_i.i_original)
+ key = inode->i_ino;
+ inode->u.affs_i.i_ec->ec[0] = key;
+ for (i = 0; i < 4; i++) {
+ inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
+ inode->u.affs_i.i_ec->kc[i].kc_last = -1;
}
- return 0;
+out:
+ return error;
+
+out_free:
+ if (cache_page)
+ free_page(cache_page);
+ goto out;
+
+out_error:
+ affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
+ error = -ENOMEM;
+ goto out;
}
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 676e43470..9dc72d6e0 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -220,44 +220,49 @@ affs_write_inode(struct inode *inode)
}
int
-affs_notify_change(struct inode *inode, struct iattr *attr)
+affs_notify_change(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = dentry->d_inode;
int error;
pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid);
error = inode_change_ok(inode,attr);
if (error)
- return error;
+ goto out;
if (((attr->ia_valid & ATTR_UID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) ||
((attr->ia_valid & ATTR_GID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) ||
((attr->ia_valid & ATTR_MODE) &&
- (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE))))
- error = -EPERM;
-
- if (error)
- return (inode->i_sb->u.affs_sb.s_flags & SF_QUIET) ? 0 : error;
+ (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE)))) {
+ if (!(inode->i_sb->u.affs_sb.s_flags & SF_QUIET))
+ error = -EPERM;
+ goto out;
+ }
if (attr->ia_valid & ATTR_MODE)
inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
- inode_setattr(inode,attr);
-
- return 0;
+ inode_setattr(inode, attr);
+ mark_inode_dirty(inode);
+ error = 0;
+out:
+ return error;
}
void
affs_put_inode(struct inode *inode)
{
- pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
- lock_super(inode->i_sb);
- if (inode->u.affs_i.i_ec) {
- pr_debug("AFFS: freeing ext cache\n");
- free_page((unsigned long)inode->u.affs_i.i_ec);
- inode->u.affs_i.i_ec = NULL;
+ pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",
+ inode->i_ino,inode->i_nlink);
+ if (inode->i_count == 1) {
+ unsigned long cache_page = (unsigned long) inode->u.affs_i.i_ec;
+ if (cache_page) {
+ pr_debug("AFFS: freeing ext cache\n");
+ inode->u.affs_i.i_ec = NULL;
+ free_page(cache_page);
+ }
}
- unlock_super(inode->i_sb);
}
void
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 2ec0401d5..96d8c6f5a 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -23,15 +23,15 @@
/* Simple toupper() for DOS\1 */
-static inline unsigned int
+static unsigned int
affs_toupper(unsigned int ch)
{
return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
}
-/* International toupper() for DOS\3 */
+/* International toupper() for DOS\3 ("international") */
-static inline unsigned int
+static unsigned int
affs_intl_toupper(unsigned int ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
@@ -39,6 +39,75 @@ affs_intl_toupper(unsigned int ch)
ch - ('a' - 'A') : ch;
}
+static int affs_hash_dentry(struct dentry *, struct qstr *);
+static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+struct dentry_operations affs_dentry_operations = {
+ NULL, /* d_validate */
+ affs_hash_dentry, /* d_hash */
+ affs_compare_dentry, /* d_compare */
+ NULL /* d_delete */
+};
+
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
+static int
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned int (*toupper)(unsigned int) = affs_toupper;
+ unsigned long hash;
+ int i;
+
+ if ((i = affs_check_name(qstr->name,qstr->len)))
+ return i;
+
+ /* Check whether to use the international 'toupper' routine */
+ if (AFFS_I2FSTYPE(dentry->d_inode))
+ toupper = affs_intl_toupper;
+ hash = init_name_hash();
+ for (i = 0; i < qstr->len && i < 30; i++)
+ hash = partial_name_hash(toupper(qstr->name[i]), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ unsigned int (*toupper)(unsigned int) = affs_toupper;
+ int alen = a->len;
+ int blen = b->len;
+ int i;
+
+ /* 'a' is the qstr of an already existing dentry, so the name
+ * must be valid. 'b' must be validated first.
+ */
+
+ if (affs_check_name(b->name,b->len))
+ return 1;
+
+ /* If the names are longer than the allowed 30 chars,
+ * the excess is ignored, so their length may differ.
+ */
+ if (alen > 30)
+ alen = 30;
+ if (blen > 30)
+ blen = 30;
+ if (alen != blen)
+ return 1;
+
+ /* Check whether to use the international 'toupper' routine */
+ if (AFFS_I2FSTYPE(dentry->d_inode))
+ toupper = affs_intl_toupper;
+
+ for (i = 0; i < alen; i++)
+ if (toupper(a->name[i]) != toupper(b->name[i]))
+ return 1;
+
+ return 0;
+}
+
/*
* NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
*/
@@ -46,6 +115,9 @@ affs_intl_toupper(unsigned int ch)
static int
affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
{
+ unsigned int (*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
+ int i;
+
if (!compare)
return 0;
@@ -59,21 +131,9 @@ affs_match(const unsigned char *name, int len, const unsigned char *compare, int
return 1;
if (dlen != len)
return 0;
- if (intl) {
- while (dlen--) {
- if (affs_intl_toupper(*name) != affs_intl_toupper(*compare))
- return 0;
- name++;
- compare++;
- }
- } else {
- while (dlen--) {
- if (affs_toupper(*name) != affs_toupper(*compare))
- return 0;
- name++;
- compare++;
- }
- }
+ for (i = 0; i < len; i++)
+ if (toupper(name[i]) != toupper(compare[i]))
+ return 0;
return 1;
}
@@ -99,23 +159,22 @@ static struct buffer_head *
affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
{
struct buffer_head *bh;
- int intl;
+ int intl = AFFS_I2FSTYPE(dir);
s32 key;
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
- intl = AFFS_I2FSTYPE(dir);
- bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+ bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
if (!bh)
return NULL;
- if (affs_match(name,namelen,".",1,intl)) {
+ if (namelen == 1 && name[0] == '.') {
*ino = dir->i_ino;
return bh;
}
- if (affs_match(name,namelen,"..",2,intl)) {
+ if (namelen == 2 && name[0] == '.' && name[1] == '.') {
*ino = affs_parent_ino(dir);
return bh;
}
@@ -127,10 +186,9 @@ affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
int cnamelen;
affs_brelse(bh);
- if (key == 0) {
- bh = NULL;
+ bh = NULL;
+ if (key == 0)
break;
- }
bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
if (!bh)
break;
@@ -162,6 +220,7 @@ affs_lookup(struct inode *dir, struct dentry *dentry)
if (!inode)
return -EACCES;
}
+ dentry->d_op = &affs_dentry_operations;
d_add(dentry,inode);
return 0;
}
@@ -177,10 +236,7 @@ affs_unlink(struct inode *dir, struct dentry *dentry)
pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name);
- bh = NULL;
retval = -ENOENT;
- if (!dir)
- goto unlink_done;
if (!(bh = affs_find_entry(dir,dentry,&ino)))
goto unlink_done;
@@ -198,8 +254,10 @@ affs_unlink(struct inode *dir, struct dentry *dentry)
inode->i_nlink = retval;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(inode);
- retval = 0;
d_delete(dentry);
+ mark_inode_dirty(dir);
+ retval = 0;
+
unlink_done:
affs_brelse(bh);
return retval;
@@ -214,11 +272,10 @@ affs_create(struct inode *dir, struct dentry *dentry, int mode)
pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
dentry->d_name.name,mode);
- if (!dir)
- return -ENOENT;
+ error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- return -ENOSPC;
+ goto out;
pr_debug(" -- ino=%lu\n",inode->i_ino);
if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
@@ -227,19 +284,22 @@ affs_create(struct inode *dir, struct dentry *dentry, int mode)
inode->i_op = &affs_file_inode_operations;
error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
- if (error) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- return error;
- }
+ if (error)
+ goto out_iput;
inode->i_mode = mode;
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ d_instantiate(dentry,inode);
+ mark_inode_dirty(inode);
dir->i_version = ++event;
mark_inode_dirty(dir);
- d_instantiate(dentry,inode);
+out:
+ return error;
- return 0;
+out_iput:
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
}
int
@@ -251,25 +311,29 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,mode);
+ error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- return -ENOSPC;
+ goto out;
inode->i_op = &affs_dir_inode_operations;
error = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
- if (error) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- return error;
- }
+ if (error)
+ goto out_iput;
inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ d_instantiate(dentry,inode);
+ mark_inode_dirty(inode);
dir->i_version = ++event;
mark_inode_dirty(dir);
- d_instantiate(dentry,inode);
+out:
+ return error;
- return 0;
+out_iput:
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
}
static int
@@ -285,24 +349,18 @@ empty_dir(struct buffer_head *bh, int hashsize)
int
affs_rmdir(struct inode *dir, struct dentry *dentry)
{
+ struct inode *inode = dentry->d_inode;
int retval;
unsigned long ino;
- struct inode *inode;
struct buffer_head *bh;
pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name);
- inode = NULL;
- bh = NULL;
retval = -ENOENT;
- if (!dir)
- goto rmdir_done;
if (!(bh = affs_find_entry(dir,dentry,&ino)))
goto rmdir_done;
- inode = dentry->d_inode;
-
retval = -EPERM;
if (current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid && !fsuser())
@@ -311,31 +369,31 @@ affs_rmdir(struct inode *dir, struct dentry *dentry)
goto rmdir_done;
if (inode == dir) /* we may not delete ".", but "../dir" is ok */
goto rmdir_done;
- if (!S_ISDIR(inode->i_mode)) {
- retval = -ENOTDIR;
+ retval = -ENOTDIR;
+ if (!S_ISDIR(inode->i_mode))
goto rmdir_done;
- }
- down(&inode->i_sem);
- if (dentry->d_count > 1) {
+ /*
+ * Make sure the directory is empty and the dentry isn't busy.
+ */
+ if (dentry->d_count > 1)
shrink_dcache_parent(dentry);
- }
- up(&inode->i_sem);
- if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
- retval = -ENOTEMPTY;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
goto rmdir_done;
- }
- if (inode->i_count > 1) {
- retval = -EBUSY;
+ retval = -EBUSY;
+ if (dentry->d_count > 1)
goto rmdir_done;
- }
+
if ((retval = affs_remove_header(bh,inode)) < 0)
goto rmdir_done;
inode->i_nlink = retval;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
retval = 0;
+ mark_inode_dirty(dir);
mark_inode_dirty(inode);
d_delete(dentry);
+
rmdir_done:
affs_brelse(bh);
return retval;
@@ -348,27 +406,25 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
struct inode *inode;
char *p;
unsigned long tmp;
- int i, maxlen;
+ int i, maxlen, error;
char c, lc;
pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,symname);
maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+ error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- return -ENOSPC;
+ goto out;
inode->i_op = &affs_symlink_inode_operations;
inode->i_mode = S_IFLNK | 0777;
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ error = -EIO;
bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
- if (!bh) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- return -EIO;
- }
+ if (!bh)
+ goto out_iput;
i = 0;
p = ((struct slink_front *)bh->b_data)->symname;
lc = '/';
@@ -400,30 +456,36 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
mark_buffer_dirty(bh,1);
affs_brelse(bh);
mark_inode_dirty(inode);
+
+ /* N.B. This test shouldn't be necessary ... dentry must be negative */
+ error = -EEXIST;
bh = affs_find_entry(dir,dentry,&tmp);
- if (bh) {
- inode->i_nlink = 0;
- iput(inode);
- affs_brelse(bh);
- return -EEXIST;
- }
- i = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
- if (i) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- affs_brelse(bh);
- return i;
- }
- dir->i_version = ++event;
+ if (bh)
+ goto out_release;
+ /* N.B. Shouldn't we add the entry before dirtying the buffer? */
+ error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
+ if (error)
+ goto out_release;
d_instantiate(dentry,inode);
-
- return 0;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
+
+out:
+ return error;
+
+out_release:
+ affs_brelse(bh);
+out_iput:
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
}
int
-affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
+affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
+ struct inode *oldinode = old_dentry->d_inode;
struct inode *inode;
struct buffer_head *bh;
unsigned long i;
@@ -432,6 +494,7 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name);
+ /* N.B. Do we need this test? The dentry must be negative ... */
bh = affs_find_entry(dir,dentry,&i);
if (bh) {
affs_brelse(bh);
@@ -441,8 +504,9 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
affs_warning(dir->i_sb,"link","Impossible link to link");
return -EINVAL;
}
+ error = -ENOSPC;
if (!(inode = affs_new_inode(dir)))
- return -ENOSPC;
+ goto out;
inode->i_op = oldinode->i_op;
inode->u.affs_i.i_protect = mode_to_prot(oldinode->i_mode);
@@ -458,6 +522,7 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
inode->i_nlink = 0;
else {
dir->i_version = ++event;
+ mark_inode_dirty(dir);
mark_inode_dirty(oldinode);
oldinode->i_count++;
d_instantiate(dentry,oldinode);
@@ -465,31 +530,10 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
mark_inode_dirty(inode);
iput(inode);
+out:
return error;
}
-/* This is copied from the ext2 fs. No need to reinvent the wheel. */
-
-static int
-subdir(struct dentry * new_dentry, struct dentry * old_dentry)
-{
- int result;
-
- result = 0;
- for (;;) {
- if (new_dentry != old_dentry) {
- struct dentry * parent = new_dentry->d_parent;
- if (parent == new_dentry)
- break;
- new_dentry = parent;
- continue;
- }
- result = 1;
- break;
- }
- return result;
-}
-
int
affs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
@@ -507,7 +551,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
- return retval;
+ goto out;
new_bh = NULL;
retval = -ENOENT;
@@ -537,8 +581,10 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry,old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
retval = -ENOTEMPTY;
if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
goto end_rename;
@@ -551,7 +597,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry,old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
if (affs_parent_ino(old_inode) != old_dir->i_ino)
goto end_rename;
@@ -588,6 +634,6 @@ new_checksum:
end_rename:
affs_brelse(old_bh);
affs_brelse(new_bh);
-
+out:
return retval;
}
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 7bd31dc5b..b4ce6756f 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -62,12 +62,10 @@ affs_put_super(struct super_block *sb)
kfree(sb->u.affs_sb.s_bitmap);
affs_brelse(sb->u.affs_sb.s_root_bh);
- /* I'm not happy with this. It would be better to save the previous
- * value of this devices blksize_size[][] in the super block and
- * restore it here, but with the affs superblock being quite large
- * already ...
+ /*
+ * Restore the previous value of this device's blksize_size[][]
*/
- set_blocksize(sb->s_dev,BLOCK_SIZE);
+ set_blocksize(sb->s_dev, sb->u.affs_sb.s_blksize);
sb->s_dev = 0;
unlock_super(sb);
@@ -100,7 +98,7 @@ affs_write_super(struct super_block *sb)
} else
sb->s_dirt = 0;
- pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
+ pr_debug("AFFS: write_super() at %lu, clean=%d\n", CURRENT_TIME, clean);
}
static struct super_operations affs_sops = {
@@ -119,7 +117,7 @@ static int
parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
{
- char *this_char, *value;
+ char *this_char, *value, *optn;
int f;
/* Fill in defaults */
@@ -138,18 +136,18 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
f = 0;
if ((value = strchr(this_char,'=')) != NULL)
*value++ = 0;
- if (!strcmp(this_char,"protect")) {
- if (value) {
- printk("AFFS: Option protect does not take an argument\n");
- return 0;
- }
+ if ((optn = "protect") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
*mount_opts |= SF_IMMUTABLE;
- } else if (!strcmp(this_char,"verbose")) {
- if (value) {
- printk("AFFS: Option verbose does not take an argument\n");
- return 0;
- }
+ } else if ((optn = "verbose") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
*mount_opts |= SF_VERBOSE;
+ } else if ((optn = "mufs") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
+ *mount_opts |= SF_MUFS;
} else if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) {
if (value) {
if (!*value) {
@@ -165,55 +163,51 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
}
}
} else if (!strcmp(this_char,"prefix")) {
- if (!value || !*value) {
- printk("AFFS: The prefix option requires an argument\n");
- return 0;
- }
- if (*prefix) /* Free any previous prefix */
+ optn = "prefix";
+ if (!value || !*value)
+ goto out_no_arg;
+ if (*prefix) { /* Free any previous prefix */
kfree(*prefix);
+ *prefix = NULL;
+ }
*prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
if (!*prefix)
return 0;
strcpy(*prefix,value);
*mount_opts |= SF_PREFIX;
} else if (!strcmp(this_char,"volume")) {
- if (!value || !*value) {
- printk("AFFS: The volume option requires an argument\n");
- return 0;
- }
+ optn = "volume";
+ if (!value || !*value)
+ goto out_no_arg;
if (strlen(value) > 30)
value[30] = 0;
strncpy(volume,value,30);
} else if (!strcmp(this_char,"mode")) {
- if (!value || !*value) {
- printk("AFFS: The mode option requires an argument\n");
- return 0;
- }
+ optn = "mode";
+ if (!value || !*value)
+ goto out_no_arg;
*mode = simple_strtoul(value,&value,8) & 0777;
if (*value)
return 0;
*mount_opts |= SF_SETMODE;
} else if (!strcmp(this_char,"reserved")) {
- if (!value || !*value) {
- printk("AFFS: The reserved option requires an argument\n");
- return 0;
- }
+ optn = "reserved";
+ if (!value || !*value)
+ goto out_no_arg;
*reserved = simple_strtoul(value,&value,0);
if (*value)
return 0;
} else if (!strcmp(this_char,"root")) {
- if (!value || !*value) {
- printk("AFFS: The root option requires an argument\n");
- return 0;
- }
+ optn = "root";
+ if (!value || !*value)
+ goto out_no_arg;
*root = simple_strtoul(value,&value,0);
if (*value)
return 0;
} else if (!strcmp(this_char,"bs")) {
- if (!value || !*value) {
- printk("AFFS: The bs option requires an argument\n");
- return 0;
- }
+ optn = "bs";
+ if (!value || !*value)
+ goto out_no_arg;
*blocksize = simple_strtoul(value,&value,0);
if (*value)
return 0;
@@ -234,6 +228,13 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
}
}
return 1;
+
+out_no_arg:
+ printk("AFFS: The %s option requires an argument\n", optn);
+ return 0;
+out_inv_arg:
+ printk("AFFS: Option %s does not take an argument\n", optn);
+ return 0;
}
/* This function definitely needs to be split up. Some fine day I'll
@@ -241,14 +242,14 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
*/
static struct super_block *
-affs_read_super(struct super_block *s,void *data, int silent)
+affs_read_super(struct super_block *s, void *data, int silent)
{
struct buffer_head *bh = NULL;
struct buffer_head *bb;
struct inode *root_inode;
kdev_t dev = s->s_dev;
s32 root_block;
- int size;
+ int blocks, size, blocksize;
u32 chksum;
u32 *bm;
s32 ptype, stype;
@@ -256,7 +257,6 @@ affs_read_super(struct super_block *s,void *data, int silent)
int num_bm;
int i, j;
s32 key;
- int blocksize;
uid_t uid;
gid_t gid;
int reserved;
@@ -268,57 +268,55 @@ affs_read_super(struct super_block *s,void *data, int silent)
pr_debug("affs_read_super(%s)\n",data ? (const char *)data : "no options");
MOD_INC_USE_COUNT;
-
- s->u.affs_sb.s_prefix = NULL;
- if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
- &blocksize,&s->u.affs_sb.s_prefix,s->u.affs_sb.s_volume,&mount_flags)) {
- s->s_dev = 0;
- printk(KERN_ERR "AFFS: Error parsing options\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
lock_super(s);
-
- /* Get the size of the device in 512-byte blocks.
- * If we later see that the partition uses bigger
- * blocks, we will have to change it.
- */
-
- size = blksize_size[MAJOR(dev)][MINOR(dev)];
- size = (size ? size : BLOCK_SIZE) / 512 * blk_size[MAJOR(dev)][MINOR(dev)];
-
+ s->s_magic = AFFS_SUPER_MAGIC;
+ s->s_op = &affs_sops;
s->u.affs_sb.s_bitmap = NULL;
s->u.affs_sb.s_root_bh = NULL;
+ s->u.affs_sb.s_prefix = NULL;
+ s->u.affs_sb.s_hashsize= 0;
+
+ if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
+ &blocksize,&s->u.affs_sb.s_prefix,
+ s->u.affs_sb.s_volume, &mount_flags))
+ goto out_bad_opts;
+ /* N.B. after this point s_prefix must be released */
+
s->u.affs_sb.s_flags = mount_flags;
s->u.affs_sb.s_mode = i;
s->u.affs_sb.s_uid = uid;
s->u.affs_sb.s_gid = gid;
+ s->u.affs_sb.s_reserved= reserved;
- if (size == 0) {
- s->s_dev = 0;
- unlock_super(s);
- printk(KERN_ERR "AFFS: Could not determine device size\n");
- goto out;
- }
- s->u.affs_sb.s_partition_size = size;
- s->u.affs_sb.s_reserved = reserved;
+ /* Get the size of the device in 512-byte blocks.
+ * If we later see that the partition uses bigger
+ * blocks, we will have to change it.
+ */
+
+ blocks = blk_size[MAJOR(dev)][MINOR(dev)];
+ if (blocks == 0)
+ goto out_bad_size;
+ s->u.affs_sb.s_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
+ if (!s->u.affs_sb.s_blksize)
+ s->u.affs_sb.s_blksize = BLOCK_SIZE;
+ size = (s->u.affs_sb.s_blksize / 512) * blocks;
+ pr_debug("AFFS: initial blksize=%d, blocks=%d\n",
+ s->u.affs_sb.s_blksize, blocks);
/* Try to find root block. Its location depends on the block size. */
- s->u.affs_sb.s_hashsize = 0;
+ i = 512;
+ j = 4096;
if (blocksize > 0) {
- i = blocksize;
- j = blocksize;
- } else {
- i = 512;
- j = 4096;
+ i = j = blocksize;
+ size = size / (blocksize / 512);
}
for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
+ s->u.affs_sb.s_root_block = root_block;
if (root_block < 0)
s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
- else
- s->u.affs_sb.s_root_block = root_block;
- set_blocksize(dev,blocksize);
+ pr_debug("AFFS: setting blocksize to %d\n", blocksize);
+ set_blocksize(dev, blocksize);
/* The root block location that was calculated above is not
* correct if the partition size is an odd number of 512-
@@ -331,35 +329,31 @@ affs_read_super(struct super_block *s,void *data, int silent)
* block behind the calculated one. So we check this one, too.
*/
for (num_bm = 0; num_bm < 2; num_bm++) {
- pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %u, "
- "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
- s->u.affs_sb.s_root_block + num_bm,size,reserved);
- bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
- if (!bh) {
- printk(KERN_ERR "AFFS: Cannot read root block\n");
- goto out;
- }
+ pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
+ "size=%d, reserved=%d\n",
+ kdevname(dev),
+ s->u.affs_sb.s_root_block + num_bm,
+ blocksize, size, reserved);
+ bh = affs_bread(dev, s->u.affs_sb.s_root_block + num_bm,
+ blocksize);
+ if (!bh)
+ continue;
if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
ptype == T_SHORT && stype == ST_ROOT) {
s->s_blocksize = blocksize;
s->u.affs_sb.s_hashsize = blocksize / 4 - 56;
s->u.affs_sb.s_root_block += num_bm;
key = 1;
- break;
+ goto got_root;
}
+ affs_brelse(bh);
+ bh = NULL;
}
- if (key)
- break;
- affs_brelse(bh);
- bh = NULL;
- }
- if (!key) {
- affs_brelse(bh);
- if (!silent)
- printk(KERN_ERR "AFFS: Cannot find a valid root block on device %s\n",
- kdevname(dev));
- goto out;
}
+ goto out_no_valid_block;
+
+ /* N.B. after this point bh must be released */
+got_root:
root_block = s->u.affs_sb.s_root_block;
s->u.affs_sb.s_partition_size = size;
@@ -369,59 +363,54 @@ affs_read_super(struct super_block *s,void *data, int silent)
/* Find out which kind of FS we have */
bb = affs_bread(dev,0,s->s_blocksize);
- if (bb) {
- chksum = be32_to_cpu(*(u32 *)bb->b_data);
-
- /* Dircache filesystems are compatible with non-dircache ones
- * when reading. As long as they aren't supported, writing is
- * not recommended.
- */
- if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
- || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
- printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
- kdevname(dev));
- s->s_flags |= MS_RDONLY;
- s->u.affs_sb.s_flags |= SF_READONLY;
- }
- switch (chksum) {
- case MUFS_FS:
- case MUFS_INTLFFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
- /* fall thru */
- case FS_INTLFFS:
- s->u.affs_sb.s_flags |= SF_INTL;
- break;
- case MUFS_DCFFS:
- case MUFS_FFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
- break;
- case FS_DCFFS:
- case FS_FFS:
- break;
- case MUFS_OFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
- /* fall thru */
- case FS_OFS:
- s->u.affs_sb.s_flags |= SF_OFS;
- break;
- case MUFS_DCOFS:
- case MUFS_INTLOFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
- case FS_DCOFS:
- case FS_INTLOFS:
- s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
- break;
- default:
- printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
- kdevname(dev),chksum);
- affs_brelse(bb);
- goto out;
- }
- affs_brelse(bb);
- } else {
- printk(KERN_ERR "AFFS: Cannot read boot block\n");
- goto out;
+ if (!bb)
+ goto out_no_root_block;
+ chksum = be32_to_cpu(*(u32 *)bb->b_data);
+ affs_brelse(bb);
+
+ /* Dircache filesystems are compatible with non-dircache ones
+ * when reading. As long as they aren't supported, writing is
+ * not recommended.
+ */
+ if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
+ || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
+ printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
+ kdevname(dev));
+ s->s_flags |= MS_RDONLY;
+ s->u.affs_sb.s_flags |= SF_READONLY;
+ }
+ switch (chksum) {
+ case MUFS_FS:
+ case MUFS_INTLFFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ /* fall thru */
+ case FS_INTLFFS:
+ s->u.affs_sb.s_flags |= SF_INTL;
+ break;
+ case MUFS_DCFFS:
+ case MUFS_FFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ break;
+ case FS_DCFFS:
+ case FS_FFS:
+ break;
+ case MUFS_OFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ /* fall thru */
+ case FS_OFS:
+ s->u.affs_sb.s_flags |= SF_OFS;
+ break;
+ case MUFS_DCOFS:
+ case MUFS_INTLOFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ case FS_DCOFS:
+ case FS_INTLOFS:
+ s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
+ break;
+ default:
+ goto out_unknown_fs;
}
+
if (mount_flags & SF_VERBOSE) {
chksum = cpu_to_be32(chksum);
printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
@@ -430,14 +419,14 @@ affs_read_super(struct super_block *s,void *data, int silent)
(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
}
- s->s_magic = AFFS_SUPER_MAGIC;
s->s_flags |= MS_NODEV | MS_NOSUID;
/* Keep super block in cache */
- if (!(s->u.affs_sb.s_root_bh = affs_bread(dev,root_block,s->s_blocksize))) {
- printk(KERN_ERR "AFFS: Cannot read root block\n");
- goto out;
- }
+ bb = affs_bread(dev,root_block,s->s_blocksize);
+ if (!bb)
+ goto out_no_root_block;
+ s->u.affs_sb.s_root_bh = bb;
+ /* N.B. after this point s_root_bh must be released */
/* Allocate space for bitmaps, zones and others */
@@ -448,11 +437,10 @@ affs_read_super(struct super_block *s,void *data, int silent)
az_no * sizeof(struct affs_alloc_zone) +
MAX_ZONES * sizeof(struct affs_zone);
pr_debug("num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
- if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype,GFP_KERNEL))) {
- printk(KERN_ERR "AFFS: Not enough memory\n");
- goto out;
- }
+ if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype, GFP_KERNEL)))
+ goto out_no_bitmap;
memset(s->u.affs_sb.s_bitmap,0,ptype);
+ /* N.B. after the point s_bitmap must be released */
s->u.affs_sb.s_zones = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
s->u.affs_sb.s_alloc = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
@@ -490,89 +478,79 @@ affs_read_super(struct super_block *s,void *data, int silent)
s->u.affs_sb.s_flags |= SF_READONLY;
continue;
}
- bb = affs_bread(s->s_dev,be32_to_cpu(bm[i]),s->s_blocksize);
- if (bb) {
- if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
- !(s->s_flags & MS_RDONLY)) {
- printk(KERN_WARNING "AFFS: Bitmap (%d,key=%lu) invalid - "
- "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
- kdevname(dev));
- s->s_flags |= MS_RDONLY;
- s->u.affs_sb.s_flags |= SF_READONLY;
- }
- /* Mark unused bits in the last word as allocated */
- if (size <= s->s_blocksize * 8 - 32) { /* last bitmap */
- ptype = size / 32 + 1; /* word number */
- key = size & 0x1F; /* used bits */
- if (key && !(s->s_flags & MS_RDONLY)) {
- chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
- ((u32 *)bb->b_data)[ptype] &= chksum;
- affs_fix_checksum(s->s_blocksize,bb->b_data,0);
- mark_buffer_dirty(bb,1);
- bmalt = 1;
- }
- ptype = (size + 31) & ~0x1F;
- size = 0;
- s->u.affs_sb.s_flags |= SF_BM_VALID;
- } else {
- ptype = s->s_blocksize * 8 - 32;
- size -= ptype;
- }
- s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
- s->u.affs_sb.s_bitmap[mapidx].bm_bh = NULL;
- s->u.affs_sb.s_bitmap[mapidx].bm_key = be32_to_cpu(bm[i]);
- s->u.affs_sb.s_bitmap[mapidx].bm_count = 0;
- offset += ptype;
-
- for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
- key = MIN(ptype,AFFS_ZONE_SIZE); /* size in bits */
- s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
- s->u.affs_sb.s_alloc[az_no].az_free =
- affs_count_free_bits(key / 8,bb->b_data +
- j * (AFFS_ZONE_SIZE / 8) + 4);
+ bb = affs_bread(dev,be32_to_cpu(bm[i]),s->s_blocksize);
+ if (!bb)
+ goto out_no_read_bm;
+ if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
+ !(s->s_flags & MS_RDONLY)) {
+ printk(KERN_WARNING "AFFS: Bitmap (%d,key=%u) invalid - "
+ "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
+ kdevname(dev));
+ s->s_flags |= MS_RDONLY;
+ s->u.affs_sb.s_flags |= SF_READONLY;
+ }
+ /* Mark unused bits in the last word as allocated */
+ if (size <= s->s_blocksize * 8 - 32) { /* last bitmap */
+ ptype = size / 32 + 1; /* word number */
+ key = size & 0x1F; /* used bits */
+ if (key && !(s->s_flags & MS_RDONLY)) {
+ chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
+ ((u32 *)bb->b_data)[ptype] &= chksum;
+ affs_fix_checksum(s->s_blocksize,bb->b_data,0);
+ mark_buffer_dirty(bb,1);
+ bmalt = 1;
}
- affs_brelse(bb);
+ ptype = (size + 31) & ~0x1F;
+ size = 0;
+ s->u.affs_sb.s_flags |= SF_BM_VALID;
} else {
- printk(KERN_ERR "AFFS: Cannot read bitmap\n");
- goto out;
+ ptype = s->s_blocksize * 8 - 32;
+ size -= ptype;
+ }
+ s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
+ s->u.affs_sb.s_bitmap[mapidx].bm_bh = NULL;
+ s->u.affs_sb.s_bitmap[mapidx].bm_key = be32_to_cpu(bm[i]);
+ s->u.affs_sb.s_bitmap[mapidx].bm_count = 0;
+ offset += ptype;
+
+ for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
+ key = MIN(ptype,AFFS_ZONE_SIZE); /* size in bits */
+ s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
+ s->u.affs_sb.s_alloc[az_no].az_free =
+ affs_count_free_bits(key / 8,bb->b_data +
+ j * (AFFS_ZONE_SIZE / 8) + 4);
}
+ affs_brelse(bb);
}
key = be32_to_cpu(bm[stype]); /* Next block of bitmap pointers */
ptype = 0;
stype = s->s_blocksize / 4 - 1;
affs_brelse(bh);
+ bh = NULL;
if (key) {
- if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
- printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
- goto out;
- }
- } else
- bh = NULL;
- }
- if (mapidx < num_bm) {
- printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",mapidx,num_bm);
- goto out;
+ bh = affs_bread(dev,key,s->s_blocksize);
+ if (!bh)
+ goto out_no_bm_ext;
+ }
}
+ if (mapidx < num_bm)
+ goto out_bad_num;
+
nobitmap:
s->u.affs_sb.s_bm_count = num_bm;
/* set up enough so that it can read an inode */
- s->s_dev = dev;
- s->s_op = &affs_sops;
s->s_dirt = 1;
root_inode = iget(s,root_block);
- s->s_root = d_alloc_root(root_inode,NULL);
- unlock_super(s);
-
- if (!(s->s_root)) {
- s->s_dev = 0;
- affs_brelse(s->u.affs_sb.s_root_bh);
- printk(KERN_ERR "AFFS: get root inode failed\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (!root_inode)
+ goto out_no_root;
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root)
+ goto out_no_root;
+ s->s_root->d_op = &affs_dentry_operations;
+ unlock_super(s);
/* Record date of last change if the bitmap was truncated and
* create data zones if the volume is writable.
*/
@@ -590,12 +568,56 @@ nobitmap:
pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
return s;
- out: /* Kick out for various error conditions */
- affs_brelse (bh);
+out_bad_opts:
+ printk(KERN_ERR "AFFS: Error parsing options\n");
+ goto out_fail;
+out_bad_size:
+ printk(KERN_ERR "AFFS: Could not determine device size\n");
+ goto out_free_prefix;
+out_no_valid_block:
+ if (!silent)
+ printk(KERN_ERR "AFFS: No valid root block on device %s\n",
+ kdevname(dev));
+ goto out_restore;
+out_unknown_fs:
+ printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+ kdevname(dev), chksum);
+ goto out_free_bh;
+out_no_root_block:
+ printk(KERN_ERR "AFFS: Cannot read root block\n");
+ goto out_free_bh;
+out_no_bitmap:
+ printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
+ goto out_free_root_block;
+out_no_read_bm:
+ printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+ goto out_free_bitmap;
+out_no_bm_ext:
+ printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+ goto out_free_bitmap;
+out_bad_num:
+ printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",
+ mapidx, num_bm);
+ goto out_free_bitmap;
+out_no_root:
+ printk(KERN_ERR "AFFS: get root inode failed\n");
+
+ /*
+ * Begin the cascaded cleanup ...
+ */
+ iput(root_inode);
+out_free_bitmap:
+ kfree(s->u.affs_sb.s_bitmap);
+out_free_root_block:
affs_brelse(s->u.affs_sb.s_root_bh);
- if (s->u.affs_sb.s_bitmap)
- kfree(s->u.affs_sb.s_bitmap);
- set_blocksize(dev,BLOCK_SIZE);
+out_free_bh:
+ affs_brelse(bh);
+out_restore:
+ set_blocksize(dev, s->u.affs_sb.s_blksize);
+out_free_prefix:
+ if (s->u.affs_sb.s_prefix)
+ kfree(s->u.affs_sb.s_prefix);
+out_fail:
s->s_dev = 0;
unlock_super(s);
MOD_DEC_USE_COUNT;
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index 22042158a..2bf4cd00a 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -19,8 +19,8 @@
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-static int affs_readlink(struct inode *, char *, int);
-static struct dentry *affs_follow_link(struct inode *inode, struct dentry *base);
+static int affs_readlink(struct dentry *, char *, int);
+static struct dentry *affs_follow_link(struct dentry *dentry, struct dentry *base);
struct inode_operations affs_symlink_inode_operations = {
NULL, /* no file-operations */
@@ -44,8 +44,9 @@ struct inode_operations affs_symlink_inode_operations = {
};
static int
-affs_readlink(struct inode *inode, char *buffer, int buflen)
+affs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
struct slink_front *lf;
int i, j;
@@ -97,8 +98,9 @@ affs_readlink(struct inode *inode, char *buffer, int buflen)
}
static struct dentry *
-affs_follow_link(struct inode *inode, struct dentry *base)
+affs_follow_link(struct dentry *dentry, struct dentry *base)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
struct slink_front *lf;
char *buffer;