diff options
Diffstat (limited to 'fs/minix')
-rw-r--r-- | fs/minix/Makefile | 7 | ||||
-rw-r--r-- | fs/minix/bitmap.c | 269 | ||||
-rw-r--r-- | fs/minix/dir.c | 409 | ||||
-rw-r--r-- | fs/minix/file.c | 45 | ||||
-rw-r--r-- | fs/minix/inode.c | 598 | ||||
-rw-r--r-- | fs/minix/itree_common.c | 362 | ||||
-rw-r--r-- | fs/minix/itree_v1.c | 61 | ||||
-rw-r--r-- | fs/minix/itree_v2.c | 66 | ||||
-rw-r--r-- | fs/minix/minix.h | 96 | ||||
-rw-r--r-- | fs/minix/namei.c | 317 |
10 files changed, 2230 insertions, 0 deletions
diff --git a/fs/minix/Makefile b/fs/minix/Makefile new file mode 100644 index 00000000000..3063015abfd --- /dev/null +++ b/fs/minix/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux minix filesystem routines. +# + +obj-$(CONFIG_MINIX_FS) += minix.o + +minix-objs := bitmap.o itree_v1.o itree_v2.o namei.o inode.o file.o dir.o diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c new file mode 100644 index 00000000000..dc6a4e4abcd --- /dev/null +++ b/fs/minix/bitmap.c @@ -0,0 +1,269 @@ +/* + * linux/fs/minix/bitmap.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * Modified for 680x0 by Hamish Macdonald + * Fixed for 680x0 by Andreas Schwab + */ + +/* bitmap.c contains the code that handles the inode and block bitmaps */ + +#include "minix.h" +#include <linux/smp_lock.h> +#include <linux/buffer_head.h> +#include <linux/bitops.h> + +static int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 }; + +static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, __u32 numbits) +{ + unsigned i, j, sum = 0; + struct buffer_head *bh; + + for (i=0; i<numblocks-1; i++) { + if (!(bh=map[i])) + return(0); + for (j=0; j<BLOCK_SIZE; j++) + sum += nibblemap[bh->b_data[j] & 0xf] + + nibblemap[(bh->b_data[j]>>4) & 0xf]; + } + + if (numblocks==0 || !(bh=map[numblocks-1])) + return(0); + i = ((numbits-(numblocks-1)*BLOCK_SIZE*8)/16)*2; + for (j=0; j<i; j++) { + sum += nibblemap[bh->b_data[j] & 0xf] + + nibblemap[(bh->b_data[j]>>4) & 0xf]; + } + + i = numbits%16; + if (i!=0) { + i = *(__u16 *)(&bh->b_data[j]) | ~((1<<i) - 1); + sum += nibblemap[i & 0xf] + nibblemap[(i>>4) & 0xf]; + sum += nibblemap[(i>>8) & 0xf] + nibblemap[(i>>12) & 0xf]; + } + return(sum); +} + +void minix_free_block(struct inode * inode, int block) +{ + struct super_block * sb = inode->i_sb; + struct minix_sb_info * sbi = minix_sb(sb); + struct buffer_head * bh; + unsigned int bit,zone; + + if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) { + printk("trying to free block not in datazone\n"); + return; + } + zone = block - sbi->s_firstdatazone + 1; + bit = zone & 8191; + zone >>= 13; + if (zone >= sbi->s_zmap_blocks) { + printk("minix_free_block: nonexistent bitmap buffer\n"); + return; + } + bh = sbi->s_zmap[zone]; + lock_kernel(); + if (!minix_test_and_clear_bit(bit,bh->b_data)) + printk("free_block (%s:%d): bit already cleared\n", + sb->s_id, block); + unlock_kernel(); + mark_buffer_dirty(bh); + return; +} + +int minix_new_block(struct inode * inode) +{ + struct minix_sb_info *sbi = minix_sb(inode->i_sb); + int i; + + for (i = 0; i < sbi->s_zmap_blocks; i++) { + struct buffer_head *bh = sbi->s_zmap[i]; + int j; + + lock_kernel(); + if ((j = minix_find_first_zero_bit(bh->b_data, 8192)) < 8192) { + minix_set_bit(j,bh->b_data); + unlock_kernel(); + mark_buffer_dirty(bh); + j += i*8192 + sbi->s_firstdatazone-1; + if (j < sbi->s_firstdatazone || j >= sbi->s_nzones) + break; + return j; + } + unlock_kernel(); + } + return 0; +} + +unsigned long minix_count_free_blocks(struct minix_sb_info *sbi) +{ + return (count_free(sbi->s_zmap, sbi->s_zmap_blocks, + sbi->s_nzones - sbi->s_firstdatazone + 1) + << sbi->s_log_zone_size); +} + +struct minix_inode * +minix_V1_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh) +{ + int block; + struct minix_sb_info *sbi = minix_sb(sb); + struct minix_inode *p; + + if (!ino || ino > sbi->s_ninodes) { + printk("Bad inode number on dev %s: %ld is out of range\n", + sb->s_id, (long)ino); + return NULL; + } + ino--; + block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks + + ino / MINIX_INODES_PER_BLOCK; + *bh = sb_bread(sb, block); + if (!*bh) { + printk("unable to read i-node block\n"); + return NULL; + } + p = (void *)(*bh)->b_data; + return p + ino % MINIX_INODES_PER_BLOCK; +} + +struct minix2_inode * +minix_V2_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh) +{ + int block; + struct minix_sb_info *sbi = minix_sb(sb); + struct minix2_inode *p; + + *bh = NULL; + if (!ino || ino > sbi->s_ninodes) { + printk("Bad inode number on dev %s: %ld is out of range\n", + sb->s_id, (long)ino); + return NULL; + } + ino--; + block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks + + ino / MINIX2_INODES_PER_BLOCK; + *bh = sb_bread(sb, block); + if (!*bh) { + printk("unable to read i-node block\n"); + return NULL; + } + p = (void *)(*bh)->b_data; + return p + ino % MINIX2_INODES_PER_BLOCK; +} + +/* Clear the link count and mode of a deleted inode on disk. */ + +static void minix_clear_inode(struct inode *inode) +{ + struct buffer_head *bh = NULL; + + if (INODE_VERSION(inode) == MINIX_V1) { + struct minix_inode *raw_inode; + raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh); + if (raw_inode) { + raw_inode->i_nlinks = 0; + raw_inode->i_mode = 0; + } + } else { + struct minix2_inode *raw_inode; + raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh); + if (raw_inode) { + raw_inode->i_nlinks = 0; + raw_inode->i_mode = 0; + } + } + if (bh) { + mark_buffer_dirty(bh); + brelse (bh); + } +} + +void minix_free_inode(struct inode * inode) +{ + struct minix_sb_info *sbi = minix_sb(inode->i_sb); + struct buffer_head * bh; + unsigned long ino; + + ino = inode->i_ino; + if (ino < 1 || ino > sbi->s_ninodes) { + printk("minix_free_inode: inode 0 or nonexistent inode\n"); + goto out; + } + if ((ino >> 13) >= sbi->s_imap_blocks) { + printk("minix_free_inode: nonexistent imap in superblock\n"); + goto out; + } + + minix_clear_inode(inode); /* clear on-disk copy */ + + bh = sbi->s_imap[ino >> 13]; + lock_kernel(); + if (!minix_test_and_clear_bit(ino & 8191, bh->b_data)) + printk("minix_free_inode: bit %lu already cleared.\n", ino); + unlock_kernel(); + mark_buffer_dirty(bh); + out: + clear_inode(inode); /* clear in-memory copy */ +} + +struct inode * minix_new_inode(const struct inode * dir, int * error) +{ + struct super_block *sb = dir->i_sb; + struct minix_sb_info *sbi = minix_sb(sb); + struct inode *inode = new_inode(sb); + struct buffer_head * bh; + int i,j; + + if (!inode) { + *error = -ENOMEM; + return NULL; + } + j = 8192; + bh = NULL; + *error = -ENOSPC; + lock_kernel(); + for (i = 0; i < sbi->s_imap_blocks; i++) { + bh = sbi->s_imap[i]; + if ((j = minix_find_first_zero_bit(bh->b_data, 8192)) < 8192) + break; + } + if (!bh || j >= 8192) { + unlock_kernel(); + iput(inode); + return NULL; + } + if (minix_test_and_set_bit(j,bh->b_data)) { /* shouldn't happen */ + printk("new_inode: bit already set"); + unlock_kernel(); + iput(inode); + return NULL; + } + unlock_kernel(); + mark_buffer_dirty(bh); + j += i*8192; + if (!j || j > sbi->s_ninodes) { + iput(inode); + return NULL; + } + inode->i_uid = current->fsuid; + inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; + inode->i_ino = j; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; + inode->i_blocks = inode->i_blksize = 0; + memset(&minix_i(inode)->u, 0, sizeof(minix_i(inode)->u)); + insert_inode_hash(inode); + mark_inode_dirty(inode); + + *error = 0; + return inode; +} + +unsigned long minix_count_free_inodes(struct minix_sb_info *sbi) +{ + return count_free(sbi->s_imap, sbi->s_imap_blocks, sbi->s_ninodes + 1); +} diff --git a/fs/minix/dir.c b/fs/minix/dir.c new file mode 100644 index 00000000000..732502aabc0 --- /dev/null +++ b/fs/minix/dir.c @@ -0,0 +1,409 @@ +/* + * linux/fs/minix/dir.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * minix directory handling functions + */ + +#include "minix.h" +#include <linux/highmem.h> +#include <linux/smp_lock.h> + +typedef struct minix_dir_entry minix_dirent; + +static int minix_readdir(struct file *, void *, filldir_t); + +struct file_operations minix_dir_operations = { + .read = generic_read_dir, + .readdir = minix_readdir, + .fsync = minix_sync_file, +}; + +static inline void dir_put_page(struct page *page) +{ + kunmap(page); + page_cache_release(page); +} + +/* + * Return the offset into page `page_nr' of the last valid + * byte in that page, plus one. + */ +static unsigned +minix_last_byte(struct inode *inode, unsigned long page_nr) +{ + unsigned last_byte = PAGE_CACHE_SIZE; + + if (page_nr == (inode->i_size >> PAGE_CACHE_SHIFT)) + last_byte = inode->i_size & (PAGE_CACHE_SIZE - 1); + return last_byte; +} + +static inline unsigned long dir_pages(struct inode *inode) +{ + return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; +} + +static int dir_commit_chunk(struct page *page, unsigned from, unsigned to) +{ + struct inode *dir = (struct inode *)page->mapping->host; + int err = 0; + page->mapping->a_ops->commit_write(NULL, page, from, to); + if (IS_DIRSYNC(dir)) + err = write_one_page(page, 1); + else + unlock_page(page); + return err; +} + +static struct page * dir_get_page(struct inode *dir, unsigned long n) +{ + struct address_space *mapping = dir->i_mapping; + struct page *page = read_cache_page(mapping, n, + (filler_t*)mapping->a_ops->readpage, NULL); + if (!IS_ERR(page)) { + wait_on_page_locked(page); + kmap(page); + if (!PageUptodate(page)) + goto fail; + } + return page; + +fail: + dir_put_page(page); + return ERR_PTR(-EIO); +} + +static inline void *minix_next_entry(void *de, struct minix_sb_info *sbi) +{ + return (void*)((char*)de + sbi->s_dirsize); +} + +static int minix_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + unsigned long pos = filp->f_pos; + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + unsigned offset = pos & ~PAGE_CACHE_MASK; + unsigned long n = pos >> PAGE_CACHE_SHIFT; + unsigned long npages = dir_pages(inode); + struct minix_sb_info *sbi = minix_sb(sb); + unsigned chunk_size = sbi->s_dirsize; + + lock_kernel(); + + pos = (pos + chunk_size-1) & ~(chunk_size-1); + if (pos >= inode->i_size) + goto done; + + for ( ; n < npages; n++, offset = 0) { + char *p, *kaddr, *limit; + struct page *page = dir_get_page(inode, n); + + if (IS_ERR(page)) + continue; + kaddr = (char *)page_address(page); + p = kaddr+offset; + limit = kaddr + minix_last_byte(inode, n) - chunk_size; + for ( ; p <= limit ; p = minix_next_entry(p, sbi)) { + minix_dirent *de = (minix_dirent *)p; + if (de->inode) { + int over; + unsigned l = strnlen(de->name,sbi->s_namelen); + + offset = p - kaddr; + over = filldir(dirent, de->name, l, + (n<<PAGE_CACHE_SHIFT) | offset, + de->inode, DT_UNKNOWN); + if (over) { + dir_put_page(page); + goto done; + } + } + } + dir_put_page(page); + } + +done: + filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; + unlock_kernel(); + return 0; +} + +static inline int namecompare(int len, int maxlen, + const char * name, const char * buffer) +{ + if (len < maxlen && buffer[len]) + return 0; + return !memcmp(name, buffer, len); +} + +/* + * minix_find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the cache buffer in which the entry was found, and the entry + * itself (as a parameter - res_dir). It does NOT read the inode of the + * entry - you'll have to do that yourself if you want to. + */ +minix_dirent *minix_find_entry(struct dentry *dentry, struct page **res_page) +{ + const char * name = dentry->d_name.name; + int namelen = dentry->d_name.len; + struct inode * dir = dentry->d_parent->d_inode; + struct super_block * sb = dir->i_sb; + struct minix_sb_info * sbi = minix_sb(sb); + unsigned long n; + unsigned long npages = dir_pages(dir); + struct page *page = NULL; + struct minix_dir_entry *de; + + *res_page = NULL; + + for (n = 0; n < npages; n++) { + char *kaddr; + page = dir_get_page(dir, n); + if (IS_ERR(page)) + continue; + + kaddr = (char*)page_address(page); + de = (struct minix_dir_entry *) kaddr; + kaddr += minix_last_byte(dir, n) - sbi->s_dirsize; + for ( ; (char *) de <= kaddr ; de = minix_next_entry(de,sbi)) { + if (!de->inode) + continue; + if (namecompare(namelen,sbi->s_namelen,name,de->name)) + goto found; + } + dir_put_page(page); + } + return NULL; + +found: + *res_page = page; + return de; +} + +int minix_add_link(struct dentry *dentry, struct inode *inode) +{ + struct inode *dir = dentry->d_parent->d_inode; + const char * name = dentry->d_name.name; + int namelen = dentry->d_name.len; + struct super_block * sb = dir->i_sb; + struct minix_sb_info * sbi = minix_sb(sb); + struct page *page = NULL; + struct minix_dir_entry * de; + unsigned long npages = dir_pages(dir); + unsigned long n; + char *kaddr; + unsigned from, to; + int err; + + /* + * We take care of directory expansion in the same loop + * This code plays outside i_size, so it locks the page + * to protect that region. + */ + for (n = 0; n <= npages; n++) { + char *dir_end; + + page = dir_get_page(dir, n); + err = PTR_ERR(page); + if (IS_ERR(page)) + goto out; + lock_page(page); + kaddr = (char*)page_address(page); + dir_end = kaddr + minix_last_byte(dir, n); + de = (minix_dirent *)kaddr; + kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize; + while ((char *)de <= kaddr) { + if ((char *)de == dir_end) { + /* We hit i_size */ + de->inode = 0; + goto got_it; + } + if (!de->inode) + goto got_it; + err = -EEXIST; + if (namecompare(namelen,sbi->s_namelen,name,de->name)) + goto out_unlock; + de = minix_next_entry(de, sbi); + } + unlock_page(page); + dir_put_page(page); + } + BUG(); + return -EINVAL; + +got_it: + from = (char*)de - (char*)page_address(page); + to = from + sbi->s_dirsize; + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + goto out_unlock; + memcpy (de->name, name, namelen); + memset (de->name + namelen, 0, sbi->s_dirsize - namelen - 2); + de->inode = inode->i_ino; + err = dir_commit_chunk(page, from, to); + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(dir); +out_put: + dir_put_page(page); +out: + return err; +out_unlock: + unlock_page(page); + goto out_put; +} + +int minix_delete_entry(struct minix_dir_entry *de, struct page *page) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = (struct inode*)mapping->host; + char *kaddr = page_address(page); + unsigned from = (char*)de - kaddr; + unsigned to = from + minix_sb(inode->i_sb)->s_dirsize; + int err; + + lock_page(page); + err = mapping->a_ops->prepare_write(NULL, page, from, to); + if (err == 0) { + de->inode = 0; + err = dir_commit_chunk(page, from, to); + } else { + unlock_page(page); + } + dir_put_page(page); + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + return err; +} + +int minix_make_empty(struct inode *inode, struct inode *dir) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page = grab_cache_page(mapping, 0); + struct minix_sb_info * sbi = minix_sb(inode->i_sb); + struct minix_dir_entry * de; + char *kaddr; + int err; + + if (!page) + return -ENOMEM; + err = mapping->a_ops->prepare_write(NULL, page, 0, 2 * sbi->s_dirsize); + if (err) { + unlock_page(page); + goto fail; + } + + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr, 0, PAGE_CACHE_SIZE); + + de = (struct minix_dir_entry *)kaddr; + de->inode = inode->i_ino; + strcpy(de->name,"."); + de = minix_next_entry(de, sbi); + de->inode = dir->i_ino; + strcpy(de->name,".."); + kunmap_atomic(kaddr, KM_USER0); + + err = dir_commit_chunk(page, 0, 2 * sbi->s_dirsize); +fail: + page_cache_release(page); + return err; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +int minix_empty_dir(struct inode * inode) +{ + struct page *page = NULL; + unsigned long i, npages = dir_pages(inode); + struct minix_sb_info *sbi = minix_sb(inode->i_sb); + + for (i = 0; i < npages; i++) { + char *kaddr; + minix_dirent * de; + page = dir_get_page(inode, i); + + if (IS_ERR(page)) + continue; + + kaddr = (char *)page_address(page); + de = (minix_dirent *)kaddr; + kaddr += minix_last_byte(inode, i) - sbi->s_dirsize; + + while ((char *)de <= kaddr) { + if (de->inode != 0) { + /* check for . and .. */ + if (de->name[0] != '.') + goto not_empty; + if (!de->name[1]) { + if (de->inode != inode->i_ino) + goto not_empty; + } else if (de->name[1] != '.') + goto not_empty; + else if (de->name[2]) + goto not_empty; + } + de = minix_next_entry(de, sbi); + } + dir_put_page(page); + } + return 1; + +not_empty: + dir_put_page(page); + return 0; +} + +/* Releases the page */ +void minix_set_link(struct minix_dir_entry *de, struct page *page, + struct inode *inode) +{ + struct inode *dir = (struct inode*)page->mapping->host; + struct minix_sb_info *sbi = minix_sb(dir->i_sb); + unsigned from = (char *)de-(char*)page_address(page); + unsigned to = from + sbi->s_dirsize; + int err; + + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err == 0) { + de->inode = inode->i_ino; + err = dir_commit_chunk(page, from, to); + } else { + unlock_page(page); + } + dir_put_page(page); + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(dir); +} + +struct minix_dir_entry * minix_dotdot (struct inode *dir, struct page **p) +{ + struct page *page = dir_get_page(dir, 0); + struct minix_sb_info *sbi = minix_sb(dir->i_sb); + struct minix_dir_entry *de = NULL; + + if (!IS_ERR(page)) { + de = minix_next_entry(page_address(page), sbi); + *p = page; + } + return de; +} + +ino_t minix_inode_by_name(struct dentry *dentry) +{ + struct page *page; + struct minix_dir_entry *de = minix_find_entry(dentry, &page); + ino_t res = 0; + + if (de) { + res = de->inode; + dir_put_page(page); + } + return res; +} diff --git a/fs/minix/file.c b/fs/minix/file.c new file mode 100644 index 00000000000..f1d77acb3f0 --- /dev/null +++ b/fs/minix/file.c @@ -0,0 +1,45 @@ +/* + * linux/fs/minix/file.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * minix regular file handling primitives + */ + +#include <linux/buffer_head.h> /* for fsync_inode_buffers() */ +#include "minix.h" + +/* + * We have mostly NULLs here: the current defaults are OK for + * the minix filesystem. + */ +int minix_sync_file(struct file *, struct dentry *, int); + +struct file_operations minix_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .fsync = minix_sync_file, + .sendfile = generic_file_sendfile, +}; + +struct inode_operations minix_file_inode_operations = { + .truncate = minix_truncate, + .getattr = minix_getattr, +}; + +int minix_sync_file(struct file * file, struct dentry *dentry, int datasync) +{ + struct inode *inode = dentry->d_inode; + int err; + + err = sync_mapping_buffers(inode->i_mapping); + if (!(inode->i_state & I_DIRTY)) + return err; + if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) + return err; + + err |= minix_sync_inode(inode); + return err ? -EIO : 0; +} diff --git a/fs/minix/inode.c b/fs/minix/inode.c new file mode 100644 index 00000000000..3f18c21198d --- /dev/null +++ b/fs/minix/inode.c @@ -0,0 +1,598 @@ +/* + * linux/fs/minix/inode.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl) + * Minix V2 fs support. + * + * Modified for 680x0 by Andreas Schwab + */ + +#include <linux/module.h> +#include "minix.h" +#include <linux/buffer_head.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/highuid.h> +#include <linux/vfs.h> + +static void minix_read_inode(struct inode * inode); +static int minix_write_inode(struct inode * inode, int wait); +static int minix_statfs(struct super_block *sb, struct kstatfs *buf); +static int minix_remount (struct super_block * sb, int * flags, char * data); + +static void minix_delete_inode(struct inode *inode) +{ + inode->i_size = 0; + minix_truncate(inode); + minix_free_inode(inode); +} + +static void minix_put_super(struct super_block *sb) +{ + int i; + struct minix_sb_info *sbi = minix_sb(sb); + + if (!(sb->s_flags & MS_RDONLY)) { + sbi->s_ms->s_state = sbi->s_mount_state; + mark_buffer_dirty(sbi->s_sbh); + } + for (i = 0; i < sbi->s_imap_blocks; i++) + brelse(sbi->s_imap[i]); + for (i = 0; i < sbi->s_zmap_blocks; i++) + brelse(sbi->s_zmap[i]); + brelse (sbi->s_sbh); + kfree(sbi->s_imap); + sb->s_fs_info = NULL; + kfree(sbi); + + return; +} + +static kmem_cache_t * minix_inode_cachep; + +static struct inode *minix_alloc_inode(struct super_block *sb) +{ + struct minix_inode_info *ei; + ei = (struct minix_inode_info *)kmem_cache_alloc(minix_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void minix_destroy_inode(struct inode *inode) +{ + kmem_cache_free(minix_inode_cachep, minix_i(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct minix_inode_info *ei = (struct minix_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + minix_inode_cachep = kmem_cache_create("minix_inode_cache", + sizeof(struct minix_inode_info), + 0, SLAB_RECLAIM_ACCOUNT, + init_once, NULL); + if (minix_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(minix_inode_cachep)) + printk(KERN_INFO "minix_inode_cache: not all structures were freed\n"); +} + +static struct super_operations minix_sops = { + .alloc_inode = minix_alloc_inode, + .destroy_inode = minix_destroy_inode, + .read_inode = minix_read_inode, + .write_inode = minix_write_inode, + .delete_inode = minix_delete_inode, + .put_super = minix_put_super, + .statfs = minix_statfs, + .remount_fs = minix_remount, +}; + +static int minix_remount (struct super_block * sb, int * flags, char * data) +{ + struct minix_sb_info * sbi = minix_sb(sb); + struct minix_super_block * ms; + + ms = sbi->s_ms; + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + if (*flags & MS_RDONLY) { + if (ms->s_state & MINIX_VALID_FS || + !(sbi->s_mount_state & MINIX_VALID_FS)) + return 0; + /* Mounting a rw partition read-only. */ + ms->s_state = sbi->s_mount_state; + mark_buffer_dirty(sbi->s_sbh); + } else { + /* Mount a partition which is read-only, read-write. */ + sbi->s_mount_state = ms->s_state; + ms->s_state &= ~MINIX_VALID_FS; + mark_buffer_dirty(sbi->s_sbh); + + if (!(sbi->s_mount_state & MINIX_VALID_FS)) + printk ("MINIX-fs warning: remounting unchecked fs, " + "running fsck is recommended.\n"); + else if ((sbi->s_mount_state & MINIX_ERROR_FS)) + printk ("MINIX-fs warning: remounting fs with errors, " + "running fsck is recommended.\n"); + } + return 0; +} + +static int minix_fill_super(struct super_block *s, void *data, int silent) +{ + struct buffer_head *bh; + struct buffer_head **map; + struct minix_super_block *ms; + int i, block; + struct inode *root_inode; + struct minix_sb_info *sbi; + + sbi = kmalloc(sizeof(struct minix_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + s->s_fs_info = sbi; + memset(sbi, 0, sizeof(struct minix_sb_info)); + + /* N.B. These should be compile-time tests. + Unfortunately that is impossible. */ + if (32 != sizeof (struct minix_inode)) + panic("bad V1 i-node size"); + if (64 != sizeof(struct minix2_inode)) + panic("bad V2 i-node size"); + + if (!sb_set_blocksize(s, BLOCK_SIZE)) + goto out_bad_hblock; + + if (!(bh = sb_bread(s, 1))) + goto out_bad_sb; + + ms = (struct minix_super_block *) bh->b_data; + sbi->s_ms = ms; + sbi->s_sbh = bh; + sbi->s_mount_state = ms->s_state; + sbi->s_ninodes = ms->s_ninodes; + sbi->s_nzones = ms->s_nzones; + sbi->s_imap_blocks = ms->s_imap_blocks; + sbi->s_zmap_blocks = ms->s_zmap_blocks; + sbi->s_firstdatazone = ms->s_firstdatazone; + sbi->s_log_zone_size = ms->s_log_zone_size; + sbi->s_max_size = ms->s_max_size; + s->s_magic = ms->s_magic; + if (s->s_magic == MINIX_SUPER_MAGIC) { + sbi->s_version = MINIX_V1; + sbi->s_dirsize = 16; + sbi->s_namelen = 14; + sbi->s_link_max = MINIX_LINK_MAX; + } else if (s->s_magic == MINIX_SUPER_MAGIC2) { + sbi->s_version = MINIX_V1; + sbi->s_dirsize = 32; + sbi->s_namelen = 30; + sbi->s_link_max = MINIX_LINK_MAX; + } else if (s->s_magic == MINIX2_SUPER_MAGIC) { + sbi->s_version = MINIX_V2; + sbi->s_nzones = ms->s_zones; + sbi->s_dirsize = 16; + sbi->s_namelen = 14; + sbi->s_link_max = MINIX2_LINK_MAX; + } else if (s->s_magic == MINIX2_SUPER_MAGIC2) { + sbi->s_version = MINIX_V2; + sbi->s_nzones = ms->s_zones; + sbi->s_dirsize = 32; + sbi->s_namelen = 30; + sbi->s_link_max = MINIX2_LINK_MAX; + } else + goto out_no_fs; + + /* + * Allocate the buffer map to keep the superblock small. + */ + i = (sbi->s_imap_blocks + sbi->s_zmap_blocks) * sizeof(bh); + map = kmalloc(i, GFP_KERNEL); + if (!map) + goto out_no_map; + memset(map, 0, i); + sbi->s_imap = &map[0]; + sbi->s_zmap = &map[sbi->s_imap_blocks]; + + block=2; + for (i=0 ; i < sbi->s_imap_blocks ; i++) { + if (!(sbi->s_imap[i]=sb_bread(s, block))) + goto out_no_bitmap; + block++; + } + for (i=0 ; i < sbi->s_zmap_blocks ; i++) { + if (!(sbi->s_zmap[i]=sb_bread(s, block))) + goto out_no_bitmap; + block++; + } + + minix_set_bit(0,sbi->s_imap[0]->b_data); + minix_set_bit(0,sbi->s_zmap[0]->b_data); + + /* set up enough so that it can read an inode */ + s->s_op = &minix_sops; + root_inode = iget(s, MINIX_ROOT_INO); + if (!root_inode || is_bad_inode(root_inode)) + goto out_no_root; + + s->s_root = d_alloc_root(root_inode); + if (!s->s_root) + goto out_iput; + + if (!NO_TRUNCATE) + s->s_root->d_op = &minix_dentry_operations; + + if (!(s->s_flags & MS_RDONLY)) { + ms->s_state &= ~MINIX_VALID_FS; + mark_buffer_dirty(bh); + } + if (!(sbi->s_mount_state & MINIX_VALID_FS)) + printk ("MINIX-fs: mounting unchecked file system, " + "running fsck is recommended.\n"); + else if (sbi->s_mount_state & MINIX_ERROR_FS) + printk ("MINIX-fs: mounting file system with errors, " + "running fsck is recommended.\n"); + return 0; + +out_iput: + iput(root_inode); + goto out_freemap; + +out_no_root: + if (!silent) + printk("MINIX-fs: get root inode failed\n"); + goto out_freemap; + +out_no_bitmap: + printk("MINIX-fs: bad superblock or unable to read bitmaps\n"); + out_freemap: + for (i = 0; i < sbi->s_imap_blocks; i++) + brelse(sbi->s_imap[i]); + for (i = 0; i < sbi->s_zmap_blocks; i++) + brelse(sbi->s_zmap[i]); + kfree(sbi->s_imap); + goto out_release; + +out_no_map: + if (!silent) + printk ("MINIX-fs: can't allocate map\n"); + goto out_release; + +out_no_fs: + if (!silent) + printk("VFS: Can't find a Minix or Minix V2 filesystem on device " + "%s.\n", s->s_id); + out_release: + brelse(bh); + goto out; + +out_bad_hblock: + printk("MINIX-fs: blocksize too small for device.\n"); + goto out; + +out_bad_sb: + printk("MINIX-fs: unable to read superblock\n"); + out: + s->s_fs_info = NULL; + kfree(sbi); + return -EINVAL; +} + +static int minix_statfs(struct super_block *sb, struct kstatfs *buf) +{ + struct minix_sb_info *sbi = minix_sb(sb); + buf->f_type = sb->s_magic; + buf->f_bsize = sb->s_blocksize; + buf->f_blocks = (sbi->s_nzones - sbi->s_firstdatazone) << sbi->s_log_zone_size; + buf->f_bfree = minix_count_free_blocks(sbi); + buf->f_bavail = buf->f_bfree; + buf->f_files = sbi->s_ninodes; + buf->f_ffree = minix_count_free_inodes(sbi); + buf->f_namelen = sbi->s_namelen; + return 0; +} + +static int minix_get_block(struct inode *inode, sector_t block, + struct buffer_head *bh_result, int create) +{ + if (INODE_VERSION(inode) == MINIX_V1) + return V1_minix_get_block(inode, block, bh_result, create); + else + return V2_minix_get_block(inode, block, bh_result, create); +} + +static int minix_writepage(struct page *page, struct writeback_control *wbc) +{ + return block_write_full_page(page, minix_get_block, wbc); +} +static int minix_readpage(struct file *file, struct page *page) +{ + return block_read_full_page(page,minix_get_block); +} +static int minix_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,minix_get_block); +} +static sector_t minix_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping,block,minix_get_block); +} +static struct address_space_operations minix_aops = { + .readpage = minix_readpage, + .writepage = minix_writepage, + .sync_page = block_sync_page, + .prepare_write = minix_prepare_write, + .commit_write = generic_commit_write, + .bmap = minix_bmap +}; + +static struct inode_operations minix_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = page_follow_link_light, + .put_link = page_put_link, + .getattr = minix_getattr, +}; + +void minix_set_inode(struct inode *inode, dev_t rdev) +{ + if (S_ISREG(inode->i_mode)) { + inode->i_op = &minix_file_inode_operations; + inode->i_fop = &minix_file_operations; + inode->i_mapping->a_ops = &minix_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &minix_dir_inode_operations; + inode->i_fop = &minix_dir_operations; + inode->i_mapping->a_ops = &minix_aops; + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &minix_symlink_inode_operations; + inode->i_mapping->a_ops = &minix_aops; + } else + init_special_inode(inode, inode->i_mode, rdev); +} + +/* + * The minix V1 function to read an inode. + */ +static void V1_minix_read_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct minix_inode * raw_inode; + struct minix_inode_info *minix_inode = minix_i(inode); + int i; + + raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh); + if (!raw_inode) { + make_bad_inode(inode); + return; + } + inode->i_mode = raw_inode->i_mode; + inode->i_uid = (uid_t)raw_inode->i_uid; + inode->i_gid = (gid_t)raw_inode->i_gid; + inode->i_nlink = raw_inode->i_nlinks; + inode->i_size = raw_inode->i_size; + inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = raw_inode->i_time; + inode->i_mtime.tv_nsec = 0; + inode->i_atime.tv_nsec = 0; + inode->i_ctime.tv_nsec = 0; + inode->i_blocks = inode->i_blksize = 0; + for (i = 0; i < 9; i++) + minix_inode->u.i1_data[i] = raw_inode->i_zone[i]; + minix_set_inode(inode, old_decode_dev(raw_inode->i_zone[0])); + brelse(bh); +} + +/* + * The minix V2 function to read an inode. + */ +static void V2_minix_read_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct minix2_inode * raw_inode; + struct minix_inode_info *minix_inode = minix_i(inode); + int i; + + raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh); + if (!raw_inode) { + make_bad_inode(inode); + return; + } + inode->i_mode = raw_inode->i_mode; + inode->i_uid = (uid_t)raw_inode->i_uid; + inode->i_gid = (gid_t)raw_inode->i_gid; + inode->i_nlink = raw_inode->i_nlinks; + inode->i_size = raw_inode->i_size; + inode->i_mtime.tv_sec = raw_inode->i_mtime; + inode->i_atime.tv_sec = raw_inode->i_atime; + inode->i_ctime.tv_sec = raw_inode->i_ctime; + inode->i_mtime.tv_nsec = 0; + inode->i_atime.tv_nsec = 0; + inode->i_ctime.tv_nsec = 0; + inode->i_blocks = inode->i_blksize = 0; + for (i = 0; i < 10; i++) + minix_inode->u.i2_data[i] = raw_inode->i_zone[i]; + minix_set_inode(inode, old_decode_dev(raw_inode->i_zone[0])); + brelse(bh); +} + +/* + * The global function to read an inode. + */ +static void minix_read_inode(struct inode * inode) +{ + if (INODE_VERSION(inode) == MINIX_V1) + V1_minix_read_inode(inode); + else + V2_minix_read_inode(inode); +} + +/* + * The minix V1 function to synchronize an inode. + */ +static struct buffer_head * V1_minix_update_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct minix_inode * raw_inode; + struct minix_inode_info *minix_inode = minix_i(inode); + int i; + + raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh); + if (!raw_inode) + return NULL; + raw_inode->i_mode = inode->i_mode; + raw_inode->i_uid = fs_high2lowuid(inode->i_uid); + raw_inode->i_gid = fs_high2lowgid(inode->i_gid); + raw_inode->i_nlinks = inode->i_nlink; + raw_inode->i_size = inode->i_size; + raw_inode->i_time = inode->i_mtime.tv_sec; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + raw_inode->i_zone[0] = old_encode_dev(inode->i_rdev); + else for (i = 0; i < 9; i++) + raw_inode->i_zone[i] = minix_inode->u.i1_data[i]; + mark_buffer_dirty(bh); + return bh; +} + +/* + * The minix V2 function to synchronize an inode. + */ +static struct buffer_head * V2_minix_update_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct minix2_inode * raw_inode; + struct minix_inode_info *minix_inode = minix_i(inode); + int i; + + raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh); + if (!raw_inode) + return NULL; + raw_inode->i_mode = inode->i_mode; + raw_inode->i_uid = fs_high2lowuid(inode->i_uid); + raw_inode->i_gid = fs_high2lowgid(inode->i_gid); + raw_inode->i_nlinks = inode->i_nlink; + raw_inode->i_size = inode->i_size; + raw_inode->i_mtime = inode->i_mtime.tv_sec; + raw_inode->i_atime = inode->i_atime.tv_sec; + raw_inode->i_ctime = inode->i_ctime.tv_sec; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + raw_inode->i_zone[0] = old_encode_dev(inode->i_rdev); + else for (i = 0; i < 10; i++) + raw_inode->i_zone[i] = minix_inode->u.i2_data[i]; + mark_buffer_dirty(bh); + return bh; +} + +static struct buffer_head *minix_update_inode(struct inode *inode) +{ + if (INODE_VERSION(inode) == MINIX_V1) + return V1_minix_update_inode(inode); + else + return V2_minix_update_inode(inode); +} + +static int minix_write_inode(struct inode * inode, int wait) +{ + brelse(minix_update_inode(inode)); + return 0; +} + +int minix_sync_inode(struct inode * inode) +{ + int err = 0; + struct buffer_head *bh; + + bh = minix_update_inode(inode); + if (bh && buffer_dirty(bh)) + { + sync_dirty_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + { + printk ("IO error syncing minix inode [%s:%08lx]\n", + inode->i_sb->s_id, inode->i_ino); + err = -1; + } + } + else if (!bh) + err = -1; + brelse (bh); + return err; +} + +int minix_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + generic_fillattr(dentry->d_inode, stat); + if (INODE_VERSION(dentry->d_inode) == MINIX_V1) + stat->blocks = (BLOCK_SIZE / 512) * V1_minix_blocks(stat->size); + else + stat->blocks = (BLOCK_SIZE / 512) * V2_minix_blocks(stat->size); + stat->blksize = BLOCK_SIZE; + return 0; +} + +/* + * The function that is called for file truncation. + */ +void minix_truncate(struct inode * inode) +{ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) + return; + if (INODE_VERSION(inode) == MINIX_V1) + V1_minix_truncate(inode); + else + V2_minix_truncate(inode); +} + +static struct super_block *minix_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, minix_fill_super); +} + +static struct file_system_type minix_fs_type = { + .owner = THIS_MODULE, + .name = "minix", + .get_sb = minix_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_minix_fs(void) +{ + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&minix_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; +} + +static void __exit exit_minix_fs(void) +{ + unregister_filesystem(&minix_fs_type); + destroy_inodecache(); +} + +module_init(init_minix_fs) +module_exit(exit_minix_fs) +MODULE_LICENSE("GPL"); + diff --git a/fs/minix/itree_common.c b/fs/minix/itree_common.c new file mode 100644 index 00000000000..429baf8de10 --- /dev/null +++ b/fs/minix/itree_common.c @@ -0,0 +1,362 @@ +/* Generic part */ + +typedef struct { + block_t *p; + block_t key; + struct buffer_head *bh; +} Indirect; + +static DEFINE_RWLOCK(pointers_lock); + +static inline void add_chain(Indirect *p, struct buffer_head *bh, block_t *v) +{ + p->key = *(p->p = v); + p->bh = bh; +} + +static inline int verify_chain(Indirect *from, Indirect *to) +{ + while (from <= to && from->key == *from->p) + from++; + return (from > to); +} + +static inline block_t *block_end(struct buffer_head *bh) +{ + return (block_t *)((char*)bh->b_data + BLOCK_SIZE); +} + +static inline Indirect *get_branch(struct inode *inode, + int depth, + int *offsets, + Indirect chain[DEPTH], + int *err) +{ + struct super_block *sb = inode->i_sb; + Indirect *p = chain; + struct buffer_head *bh; + + *err = 0; + /* i_data is not going away, no lock needed */ + add_chain (chain, NULL, i_data(inode) + *offsets); + if (!p->key) + goto no_block; + while (--depth) { + bh = sb_bread(sb, block_to_cpu(p->key)); + if (!bh) + goto failure; + read_lock(&pointers_lock); + if (!verify_chain(chain, p)) + goto changed; + add_chain(++p, bh, (block_t *)bh->b_data + *++offsets); + read_unlock(&pointers_lock); + if (!p->key) + goto no_block; + } + return NULL; + +changed: + read_unlock(&pointers_lock); + brelse(bh); + *err = -EAGAIN; + goto no_block; +failure: + *err = -EIO; +no_block: + return p; +} + +static int alloc_branch(struct inode *inode, + int num, + int *offsets, + Indirect *branch) +{ + int n = 0; + int i; + int parent = minix_new_block(inode); + + branch[0].key = cpu_to_block(parent); + if (parent) for (n = 1; n < num; n++) { + struct buffer_head *bh; + /* Allocate the next block */ + int nr = minix_new_block(inode); + if (!nr) + break; + branch[n].key = cpu_to_block(nr); + bh = sb_getblk(inode->i_sb, parent); + lock_buffer(bh); + memset(bh->b_data, 0, BLOCK_SIZE); + branch[n].bh = bh; + branch[n].p = (block_t*) bh->b_data + offsets[n]; + *branch[n].p = branch[n].key; + set_buffer_uptodate(bh); + unlock_buffer(bh); + mark_buffer_dirty_inode(bh, inode); + parent = nr; + } + if (n == num) + return 0; + + /* Allocation failed, free what we already allocated */ + for (i = 1; i < n; i++) + bforget(branch[i].bh); + for (i = 0; i < n; i++) + minix_free_block(inode, block_to_cpu(branch[i].key)); + return -ENOSPC; +} + +static inline int splice_branch(struct inode *inode, + Indirect chain[DEPTH], + Indirect *where, + int num) +{ + int i; + + write_lock(&pointers_lock); + + /* Verify that place we are splicing to is still there and vacant */ + if (!verify_chain(chain, where-1) || *where->p) + goto changed; + + *where->p = where->key; + + write_unlock(&pointers_lock); + + /* We are done with atomic stuff, now do the rest of housekeeping */ + + inode->i_ctime = CURRENT_TIME_SEC; + + /* had we spliced it onto indirect block? */ + if (where->bh) + mark_buffer_dirty_inode(where->bh, inode); + + mark_inode_dirty(inode); + return 0; + +changed: + write_unlock(&pointers_lock); + for (i = 1; i < num; i++) + bforget(where[i].bh); + for (i = 0; i < num; i++) + minix_free_block(inode, block_to_cpu(where[i].key)); + return -EAGAIN; +} + +static inline int get_block(struct inode * inode, sector_t block, + struct buffer_head *bh, int create) +{ + int err = -EIO; + int offsets[DEPTH]; + Indirect chain[DEPTH]; + Indirect *partial; + int left; + int depth = block_to_path(inode, block, offsets); + + if (depth == 0) + goto out; + +reread: + partial = get_branch(inode, depth, offsets, chain, &err); + + /* Simplest case - block found, no allocation needed */ + if (!partial) { +got_it: + map_bh(bh, inode->i_sb, block_to_cpu(chain[depth-1].key)); + /* Clean up and exit */ + partial = chain+depth-1; /* the whole chain */ + goto cleanup; + } + + /* Next simple case - plain lookup or failed read of indirect block */ + if (!create || err == -EIO) { +cleanup: + while (partial > chain) { + brelse(partial->bh); + partial--; + } +out: + return err; + } + + /* + * Indirect block might be removed by truncate while we were + * reading it. Handling of that case (forget what we've got and + * reread) is taken out of the main path. + */ + if (err == -EAGAIN) + goto changed; + + left = (chain + depth) - partial; + err = alloc_branch(inode, left, offsets+(partial-chain), partial); + if (err) + goto cleanup; + + if (splice_branch(inode, chain, partial, left) < 0) + goto changed; + + set_buffer_new(bh); + goto got_it; + +changed: + while (partial > chain) { + brelse(partial->bh); + partial--; + } + goto reread; +} + +static inline int all_zeroes(block_t *p, block_t *q) +{ + while (p < q) + if (*p++) + return 0; + return 1; +} + +static Indirect *find_shared(struct inode *inode, + int depth, + int offsets[DEPTH], + Indirect chain[DEPTH], + block_t *top) +{ + Indirect *partial, *p; + int k, err; + + *top = 0; + for (k = depth; k > 1 && !offsets[k-1]; k--) + ; + partial = get_branch(inode, k, offsets, chain, &err); + + write_lock(&pointers_lock); + if (!partial) + partial = chain + k-1; + if (!partial->key && *partial->p) { + write_unlock(&pointers_lock); + goto no_top; + } + for (p=partial;p>chain && all_zeroes((block_t*)p->bh->b_data,p->p);p--) + ; + if (p == chain + k - 1 && p > chain) { + p->p--; + } else { + *top = *p->p; + *p->p = 0; + } + write_unlock(&pointers_lock); + + while(partial > p) + { + brelse(partial->bh); + partial--; + } +no_top: + return partial; +} + +static inline void free_data(struct inode *inode, block_t *p, block_t *q) +{ + unsigned long nr; + + for ( ; p < q ; p++) { + nr = block_to_cpu(*p); + if (nr) { + *p = 0; + minix_free_block(inode, nr); + } + } +} + +static void free_branches(struct inode *inode, block_t *p, block_t *q, int depth) +{ + struct buffer_head * bh; + unsigned long nr; + + if (depth--) { + for ( ; p < q ; p++) { + nr = block_to_cpu(*p); + if (!nr) + continue; + *p = 0; + bh = sb_bread(inode->i_sb, nr); + if (!bh) + continue; + free_branches(inode, (block_t*)bh->b_data, + block_end(bh), depth); + bforget(bh); + minix_free_block(inode, nr); + mark_inode_dirty(inode); + } + } else + free_data(inode, p, q); +} + +static inline void truncate (struct inode * inode) +{ + block_t *idata = i_data(inode); + int offsets[DEPTH]; + Indirect chain[DEPTH]; + Indirect *partial; + block_t nr = 0; + int n; + int first_whole; + long iblock; + + iblock = (inode->i_size + BLOCK_SIZE-1) >> 10; + block_truncate_page(inode->i_mapping, inode->i_size, get_block); + + n = block_to_path(inode, iblock, offsets); + if (!n) + return; + + if (n == 1) { + free_data(inode, idata+offsets[0], idata + DIRECT); + first_whole = 0; + goto do_indirects; + } + + first_whole = offsets[0] + 1 - DIRECT; + partial = find_shared(inode, n, offsets, chain, &nr); + if (nr) { + if (partial == chain) + mark_inode_dirty(inode); + else + mark_buffer_dirty_inode(partial->bh, inode); + free_branches(inode, &nr, &nr+1, (chain+n-1) - partial); + } + /* Clear the ends of indirect blocks on the shared branch */ + while (partial > chain) { + free_branches(inode, partial->p + 1, block_end(partial->bh), + (chain+n-1) - partial); + mark_buffer_dirty_inode(partial->bh, inode); + brelse (partial->bh); + partial--; + } +do_indirects: + /* Kill the remaining (whole) subtrees */ + while (first_whole < DEPTH-1) { + nr = idata[DIRECT+first_whole]; + if (nr) { + idata[DIRECT+first_whole] = 0; + mark_inode_dirty(inode); + free_branches(inode, &nr, &nr+1, first_whole+1); + } + first_whole++; + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); +} + +static inline unsigned nblocks(loff_t size) +{ + unsigned blocks, res, direct = DIRECT, i = DEPTH; + blocks = (size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS; + res = blocks; + while (--i && blocks > direct) { + blocks -= direct; + blocks += BLOCK_SIZE/sizeof(block_t) - 1; + blocks /= BLOCK_SIZE/sizeof(block_t); + res += blocks; + direct = 1; + } + return res; +} diff --git a/fs/minix/itree_v1.c b/fs/minix/itree_v1.c new file mode 100644 index 00000000000..ba06aef4aca --- /dev/null +++ b/fs/minix/itree_v1.c @@ -0,0 +1,61 @@ +#include <linux/buffer_head.h> +#include "minix.h" + +enum {DEPTH = 3, DIRECT = 7}; /* Only double indirect */ + +typedef u16 block_t; /* 16 bit, host order */ + +static inline unsigned long block_to_cpu(block_t n) +{ + return n; +} + +static inline block_t cpu_to_block(unsigned long n) +{ + return n; +} + +static inline block_t *i_data(struct inode *inode) +{ + return (block_t *)minix_i(inode)->u.i1_data; +} + +static int block_to_path(struct inode * inode, long block, int offsets[DEPTH]) +{ + int n = 0; + + if (block < 0) { + printk("minix_bmap: block<0"); + } else if (block >= (minix_sb(inode->i_sb)->s_max_size/BLOCK_SIZE)) { + printk("minix_bmap: block>big"); + } else if (block < 7) { + offsets[n++] = block; + } else if ((block -= 7) < 512) { + offsets[n++] = 7; + offsets[n++] = block; + } else { + block -= 512; + offsets[n++] = 8; + offsets[n++] = block>>9; + offsets[n++] = block & 511; + } + return n; +} + +#include "itree_common.c" + +int V1_minix_get_block(struct inode * inode, long block, + struct buffer_head *bh_result, int create) +{ + return get_block(inode, block, bh_result, create); +} + +void V1_minix_truncate(struct inode * inode) +{ + truncate(inode); +} + +unsigned V1_minix_blocks(loff_t size) +{ + return nblocks(size); +} diff --git a/fs/minix/itree_v2.c b/fs/minix/itree_v2.c new file mode 100644 index 00000000000..3adc7675560 --- /dev/null +++ b/fs/minix/itree_v2.c @@ -0,0 +1,66 @@ +#include <linux/buffer_head.h> +#include "minix.h" + +enum {DIRECT = 7, DEPTH = 4}; /* Have triple indirect */ + +typedef u32 block_t; /* 32 bit, host order */ + +static inline unsigned long block_to_cpu(block_t n) +{ + return n; +} + +static inline block_t cpu_to_block(unsigned long n) +{ + return n; +} + +static inline block_t *i_data(struct inode *inode) +{ + return (block_t *)minix_i(inode)->u.i2_data; +} + +static int block_to_path(struct inode * inode, long block, int offsets[DEPTH]) +{ + int n = 0; + + if (block < 0) { + printk("minix_bmap: block<0"); + } else if (block >= (minix_sb(inode->i_sb)->s_max_size/BLOCK_SIZE)) { + printk("minix_bmap: block>big"); + } else if (block < 7) { + offsets[n++] = block; + } else if ((block -= 7) < 256) { + offsets[n++] = 7; + offsets[n++] = block; + } else if ((block -= 256) < 256*256) { + offsets[n++] = 8; + offsets[n++] = block>>8; + offsets[n++] = block & 255; + } else { + block -= 256*256; + offsets[n++] = 9; + offsets[n++] = block>>16; + offsets[n++] = (block>>8) & 255; + offsets[n++] = block & 255; + } + return n; +} + +#include "itree_common.c" + +int V2_minix_get_block(struct inode * inode, long block, + struct buffer_head *bh_result, int create) +{ + return get_block(inode, block, bh_result, create); +} + +void V2_minix_truncate(struct inode * inode) +{ + truncate(inode); +} + +unsigned V2_minix_blocks(loff_t size) +{ + return nblocks(size); +} diff --git a/fs/minix/minix.h b/fs/minix/minix.h new file mode 100644 index 00000000000..e42a8bb8900 --- /dev/null +++ b/fs/minix/minix.h @@ -0,0 +1,96 @@ +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/minix_fs.h> + +/* + * change the define below to 0 if you want names > info->s_namelen chars to be + * truncated. Else they will be disallowed (ENAMETOOLONG). + */ +#define NO_TRUNCATE 1 + +#define INODE_VERSION(inode) minix_sb(inode->i_sb)->s_version + +#define MINIX_V1 0x0001 /* original minix fs */ +#define MINIX_V2 0x0002 /* minix V2 fs */ + +/* + * minix fs inode data in memory + */ +struct minix_inode_info { + union { + __u16 i1_data[16]; + __u32 i2_data[16]; + } u; + struct inode vfs_inode; +}; + +/* + * minix super-block data in memory + */ +struct minix_sb_info { + unsigned long s_ninodes; + unsigned long s_nzones; + unsigned long s_imap_blocks; + unsigned long s_zmap_blocks; + unsigned long s_firstdatazone; + unsigned long s_log_zone_size; + unsigned long s_max_size; + int s_dirsize; + int s_namelen; + int s_link_max; + struct buffer_head ** s_imap; + struct buffer_head ** s_zmap; + struct buffer_head * s_sbh; + struct minix_super_block * s_ms; + unsigned short s_mount_state; + unsigned short s_version; +}; + +extern struct minix_inode * minix_V1_raw_inode(struct super_block *, ino_t, struct buffer_head **); +extern struct minix2_inode * minix_V2_raw_inode(struct super_block *, ino_t, struct buffer_head **); +extern struct inode * minix_new_inode(const struct inode * dir, int * error); +extern void minix_free_inode(struct inode * inode); +extern unsigned long minix_count_free_inodes(struct minix_sb_info *sbi); +extern int minix_new_block(struct inode * inode); +extern void minix_free_block(struct inode * inode, int block); +extern unsigned long minix_count_free_blocks(struct minix_sb_info *sbi); + +extern int minix_getattr(struct vfsmount *, struct dentry *, struct kstat *); + +extern void V2_minix_truncate(struct inode *); +extern void V1_minix_truncate(struct inode *); +extern void V2_minix_truncate(struct inode *); +extern void minix_truncate(struct inode *); +extern int minix_sync_inode(struct inode *); +extern void minix_set_inode(struct inode *, dev_t); +extern int V1_minix_get_block(struct inode *, long, struct buffer_head *, int); +extern int V2_minix_get_block(struct inode *, long, struct buffer_head *, int); +extern unsigned V1_minix_blocks(loff_t); +extern unsigned V2_minix_blocks(loff_t); + +extern struct minix_dir_entry *minix_find_entry(struct dentry*, struct page**); +extern int minix_add_link(struct dentry*, struct inode*); +extern int minix_delete_entry(struct minix_dir_entry*, struct page*); +extern int minix_make_empty(struct inode*, struct inode*); +extern int minix_empty_dir(struct inode*); +extern void minix_set_link(struct minix_dir_entry*, struct page*, struct inode*); +extern struct minix_dir_entry *minix_dotdot(struct inode*, struct page**); +extern ino_t minix_inode_by_name(struct dentry*); + +extern int minix_sync_file(struct file *, struct dentry *, int); + +extern struct inode_operations minix_file_inode_operations; +extern struct inode_operations minix_dir_inode_operations; +extern struct file_operations minix_file_operations; +extern struct file_operations minix_dir_operations; +extern struct dentry_operations minix_dentry_operations; + +static inline struct minix_sb_info *minix_sb(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct minix_inode_info *minix_i(struct inode *inode) +{ + return list_entry(inode, struct minix_inode_info, vfs_inode); +} diff --git a/fs/minix/namei.c b/fs/minix/namei.c new file mode 100644 index 00000000000..b25bca5bdb5 --- /dev/null +++ b/fs/minix/namei.c @@ -0,0 +1,317 @@ +/* + * linux/fs/minix/namei.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include "minix.h" + +static inline void inc_count(struct inode *inode) +{ + inode->i_nlink++; + mark_inode_dirty(inode); +} + +static inline void dec_count(struct inode *inode) +{ + inode->i_nlink--; + mark_inode_dirty(inode); +} + +static int add_nondir(struct dentry *dentry, struct inode *inode) +{ + int err = minix_add_link(dentry, inode); + if (!err) { + d_instantiate(dentry, inode); + return 0; + } + dec_count(inode); + iput(inode); + return err; +} + +static int minix_hash(struct dentry *dentry, struct qstr *qstr) +{ + unsigned long hash; + int i; + const unsigned char *name; + + i = minix_sb(dentry->d_inode->i_sb)->s_namelen; + if (i >= qstr->len) + return 0; + /* Truncate the name in place, avoids having to define a compare + function. */ + qstr->len = i; + name = qstr->name; + hash = init_name_hash(); + while (i--) + hash = partial_name_hash(*name++, hash); + qstr->hash = end_name_hash(hash); + return 0; +} + +struct dentry_operations minix_dentry_operations = { + .d_hash = minix_hash, +}; + +static struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) +{ + struct inode * inode = NULL; + ino_t ino; + + dentry->d_op = dir->i_sb->s_root->d_op; + + if (dentry->d_name.len > minix_sb(dir->i_sb)->s_namelen) + return ERR_PTR(-ENAMETOOLONG); + + ino = minix_inode_by_name(dentry); + if (ino) { + inode = iget(dir->i_sb, ino); + + if (!inode) + return ERR_PTR(-EACCES); + } + d_add(dentry, inode); + return NULL; +} + +static int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, dev_t rdev) +{ + int error; + struct inode *inode; + + if (!old_valid_dev(rdev)) + return -EINVAL; + + inode = minix_new_inode(dir, &error); + + if (inode) { + inode->i_mode = mode; + minix_set_inode(inode, rdev); + mark_inode_dirty(inode); + error = add_nondir(dentry, inode); + } + return error; +} + +static int minix_create(struct inode * dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + return minix_mknod(dir, dentry, mode, 0); +} + +static int minix_symlink(struct inode * dir, struct dentry *dentry, + const char * symname) +{ + int err = -ENAMETOOLONG; + int i = strlen(symname)+1; + struct inode * inode; + + if (i > dir->i_sb->s_blocksize) + goto out; + + inode = minix_new_inode(dir, &err); + if (!inode) + goto out; + + inode->i_mode = S_IFLNK | 0777; + minix_set_inode(inode, 0); + err = page_symlink(inode, symname, i); + if (err) + goto out_fail; + + err = add_nondir(dentry, inode); +out: + return err; + +out_fail: + dec_count(inode); + iput(inode); + goto out; +} + +static int minix_link(struct dentry * old_dentry, struct inode * dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + + if (inode->i_nlink >= minix_sb(inode->i_sb)->s_link_max) + return -EMLINK; + + inode->i_ctime = CURRENT_TIME_SEC; + inc_count(inode); + atomic_inc(&inode->i_count); + return add_nondir(dentry, inode); +} + +static int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) +{ + struct inode * inode; + int err = -EMLINK; + + if (dir->i_nlink >= minix_sb(dir->i_sb)->s_link_max) + goto out; + + inc_count(dir); + + inode = minix_new_inode(dir, &err); + if (!inode) + goto out_dir; + + inode->i_mode = S_IFDIR | mode; + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + minix_set_inode(inode, 0); + + inc_count(inode); + + err = minix_make_empty(inode, dir); + if (err) + goto out_fail; + + err = minix_add_link(dentry, inode); + if (err) + goto out_fail; + + d_instantiate(dentry, inode); +out: + return err; + +out_fail: + dec_count(inode); + dec_count(inode); + iput(inode); +out_dir: + dec_count(dir); + goto out; +} + +static int minix_unlink(struct inode * dir, struct dentry *dentry) +{ + int err = -ENOENT; + struct inode * inode = dentry->d_inode; + struct page * page; + struct minix_dir_entry * de; + + de = minix_find_entry(dentry, &page); + if (!de) + goto end_unlink; + + err = minix_delete_entry(de, page); + if (err) + goto end_unlink; + + inode->i_ctime = dir->i_ctime; + dec_count(inode); +end_unlink: + return err; +} + +static int minix_rmdir(struct inode * dir, struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + int err = -ENOTEMPTY; + + if (minix_empty_dir(inode)) { + err = minix_unlink(dir, dentry); + if (!err) { + dec_count(dir); + dec_count(inode); + } + } + return err; +} + +static int minix_rename(struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) +{ + struct minix_sb_info * info = minix_sb(old_dir->i_sb); + struct inode * old_inode = old_dentry->d_inode; + struct inode * new_inode = new_dentry->d_inode; + struct page * dir_page = NULL; + struct minix_dir_entry * dir_de = NULL; + struct page * old_page; + struct minix_dir_entry * old_de; + int err = -ENOENT; + + old_de = minix_find_entry(old_dentry, &old_page); + if (!old_de) + goto out; + + if (S_ISDIR(old_inode->i_mode)) { + err = -EIO; + dir_de = minix_dotdot(old_inode, &dir_page); + if (!dir_de) + goto out_old; + } + + if (new_inode) { + struct page * new_page; + struct minix_dir_entry * new_de; + + err = -ENOTEMPTY; + if (dir_de && !minix_empty_dir(new_inode)) + goto out_dir; + + err = -ENOENT; + new_de = minix_find_entry(new_dentry, &new_page); + if (!new_de) + goto out_dir; + inc_count(old_inode); + minix_set_link(new_de, new_page, old_inode); + new_inode->i_ctime = CURRENT_TIME_SEC; + if (dir_de) + new_inode->i_nlink--; + dec_count(new_inode); + } else { + if (dir_de) { + err = -EMLINK; + if (new_dir->i_nlink >= info->s_link_max) + goto out_dir; + } + inc_count(old_inode); + err = minix_add_link(new_dentry, old_inode); + if (err) { + dec_count(old_inode); + goto out_dir; + } + if (dir_de) + inc_count(new_dir); + } + + minix_delete_entry(old_de, old_page); + dec_count(old_inode); + + if (dir_de) { + minix_set_link(dir_de, dir_page, new_dir); + dec_count(old_dir); + } + return 0; + +out_dir: + if (dir_de) { + kunmap(dir_page); + page_cache_release(dir_page); + } +out_old: + kunmap(old_page); + page_cache_release(old_page); +out: + return err; +} + +/* + * directories can handle most operations... + */ +struct inode_operations minix_dir_inode_operations = { + .create = minix_create, + .lookup = minix_lookup, + .link = minix_link, + .unlink = minix_unlink, + .symlink = minix_symlink, + .mkdir = minix_mkdir, + .rmdir = minix_rmdir, + .mknod = minix_mknod, + .rename = minix_rename, + .getattr = minix_getattr, +}; |