diff options
Diffstat (limited to 'fs/affs')
-rw-r--r-- | fs/affs/.cvsignore | 1 | ||||
-rw-r--r-- | fs/affs/Changes | 63 | ||||
-rw-r--r-- | fs/affs/bitmap.c | 1 | ||||
-rw-r--r-- | fs/affs/file.c | 249 | ||||
-rw-r--r-- | fs/affs/inode.c | 39 | ||||
-rw-r--r-- | fs/affs/namei.c | 292 | ||||
-rw-r--r-- | fs/affs/super.c | 484 | ||||
-rw-r--r-- | fs/affs/symlink.c | 10 |
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; |