URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/tags/before_ORP/uclinux/uClinux-2.0.x/fs/ext
- from Rev 901 to Rev 1765
- ↔ Reverse comparison
Rev 901 → Rev 1765
/dir.c
0,0 → 1,121
/* |
* linux/fs/ext/dir.c |
* |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* |
* from |
* |
* linux/fs/minix/dir.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* ext directory handling functions |
*/ |
|
#include <asm/segment.h> |
|
#include <linux/errno.h> |
#include <linux/kernel.h> |
#include <linux/fs.h> |
#include <linux/ext_fs.h> |
#include <linux/stat.h> |
|
static int ext_dir_read(struct inode * inode, struct file * filp, char * buf, int count) |
{ |
return -EISDIR; |
} |
|
static int ext_readdir(struct inode *, struct file *, void *, filldir_t); |
|
static struct file_operations ext_dir_operations = { |
NULL, /* lseek - default */ |
ext_dir_read, /* read */ |
NULL, /* write - bad */ |
ext_readdir, /* readdir */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
NULL, /* no special release code */ |
file_fsync /* fsync */ |
}; |
|
/* |
* directories can handle most operations... |
*/ |
struct inode_operations ext_dir_inode_operations = { |
&ext_dir_operations, /* default directory file-ops */ |
ext_create, /* create */ |
ext_lookup, /* lookup */ |
ext_link, /* link */ |
ext_unlink, /* unlink */ |
ext_symlink, /* symlink */ |
ext_mkdir, /* mkdir */ |
ext_rmdir, /* rmdir */ |
ext_mknod, /* mknod */ |
ext_rename, /* rename */ |
NULL, /* readlink */ |
NULL, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
ext_truncate, /* truncate */ |
NULL /* permission */ |
}; |
|
static int ext_readdir(struct inode * inode, struct file * filp, |
void * dirent, filldir_t filldir) |
{ |
int error; |
unsigned int i; |
off_t offset; |
struct buffer_head * bh; |
struct ext_dir_entry * de; |
|
if (!inode || !S_ISDIR(inode->i_mode)) |
return -EBADF; |
if ((filp->f_pos & 7) != 0) |
return -EBADF; |
error = 0; |
while (!error && filp->f_pos < inode->i_size) { |
offset = filp->f_pos & 1023; |
bh = ext_bread(inode,(filp->f_pos)>>BLOCK_SIZE_BITS,0); |
if (!bh) { |
filp->f_pos += 1024-offset; |
continue; |
} |
for (i = 0; i < 1024 && i < offset; ) { |
de = (struct ext_dir_entry *) (bh->b_data + i); |
if (!de->rec_len) |
break; |
i += de->rec_len; |
} |
offset = i; |
de = (struct ext_dir_entry *) (offset + bh->b_data); |
while (offset < 1024 && filp->f_pos < inode->i_size) { |
if (de->rec_len < 8 || de->rec_len % 8 != 0 || |
de->rec_len < de->name_len + 8 || |
(de->rec_len + (off_t) filp->f_pos - 1) / 1024 > ((off_t) filp->f_pos / 1024)) { |
printk ("ext_readdir: bad dir entry, skipping\n"); |
printk ("dev=%s, dir=%ld, " |
"offset=%ld, rec_len=%d, name_len=%d\n", |
kdevname(inode->i_dev), inode->i_ino, |
offset, de->rec_len, de->name_len); |
filp->f_pos += 1024-offset; |
if (filp->f_pos > inode->i_size) |
filp->f_pos = inode->i_size; |
continue; |
} |
if (de->inode) { |
error = filldir(dirent, de->name, de->name_len, filp->f_pos, de->inode); |
if (error) |
break; |
} |
offset += de->rec_len; |
filp->f_pos += de->rec_len; |
((char *) de) += de->rec_len; |
} |
brelse(bh); |
} |
return 0; |
} |
/inode.c
0,0 → 1,479
/* |
* linux/fs/ext/inode.c |
* |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* |
* from |
* |
* linux/fs/minix/inode.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
|
#include <linux/module.h> |
|
#include <linux/sched.h> |
#include <linux/ext_fs.h> |
#include <linux/kernel.h> |
#include <linux/mm.h> |
#include <linux/string.h> |
#include <linux/stat.h> |
#include <linux/locks.h> |
|
#include <asm/system.h> |
#include <asm/segment.h> |
|
void ext_put_inode(struct inode *inode) |
{ |
if (inode->i_nlink) |
return; |
inode->i_size = 0; |
ext_truncate(inode); |
ext_free_inode(inode); |
} |
|
void ext_put_super(struct super_block *sb) |
{ |
|
lock_super(sb); |
sb->s_dev = 0; |
if (sb->u.ext_sb.s_firstfreeinodeblock) |
brelse (sb->u.ext_sb.s_firstfreeinodeblock); |
if (sb->u.ext_sb.s_firstfreeblock) |
brelse (sb->u.ext_sb.s_firstfreeblock); |
unlock_super(sb); |
MOD_DEC_USE_COUNT; |
return; |
} |
|
static struct super_operations ext_sops = { |
ext_read_inode, |
NULL, |
ext_write_inode, |
ext_put_inode, |
ext_put_super, |
ext_write_super, |
ext_statfs, |
NULL |
}; |
|
struct super_block *ext_read_super(struct super_block *s,void *data, |
int silent) |
{ |
struct buffer_head *bh; |
struct ext_super_block *es; |
kdev_t dev = s->s_dev; |
int block; |
|
MOD_INC_USE_COUNT; |
lock_super(s); |
set_blocksize(dev, BLOCK_SIZE); |
if (!(bh = bread(dev, 1, BLOCK_SIZE))) { |
s->s_dev = 0; |
unlock_super(s); |
printk("EXT-fs: unable to read superblock\n"); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
es = (struct ext_super_block *) bh->b_data; |
s->s_blocksize = 1024; |
s->s_blocksize_bits = 10; |
s->u.ext_sb.s_ninodes = es->s_ninodes; |
s->u.ext_sb.s_nzones = es->s_nzones; |
s->u.ext_sb.s_firstdatazone = es->s_firstdatazone; |
s->u.ext_sb.s_log_zone_size = es->s_log_zone_size; |
s->u.ext_sb.s_max_size = es->s_max_size; |
s->s_magic = es->s_magic; |
s->u.ext_sb.s_firstfreeblocknumber = es->s_firstfreeblock; |
s->u.ext_sb.s_freeblockscount = es->s_freeblockscount; |
s->u.ext_sb.s_firstfreeinodenumber = es->s_firstfreeinode; |
s->u.ext_sb.s_freeinodescount = es->s_freeinodescount; |
brelse(bh); |
if (s->s_magic != EXT_SUPER_MAGIC) { |
s->s_dev = 0; |
unlock_super(s); |
if (!silent) |
printk("VFS: Can't find an extfs filesystem on dev " |
"%s.\n", kdevname(dev)); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
if (!s->u.ext_sb.s_firstfreeblocknumber) |
s->u.ext_sb.s_firstfreeblock = NULL; |
else |
if (!(s->u.ext_sb.s_firstfreeblock = bread(dev, |
s->u.ext_sb.s_firstfreeblocknumber, BLOCK_SIZE))) { |
printk("ext_read_super: unable to read first free block\n"); |
s->s_dev = 0; |
unlock_super(s); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
if (!s->u.ext_sb.s_firstfreeinodenumber) |
s->u.ext_sb.s_firstfreeinodeblock = NULL; |
else { |
block = 2 + (s->u.ext_sb.s_firstfreeinodenumber - 1) / EXT_INODES_PER_BLOCK; |
if (!(s->u.ext_sb.s_firstfreeinodeblock = bread(dev, block, BLOCK_SIZE))) { |
printk("ext_read_super: unable to read first free inode block\n"); |
brelse(s->u.ext_sb.s_firstfreeblock); |
s->s_dev = 0; |
unlock_super (s); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
} |
unlock_super(s); |
/* set up enough so that it can read an inode */ |
s->s_dev = dev; |
s->s_op = &ext_sops; |
if (!(s->s_mounted = iget(s,EXT_ROOT_INO))) { |
s->s_dev = 0; |
printk("EXT-fs: get root inode failed\n"); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
return s; |
} |
|
void ext_write_super (struct super_block *sb) |
{ |
struct buffer_head * bh; |
struct ext_super_block * es; |
|
if (!(bh = bread(sb->s_dev, 1, BLOCK_SIZE))) { |
printk ("ext_write_super: bread failed\n"); |
return; |
} |
es = (struct ext_super_block *) bh->b_data; |
es->s_firstfreeblock = sb->u.ext_sb.s_firstfreeblocknumber; |
es->s_freeblockscount = sb->u.ext_sb.s_freeblockscount; |
es->s_firstfreeinode = sb->u.ext_sb.s_firstfreeinodenumber; |
es->s_freeinodescount = sb->u.ext_sb.s_freeinodescount; |
mark_buffer_dirty(bh, 1); |
brelse (bh); |
sb->s_dirt = 0; |
} |
|
void ext_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) |
{ |
struct statfs tmp; |
|
tmp.f_type = EXT_SUPER_MAGIC; |
tmp.f_bsize = 1024; |
tmp.f_blocks = sb->u.ext_sb.s_nzones << sb->u.ext_sb.s_log_zone_size; |
tmp.f_bfree = ext_count_free_blocks(sb); |
tmp.f_bavail = tmp.f_bfree; |
tmp.f_files = sb->u.ext_sb.s_ninodes; |
tmp.f_ffree = ext_count_free_inodes(sb); |
tmp.f_namelen = EXT_NAME_LEN; |
memcpy_tofs(buf, &tmp, bufsiz); |
} |
|
#define inode_bmap(inode,nr) ((inode)->u.ext_i.i_data[(nr)]) |
|
static inline int block_bmap(struct buffer_head * bh, int nr) |
{ |
int tmp; |
|
if (!bh) |
return 0; |
tmp = ((unsigned long *) bh->b_data)[nr]; |
brelse(bh); |
return tmp; |
} |
|
int ext_bmap(struct inode * inode,int block) |
{ |
int i; |
|
if (block<0) { |
printk("ext_bmap: block<0"); |
return 0; |
} |
if (block >= 9+256+256*256+256*256*256) { |
printk("ext_bmap: block>big"); |
return 0; |
} |
if (block<9) |
return inode_bmap(inode,block); |
block -= 9; |
if (block<256) { |
i = inode_bmap(inode,9); |
if (!i) |
return 0; |
return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block); |
} |
block -= 256; |
if (block<256*256) { |
i = inode_bmap(inode,10); |
if (!i) |
return 0; |
i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>8); |
if (!i) |
return 0; |
return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255); |
} |
block -= 256*256; |
i = inode_bmap(inode,11); |
if (!i) |
return 0; |
i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>16); |
if (!i) |
return 0; |
i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),(block>>8) & 255); |
if (!i) |
return 0; |
return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255); |
} |
|
static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create) |
{ |
int tmp; |
unsigned long * p; |
struct buffer_head * result; |
|
p = inode->u.ext_i.i_data + nr; |
repeat: |
tmp = *p; |
if (tmp) { |
result = getblk(inode->i_dev, tmp, BLOCK_SIZE); |
if (tmp == *p) |
return result; |
brelse(result); |
goto repeat; |
} |
if (!create) |
return NULL; |
tmp = ext_new_block(inode->i_sb); |
if (!tmp) |
return NULL; |
result = getblk(inode->i_dev, tmp, BLOCK_SIZE); |
if (*p) { |
ext_free_block(inode->i_sb,tmp); |
brelse(result); |
goto repeat; |
} |
*p = tmp; |
inode->i_ctime = CURRENT_TIME; |
inode->i_dirt = 1; |
return result; |
} |
|
static struct buffer_head * block_getblk(struct inode * inode, |
struct buffer_head * bh, int nr, int create) |
{ |
int tmp; |
unsigned long * p; |
struct buffer_head * result; |
|
if (!bh) |
return NULL; |
if (!buffer_uptodate(bh)) { |
ll_rw_block(READ, 1, &bh); |
wait_on_buffer(bh); |
if (!buffer_uptodate(bh)) { |
brelse(bh); |
return NULL; |
} |
} |
p = nr + (unsigned long *) bh->b_data; |
repeat: |
tmp = *p; |
if (tmp) { |
result = getblk(bh->b_dev, tmp, BLOCK_SIZE); |
if (tmp == *p) { |
brelse(bh); |
return result; |
} |
brelse(result); |
goto repeat; |
} |
if (!create) { |
brelse(bh); |
return NULL; |
} |
tmp = ext_new_block(inode->i_sb); |
if (!tmp) { |
brelse(bh); |
return NULL; |
} |
result = getblk(bh->b_dev, tmp, BLOCK_SIZE); |
if (*p) { |
ext_free_block(inode->i_sb,tmp); |
brelse(result); |
goto repeat; |
} |
*p = tmp; |
mark_buffer_dirty(bh, 1); |
brelse(bh); |
return result; |
} |
|
struct buffer_head * ext_getblk(struct inode * inode, int block, int create) |
{ |
struct buffer_head * bh; |
|
if (block<0) { |
printk("ext_getblk: block<0\n"); |
return NULL; |
} |
if (block >= 9+256+256*256+256*256*256) { |
printk("ext_getblk: block>big\n"); |
return NULL; |
} |
if (block<9) |
return inode_getblk(inode,block,create); |
block -= 9; |
if (block<256) { |
bh = inode_getblk(inode,9,create); |
return block_getblk(inode,bh,block,create); |
} |
block -= 256; |
if (block<256*256) { |
bh = inode_getblk(inode,10,create); |
bh = block_getblk(inode,bh,block>>8,create); |
return block_getblk(inode,bh,block & 255,create); |
} |
block -= 256*256; |
bh = inode_getblk(inode,11,create); |
bh = block_getblk(inode,bh,block>>16,create); |
bh = block_getblk(inode,bh,(block>>8) & 255,create); |
return block_getblk(inode,bh,block & 255,create); |
} |
|
struct buffer_head * ext_bread(struct inode * inode, int block, int create) |
{ |
struct buffer_head * bh; |
|
bh = ext_getblk(inode,block,create); |
if (!bh || buffer_uptodate(bh)) |
return bh; |
ll_rw_block(READ, 1, &bh); |
wait_on_buffer(bh); |
if (buffer_uptodate(bh)) |
return bh; |
brelse(bh); |
return NULL; |
} |
|
void ext_read_inode(struct inode * inode) |
{ |
struct buffer_head * bh; |
struct ext_inode * raw_inode; |
int block; |
|
block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK; |
if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) |
panic("unable to read i-node block"); |
raw_inode = ((struct ext_inode *) bh->b_data) + |
(inode->i_ino-1)%EXT_INODES_PER_BLOCK; |
inode->i_mode = raw_inode->i_mode; |
inode->i_uid = raw_inode->i_uid; |
inode->i_gid = raw_inode->i_gid; |
inode->i_nlink = raw_inode->i_nlinks; |
inode->i_size = raw_inode->i_size; |
inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time; |
inode->i_blocks = inode->i_blksize = 0; |
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) |
inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]); |
else for (block = 0; block < 12; block++) |
inode->u.ext_i.i_data[block] = raw_inode->i_zone[block]; |
brelse(bh); |
inode->i_op = NULL; |
if (S_ISREG(inode->i_mode)) |
inode->i_op = &ext_file_inode_operations; |
else if (S_ISDIR(inode->i_mode)) |
inode->i_op = &ext_dir_inode_operations; |
else if (S_ISLNK(inode->i_mode)) |
inode->i_op = &ext_symlink_inode_operations; |
else if (S_ISCHR(inode->i_mode)) |
inode->i_op = &chrdev_inode_operations; |
else if (S_ISBLK(inode->i_mode)) |
inode->i_op = &blkdev_inode_operations; |
else if (S_ISFIFO(inode->i_mode)) |
init_fifo(inode); |
} |
|
static struct buffer_head * ext_update_inode(struct inode * inode) |
{ |
struct buffer_head * bh; |
struct ext_inode * raw_inode; |
int block; |
|
block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK; |
if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) |
panic("unable to read i-node block"); |
raw_inode = ((struct ext_inode *)bh->b_data) + |
(inode->i_ino-1)%EXT_INODES_PER_BLOCK; |
raw_inode->i_mode = inode->i_mode; |
raw_inode->i_uid = inode->i_uid; |
raw_inode->i_gid = inode->i_gid; |
raw_inode->i_nlinks = inode->i_nlink; |
raw_inode->i_size = inode->i_size; |
raw_inode->i_time = inode->i_mtime; |
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) |
raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev); |
else for (block = 0; block < 12; block++) |
raw_inode->i_zone[block] = inode->u.ext_i.i_data[block]; |
mark_buffer_dirty(bh, 1); |
inode->i_dirt=0; |
return bh; |
} |
|
void ext_write_inode(struct inode * inode) |
{ |
struct buffer_head *bh; |
bh = ext_update_inode (inode); |
brelse(bh); |
} |
|
int ext_sync_inode (struct inode *inode) |
{ |
int err = 0; |
struct buffer_head *bh; |
|
bh = ext_update_inode(inode); |
if (bh && buffer_dirty(bh)) |
{ |
ll_rw_block(WRITE, 1, &bh); |
wait_on_buffer(bh); |
if (buffer_req(bh) && !buffer_uptodate(bh)) |
{ |
printk ("IO error syncing ext inode [" |
"%s:%08lx]\n", |
kdevname(inode->i_dev), inode->i_ino); |
err = -1; |
} |
} |
else if (!bh) |
err = -1; |
brelse (bh); |
return err; |
} |
|
|
static struct file_system_type ext_fs_type = { |
ext_read_super, "ext", 1, NULL |
}; |
|
int init_ext_fs(void) |
{ |
return register_filesystem(&ext_fs_type); |
} |
|
#ifdef MODULE |
int init_module(void) |
{ |
int status; |
|
if ((status = init_ext_fs()) == 0) |
register_symtab(0); |
return status; |
} |
|
void cleanup_module(void) |
{ |
unregister_filesystem(&ext_fs_type); |
} |
|
#endif |
/file.c
0,0 → 1,262
/* |
* linux/fs/ext/file.c |
* |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* |
* from |
* |
* linux/fs/minix/file.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* ext regular file handling primitives |
*/ |
|
#include <asm/segment.h> |
#include <asm/system.h> |
|
#include <linux/sched.h> |
#include <linux/ext_fs.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/fcntl.h> |
#include <linux/stat.h> |
#include <linux/locks.h> |
#include <linux/pagemap.h> |
|
#define NBUF 32 |
|
#define MIN(a,b) (((a)<(b))?(a):(b)) |
#define MAX(a,b) (((a)>(b))?(a):(b)) |
|
#include <linux/fs.h> |
#include <linux/ext_fs.h> |
|
static int ext_file_read(struct inode *, struct file *, char *, int); |
static int ext_file_write(struct inode *, struct file *, const char *, int); |
|
/* |
* We have mostly NULL's here: the current defaults are ok for |
* the ext filesystem. |
*/ |
static struct file_operations ext_file_operations = { |
NULL, /* lseek - default */ |
ext_file_read, /* read */ |
ext_file_write, /* write */ |
NULL, /* readdir - bad */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
generic_file_mmap, /* mmap */ |
NULL, /* no special open is needed */ |
NULL, /* release */ |
ext_sync_file /* fsync */ |
}; |
|
struct inode_operations ext_file_inode_operations = { |
&ext_file_operations, /* default file operations */ |
NULL, /* create */ |
NULL, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
NULL, /* readlink */ |
NULL, /* follow_link */ |
generic_readpage, /* readpage */ |
NULL, /* writepage */ |
ext_bmap, /* bmap */ |
ext_truncate, /* truncate */ |
NULL /* permission */ |
}; |
|
static int ext_file_read(struct inode * inode, struct file * filp, char * buf, int count) |
{ |
int read,left,chars; |
int block, blocks, offset; |
int bhrequest, uptodate; |
struct buffer_head ** bhb, ** bhe; |
struct buffer_head * bhreq[NBUF]; |
struct buffer_head * buflist[NBUF]; |
unsigned int size; |
|
if (!inode) { |
printk("ext_file_read: inode = NULL\n"); |
return -EINVAL; |
} |
if (!S_ISREG(inode->i_mode)) { |
printk("ext_file_read: mode = %07o\n",inode->i_mode); |
return -EINVAL; |
} |
offset = filp->f_pos; |
size = inode->i_size; |
if (offset > size) |
left = 0; |
else |
left = size - offset; |
if (left > count) |
left = count; |
if (left <= 0) |
return 0; |
read = 0; |
block = offset >> BLOCK_SIZE_BITS; |
offset &= BLOCK_SIZE-1; |
size = (size + (BLOCK_SIZE-1)) >> BLOCK_SIZE_BITS; |
blocks = (left + offset + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS; |
bhb = bhe = buflist; |
if (filp->f_reada) { |
if(blocks < read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9)) |
blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9); |
if (block + blocks > size) |
blocks = size - block; |
} |
|
/* We do this in a two stage process. We first try to request |
as many blocks as we can, then we wait for the first one to |
complete, and then we try to wrap up as many as are actually |
done. This routine is rather generic, in that it can be used |
in a filesystem by substituting the appropriate function in |
for getblk. |
|
This routine is optimized to make maximum use of the various |
buffers and caches. */ |
|
do { |
bhrequest = 0; |
uptodate = 1; |
while (blocks) { |
--blocks; |
*bhb = ext_getblk(inode, block++, 0); |
if (*bhb && !buffer_uptodate(*bhb)) { |
uptodate = 0; |
bhreq[bhrequest++] = *bhb; |
} |
|
if (++bhb == &buflist[NBUF]) |
bhb = buflist; |
|
/* If the block we have on hand is uptodate, go ahead |
and complete processing. */ |
if (uptodate) |
break; |
if (bhb == bhe) |
break; |
} |
|
/* Now request them all */ |
if (bhrequest) |
ll_rw_block(READ, bhrequest, bhreq); |
|
do { /* Finish off all I/O that has actually completed */ |
if (*bhe) { |
wait_on_buffer(*bhe); |
if (!buffer_uptodate(*bhe)) { /* read error? */ |
brelse(*bhe); |
if (++bhe == &buflist[NBUF]) |
bhe = buflist; |
left = 0; |
break; |
} |
} |
if (left < BLOCK_SIZE - offset) |
chars = left; |
else |
chars = BLOCK_SIZE - offset; |
filp->f_pos += chars; |
left -= chars; |
read += chars; |
if (*bhe) { |
memcpy_tofs(buf,offset+(*bhe)->b_data,chars); |
brelse(*bhe); |
buf += chars; |
} else { |
while (chars-->0) |
put_user(0,buf++); |
} |
offset = 0; |
if (++bhe == &buflist[NBUF]) |
bhe = buflist; |
} while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe))); |
} while (left > 0); |
|
/* Release the read-ahead blocks */ |
while (bhe != bhb) { |
brelse(*bhe); |
if (++bhe == &buflist[NBUF]) |
bhe = buflist; |
}; |
if (!read) |
return -EIO; |
filp->f_reada = 1; |
if (!IS_RDONLY(inode)) { |
inode->i_atime = CURRENT_TIME; |
inode->i_dirt = 1; |
} |
return read; |
} |
|
static int ext_file_write(struct inode * inode, struct file * filp, const char * buf, int count) |
{ |
off_t pos; |
int written,c; |
struct buffer_head * bh; |
char * p; |
|
if (!inode) { |
printk("ext_file_write: inode = NULL\n"); |
return -EINVAL; |
} |
if (!S_ISREG(inode->i_mode)) { |
printk("ext_file_write: mode = %07o\n",inode->i_mode); |
return -EINVAL; |
} |
/* |
* ok, append may not work when many processes are writing at the same time |
* but so what. That way leads to madness anyway. |
*/ |
if (filp->f_flags & O_APPEND) |
pos = inode->i_size; |
else |
pos = filp->f_pos; |
written = 0; |
while (written<count) { |
bh = ext_getblk(inode,pos/BLOCK_SIZE,1); |
if (!bh) { |
if (!written) |
written = -ENOSPC; |
break; |
} |
c = BLOCK_SIZE - (pos % BLOCK_SIZE); |
if (c > count-written) |
c = count-written; |
if (c != BLOCK_SIZE && !buffer_uptodate(bh)) { |
ll_rw_block(READ, 1, &bh); |
wait_on_buffer(bh); |
if (!buffer_uptodate(bh)) { |
brelse(bh); |
if (!written) |
written = -EIO; |
break; |
} |
} |
p = (pos % BLOCK_SIZE) + bh->b_data; |
memcpy_fromfs(p,buf,c); |
update_vm_cache(inode, pos, p, c); |
pos += c; |
if (pos > inode->i_size) { |
inode->i_size = pos; |
inode->i_dirt = 1; |
} |
written += c; |
buf += c; |
mark_buffer_uptodate(bh, 1); |
mark_buffer_dirty(bh, 0); |
brelse(bh); |
} |
inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
filp->f_pos = pos; |
inode->i_dirt = 1; |
return written; |
} |
/freelists.c
0,0 → 1,341
/* |
* linux/fs/ext/freelists.c |
* |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* |
*/ |
|
/* freelists.c contains the code that handles the inode and block free lists */ |
|
|
/* |
|
The free blocks are managed by a linked list. The super block contains the |
number of the first free block. This block contains 254 numbers of other |
free blocks and the number of the next block in the list. |
|
When an ext fs is mounted, the number of the first free block is stored |
in s->u.ext_sb.s_firstfreeblocknumber and the block header is stored in |
s->u.ext_sb.s_firstfreeblock. u.ext_sb.s_freeblockscount contains the count |
of free blocks. |
|
The free inodes are also managed by a linked list in a similar way. The |
super block contains the number of the first free inode. This inode contains |
14 numbers of other free inodes and the number of the next inode in the list. |
|
The number of the first free inode is stored in |
s->u.ext_sb.s_firstfreeinodenumber and the header of the block containing |
the inode is stored in s->u.ext_sb.s_firstfreeinodeblock. |
u.ext_sb.s_freeinodescount contains the count of free inodes. |
|
*/ |
|
#include <linux/sched.h> |
#include <linux/ext_fs.h> |
#include <linux/stat.h> |
#include <linux/kernel.h> |
#include <linux/string.h> |
#include <linux/locks.h> |
|
void ext_free_block(struct super_block * sb, int block) |
{ |
struct buffer_head * bh; |
struct ext_free_block * efb; |
|
if (!sb) { |
printk("trying to free block on non-existent device\n"); |
return; |
} |
lock_super (sb); |
if (block < sb->u.ext_sb.s_firstdatazone || |
block >= sb->u.ext_sb.s_nzones) { |
printk("trying to free block not in datazone\n"); |
return; |
} |
bh = get_hash_table(sb->s_dev, block, sb->s_blocksize); |
if (bh) |
mark_buffer_clean(bh); |
brelse(bh); |
if (sb->u.ext_sb.s_firstfreeblock) |
efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data; |
if (!sb->u.ext_sb.s_firstfreeblock || efb->count == 254) { |
#ifdef EXTFS_DEBUG |
printk("ext_free_block: block full, skipping to %d\n", block); |
#endif |
if (sb->u.ext_sb.s_firstfreeblock) |
brelse (sb->u.ext_sb.s_firstfreeblock); |
if (!(sb->u.ext_sb.s_firstfreeblock = bread (sb->s_dev, |
block, sb->s_blocksize))) |
panic ("ext_free_block: unable to read block to free\n"); |
efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data; |
efb->next = sb->u.ext_sb.s_firstfreeblocknumber; |
efb->count = 0; |
sb->u.ext_sb.s_firstfreeblocknumber = block; |
} else { |
efb->free[efb->count++] = block; |
} |
sb->u.ext_sb.s_freeblockscount ++; |
sb->s_dirt = 1; |
mark_buffer_dirty(sb->u.ext_sb.s_firstfreeblock, 1); |
unlock_super (sb); |
return; |
} |
|
int ext_new_block(struct super_block * sb) |
{ |
struct buffer_head * bh; |
struct ext_free_block * efb; |
int j; |
|
if (!sb) { |
printk("trying to get new block from non-existent device\n"); |
return 0; |
} |
if (!sb->u.ext_sb.s_firstfreeblock) |
return 0; |
lock_super (sb); |
efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data; |
if (efb->count) { |
j = efb->free[--efb->count]; |
mark_buffer_dirty(sb->u.ext_sb.s_firstfreeblock, 1); |
} else { |
#ifdef EXTFS_DEBUG |
printk("ext_new_block: block empty, skipping to %d\n", efb->next); |
#endif |
j = sb->u.ext_sb.s_firstfreeblocknumber; |
sb->u.ext_sb.s_firstfreeblocknumber = efb->next; |
brelse (sb->u.ext_sb.s_firstfreeblock); |
if (!sb->u.ext_sb.s_firstfreeblocknumber) { |
sb->u.ext_sb.s_firstfreeblock = NULL; |
} else { |
if (!(sb->u.ext_sb.s_firstfreeblock = bread (sb->s_dev, |
sb->u.ext_sb.s_firstfreeblocknumber, |
sb->s_blocksize))) |
panic ("ext_new_block: unable to read next free block\n"); |
} |
} |
if (j < sb->u.ext_sb.s_firstdatazone || j > sb->u.ext_sb.s_nzones) { |
printk ("ext_new_block: blk = %d\n", j); |
printk("allocating block not in data zone\n"); |
return 0; |
} |
sb->u.ext_sb.s_freeblockscount --; |
sb->s_dirt = 1; |
|
if (!(bh=getblk(sb->s_dev, j, sb->s_blocksize))) { |
printk("new_block: cannot get block"); |
return 0; |
} |
memset(bh->b_data, 0, BLOCK_SIZE); |
mark_buffer_uptodate(bh, 1); |
mark_buffer_dirty(bh, 1); |
brelse(bh); |
#ifdef EXTFS_DEBUG |
printk("ext_new_block: allocating block %d\n", j); |
#endif |
unlock_super (sb); |
return j; |
} |
|
unsigned long ext_count_free_blocks(struct super_block *sb) |
{ |
#ifdef EXTFS_DEBUG |
struct buffer_head * bh; |
struct ext_free_block * efb; |
unsigned long count, block; |
|
lock_super (sb); |
if (!sb->u.ext_sb.s_firstfreeblock) |
count = 0; |
else { |
efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data; |
count = efb->count + 1; |
block = efb->next; |
while (block) { |
if (!(bh = bread (sb->s_dev, block, sb->s_blocksize))) { |
printk ("ext_count_free: error while reading free blocks list\n"); |
block = 0; |
} else { |
efb = (struct ext_free_block *) bh->b_data; |
count += efb->count + 1; |
block = efb->next; |
brelse (bh); |
} |
} |
} |
printk("ext_count_free_blocks: stored = %d, computed = %d\n", |
sb->u.ext_sb.s_freeblockscount, count); |
unlock_super (sb); |
return count; |
#else |
return sb->u.ext_sb.s_freeblockscount; |
#endif |
} |
|
void ext_free_inode(struct inode * inode) |
{ |
struct buffer_head * bh; |
struct ext_free_inode * efi; |
struct super_block * sb; |
unsigned long block; |
unsigned long ino; |
kdev_t dev; |
|
if (!inode) |
return; |
if (!inode->i_dev) { |
printk("free_inode: inode has no device\n"); |
return; |
} |
if (inode->i_count != 1) { |
printk("free_inode: inode has count=%ld\n",inode->i_count); |
return; |
} |
if (inode->i_nlink) { |
printk("free_inode: inode has nlink=%d\n",inode->i_nlink); |
return; |
} |
if (!inode->i_sb) { |
printk("free_inode: inode on non-existent device\n"); |
return; |
} |
sb = inode->i_sb; |
ino = inode->i_ino; |
dev = inode->i_dev; |
clear_inode(inode); |
lock_super (sb); |
if (ino < 1 || ino > sb->u.ext_sb.s_ninodes) { |
printk("free_inode: inode 0 or non-existent inode\n"); |
unlock_super (sb); |
return; |
} |
if (sb->u.ext_sb.s_firstfreeinodeblock) |
efi = ((struct ext_free_inode *) sb->u.ext_sb.s_firstfreeinodeblock->b_data) + |
(sb->u.ext_sb.s_firstfreeinodenumber-1)%EXT_INODES_PER_BLOCK; |
if (!sb->u.ext_sb.s_firstfreeinodeblock || efi->count == 14) { |
#ifdef EXTFS_DEBUG |
printk("ext_free_inode: inode full, skipping to %d\n", ino); |
#endif |
if (sb->u.ext_sb.s_firstfreeinodeblock) |
brelse (sb->u.ext_sb.s_firstfreeinodeblock); |
block = 2 + (ino - 1) / EXT_INODES_PER_BLOCK; |
if (!(bh = bread(dev, block, sb->s_blocksize))) |
panic("ext_free_inode: unable to read inode block\n"); |
efi = ((struct ext_free_inode *) bh->b_data) + |
(ino - 1) % EXT_INODES_PER_BLOCK; |
efi->next = sb->u.ext_sb.s_firstfreeinodenumber; |
efi->count = 0; |
sb->u.ext_sb.s_firstfreeinodenumber = ino; |
sb->u.ext_sb.s_firstfreeinodeblock = bh; |
} else { |
efi->free[efi->count++] = ino; |
} |
sb->u.ext_sb.s_freeinodescount ++; |
sb->s_dirt = 1; |
mark_buffer_dirty(sb->u.ext_sb.s_firstfreeinodeblock, 1); |
unlock_super (sb); |
} |
|
struct inode * ext_new_inode(const struct inode * dir) |
{ |
struct super_block * sb; |
struct inode * inode; |
struct ext_free_inode * efi; |
unsigned long block; |
int j; |
|
if (!dir || !(inode=get_empty_inode())) |
return NULL; |
sb = dir->i_sb; |
inode->i_sb = sb; |
inode->i_flags = sb->s_flags; |
if (!sb->u.ext_sb.s_firstfreeinodeblock) |
return 0; |
lock_super (sb); |
efi = ((struct ext_free_inode *) sb->u.ext_sb.s_firstfreeinodeblock->b_data) + |
(sb->u.ext_sb.s_firstfreeinodenumber-1)%EXT_INODES_PER_BLOCK; |
if (efi->count) { |
j = efi->free[--efi->count]; |
mark_buffer_dirty(sb->u.ext_sb.s_firstfreeinodeblock, 1); |
} else { |
#ifdef EXTFS_DEBUG |
printk("ext_free_inode: inode empty, skipping to %d\n", efi->next); |
#endif |
j = sb->u.ext_sb.s_firstfreeinodenumber; |
if (efi->next > sb->u.ext_sb.s_ninodes) { |
printk ("efi->next = %ld\n", efi->next); |
panic ("ext_new_inode: bad inode number in free list\n"); |
} |
sb->u.ext_sb.s_firstfreeinodenumber = efi->next; |
block = 2 + (((unsigned long) efi->next) - 1) / EXT_INODES_PER_BLOCK; |
brelse (sb->u.ext_sb.s_firstfreeinodeblock); |
if (!sb->u.ext_sb.s_firstfreeinodenumber) { |
sb->u.ext_sb.s_firstfreeinodeblock = NULL; |
} else { |
if (!(sb->u.ext_sb.s_firstfreeinodeblock = |
bread(sb->s_dev, block, sb->s_blocksize))) |
panic ("ext_new_inode: unable to read next free inode block\n"); |
} |
} |
sb->u.ext_sb.s_freeinodescount --; |
sb->s_dirt = 1; |
inode->i_count = 1; |
inode->i_nlink = 1; |
inode->i_dev = sb->s_dev; |
inode->i_uid = current->fsuid; |
inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; |
inode->i_dirt = 1; |
inode->i_ino = j; |
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; |
inode->i_op = NULL; |
inode->i_blocks = inode->i_blksize = 0; |
insert_inode_hash(inode); |
#ifdef EXTFS_DEBUG |
printk("ext_new_inode : allocating inode %d\n", inode->i_ino); |
#endif |
unlock_super (sb); |
return inode; |
} |
|
unsigned long ext_count_free_inodes(struct super_block *sb) |
{ |
#ifdef EXTFS_DEBUG |
struct buffer_head * bh; |
struct ext_free_inode * efi; |
unsigned long count, block, ino; |
|
lock_super (sb); |
if (!sb->u.ext_sb.s_firstfreeinodeblock) |
count = 0; |
else { |
efi = ((struct ext_free_inode *) sb->u.ext_sb.s_firstfreeinodeblock->b_data) + |
((sb->u.ext_sb.s_firstfreeinodenumber-1)%EXT_INODES_PER_BLOCK); |
count = efi->count + 1; |
ino = efi->next; |
while (ino) { |
if (ino < 1 || ino > sb->u.ext_sb.s_ninodes) { |
printk ("u.ext_sb.s_firstfreeinodenumber = %d, ino = %d\n", |
(int) sb->u.ext_sb.s_firstfreeinodenumber,ino); |
panic ("ext_count_fre_inodes: bad inode number in free list\n"); |
} |
block = 2 + ((ino - 1) / EXT_INODES_PER_BLOCK); |
if (!(bh = bread (sb->s_dev, block, sb->s_blocksize))) { |
printk ("ext_count_free_inodes: error while reading free inodes list\n"); |
block = 0; |
} else { |
efi = ((struct ext_free_inode *) bh->b_data) + |
((ino - 1) % EXT_INODES_PER_BLOCK); |
count += efi->count + 1; |
ino = efi->next; |
brelse (bh); |
} |
} |
} |
printk("ext_count_free_inodes: stored = %d, computed = %d\n", |
sb->u.ext_sb.s_freeinodescount, count); |
unlock_super (sb); |
return count; |
#else |
return sb->u.ext_sb.s_freeinodescount; |
#endif |
} |
/fsync.c
0,0 → 1,185
|
/* |
* linux/fs/ext/fsync.c |
* |
* Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk) |
* from |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* from |
* linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds |
* |
* extfs fsync primitive |
*/ |
|
#include <asm/segment.h> |
#include <asm/system.h> |
|
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/stat.h> |
#include <linux/fcntl.h> |
#include <linux/locks.h> |
|
#include <linux/fs.h> |
#include <linux/ext_fs.h> |
|
|
#define blocksize BLOCK_SIZE |
#define addr_per_block 256 |
|
static int sync_block (struct inode * inode, unsigned long * block, int wait) |
{ |
struct buffer_head * bh; |
int tmp; |
|
if (!*block) |
return 0; |
tmp = *block; |
bh = get_hash_table(inode->i_dev, *block, blocksize); |
if (!bh) |
return 0; |
if (*block != tmp) { |
brelse (bh); |
return 1; |
} |
if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { |
brelse(bh); |
return -1; |
} |
if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) |
{ |
brelse(bh); |
return 0; |
} |
ll_rw_block(WRITE, 1, &bh); |
bh->b_count--; |
return 0; |
} |
|
static int sync_iblock (struct inode * inode, unsigned long * iblock, |
struct buffer_head **bh, int wait) |
{ |
int rc, tmp; |
|
*bh = NULL; |
tmp = *iblock; |
if (!tmp) |
return 0; |
rc = sync_block (inode, iblock, wait); |
if (rc) |
return rc; |
*bh = bread(inode->i_dev, tmp, blocksize); |
if (tmp != *iblock) { |
brelse(*bh); |
*bh = NULL; |
return 1; |
} |
if (!*bh) |
return -1; |
return 0; |
} |
|
|
static int sync_direct(struct inode *inode, int wait) |
{ |
int i; |
int rc, err = 0; |
|
for (i = 0; i < 9; i++) { |
rc = sync_block (inode, inode->u.ext_i.i_data + i, wait); |
if (rc > 0) |
break; |
if (rc) |
err = rc; |
} |
return err; |
} |
|
static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait) |
{ |
int i; |
struct buffer_head * ind_bh; |
int rc, err = 0; |
|
rc = sync_iblock (inode, iblock, &ind_bh, wait); |
if (rc || !ind_bh) |
return rc; |
|
for (i = 0; i < addr_per_block; i++) { |
rc = sync_block (inode, |
((unsigned long *) ind_bh->b_data) + i, |
wait); |
if (rc > 0) |
break; |
if (rc) |
err = rc; |
} |
brelse(ind_bh); |
return err; |
} |
|
static int sync_dindirect(struct inode *inode, unsigned long *diblock, |
int wait) |
{ |
int i; |
struct buffer_head * dind_bh; |
int rc, err = 0; |
|
rc = sync_iblock (inode, diblock, &dind_bh, wait); |
if (rc || !dind_bh) |
return rc; |
|
for (i = 0; i < addr_per_block; i++) { |
rc = sync_indirect (inode, |
((unsigned long *) dind_bh->b_data) + i, |
wait); |
if (rc > 0) |
break; |
if (rc) |
err = rc; |
} |
brelse(dind_bh); |
return err; |
} |
|
static int sync_tindirect(struct inode *inode, unsigned long *tiblock, |
int wait) |
{ |
int i; |
struct buffer_head * tind_bh; |
int rc, err = 0; |
|
rc = sync_iblock (inode, tiblock, &tind_bh, wait); |
if (rc || !tind_bh) |
return rc; |
|
for (i = 0; i < addr_per_block; i++) { |
rc = sync_dindirect (inode, |
((unsigned long *) tind_bh->b_data) + i, |
wait); |
if (rc > 0) |
break; |
if (rc) |
err = rc; |
} |
brelse(tind_bh); |
return err; |
} |
|
int ext_sync_file(struct inode * inode, struct file *file) |
{ |
int wait, err = 0; |
|
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || |
S_ISLNK(inode->i_mode))) |
return -EINVAL; |
for (wait=0; wait<=1; wait++) |
{ |
err |= sync_direct(inode, wait); |
err |= sync_indirect(inode, inode->u.ext_i.i_data+9, wait); |
err |= sync_dindirect(inode, inode->u.ext_i.i_data+10, wait); |
err |= sync_tindirect(inode, inode->u.ext_i.i_data+11, wait); |
} |
err |= ext_sync_inode (inode); |
return (err < 0) ? -EIO : 0; |
} |
/truncate.c
0,0 → 1,252
/* |
* linux/fs/ext/truncate.c |
* |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* |
* from |
* |
* linux/fs/minix/truncate.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
|
#include <linux/sched.h> |
#include <linux/ext_fs.h> |
#include <linux/stat.h> |
#include <linux/fcntl.h> |
#include <linux/errno.h> |
|
/* |
* Truncate has the most races in the whole filesystem: coding it is |
* a pain in the a**. Especially as I don't do any locking... |
* |
* The code may look a bit weird, but that's just because I've tried to |
* handle things like file-size changes in a somewhat graceful manner. |
* Anyway, truncating a file at the same time somebody else writes to it |
* is likely to result in pretty weird behaviour... |
* |
* The new code handles normal truncates (size = 0) as well as the more |
* general case (size = XXX). I hope. |
*/ |
|
static int trunc_direct(struct inode * inode) |
{ |
int i, tmp; |
unsigned long * p; |
struct buffer_head * bh; |
int retry = 0; |
#define DIRECT_BLOCK ((inode->i_size + 1023) >> 10) |
|
repeat: |
for (i = DIRECT_BLOCK ; i < 9 ; i++) { |
p = inode->u.ext_i.i_data+i; |
if (!(tmp = *p)) |
continue; |
bh = getblk(inode->i_dev,tmp,BLOCK_SIZE); |
if (i < DIRECT_BLOCK) { |
brelse(bh); |
goto repeat; |
} |
if ((bh && bh->b_count != 1) || tmp != *p) { |
retry = 1; |
brelse(bh); |
continue; |
} |
*p = 0; |
inode->i_dirt = 1; |
brelse(bh); |
ext_free_block(inode->i_sb,tmp); |
} |
return retry; |
} |
|
static int trunc_indirect(struct inode * inode, int offset, unsigned long * p) |
{ |
int i, tmp; |
struct buffer_head * bh; |
struct buffer_head * ind_bh; |
unsigned long * ind; |
int retry = 0; |
#define INDIRECT_BLOCK (DIRECT_BLOCK-offset) |
|
tmp = *p; |
if (!tmp) |
return 0; |
ind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); |
if (tmp != *p) { |
brelse(ind_bh); |
return 1; |
} |
if (!ind_bh) { |
*p = 0; |
return 0; |
} |
repeat: |
for (i = INDIRECT_BLOCK ; i < 256 ; i++) { |
if (i < 0) |
i = 0; |
if (i < INDIRECT_BLOCK) |
goto repeat; |
ind = i+(unsigned long *) ind_bh->b_data; |
tmp = *ind; |
if (!tmp) |
continue; |
bh = getblk(inode->i_dev,tmp,BLOCK_SIZE); |
if (i < INDIRECT_BLOCK) { |
brelse(bh); |
goto repeat; |
} |
if ((bh && bh->b_count != 1) || tmp != *ind) { |
retry = 1; |
brelse(bh); |
continue; |
} |
*ind = 0; |
mark_buffer_dirty(ind_bh, 1); |
brelse(bh); |
ext_free_block(inode->i_sb,tmp); |
} |
ind = (unsigned long *) ind_bh->b_data; |
for (i = 0; i < 256; i++) |
if (*(ind++)) |
break; |
if (i >= 256) |
if (ind_bh->b_count != 1) |
retry = 1; |
else { |
tmp = *p; |
*p = 0; |
inode->i_dirt = 1; |
ext_free_block(inode->i_sb,tmp); |
} |
brelse(ind_bh); |
return retry; |
} |
|
static int trunc_dindirect(struct inode * inode, int offset, unsigned long * p) |
{ |
int i,tmp; |
struct buffer_head * dind_bh; |
unsigned long * dind; |
int retry = 0; |
#define DINDIRECT_BLOCK ((DIRECT_BLOCK-offset)>>8) |
|
tmp = *p; |
if (!tmp) |
return 0; |
dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); |
if (tmp != *p) { |
brelse(dind_bh); |
return 1; |
} |
if (!dind_bh) { |
*p = 0; |
return 0; |
} |
repeat: |
for (i = DINDIRECT_BLOCK ; i < 256 ; i ++) { |
if (i < 0) |
i = 0; |
if (i < DINDIRECT_BLOCK) |
goto repeat; |
dind = i+(unsigned long *) dind_bh->b_data; |
tmp = *dind; |
if (!tmp) |
continue; |
retry |= trunc_indirect(inode,offset+(i<<8),dind); |
mark_buffer_dirty(dind_bh, 1); |
} |
dind = (unsigned long *) dind_bh->b_data; |
for (i = 0; i < 256; i++) |
if (*(dind++)) |
break; |
if (i >= 256) |
if (dind_bh->b_count != 1) |
retry = 1; |
else { |
tmp = *p; |
*p = 0; |
inode->i_dirt = 1; |
ext_free_block(inode->i_sb,tmp); |
} |
brelse(dind_bh); |
return retry; |
} |
|
static int trunc_tindirect(struct inode * inode) |
{ |
int i,tmp; |
struct buffer_head * tind_bh; |
unsigned long * tind, * p; |
int retry = 0; |
#define TINDIRECT_BLOCK ((DIRECT_BLOCK-(256*256+256+9))>>16) |
|
p = inode->u.ext_i.i_data+11; |
if (!(tmp = *p)) |
return 0; |
tind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); |
if (tmp != *p) { |
brelse(tind_bh); |
return 1; |
} |
if (!tind_bh) { |
*p = 0; |
return 0; |
} |
repeat: |
for (i = TINDIRECT_BLOCK ; i < 256 ; i ++) { |
if (i < 0) |
i = 0; |
if (i < TINDIRECT_BLOCK) |
goto repeat; |
tind = i+(unsigned long *) tind_bh->b_data; |
retry |= trunc_dindirect(inode,9+256+256*256+(i<<16),tind); |
mark_buffer_dirty(tind_bh, 1); |
} |
tind = (unsigned long *) tind_bh->b_data; |
for (i = 0; i < 256; i++) |
if (*(tind++)) |
break; |
if (i >= 256) |
if (tind_bh->b_count != 1) |
retry = 1; |
else { |
tmp = *p; |
*p = 0; |
inode->i_dirt = 1; |
ext_free_block(inode->i_sb,tmp); |
} |
brelse(tind_bh); |
return retry; |
} |
|
void ext_truncate(struct inode * inode) |
{ |
int retry; |
|
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || |
S_ISLNK(inode->i_mode))) |
return; |
while (1) { |
retry = trunc_direct(inode); |
retry |= trunc_indirect(inode,9,inode->u.ext_i.i_data+9); |
retry |= trunc_dindirect(inode,9+256,inode->u.ext_i.i_data+10); |
retry |= trunc_tindirect(inode); |
if (!retry) |
break; |
current->counter = 0; |
schedule(); |
} |
inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
inode->i_dirt = 1; |
} |
|
/* |
* Called when a inode is released. Note that this is different |
* from ext_open: open gets called at every open, but release |
* gets called only when /all/ the files are closed. |
*/ |
void ext_release(struct inode * inode, struct file * filp) |
{ |
printk("ext_release not implemented\n"); |
} |
/symlink.c
0,0 → 1,110
/* |
* linux/fs/ext/symlink.c |
* |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* |
* from |
* |
* linux/fs/minix/symlink.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* ext symlink handling code |
*/ |
|
#include <asm/segment.h> |
|
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/fs.h> |
#include <linux/ext_fs.h> |
#include <linux/stat.h> |
|
static int ext_readlink(struct inode *, char *, int); |
static int ext_follow_link(struct inode *, struct inode *, int, int, struct inode **); |
|
/* |
* symlinks can't do much... |
*/ |
struct inode_operations ext_symlink_inode_operations = { |
NULL, /* no file-operations */ |
NULL, /* create */ |
NULL, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
ext_readlink, /* readlink */ |
ext_follow_link, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
static int ext_follow_link(struct inode * dir, struct inode * inode, |
int flag, int mode, struct inode ** res_inode) |
{ |
int error; |
struct buffer_head * bh; |
|
*res_inode = NULL; |
if (!dir) { |
dir = current->fs->root; |
dir->i_count++; |
} |
if (!inode) { |
iput(dir); |
return -ENOENT; |
} |
if (!S_ISLNK(inode->i_mode)) { |
iput(dir); |
*res_inode = inode; |
return 0; |
} |
if (current->link_count > 5) { |
iput(dir); |
iput(inode); |
return -ELOOP; |
} |
if (!(bh = ext_bread(inode, 0, 0))) { |
iput(inode); |
iput(dir); |
return -EIO; |
} |
iput(inode); |
current->link_count++; |
error = open_namei(bh->b_data,flag,mode,res_inode,dir); |
current->link_count--; |
brelse(bh); |
return error; |
} |
|
static int ext_readlink(struct inode * inode, char * buffer, int buflen) |
{ |
struct buffer_head * bh; |
int i; |
char c; |
|
if (!S_ISLNK(inode->i_mode)) { |
iput(inode); |
return -EINVAL; |
} |
if (buflen > 1023) |
buflen = 1023; |
bh = ext_bread(inode, 0, 0); |
iput(inode); |
if (!bh) |
return 0; |
i = 0; |
while (i<buflen && (c = bh->b_data[i])) { |
i++; |
put_user(c,buffer++); |
} |
brelse(bh); |
return i; |
} |
/namei.c
0,0 → 1,903
/* |
* linux/fs/ext/namei.c |
* |
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr) |
* |
* from |
* |
* linux/fs/minix/namei.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
|
#include <linux/sched.h> |
#include <linux/ext_fs.h> |
#include <linux/kernel.h> |
#include <linux/string.h> |
#include <linux/stat.h> |
#include <linux/fcntl.h> |
#include <linux/errno.h> |
|
#include <asm/segment.h> |
|
/* |
* comment out this line if you want names > EXT_NAME_LEN chars to be |
* truncated. Else they will be disallowed. |
*/ |
/* #define NO_TRUNCATE */ |
|
/* |
* EXT_DIR_PAD defines the directory entries boundaries |
* |
* NOTE: It must be a power of 2 and must be greater or equal than 8 |
* because a directory entry needs 8 bytes for its fixed part |
* (4 bytes for the inode, 2 bytes for the entry length and 2 bytes |
* for the name length) |
*/ |
#define EXT_DIR_PAD 8 |
|
/* |
* |
* EXT_DIR_MIN_SIZE is the minimal size of a directory entry |
* |
* During allocations, a directory entry is split into 2 ones |
* *ONLY* if the size of the unused part is greater than or |
* equal to EXT_DIR_MIN_SIZE |
*/ |
#define EXT_DIR_MIN_SIZE 12 |
|
/* |
* ok, we cannot use strncmp, as the name is not in our data space. |
* Thus we'll have to use ext_match. No big problem. Match also makes |
* some sanity tests. |
* |
* NOTE! unlike strncmp, ext_match returns 1 for success, 0 for failure. |
*/ |
static int ext_match(int len,const char * name,struct ext_dir_entry * de) |
{ |
if (!de || !de->inode || len > EXT_NAME_LEN) |
return 0; |
/* "" means "." ---> so paths like "/usr/lib//libc.a" work */ |
if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) |
return 1; |
if (len != de->name_len) |
return 0; |
return !memcmp(name, de->name, len); |
} |
|
/* |
* ext_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. |
* |
* addition for the ext file system : this function returns the previous |
* and next directory entries in the parameters prev_dir and next_dir |
*/ |
static struct buffer_head * ext_find_entry(struct inode * dir, |
const char * name, int namelen, struct ext_dir_entry ** res_dir, |
struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir) |
{ |
long offset; |
struct buffer_head * bh; |
struct ext_dir_entry * de; |
|
*res_dir = NULL; |
if (!dir) |
return NULL; |
#ifdef NO_TRUNCATE |
if (namelen > EXT_NAME_LEN) |
return NULL; |
#else |
if (namelen > EXT_NAME_LEN) |
namelen = EXT_NAME_LEN; |
#endif |
bh = ext_bread(dir,0,0); |
if (!bh) |
return NULL; |
if (prev_dir) |
*prev_dir = NULL; |
if (next_dir) |
*next_dir = NULL; |
offset = 0; |
de = (struct ext_dir_entry *) bh->b_data; |
while (offset < dir->i_size) { |
if ((char *)de >= BLOCK_SIZE+bh->b_data) { |
brelse(bh); |
bh = NULL; |
bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0); |
if (!bh) |
continue; |
de = (struct ext_dir_entry *) bh->b_data; |
if (prev_dir) |
*prev_dir = NULL; |
} |
if (de->rec_len < 8 || de->rec_len % 8 != 0 || |
de->rec_len < de->name_len + 8 || |
(((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) { |
printk ("ext_find_entry: bad dir entry\n"); |
printk ("dev=%s, dir=%ld, offset=%ld, " |
"rec_len=%d, name_len=%d\n", |
kdevname(dir->i_dev), dir->i_ino, offset, |
de->rec_len, de->name_len); |
de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE); |
offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE; |
continue; |
/* brelse (bh); |
return NULL; */ |
} |
if (ext_match(namelen,name,de)) { |
*res_dir = de; |
if (next_dir) |
if (offset + de->rec_len < dir->i_size && |
((char *)de) + de->rec_len < BLOCK_SIZE+bh->b_data) |
*next_dir = (struct ext_dir_entry *) |
((char *) de + de->rec_len); |
else |
*next_dir = NULL; |
return bh; |
} |
offset += de->rec_len; |
if (prev_dir) |
*prev_dir = de; |
de = (struct ext_dir_entry *) ((char *) de + de->rec_len); |
} |
brelse(bh); |
return NULL; |
} |
|
int ext_lookup(struct inode * dir,const char * name, int len, |
struct inode ** result) |
{ |
int ino; |
struct ext_dir_entry * de; |
struct buffer_head * bh; |
|
*result = NULL; |
if (!dir) |
return -ENOENT; |
if (!S_ISDIR(dir->i_mode)) { |
iput(dir); |
return -ENOENT; |
} |
if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) { |
iput(dir); |
return -ENOENT; |
} |
ino = de->inode; |
brelse(bh); |
if (!(*result = iget(dir->i_sb,ino))) { |
iput(dir); |
return -EACCES; |
} |
iput(dir); |
return 0; |
} |
|
/* |
* ext_add_entry() |
* |
* adds a file entry to the specified directory, using the same |
* semantics as ext_find_entry(). It returns NULL if it failed. |
* |
* NOTE!! The inode part of 'de' is left at 0 - which means you |
* may not sleep between calling this and putting something into |
* the entry, as someone else might have used it while you slept. |
*/ |
static struct buffer_head * ext_add_entry(struct inode * dir, |
const char * name, int namelen, struct ext_dir_entry ** res_dir) |
{ |
int i; |
long offset; |
unsigned short rec_len; |
struct buffer_head * bh; |
struct ext_dir_entry * de, * de1; |
|
*res_dir = NULL; |
if (!dir) |
return NULL; |
#ifdef NO_TRUNCATE |
if (namelen > EXT_NAME_LEN) |
return NULL; |
#else |
if (namelen > EXT_NAME_LEN) |
namelen = EXT_NAME_LEN; |
#endif |
if (!namelen) |
return NULL; |
bh = ext_bread(dir,0,0); |
if (!bh) |
return NULL; |
rec_len = ((8 + namelen + EXT_DIR_PAD - 1) / EXT_DIR_PAD) * EXT_DIR_PAD; |
offset = 0; |
de = (struct ext_dir_entry *) bh->b_data; |
while (1) { |
if ((char *)de >= BLOCK_SIZE+bh->b_data && offset < dir->i_size) { |
#ifdef EXTFS_DEBUG |
printk ("ext_add_entry: skipping to next block\n"); |
#endif |
brelse(bh); |
bh = NULL; |
bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0); |
if (!bh) |
return NULL; |
de = (struct ext_dir_entry *) bh->b_data; |
} |
if (offset >= dir->i_size) { |
/* Check that the directory entry fits in the block */ |
if (offset % BLOCK_SIZE == 0 || |
(BLOCK_SIZE - (offset % BLOCK_SIZE)) < rec_len) { |
if ((offset % BLOCK_SIZE) != 0) { |
/* If the entry does not fit in the |
block, the remainder of the block |
becomes an unused entry */ |
de->inode = 0; |
de->rec_len = BLOCK_SIZE |
- (offset & (BLOCK_SIZE - 1)); |
de->name_len = 0; |
offset += de->rec_len; |
dir->i_size += de->rec_len; |
dir->i_dirt = 1; |
#if 0 |
dir->i_ctime = CURRENT_TIME; |
#endif |
mark_buffer_dirty(bh, 1); |
} |
brelse (bh); |
bh = NULL; |
#ifdef EXTFS_DEBUG |
printk ("ext_add_entry : creating next block\n"); |
#endif |
bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,1); |
if (!bh) |
return NULL; /* Other thing to do ??? */ |
de = (struct ext_dir_entry *) bh->b_data; |
} |
/* Allocate the entry */ |
de->inode=0; |
de->rec_len = rec_len; |
dir->i_size += de->rec_len; |
dir->i_dirt = 1; |
#if 0 |
dir->i_ctime = CURRENT_TIME; |
#endif |
} |
if (de->rec_len < 8 || de->rec_len % 4 != 0 || |
de->rec_len < de->name_len + 8 || |
(((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) { |
printk ("ext_addr_entry: bad dir entry\n"); |
printk ("dev=%s, dir=%ld, offset=%ld, " |
"rec_len=%d, name_len=%d\n", |
kdevname(dir->i_dev), dir->i_ino, offset, |
de->rec_len, de->name_len); |
brelse (bh); |
return NULL; |
} |
if (!de->inode && de->rec_len >= rec_len) { |
if (de->rec_len > rec_len |
&& de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) { |
/* The found entry is too big : it is split |
into 2 ones : |
- the 1st one will be used to hold the name, |
- the 2nd one is unused */ |
de1 = (struct ext_dir_entry *) ((char *) de + rec_len); |
de1->inode = 0; |
de1->rec_len = de->rec_len - rec_len; |
de1->name_len = 0; |
de->rec_len = rec_len; |
} |
dir->i_mtime = dir->i_ctime = CURRENT_TIME; |
dir->i_dirt = 1; |
de->name_len = namelen; |
for (i=0; i < namelen ; i++) |
de->name[i] = name[i]; |
mark_buffer_dirty(bh, 1); |
*res_dir = de; |
return bh; |
} |
offset += de->rec_len; |
de = (struct ext_dir_entry *) ((char *) de + de->rec_len); |
} |
brelse(bh); |
return NULL; |
} |
|
int ext_create(struct inode * dir,const char * name, int len, int mode, |
struct inode ** result) |
{ |
struct inode * inode; |
struct buffer_head * bh; |
struct ext_dir_entry * de; |
|
*result = NULL; |
if (!dir) |
return -ENOENT; |
inode = ext_new_inode(dir); |
if (!inode) { |
iput(dir); |
return -ENOSPC; |
} |
inode->i_op = &ext_file_inode_operations; |
inode->i_mode = mode; |
inode->i_dirt = 1; |
bh = ext_add_entry(dir,name,len,&de); |
if (!bh) { |
inode->i_nlink--; |
inode->i_dirt = 1; |
iput(inode); |
iput(dir); |
return -ENOSPC; |
} |
de->inode = inode->i_ino; |
mark_buffer_dirty(bh, 1); |
brelse(bh); |
iput(dir); |
*result = inode; |
return 0; |
} |
|
int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev) |
{ |
struct inode * inode; |
struct buffer_head * bh; |
struct ext_dir_entry * de; |
|
if (!dir) |
return -ENOENT; |
bh = ext_find_entry(dir,name,len,&de,NULL,NULL); |
if (bh) { |
brelse(bh); |
iput(dir); |
return -EEXIST; |
} |
inode = ext_new_inode(dir); |
if (!inode) { |
iput(dir); |
return -ENOSPC; |
} |
inode->i_uid = current->fsuid; |
inode->i_mode = mode; |
inode->i_op = NULL; |
if (S_ISREG(inode->i_mode)) |
inode->i_op = &ext_file_inode_operations; |
else if (S_ISDIR(inode->i_mode)) { |
inode->i_op = &ext_dir_inode_operations; |
if (dir->i_mode & S_ISGID) |
inode->i_mode |= S_ISGID; |
} |
else if (S_ISLNK(inode->i_mode)) |
inode->i_op = &ext_symlink_inode_operations; |
else if (S_ISCHR(inode->i_mode)) |
inode->i_op = &chrdev_inode_operations; |
else if (S_ISBLK(inode->i_mode)) |
inode->i_op = &blkdev_inode_operations; |
else if (S_ISFIFO(inode->i_mode)) |
init_fifo(inode); |
if (S_ISBLK(mode) || S_ISCHR(mode)) |
inode->i_rdev = to_kdev_t(rdev); |
#if 0 |
inode->i_mtime = inode->i_atime = CURRENT_TIME; |
#endif |
inode->i_dirt = 1; |
bh = ext_add_entry(dir,name,len,&de); |
if (!bh) { |
inode->i_nlink--; |
inode->i_dirt = 1; |
iput(inode); |
iput(dir); |
return -ENOSPC; |
} |
de->inode = inode->i_ino; |
mark_buffer_dirty(bh, 1); |
brelse(bh); |
iput(dir); |
iput(inode); |
return 0; |
} |
|
int ext_mkdir(struct inode * dir, const char * name, int len, int mode) |
{ |
struct inode * inode; |
struct buffer_head * bh, *dir_block; |
struct ext_dir_entry * de; |
|
bh = ext_find_entry(dir,name,len,&de,NULL,NULL); |
if (bh) { |
brelse(bh); |
iput(dir); |
return -EEXIST; |
} |
inode = ext_new_inode(dir); |
if (!inode) { |
iput(dir); |
return -ENOSPC; |
} |
inode->i_op = &ext_dir_inode_operations; |
inode->i_size = 2 * 16; /* Each entry is coded on 16 bytes for "." and ".." |
- 4 bytes for the inode number, |
- 2 bytes for the record length |
- 2 bytes for the name length |
- 8 bytes for the name */ |
#if 0 |
inode->i_mtime = inode->i_atime = CURRENT_TIME; |
#endif |
dir_block = ext_bread(inode,0,1); |
if (!dir_block) { |
iput(dir); |
inode->i_nlink--; |
inode->i_dirt = 1; |
iput(inode); |
return -ENOSPC; |
} |
de = (struct ext_dir_entry *) dir_block->b_data; |
de->inode=inode->i_ino; |
de->rec_len=16; |
de->name_len=1; |
strcpy(de->name,"."); |
de = (struct ext_dir_entry *) ((char *) de + de->rec_len); |
de->inode = dir->i_ino; |
de->rec_len=16; |
de->name_len=2; |
strcpy(de->name,".."); |
inode->i_nlink = 2; |
mark_buffer_dirty(dir_block, 1); |
brelse(dir_block); |
inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask); |
if (dir->i_mode & S_ISGID) |
inode->i_mode |= S_ISGID; |
inode->i_dirt = 1; |
bh = ext_add_entry(dir,name,len,&de); |
if (!bh) { |
iput(dir); |
inode->i_nlink=0; |
iput(inode); |
return -ENOSPC; |
} |
de->inode = inode->i_ino; |
mark_buffer_dirty(bh, 1); |
dir->i_nlink++; |
dir->i_dirt = 1; |
iput(dir); |
iput(inode); |
brelse(bh); |
return 0; |
} |
|
/* |
* routine to check that the specified directory is empty (for rmdir) |
*/ |
static int empty_dir(struct inode * inode) |
{ |
unsigned long offset; |
struct buffer_head * bh; |
struct ext_dir_entry * de, * de1; |
|
if (inode->i_size < 2 * 12 || !(bh = ext_bread(inode,0,0))) { |
printk("warning - bad directory on dev %s\n", |
kdevname(inode->i_dev)); |
return 1; |
} |
de = (struct ext_dir_entry *) bh->b_data; |
de1 = (struct ext_dir_entry *) ((char *) de + de->rec_len); |
if (de->inode != inode->i_ino || !de1->inode || |
strcmp(".",de->name) || strcmp("..",de1->name)) { |
printk("warning - bad directory on dev %s\n", |
kdevname(inode->i_dev)); |
return 1; |
} |
offset = de->rec_len + de1->rec_len; |
de = (struct ext_dir_entry *) ((char *) de1 + de1->rec_len); |
while (offset < inode->i_size ) { |
if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) { |
brelse(bh); |
bh = ext_bread(inode, offset >> BLOCK_SIZE_BITS,1); |
if (!bh) { |
offset += BLOCK_SIZE; |
continue; |
} |
de = (struct ext_dir_entry *) bh->b_data; |
} |
if (de->rec_len < 8 || de->rec_len %4 != 0 || |
de->rec_len < de->name_len + 8) { |
printk ("empty_dir: bad dir entry\n"); |
printk ("dev=%s, dir=%ld, offset=%ld, " |
"rec_len=%d, name_len=%d\n", |
kdevname(inode->i_dev), inode->i_ino, |
offset, de->rec_len, de->name_len); |
brelse (bh); |
return 1; |
} |
if (de->inode) { |
brelse(bh); |
return 0; |
} |
offset += de->rec_len; |
de = (struct ext_dir_entry *) ((char *) de + de->rec_len); |
} |
brelse(bh); |
return 1; |
} |
|
static inline void ext_merge_entries (struct ext_dir_entry * de, |
struct ext_dir_entry * pde, struct ext_dir_entry * nde) |
{ |
if (nde && !nde->inode) |
de->rec_len += nde->rec_len; |
if (pde && !pde->inode) |
pde->rec_len += de->rec_len; |
} |
|
int ext_rmdir(struct inode * dir, const char * name, int len) |
{ |
int retval; |
struct inode * inode; |
struct buffer_head * bh; |
struct ext_dir_entry * de, * pde, * nde; |
|
inode = NULL; |
bh = ext_find_entry(dir,name,len,&de,&pde,&nde); |
retval = -ENOENT; |
if (!bh) |
goto end_rmdir; |
retval = -EPERM; |
if (!(inode = iget(dir->i_sb, de->inode))) |
goto end_rmdir; |
if ((dir->i_mode & S_ISVTX) && !fsuser() && |
current->fsuid != inode->i_uid && |
current->fsuid != dir->i_uid) |
goto end_rmdir; |
if (inode->i_dev != dir->i_dev) |
goto end_rmdir; |
if (inode == dir) /* we may not delete ".", but "../dir" is ok */ |
goto end_rmdir; |
if (!S_ISDIR(inode->i_mode)) { |
retval = -ENOTDIR; |
goto end_rmdir; |
} |
if (!empty_dir(inode)) { |
retval = -ENOTEMPTY; |
goto end_rmdir; |
} |
if (inode->i_count > 1) { |
retval = -EBUSY; |
goto end_rmdir; |
} |
if (inode->i_nlink != 2) |
printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink); |
de->inode = 0; |
de->name_len = 0; |
ext_merge_entries (de, pde, nde); |
mark_buffer_dirty(bh, 1); |
inode->i_nlink=0; |
inode->i_dirt=1; |
dir->i_nlink--; |
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; |
dir->i_dirt=1; |
retval = 0; |
end_rmdir: |
iput(dir); |
iput(inode); |
brelse(bh); |
return retval; |
} |
|
int ext_unlink(struct inode * dir, const char * name, int len) |
{ |
int retval; |
struct inode * inode; |
struct buffer_head * bh; |
struct ext_dir_entry * de, * pde, * nde; |
|
retval = -ENOENT; |
inode = NULL; |
bh = ext_find_entry(dir,name,len,&de,&pde,&nde); |
if (!bh) |
goto end_unlink; |
if (!(inode = iget(dir->i_sb, de->inode))) |
goto end_unlink; |
retval = -EPERM; |
if ((dir->i_mode & S_ISVTX) && !fsuser() && |
current->fsuid != inode->i_uid && |
current->fsuid != dir->i_uid) |
goto end_unlink; |
if (S_ISDIR(inode->i_mode)) |
goto end_unlink; |
if (!inode->i_nlink) { |
printk("Deleting nonexistent file (%s:%ld), %d\n", |
kdevname(inode->i_dev), inode->i_ino, |
inode->i_nlink); |
inode->i_nlink=1; |
} |
de->inode = 0; |
de->name_len = 0; |
ext_merge_entries (de, pde, nde); |
mark_buffer_dirty(bh, 1); |
inode->i_nlink--; |
inode->i_dirt = 1; |
inode->i_ctime = CURRENT_TIME; |
dir->i_ctime = dir->i_mtime = inode->i_ctime; |
dir->i_dirt = 1; |
retval = 0; |
end_unlink: |
brelse(bh); |
iput(inode); |
iput(dir); |
return retval; |
} |
|
int ext_symlink(struct inode * dir, const char * name, int len, const char * symname) |
{ |
struct ext_dir_entry * de; |
struct inode * inode = NULL; |
struct buffer_head * bh = NULL, * name_block = NULL; |
int i; |
char c; |
|
if (!(inode = ext_new_inode(dir))) { |
iput(dir); |
return -ENOSPC; |
} |
inode->i_mode = S_IFLNK | 0777; |
inode->i_op = &ext_symlink_inode_operations; |
name_block = ext_bread(inode,0,1); |
if (!name_block) { |
iput(dir); |
inode->i_nlink--; |
inode->i_dirt = 1; |
iput(inode); |
return -ENOSPC; |
} |
i = 0; |
while (i < 1023 && (c = *(symname++))) |
name_block->b_data[i++] = c; |
name_block->b_data[i] = 0; |
mark_buffer_dirty(name_block, 1); |
brelse(name_block); |
inode->i_size = i; |
inode->i_dirt = 1; |
bh = ext_find_entry(dir,name,len,&de,NULL,NULL); |
if (bh) { |
inode->i_nlink--; |
inode->i_dirt = 1; |
iput(inode); |
brelse(bh); |
iput(dir); |
return -EEXIST; |
} |
bh = ext_add_entry(dir,name,len,&de); |
if (!bh) { |
inode->i_nlink--; |
inode->i_dirt = 1; |
iput(inode); |
iput(dir); |
return -ENOSPC; |
} |
de->inode = inode->i_ino; |
mark_buffer_dirty(bh, 1); |
brelse(bh); |
iput(dir); |
iput(inode); |
return 0; |
} |
|
int ext_link(struct inode * oldinode, struct inode * dir, const char * name, int len) |
{ |
struct ext_dir_entry * de; |
struct buffer_head * bh; |
|
if (S_ISDIR(oldinode->i_mode)) { |
iput(oldinode); |
iput(dir); |
return -EPERM; |
} |
if (oldinode->i_nlink > 32000) { |
iput(oldinode); |
iput(dir); |
return -EMLINK; |
} |
bh = ext_find_entry(dir,name,len,&de,NULL,NULL); |
if (bh) { |
brelse(bh); |
iput(dir); |
iput(oldinode); |
return -EEXIST; |
} |
bh = ext_add_entry(dir,name,len,&de); |
if (!bh) { |
iput(dir); |
iput(oldinode); |
return -ENOSPC; |
} |
de->inode = oldinode->i_ino; |
mark_buffer_dirty(bh, 1); |
brelse(bh); |
iput(dir); |
oldinode->i_nlink++; |
oldinode->i_ctime = CURRENT_TIME; |
oldinode->i_dirt = 1; |
iput(oldinode); |
return 0; |
} |
|
static int subdir(struct inode * new_inode, struct inode * old_inode) |
{ |
int ino; |
int result; |
|
new_inode->i_count++; |
result = 0; |
for (;;) { |
if (new_inode == old_inode) { |
result = 1; |
break; |
} |
if (new_inode->i_dev != old_inode->i_dev) |
break; |
ino = new_inode->i_ino; |
if (ext_lookup(new_inode,"..",2,&new_inode)) |
break; |
if (new_inode->i_ino == ino) |
break; |
} |
iput(new_inode); |
return result; |
} |
|
#define PARENT_INO(buffer) \ |
((struct ext_dir_entry *) ((char *) buffer + \ |
((struct ext_dir_entry *) buffer)->rec_len))->inode |
|
#define PARENT_NAME(buffer) \ |
((struct ext_dir_entry *) ((char *) buffer + \ |
((struct ext_dir_entry *) buffer)->rec_len))->name |
|
/* |
* rename uses retrying to avoid race-conditions: at least they should be minimal. |
* it tries to allocate all the blocks, then sanity-checks, and if the sanity- |
* checks fail, it tries to restart itself again. Very practical - no changes |
* are done until we know everything works ok.. and then all the changes can be |
* done in one fell swoop when we have claimed all the buffers needed. |
* |
* Anybody can rename anything with this: the permission checks are left to the |
* higher-level routines. |
*/ |
static int do_ext_rename(struct inode * old_dir, const char * old_name, int old_len, |
struct inode * new_dir, const char * new_name, int new_len) |
{ |
struct inode * old_inode, * new_inode; |
struct buffer_head * old_bh, * new_bh, * dir_bh; |
struct ext_dir_entry * old_de, * new_de, * pde, * nde; |
int retval; |
|
goto start_up; |
try_again: |
brelse(old_bh); |
brelse(new_bh); |
brelse(dir_bh); |
iput(old_inode); |
iput(new_inode); |
current->counter = 0; |
schedule(); |
start_up: |
old_inode = new_inode = NULL; |
old_bh = new_bh = dir_bh = NULL; |
old_bh = ext_find_entry(old_dir,old_name,old_len,&old_de,&pde,&nde); |
retval = -ENOENT; |
if (!old_bh) |
goto end_rename; |
old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */ |
if (!old_inode) |
goto end_rename; |
retval = -EPERM; |
if ((old_dir->i_mode & S_ISVTX) && |
current->fsuid != old_inode->i_uid && |
current->fsuid != old_dir->i_uid && !fsuser()) |
goto end_rename; |
new_bh = ext_find_entry(new_dir,new_name,new_len,&new_de,NULL,NULL); |
if (new_bh) { |
new_inode = __iget(new_dir->i_sb, new_de->inode,0); /* don't cross mnt-points */ |
if (!new_inode) { |
brelse(new_bh); |
new_bh = NULL; |
} |
} |
if (new_inode == old_inode) { |
retval = 0; |
goto end_rename; |
} |
if (new_inode && S_ISDIR(new_inode->i_mode)) { |
retval = -EEXIST; |
goto end_rename; |
} |
retval = -EPERM; |
if (new_inode && (new_dir->i_mode & S_ISVTX) && |
current->fsuid != new_inode->i_uid && |
current->fsuid != new_dir->i_uid && !fsuser()) |
goto end_rename; |
if (S_ISDIR(old_inode->i_mode)) { |
retval = -EEXIST; |
if (new_bh) |
goto end_rename; |
if ((retval = permission(old_inode, MAY_WRITE)) != 0) |
goto end_rename; |
retval = -EINVAL; |
if (subdir(new_dir, old_inode)) |
goto end_rename; |
retval = -EIO; |
dir_bh = ext_bread(old_inode,0,0); |
if (!dir_bh) |
goto end_rename; |
if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) |
goto end_rename; |
} |
if (!new_bh) |
new_bh = ext_add_entry(new_dir,new_name,new_len,&new_de); |
retval = -ENOSPC; |
if (!new_bh) |
goto end_rename; |
/* sanity checking before doing the rename - avoid races */ |
if (new_inode && (new_de->inode != new_inode->i_ino)) |
goto try_again; |
if (new_de->inode && !new_inode) |
goto try_again; |
if (old_de->inode != old_inode->i_ino) |
goto try_again; |
/* ok, that's it */ |
old_de->inode = 0; |
old_de->name_len = 0; |
new_de->inode = old_inode->i_ino; |
ext_merge_entries (old_de, pde, nde); |
if (new_inode) { |
new_inode->i_nlink--; |
new_inode->i_dirt = 1; |
} |
mark_buffer_dirty(old_bh, 1); |
mark_buffer_dirty(new_bh, 1); |
if (dir_bh) { |
PARENT_INO(dir_bh->b_data) = new_dir->i_ino; |
mark_buffer_dirty(dir_bh, 1); |
old_dir->i_nlink--; |
new_dir->i_nlink++; |
old_dir->i_dirt = 1; |
new_dir->i_dirt = 1; |
} |
retval = 0; |
end_rename: |
brelse(dir_bh); |
brelse(old_bh); |
brelse(new_bh); |
iput(old_inode); |
iput(new_inode); |
iput(old_dir); |
iput(new_dir); |
return retval; |
} |
|
/* |
* Ok, rename also locks out other renames, as they can change the parent of |
* a directory, and we don't want any races. Other races are checked for by |
* "do_rename()", which restarts if there are inconsistencies. |
* |
* Note that there is no race between different filesystems: it's only within |
* the same device that races occur: many renames can happen at once, as long |
* as they are on different partitions. |
*/ |
int ext_rename(struct inode * old_dir, const char * old_name, int old_len, |
struct inode * new_dir, const char * new_name, int new_len, |
int must_be_dir) |
{ |
static struct wait_queue * wait = NULL; |
static int lock = 0; |
int result; |
|
while (lock) |
sleep_on(&wait); |
lock = 1; |
result = do_ext_rename(old_dir, old_name, old_len, |
new_dir, new_name, new_len); |
lock = 0; |
wake_up(&wait); |
return result; |
} |
/Makefile
0,0 → 1,15
# |
# Makefile for the linux ext-filesystem routines. |
# |
# Note! Dependencies are done automagically by 'make dep', which also |
# removes any old dependencies. DON'T put your own dependencies here |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definitions are now in the main makefile... |
|
O_TARGET := ext.o |
O_OBJS := freelists.o truncate.o namei.o inode.o file.o dir.o \ |
symlink.o fsync.o |
M_OBJS := $(O_TARGET) |
|
include $(TOPDIR)/Rules.make |