OpenCores
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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.