summaryrefslogtreecommitdiffstats
path: root/fs/xiafs/bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xiafs/bitmap.c')
-rw-r--r--fs/xiafs/bitmap.c388
1 files changed, 388 insertions, 0 deletions
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), &not_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), &not_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;
+}