diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /fs/xiafs |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/xiafs')
-rw-r--r-- | fs/xiafs/Makefile | 31 | ||||
-rw-r--r-- | fs/xiafs/bitmap.c | 388 | ||||
-rw-r--r-- | fs/xiafs/dir.c | 135 | ||||
-rw-r--r-- | fs/xiafs/file.c | 252 | ||||
-rw-r--r-- | fs/xiafs/fsync.c | 159 | ||||
-rw-r--r-- | fs/xiafs/inode.c | 502 | ||||
-rw-r--r-- | fs/xiafs/namei.c | 848 | ||||
-rw-r--r-- | fs/xiafs/symlink.c | 118 | ||||
-rw-r--r-- | fs/xiafs/truncate.c | 197 | ||||
-rw-r--r-- | fs/xiafs/xiafs_mac.h | 32 |
10 files changed, 2662 insertions, 0 deletions
diff --git a/fs/xiafs/Makefile b/fs/xiafs/Makefile new file mode 100644 index 000000000..097563244 --- /dev/null +++ b/fs/xiafs/Makefile @@ -0,0 +1,31 @@ +# +# Makefile for the XIAFS 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... + +.c.s: + $(CC) $(CFLAGS) -S $< +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< + +OBJS= bitmap.o truncate.o namei.o inode.o \ + file.o dir.o symlink.o fsync.o + +xiafs.o: $(OBJS) + $(LD) -r -o xiafs.o $(OBJS) + +dep: + $(CPP) -M *.c > .depend + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/fs/xiafs/bitmap.c b/fs/xiafs/bitmap.c new file mode 100644 index 000000000..4dee5cfbb --- /dev/null +++ b/fs/xiafs/bitmap.c @@ -0,0 +1,388 @@ +/* + * linux/fs/xiafs/bitmap.c + * + * Copyright (C) Q. Frank Xia, 1993. + * + * Based on Linus' minix/bitmap.c + * Copyright (C) Linus Torvalds, 1991, 1992. + * + * This software may be redistributed per Linux Copyright. + */ + +/* bitmap.c contains the code that handles the inode and block bitmaps */ + +#include <linux/sched.h> +#include <linux/locks.h> +#include <linux/xia_fs.h> +#include <linux/stat.h> +#include <linux/kernel.h> +#include <linux/string.h> + +#include <asm/bitops.h> + +#include "xiafs_mac.h" + + +char internal_error_message[]="XIA-FS: internal error %s %d\n"; + +static int find_first_zero(struct buffer_head *bh, int start_bit, int end_bit) +{ + /* This routine searches first 0 bit from (start_bit) to (end_bit-1). + * If found the bit is set to 1 and the bit # is returned, otherwise, + * -1 is returned. Race condition is avoid by using "btsl" and + * "goto repeat". ---Frank. + */ + + int end, i, j, tmp; + u_long *bmap; + + bmap=(u_long *)bh->b_data; + end = end_bit >> 5; + +repeat: + i=start_bit >> 5; + if ( (tmp=(~bmap[i]) & (0xffffffff << (start_bit & 31))) ) + goto zone_found; + while (++i < end) + if (~bmap[i]) { + tmp=~bmap[i]; + goto zone_found; + } + if ( !(tmp=~bmap[i] & ((1 << (end_bit & 31)) -1)) ) + return -1; +zone_found: + for (j=0; j < 32; j++) + if (tmp & (1 << j)) + break; + if (set_bit(j,bmap+i)) { + start_bit=j + (i << 5) + 1; + goto repeat; + } + mark_buffer_dirty(bh, 1); + return j + (i << 5); +} + +static void clear_buf(struct buffer_head * bh) +{ + register int i; + register long * lp; + + lp=(long *)bh->b_data; + for (i= bh->b_size >> 2; i-- > 0; ) + *lp++=0; +} + +static void que(struct buffer_head * bmap[], int bznr[], int pos) +{ + struct buffer_head * tbh; + int tmp; + int i; + + tbh=bmap[pos]; + tmp=bznr[pos]; + for (i=pos; i > 0; i--) { + bmap[i]=bmap[i-1]; + bznr[i]=bznr[i-1]; + } + bmap[0]=tbh; + bznr[0]=tmp; +} + +#define get_imap_zone(sb, bit_nr, not_que) \ + get__map_zone((sb), (sb)->u.xiafs_sb.s_imap_buf, \ + (sb)->u.xiafs_sb.s_imap_iznr, \ + (sb)->u.xiafs_sb.s_imap_cached, 1, \ + (sb)->u.xiafs_sb.s_imap_zones, _XIAFS_IMAP_SLOTS, \ + bit_nr, not_que) + +#define get_zmap_zone(sb, bit_nr, not_que) \ + get__map_zone((sb), (sb)->u.xiafs_sb.s_zmap_buf, \ + (sb)->u.xiafs_sb.s_zmap_zznr, \ + (sb)->u.xiafs_sb.s_zmap_cached, \ + 1+(sb)->u.xiafs_sb.s_imap_zones, \ + (sb)->u.xiafs_sb.s_zmap_zones, _XIAFS_ZMAP_SLOTS, \ + bit_nr, not_que) + +static struct buffer_head * +get__map_zone(struct super_block *sb, struct buffer_head * bmap_buf[], + int bznr[], u_char cache, int first_zone, + int bmap_zones, int slots, u_long bit_nr, int * not_que) +{ + struct buffer_head * tmp_bh; + int z_nr, i; + + z_nr = bit_nr >> XIAFS_BITS_PER_Z_BITS(sb); + if (z_nr >= bmap_zones) { + printk("XIA-FS: bad inode/zone number (%s %d)\n", WHERE_ERR); + return NULL; + } + if (!cache) + return bmap_buf[z_nr]; + lock_super(sb); + for (i=0; i < slots; i++) + if (bznr[i]==z_nr) + break; + if (i < slots) { /* cache hit */ + if (not_que) { + *not_que=i; + return bmap_buf[i]; + } else { + que(bmap_buf, bznr, i); + return bmap_buf[0]; + } + } + tmp_bh=bread(sb->s_dev, z_nr+first_zone, XIAFS_ZSIZE(sb)); /* cache not hit */ + if (!tmp_bh) { + printk("XIA-FS: read bitmap failed (%s %d)\n", WHERE_ERR); + unlock_super(sb); + return NULL; + } + brelse(bmap_buf[slots-1]); + bmap_buf[slots-1]=tmp_bh; + bznr[slots-1]=z_nr; + if (not_que) + *not_que=slots-1; + else + que(bmap_buf, bznr, slots-1); + return tmp_bh; +} + +#define xiafs_unlock_super(sb, cache) if (cache) unlock_super(sb); + +#define get_free_ibit(sb, prev_bit) \ + get_free__bit(sb, sb->u.xiafs_sb.s_imap_buf, \ + sb->u.xiafs_sb.s_imap_iznr, \ + sb->u.xiafs_sb.s_imap_cached, \ + 1, sb->u.xiafs_sb.s_imap_zones, \ + _XIAFS_IMAP_SLOTS, prev_bit); + +#define get_free_zbit(sb, prev_bit) \ + get_free__bit(sb, sb->u.xiafs_sb.s_zmap_buf, \ + sb->u.xiafs_sb.s_zmap_zznr, \ + sb->u.xiafs_sb.s_zmap_cached, \ + 1 + sb->u.xiafs_sb.s_imap_zones, \ + sb->u.xiafs_sb.s_zmap_zones, \ + _XIAFS_ZMAP_SLOTS, prev_bit); + +static u_long +get_free__bit(struct super_block *sb, struct buffer_head * bmap_buf[], + int bznr[], u_char cache, int first_zone, int bmap_zones, + int slots, u_long prev_bit) +{ + struct buffer_head * bh; + int not_done=0; + u_long pos, start_bit, end_bit, total_bits; + int z_nr, tmp; + + total_bits=bmap_zones << XIAFS_BITS_PER_Z_BITS(sb); + if (prev_bit >= total_bits) + prev_bit=0; + pos=prev_bit+1; + end_bit=XIAFS_BITS_PER_Z(sb); + + do { + if (pos >= total_bits) + pos=0; + if (!not_done) { /* first time */ + not_done=1; + start_bit= pos & (end_bit-1); + } else + start_bit=0; + if ( pos < prev_bit && pos+end_bit >= prev_bit) { /* last time */ + not_done=0; + end_bit=prev_bit & (end_bit-1); /* only here end_bit modified */ + } + bh = get__map_zone(sb, bmap_buf, bznr, cache, first_zone, + bmap_zones, slots, pos, &z_nr); + if (!bh) + return 0; + tmp=find_first_zero(bh, start_bit, end_bit); + if (tmp >= 0) + break; + xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached); + pos=(pos & ~(end_bit-1))+end_bit; + } while (not_done); + + if (tmp < 0) + return 0; + if (cache) + que(bmap_buf, bznr, z_nr); + xiafs_unlock_super(sb, cache); + return (pos & ~(XIAFS_BITS_PER_Z(sb)-1))+tmp; +} + +void xiafs_free_zone(struct super_block * sb, int d_addr) +{ + struct buffer_head * bh; + unsigned int bit, offset; + + if (!sb) { + printk(INTERN_ERR); + return; + } + if (d_addr < sb->u.xiafs_sb.s_firstdatazone || + d_addr >= sb->u.xiafs_sb.s_nzones) { + printk("XIA-FS: bad zone number (%s %d)\n", WHERE_ERR); + return; + } + bh = get_hash_table(sb->s_dev, d_addr, XIAFS_ZSIZE(sb)); + if (bh) + bh->b_dirt=0; + brelse(bh); + bit=d_addr - sb->u.xiafs_sb.s_firstdatazone + 1; + bh = get_zmap_zone(sb, bit, NULL); + if (!bh) + return; + offset = bit & (XIAFS_BITS_PER_Z(sb) -1); + if (!clear_bit(offset, bh->b_data)) + printk("XIA-FS: dev %04x" + " block bit %u (0x%x) already cleared (%s %d)\n", + sb->s_dev, bit, bit, WHERE_ERR); + mark_buffer_dirty(bh, 1); + xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached); +} + +int xiafs_new_zone(struct super_block * sb, u_long prev_addr) +{ + struct buffer_head * bh; + int prev_znr, tmp; + + if (!sb) { + printk(INTERN_ERR); + return 0; + } + if (prev_addr < sb->u.xiafs_sb.s_firstdatazone || + prev_addr >= sb->u.xiafs_sb.s_nzones) { + prev_addr=sb->u.xiafs_sb.s_firstdatazone; + } + prev_znr=prev_addr-sb->u.xiafs_sb.s_firstdatazone+1; + tmp=get_free_zbit(sb, prev_znr); + if (!tmp) + return 0; + tmp += sb->u.xiafs_sb.s_firstdatazone -1; + if (!(bh = getblk(sb->s_dev, tmp, XIAFS_ZSIZE(sb)))) { + printk("XIA-FS: I/O error (%s %d)\n", WHERE_ERR); + return 0; + } + if (bh->b_count != 1) { + printk(INTERN_ERR); + return 0; + } + clear_buf(bh); + bh->b_uptodate = 1; + mark_buffer_dirty(bh, 1); + brelse(bh); + return tmp; +} + +void xiafs_free_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct super_block * sb; + unsigned long ino; + + if (!inode) + return; + if (!inode->i_dev || inode->i_count!=1 || inode->i_nlink || !inode->i_sb || + inode->i_ino < 3 || inode->i_ino > inode->i_sb->u.xiafs_sb.s_ninodes) { + printk("XIA-FS: bad inode (%s %d)\n", WHERE_ERR); + return; + } + sb = inode->i_sb; + ino = inode->i_ino; + bh = get_imap_zone(sb, ino, NULL); + if (!bh) + return; + clear_inode(inode); + if (!clear_bit(ino & (XIAFS_BITS_PER_Z(sb)-1), bh->b_data)) + printk("XIA-FS: dev %04x" + "inode bit %ld (0x%lx) already cleared (%s %d)\n", + inode->i_dev, ino, ino, WHERE_ERR); + mark_buffer_dirty(bh, 1); + xiafs_unlock_super(sb, sb->u.xiafs_sb.s_imap_cached); +} + +struct inode * xiafs_new_inode(struct inode * dir) +{ + struct super_block * sb; + struct inode * inode; + ino_t tmp; + + sb = dir->i_sb; + if (!dir || !(inode = get_empty_inode())) + return NULL; + inode->i_sb = sb; + inode->i_flags = inode->i_sb->s_flags; + + tmp=get_free_ibit(sb, dir->i_ino); + if (!tmp) { + iput(inode); + return NULL; + } + 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 = tmp; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_op = NULL; + inode->i_blocks = 0; + inode->i_blksize = XIAFS_ZSIZE(inode->i_sb); + insert_inode_hash(inode); + return inode; +} + +static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; + +static u_long count_zone(struct buffer_head * bh) +{ + int i, tmp; + u_long sum; + + sum=0; + for (i=bh->b_size; i-- > 0; ) { + tmp=bh->b_data[i]; + sum += nibblemap[tmp & 0xf] + nibblemap[(tmp & 0xff) >> 4]; + } + return sum; +} + +unsigned long xiafs_count_free_inodes(struct super_block *sb) +{ + struct buffer_head * bh; + int izones, i, not_que; + u_long sum; + + sum=0; + izones=sb->u.xiafs_sb.s_imap_zones; + for (i=0; i < izones; i++) { + bh=get_imap_zone(sb, i << XIAFS_BITS_PER_Z_BITS(sb), ¬_que); + if (bh) { + sum += count_zone(bh); + xiafs_unlock_super(sb, sb->u.xiafs_sb.s_imap_cached); + } + } + i=izones << XIAFS_BITS_PER_Z_BITS(sb); + return i - sum; +} + +unsigned long xiafs_count_free_zones(struct super_block *sb) +{ + struct buffer_head * bh; + int zzones, i, not_que; + u_long sum; + + sum=0; + zzones=sb->u.xiafs_sb.s_zmap_zones; + for (i=0; i < zzones; i++) { + bh=get_zmap_zone(sb, i << XIAFS_BITS_PER_Z_BITS(sb), ¬_que); + if (bh) { + sum += count_zone(bh); + xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached); + } + } + i=zzones << XIAFS_BITS_PER_Z_BITS(sb); + return i - sum; +} diff --git a/fs/xiafs/dir.c b/fs/xiafs/dir.c new file mode 100644 index 000000000..d9db56ddc --- /dev/null +++ b/fs/xiafs/dir.c @@ -0,0 +1,135 @@ +/* + * linux/fs/xiafs/dir.c + * + * Copyright (C) Q. Frank Xia, 1993. + * + * Based on Linus' minix/dir.c + * Copyright (C) Linus Torvalds, 1991, 1992. + * + * This software may be redistributed per Linux Copyright. + */ + +#include <asm/segment.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/xia_fs.h> +#include <linux/stat.h> + +#include "xiafs_mac.h" + +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+3) & ~3) + +static int xiafs_dir_read(struct inode *, struct file *, char *, int); +static int xiafs_readdir(struct inode *, struct file *, struct dirent *, int); + +static struct file_operations xiafs_dir_operations = { + NULL, /* lseek - default */ + xiafs_dir_read, /* read */ + NULL, /* write - bad */ + xiafs_readdir, /* readdir */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + file_fsync /* default fsync */ +}; + +/* + * directories can handle most operations... + */ +struct inode_operations xiafs_dir_inode_operations = { + &xiafs_dir_operations, /* default directory file-ops */ + xiafs_create, /* create */ + xiafs_lookup, /* lookup */ + xiafs_link, /* link */ + xiafs_unlink, /* unlink */ + xiafs_symlink, /* symlink */ + xiafs_mkdir, /* mkdir */ + xiafs_rmdir, /* rmdir */ + xiafs_mknod, /* mknod */ + xiafs_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + xiafs_truncate, /* truncate */ + NULL /* permission */ +}; + +static int xiafs_dir_read(struct inode * inode, + struct file * filp, char * buf, int count) +{ + return -EISDIR; +} + +static int xiafs_readdir(struct inode * inode, + struct file * filp, struct dirent * dirent, int count) +{ + u_int offset, i,ret; + struct buffer_head * bh; + struct xiafs_direct * de; + + if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) + return -EBADF; + if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb) - 1) ) + return -EBADF; + ret = 0; + while (!ret && filp->f_pos < inode->i_size) { + offset = filp->f_pos & (XIAFS_ZSIZE(inode->i_sb) - 1); + bh = xiafs_bread(inode, filp->f_pos >> XIAFS_ZSIZE_BITS(inode->i_sb),0); + if (!bh) { + filp->f_pos += XIAFS_ZSIZE(inode->i_sb)-offset; + continue; + } + for (i = 0; i < XIAFS_ZSIZE(inode->i_sb) && i < offset; ) { + de = (struct xiafs_direct *) (bh->b_data + i); + if (!de->d_rec_len) + break; + i += de->d_rec_len; + } + offset = i; + de = (struct xiafs_direct *) (offset + bh->b_data); + + while (!ret && offset < XIAFS_ZSIZE(inode->i_sb) && filp->f_pos < inode->i_size) { + if (de->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes || + de->d_rec_len < 12 || + (char *)de+de->d_rec_len > XIAFS_ZSIZE(inode->i_sb)+bh->b_data || + de->d_name_len < 1 || de->d_name_len + 8 > de->d_rec_len || + de->d_name_len > _XIAFS_NAME_LEN || + de->d_name[de->d_name_len] ) { + printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR); + brelse(bh); + return 0; + } + offset += de->d_rec_len; + filp->f_pos += de->d_rec_len; + if (de->d_ino) { + for (i = 0; i < de->d_name_len ; i++) + put_fs_byte(de->d_name[i],i+dirent->d_name); + put_fs_byte(0,i+dirent->d_name); + put_fs_long(de->d_ino,&dirent->d_ino); + put_fs_word(i,&dirent->d_reclen); + if (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } + ret = ROUND_UP(NAME_OFFSET(dirent)+i+1); + break; + } + de = (struct xiafs_direct *) (offset + bh->b_data); + } + brelse(bh); + if (offset > XIAFS_ZSIZE(inode->i_sb)) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + return 0; + } + } + if (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } + return ret; +} diff --git a/fs/xiafs/file.c b/fs/xiafs/file.c new file mode 100644 index 000000000..5678ffd0b --- /dev/null +++ b/fs/xiafs/file.c @@ -0,0 +1,252 @@ +/* + * linux/fs/xiafs/file.c + * + * Copyright (C) Q. Frank Xia, 1993. + * + * Based on Linus' minix/file.c + * Copyright (C) Linus Torvalds, 1991, 1992. + * + * This software may be redistributed per Linux Copyright. + */ + +#include <asm/segment.h> +#include <asm/system.h> + +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/xia_fs.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/stat.h> +#include <linux/locks.h> + +#include "xiafs_mac.h" + +#define NBUF 32 + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +static int xiafs_file_read(struct inode *, struct file *, char *, int); +static int xiafs_file_write(struct inode *, struct file *, char *, int); + +/* + * We have mostly NULL's here: the current defaults are ok for + * the xiafs filesystem. + */ +static struct file_operations xiafs_file_operations = { + NULL, /* lseek - default */ + xiafs_file_read, /* read */ + xiafs_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + generic_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + xiafs_sync_file /* fsync */ +}; + +struct inode_operations xiafs_file_inode_operations = { + &xiafs_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 */ + xiafs_bmap, /* bmap */ + xiafs_truncate, /* truncate */ + NULL /* permission */ +}; + +static int +xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count) +{ + int read, left, chars; + int zone_nr, zones, f_zones, offset; + int bhrequest, uptodate; + struct buffer_head ** bhb, ** bhe; + struct buffer_head * bhreq[NBUF]; + struct buffer_head * buflist[NBUF]; + + if (!inode) { + printk("XIA-FS: inode = NULL (%s %d)\n", WHERE_ERR); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("XIA-FS: mode != regular (%s %d)\n", WHERE_ERR); + return -EINVAL; + } + offset = filp->f_pos; + left = inode->i_size - offset; + if (left > count) + left = count; + if (left <= 0) + return 0; + read = 0; + zone_nr = offset >> XIAFS_ZSIZE_BITS(inode->i_sb); + offset &= XIAFS_ZSIZE(inode->i_sb) -1 ; + f_zones =(inode->i_size+XIAFS_ZSIZE(inode->i_sb)-1)>>XIAFS_ZSIZE_BITS(inode->i_sb); + zones = (left+offset+XIAFS_ZSIZE(inode->i_sb)-1) >> XIAFS_ZSIZE_BITS(inode->i_sb); + bhb = bhe = buflist; + if (filp->f_reada) { + if(zones < read_ahead[MAJOR(inode->i_dev)] >> (1+XIAFS_ZSHIFT(inode->i_sb))) + zones = read_ahead[MAJOR(inode->i_dev)] >> (1+XIAFS_ZSHIFT(inode->i_sb)); + if (zone_nr + zones > f_zones) + zones = f_zones - zone_nr; + } + + /* We do this in a two stage process. We first try and request + as many blocks as we can, then we wait for the first one to + complete, and then we try and 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 (zones--) { + *bhb = xiafs_getblk(inode, zone_nr++, 0); + if (*bhb && !(*bhb)->b_uptodate) { + 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 (!(*bhe)->b_uptodate) { /* read error? */ + brelse(*bhe); + if (++bhe == &buflist[NBUF]) + bhe = buflist; + left = 0; + break; + } + } + if (left < XIAFS_ZSIZE(inode->i_sb) - offset) + chars = left; + else + chars = XIAFS_ZSIZE(inode->i_sb) - 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_fs_byte(0,buf++); + } + offset = 0; + if (++bhe == &buflist[NBUF]) + bhe = buflist; + } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock)); + } 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 +xiafs_file_write(struct inode * inode, struct file * filp, char * buf, int count) +{ + off_t pos; + int written, c; + struct buffer_head * bh; + char * cp; + + if (!inode) { + printk("XIA-FS: inode = NULL (%s %d)\n", WHERE_ERR); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("XIA-FS: mode != regular (%s %d)\n", WHERE_ERR); + 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 = xiafs_getblk(inode, pos >> XIAFS_ZSIZE_BITS(inode->i_sb), 1); + if (!bh) { + if (!written) + written = -ENOSPC; + break; + } + c = XIAFS_ZSIZE(inode->i_sb) - (pos & (XIAFS_ZSIZE(inode->i_sb) - 1)); + if (c > count-written) + c = count-written; + if (c != XIAFS_ZSIZE(inode->i_sb) && !bh->b_uptodate) { + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (!bh->b_uptodate) { + brelse(bh); + if (!written) + written = -EIO; + break; + } + } + cp = (pos & (XIAFS_ZSIZE(inode->i_sb)-1)) + bh->b_data; + pos += c; + if (pos > inode->i_size) { + inode->i_size = pos; + inode->i_dirt = 1; + } + written += c; + memcpy_fromfs(cp,buf,c); + buf += c; + bh->b_uptodate = 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; +} diff --git a/fs/xiafs/fsync.c b/fs/xiafs/fsync.c new file mode 100644 index 000000000..67681b2c6 --- /dev/null +++ b/fs/xiafs/fsync.c @@ -0,0 +1,159 @@ +/* + * linux/fs/xiafs/fsync.c + * + * Changes Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk) + * from + * Copyright (C) 1991, 1992 Linus Torvalds + * + * xiafs 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/xia_fs.h> + +#include "xiafs_mac.h" + + +#define blocksize (XIAFS_ZSIZE(inode->i_sb)) +#define addr_per_block (XIAFS_ADDRS_PER_Z(inode->i_sb)) + +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 && bh->b_req && !bh->b_uptodate) { + brelse(bh); + return -1; + } + if (wait || !bh->b_uptodate || !bh->b_dirt) + { + 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 < 8; 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; +} + +int xiafs_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.xiafs_i.i_ind_zone, wait); + err |= sync_dindirect(inode, &inode->u.xiafs_i.i_dind_zone, wait); + } + err |= xiafs_sync_inode (inode); + return (err < 0) ? -EIO : 0; +} diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c new file mode 100644 index 000000000..171499a95 --- /dev/null +++ b/fs/xiafs/inode.c @@ -0,0 +1,502 @@ +/* + * linux/fs/xiafs/inode.c + * + * Copyright (C) Q. Frank Xia, 1993. + * + * Based on Linus' minix/inode.c + * Copyright (C) Linus Torvalds, 1991, 1992. + * + * This software may be redistributed per Linux Copyright. + */ + +#include <linux/sched.h> +#include <linux/xia_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> + +#include "xiafs_mac.h" + +static u_long random_nr; + +void xiafs_put_inode(struct inode *inode) +{ + if (inode->i_nlink) + return; + inode->i_size = 0; + xiafs_truncate(inode); + xiafs_free_inode(inode); +} + +void xiafs_put_super(struct super_block *sb) +{ + int i; + + lock_super(sb); + sb->s_dev = 0; + for(i = 0 ; i < _XIAFS_IMAP_SLOTS ; i++) + brelse(sb->u.xiafs_sb.s_imap_buf[i]); + for(i = 0 ; i < _XIAFS_ZMAP_SLOTS ; i++) + brelse(sb->u.xiafs_sb.s_zmap_buf[i]); + unlock_super(sb); +} + +static struct super_operations xiafs_sops = { + xiafs_read_inode, + NULL, + xiafs_write_inode, + xiafs_put_inode, + xiafs_put_super, + NULL, + xiafs_statfs, + NULL +}; + +struct super_block *xiafs_read_super(struct super_block *s, void *data, + int silent) +{ + struct buffer_head *bh; + struct xiafs_super_block *sp; + int i, z, dev; + + dev=s->s_dev; + lock_super(s); + + set_blocksize(dev, BLOCK_SIZE); + + if (!(bh = bread(dev, 0, BLOCK_SIZE))) { + s->s_dev=0; + unlock_super(s); + printk("XIA-FS: read super_block failed (%s %d)\n", WHERE_ERR); + return NULL; + } + sp = (struct xiafs_super_block *) bh->b_data; + s->s_magic = sp->s_magic; + if (s->s_magic != _XIAFS_SUPER_MAGIC) { + s->s_dev = 0; + unlock_super(s); + brelse(bh); + if (!silent) + printk("VFS: Can't find a xiafs filesystem on dev 0x%04x.\n", + dev); + return NULL; + } + s->s_blocksize = sp->s_zone_size; + s->s_blocksize_bits = 10 + sp->s_zone_shift; + if (s->s_blocksize != BLOCK_SIZE && + (s->s_blocksize == 1024 || s->s_blocksize == 2048 || + s->s_blocksize == 4096)) { + brelse(bh); + set_blocksize(dev, s->s_blocksize); + bh = bread (dev, 0, s->s_blocksize); + if(!bh) return NULL; + sp = (struct xiafs_super_block *) (((char *)bh->b_data) + BLOCK_SIZE) ; + }; + s->u.xiafs_sb.s_nzones = sp->s_nzones; + s->u.xiafs_sb.s_ninodes = sp->s_ninodes; + s->u.xiafs_sb.s_ndatazones = sp->s_ndatazones; + s->u.xiafs_sb.s_imap_zones = sp->s_imap_zones; + s->u.xiafs_sb.s_zmap_zones = sp->s_zmap_zones; + s->u.xiafs_sb.s_firstdatazone = sp->s_firstdatazone; + s->u.xiafs_sb.s_zone_shift = sp->s_zone_shift; + s->u.xiafs_sb.s_max_size = sp->s_max_size; + brelse(bh); + for (i=0;i < _XIAFS_IMAP_SLOTS;i++) { + s->u.xiafs_sb.s_imap_buf[i] = NULL; + s->u.xiafs_sb.s_imap_iznr[i] = -1; + } + for (i=0;i < _XIAFS_ZMAP_SLOTS;i++) { + s->u.xiafs_sb.s_zmap_buf[i] = NULL; + s->u.xiafs_sb.s_zmap_zznr[i] = -1; + } + z=1; + if ( s->u.xiafs_sb.s_imap_zones > _XIAFS_IMAP_SLOTS ) + s->u.xiafs_sb.s_imap_cached=1; + else { + s->u.xiafs_sb.s_imap_cached=0; + for (i=0 ; i < s->u.xiafs_sb.s_imap_zones ; i++) { + if (!(s->u.xiafs_sb.s_imap_buf[i]=bread(dev, z++, XIAFS_ZSIZE(s)))) + goto xiafs_read_super_fail; + s->u.xiafs_sb.s_imap_iznr[i]=i; + } + } + if ( s->u.xiafs_sb.s_zmap_zones > _XIAFS_ZMAP_SLOTS ) + s->u.xiafs_sb.s_zmap_cached=1; + else { + s->u.xiafs_sb.s_zmap_cached=0; + for (i=0 ; i < s->u.xiafs_sb.s_zmap_zones ; i++) { + if (!(s->u.xiafs_sb.s_zmap_buf[i]=bread(dev, z++, XIAFS_ZSIZE(s)))) + goto xiafs_read_super_fail; + s->u.xiafs_sb.s_zmap_zznr[i]=i; + } + } + /* set up enough so that it can read an inode */ + s->s_dev = dev; + s->s_op = &xiafs_sops; + s->s_mounted = iget(s, _XIAFS_ROOT_INO); + if (!s->s_mounted) + goto xiafs_read_super_fail; + unlock_super(s); + random_nr=CURRENT_TIME; + return s; + +xiafs_read_super_fail: + for(i=0; i < _XIAFS_IMAP_SLOTS; i++) + brelse(s->u.xiafs_sb.s_imap_buf[i]); + for(i=0; i < _XIAFS_ZMAP_SLOTS; i++) + brelse(s->u.xiafs_sb.s_zmap_buf[i]); + s->s_dev=0; + unlock_super(s); + printk("XIA-FS: read bitmaps failed (%s %d)\n", WHERE_ERR); + return NULL; +} + +void xiafs_statfs(struct super_block *sb, struct statfs *buf) +{ + long tmp; + + put_fs_long(_XIAFS_SUPER_MAGIC, &buf->f_type); + put_fs_long(XIAFS_ZSIZE(sb), &buf->f_bsize); + put_fs_long(sb->u.xiafs_sb.s_ndatazones, &buf->f_blocks); + tmp = xiafs_count_free_zones(sb); + put_fs_long(tmp, &buf->f_bfree); + put_fs_long(tmp, &buf->f_bavail); + put_fs_long(sb->u.xiafs_sb.s_ninodes, &buf->f_files); + put_fs_long(xiafs_count_free_inodes(sb), &buf->f_ffree); + put_fs_long(_XIAFS_NAME_LEN, &buf->f_namelen); + /* don't know what should be put in buf->f_fsid */ +} + +static int zone_bmap(struct buffer_head * bh, int nr) +{ + int tmp; + + if (!bh) + return 0; + tmp = ((u_long *) bh->b_data)[nr]; + brelse(bh); + return tmp; +} + +int xiafs_bmap(struct inode * inode,int zone) +{ + int i; + + if (zone < 0) { + printk("XIA-FS: block < 0 (%s %d)\n", WHERE_ERR); + return 0; + } + if (zone >= 8+(1+XIAFS_ADDRS_PER_Z(inode->i_sb))*XIAFS_ADDRS_PER_Z(inode->i_sb)) { + printk("XIA-FS: zone > big (%s %d)\n", WHERE_ERR); + return 0; + } + if (!IS_RDONLY (inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + if (zone < 8) + return inode->u.xiafs_i.i_zone[zone]; + zone -= 8; + if (zone < XIAFS_ADDRS_PER_Z(inode->i_sb)) { + i = inode->u.xiafs_i.i_ind_zone; + if (i) + i = zone_bmap(bread(inode->i_dev, i, XIAFS_ZSIZE(inode->i_sb)), zone); + return i; + } + zone -= XIAFS_ADDRS_PER_Z(inode->i_sb); + i = inode->u.xiafs_i.i_dind_zone; + if (i) + i = zone_bmap(bread(inode->i_dev, i, XIAFS_ZSIZE(inode->i_sb)), + zone >> XIAFS_ADDRS_PER_Z_BITS(inode->i_sb)); + if (i) + i= zone_bmap(bread(inode->i_dev,i, XIAFS_ZSIZE(inode->i_sb)), + zone & (XIAFS_ADDRS_PER_Z(inode->i_sb)-1)); + return i; +} + +static u_long get_prev_addr(struct inode * inode, int zone) +{ + u_long tmp; + + if (zone > 0) + while (--zone >= 0) /* only files with holes suffer */ + if ((tmp=xiafs_bmap(inode, zone))) + return tmp; + random_nr=(random_nr+23)%inode->i_sb->u.xiafs_sb.s_ndatazones; + return random_nr + inode->i_sb->u.xiafs_sb.s_firstdatazone; +} + +static struct buffer_head * +dt_getblk(struct inode * inode, u_long *lp, int create, u_long prev_addr) +{ + int tmp; + struct buffer_head * result; + +repeat: + if ((tmp=*lp)) { + result = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (tmp == *lp) + return result; + brelse(result); + goto repeat; + } + if (!create) + return NULL; + tmp = xiafs_new_zone(inode->i_sb, prev_addr); + if (!tmp) + return NULL; + result = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (*lp) { + xiafs_free_zone(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + *lp = tmp; + inode->i_blocks+=2 << XIAFS_ZSHIFT(inode->i_sb); + return result; +} + +static struct buffer_head * +indt_getblk(struct inode * inode, struct buffer_head * bh, + int nr, int create, u_long prev_addr) +{ + int tmp; + u_long *lp; + struct buffer_head * result; + + if (!bh) + return NULL; + if (!bh->b_uptodate) { + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (!bh->b_uptodate) { + brelse(bh); + return NULL; + } + } + lp = nr + (u_long *) bh->b_data; +repeat: + if ((tmp=*lp)) { + result = getblk(bh->b_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (tmp == *lp) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } + if (!create) { + brelse(bh); + return NULL; + } + tmp = xiafs_new_zone(inode->i_sb, prev_addr); + if (!tmp) { + brelse(bh); + return NULL; + } + result = getblk(bh->b_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (*lp) { + xiafs_free_zone(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + *lp = tmp; + inode->i_blocks+=2 << XIAFS_ZSHIFT(inode->i_sb); + mark_buffer_dirty(bh, 1); + brelse(bh); + return result; +} + +struct buffer_head * xiafs_getblk(struct inode * inode, int zone, int create) +{ + struct buffer_head * bh; + u_long prev_addr=0; + + if (zone<0) { + printk("XIA-FS: zone < 0 (%s %d)\n", WHERE_ERR); + return NULL; + } + if (zone >= 8+(1+XIAFS_ADDRS_PER_Z(inode->i_sb))*XIAFS_ADDRS_PER_Z(inode->i_sb)) { + if (!create) + printk("XIA-FS: zone > big (%s %d)\n", WHERE_ERR); + return NULL; + } + if (create) + prev_addr=get_prev_addr(inode, zone); + if (zone < 8) + return dt_getblk(inode, zone+inode->u.xiafs_i.i_zone, create, prev_addr); + zone -= 8; + if (zone < XIAFS_ADDRS_PER_Z(inode->i_sb)) { + bh = dt_getblk(inode, &(inode->u.xiafs_i.i_ind_zone), create, prev_addr); + bh = indt_getblk(inode, bh, zone, create, prev_addr); + return bh; + } + zone -= XIAFS_ADDRS_PER_Z(inode->i_sb); + bh = dt_getblk(inode, &(inode->u.xiafs_i.i_dind_zone), create, prev_addr); + bh = indt_getblk(inode, bh, zone>>XIAFS_ADDRS_PER_Z_BITS(inode->i_sb), + create, prev_addr); + bh = indt_getblk(inode, bh, zone&(XIAFS_ADDRS_PER_Z(inode->i_sb)-1), + create, prev_addr); + return bh; +} + +struct buffer_head * xiafs_bread(struct inode * inode, int zone, int create) +{ + struct buffer_head * bh; + + bh = xiafs_getblk(inode, zone, create); + if (!bh || bh->b_uptodate) + return bh; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (bh->b_uptodate) + return bh; + brelse(bh); + return NULL; +} + +void xiafs_read_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct xiafs_inode * raw_inode; + int zone; + ino_t ino; + + ino = inode->i_ino; + inode->i_op = NULL; + inode->i_mode=0; + if (!ino || ino > inode->i_sb->u.xiafs_sb.s_ninodes) { + printk("XIA-FS: bad inode number (%s %d)\n", WHERE_ERR); + return; + } + zone = 1 + inode->i_sb->u.xiafs_sb.s_imap_zones + + inode->i_sb->u.xiafs_sb.s_zmap_zones + + (ino-1)/ XIAFS_INODES_PER_Z(inode->i_sb); + if (!(bh=bread(inode->i_dev, zone, XIAFS_ZSIZE(inode->i_sb)))) { + printk("XIA-FS: read i-node zone failed (%s %d)\n", WHERE_ERR); + return; + } + raw_inode = ((struct xiafs_inode *) bh->b_data) + + ((ino-1) & (XIAFS_INODES_PER_Z(inode->i_sb) - 1)); + 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 = raw_inode->i_mtime; + inode->i_atime = raw_inode->i_atime; + inode->i_ctime = raw_inode->i_ctime; + inode->i_blksize = XIAFS_ZSIZE(inode->i_sb); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + inode->i_blocks=0; + inode->i_rdev = raw_inode->i_zone[0]; + } else { + XIAFS_GET_BLOCKS(raw_inode, inode->i_blocks); + for (zone = 0; zone < 8; zone++) + inode->u.xiafs_i.i_zone[zone] = raw_inode->i_zone[zone] & 0xffffff; + inode->u.xiafs_i.i_ind_zone = raw_inode->i_ind_zone & 0xffffff; + inode->u.xiafs_i.i_dind_zone = raw_inode->i_dind_zone & 0xffffff; + } + brelse(bh); + if (S_ISREG(inode->i_mode)) + inode->i_op = &xiafs_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &xiafs_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &xiafs_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 * xiafs_update_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct xiafs_inode * raw_inode; + int zone; + ino_t ino; + + if (IS_RDONLY (inode)) { + printk("XIA-FS: write_inode on a read-only filesystem (%s %d)\n", WHERE_ERR); + inode->i_dirt = 0; + return 0; + } + + ino = inode->i_ino; + if (!ino || ino > inode->i_sb->u.xiafs_sb.s_ninodes) { + printk("XIA-FS: bad inode number (%s %d)\n", WHERE_ERR); + inode->i_dirt=0; + return 0; + } + zone = 1 + inode->i_sb->u.xiafs_sb.s_imap_zones + + inode->i_sb->u.xiafs_sb.s_zmap_zones + + (ino-1) / XIAFS_INODES_PER_Z(inode->i_sb); + if (!(bh=bread(inode->i_dev, zone, XIAFS_ZSIZE(inode->i_sb)))) { + printk("XIA-FS: read i-node zone failed (%s %d)\n", WHERE_ERR); + inode->i_dirt=0; + return 0; + } + raw_inode = ((struct xiafs_inode *)bh->b_data) + + ((ino-1) & (XIAFS_INODES_PER_Z(inode->i_sb) -1)); + 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_atime = inode->i_atime; + raw_inode->i_ctime = inode->i_ctime; + raw_inode->i_mtime = inode->i_mtime; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + raw_inode->i_zone[0] = inode->i_rdev; + else { + XIAFS_PUT_BLOCKS(raw_inode, inode->i_blocks); + for (zone = 0; zone < 8; zone++) + raw_inode->i_zone[zone] = (raw_inode->i_zone[zone] & 0xff000000) + | (inode->u.xiafs_i.i_zone[zone] & 0xffffff); + raw_inode->i_ind_zone = (raw_inode->i_ind_zone & 0xff000000) + | (inode->u.xiafs_i.i_ind_zone & 0xffffff); + raw_inode->i_dind_zone = (raw_inode->i_dind_zone & 0xff000000) + | (inode->u.xiafs_i.i_dind_zone & 0xffffff); + } + inode->i_dirt=0; + mark_buffer_dirty(bh, 1); + return bh; +} + + +void xiafs_write_inode(struct inode * inode) +{ + struct buffer_head * bh; + bh = xiafs_update_inode(inode); + brelse (bh); +} + +int xiafs_sync_inode (struct inode *inode) +{ + int err = 0; + struct buffer_head *bh; + + bh = xiafs_update_inode(inode); + if (bh && bh->b_dirt) + { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + if (bh->b_req && !bh->b_uptodate) + { + printk ("IO error syncing xiafs inode [%04X:%lu]\n", + inode->i_dev, inode->i_ino); + err = -1; + } + } + else if (!bh) + err = -1; + brelse (bh); + return err; +} diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c new file mode 100644 index 000000000..0532b1754 --- /dev/null +++ b/fs/xiafs/namei.c @@ -0,0 +1,848 @@ +/* + * Linux/fs/xiafs/namei.c + * + * Copyright (C) Q. Frank Xia, 1993. + * + * Based on Linus' minix/namei.c + * Copyright (C) Linus Torvalds, 1991, 1992. + * + * This software may be redistributed per Linux Copyright. + */ + +#include <linux/sched.h> +#include <linux/xia_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> + +#include "xiafs_mac.h" + +#define RNDUP4(x) ((3+(u_long)(x)) & ~3) +/* + * ok, we cannot use strncmp, as the name is not in our data space. + * Thus we'll have to use xiafs_match. No big problem. Match also makes + * some sanity tests. + * + * NOTE! unlike strncmp, xiafs_match returns 1 for success, 0 for failure. + */ +static int xiafs_match(int len, const char * name, struct xiafs_direct * dep) +{ + int i; + + if (!dep || !dep->d_ino || len > _XIAFS_NAME_LEN) + return 0; + /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ + if (!len && (dep->d_name[0]=='.') && (dep->d_name[1]=='\0')) + return 1; + if (len != dep->d_name_len) + return 0; + for (i=0; i < len; i++) + if (*name++ != dep->d_name[i]) + return 0; + return 1; +} + +/* + * xiafs_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. + */ +static struct buffer_head * +xiafs_find_entry(struct inode * inode, const char * name, int namelen, + struct xiafs_direct ** res_dir, struct xiafs_direct ** res_pre) +{ + int i, zones, pos; + struct buffer_head * bh; + struct xiafs_direct * dep, * dep_pre; + + *res_dir = NULL; + if (!inode) + return NULL; + if (namelen > _XIAFS_NAME_LEN) + return NULL; + + if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb) - 1)) { + printk("XIA-FS: bad dir size (%s %d)\n", WHERE_ERR); + return NULL; + } + zones=inode->i_size >> XIAFS_ZSIZE_BITS(inode->i_sb); + for (i=0; i < zones; i++ ) { + bh = xiafs_bread(inode, i, 0); + if (!bh) + continue; + dep_pre=dep=(struct xiafs_direct *)bh->b_data; + if (!i && (dep->d_rec_len != 12 || !dep->d_ino || + dep->d_name_len != 1 || strcmp(dep->d_name, "."))) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + brelse(bh); + return NULL; + } + pos = 0; + while ( pos < XIAFS_ZSIZE(inode->i_sb) ) { + if (dep->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes || + dep->d_rec_len < 12 || + dep->d_rec_len+(char *)dep > bh->b_data+XIAFS_ZSIZE(inode->i_sb) || + dep->d_name_len + 8 > dep->d_rec_len || dep->d_name_len <= 0 || + dep->d_name[dep->d_name_len] ) { + brelse(bh); + return NULL; + } + if (xiafs_match(namelen, name, dep)) { + *res_dir=dep; + if (res_pre) + *res_pre=dep_pre; + return bh; + } + pos += dep->d_rec_len; + dep_pre=dep; + dep=(struct xiafs_direct *)(bh->b_data + pos); + } + brelse(bh); + if (pos > XIAFS_ZSIZE(inode->i_sb)) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + return NULL; + } + } + return NULL; +} + +int xiafs_lookup(struct inode * dir, const char * name, int len, + struct inode ** result) +{ + int ino; + struct xiafs_direct * dep; + struct buffer_head * bh; + + *result = NULL; + if (!dir) + return -ENOENT; + if (!S_ISDIR(dir->i_mode)) { + iput(dir); + return -ENOENT; + } + if (!(bh = xiafs_find_entry(dir, name, len, &dep, NULL))) { + iput(dir); + return -ENOENT; + } + ino = dep->d_ino; + brelse(bh); + if (!(*result = iget(dir->i_sb, ino))) { + iput(dir); + return -EACCES; + } + iput(dir); + return 0; +} + +/* + * xiafs_add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as xiafs_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 * xiafs_add_entry(struct inode * dir, + const char * name, int namelen, struct xiafs_direct ** res_dir, + struct xiafs_direct ** res_pre) +{ + int i, pos, offset; + struct buffer_head * bh; + struct xiafs_direct * de, * de_pre; + + *res_dir = NULL; + if (!dir || !namelen || namelen > _XIAFS_NAME_LEN) + return NULL; + + if (dir->i_size & (XIAFS_ZSIZE(dir->i_sb) - 1)) { + printk("XIA-FS: bad dir size (%s %d)\n", WHERE_ERR); + return NULL; + } + pos=0; + for ( ; ; ) { + bh = xiafs_bread(dir, pos >> XIAFS_ZSIZE_BITS(dir->i_sb), pos ? 1:0); + if (!bh) + return NULL; + de_pre=de=(struct xiafs_direct *)bh->b_data; + if (!pos) { + if (de->d_rec_len != 12 || !de->d_ino || de->d_name_len != 1 || + strcmp(de->d_name, ".")) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + brelse(bh); + return NULL; + } + offset = 12; + de_pre=de=(struct xiafs_direct *)(bh->b_data+12); + } else + offset = 0; + while (offset < XIAFS_ZSIZE(dir->i_sb)) { + if (pos >= dir->i_size) { + de->d_ino=0; + de->d_name_len=0; + de->d_name[0]=0; + de->d_rec_len=XIAFS_ZSIZE(dir->i_sb); + dir->i_size += XIAFS_ZSIZE(dir->i_sb); + dir->i_dirt = 1; + } else { + if (de->d_ino > dir->i_sb->u.xiafs_sb.s_ninodes || + de->d_rec_len < 12 || + (char *)de+de->d_rec_len > bh->b_data+XIAFS_ZSIZE(dir->i_sb) || + de->d_name_len + 8 > de->d_rec_len || + de->d_name[de->d_name_len]) { + printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR); + brelse(bh); + return NULL; + } + if (de->d_ino && + RNDUP4(de->d_name_len)+RNDUP4(namelen)+16<=de->d_rec_len) { + i=RNDUP4(de->d_name_len)+8; + de_pre=de; + de=(struct xiafs_direct *)(i+(u_char *)de_pre); + de->d_ino=0; + de->d_rec_len=de_pre->d_rec_len-i; + de_pre->d_rec_len=i; + } + } + if (!de->d_ino && RNDUP4(namelen)+8 <= de->d_rec_len) { + /* + * XXX all times should be set by caller upon successful + * completion. + */ + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt = 1; + memcpy(de->d_name, name, namelen); + de->d_name[namelen]=0; + de->d_name_len=namelen; + mark_buffer_dirty(bh, 1); + *res_dir = de; + if (res_pre) + *res_pre = de_pre; + return bh; + } + offset+=de->d_rec_len; + de_pre=de; + de=(struct xiafs_direct *)(bh->b_data+offset); + } + brelse(bh); + if (offset > XIAFS_ZSIZE(dir->i_sb)) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + return NULL; + } + pos+=XIAFS_ZSIZE(dir->i_sb); + } + return NULL; +} + +int xiafs_create(struct inode * dir, const char * name, int len, int mode, + struct inode ** result) +{ + struct inode * inode; + struct buffer_head * bh; + struct xiafs_direct * de; + + *result = NULL; + if (!dir) + return -ENOENT; + inode = xiafs_new_inode(dir); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_op = &xiafs_file_inode_operations; + inode->i_mode = mode; + inode->i_dirt = 1; + bh = xiafs_add_entry(dir, name, len, &de, NULL); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->d_ino = inode->i_ino; + mark_buffer_dirty(bh, 1); + brelse(bh); + iput(dir); + *result = inode; + return 0; +} + +int xiafs_mknod(struct inode *dir, const char *name, int len, int mode, int rdev) +{ + struct inode * inode; + struct buffer_head * bh; + struct xiafs_direct * de; + + if (!dir) + return -ENOENT; + bh = xiafs_find_entry(dir,name,len,&de, NULL); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = xiafs_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 = &xiafs_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &xiafs_dir_inode_operations; + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + } + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &xiafs_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 = rdev; + inode->i_atime = inode->i_ctime = inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + bh = xiafs_add_entry(dir, name, len, &de, NULL); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->d_ino = inode->i_ino; + mark_buffer_dirty(bh, 1); + brelse(bh); + iput(dir); + iput(inode); + return 0; +} + +int xiafs_mkdir(struct inode * dir, const char * name, int len, int mode) +{ + struct inode * inode; + struct buffer_head * bh, *dir_block; + struct xiafs_direct * de; + + bh = xiafs_find_entry(dir,name,len,&de, NULL); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + if (dir->i_nlink > 64000) { + iput(dir); + return -EMLINK; + } + inode = xiafs_new_inode(dir); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_op = &xiafs_dir_inode_operations; + inode->i_size = XIAFS_ZSIZE(dir->i_sb); + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; + dir_block = xiafs_bread(inode,0,1); + if (!dir_block) { + iput(dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + return -ENOSPC; + } + de = (struct xiafs_direct *) dir_block->b_data; + de->d_ino=inode->i_ino; + strcpy(de->d_name,"."); + de->d_name_len=1; + de->d_rec_len=12; + de =(struct xiafs_direct *)(12 + dir_block->b_data); + de->d_ino = dir->i_ino; + strcpy(de->d_name,".."); + de->d_name_len=2; + de->d_rec_len=XIAFS_ZSIZE(dir->i_sb)-12; + inode->i_nlink = 2; + mark_buffer_dirty(dir_block, 1); + brelse(dir_block); + inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->fs->umask); + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + inode->i_dirt = 1; + bh = xiafs_add_entry(dir, name, len, &de, NULL); + if (!bh) { + iput(dir); + inode->i_nlink=0; + iput(inode); + return -ENOSPC; + } + de->d_ino = 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) +{ + int i, zones, offset; + struct buffer_head * bh; + struct xiafs_direct * de; + + if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb)-1) ) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + return 1; + } + + zones=inode->i_size >> XIAFS_ZSIZE_BITS(inode->i_sb); + for (i=0; i < zones; i++) { + bh = xiafs_bread(inode, i, 0); + if (!i) { + if (!bh) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + return 1; + } + de=(struct xiafs_direct *)bh->b_data; + if (de->d_ino != inode->i_ino || strcmp(".", de->d_name) || + de->d_rec_len != 12 ) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + brelse(bh); + return 1; + } + de=(struct xiafs_direct *)(12 + bh->b_data); + if (!de->d_ino || strcmp("..", de->d_name)) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + brelse(bh); + return 1; + } + offset=de->d_rec_len+12; + } + else + offset = 0; + if (!bh) + continue; + while (offset < XIAFS_ZSIZE(inode->i_sb)) { + de=(struct xiafs_direct *)(bh->b_data+offset); + if (de->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes || + de->d_rec_len < 12 || + (char *)de+de->d_rec_len > bh->b_data+XIAFS_ZSIZE(inode->i_sb) || + de->d_name_len + 8 > de->d_rec_len || + de->d_name[de->d_name_len]) { + printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); + brelse(bh); + return 1; + } + if (de->d_ino) { + brelse(bh); + return 0; + } + offset+=de->d_rec_len; + } + brelse(bh); + } + return 1; +} + +static void xiafs_rm_entry(struct xiafs_direct *de, struct xiafs_direct * de_pre) +{ + if (de==de_pre) { + de->d_ino=0; + return; + } + while (de_pre->d_rec_len+(u_char *)de_pre < (u_char *)de) { + if (de_pre->d_rec_len < 12) { + printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR); + return; + } + de_pre=(struct xiafs_direct *)(de_pre->d_rec_len+(u_char *)de_pre); + } + if (de_pre->d_rec_len+(u_char *)de_pre > (u_char *)de) { + printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR); + return; + } + de_pre->d_rec_len+=de->d_rec_len; +} + +int xiafs_rmdir(struct inode * dir, const char * name, int len) +{ + int retval; + struct inode * inode; + struct buffer_head * bh; + struct xiafs_direct * de, * de_pre; + + inode = NULL; + bh = xiafs_find_entry(dir, name, len, &de, &de_pre); + retval = -ENOENT; + if (!bh) + goto end_rmdir; + retval = -EPERM; + if (!(inode = iget(dir->i_sb, de->d_ino))) + 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("XIA-FS: empty directory has nlink!=2 (%s %d)\n", WHERE_ERR); + xiafs_rm_entry(de, de_pre); + 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 xiafs_unlink(struct inode * dir, const char * name, int len) +{ + int retval; + struct inode * inode; + struct buffer_head * bh; + struct xiafs_direct * de, * de_pre; + +repeat: + retval = -ENOENT; + inode = NULL; + bh = xiafs_find_entry(dir, name, len, &de, &de_pre); + if (!bh) + goto end_unlink; + if (!(inode = iget(dir->i_sb, de->d_ino))) + goto end_unlink; + retval = -EPERM; + if (S_ISDIR(inode->i_mode)) + goto end_unlink; + if (de->d_ino != inode->i_ino) { + iput(inode); + brelse(bh); + current->counter = 0; + schedule(); + goto repeat; + } + if ((dir->i_mode & S_ISVTX) && !fsuser() && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid) + goto end_unlink; + if (!inode->i_nlink) { + printk("XIA-FS: Deleting nonexistent file (%s %d)\n", WHERE_ERR); + inode->i_nlink=1; + } + xiafs_rm_entry(de, de_pre); + mark_buffer_dirty(bh, 1); + inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt = 1; + inode->i_nlink--; + inode->i_dirt = 1; + retval = 0; +end_unlink: + brelse(bh); + iput(inode); + iput(dir); + return retval; +} + +int xiafs_symlink(struct inode * dir, const char * name, + int len, const char * symname) +{ + struct xiafs_direct * de; + struct inode * inode = NULL; + struct buffer_head * bh = NULL, * name_block = NULL; + int i; + char c; + + bh = xiafs_find_entry(dir,name,len, &de, NULL); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + if (!(inode = xiafs_new_inode(dir))) { + iput(dir); + return -ENOSPC; + } + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &xiafs_symlink_inode_operations; + name_block = xiafs_bread(inode,0,1); + if (!name_block) { + iput(dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + return -ENOSPC; + } + for (i = 0; i < BLOCK_SIZE-1 && (c=*symname++); i++) + 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 = xiafs_add_entry(dir, name, len, &de, NULL); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->d_ino = inode->i_ino; + mark_buffer_dirty(bh, 1); + brelse(bh); + iput(dir); + iput(inode); + return 0; +} + +int xiafs_link(struct inode * oldinode, struct inode * dir, + const char * name, int len) +{ + struct xiafs_direct * de; + struct buffer_head * bh; + + if (S_ISDIR(oldinode->i_mode)) { + iput(oldinode); + iput(dir); + return -EPERM; + } + if (oldinode->i_nlink > 64000) { + iput(oldinode); + iput(dir); + return -EMLINK; + } + bh = xiafs_find_entry(dir, name, len, &de, NULL); + if (bh) { + brelse(bh); + iput(dir); + iput(oldinode); + return -EEXIST; + } + bh = xiafs_add_entry(dir, name, len, &de, NULL); + if (!bh) { + iput(dir); + iput(oldinode); + return -ENOSPC; + } + de->d_ino = 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 (xiafs_lookup(new_inode,"..",2,&new_inode)) + break; + if (new_inode->i_ino == ino) + break; + } + iput(new_inode); + return result; +} + +#define PARENT_INO(buffer) \ + (((struct xiafs_direct *) ((u_char *)(buffer) + 12))->d_ino) + +/* + * rename uses retry 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_xiafs_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 xiafs_direct * old_de, * old_de_pre, * new_de, * new_de_pre; + int retval; + +try_again: + old_inode = new_inode = NULL; + old_bh = new_bh = dir_bh = NULL; + old_bh = xiafs_find_entry(old_dir, old_name, old_len, &old_de, &old_de_pre); + retval = -ENOENT; + if (!old_bh) + goto end_rename; + old_inode = __iget(old_dir->i_sb, old_de->d_ino, 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 = xiafs_find_entry(new_dir, new_name, new_len, &new_de, NULL); + if (new_bh) { + new_inode = __iget(new_dir->i_sb, new_de->d_ino, 0); + 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; + retval = -EACCES; + if (!permission(old_inode, MAY_WRITE)) + goto end_rename; + retval = -EINVAL; + if (subdir(new_dir, old_inode)) + goto end_rename; + retval = -EIO; + dir_bh = xiafs_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; + retval = -EMLINK; + if (new_dir->i_nlink > 64000) + goto end_rename; + } + if (!new_bh) + new_bh = xiafs_add_entry(new_dir, new_name, new_len, &new_de, &new_de_pre); + retval = -ENOSPC; + if (!new_bh) + goto end_rename; + /* sanity checking */ + if ( (new_inode && (new_de->d_ino != new_inode->i_ino)) + || (new_de->d_ino && !new_inode) + || (old_de->d_ino != old_inode->i_ino)) { + xiafs_rm_entry(new_de, new_de_pre); + brelse(old_bh); + brelse(new_bh); + brelse(dir_bh); + iput(old_inode); + iput(new_inode); + current->counter=0; + schedule(); + goto try_again; + } + xiafs_rm_entry(old_de, old_de_pre); + new_de->d_ino = old_inode->i_ino; + 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 xiafs_rename(struct inode * old_dir, const char * old_name, int old_len, + struct inode * new_dir, const char * new_name, int new_len) +{ + static struct wait_queue * wait = NULL; + static int lock = 0; + int result; + + while (lock) + sleep_on(&wait); + lock = 1; + result = do_xiafs_rename(old_dir, old_name, old_len, + new_dir, new_name, new_len); + lock = 0; + wake_up(&wait); + return result; +} diff --git a/fs/xiafs/symlink.c b/fs/xiafs/symlink.c new file mode 100644 index 000000000..757ad5796 --- /dev/null +++ b/fs/xiafs/symlink.c @@ -0,0 +1,118 @@ +/* + * linux/fs/xiafs/symlink.c + * + * Copyright (C) Q. Frank Xia, 1993. + * + * Based on Linus' minix/symlink.c + * Copyright (C) Linus Torvalds, 1991, 1992. + * + * This software may be redistributed per Linux Copyright. + */ + +#include <asm/segment.h> + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/xia_fs.h> +#include <linux/stat.h> + +static int +xiafs_readlink(struct inode *, char *, int); + +static int +xiafs_follow_link(struct inode *, struct inode *, int, int, struct inode **); + +/* + * symlinks can't do much... + */ +struct inode_operations xiafs_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 */ + xiafs_readlink, /* readlink */ + xiafs_follow_link, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +static int xiafs_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 > BLOCK_SIZE) + buflen = BLOCK_SIZE; + bh = xiafs_bread(inode, 0, 0); + if (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } + iput(inode); + if (!bh) + return 0; + for (i=0; i < buflen && (c=bh->b_data[i]); i++) + put_fs_byte(c, buffer++); + if (i < buflen-1) + put_fs_byte((char)0, buffer); + brelse(bh); + return i; +} + +static int xiafs_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 (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } + if (current->link_count > 5) { + iput(inode); + iput(dir); + return -ELOOP; + } + if (!(bh = xiafs_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; +} + + + diff --git a/fs/xiafs/truncate.c b/fs/xiafs/truncate.c new file mode 100644 index 000000000..bdb9d39be --- /dev/null +++ b/fs/xiafs/truncate.c @@ -0,0 +1,197 @@ +/* + * linux/fs/xiafs/truncate.c + * + * Copyright (C) Q. Frank Xia, 1993. + * + * Based on Linus' minix/truncate.c + * Copyright (C) Linus Torvalds, 1991, 1992. + * + * This software may be redistributed per Linux Copyright. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/xia_fs.h> +#include <linux/stat.h> +#include <linux/fcntl.h> + +#include "xiafs_mac.h" + +/* + * Linus' comment: + * + * 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. + */ + +#define DT_ZONE ((inode->i_size + XIAFS_ZSIZE(inode->i_sb) - 1) \ + >> XIAFS_ZSIZE_BITS(inode->i_sb) ) + +static int trunc_direct(struct inode * inode) +{ + u_long * lp; + struct buffer_head * bh; + int i, tmp; + int retry = 0; + +repeat: + for (i = DT_ZONE ; i < 8 ; i++) { + if (i < DT_ZONE) + goto repeat; + lp=i + inode->u.xiafs_i.i_zone; + if (!(tmp = *lp)) + continue; + bh = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (i < DT_ZONE) { + brelse(bh); + goto repeat; + } + if ((bh && bh->b_count != 1) || tmp != *lp) + retry = 1; + else { + *lp = 0; + inode->i_dirt = 1; + inode->i_blocks-=2 << XIAFS_ZSHIFT(inode->i_sb); + xiafs_free_zone(inode->i_sb, tmp); + } + brelse(bh); + } + return retry; +} + +static int trunc_indirect(struct inode * inode, int addr_off, u_long * lp) +{ + +#define INDT_ZONE (DT_ZONE - addr_off) + + struct buffer_head * bh, * ind_bh; + int i, tmp; + u_long * indp; + int retry = 0; + + if ( !(tmp=*lp) ) + return 0; + ind_bh = bread(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (tmp != *lp) { + brelse(ind_bh); + return 1; + } + if (!ind_bh) { + *lp = 0; + return 0; + } +repeat: + for (i = INDT_ZONE<0?0:INDT_ZONE; i < XIAFS_ADDRS_PER_Z(inode->i_sb); i++) { + if (i < INDT_ZONE) + goto repeat; + indp = i+(u_long *) ind_bh->b_data; + if (!(tmp=*indp)) + continue; + bh = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (i < INDT_ZONE) { + brelse(bh); + goto repeat; + } + if ((bh && bh->b_count != 1) || tmp != *indp) + retry = 1; + else { + *indp = 0; + mark_buffer_dirty(ind_bh, 1); + inode->i_blocks-= 2 << XIAFS_ZSHIFT(inode->i_sb); + xiafs_free_zone(inode->i_sb, tmp); + } + brelse(bh); + } + indp = (u_long *) ind_bh->b_data; + for (i = 0; i < XIAFS_ADDRS_PER_Z(inode->i_sb) && !(*indp++); i++) ; + if (i >= XIAFS_ADDRS_PER_Z(inode->i_sb)) { + if (ind_bh->b_count != 1) + retry = 1; + else { + tmp = *lp; + *lp = 0; + inode->i_blocks-= 2 << XIAFS_ZSHIFT(inode->i_sb); + xiafs_free_zone(inode->i_sb, tmp); + } + } + brelse(ind_bh); + return retry; +} + +static int trunc_dindirect(struct inode * inode) +{ + +#define DINDT_ZONE \ + ((DT_ZONE-XIAFS_ADDRS_PER_Z(inode->i_sb)-8)>>XIAFS_ADDRS_PER_Z_BITS(inode->i_sb)) + + int i, tmp; + struct buffer_head * dind_bh; + u_long * dindp, * lp; + int retry = 0; + + lp = &(inode->u.xiafs_i.i_dind_zone); + if (!(tmp = *lp)) + return 0; + dind_bh = bread(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); + if (tmp != *lp) { + brelse(dind_bh); + return 1; + } + if (!dind_bh) { + *lp = 0; + return 0; + } +repeat: + for (i=DINDT_ZONE<0?0:DINDT_ZONE ; i < XIAFS_ADDRS_PER_Z(inode->i_sb) ; i ++) { + if (i < DINDT_ZONE) + goto repeat; + dindp = i+(u_long *) dind_bh->b_data; + retry |= trunc_indirect(inode, + 8+((1+i)<<XIAFS_ADDRS_PER_Z_BITS(inode->i_sb)), + dindp); + mark_buffer_dirty(dind_bh, 1); + } + dindp = (u_long *) dind_bh->b_data; + for (i = 0; i < XIAFS_ADDRS_PER_Z(inode->i_sb) && !(*dindp++); i++); + if (i >= XIAFS_ADDRS_PER_Z(inode->i_sb)) { + if (dind_bh->b_count != 1) + retry = 1; + else { + tmp = *lp; + *lp = 0; + inode->i_dirt = 1; + inode->i_blocks-=2 << XIAFS_ZSHIFT(inode->i_sb); + xiafs_free_zone(inode->i_sb, tmp); + } + } + brelse(dind_bh); + return retry; +} + +void xiafs_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, 8, &(inode->u.xiafs_i.i_ind_zone)); + retry |= trunc_dindirect(inode); + if (!retry) + break; + current->counter = 0; + schedule(); + } + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + inode->i_dirt = 1; +} diff --git a/fs/xiafs/xiafs_mac.h b/fs/xiafs/xiafs_mac.h new file mode 100644 index 000000000..05af6e42a --- /dev/null +++ b/fs/xiafs/xiafs_mac.h @@ -0,0 +1,32 @@ +/* + * linux/fs/xiafs/xiafs_mac.h + * + * Copyright (C) Q. Frank Xia, 1993. + */ + +extern char internal_error_message[]; +#define INTERN_ERR internal_error_message, __FILE__, __LINE__ +#define WHERE_ERR __FILE__, __LINE__ + +#define XIAFS_ZSHIFT(sp) ((sp)->u.xiafs_sb.s_zone_shift) +#define XIAFS_ZSIZE(sp) (BLOCK_SIZE << XIAFS_ZSHIFT(sp)) +#define XIAFS_ZSIZE_BITS(sp) (BLOCK_SIZE_BITS + XIAFS_ZSHIFT(sp)) +#define XIAFS_ADDRS_PER_Z(sp) (BLOCK_SIZE >> (2 - XIAFS_ZSHIFT(sp))) +#define XIAFS_ADDRS_PER_Z_BITS(sp) (BLOCK_SIZE_BITS - 2 + XIAFS_ZSHIFT(sp)) +#define XIAFS_BITS_PER_Z(sp) (BLOCK_SIZE << (3 + XIAFS_ZSHIFT(sp))) +#define XIAFS_BITS_PER_Z_BITS(sp) (BLOCK_SIZE_BITS + 3 + XIAFS_ZSHIFT(sp)) +#define XIAFS_INODES_PER_Z(sp) (_XIAFS_INODES_PER_BLOCK << XIAFS_ZSHIFT(sp)) + +/* Use the most significant bytes of zone pointers to store block counter. */ +/* This is ugly, but it works. Note, We have another 7 bytes for "expansion". */ + +#define XIAFS_GET_BLOCKS(row_ip, blocks) \ + blocks=((((row_ip)->i_zone[0] >> 24) & 0xff )|\ + (((row_ip)->i_zone[1] >> 16) & 0xff00 )|\ + (((row_ip)->i_zone[2] >> 8) & 0xff0000 ) ) + +/* XIAFS_PUT_BLOCKS should be called before saving zone pointers */ +#define XIAFS_PUT_BLOCKS(row_ip, blocks) \ + (row_ip)->i_zone[2]=((blocks)<< 8) & 0xff000000;\ + (row_ip)->i_zone[1]=((blocks)<<16) & 0xff000000;\ + (row_ip)->i_zone[0]=((blocks)<<24) & 0xff000000 |