diff options
Diffstat (limited to 'fs/sysv')
-rw-r--r-- | fs/sysv/dir.c | 21 | ||||
-rw-r--r-- | fs/sysv/file.c | 22 | ||||
-rw-r--r-- | fs/sysv/ialloc.c | 1 | ||||
-rw-r--r-- | fs/sysv/inode.c | 19 | ||||
-rw-r--r-- | fs/sysv/itree.c | 19 | ||||
-rw-r--r-- | fs/sysv/super.c | 74 | ||||
-rw-r--r-- | fs/sysv/sysv.h | 4 |
7 files changed, 105 insertions, 55 deletions
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 79941e4..a77c421 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -218,8 +218,7 @@ got_it: pos = page_offset(page) + (char*)de - (char*)page_address(page); lock_page(page); - err = __sysv_write_begin(NULL, page->mapping, pos, SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); if (err) goto out_unlock; memcpy (de->name, name, namelen); @@ -239,15 +238,13 @@ out_unlock: int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page) { - struct address_space *mapping = page->mapping; - struct inode *inode = (struct inode*)mapping->host; + struct inode *inode = page->mapping->host; char *kaddr = (char*)page_address(page); loff_t pos = page_offset(page) + (char *)de - kaddr; int err; lock_page(page); - err = __sysv_write_begin(NULL, mapping, pos, SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); BUG_ON(err); de->inode = 0; err = dir_commit_chunk(page, pos, SYSV_DIRSIZE); @@ -259,16 +256,14 @@ int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page) int sysv_make_empty(struct inode *inode, struct inode *dir) { - struct address_space *mapping = inode->i_mapping; - struct page *page = grab_cache_page(mapping, 0); + struct page *page = grab_cache_page(inode->i_mapping, 0); struct sysv_dir_entry * de; char *base; int err; if (!page) return -ENOMEM; - err = __sysv_write_begin(NULL, mapping, 0, 2 * SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, 0, 2 * SYSV_DIRSIZE); if (err) { unlock_page(page); goto fail; @@ -341,15 +336,13 @@ not_empty: void sysv_set_link(struct sysv_dir_entry *de, struct page *page, struct inode *inode) { - struct address_space *mapping = page->mapping; - struct inode *dir = mapping->host; + struct inode *dir = page->mapping->host; loff_t pos = page_offset(page) + (char *)de-(char*)page_address(page); int err; lock_page(page); - err = __sysv_write_begin(NULL, mapping, pos, SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); BUG_ON(err); de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino); err = dir_commit_chunk(page, pos, SYSV_DIRSIZE); diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 750cc22..0a65939 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -30,7 +30,29 @@ const struct file_operations sysv_file_operations = { .splice_read = generic_file_splice_read, }; +static int sysv_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; +} + const struct inode_operations sysv_file_inode_operations = { .truncate = sysv_truncate, + .setattr = sysv_setattr, .getattr = sysv_getattr, }; diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index fcc498e..0c96c98 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -113,7 +113,6 @@ void sysv_free_inode(struct inode * inode) return; } raw_inode = sysv_raw_inode(sb, ino, &bh); - clear_inode(inode); if (!raw_inode) { printk("sysv_free_inode: unable to read inode block on device " "%s\n", inode->i_sb->s_id); diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index d4a5380..de44d06 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -71,8 +71,8 @@ static int sysv_remount(struct super_block *sb, int *flags, char *data) lock_super(sb); if (sbi->s_forced_ro) *flags |= MS_RDONLY; - if (!(*flags & MS_RDONLY)) - sb->s_dirt = 1; + if (*flags & MS_RDONLY) + sysv_write_super(sb); unlock_super(sb); return 0; } @@ -308,12 +308,17 @@ int sysv_sync_inode(struct inode *inode) return __sysv_write_inode(inode, 1); } -static void sysv_delete_inode(struct inode *inode) +static void sysv_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); - inode->i_size = 0; - sysv_truncate(inode); - sysv_free_inode(inode); + if (!inode->i_nlink) { + inode->i_size = 0; + sysv_truncate(inode); + } + invalidate_inode_buffers(inode); + end_writeback(inode); + if (!inode->i_nlink) + sysv_free_inode(inode); } static struct kmem_cache *sysv_inode_cachep; @@ -344,7 +349,7 @@ const struct super_operations sysv_sops = { .alloc_inode = sysv_alloc_inode, .destroy_inode = sysv_destroy_inode, .write_inode = sysv_write_inode, - .delete_inode = sysv_delete_inode, + .evict_inode = sysv_evict_inode, .put_super = sysv_put_super, .write_super = sysv_write_super, .sync_fs = sysv_sync_fs, diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c index f042eec..9ca6627 100644 --- a/fs/sysv/itree.c +++ b/fs/sysv/itree.c @@ -459,20 +459,25 @@ static int sysv_readpage(struct file *file, struct page *page) return block_read_full_page(page,get_block); } -int __sysv_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) +int sysv_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - get_block); + return __block_write_begin(page, pos, len, get_block); } static int sysv_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return __sysv_write_begin(file, mapping, pos, len, flags, pagep, fsdata); + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, get_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t sysv_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 5a903da..a0b0cda 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -347,7 +347,6 @@ static int complete_read_super(struct super_block *sb, int silent, int size) sb->s_flags |= MS_RDONLY; if (sbi->s_truncate) sb->s_root->d_op = &sysv_dentry_operations; - sb->s_dirt = 1; return 1; } @@ -435,12 +434,46 @@ Ebadsize: goto failed; } -static int v7_fill_super(struct super_block *sb, void *data, int silent) +static int v7_sanity_check(struct super_block *sb, struct buffer_head *bh) { - struct sysv_sb_info *sbi; - struct buffer_head *bh, *bh2 = NULL; struct v7_super_block *v7sb; struct sysv_inode *v7i; + struct buffer_head *bh2; + struct sysv_sb_info *sbi; + + sbi = sb->s_fs_info; + + /* plausibility check on superblock */ + v7sb = (struct v7_super_block *) bh->b_data; + if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || + fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) + return 0; + + /* plausibility check on root inode: it is a directory, + with a nonzero size that is a multiple of 16 */ + bh2 = sb_bread(sb, 2); + if (bh2 == NULL) + return 0; + + v7i = (struct sysv_inode *)(bh2->b_data + 64); + if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || + (fs32_to_cpu(sbi, v7i->i_size) == 0) || + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof(struct sysv_dir_entry))) { + brelse(bh2); + return 0; + } + + brelse(bh2); + return 1; +} + +static int v7_fill_super(struct super_block *sb, void *data, int silent) +{ + struct sysv_sb_info *sbi; + struct buffer_head *bh; if (440 != sizeof (struct v7_super_block)) panic("V7 FS: bad super-block size"); @@ -454,7 +487,6 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) sbi->s_sb = sb; sbi->s_block_base = 0; sbi->s_type = FSTYPE_V7; - sbi->s_bytesex = BYTESEX_PDP; sb->s_fs_info = sbi; sb_set_blocksize(sb, 512); @@ -466,32 +498,27 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) goto failed; } - /* plausibility check on superblock */ - v7sb = (struct v7_super_block *) bh->b_data; - if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || - fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_time) == 0) - goto failed; + /* Try PDP-11 UNIX */ + sbi->s_bytesex = BYTESEX_PDP; + if (v7_sanity_check(sb, bh)) + goto detected; - /* plausibility check on root inode: it is a directory, - with a nonzero size that is a multiple of 16 */ - if ((bh2 = sb_bread(sb, 2)) == NULL) - goto failed; - v7i = (struct sysv_inode *)(bh2->b_data + 64); - if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || - (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) != 0) - goto failed; - brelse(bh2); - bh2 = NULL; + /* Try PC/IX, v7/x86 */ + sbi->s_bytesex = BYTESEX_LE; + if (v7_sanity_check(sb, bh)) + goto detected; + goto failed; + +detected: sbi->s_bh1 = bh; sbi->s_bh2 = bh; if (complete_read_super(sb, silent, 1)) return 0; failed: - brelse(bh2); + printk(KERN_ERR "VFS: could not find a valid V7 on %s.\n", + sb->s_id); brelse(bh); kfree(sbi); return -EINVAL; @@ -560,4 +587,5 @@ static void __exit exit_sysv_fs(void) module_init(init_sysv_fs) module_exit(exit_sysv_fs) +MODULE_ALIAS("v7"); MODULE_LICENSE("GPL"); diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index 94cb9b4..bb55cdb 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -136,9 +136,7 @@ extern unsigned long sysv_count_free_blocks(struct super_block *); /* itree.c */ extern void sysv_truncate(struct inode *); -extern int __sysv_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata); +extern int sysv_prepare_chunk(struct page *page, loff_t pos, unsigned len); /* inode.c */ extern struct inode *sysv_iget(struct super_block *, unsigned int); |