Initial commit
This commit is contained in:
commit
169c65d57e
51358 changed files with 23120455 additions and 0 deletions
25
fs/minix/Kconfig
Normal file
25
fs/minix/Kconfig
Normal file
|
@ -0,0 +1,25 @@
|
|||
config MINIX_FS
|
||||
tristate "Minix file system support"
|
||||
depends on BLOCK
|
||||
help
|
||||
Minix is a simple operating system used in many classes about OS's.
|
||||
The minix file system (method to organize files on a hard disk
|
||||
partition or a floppy disk) was the original file system for Linux,
|
||||
but has been superseded by the second extended file system ext2fs.
|
||||
You don't want to use the minix file system on your hard disk
|
||||
because of certain built-in restrictions, but it is sometimes found
|
||||
on older Linux floppy disks. This option will enlarge your kernel
|
||||
by about 28 KB. If unsure, say N.
|
||||
|
||||
To compile this file system support as a module, choose M here: the
|
||||
module will be called minix. Note that the file system of your root
|
||||
partition (the one containing the directory /) cannot be compiled as
|
||||
a module.
|
||||
|
||||
config MINIX_FS_NATIVE_ENDIAN
|
||||
def_bool MINIX_FS
|
||||
depends on H8300 || M32R || MICROBLAZE || MIPS || S390 || SUPERH || SPARC || XTENSA || (M68K && !MMU)
|
||||
|
||||
config MINIX_FS_BIG_ENDIAN_16BIT_INDEXED
|
||||
def_bool MINIX_FS
|
||||
depends on M68K && MMU
|
7
fs/minix/Makefile
Normal file
7
fs/minix/Makefile
Normal file
|
@ -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
|
272
fs/minix/bitmap.c
Normal file
272
fs/minix/bitmap.c
Normal file
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* 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/buffer_head.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
static DEFINE_SPINLOCK(bitmap_lock);
|
||||
|
||||
/*
|
||||
* bitmap consists of blocks filled with 16bit words
|
||||
* bit set == busy, bit clear == free
|
||||
* endianness is a mess, but for counting zero bits it really doesn't matter...
|
||||
*/
|
||||
static __u32 count_free(struct buffer_head *map[], unsigned blocksize, __u32 numbits)
|
||||
{
|
||||
__u32 sum = 0;
|
||||
unsigned blocks = DIV_ROUND_UP(numbits, blocksize * 8);
|
||||
|
||||
while (blocks--) {
|
||||
unsigned words = blocksize / 2;
|
||||
__u16 *p = (__u16 *)(*map++)->b_data;
|
||||
while (words--)
|
||||
sum += 16 - hweight16(*p++);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
void minix_free_block(struct inode *inode, unsigned long block)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct minix_sb_info *sbi = minix_sb(sb);
|
||||
struct buffer_head *bh;
|
||||
int k = sb->s_blocksize_bits + 3;
|
||||
unsigned long 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 & ((1<<k) - 1);
|
||||
zone >>= k;
|
||||
if (zone >= sbi->s_zmap_blocks) {
|
||||
printk("minix_free_block: nonexistent bitmap buffer\n");
|
||||
return;
|
||||
}
|
||||
bh = sbi->s_zmap[zone];
|
||||
spin_lock(&bitmap_lock);
|
||||
if (!minix_test_and_clear_bit(bit, bh->b_data))
|
||||
printk("minix_free_block (%s:%lu): bit already cleared\n",
|
||||
sb->s_id, block);
|
||||
spin_unlock(&bitmap_lock);
|
||||
mark_buffer_dirty(bh);
|
||||
return;
|
||||
}
|
||||
|
||||
int minix_new_block(struct inode * inode)
|
||||
{
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
int bits_per_zone = 8 * inode->i_sb->s_blocksize;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sbi->s_zmap_blocks; i++) {
|
||||
struct buffer_head *bh = sbi->s_zmap[i];
|
||||
int j;
|
||||
|
||||
spin_lock(&bitmap_lock);
|
||||
j = minix_find_first_zero_bit(bh->b_data, bits_per_zone);
|
||||
if (j < bits_per_zone) {
|
||||
minix_set_bit(j, bh->b_data);
|
||||
spin_unlock(&bitmap_lock);
|
||||
mark_buffer_dirty(bh);
|
||||
j += i * bits_per_zone + sbi->s_firstdatazone-1;
|
||||
if (j < sbi->s_firstdatazone || j >= sbi->s_nzones)
|
||||
break;
|
||||
return j;
|
||||
}
|
||||
spin_unlock(&bitmap_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long minix_count_free_blocks(struct super_block *sb)
|
||||
{
|
||||
struct minix_sb_info *sbi = minix_sb(sb);
|
||||
u32 bits = sbi->s_nzones - (sbi->s_firstdatazone + 1);
|
||||
|
||||
return (count_free(sbi->s_zmap, sb->s_blocksize, bits)
|
||||
<< 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 inode 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;
|
||||
int minix2_inodes_per_block = sb->s_blocksize / sizeof(struct minix2_inode);
|
||||
|
||||
*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 inode 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 super_block *sb = inode->i_sb;
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
struct buffer_head *bh;
|
||||
int k = sb->s_blocksize_bits + 3;
|
||||
unsigned long ino, bit;
|
||||
|
||||
ino = inode->i_ino;
|
||||
if (ino < 1 || ino > sbi->s_ninodes) {
|
||||
printk("minix_free_inode: inode 0 or nonexistent inode\n");
|
||||
return;
|
||||
}
|
||||
bit = ino & ((1<<k) - 1);
|
||||
ino >>= k;
|
||||
if (ino >= sbi->s_imap_blocks) {
|
||||
printk("minix_free_inode: nonexistent imap in superblock\n");
|
||||
return;
|
||||
}
|
||||
|
||||
minix_clear_inode(inode); /* clear on-disk copy */
|
||||
|
||||
bh = sbi->s_imap[ino];
|
||||
spin_lock(&bitmap_lock);
|
||||
if (!minix_test_and_clear_bit(bit, bh->b_data))
|
||||
printk("minix_free_inode: bit %lu already cleared\n", bit);
|
||||
spin_unlock(&bitmap_lock);
|
||||
mark_buffer_dirty(bh);
|
||||
}
|
||||
|
||||
struct inode *minix_new_inode(const struct inode *dir, umode_t mode, 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 bits_per_zone = 8 * sb->s_blocksize;
|
||||
unsigned long j;
|
||||
int i;
|
||||
|
||||
if (!inode) {
|
||||
*error = -ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
j = bits_per_zone;
|
||||
bh = NULL;
|
||||
*error = -ENOSPC;
|
||||
spin_lock(&bitmap_lock);
|
||||
for (i = 0; i < sbi->s_imap_blocks; i++) {
|
||||
bh = sbi->s_imap[i];
|
||||
j = minix_find_first_zero_bit(bh->b_data, bits_per_zone);
|
||||
if (j < bits_per_zone)
|
||||
break;
|
||||
}
|
||||
if (!bh || j >= bits_per_zone) {
|
||||
spin_unlock(&bitmap_lock);
|
||||
iput(inode);
|
||||
return NULL;
|
||||
}
|
||||
if (minix_test_and_set_bit(j, bh->b_data)) { /* shouldn't happen */
|
||||
spin_unlock(&bitmap_lock);
|
||||
printk("minix_new_inode: bit already set\n");
|
||||
iput(inode);
|
||||
return NULL;
|
||||
}
|
||||
spin_unlock(&bitmap_lock);
|
||||
mark_buffer_dirty(bh);
|
||||
j += i * bits_per_zone;
|
||||
if (!j || j > sbi->s_ninodes) {
|
||||
iput(inode);
|
||||
return NULL;
|
||||
}
|
||||
inode_init_owner(inode, dir, mode);
|
||||
inode->i_ino = j;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
inode->i_blocks = 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 super_block *sb)
|
||||
{
|
||||
struct minix_sb_info *sbi = minix_sb(sb);
|
||||
u32 bits = sbi->s_ninodes + 1;
|
||||
|
||||
return count_free(sbi->s_imap, sb->s_blocksize, bits);
|
||||
}
|
477
fs/minix/dir.c
Normal file
477
fs/minix/dir.c
Normal file
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* linux/fs/minix/dir.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*
|
||||
* minix directory handling functions
|
||||
*
|
||||
* Updated to filesystem version 3 by Daniel Aragones
|
||||
*/
|
||||
|
||||
#include "minix.h"
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/swap.h>
|
||||
|
||||
typedef struct minix_dir_entry minix_dirent;
|
||||
typedef struct minix3_dir_entry minix3_dirent;
|
||||
|
||||
static int minix_readdir(struct file *, void *, filldir_t);
|
||||
|
||||
const struct file_operations minix_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.readdir = minix_readdir,
|
||||
.fsync = generic_file_fsync,
|
||||
};
|
||||
|
||||
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, loff_t pos, unsigned len)
|
||||
{
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct inode *dir = mapping->host;
|
||||
int err = 0;
|
||||
block_write_end(NULL, mapping, pos, len, len, page, NULL);
|
||||
|
||||
if (pos+len > dir->i_size) {
|
||||
i_size_write(dir, pos+len);
|
||||
mark_inode_dirty(dir);
|
||||
}
|
||||
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_mapping_page(mapping, n, NULL);
|
||||
if (!IS_ERR(page))
|
||||
kmap(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
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 = file_inode(filp);
|
||||
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;
|
||||
char *name;
|
||||
__u32 inumber;
|
||||
|
||||
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)) {
|
||||
if (sbi->s_version == MINIX_V3) {
|
||||
minix3_dirent *de3 = (minix3_dirent *)p;
|
||||
name = de3->name;
|
||||
inumber = de3->inode;
|
||||
} else {
|
||||
minix_dirent *de = (minix_dirent *)p;
|
||||
name = de->name;
|
||||
inumber = de->inode;
|
||||
}
|
||||
if (inumber) {
|
||||
int over;
|
||||
|
||||
unsigned l = strnlen(name, sbi->s_namelen);
|
||||
offset = p - kaddr;
|
||||
over = filldir(dirent, name, l,
|
||||
(n << PAGE_CACHE_SHIFT) | offset,
|
||||
inumber, DT_UNKNOWN);
|
||||
if (over) {
|
||||
dir_put_page(page);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
dir_put_page(page);
|
||||
}
|
||||
|
||||
done:
|
||||
filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
|
||||
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;
|
||||
char *p;
|
||||
|
||||
char *namx;
|
||||
__u32 inumber;
|
||||
*res_page = NULL;
|
||||
|
||||
for (n = 0; n < npages; n++) {
|
||||
char *kaddr, *limit;
|
||||
|
||||
page = dir_get_page(dir, n);
|
||||
if (IS_ERR(page))
|
||||
continue;
|
||||
|
||||
kaddr = (char*)page_address(page);
|
||||
limit = kaddr + minix_last_byte(dir, n) - sbi->s_dirsize;
|
||||
for (p = kaddr; p <= limit; p = minix_next_entry(p, sbi)) {
|
||||
if (sbi->s_version == MINIX_V3) {
|
||||
minix3_dirent *de3 = (minix3_dirent *)p;
|
||||
namx = de3->name;
|
||||
inumber = de3->inode;
|
||||
} else {
|
||||
minix_dirent *de = (minix_dirent *)p;
|
||||
namx = de->name;
|
||||
inumber = de->inode;
|
||||
}
|
||||
if (!inumber)
|
||||
continue;
|
||||
if (namecompare(namelen, sbi->s_namelen, name, namx))
|
||||
goto found;
|
||||
}
|
||||
dir_put_page(page);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
*res_page = page;
|
||||
return (minix_dirent *)p;
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned long npages = dir_pages(dir);
|
||||
unsigned long n;
|
||||
char *kaddr, *p;
|
||||
minix_dirent *de;
|
||||
minix3_dirent *de3;
|
||||
loff_t pos;
|
||||
int err;
|
||||
char *namx = NULL;
|
||||
__u32 inumber;
|
||||
|
||||
/*
|
||||
* 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 *limit, *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);
|
||||
limit = kaddr + PAGE_CACHE_SIZE - sbi->s_dirsize;
|
||||
for (p = kaddr; p <= limit; p = minix_next_entry(p, sbi)) {
|
||||
de = (minix_dirent *)p;
|
||||
de3 = (minix3_dirent *)p;
|
||||
if (sbi->s_version == MINIX_V3) {
|
||||
namx = de3->name;
|
||||
inumber = de3->inode;
|
||||
} else {
|
||||
namx = de->name;
|
||||
inumber = de->inode;
|
||||
}
|
||||
if (p == dir_end) {
|
||||
/* We hit i_size */
|
||||
if (sbi->s_version == MINIX_V3)
|
||||
de3->inode = 0;
|
||||
else
|
||||
de->inode = 0;
|
||||
goto got_it;
|
||||
}
|
||||
if (!inumber)
|
||||
goto got_it;
|
||||
err = -EEXIST;
|
||||
if (namecompare(namelen, sbi->s_namelen, name, namx))
|
||||
goto out_unlock;
|
||||
}
|
||||
unlock_page(page);
|
||||
dir_put_page(page);
|
||||
}
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
|
||||
got_it:
|
||||
pos = page_offset(page) + p - (char *)page_address(page);
|
||||
err = minix_prepare_chunk(page, pos, sbi->s_dirsize);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
memcpy (namx, name, namelen);
|
||||
if (sbi->s_version == MINIX_V3) {
|
||||
memset (namx + namelen, 0, sbi->s_dirsize - namelen - 4);
|
||||
de3->inode = inode->i_ino;
|
||||
} else {
|
||||
memset (namx + namelen, 0, sbi->s_dirsize - namelen - 2);
|
||||
de->inode = inode->i_ino;
|
||||
}
|
||||
err = dir_commit_chunk(page, pos, sbi->s_dirsize);
|
||||
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 inode *inode = page->mapping->host;
|
||||
char *kaddr = page_address(page);
|
||||
loff_t pos = page_offset(page) + (char*)de - kaddr;
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
unsigned len = sbi->s_dirsize;
|
||||
int err;
|
||||
|
||||
lock_page(page);
|
||||
err = minix_prepare_chunk(page, pos, len);
|
||||
if (err == 0) {
|
||||
if (sbi->s_version == MINIX_V3)
|
||||
((minix3_dirent *) de)->inode = 0;
|
||||
else
|
||||
de->inode = 0;
|
||||
err = dir_commit_chunk(page, pos, len);
|
||||
} 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 page *page = grab_cache_page(inode->i_mapping, 0);
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
char *kaddr;
|
||||
int err;
|
||||
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
err = minix_prepare_chunk(page, 0, 2 * sbi->s_dirsize);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kaddr = kmap_atomic(page);
|
||||
memset(kaddr, 0, PAGE_CACHE_SIZE);
|
||||
|
||||
if (sbi->s_version == MINIX_V3) {
|
||||
minix3_dirent *de3 = (minix3_dirent *)kaddr;
|
||||
|
||||
de3->inode = inode->i_ino;
|
||||
strcpy(de3->name, ".");
|
||||
de3 = minix_next_entry(de3, sbi);
|
||||
de3->inode = dir->i_ino;
|
||||
strcpy(de3->name, "..");
|
||||
} else {
|
||||
minix_dirent *de = (minix_dirent *)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);
|
||||
|
||||
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);
|
||||
char *name;
|
||||
__u32 inumber;
|
||||
|
||||
for (i = 0; i < npages; i++) {
|
||||
char *p, *kaddr, *limit;
|
||||
|
||||
page = dir_get_page(inode, i);
|
||||
if (IS_ERR(page))
|
||||
continue;
|
||||
|
||||
kaddr = (char *)page_address(page);
|
||||
limit = kaddr + minix_last_byte(inode, i) - sbi->s_dirsize;
|
||||
for (p = kaddr; p <= limit; p = minix_next_entry(p, sbi)) {
|
||||
if (sbi->s_version == MINIX_V3) {
|
||||
minix3_dirent *de3 = (minix3_dirent *)p;
|
||||
name = de3->name;
|
||||
inumber = de3->inode;
|
||||
} else {
|
||||
minix_dirent *de = (minix_dirent *)p;
|
||||
name = de->name;
|
||||
inumber = de->inode;
|
||||
}
|
||||
|
||||
if (inumber != 0) {
|
||||
/* check for . and .. */
|
||||
if (name[0] != '.')
|
||||
goto not_empty;
|
||||
if (!name[1]) {
|
||||
if (inumber != inode->i_ino)
|
||||
goto not_empty;
|
||||
} else if (name[1] != '.')
|
||||
goto not_empty;
|
||||
else if (name[2])
|
||||
goto not_empty;
|
||||
}
|
||||
}
|
||||
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 = page->mapping->host;
|
||||
struct minix_sb_info *sbi = minix_sb(dir->i_sb);
|
||||
loff_t pos = page_offset(page) +
|
||||
(char *)de-(char*)page_address(page);
|
||||
int err;
|
||||
|
||||
lock_page(page);
|
||||
|
||||
err = minix_prepare_chunk(page, pos, sbi->s_dirsize);
|
||||
if (err == 0) {
|
||||
if (sbi->s_version == MINIX_V3)
|
||||
((minix3_dirent *) de)->inode = inode->i_ino;
|
||||
else
|
||||
de->inode = inode->i_ino;
|
||||
err = dir_commit_chunk(page, pos, sbi->s_dirsize);
|
||||
} 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) {
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
|
||||
if (sbi->s_version == MINIX_V3)
|
||||
res = ((minix3_dirent *) de)->inode;
|
||||
else
|
||||
res = de->inode;
|
||||
dir_put_page(page);
|
||||
}
|
||||
return res;
|
||||
}
|
53
fs/minix/file.c
Normal file
53
fs/minix/file.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* linux/fs/minix/file.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*
|
||||
* minix regular file handling primitives
|
||||
*/
|
||||
|
||||
#include "minix.h"
|
||||
|
||||
/*
|
||||
* We have mostly NULLs here: the current defaults are OK for
|
||||
* the minix filesystem.
|
||||
*/
|
||||
const struct file_operations minix_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = do_sync_read,
|
||||
.aio_read = generic_file_aio_read,
|
||||
.write = do_sync_write,
|
||||
.aio_write = generic_file_aio_write,
|
||||
.mmap = generic_file_mmap,
|
||||
.fsync = generic_file_fsync,
|
||||
.splice_read = generic_file_splice_read,
|
||||
};
|
||||
|
||||
static int minix_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 = inode_newsize_ok(inode, attr->ia_size);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
truncate_setsize(inode, attr->ia_size);
|
||||
minix_truncate(inode);
|
||||
}
|
||||
|
||||
setattr_copy(inode, attr);
|
||||
mark_inode_dirty(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct inode_operations minix_file_inode_operations = {
|
||||
.setattr = minix_setattr,
|
||||
.getattr = minix_getattr,
|
||||
};
|
689
fs/minix/inode.c
Normal file
689
fs/minix/inode.c
Normal file
|
@ -0,0 +1,689 @@
|
|||
/*
|
||||
* linux/fs/minix/inode.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*
|
||||
* Copyright (C) 1996 Gertjan van Wingerde
|
||||
* Minix V2 fs support.
|
||||
*
|
||||
* Modified for 680x0 by Andreas Schwab
|
||||
* Updated to filesystem version 3 by Daniel Aragones
|
||||
*/
|
||||
|
||||
#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>
|
||||
#include <linux/writeback.h>
|
||||
|
||||
static int minix_write_inode(struct inode *inode,
|
||||
struct writeback_control *wbc);
|
||||
static int minix_statfs(struct dentry *dentry, struct kstatfs *buf);
|
||||
static int minix_remount (struct super_block * sb, int * flags, char * data);
|
||||
|
||||
static void minix_evict_inode(struct inode *inode)
|
||||
{
|
||||
truncate_inode_pages(&inode->i_data, 0);
|
||||
if (!inode->i_nlink) {
|
||||
inode->i_size = 0;
|
||||
minix_truncate(inode);
|
||||
}
|
||||
invalidate_inode_buffers(inode);
|
||||
clear_inode(inode);
|
||||
if (!inode->i_nlink)
|
||||
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)) {
|
||||
if (sbi->s_version != MINIX_V3) /* s_state is now out from V3 sb */
|
||||
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);
|
||||
}
|
||||
|
||||
static struct kmem_cache * 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, GFP_KERNEL);
|
||||
if (!ei)
|
||||
return NULL;
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void minix_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
kmem_cache_free(minix_inode_cachep, minix_i(inode));
|
||||
}
|
||||
|
||||
static void minix_destroy_inode(struct inode *inode)
|
||||
{
|
||||
call_rcu(&inode->i_rcu, minix_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
{
|
||||
struct minix_inode_info *ei = (struct minix_inode_info *) foo;
|
||||
|
||||
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|
|
||||
SLAB_MEM_SPREAD),
|
||||
init_once);
|
||||
if (minix_inode_cachep == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_inodecache(void)
|
||||
{
|
||||
/*
|
||||
* Make sure all delayed rcu free inodes are flushed before we
|
||||
* destroy cache.
|
||||
*/
|
||||
rcu_barrier();
|
||||
kmem_cache_destroy(minix_inode_cachep);
|
||||
}
|
||||
|
||||
static const struct super_operations minix_sops = {
|
||||
.alloc_inode = minix_alloc_inode,
|
||||
.destroy_inode = minix_destroy_inode,
|
||||
.write_inode = minix_write_inode,
|
||||
.evict_inode = minix_evict_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. */
|
||||
if (sbi->s_version != MINIX_V3)
|
||||
ms->s_state = sbi->s_mount_state;
|
||||
mark_buffer_dirty(sbi->s_sbh);
|
||||
} else {
|
||||
/* Mount a partition which is read-only, read-write. */
|
||||
if (sbi->s_version != MINIX_V3) {
|
||||
sbi->s_mount_state = ms->s_state;
|
||||
ms->s_state &= ~MINIX_VALID_FS;
|
||||
} else {
|
||||
sbi->s_mount_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;
|
||||
struct minix3_super_block *m3s = NULL;
|
||||
unsigned long i, block;
|
||||
struct inode *root_inode;
|
||||
struct minix_sb_info *sbi;
|
||||
int ret = -EINVAL;
|
||||
|
||||
sbi = kzalloc(sizeof(struct minix_sb_info), GFP_KERNEL);
|
||||
if (!sbi)
|
||||
return -ENOMEM;
|
||||
s->s_fs_info = sbi;
|
||||
|
||||
BUILD_BUG_ON(32 != sizeof (struct minix_inode));
|
||||
BUILD_BUG_ON(64 != sizeof(struct minix2_inode));
|
||||
|
||||
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;
|
||||
s->s_max_links = MINIX_LINK_MAX;
|
||||
} else if (s->s_magic == MINIX_SUPER_MAGIC2) {
|
||||
sbi->s_version = MINIX_V1;
|
||||
sbi->s_dirsize = 32;
|
||||
sbi->s_namelen = 30;
|
||||
s->s_max_links = 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;
|
||||
s->s_max_links = 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;
|
||||
s->s_max_links = MINIX2_LINK_MAX;
|
||||
} else if ( *(__u16 *)(bh->b_data + 24) == MINIX3_SUPER_MAGIC) {
|
||||
m3s = (struct minix3_super_block *) bh->b_data;
|
||||
s->s_magic = m3s->s_magic;
|
||||
sbi->s_imap_blocks = m3s->s_imap_blocks;
|
||||
sbi->s_zmap_blocks = m3s->s_zmap_blocks;
|
||||
sbi->s_firstdatazone = m3s->s_firstdatazone;
|
||||
sbi->s_log_zone_size = m3s->s_log_zone_size;
|
||||
sbi->s_max_size = m3s->s_max_size;
|
||||
sbi->s_ninodes = m3s->s_ninodes;
|
||||
sbi->s_nzones = m3s->s_zones;
|
||||
sbi->s_dirsize = 64;
|
||||
sbi->s_namelen = 60;
|
||||
sbi->s_version = MINIX_V3;
|
||||
sbi->s_mount_state = MINIX_VALID_FS;
|
||||
sb_set_blocksize(s, m3s->s_blocksize);
|
||||
s->s_max_links = MINIX2_LINK_MAX;
|
||||
} else
|
||||
goto out_no_fs;
|
||||
|
||||
/*
|
||||
* Allocate the buffer map to keep the superblock small.
|
||||
*/
|
||||
if (sbi->s_imap_blocks == 0 || sbi->s_zmap_blocks == 0)
|
||||
goto out_illegal_sb;
|
||||
i = (sbi->s_imap_blocks + sbi->s_zmap_blocks) * sizeof(bh);
|
||||
map = kzalloc(i, GFP_KERNEL);
|
||||
if (!map)
|
||||
goto out_no_map;
|
||||
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);
|
||||
|
||||
/* Apparently minix can create filesystems that allocate more blocks for
|
||||
* the bitmaps than needed. We simply ignore that, but verify it didn't
|
||||
* create one with not enough blocks and bail out if so.
|
||||
*/
|
||||
block = minix_blocks_needed(sbi->s_ninodes, s->s_blocksize);
|
||||
if (sbi->s_imap_blocks < block) {
|
||||
printk("MINIX-fs: file system does not have enough "
|
||||
"imap blocks allocated. Refusing to mount\n");
|
||||
goto out_no_bitmap;
|
||||
}
|
||||
|
||||
block = minix_blocks_needed(
|
||||
(sbi->s_nzones - (sbi->s_firstdatazone + 1)),
|
||||
s->s_blocksize);
|
||||
if (sbi->s_zmap_blocks < block) {
|
||||
printk("MINIX-fs: file system does not have enough "
|
||||
"zmap blocks allocated. Refusing to mount.\n");
|
||||
goto out_no_bitmap;
|
||||
}
|
||||
|
||||
/* set up enough so that it can read an inode */
|
||||
s->s_op = &minix_sops;
|
||||
root_inode = minix_iget(s, MINIX_ROOT_INO);
|
||||
if (IS_ERR(root_inode)) {
|
||||
ret = PTR_ERR(root_inode);
|
||||
goto out_no_root;
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
s->s_root = d_make_root(root_inode);
|
||||
if (!s->s_root)
|
||||
goto out_no_root;
|
||||
|
||||
if (!(s->s_flags & MS_RDONLY)) {
|
||||
if (sbi->s_version != MINIX_V3) /* s_state is now out from V3 sb */
|
||||
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_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:
|
||||
ret = -ENOMEM;
|
||||
if (!silent)
|
||||
printk("MINIX-fs: can't allocate map\n");
|
||||
goto out_release;
|
||||
|
||||
out_illegal_sb:
|
||||
if (!silent)
|
||||
printk("MINIX-fs: bad superblock\n");
|
||||
goto out_release;
|
||||
|
||||
out_no_fs:
|
||||
if (!silent)
|
||||
printk("VFS: Can't find a Minix filesystem V1 | V2 | V3 "
|
||||
"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 ret;
|
||||
}
|
||||
|
||||
static int minix_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct minix_sb_info *sbi = minix_sb(sb);
|
||||
u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
|
||||
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(sb);
|
||||
buf->f_bavail = buf->f_bfree;
|
||||
buf->f_files = sbi->s_ninodes;
|
||||
buf->f_ffree = minix_count_free_inodes(sb);
|
||||
buf->f_namelen = sbi->s_namelen;
|
||||
buf->f_fsid.val[0] = (u32)id;
|
||||
buf->f_fsid.val[1] = (u32)(id >> 32);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int minix_prepare_chunk(struct page *page, loff_t pos, unsigned len)
|
||||
{
|
||||
return __block_write_begin(page, pos, len, minix_get_block);
|
||||
}
|
||||
|
||||
static void minix_write_failed(struct address_space *mapping, loff_t to)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
|
||||
if (to > inode->i_size) {
|
||||
truncate_pagecache(inode, to, inode->i_size);
|
||||
minix_truncate(inode);
|
||||
}
|
||||
}
|
||||
|
||||
static int minix_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned flags,
|
||||
struct page **pagep, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = block_write_begin(mapping, pos, len, flags, pagep,
|
||||
minix_get_block);
|
||||
if (unlikely(ret))
|
||||
minix_write_failed(mapping, pos + len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static sector_t minix_bmap(struct address_space *mapping, sector_t block)
|
||||
{
|
||||
return generic_block_bmap(mapping,block,minix_get_block);
|
||||
}
|
||||
|
||||
static const struct address_space_operations minix_aops = {
|
||||
.readpage = minix_readpage,
|
||||
.writepage = minix_writepage,
|
||||
.write_begin = minix_write_begin,
|
||||
.write_end = generic_write_end,
|
||||
.bmap = minix_bmap
|
||||
};
|
||||
|
||||
static const 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 struct inode *V1_minix_iget(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) {
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
inode->i_mode = raw_inode->i_mode;
|
||||
i_uid_write(inode, raw_inode->i_uid);
|
||||
i_gid_write(inode, raw_inode->i_gid);
|
||||
set_nlink(inode, 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 = 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);
|
||||
unlock_new_inode(inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* The minix V2 function to read an inode.
|
||||
*/
|
||||
static struct inode *V2_minix_iget(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) {
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
inode->i_mode = raw_inode->i_mode;
|
||||
i_uid_write(inode, raw_inode->i_uid);
|
||||
i_gid_write(inode, raw_inode->i_gid);
|
||||
set_nlink(inode, 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 = 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);
|
||||
unlock_new_inode(inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* The global function to read an inode.
|
||||
*/
|
||||
struct inode *minix_iget(struct super_block *sb, unsigned long ino)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = iget_locked(sb, ino);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!(inode->i_state & I_NEW))
|
||||
return inode;
|
||||
|
||||
if (INODE_VERSION(inode) == MINIX_V1)
|
||||
return V1_minix_iget(inode);
|
||||
else
|
||||
return V2_minix_iget(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(i_uid_read(inode));
|
||||
raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));
|
||||
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(i_uid_read(inode));
|
||||
raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));
|
||||
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 int minix_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
int err = 0;
|
||||
struct buffer_head *bh;
|
||||
|
||||
if (INODE_VERSION(inode) == MINIX_V1)
|
||||
bh = V1_minix_update_inode(inode);
|
||||
else
|
||||
bh = V2_minix_update_inode(inode);
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
if (wbc->sync_mode == WB_SYNC_ALL && 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 = -EIO;
|
||||
}
|
||||
}
|
||||
brelse (bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
int minix_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
generic_fillattr(dentry->d_inode, stat);
|
||||
if (INODE_VERSION(dentry->d_inode) == MINIX_V1)
|
||||
stat->blocks = (BLOCK_SIZE / 512) * V1_minix_blocks(stat->size, sb);
|
||||
else
|
||||
stat->blocks = (sb->s_blocksize / 512) * V2_minix_blocks(stat->size, sb);
|
||||
stat->blksize = sb->s_blocksize;
|
||||
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 dentry *minix_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return mount_bdev(fs_type, flags, dev_name, data, minix_fill_super);
|
||||
}
|
||||
|
||||
static struct file_system_type minix_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "minix",
|
||||
.mount = minix_mount,
|
||||
.kill_sb = kill_block_super,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
MODULE_ALIAS_FS("minix");
|
||||
|
||||
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");
|
||||
|
364
fs/minix/itree_common.c
Normal file
364
fs/minix/itree_common.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
/* 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 + bh->b_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, bh->b_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)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
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 + sb->s_blocksize -1) >> sb->s_blocksize_bits;
|
||||
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, struct super_block *sb)
|
||||
{
|
||||
int k = sb->s_blocksize_bits - 10;
|
||||
unsigned blocks, res, direct = DIRECT, i = DEPTH;
|
||||
blocks = (size + sb->s_blocksize - 1) >> (BLOCK_SIZE_BITS + k);
|
||||
res = blocks;
|
||||
while (--i && blocks > direct) {
|
||||
blocks -= direct;
|
||||
blocks += sb->s_blocksize/sizeof(block_t) - 1;
|
||||
blocks /= sb->s_blocksize/sizeof(block_t);
|
||||
res += blocks;
|
||||
direct = 1;
|
||||
}
|
||||
return res;
|
||||
}
|
67
fs/minix/itree_v1.c
Normal file
67
fs/minix/itree_v1.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include <linux/buffer_head.h>
|
||||
#include <linux/slab.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;
|
||||
char b[BDEVNAME_SIZE];
|
||||
|
||||
if (block < 0) {
|
||||
printk("MINIX-fs: block_to_path: block %ld < 0 on dev %s\n",
|
||||
block, bdevname(inode->i_sb->s_bdev, b));
|
||||
} else if (block >= (minix_sb(inode->i_sb)->s_max_size/BLOCK_SIZE)) {
|
||||
if (printk_ratelimit())
|
||||
printk("MINIX-fs: block_to_path: "
|
||||
"block %ld too big on dev %s\n",
|
||||
block, bdevname(inode->i_sb->s_bdev, b));
|
||||
} 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, struct super_block *sb)
|
||||
{
|
||||
return nblocks(size, sb);
|
||||
}
|
76
fs/minix/itree_v2.c
Normal file
76
fs/minix/itree_v2.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
#define DIRCOUNT 7
|
||||
#define INDIRCOUNT(sb) (1 << ((sb)->s_blocksize_bits - 2))
|
||||
|
||||
static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
|
||||
{
|
||||
int n = 0;
|
||||
char b[BDEVNAME_SIZE];
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
||||
if (block < 0) {
|
||||
printk("MINIX-fs: block_to_path: block %ld < 0 on dev %s\n",
|
||||
block, bdevname(sb->s_bdev, b));
|
||||
} else if ((u64)block * (u64)sb->s_blocksize >=
|
||||
minix_sb(sb)->s_max_size) {
|
||||
if (printk_ratelimit())
|
||||
printk("MINIX-fs: block_to_path: "
|
||||
"block %ld too big on dev %s\n",
|
||||
block, bdevname(sb->s_bdev, b));
|
||||
} else if (block < DIRCOUNT) {
|
||||
offsets[n++] = block;
|
||||
} else if ((block -= DIRCOUNT) < INDIRCOUNT(sb)) {
|
||||
offsets[n++] = DIRCOUNT;
|
||||
offsets[n++] = block;
|
||||
} else if ((block -= INDIRCOUNT(sb)) < INDIRCOUNT(sb) * INDIRCOUNT(sb)) {
|
||||
offsets[n++] = DIRCOUNT + 1;
|
||||
offsets[n++] = block / INDIRCOUNT(sb);
|
||||
offsets[n++] = block % INDIRCOUNT(sb);
|
||||
} else {
|
||||
block -= INDIRCOUNT(sb) * INDIRCOUNT(sb);
|
||||
offsets[n++] = DIRCOUNT + 2;
|
||||
offsets[n++] = (block / INDIRCOUNT(sb)) / INDIRCOUNT(sb);
|
||||
offsets[n++] = (block / INDIRCOUNT(sb)) % INDIRCOUNT(sb);
|
||||
offsets[n++] = block % INDIRCOUNT(sb);
|
||||
}
|
||||
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, struct super_block *sb)
|
||||
{
|
||||
return nblocks(size, sb);
|
||||
}
|
169
fs/minix/minix.h
Normal file
169
fs/minix/minix.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
#ifndef FS_MINIX_H
|
||||
#define FS_MINIX_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/minix_fs.h>
|
||||
|
||||
#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 */
|
||||
#define MINIX_V3 0x0003 /* minix V3 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;
|
||||
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 inode *minix_iget(struct super_block *, unsigned long);
|
||||
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 *, umode_t, int *);
|
||||
extern void minix_free_inode(struct inode * inode);
|
||||
extern unsigned long minix_count_free_inodes(struct super_block *sb);
|
||||
extern int minix_new_block(struct inode * inode);
|
||||
extern void minix_free_block(struct inode *inode, unsigned long block);
|
||||
extern unsigned long minix_count_free_blocks(struct super_block *sb);
|
||||
extern int minix_getattr(struct vfsmount *, struct dentry *, struct kstat *);
|
||||
extern int minix_prepare_chunk(struct page *page, loff_t pos, unsigned len);
|
||||
|
||||
extern void V1_minix_truncate(struct inode *);
|
||||
extern void V2_minix_truncate(struct inode *);
|
||||
extern void minix_truncate(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, struct super_block *);
|
||||
extern unsigned V2_minix_blocks(loff_t, struct super_block *);
|
||||
|
||||
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 const struct inode_operations minix_file_inode_operations;
|
||||
extern const struct inode_operations minix_dir_inode_operations;
|
||||
extern const struct file_operations minix_file_operations;
|
||||
extern const struct file_operations minix_dir_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);
|
||||
}
|
||||
|
||||
static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize)
|
||||
{
|
||||
return DIV_ROUND_UP(bits, blocksize * 8);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MINIX_FS_NATIVE_ENDIAN) && \
|
||||
defined(CONFIG_MINIX_FS_BIG_ENDIAN_16BIT_INDEXED)
|
||||
|
||||
#error Minix file system byte order broken
|
||||
|
||||
#elif defined(CONFIG_MINIX_FS_NATIVE_ENDIAN)
|
||||
|
||||
/*
|
||||
* big-endian 32 or 64 bit indexed bitmaps on big-endian system or
|
||||
* little-endian bitmaps on little-endian system
|
||||
*/
|
||||
|
||||
#define minix_test_and_set_bit(nr, addr) \
|
||||
__test_and_set_bit((nr), (unsigned long *)(addr))
|
||||
#define minix_set_bit(nr, addr) \
|
||||
__set_bit((nr), (unsigned long *)(addr))
|
||||
#define minix_test_and_clear_bit(nr, addr) \
|
||||
__test_and_clear_bit((nr), (unsigned long *)(addr))
|
||||
#define minix_test_bit(nr, addr) \
|
||||
test_bit((nr), (unsigned long *)(addr))
|
||||
#define minix_find_first_zero_bit(addr, size) \
|
||||
find_first_zero_bit((unsigned long *)(addr), (size))
|
||||
|
||||
#elif defined(CONFIG_MINIX_FS_BIG_ENDIAN_16BIT_INDEXED)
|
||||
|
||||
/*
|
||||
* big-endian 16bit indexed bitmaps
|
||||
*/
|
||||
|
||||
static inline int minix_find_first_zero_bit(const void *vaddr, unsigned size)
|
||||
{
|
||||
const unsigned short *p = vaddr, *addr = vaddr;
|
||||
unsigned short num;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
size >>= 4;
|
||||
while (*p++ == 0xffff) {
|
||||
if (--size == 0)
|
||||
return (p - addr) << 4;
|
||||
}
|
||||
|
||||
num = *--p;
|
||||
return ((p - addr) << 4) + ffz(num);
|
||||
}
|
||||
|
||||
#define minix_test_and_set_bit(nr, addr) \
|
||||
__test_and_set_bit((nr) ^ 16, (unsigned long *)(addr))
|
||||
#define minix_set_bit(nr, addr) \
|
||||
__set_bit((nr) ^ 16, (unsigned long *)(addr))
|
||||
#define minix_test_and_clear_bit(nr, addr) \
|
||||
__test_and_clear_bit((nr) ^ 16, (unsigned long *)(addr))
|
||||
|
||||
static inline int minix_test_bit(int nr, const void *vaddr)
|
||||
{
|
||||
const unsigned short *p = vaddr;
|
||||
return (p[nr >> 4] & (1U << (nr & 15))) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* little-endian bitmaps
|
||||
*/
|
||||
|
||||
#define minix_test_and_set_bit __test_and_set_bit_le
|
||||
#define minix_set_bit __set_bit_le
|
||||
#define minix_test_and_clear_bit __test_and_clear_bit_le
|
||||
#define minix_test_bit test_bit_le
|
||||
#define minix_find_first_zero_bit find_first_zero_bit_le
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* FS_MINIX_H */
|
257
fs/minix/namei.c
Normal file
257
fs/minix/namei.c
Normal file
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* linux/fs/minix/namei.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*/
|
||||
|
||||
#include "minix.h"
|
||||
|
||||
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;
|
||||
}
|
||||
inode_dec_link_count(inode);
|
||||
iput(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct inode * inode = NULL;
|
||||
ino_t ino;
|
||||
|
||||
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 = minix_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
}
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int minix_mknod(struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev)
|
||||
{
|
||||
int error;
|
||||
struct inode *inode;
|
||||
|
||||
if (!old_valid_dev(rdev))
|
||||
return -EINVAL;
|
||||
|
||||
inode = minix_new_inode(dir, mode, &error);
|
||||
|
||||
if (inode) {
|
||||
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, umode_t mode,
|
||||
bool excl)
|
||||
{
|
||||
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, S_IFLNK | 0777, &err);
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
||||
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:
|
||||
inode_dec_link_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;
|
||||
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
inode_inc_link_count(inode);
|
||||
ihold(inode);
|
||||
return add_nondir(dentry, inode);
|
||||
}
|
||||
|
||||
static int minix_mkdir(struct inode * dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct inode * inode;
|
||||
int err;
|
||||
|
||||
inode_inc_link_count(dir);
|
||||
|
||||
inode = minix_new_inode(dir, S_IFDIR | mode, &err);
|
||||
if (!inode)
|
||||
goto out_dir;
|
||||
|
||||
minix_set_inode(inode, 0);
|
||||
|
||||
inode_inc_link_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:
|
||||
inode_dec_link_count(inode);
|
||||
inode_dec_link_count(inode);
|
||||
iput(inode);
|
||||
out_dir:
|
||||
inode_dec_link_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;
|
||||
inode_dec_link_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) {
|
||||
inode_dec_link_count(dir);
|
||||
inode_dec_link_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 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;
|
||||
minix_set_link(new_de, new_page, old_inode);
|
||||
new_inode->i_ctime = CURRENT_TIME_SEC;
|
||||
if (dir_de)
|
||||
drop_nlink(new_inode);
|
||||
inode_dec_link_count(new_inode);
|
||||
} else {
|
||||
err = minix_add_link(new_dentry, old_inode);
|
||||
if (err)
|
||||
goto out_dir;
|
||||
if (dir_de)
|
||||
inode_inc_link_count(new_dir);
|
||||
}
|
||||
|
||||
minix_delete_entry(old_de, old_page);
|
||||
mark_inode_dirty(old_inode);
|
||||
|
||||
if (dir_de) {
|
||||
minix_set_link(dir_de, dir_page, new_dir);
|
||||
inode_dec_link_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...
|
||||
*/
|
||||
const 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,
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue