diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
commit | c7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch) | |
tree | 3682407a599b8f9f03fc096298134cafba1c9b2f /fs/ufs | |
parent | 1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff) |
o Merge with Linux 2.1.116.
o New Newport console code.
o New G364 console code.
Diffstat (limited to 'fs/ufs')
-rw-r--r-- | fs/ufs/Makefile | 10 | ||||
-rw-r--r-- | fs/ufs/acl.c | 66 | ||||
-rw-r--r-- | fs/ufs/balloc.c | 792 | ||||
-rw-r--r-- | fs/ufs/cylinder.c | 212 | ||||
-rw-r--r-- | fs/ufs/dir.c (renamed from fs/ufs/ufs_dir.c) | 105 | ||||
-rw-r--r-- | fs/ufs/file.c | 281 | ||||
-rw-r--r-- | fs/ufs/ialloc.c | 341 | ||||
-rw-r--r-- | fs/ufs/inode.c | 665 | ||||
-rw-r--r-- | fs/ufs/namei.c | 1147 | ||||
-rw-r--r-- | fs/ufs/super.c | 690 | ||||
-rw-r--r-- | fs/ufs/swab.h | 114 | ||||
-rw-r--r-- | fs/ufs/symlink.c | 138 | ||||
-rw-r--r-- | fs/ufs/truncate.c | 473 | ||||
-rw-r--r-- | fs/ufs/ufs_file.c | 52 | ||||
-rw-r--r-- | fs/ufs/ufs_inode.c | 332 | ||||
-rw-r--r-- | fs/ufs/ufs_namei.c | 195 | ||||
-rw-r--r-- | fs/ufs/ufs_super.c | 384 | ||||
-rw-r--r-- | fs/ufs/ufs_swab.c | 178 | ||||
-rw-r--r-- | fs/ufs/ufs_swab.h | 121 | ||||
-rw-r--r-- | fs/ufs/ufs_symlink.c | 146 | ||||
-rw-r--r-- | fs/ufs/util.c | 197 | ||||
-rw-r--r-- | fs/ufs/util.h | 322 |
22 files changed, 5514 insertions, 1447 deletions
diff --git a/fs/ufs/Makefile b/fs/ufs/Makefile index 0a74c36c7..f8326866d 100644 --- a/fs/ufs/Makefile +++ b/fs/ufs/Makefile @@ -1,15 +1,15 @@ # -# Makefile for the linux ufs-filesystem routines. +# Makefile for the Linux ufs 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). +# unless it's something special (not a .c file). # -# Note 2! The CFLAGS definitions are now in the main makefile... +# Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := ufs.o -O_OBJS := ufs_dir.o ufs_file.o ufs_inode.o ufs_namei.o \ - ufs_super.o ufs_symlink.o ufs_swab.o +O_OBJS := acl.o balloc.o cylinder.o dir.o file.o ialloc.o inode.o \ + namei.o super.o symlink.o truncate.o util.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/ufs/acl.c b/fs/ufs/acl.c new file mode 100644 index 000000000..934e474a1 --- /dev/null +++ b/fs/ufs/acl.c @@ -0,0 +1,66 @@ +/* + * linux/fs/ufs/acl.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles Uiversity, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/acl.c + * + * Copyright (C) 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + */ + +/* + * This file will contain the Access Control Lists management for the + * second extended file system. + */ + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/sched.h> +#include <linux/stat.h> + +/* + * ufs_permission () + * + * Check for access rights + */ +int ufs_permission (struct inode * inode, int mask) +{ + unsigned short mode = inode->i_mode; + + /* + * Nobody gets write access to a file on a readonly-fs + */ + if ((mask & S_IWOTH) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) && + IS_RDONLY(inode)) + return -EROFS; + /* + * Nobody gets write access to an immutable file + */ + if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) + return -EACCES; + + /* + * If no ACL, checks using the file mode + */ + else if (current->fsuid == inode->i_uid) + mode >>= 6; + else if (in_group_p (inode->i_gid)) + mode >>= 3; + /* + * Access is always granted for root. We now check last, + * though, for BSD process accounting correctness + */ + if (((mode & mask & S_IRWXO) == mask) || fsuser()) + return 0; + else + return -EACCES; +} diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c new file mode 100644 index 000000000..1022ad383 --- /dev/null +++ b/fs/ufs/balloc.c @@ -0,0 +1,792 @@ +/* + * linux/fs/ufs/balloc.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + */ + +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/stat.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/locks.h> +#include <linux/quotaops.h> +#include <asm/bitops.h> +#include <asm/byteorder.h> + +#include "swab.h" +#include "util.h" + +#undef UFS_BALLOC_DEBUG +#undef UFS_BALLOC_DEBUG_MORE + +#ifdef UFS_BALLOC_DEBUG +#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +#ifdef UFS_BALLOC_DEBUG_MORE +#define UFSDM \ +ufs_print_cylinder_stuff (ucg, swab); \ +printk("inode: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nifree), \ +swab32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \ +printk("block: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree), \ +SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nbfree), SWAB32(ucg->cg_cs.cs_nbfree)); \ +printk("fragment: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nffree), \ +SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nffree), SWAB32(ucg->cg_cs.cs_nffree)); \ +printk("ndir: total %u, fs %u, cg %u\n\n", SWAB32(usb1->fs_cstotal.cs_ndir), \ +SWAB32(sb->fs_cs(ucpi->c_cgx).cs_ndir), SWAB32(ucg->cg_cs.cs_ndir)); +#else +#define UFSDM +#endif + + +unsigned ufs_add_fragments (struct inode *, unsigned, unsigned, unsigned, int *); +unsigned ufs_alloc_fragments (struct inode *, unsigned, unsigned, unsigned, int *); +unsigned ufs_alloccg_block (struct inode *, struct ufs_cg_private_info *, unsigned, int *); +unsigned ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info *, unsigned, unsigned); +static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[]; + +/* + * Free 'count' fragments from fragment number 'fragment' + */ +void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count) { + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + unsigned cgno, bit, end_bit, bbase, blkmap, i, blkno, cylno; + unsigned swab; + + sb = inode->i_sb; + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; + usb1 = ubh_get_usb_first(USPI_UBH); + + UFSD(("ENTER, fragment %u, count %u\n", fragment, count)) + + if (ufs_fragnum(fragment) + count > uspi->s_fpg) + ufs_error (sb, "ufs_free_fragments", "internal error"); + + lock_super(sb); + + cgno = ufs_dtog(fragment); + bit = ufs_dtogd(fragment); + if (cgno >= uspi->s_ncg) { + ufs_panic (sb, "ufs_free_fragments", "freeing blocks are outside device"); + goto failed; + } + + ucpi = ufs_load_cylinder (sb, cgno); + if (!ucpi) + goto failed; + ucg = ubh_get_ucg (UCPI_UBH); + if (!ufs_cg_chkmagic (ucg)) { + ufs_panic (sb, "ufs_free_fragments", "internal error, bad magic number on cg %u", cgno); + goto failed; + } + + UFSDM + + end_bit = bit + count; + bbase = ufs_blknum (bit); + blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase); + ufs_fragacct (sb, blkmap, ucg->cg_frsum, -1); + for (i = bit; i < end_bit; i++) { + if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, i)) + ubh_setbit (UCPI_UBH, ucpi->c_freeoff, i); + else ufs_error (sb, "ufs_free_fragments", + "bit already cleared for fragment %u", i); + } + + DQUOT_FREE_BLOCK (sb, inode, count); + ADD_SWAB32(ucg->cg_cs.cs_nffree, count); + ADD_SWAB32(usb1->fs_cstotal.cs_nffree, count); + ADD_SWAB32(sb->fs_cs(cgno).cs_nffree, count); + blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase); + ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1); + + /* + * Trying to reasembly free fragments into block + */ + blkno = ufs_fragstoblks (bbase); + if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) { + SUB_SWAB32(ucg->cg_cs.cs_nffree, uspi->s_fpb); + SUB_SWAB32(usb1->fs_cstotal.cs_nffree, uspi->s_fpb); + SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, uspi->s_fpb); + INC_SWAB32(ucg->cg_cs.cs_nbfree); + INC_SWAB32(usb1->fs_cstotal.cs_nbfree); + INC_SWAB32(sb->fs_cs(cgno).cs_nbfree); + cylno = ufs_cbtocylno (bbase); + INC_SWAB16(ubh_cg_blks (ucpi, cylno, ufs_cbtorpos(bbase))); + INC_SWAB32(ubh_cg_blktot (ucpi, cylno)); + } + + UFSDM + + ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (UCPI_UBH, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); + ubh_wait_on_buffer (UCPI_UBH); + } + sb->s_dirt = 1; + + unlock_super (sb); + UFSD(("EXIT\n")) + return; + +failed: + unlock_super (sb); + UFSD(("EXIT (FAILED)\n")) + return; +} + +/* + * Free 'count' fragments from fragment number 'fragment' (free whole blocks) + */ +void ufs_free_blocks (struct inode * inode, unsigned fragment, unsigned count) { + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + unsigned overflow, cgno, bit, end_bit, blkno, i, cylno; + unsigned swab; + + sb = inode->i_sb; + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; + usb1 = ubh_get_usb_first(USPI_UBH); + + UFSD(("ENTER, fragment %u, count %u\n", fragment, count)) + + if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) { + ufs_error (sb, "ufs_free_blocks", "internal error"); + goto failed; + } + + lock_super(sb); + +do_more: + overflow = 0; + cgno = ufs_dtog (fragment); + bit = ufs_dtogd (fragment); + if (cgno >= uspi->s_ncg) { + ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device"); + goto failed; + } + end_bit = bit + count; + if (end_bit > uspi->s_fpg) { + overflow = bit + count - uspi->s_fpg; + count -= overflow; + end_bit -= overflow; + } + + ucpi = ufs_load_cylinder (sb, cgno); + if (!ucpi) + goto failed; + ucg = ubh_get_ucg (UCPI_UBH); + if (!ufs_cg_chkmagic (ucg)) { + ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno); + goto failed; + } + + UFSDM + + for (i = bit; i < end_bit; i += uspi->s_fpb) { + blkno = ufs_fragstoblks(i); + if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) { + ufs_error(sb, "ufs_free_blocks", "freeing free fragment"); + } + ubh_setblock(UCPI_UBH, ucpi->c_freeoff, blkno); + DQUOT_FREE_BLOCK(sb, inode, uspi->s_fpb); + INC_SWAB32(ucg->cg_cs.cs_nbfree); + INC_SWAB32(usb1->fs_cstotal.cs_nbfree); + INC_SWAB32(sb->fs_cs(cgno).cs_nbfree); + cylno = ufs_cbtocylno(i); + INC_SWAB16(ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(i))); + INC_SWAB32(ubh_cg_blktot(ucpi, cylno)); + } + + UFSDM + + ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (UCPI_UBH, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); + ubh_wait_on_buffer (UCPI_UBH); + } + + if (overflow) { + fragment += count; + count = overflow; + goto do_more; + } + + sb->s_dirt = 1; + unlock_super (sb); + UFSD(("EXIT\n")) + return; + +failed: + unlock_super (sb); + UFSD(("EXIT (FAILED)\n")) + return; +} + + + +#define NULLIFY_FRAGMENTS \ + for (i = oldcount; i < newcount; i++) { \ + bh = getblk (sb->s_dev, result + i, sb->s_blocksize); \ + memset (bh->b_data, 0, sb->s_blocksize); \ + mark_buffer_uptodate(bh, 1); \ + mark_buffer_dirty (bh, 1); \ + if (IS_SYNC(inode)) { \ + ll_rw_block (WRITE, 1, &bh); \ + wait_on_buffer (bh); \ + } \ + brelse (bh); \ + } + +unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment, + unsigned goal, unsigned count, int * err ) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct buffer_head * bh; + unsigned cgno, oldcount, newcount, tmp, request, i, result; + unsigned swab; + + UFSD(("ENTER, ino %lu, fragment %u, goal %u, count %u\n", inode->i_ino, fragment, goal, count)) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + *err = -ENOSPC; + + lock_super (sb); + + tmp = SWAB32(*p); + if (count + ufs_fragnum(fragment) > uspi->s_fpb) { + ufs_warning (sb, "ufs_new_fragments", "internal warning" + " fragment %u, count %u", fragment, count); + count = uspi->s_fpb - ufs_fragnum(fragment); + } + oldcount = ufs_fragnum (fragment); + newcount = oldcount + count; + + /* + * Somebody else has just allocated our fragments + */ + if (oldcount) { + if (!tmp) { + ufs_error (sb, "ufs_new_fragments", "internal error, " + "fragment %u, tmp %u\n", fragment, tmp); + return (unsigned)-1; + } + if (fragment < inode->u.ufs_i.i_lastfrag) { + UFSD(("EXIT (ALREADY ALLOCATED)\n")) + printk("hlaska 2\n"); + unlock_super (sb); + return 0; + } + } + else { + if (tmp) { + UFSD(("EXIT (ALREADY ALLOCATED)\n")) + printk("hlaska 3, fragment %u, tmp %u, oldcount %u\n", fragment, tmp, oldcount); + unlock_super(sb); + return 0; + } + } + + /* + * There is not enough space for user on the device + */ + if (!fsuser() && ufs_freespace(usb1, UFS_MINFREE) <= 0) { + unlock_super (sb); + UFSD(("EXIT (FAILED)\n")) + return 0; + } + + if (goal >= uspi->s_size) + goal = 0; + if (goal == 0) + cgno = ufs_inotocg (inode->i_ino); + else + cgno = ufs_dtog (goal); + + /* + * allocate new fragment + */ + if (oldcount == 0) { + result = ufs_alloc_fragments (inode, cgno, goal, count, err); + if (result) { + *p = SWAB32(result); + *err = 0; + inode->i_blocks += count << uspi->s_nspfshift; + inode->u.ufs_i.i_lastfrag = max (inode->u.ufs_i.i_lastfrag, fragment + count); + NULLIFY_FRAGMENTS + } + unlock_super(sb); + UFSD(("EXIT, result %u\n", result)) + return result; + } + + /* + * resize block + */ + result = ufs_add_fragments (inode, tmp, oldcount, newcount, err); + if (result) { + *err = 0; + inode->i_blocks += count << uspi->s_nspfshift; + inode->u.ufs_i.i_lastfrag = max (inode->u.ufs_i.i_lastfrag, fragment + count); + NULLIFY_FRAGMENTS + unlock_super(sb); + UFSD(("EXIT, result %u\n", result)) + return result; + } + + /* + * allocate new block and move data + */ + switch (SWAB32(usb1->fs_optim)) { + case UFS_OPTSPACE: + request = newcount; + if (uspi->s_minfree < 5 || SWAB32(usb1->fs_cstotal.cs_nffree) + > uspi->s_dsize * uspi->s_minfree / (2 * 100) ) + break; + usb1->fs_optim = SWAB32(UFS_OPTTIME); + break; + default: + usb1->fs_optim = SWAB32(UFS_OPTTIME); + + case UFS_OPTTIME: + request = uspi->s_fpb; + if (SWAB32(usb1->fs_cstotal.cs_nffree) < uspi->s_dsize * + (uspi->s_minfree - 2) / 100) + break; + usb1->fs_optim = SWAB32(UFS_OPTSPACE); + break; + } + result = ufs_alloc_fragments (inode, cgno, goal, request, err); + if (result) { + for (i = 0; i < oldcount; i++) { + bh = bread (sb->s_dev, tmp + i, sb->s_blocksize); + mark_buffer_clean (bh); + bh->b_blocknr = result + i; + mark_buffer_dirty (bh, 0); + if (IS_SYNC(inode)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + } + *p = SWAB32(result); + *err = 0; + inode->i_blocks += count << uspi->s_nspfshift; + inode->u.ufs_i.i_lastfrag = max (inode->u.ufs_i.i_lastfrag, fragment + count); + NULLIFY_FRAGMENTS + unlock_super(sb); + if (newcount < request) + ufs_free_fragments (inode, result + newcount, request - newcount); + ufs_free_fragments (inode, tmp, oldcount); + UFSD(("EXIT, result %u\n", result)) + return result; + } + + unlock_super(sb); + UFSD(("EXIT (FAILED)\n")) + return 0; +} + +unsigned ufs_add_fragments (struct inode * inode, unsigned fragment, + unsigned oldcount, unsigned newcount, int * err) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + unsigned cgno, fragno, fragoff, count, fragsize, i; + unsigned swab; + + UFSD(("ENTER, fragment %u, oldcount %u, newcount %u\n", fragment, oldcount, newcount)) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first (USPI_UBH); + count = newcount - oldcount; + + cgno = ufs_dtog(fragment); + if (sb->fs_cs(cgno).cs_nffree < count) + return 0; + if ((ufs_fragnum (fragment) + newcount) > uspi->s_fpb) + return 0; + ucpi = ufs_load_cylinder (sb, cgno); + if (!ucpi) + return 0; + ucg = ubh_get_ucg (UCPI_UBH); + if (!ufs_cg_chkmagic(ucg)) { + ufs_panic (sb, "ufs_add_fragments", + "internal error, bad magic number on cg %u", cgno); + return 0; + } + + UFSDM + + fragno = ufs_dtogd (fragment); + fragoff = ufs_fragnum (fragno); + for (i = oldcount; i < newcount; i++) + if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, fragno + i)) + return 0; + /* + * Block can be extended + */ + ucg->cg_time = SWAB32(CURRENT_TIME); + for (i = newcount; i < (uspi->s_fpb - fragoff); i++) + if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, fragno + i)) + break; + fragsize = i - oldcount; + if (!SWAB32(ucg->cg_frsum[fragsize])) + ufs_panic (sb, "ufs_add_fragments", + "internal error or corruted bitmap on cg %u", cgno); + DEC_SWAB32(ucg->cg_frsum[fragsize]); + if (fragsize != count) + INC_SWAB32(ucg->cg_frsum[fragsize - count]); + for (i = oldcount; i < newcount; i++) + ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, fragno + i); + if(DQUOT_ALLOC_BLOCK(sb, inode, count)) { + *err = -EDQUOT; + return 0; + } + SUB_SWAB32(ucg->cg_cs.cs_nffree, count); + SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, count); + SUB_SWAB32(usb1->fs_cstotal.cs_nffree, count); + usb1->fs_fmod = SWAB32(1); + + UFSDM + + ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (UCPI_UBH, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); + ubh_wait_on_buffer (UCPI_UBH); + } + sb->s_dirt = 1; + + UFSD(("EXIT, fragment %u\n", fragment)) + + return fragment; +} + +#define UFS_TEST_FREE_SPACE_CG \ + ucg = (struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[cgno]->b_data; \ + if (SWAB32(ucg->cg_cs.cs_nbfree)) \ + goto cg_found; \ + for (k = count; k < uspi->s_fpb; k++) \ + if (SWAB32(ucg->cg_frsum[k])) \ + goto cg_found; + +unsigned ufs_alloc_fragments (struct inode * inode, unsigned cgno, + unsigned goal, unsigned count, int * err) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + unsigned oldcg, i, j, k, result, allocsize; + unsigned swab; + + UFSD(("ENTER, ino %lu, cgno %u, goal %u, count %u\n", inode->i_ino, cgno, goal, count)) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + oldcg = cgno; + + /* + * 1. searching on preferred cylinder group + */ + UFS_TEST_FREE_SPACE_CG + + /* + * 2. quadratic rehash + */ + for (j = 1; j < uspi->s_ncg; j *= 2) { + cgno += j; + if (cgno >= uspi->s_ncg) + cgno -= uspi->s_ncg; + UFS_TEST_FREE_SPACE_CG + } + + /* + * 3. brute force search + * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step ) + */ + cgno = (oldcg + 1) % uspi->s_ncg; + for (j = 2; j < uspi->s_ncg; j++) { + cgno++; + if (cgno >= uspi->s_ncg) + cgno = 0; + UFS_TEST_FREE_SPACE_CG + } + + UFSD(("EXIT (FAILED)\n")) + return 0; + +cg_found: + ucpi = ufs_load_cylinder (sb, cgno); + if (!ucpi) + return 0; + ucg = ubh_get_ucg (UCPI_UBH); + if (!ufs_cg_chkmagic(ucg)) + ufs_panic (sb, "ufs_alloc_fragments", + "internal error, bad magic number on cg %u", cgno); + ucg->cg_time = SWAB32(CURRENT_TIME); + + UFSDM + + if (count == uspi->s_fpb) { + result = ufs_alloccg_block (inode, ucpi, goal, err); + if (result == (unsigned)-1) + return 0; + goto succed; + } + + for (allocsize = count; allocsize < uspi->s_fpb; allocsize++) + if (SWAB32(ucg->cg_frsum[allocsize]) != 0) + break; + + if (allocsize == uspi->s_fpb) { + result = ufs_alloccg_block (inode, ucpi, goal, err); + if (result == (unsigned)-1) + return 0; + goal = ufs_dtogd (result); + for (i = count; i < uspi->s_fpb; i++) + ubh_setbit (UCPI_UBH, ucpi->c_freeoff, goal + i); + i = uspi->s_fpb - count; + DQUOT_FREE_BLOCK(sb, inode, i); + ADD_SWAB32(ucg->cg_cs.cs_nffree, i); + ADD_SWAB32(usb1->fs_cstotal.cs_nffree, i); + ADD_SWAB32(sb->fs_cs(cgno).cs_nffree, i); + INC_SWAB32(ucg->cg_frsum[i]); + goto succed; + } + + result = ufs_bitmap_search (sb, ucpi, goal, allocsize); + if (result == (unsigned)-1) + return 0; + if(DQUOT_ALLOC_BLOCK(sb, inode, count)) { + *err = -EDQUOT; + return 0; + } + for (i = 0; i < count; i++) + ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, result + i); + SUB_SWAB32(ucg->cg_cs.cs_nffree, count); + SUB_SWAB32(usb1->fs_cstotal.cs_nffree, count); + SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, count); + DEC_SWAB32(ucg->cg_frsum[allocsize]); + if (count != allocsize) + INC_SWAB32(ucg->cg_frsum[allocsize - count]); + +succed: + usb1->fs_fmod = SWAB32(1); + + UFSDM + + ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (UCPI_UBH, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); + ubh_wait_on_buffer (UCPI_UBH); + } + sb->s_dirt = 1; + + result += cgno * uspi->s_fpg; + UFSD(("EXIT3, result %u\n", result)) + return result; +} + +unsigned ufs_alloccg_block (struct inode * inode, + struct ufs_cg_private_info * ucpi, unsigned goal, int * err) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cylinder_group * ucg; + unsigned result, cylno, blkno; + unsigned swab; + + UFSD(("ENTER, goal %u\n", goal)) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + ucg = ubh_get_ucg(UCPI_UBH); + + if (goal == 0) { + goal = ucpi->c_rotor; + goto norot; + } + goal = ufs_blknum (goal); + goal = ufs_dtogd (goal); + + /* + * If the requested block is available, use it. + */ + if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, ufs_fragstoblks(goal))) { + result = goal; + goto gotit; + } + + /*** This function should be optimalized later ***/ + +norot: + result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb); + if (result == (unsigned)-1) + return (unsigned)-1; + ucpi->c_rotor = result; +gotit: + blkno = ufs_fragstoblks(result); + ubh_clrblock(UCPI_UBH, ucpi->c_freeoff, blkno); + if(DQUOT_ALLOC_BLOCK(sb, inode, uspi->s_fpb)) { + *err = -EDQUOT; + return (unsigned)-1; + } + DEC_SWAB32(ucg->cg_cs.cs_nbfree); + DEC_SWAB32(usb1->fs_cstotal.cs_nbfree); + DEC_SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nbfree); + cylno = ufs_cbtocylno(result); + DEC_SWAB16(ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(result))); + DEC_SWAB32(ubh_cg_blktot(ucpi, cylno)); + usb1->fs_fmod = 1; + + UFSD(("EXIT, result %u\n", result)) + + return result; +} + +unsigned ufs_bitmap_search (struct super_block * sb, + struct ufs_cg_private_info * ucpi, unsigned goal, unsigned count) +{ + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cylinder_group * ucg; + unsigned start, length, length2, location, result; + unsigned possition, fragsize, blockmap, mask; + unsigned swab; + + UFSD(("ENTER, cg %u, goal %u, count %u\n", ucpi->c_cgx, goal, count)) + + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first (USPI_UBH); + ucg = ubh_get_ucg(UCPI_UBH); + + if (goal) + start = ufs_dtogd(goal) >> 3; + else + start = ucpi->c_frotor >> 3; + + length = howmany(uspi->s_fpg, 8) - start; + location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff + start, length, + (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other, + 1 << (count - 1 + (uspi->s_fpb & 7))); + if (location == 0) { + length2 = start + 1; + location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff, length2, + (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other, + 1 << (count - 1 + (uspi->s_fpb & 7))); + if (location == 0) { + ufs_error (sb, "ufs_bitmap_search", + "bitmap corrupted on cg %u, start %u, length %u, count %u, freeoff %u\n", + ucpi->c_cgx, start, length, count, ucpi->c_freeoff); + return (unsigned)-1; + } + start = 0; + length = length2; + } + result = (start + length - location) << 3; + ucpi->c_frotor = result; + + /* + * found the byte in the map + */ + blockmap = ubh_blkmap(UCPI_UBH, ucpi->c_freeoff, result); + fragsize = 0; + for (possition = 0, mask = 1; possition < 8; possition++, mask <<= 1) { + if (blockmap & mask) { + if (!(possition & uspi->s_fpbmask)) + fragsize = 1; + else + fragsize++; + } + else { + if (fragsize == count) { + result += possition - count; + UFSD(("EXIT, result %u\n", result)) + return result; + } + fragsize = 0; + } + } + if (fragsize == count) { + result += possition - count; + UFSD(("EXIT, result %u\n", result)) + return result; + } + ufs_error (sb, "ufs_bitmap_search", "block not in map on cg %u\n", ucpi->c_cgx); + UFSD(("EXIT (FAILED)\n")) + return (unsigned)-1; +} + +static unsigned char ufs_fragtable_8fpb[] = { + 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11, + 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x10, 0x11, 0x20, 0x40, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21, + 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0A, 0x12, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0C, + 0x08, 0x09, 0x09, 0x0A, 0x09, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80, +}; + +static unsigned char ufs_fragtable_other[] = { + 0x00, 0x16, 0x16, 0x2A, 0x16, 0x16, 0x26, 0x4E, 0x16, 0x16, 0x16, 0x3E, 0x2A, 0x3E, 0x4E, 0x8A, + 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E, + 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E, + 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA, + 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E, + 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E, + 0x26, 0x36, 0x36, 0x2E, 0x36, 0x36, 0x26, 0x6E, 0x36, 0x36, 0x36, 0x3E, 0x2E, 0x3E, 0x6E, 0xAE, + 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE, + 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E, + 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E, + 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E, + 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE, + 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA, + 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE, + 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE, + 0x8A, 0x9E, 0x9E, 0xAA, 0x9E, 0x9E, 0xAE, 0xCE, 0x9E, 0x9E, 0x9E, 0xBE, 0xAA, 0xBE, 0xCE, 0x8A, +}; diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c new file mode 100644 index 000000000..a822438b6 --- /dev/null +++ b/fs/ufs/cylinder.c @@ -0,0 +1,212 @@ +/* + * linux/fs/ufs/cylinder.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + * + * ext2 - inode (block) bitmap caching inspired + */ + +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/locks.h> + +#include <asm/bitops.h> +#include <asm/byteorder.h> + +#include "swab.h" +#include "util.h" + +#undef UFS_CYLINDER_DEBUG +#undef UFS_CYLINDER_DEBUG_MORE + +#ifdef UFS_CYLINDER_DEBUG +#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + + +/* + * Read cylinder group into cache. The memory space for ufs_cg_private_info + * structure is already allocated during ufs_read_super. + */ +static void ufs_read_cylinder (struct super_block * sb, + unsigned cgno, unsigned bitmap_nr) +{ + struct ufs_sb_private_info * uspi; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + unsigned i, j; + unsigned swab; + + UFSD(("ENTER, cgno %u, bitmap_nr %u\n", cgno, bitmap_nr)) + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + ucpi = sb->u.ufs_sb.s_ucpi[bitmap_nr]; + ucg = (struct ufs_cylinder_group *)sb->u.ufs_sb.s_ucg[cgno]->b_data; + +#ifdef UFS_CYLINDER_DEBUG_MORE + ufs_print_cylinder_stuff (ucg, swab); +#endif + + UCPI_UBH->fragment = ufs_cgcmin(cgno); + UCPI_UBH->count = uspi->s_cgsize >> sb->s_blocksize_bits; + /* + * We have already the first fragment of cylinder group block in buffer + */ + UCPI_UBH->bh[0] = sb->u.ufs_sb.s_ucg[cgno]; + for (i = 1; i < UCPI_UBH->count; i++) + if (!(UCPI_UBH->bh[i] = bread (sb->s_dev, UCPI_UBH->fragment + i, sb->s_blocksize))) + goto failed; + sb->u.ufs_sb.s_cgno[bitmap_nr] = cgno; + + ucpi->c_cgx = SWAB32(ucg->cg_cgx); + ucpi->c_ncyl = SWAB16(ucg->cg_ncyl); + ucpi->c_niblk = SWAB16(ucg->cg_niblk); + ucpi->c_ndblk = SWAB32(ucg->cg_ndblk); + ucpi->c_rotor = SWAB32(ucg->cg_rotor); + ucpi->c_frotor = SWAB32(ucg->cg_frotor); + ucpi->c_irotor = SWAB32(ucg->cg_irotor); + ucpi->c_btotoff = SWAB32(ucg->cg_btotoff); + ucpi->c_boff = SWAB32(ucg->cg_boff); + ucpi->c_iusedoff = SWAB32(ucg->cg_iusedoff); + ucpi->c_freeoff = SWAB32(ucg->cg_freeoff); + ucpi->c_nextfreeoff = SWAB32(ucg->cg_nextfreeoff); + + UFSD(("EXIT\n")) + return; + +failed: + for (j = 1; j < i; j++) + brelse (sb->u.ufs_sb.s_ucg[j]); + sb->u.ufs_sb.s_cgno[bitmap_nr] = UFS_CGNO_EMPTY; + ufs_error (sb, "ufs_read_cylinder", "can't read cylinder group block %u", cgno); +} + +/* + * Remove cylinder group from cache, does'n release memory + * allocated for cylinder group (this is done at ufs_put_super only). + */ +void ufs_put_cylinder (struct super_block * sb, unsigned bitmap_nr) +{ + struct ufs_sb_private_info * uspi; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + unsigned i; + unsigned swab; + + UFSD(("ENTER, bitmap_nr %u\n", bitmap_nr)) + + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + if (sb->u.ufs_sb.s_cgno[bitmap_nr] == UFS_CGNO_EMPTY) { + UFSD(("EXIT\n")) + return; + } + ucpi = sb->u.ufs_sb.s_ucpi[bitmap_nr]; + ucg = ubh_get_ucg(UCPI_UBH); + + if (uspi->s_ncg > UFS_MAX_GROUP_LOADED && bitmap_nr >= sb->u.ufs_sb.s_cg_loaded) { + ufs_panic (sb, "ufs_put_cylinder", "internal error"); + return; + } + /* + * rotor is not so important data, so we put it to disk + * at the end of working with cylinder + */ + ucg->cg_rotor = SWAB32(ucpi->c_rotor); + ucg->cg_frotor = SWAB32(ucpi->c_frotor); + ucg->cg_irotor = SWAB32(ucpi->c_irotor); + ubh_mark_buffer_dirty (UCPI_UBH, 1); + for (i = 1; i < UCPI_UBH->count; i++) { + brelse (UCPI_UBH->bh[i]); + } + + sb->u.ufs_sb.s_cgno[bitmap_nr] = UFS_CGNO_EMPTY; + UFSD(("EXIT\n")) +} + +/* + * Find cylinder group in cache and return it as pointer. + * If cylinder group is not in cache, we will load it from disk. + * + * The cache is managed by LRU alghoritm. + */ +struct ufs_cg_private_info * ufs_load_cylinder ( + struct super_block * sb, unsigned cgno) +{ + struct ufs_sb_private_info * uspi; + struct ufs_cg_private_info * ucpi; + unsigned cg, i, j; + + UFSD(("ENTER, cgno %u\n", cgno)) + + uspi = sb->u.ufs_sb.s_uspi; + if (cgno >= uspi->s_ncg) { + ufs_panic (sb, "ufs_load_cylinder", "internal error, high number of cg"); + return NULL; + } + /* + * Cylinder group number cg it in cache and it was last used + */ + if (sb->u.ufs_sb.s_cgno[0] == cgno) { + UFSD(("EXIT\n")) + return sb->u.ufs_sb.s_ucpi[0]; + } + /* + * Number of cylinder groups is not higher than UFS_MAX_GROUP_LOADED + */ + if (uspi->s_ncg <= UFS_MAX_GROUP_LOADED) { + if (sb->u.ufs_sb.s_cgno[cgno] != UFS_CGNO_EMPTY) { + if (sb->u.ufs_sb.s_cgno[cgno] != cgno) { + ufs_panic (sb, "ufs_load_cylinder", "internal error, wrog number of cg in cache"); + UFSD(("EXIT (FAILED)\n")) + return NULL; + } + else { + UFSD(("EXIT\n")) + return sb->u.ufs_sb.s_ucpi[cgno]; + } + } else { + ufs_read_cylinder (sb, cgno, cgno); + UFSD(("EXIT\n")) + return sb->u.ufs_sb.s_ucpi[cgno]; + } + } + /* + * Cylinder group number cg is in cache but it was not last used, + * we will move to the first position + */ + for (i = 0; i < sb->u.ufs_sb.s_cg_loaded && sb->u.ufs_sb.s_cgno[i] != cgno; i++); + if (i < sb->u.ufs_sb.s_cg_loaded && sb->u.ufs_sb.s_cgno[i] == cgno) { + cg = sb->u.ufs_sb.s_cgno[i]; + ucpi = sb->u.ufs_sb.s_ucpi[i]; + for (j = i; j > 0; j--) { + sb->u.ufs_sb.s_cgno[j] = sb->u.ufs_sb.s_cgno[j-1]; + sb->u.ufs_sb.s_ucpi[j] = sb->u.ufs_sb.s_ucpi[j-1]; + } + sb->u.ufs_sb.s_cgno[0] = cg; + sb->u.ufs_sb.s_ucpi[0] = ucpi; + /* + * Cylinder group number cg is not in cache, we will read it from disk + * and put it to the first possition + */ + } else { + if (sb->u.ufs_sb.s_cg_loaded < UFS_MAX_GROUP_LOADED) + sb->u.ufs_sb.s_cg_loaded++; + else + ufs_put_cylinder (sb, UFS_MAX_GROUP_LOADED-1); + for (j = sb->u.ufs_sb.s_cg_loaded - 1; j > 0; j--) { + sb->u.ufs_sb.s_cgno[j] = sb->u.ufs_sb.s_cgno[j-1]; + sb->u.ufs_sb.s_ucpi[j] = sb->u.ufs_sb.s_ucpi[j-1]; + } + ufs_read_cylinder (sb, cgno, 0); + } + UFSD(("EXIT\n")) + return sb->u.ufs_sb.s_ucpi[0]; +} diff --git a/fs/ufs/ufs_dir.c b/fs/ufs/dir.c index e6d27c217..ee0e85674 100644 --- a/fs/ufs/ufs_dir.c +++ b/fs/ufs/dir.c @@ -11,11 +11,22 @@ * 4.4BSD (FreeBSD) support added on February 1st 1998 by * Niels Kristian Bech Jensen <nkbj@image.dk> partially based * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. + * + * write support by Daniel Pirkl <daniel.pirkl@email.cz> 1998 */ #include <linux/fs.h> -#include "ufs_swab.h" +#include "swab.h" +#include "util.h" + +#undef UFS_DIR_DEBUG + +#ifdef UFS_DIR_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif /* * This is blatantly stolen from ext2fs @@ -28,10 +39,11 @@ ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) unsigned long offset, lblk, blk; int i, stored; struct buffer_head * bh; - struct ufs_direct * de; + struct ufs_dir_entry * de; struct super_block * sb; int de_reclen; - __u32 flags; + unsigned flags, swab; + /* Isn't that already done in the upper layer??? * the VFS layer really needs some explicit documentation! @@ -40,13 +52,10 @@ ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) return -EBADF; sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; flags = sb->u.ufs_sb.s_flags; - if (flags & UFS_DEBUG) { - printk("ufs_readdir: ino %lu f_pos %lu\n", - inode->i_ino, (unsigned long) filp->f_pos); - ufs_print_inode(inode); - } + UFSD(("ENTER, ino %lu f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos)) stored = 0; bh = NULL; @@ -73,8 +82,7 @@ revalidate: * to make sure. */ if (filp->f_version != inode->i_version) { for (i = 0; i < sb->s_blocksize && i < offset; ) { - de = (struct ufs_direct *) - (bh->b_data + i); + de = (struct ufs_dir_entry *)(bh->b_data + i); /* It's too expensive to do a full * dirent test each time round this * loop, but we do have to test at @@ -94,9 +102,9 @@ revalidate: while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { - de = (struct ufs_direct *) (bh->b_data + offset); + de = (struct ufs_dir_entry *) (bh->b_data + offset); /* XXX - put in a real ufs_check_dir_entry() */ - if ((de->d_reclen == 0) || (NAMLEN(de) == 0)) { + if ((de->d_reclen == 0) || (ufs_namlen(de) == 0)) { /* SWAB16() was unneeded -- compare to 0 */ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + @@ -128,11 +136,9 @@ revalidate: * during the copy operation. */ unsigned long version = inode->i_version; - if (flags & UFS_DEBUG) { - printk("ufs_readdir: filldir(%s,%u)\n", - de->d_name, SWAB32(de->d_ino)); - } - error = filldir(dirent, de->d_name, NAMLEN(de), + UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino))) + UFSD(("namlen %u\n", ufs_namlen(de))) + error = filldir(dirent, de->d_name, ufs_namlen(de), filp->f_pos, SWAB32(de->d_ino)); if (error) break; @@ -145,15 +151,45 @@ revalidate: offset = 0; brelse (bh); } -#if 0 /* XXX */ - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } -#endif /* XXX */ + UPDATE_ATIME(inode); return 0; } +int ufs_check_dir_entry (const char * function, struct inode * dir, + struct ufs_dir_entry * de, struct buffer_head * bh, + unsigned long offset) +{ + struct super_block * sb; + const char * error_msg; + unsigned flags, swab; + + sb = dir->i_sb; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; + error_msg = NULL; + + if (SWAB16(de->d_reclen) < UFS_DIR_REC_LEN(1)) + error_msg = "reclen is smaller than minimal"; + else if (SWAB16(de->d_reclen) % 4 != 0) + error_msg = "reclen % 4 != 0"; + else if (SWAB16(de->d_reclen) < UFS_DIR_REC_LEN(ufs_namlen(de))) + error_msg = "reclen is too small for namlen"; + else if (dir && ((char *) de - bh->b_data) + SWAB16(de->d_reclen) > + dir->i_sb->s_blocksize) + error_msg = "directory entry across blocks"; + else if (dir && SWAB32(de->d_ino) > (sb->u.ufs_sb.s_uspi->s_ipg * sb->u.ufs_sb.s_uspi->s_ncg)) + error_msg = "inode out of bounds"; + + if (error_msg != NULL) + ufs_error (sb, function, "bad entry in directory #%lu, size %lu: %s - " + "offset=%lu, inode=%lu, reclen=%d, namlen=%d", + dir->i_ino, dir->i_size, error_msg, offset, + (unsigned long) SWAB32(de->d_ino), + SWAB16(de->d_reclen), ufs_namlen(de)); + + return (error_msg == NULL ? 1 : 0); +} + static struct file_operations ufs_dir_operations = { NULL, /* lseek */ NULL, /* read */ @@ -172,20 +208,21 @@ static struct file_operations ufs_dir_operations = { struct inode_operations ufs_dir_inode_operations = { &ufs_dir_operations, /* default directory file operations */ - NULL, /* create */ + ufs_create, /* create */ ufs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ + ufs_link, /* link */ + ufs_unlink, /* unlink */ + ufs_symlink, /* symlink */ + ufs_mkdir, /* mkdir */ + ufs_rmdir, /* rmdir */ + ufs_mknod, /* mknod */ + ufs_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL, /* permission */ + ufs_bmap, /* bmap */ + ufs_truncate, /* truncate */ + ufs_permission, /* permission */ NULL, /* smap */ }; diff --git a/fs/ufs/file.c b/fs/ufs/file.c new file mode 100644 index 000000000..a6fa50377 --- /dev/null +++ b/fs/ufs/file.c @@ -0,0 +1,281 @@ +/* + * linux/fs/ufs/file.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/file.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/file.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext2 fs regular file handling primitives + */ + +#include <asm/uaccess.h> +#include <asm/system.h> + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/locks.h> +#include <linux/mm.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/ufs_fs.h> + +static long long ufs_file_lseek(struct file *, long long, int); +static ssize_t ufs_file_write (struct file *, const char *, size_t, loff_t *); +static int ufs_release_file (struct inode *, struct file *); + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ufs filesystem. + */ +static struct file_operations ufs_file_operations = { + ufs_file_lseek, /* lseek */ + generic_file_read, /* read */ + ufs_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + NULL, /* ioctl */ + generic_file_mmap, /* mmap */ + NULL, /* no special open is needed */ + ufs_release_file, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +struct inode_operations ufs_file_inode_operations = { + &ufs_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 */ + ufs_bmap, /* bmap */ + ufs_truncate, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; + +/* + * Make sure the offset never goes beyond the 32-bit mark.. + */ +static long long ufs_file_lseek( + struct file *file, + long long offset, + int origin ) +{ + long long retval; + struct inode *inode = file->f_dentry->d_inode; + + switch (origin) { + case 2: + offset += inode->i_size; + break; + case 1: + offset += file->f_pos; + } + retval = -EINVAL; + /* make sure the offset fits in 32 bits */ + if (((unsigned long long) offset >> 32) == 0) { + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_reada = 0; + file->f_version = ++event; + } + retval = offset; + } + return retval; +} + +static inline void remove_suid(struct inode *inode) +{ + unsigned int mode; + + /* set S_IGID if S_IXGRP is set, and always set S_ISUID */ + mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID; + + /* was any of the uid bits set? */ + mode &= inode->i_mode; + if (mode && !suser()) { + inode->i_mode &= ~mode; + mark_inode_dirty(inode); + } +} + +static ssize_t ufs_file_write ( + struct file * filp, + const char * buf, + size_t count, + loff_t *ppos ) +{ + struct inode * inode = filp->f_dentry->d_inode; + __u32 pos; + long block; + int offset; + int written, c; + struct buffer_head * bh, *bufferlist[NBUF]; + struct super_block * sb; + int err; + int i,buffercount,write_error; + + /* POSIX: mtime/ctime may not change for 0 count */ + if (!count) + return 0; + write_error = buffercount = 0; + if (!inode) + return -EINVAL; + sb = inode->i_sb; + if (sb->s_flags & MS_RDONLY) + /* + * This fs has been automatically remounted ro because of errors + */ + return -ENOSPC; + + if (!S_ISREG(inode->i_mode)) { + ufs_warning (sb, "ufs_file_write", "mode = %07o", + inode->i_mode); + return -EINVAL; + } + remove_suid(inode); + + if (filp->f_flags & O_APPEND) + pos = inode->i_size; + else { + pos = *ppos; + if (pos != *ppos) + return -EINVAL; + } + + /* Check for overflow.. */ + if (pos > (__u32) (pos + count)) { + count = ~pos; /* == 0xFFFFFFFF - pos */ + if (!count) + return -EFBIG; + } + + /* + * If a file has been opened in synchronous mode, we have to ensure + * that meta-data will also be written synchronously. Thus, we + * set the i_osync field. This field is tested by the allocation + * routines. + */ + if (filp->f_flags & O_SYNC) + inode->u.ufs_i.i_osync++; + block = pos >> sb->s_blocksize_bits; + offset = pos & (sb->s_blocksize - 1); + c = sb->s_blocksize - offset; + written = 0; + do { + bh = ufs_getfrag (inode, block, 1, &err); + if (!bh) { + if (!written) + written = err; + break; + } + if (c > count) + c = count; + if (c != sb->s_blocksize && !buffer_uptodate(bh)) { + ll_rw_block (READ, 1, &bh); + wait_on_buffer (bh); + if (!buffer_uptodate(bh)) { + brelse (bh); + if (!written) + written = -EIO; + break; + } + } + c -= copy_from_user (bh->b_data + offset, buf, c); + if (!c) { + brelse(bh); + if (!written) + written = -EFAULT; + break; + } + update_vm_cache(inode, pos, bh->b_data + offset, c); + pos += c; + written += c; + buf += c; + count -= c; + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 0); + if (filp->f_flags & O_SYNC) + bufferlist[buffercount++] = bh; + else + brelse(bh); + if (buffercount == NBUF){ + ll_rw_block(WRITE, buffercount, bufferlist); + for(i=0; i<buffercount; i++){ + wait_on_buffer(bufferlist[i]); + if (!buffer_uptodate(bufferlist[i])) + write_error=1; + brelse(bufferlist[i]); + } + buffercount=0; + } + if (write_error) + break; + block++; + offset = 0; + c = sb->s_blocksize; + } while (count); + if (buffercount){ + ll_rw_block(WRITE, buffercount, bufferlist); + for (i=0; i<buffercount; i++){ + wait_on_buffer(bufferlist[i]); + if (!buffer_uptodate(bufferlist[i])) + write_error=1; + brelse(bufferlist[i]); + } + } + if (pos > inode->i_size) + inode->i_size = pos; + if (filp->f_flags & O_SYNC) + inode->u.ufs_i.i_osync--; + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + *ppos = pos; + mark_inode_dirty(inode); + return written; +} + +/* + * Called when an inode is released. Note that this is different + * from ufs_open: open gets called at every open, but release + * gets called only when /all/ the files are closed. + */ +static int ufs_release_file (struct inode * inode, struct file * filp) +{ + return 0; +} diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c new file mode 100644 index 000000000..6da43f20a --- /dev/null +++ b/fs/ufs/ialloc.c @@ -0,0 +1,341 @@ +/* + * linux/fs/ufs/ialloc.c + * + * Copyright (c) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/ialloc.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * BSD ufs-inspired inode and directory allocation by + * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 + */ + +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/locks.h> +#include <linux/quotaops.h> +#include <asm/bitops.h> +#include <asm/byteorder.h> + +#include "swab.h" +#include "util.h" + +#undef UFS_IALLOC_DEBUG +#undef UFS_IALLOC_DEBUG_MORE + +#ifdef UFS_IALLOC_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +#ifdef UFS_IALLOC_DEBUG_MORE +#define UFSDM \ +ufs_print_cylinder_stuff (ucg, swab); \ +printk("inode: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nifree), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \ +printk("block: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nbfree), SWAB32(ucg->cg_cs.cs_nbfree)); \ +printk("fragment: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nffree), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nffree), SWAB32(ucg->cg_cs.cs_nffree)); \ +printk("ndir: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_ndir), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_ndir), SWAB32(ucg->cg_cs.cs_ndir)); +#else +#define UFSDM +#endif + + +/* + * NOTE! When we get the inode, we're the only people + * that have access to it, and as such there are no + * race conditions we have to worry about. The inode + * is not on the hash-lists, and it cannot be reached + * through the filesystem because the directory entry + * has been deleted earlier. + * + * HOWEVER: we must make sure that we get no aliases, + * which means that we have to call "clear_inode()" + * _before_ we mark the inode not in use in the inode + * bitmaps. Otherwise a newly created file might use + * the same inode number (not actually the same pointer + * though), and then we'd have two inodes sharing the + * same inode number and space on the harddisk. + */ +void ufs_free_inode (struct inode * inode) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + int is_directory; + unsigned ino, cg, bit; + unsigned swab; + + UFSD(("ENTER, ino %lu\n", inode->i_ino)) + + if (!inode) + return; + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + + if (inode->i_count > 1) { + ufs_warning(sb, "ufs_free_inode", "inode has count=%d\n", inode->i_count); + return; + } + if (inode->i_nlink) { + ufs_warning(sb, "ufs_free_inode", "inode has nlink=%d\n", inode->i_nlink); + return; + } + + ino = inode->i_ino; + + lock_super (sb); + + if (!((ino > 1) && (ino < (uspi->s_ncg * uspi->s_ipg )))) { + ufs_warning(sb, "ufs_free_inode", "reserved inode or nonexistent inode %u\n", ino); + unlock_super (sb); + return; + } + + cg = ufs_inotocg (ino); + bit = ufs_inotocgoff (ino); + ucpi = ufs_load_cylinder (sb, cg); + if (!ucpi) { + unlock_super (sb); + return; + } + ucg = ubh_get_ucg(UCPI_UBH); + if (!ufs_cg_chkmagic(ucg)) + ufs_panic (sb, "ufs_free_fragments", "internal error, bad cg magic number"); + + UFSDM + + ucg->cg_time = SWAB32(CURRENT_TIME); + + is_directory = S_ISDIR(inode->i_mode); + + DQUOT_FREE_INODE(sb, inode); + + clear_inode (inode); + + if (ubh_isclr (UCPI_UBH, ucpi->c_iusedoff, bit)) + ufs_error(sb, "ufs_free_inode", "bit already cleared for inode %u", ino); + else { + ubh_clrbit (UCPI_UBH, ucpi->c_iusedoff, bit); + if (ino < ucpi->c_irotor) + ucpi->c_irotor = ino; + INC_SWAB32(ucg->cg_cs.cs_nifree); + INC_SWAB32(usb1->fs_cstotal.cs_nifree); + INC_SWAB32(sb->fs_cs(cg).cs_nifree); + + if (is_directory) { + DEC_SWAB32(ucg->cg_cs.cs_ndir); + DEC_SWAB32(usb1->fs_cstotal.cs_ndir); + DEC_SWAB32(sb->fs_cs(cg).cs_ndir); + } + } + ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (UCPI_UBH, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **) &ucpi); + ubh_wait_on_buffer (UCPI_UBH); + } + + UFSDM + + sb->s_dirt = 1; + unlock_super (sb); + UFSD(("EXIT\n")) +} + +/* + * There are two policies for allocating an inode. If the new inode is + * a directory, then a forward search is made for a block group with both + * free space and a low directory-to-inode ratio; if that fails, then of + * the groups with above-average free space, that group with the fewest + * directories already is chosen. + * + * For other inodes, search forward from the parent directory\'s block + * group to find a free inode. + */ +struct inode * ufs_new_inode (const struct inode * dir, int mode, int * err ) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_cg_private_info * ucpi; + struct ufs_cylinder_group * ucg; + struct inode * inode; + unsigned cg, bit, i, j, start; + unsigned swab; + + UFSD(("ENTER\n")) + + /* Cannot create files in a deleted directory */ + if (!dir || !dir->i_nlink) { + *err = -EPERM; + return NULL; + } + inode = get_empty_inode (); + if (!inode) { + *err = -ENOMEM; + return NULL; + } + sb = dir->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + + inode->i_sb = sb; + inode->i_flags = sb->s_flags; + + lock_super (sb); + + *err = -ENOSPC; + + /* + * Try to place the inode in its parent directory + */ + i = ufs_inotocg(dir->i_ino); + if (SWAB32(sb->fs_cs(i).cs_nifree)) { + cg = i; + goto cg_found; + } + + /* + * Use a quadratic hash to find a group with a free inode + */ + for ( j = 1; j < uspi->s_ncg; j <<= 1 ) { + i += j; + if (i >= uspi->s_ncg) + i -= uspi->s_ncg; + if (SWAB32(sb->fs_cs(i).cs_nifree)) { + cg = i; + goto cg_found; + } + } + + /* + * That failed: try linear search for a free inode + */ + i = ufs_inotocg(dir->i_ino) + 1; + for (j = 2; j < uspi->s_ncg; j++) { + i++; + if (i >= uspi->s_ncg) + i = 0; + if (SWAB32(sb->fs_cs(i).cs_nifree)) { + cg = i; + goto cg_found; + } + } + + goto failed; + +cg_found: + ucpi = ufs_load_cylinder (sb, cg); + if (!ucpi) + goto failed; + ucg = ubh_get_ucg(UCPI_UBH); + if (!ufs_cg_chkmagic(ucg)) + ufs_panic (sb, "ufs_new_inode", "internal error, bad cg magic number"); + + UFSDM + + start = ucpi->c_irotor; + bit = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_iusedoff, uspi->s_ipg, start); + if (!(bit < uspi->s_ipg)) { + bit = ubh_find_first_zero_bit (UCPI_UBH, ucpi->c_iusedoff, start); + if (!(bit < start)) { + ufs_error (sb, "ufs_new_inode", + "cylinder group %u corrupted - error in inode bitmap\n", cg); + goto failed; + } + } + UFSD(("start = %u, bit = %u, ipg = %u\n", start, bit, uspi->s_ipg)) + if (ubh_isclr (UCPI_UBH, ucpi->c_iusedoff, bit)) + ubh_setbit (UCPI_UBH, ucpi->c_iusedoff, bit); + else { + ufs_panic (sb, "ufs_new_inode", "internal error"); + goto failed; + } + + DEC_SWAB32(ucg->cg_cs.cs_nifree); + DEC_SWAB32(usb1->fs_cstotal.cs_nifree); + DEC_SWAB32(sb->fs_cs(cg).cs_nifree); + + if (S_ISDIR(mode)) { + INC_SWAB32(ucg->cg_cs.cs_ndir); + INC_SWAB32(usb1->fs_cstotal.cs_ndir); + INC_SWAB32(sb->fs_cs(cg).cs_ndir); + } + + ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (UCPI_UBH, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **) &ucpi); + ubh_wait_on_buffer (UCPI_UBH); + } + sb->s_dirt = 1; + + inode->i_mode = mode; + inode->i_sb = sb; + inode->i_nlink = 1; + inode->i_dev = sb->s_dev; + inode->i_uid = current->fsuid; + if (test_opt (sb, GRPID)) + inode->i_gid = dir->i_gid; + else if (dir->i_mode & S_ISGID) { + inode->i_gid = dir->i_gid; + if (S_ISDIR(mode)) + mode |= S_ISGID; + } else + inode->i_gid = current->fsgid; + + inode->i_ino = cg * uspi->s_ipg + bit; + inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ + inode->i_blocks = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->u.ufs_i.i_flags = dir->u.ufs_i.i_flags; + inode->u.ufs_i.i_uid = inode->i_uid; + inode->u.ufs_i.i_gid = inode->i_gid; + inode->u.ufs_i.i_lastfrag = 0; + inode->i_op = NULL; + + insert_inode_hash(inode); + mark_inode_dirty(inode); + + UFSDM + + unlock_super (sb); + + if(DQUOT_ALLOC_INODE(sb, inode)) { + sb->dq_op->drop(inode); + inode->i_nlink = 0; + iput(inode); + *err = -EDQUOT; + return NULL; + } + + UFSD(("allocating inode %lu\n", inode->i_ino)) + *err = 0; + UFSD(("EXIT\n")) + return inode; + +failed: + unlock_super (sb); + iput (inode); + UFSD(("EXIT (FAILED)\n")) + return NULL; +} diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c new file mode 100644 index 000000000..123a76a75 --- /dev/null +++ b/fs/ufs/inode.c @@ -0,0 +1,665 @@ +/* + * linux/ufs/ufs/inode.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/inode.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/inode.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 + */ + +#include <asm/uaccess.h> +#include <asm/system.h> + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/locks.h> +#include <linux/mm.h> + +#include "swab.h" +#include "util.h" + +#undef UFS_INODE_DEBUG +#undef UFS_INODE_DEBUG_MORE + +#ifdef UFS_INODE_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +#ifdef UFS_INODE_DEBUG_MORE +static void ufs_print_inode(struct inode * inode) +{ + unsigned swab = inode->i_sb->u.ufs_sb.s_swab; + printk("ino %lu mode 0%6.6o nlink %d uid %d gid %d" + " size %lu blocks %lu\n", + inode->i_ino, inode->i_mode, inode->i_nlink, + inode->i_uid,inode->i_gid, inode->i_size, inode->i_blocks); + printk(" db <%u %u %u %u %u %u %u %u %u %u %u %u>\n", + SWAB32(inode->u.ufs_i.i_u1.i_data[0]), + SWAB32(inode->u.ufs_i.i_u1.i_data[1]), + SWAB32(inode->u.ufs_i.i_u1.i_data[2]), + SWAB32(inode->u.ufs_i.i_u1.i_data[3]), + SWAB32(inode->u.ufs_i.i_u1.i_data[4]), + SWAB32(inode->u.ufs_i.i_u1.i_data[5]), + SWAB32(inode->u.ufs_i.i_u1.i_data[6]), + SWAB32(inode->u.ufs_i.i_u1.i_data[7]), + SWAB32(inode->u.ufs_i.i_u1.i_data[8]), + SWAB32(inode->u.ufs_i.i_u1.i_data[9]), + SWAB32(inode->u.ufs_i.i_u1.i_data[10]), + SWAB32(inode->u.ufs_i.i_u1.i_data[11])); + printk(" gen %u ib <%u %u %u>\n", + inode->u.ufs_i.i_gen, + SWAB32(inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK]), + SWAB32(inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK]), + SWAB32(inode->u.ufs_i.i_u1.i_data[UFS_TIND_BLOCK])); +} +#endif + +#define ufs_inode_bmap(inode, nr) \ + (SWAB32((inode)->u.ufs_i.i_u1.i_data[(nr) >> uspi->s_fpbshift]) + ((nr) & uspi->s_fpbmask)) + +static inline unsigned ufs_block_bmap (struct buffer_head * bh, unsigned nr, + struct ufs_sb_private_info * uspi, unsigned swab) +{ + unsigned tmp; + + UFSD(("ENTER, nr %u\n", nr)) + if (!bh) + return 0; + tmp = SWAB32(((u32 *) bh->b_data)[nr >> uspi->s_fpbshift]) + (nr & uspi->s_fpbmask); + brelse (bh); + UFSD(("EXIT, resutl %u\n", tmp)) + return tmp; +} + +int ufs_bmap (struct inode * inode, int fragment) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + unsigned tmp; + unsigned swab; + + sb = inode->i_sb; + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; + + UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) + + if (fragment >= ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) { + ufs_warning (sb, "ufs_bmap", "block > big"); + return 0; + } + + /* + * direct fragment + */ + if (fragment < UFS_NDIR_FRAGMENT) + return ufs_inode_bmap (inode, fragment); + + /* + * indirect fragment + */ + fragment -= UFS_NDIR_FRAGMENT; + if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { + tmp = ufs_inode_bmap (inode, + UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift)); + if (!tmp) + return 0; + return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + fragment & uspi->s_apbmask, uspi, swab); + } + + /* + * dindirect fragment + */ + fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); + if (fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { + tmp = ufs_inode_bmap (inode, + UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift)); + if (!tmp) + return 0; + tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + (fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab); + if (!tmp) + return 0; + return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + fragment & uspi->s_apbmask, uspi, swab); + } + + /* + * tindirect fragment + */ + fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); + tmp = ufs_inode_bmap (inode, + UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift)); + if (!tmp) + return 0; + tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + (fragment >> uspi->s_2apbshift) & uspi->s_apbmask, uspi, swab); + if (!tmp) + return 0; + tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + (fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab); + if (!tmp) + return 0; + return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + fragment & uspi->s_apbmask, uspi, swab); +} + +static struct buffer_head * ufs_inode_getfrag (struct inode * inode, + unsigned fragment, unsigned new_fragment, int create, + unsigned required, int * err ) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct buffer_head * result; + unsigned long limit; + unsigned block, blockoff, lastfrag, lastblock, lastblockoff; + unsigned tmp, goal; + u32 * p, * p2; + unsigned swab; + + UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u, required %u\n", + inode->i_ino, fragment, new_fragment, required)) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + block = ufs_fragstoblks (fragment); + blockoff = ufs_fragnum (fragment); + p = inode->u.ufs_i.i_u1.i_data + block; + goal = 0; + +repeat: + tmp = SWAB32(*p); + lastfrag = inode->u.ufs_i.i_lastfrag; + if (tmp && fragment < lastfrag) { + result = getblk (sb->s_dev, tmp + blockoff, sb->s_blocksize); + if (tmp == SWAB32(*p)) { + UFSD(("EXIT, result %u\n", tmp + blockoff)) + return result; + } + brelse (result); + goto repeat; + } + *err = -EFBIG; + if (!create) + return NULL; + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= sb->s_blocksize_bits; + if (new_fragment >= limit) { + send_sig(SIGXFSZ, current, 0); + return NULL; + } + } + lastblock = ufs_fragstoblks (lastfrag); + lastblockoff = ufs_fragnum (lastfrag); + /* + * We will extend file into new block beyond last allocated block + */ + if (lastblock < block) { + /* + * We must reallocate last allocated block + */ + if (lastblockoff) { + p2 = inode->u.ufs_i.i_u1.i_data + lastblock; + tmp = ufs_new_fragments (inode, p2, lastfrag, + SWAB32(*p2), uspi->s_fpb - lastblockoff, err); + if (!tmp) { + if (lastfrag != inode->u.ufs_i.i_lastfrag) + goto repeat; + else + return NULL; + } + lastfrag = inode->u.ufs_i.i_lastfrag; + + } + goal = SWAB32(inode->u.ufs_i.i_u1.i_data[lastblock]) + uspi->s_fpb; + tmp = ufs_new_fragments (inode, p, fragment - blockoff, + goal, required + blockoff, err); + } + /* + * We will extend last allocated block + */ + else if (lastblock == block) { + tmp = ufs_new_fragments (inode, p, fragment - (blockoff - lastblockoff), + SWAB32(*p), required + (blockoff - lastblockoff), err); + } + /* + * We will allocated new block before last allocat block + */ + else /* (lastblock > block) */ { + if (lastblock && (tmp = SWAB32(inode->u.ufs_i.i_u1.i_data[lastblock-1]))) + goal = tmp + uspi->s_fpb; + tmp = ufs_new_fragments (inode, p, fragment - blockoff, + goal, uspi->s_fpb, err); + } + if (!tmp) { + if ((!blockoff && SWAB32(*p)) || + (blockoff && lastfrag != inode->u.ufs_i.i_lastfrag)) + goto repeat; + else + return NULL; + } + result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize); + inode->i_ctime = CURRENT_TIME; + if (IS_SYNC(inode)) + ufs_sync_inode (inode); + mark_inode_dirty(inode); + UFSD(("EXIT, result %u\n", tmp + blockoff)) + return result; +} + +static struct buffer_head * ufs_block_getfrag (struct inode * inode, + struct buffer_head * bh, unsigned fragment, unsigned new_fragment, + int create, unsigned blocksize, int * err) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct buffer_head * result; + unsigned tmp, goal, block, blockoff; + u32 * p; + unsigned swab; + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + block = ufs_fragstoblks (fragment); + blockoff = ufs_fragnum (fragment); + + UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u\n", inode->i_ino, fragment, new_fragment)) + + 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 = (u32 *) bh->b_data + block; +repeat: + tmp = SWAB32(*p); + if (tmp) { + result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); + if (tmp == SWAB32(*p)) { + brelse (bh); + UFSD(("EXIT, result %u\n", tmp + blockoff)) + return result; + } + brelse (result); + goto repeat; + } + if (!create || new_fragment >= (current->rlim[RLIMIT_FSIZE].rlim_cur >> sb->s_blocksize)) { + brelse (bh); + *err = -EFBIG; + return NULL; + } + if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb)) + goal = tmp + uspi->s_fpb; + else + goal = bh->b_blocknr + uspi->s_fpb; + tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err); + if (!tmp) { + if (SWAB32(*p)) { + printk("REPEAT\n"); + goto repeat; + } + else { + return NULL; + } + } + result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); + mark_buffer_dirty(bh, 1); + if (IS_SYNC(inode)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + brelse (bh); + UFSD(("EXIT, resutl %u\n", tmp + blockoff)) + return result; +} + +struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, + int create, int * err) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct buffer_head * bh; + unsigned f; + unsigned swab; + + sb = inode->i_sb; + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; + *err = -EIO; + + UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) + if (fragment > ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) { + ufs_warning (sb, "ufs_getblk", "block > big"); + return NULL; + } + + *err = -ENOSPC; + f = fragment; + + /* + * Direct fragment + */ + if (fragment < UFS_NDIR_FRAGMENT) + return ufs_inode_getfrag (inode, fragment, fragment, create, 1, err); + /* + * Indirect fragment + */ + fragment -= UFS_NDIR_FRAGMENT; + if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { + bh = ufs_inode_getfrag (inode, + UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift), + f, create, uspi->s_fpb, err); + return ufs_block_getfrag (inode, bh, + fragment & uspi->s_apbmask, + f, create, sb->s_blocksize, err); + } + /* + * Dindirect fragment + */ + fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); + if ( fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { + bh = ufs_inode_getfrag (inode, + UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), + f, create, uspi->s_fpb, err); + bh = ufs_block_getfrag (inode, bh, + (fragment >> uspi->s_apbshift) & uspi->s_apbmask, + f, create, sb->s_blocksize, err); + return ufs_block_getfrag (inode, bh, + fragment & uspi->s_apbmask, + f, create, sb->s_blocksize, err); + } + /* + * Tindirect fragment + */ + fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); + bh = ufs_inode_getfrag (inode, + UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift), + f, create, uspi->s_fpb, err); + bh = ufs_block_getfrag (inode, bh, + (fragment >> uspi->s_2apbshift) & uspi->s_apbmask, + f, create, sb->s_blocksize, err); + bh = ufs_block_getfrag (inode, bh, + (fragment >> uspi->s_apbshift) & uspi->s_apbmask, + f, create, sb->s_blocksize, err); + return ufs_block_getfrag (inode, bh, + fragment & uspi->s_apbmask, + f, create, sb->s_blocksize, err); +} + + + +struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment, + int create, int * err) +{ + struct buffer_head * bh; + + UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) + bh = ufs_getfrag (inode, fragment, create, err); + 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); + *err = -EIO; + return NULL; +} + +void ufs_read_inode (struct inode * inode) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_inode * ufs_inode; + struct buffer_head * bh; + unsigned i; + unsigned flags, swab; + + UFSD(("ENTER, ino %lu\n", inode->i_ino)) + + sb = inode->i_sb; + uspi = sb->u.ufs_sb.s_uspi; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; + + if (inode->i_ino < UFS_ROOTINO || + inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) { + ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino); + return; + } + + bh = bread (sb->s_dev, ufs_inotofsba(inode->i_ino), sb->s_blocksize); + if (!bh) { + ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino); + return; + } + ufs_inode = (struct ufs_inode *) (bh->b_data + sizeof(struct ufs_inode) * ufs_inotofsbo(inode->i_ino)); + + /* + * Copy data to the in-core inode. + */ + inode->i_mode = SWAB16(ufs_inode->ui_mode); + inode->i_nlink = SWAB16(ufs_inode->ui_nlink); + if (inode->i_nlink == 0) + ufs_error (sb, "ufs_read_inode", "inode %lu has zero nlink\n", inode->i_ino); + + /* + * Linux has only 16-bit uid and gid, so we can't support EFT. + * Files are dynamically chown()ed to root. + */ + inode->i_uid = ufs_uid(ufs_inode); + if (inode->i_uid == UFS_USEEFT) { + inode->i_uid = 0; + } + if (inode->i_gid == UFS_USEEFT) { + inode->i_gid = 0; + } + + /* + * Linux i_size can be 32 on some architektures. We will mark + * big files as read only and let user access first 32 bits. + */ + inode->u.ufs_i.i_size = SWAB64(ufs_inode->ui_size); + inode->i_size = (off_t) inode->u.ufs_i.i_size; + if (sizeof(off_t) == 4 && (inode->u.ufs_i.i_size >> 32)) + inode->i_size = (__u32)-1; + + inode->i_atime = SWAB32(ufs_inode->ui_atime.tv_sec); + inode->i_ctime = SWAB32(ufs_inode->ui_ctime.tv_sec); + inode->i_mtime = SWAB32(ufs_inode->ui_mtime.tv_sec); + inode->i_blocks = SWAB32(ufs_inode->ui_blocks); + inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat) */ + inode->i_version = ++event; + + inode->u.ufs_i.i_flags = SWAB32(ufs_inode->ui_flags); + inode->u.ufs_i.i_gen = SWAB32(ufs_inode->ui_gen); + inode->u.ufs_i.i_shadow = SWAB32(ufs_inode->ui_u3.ui_sun.ui_shadow); + inode->u.ufs_i.i_uid = SWAB32(ufs_inode->ui_u3.ui_sun.ui_uid); + inode->u.ufs_i.i_gid = SWAB32(ufs_inode->ui_u3.ui_sun.ui_gid); + inode->u.ufs_i.i_oeftflag = SWAB32(ufs_inode->ui_u3.ui_sun.ui_oeftflag); + inode->u.ufs_i.i_lastfrag = howmany (inode->i_size, uspi->s_fsize); + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + inode->i_rdev = to_kdev_t(SWAB32(ufs_inode->ui_u2.ui_addr.ui_db[0])); + else if (inode->i_blocks) { + for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) + inode->u.ufs_i.i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i]; + } + else { + for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++) + inode->u.ufs_i.i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i]; + } + + brelse (bh); + + inode->i_op = NULL; + + if (S_ISREG(inode->i_mode)) + inode->i_op = &ufs_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &ufs_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &ufs_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_ISSOCK(inode->i_mode)) + ; /* nothing */ + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); + +#ifdef UFS_INODE_DEBUG_MORE + ufs_print_inode (inode); +#endif + UFSD(("EXIT\n")) +} + +static int ufs_update_inode(struct inode * inode, int do_sync) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct buffer_head * bh; + struct ufs_inode * ufs_inode; + unsigned i; + unsigned swab; + + UFSD(("ENTER, ino %lu\n", inode->i_ino)) + + sb = inode->i_sb; + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; + + if (inode->i_ino < UFS_ROOTINO || + inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) { + ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino); + return -1; + } + + bh = bread (sb->s_dev, ufs_inotofsba(inode->i_ino), sb->s_blocksize); + if (!bh) { + ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino); + return -1; + } + ufs_inode = (struct ufs_inode *) (bh->b_data + ufs_inotofsbo(inode->i_ino) * sizeof(struct ufs_inode)); + + ufs_inode->ui_mode = SWAB16(inode->i_mode); + ufs_inode->ui_nlink = SWAB16(inode->i_nlink); + + if (inode->i_uid == 0 && inode->u.ufs_i.i_uid >= UFS_USEEFT) { + ufs_inode->ui_u3.ui_sun.ui_uid = SWAB32(inode->u.ufs_i.i_uid); + ufs_inode->ui_u1.oldids.ui_suid = (__u16)ufs_inode->ui_u3.ui_sun.ui_uid; + } + else { + ufs_inode->ui_u1.oldids.ui_suid = SWAB16(inode->i_uid); + ufs_inode->ui_u3.ui_sun.ui_uid = (__u32) ufs_inode->ui_u1.oldids.ui_suid; + } + if (inode->i_gid == 0 && inode->u.ufs_i.i_gid >= UFS_USEEFT) { + ufs_inode->ui_u3.ui_sun.ui_gid = SWAB32(inode->u.ufs_i.i_gid); + ufs_inode->ui_u1.oldids.ui_sgid = (__u16)ufs_inode->ui_u3.ui_sun.ui_gid; + } + else { + ufs_inode->ui_u1.oldids.ui_sgid = SWAB16(inode->i_gid); + ufs_inode->ui_u3.ui_sun.ui_gid = (__u32) ufs_inode->ui_u1.oldids.ui_sgid; + } + + ufs_inode->ui_size = SWAB64((u64)inode->i_size); + ufs_inode->ui_atime.tv_sec = SWAB32(inode->i_atime); + ufs_inode->ui_atime.tv_usec = SWAB32(0); + ufs_inode->ui_ctime.tv_sec = SWAB32(inode->i_ctime); + ufs_inode->ui_ctime.tv_usec = SWAB32(0); + ufs_inode->ui_mtime.tv_sec = SWAB32(inode->i_mtime); + ufs_inode->ui_mtime.tv_usec = SWAB32(0); + ufs_inode->ui_blocks = SWAB32(inode->i_blocks); + + ufs_inode->ui_flags = SWAB32(inode->u.ufs_i.i_flags); + ufs_inode->ui_gen = SWAB32(inode->u.ufs_i.i_gen); + ufs_inode->ui_u3.ui_sun.ui_shadow = SWAB32(inode->u.ufs_i.i_shadow); + ufs_inode->ui_u3.ui_sun.ui_oeftflag = SWAB32(inode->u.ufs_i.i_oeftflag); + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + ufs_inode->ui_u2.ui_addr.ui_db[0] = SWAB32(kdev_t_to_nr(inode->i_rdev)); + else if (inode->i_blocks) { + for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) + ufs_inode->ui_u2.ui_addr.ui_db[i] = inode->u.ufs_i.i_u1.i_data[i]; + } + else { + for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++) + ufs_inode->ui_u2.ui_symlink[i] = inode->u.ufs_i.i_u1.i_symlink[i]; + } + + if (!inode->i_nlink) + memset (ufs_inode, 0, sizeof(struct ufs_inode)); + + mark_buffer_dirty(bh, 1); + if (do_sync) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + + UFSD(("EXIT\n")) + return 0; +} + +void ufs_write_inode (struct inode * inode) +{ + ufs_update_inode (inode, 0); +} + +int ufs_sync_inode (struct inode *inode) +{ + return ufs_update_inode (inode, 1); +} + +void ufs_put_inode (struct inode * inode) +{ + UFSD(("ENTER & EXIT\n")) +} + +void ufs_delete_inode (struct inode * inode) +{ + /*inode->u.ufs_i.i_dtime = CURRENT_TIME;*/ + mark_inode_dirty(inode); + ufs_update_inode(inode, IS_SYNC(inode)); + inode->i_size = 0; + if (inode->i_blocks) + ufs_truncate (inode); + ufs_free_inode (inode); +} diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c new file mode 100644 index 000000000..309b31665 --- /dev/null +++ b/fs/ufs/namei.c @@ -0,0 +1,1147 @@ +/* + * linux/fs/ufs/namei.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/namei.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/namei.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 + */ + +#include <asm/uaccess.h> + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/locks.h> + +#include "swab.h" +#include "util.h" + +#undef UFS_NAMEI_DEBUG + +#ifdef UFS_NAMEI_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +/* + * define how far ahead to read directories while searching them. + */ +#define NAMEI_RA_CHUNKS 2 +#define NAMEI_RA_BLOCKS 4 +#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) +#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) + +/* + * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure. + */ +static int ufs_match (int len, const char * const name, + struct ufs_dir_entry * de, unsigned flags, unsigned swab) +{ + if (!de || !SWAB32(de->d_ino) || len > UFS_MAXNAMLEN) + return 0; + /* + * "" means "." ---> so paths like "/usr/lib//libc.a" work + */ + if (!len && ufs_namlen(de) == 1 && (de->d_name[0] == '.') && + (de->d_name[1] == '\0')) + return 1; + if (len != ufs_namlen(de)) + return 0; + return !memcmp(name, de->d_name, len); +} + +/* + * ufs_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 * ufs_find_entry (struct inode * dir, + const char * const name, int namelen, struct ufs_dir_entry ** res_dir) +{ + struct super_block * sb; + struct buffer_head * bh_use[NAMEI_RA_SIZE]; + struct buffer_head * bh_read[NAMEI_RA_SIZE]; + unsigned long offset; + int block, toread, i, err; + unsigned flags, swab; + + UFSD(("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen)) + + *res_dir = NULL; + if (!dir) + return NULL; + + sb = dir->i_sb; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; + + if (namelen > UFS_MAXNAMLEN) + return NULL; + + memset (bh_use, 0, sizeof (bh_use)); + toread = 0; + for (block = 0; block < NAMEI_RA_SIZE; ++block) { + struct buffer_head * bh; + + if ((block << sb->s_blocksize_bits) >= dir->i_size) + break; + bh = ufs_getfrag (dir, block, 0, &err); + bh_use[block] = bh; + if (bh && !buffer_uptodate(bh)) + bh_read[toread++] = bh; + } + + for (block = 0, offset = 0; offset < dir->i_size; block++) { + struct buffer_head * bh; + struct ufs_dir_entry * de; + char * dlimit; + + if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { + ll_rw_block (READ, toread, bh_read); + toread = 0; + } + bh = bh_use[block % NAMEI_RA_SIZE]; + if (!bh) { + ufs_error (sb, "ufs_find_entry", + "directory #%lu contains a hole at offset %lu", dir->i_ino, offset); + offset += sb->s_blocksize; + continue; + } + wait_on_buffer (bh); + if (!buffer_uptodate(bh)) { + /* + * read error: all bets are off + */ + break; + } + + de = (struct ufs_dir_entry *) bh->b_data; + dlimit = bh->b_data + sb->s_blocksize; + while ((char *) de < dlimit && offset < dir->i_size) { + if (!ufs_check_dir_entry ("ufs_find_entry", dir, de, bh, offset)) + goto failed; + if (SWAB32(de->d_ino) != 0 && ufs_match (namelen, name, de, flags, swab)) { + for (i = 0; i < NAMEI_RA_SIZE; ++i) { + if (bh_use[i] != bh) + brelse (bh_use[i]); + } + *res_dir = de; + UFSD(("EXIT\n")) + return bh; + } + offset += SWAB16(de->d_reclen); + de = (struct ufs_dir_entry *) + ((char *) de + SWAB16(de->d_reclen)); + } + + brelse (bh); + if (((block + NAMEI_RA_SIZE) << sb->s_blocksize_bits ) >= + dir->i_size) + bh = NULL; + else + bh = ufs_getfrag (dir, block + NAMEI_RA_SIZE, 0, &err); + bh_use[block % NAMEI_RA_SIZE] = bh; + if (bh && !buffer_uptodate(bh)) + bh_read[toread++] = bh; + } + +failed: + for (i = 0; i < NAMEI_RA_SIZE; ++i) brelse (bh_use[i]); + UFSD(("EXIT (FAILED)\n")) + return NULL; +} + +int ufs_lookup(struct inode * dir, struct dentry *dentry) +{ + struct super_block * sb; + struct inode * inode; + struct ufs_dir_entry * de; + struct buffer_head * bh; + unsigned swab; + + UFSD(("ENTER\n")) + + sb = dir->i_sb; + swab = sb->u.ufs_sb.s_swab; + + if (dentry->d_name.len > UFS_MAXNAMLEN) + return -ENAMETOOLONG; + + bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); + inode = NULL; + if (bh) { + unsigned long ino = SWAB32(de->d_ino); + brelse (bh); + inode = iget(sb, ino); + if (!inode) + return -EACCES; + } + d_add(dentry, inode); + UFSD(("EXIT\n")) + return 0; +} + +/* + * ufs_add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as ufs_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 * ufs_add_entry (struct inode * dir, + const char * name, int namelen, struct ufs_dir_entry ** res_dir, + int *err ) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + unsigned long offset; + unsigned fragoff; + unsigned short rec_len; + struct buffer_head * bh; + struct ufs_dir_entry * de, * de1; + unsigned flags, swab; + + UFSD(("ENTER, name %s, namelen %u\n", name, namelen)) + + *err = -EINVAL; + *res_dir = NULL; + if (!dir || !dir->i_nlink) + return NULL; + + sb = dir->i_sb; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + + if (namelen > UFS_MAXNAMLEN) + { + *err = -ENAMETOOLONG; + return NULL; + } + + if (!namelen) + return NULL; + /* + * Is this a busy deleted directory? Can't create new files if so + */ + if (dir->i_size == 0) + { + *err = -ENOENT; + return NULL; + } + bh = ufs_bread (dir, 0, 0, err); + if (!bh) + return NULL; + rec_len = UFS_DIR_REC_LEN(namelen); + offset = 0; + de = (struct ufs_dir_entry *) bh->b_data; + *err = -ENOSPC; + while (1) { + if ((char *)de >= SECTOR_SIZE + bh->b_data) { + fragoff = offset & ~uspi->s_fmask; + if (fragoff != 0 && fragoff != SECTOR_SIZE) + ufs_error (sb, "ufs_add_entry", "internal error" + " fragoff %u", fragoff); + if (!fragoff) { + brelse (bh); + bh = NULL; + bh = ufs_bread (dir, offset >> sb->s_blocksize_bits, 1, err); + } + if (!bh) + return NULL; + if (dir->i_size <= offset) { + if (dir->i_size == 0) { + *err = -ENOENT; + return NULL; + } + de = (struct ufs_dir_entry *) (bh->b_data + fragoff); + de->d_ino = SWAB32(0); + de->d_reclen = SWAB16(SECTOR_SIZE); + de->d_u.d_namlen = SWAB16(0); + dir->i_size = offset + SECTOR_SIZE; + mark_inode_dirty(dir); + } else { + de = (struct ufs_dir_entry *) bh->b_data; + } + } + if (!ufs_check_dir_entry ("ufs_add_entry", dir, de, bh, offset)) { + *err = -ENOENT; + brelse (bh); + return NULL; + } + if (SWAB32(de->d_ino) != 0 && ufs_match (namelen, name, de, flags, swab)) { + *err = -EEXIST; + brelse (bh); + return NULL; + } + if ((SWAB32(de->d_ino) == 0 && SWAB16(de->d_reclen) >= rec_len) || + (SWAB16(de->d_reclen) >= UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen)) + rec_len)) { + offset += SWAB16(de->d_reclen); + if (SWAB32(de->d_ino)) { + de1 = (struct ufs_dir_entry *) ((char *) de + + UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen))); + de1->d_reclen = SWAB16(SWAB16(de->d_reclen) - + UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen))); + de->d_reclen = SWAB16(UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen))); + de = de1; + } + de->d_ino = SWAB32(0); + de->d_u.d_namlen = SWAB16(namelen); + memcpy (de->d_name, name, namelen + 1); + /* + * XXX shouldn't update any times until successful + * completion of syscall, but too many callers depend + * on this. + * + * XXX similarly, too many callers depend on + * ufs_new_inode() setting the times, but error + * recovery deletes the inode, so the worst that can + * happen is that the times are slightly out of date + * and/or different from the directory change time. + */ + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + *res_dir = de; + *err = 0; + + UFSD(("EXIT\n")) + return bh; + } + offset += SWAB16(de->d_reclen); + de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); + } + brelse (bh); + UFSD(("EXIT (FAILED)\n")) + return NULL; +} + +/* + * ufs_delete_entry deletes a directory entry by merging it with the + * previous entry. + */ +static int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir, + struct buffer_head * bh ) + +{ + struct super_block * sb; + struct ufs_dir_entry * de, * pde; + unsigned i; + unsigned flags, swab; + + UFSD(("ENTER\n")) + + sb = inode->i_sb; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; + i = 0; + pde = NULL; + de = (struct ufs_dir_entry *) bh->b_data; + + UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(de->d_ino), + SWAB16(de->d_reclen), ufs_namlen(de), de->d_name)) + + while (i < bh->b_size) { + if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i)) + return -EIO; + if (de == dir) { + if (pde) + pde->d_reclen = + SWAB16(SWAB16(pde->d_reclen) + + SWAB16(dir->d_reclen)); + dir->d_ino = SWAB32(0); + UFSD(("EXIT\n")) + return 0; + } + i += SWAB16(de->d_reclen); + if (i == SECTOR_SIZE) pde = NULL; + else pde = de; + de = (struct ufs_dir_entry *) + ((char *) de + SWAB16(de->d_reclen)); + if (i == SECTOR_SIZE && SWAB16(de->d_reclen) == 0) + break; + } + UFSD(("EXIT\n")) + return -ENOENT; +} + +/* + * By the time this is called, we already have created + * the directory cache entry for the new file, but it + * is so far negative - it has no inode. + * + * If the create succeeds, we fill in the inode information + * with d_instantiate(). + */ +int ufs_create (struct inode * dir, struct dentry * dentry, int mode) +{ + struct super_block * sb; + struct inode * inode; + struct buffer_head * bh; + struct ufs_dir_entry * de; + int err = -EIO; + unsigned swab; + + sb = dir->i_sb; + swab = sb->u.ufs_sb.s_swab; + + /* + * N.B. Several error exits in ufs_new_inode don't set err. + */ + UFSD(("ENTER\n")) + + inode = ufs_new_inode (dir, mode, &err); + if (!inode) + return err; + inode->i_op = &ufs_file_inode_operations; + inode->i_mode = mode; + mark_inode_dirty(inode); + bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) { + inode->i_nlink--; + mark_inode_dirty(inode); + iput (inode); + return err; + } + de->d_ino = SWAB32(inode->i_ino); + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + d_instantiate(dentry, inode); + + UFSD(("EXIT\n")) + + return 0; +} + +int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) +{ + struct super_block * sb; + struct inode * inode; + struct buffer_head * bh; + struct ufs_dir_entry * de; + int err = -EIO; + unsigned swab; + + sb = dir->i_sb; + swab = sb->u.ufs_sb.s_swab; + + err = -ENAMETOOLONG; + if (dentry->d_name.len > UFS_MAXNAMLEN) + goto out; + + inode = ufs_new_inode (dir, mode, &err); + if (!inode) + goto out; + + inode->i_uid = current->fsuid; + inode->i_mode = mode; + inode->i_op = NULL; + if (S_ISREG(inode->i_mode)) + inode->i_op = &ufs_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &ufs_dir_inode_operations; + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + } + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &ufs_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); + mark_inode_dirty(inode); + bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) + goto out_no_entry; + de->d_ino = SWAB32(inode->i_ino); + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + d_instantiate(dentry, inode); + brelse(bh); + err = 0; +out: + return err; + +out_no_entry: + inode->i_nlink--; + mark_inode_dirty(inode); + iput(inode); + goto out; +} + +int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) +{ + struct super_block * sb; + struct inode * inode; + struct buffer_head * bh, * dir_block; + struct ufs_dir_entry * de; + int err; + unsigned swab; + + sb = dir->i_sb; + swab = sb->u.ufs_sb.s_swab; + + err = -ENAMETOOLONG; + if (dentry->d_name.len > UFS_MAXNAMLEN) + goto out; + + err = -EMLINK; + if (dir->i_nlink >= UFS_LINK_MAX) + goto out; + err = -EIO; + inode = ufs_new_inode (dir, S_IFDIR, &err); + if (!inode) + goto out; + + inode->i_op = &ufs_dir_inode_operations; + inode->i_size = SECTOR_SIZE; + dir_block = ufs_bread (inode, 0, 1, &err); + if (!dir_block) { + inode->i_nlink--; /* is this nlink == 0? */ + mark_inode_dirty(inode); + iput (inode); + return err; + } + inode->i_blocks = sb->s_blocksize / SECTOR_SIZE; + de = (struct ufs_dir_entry *) dir_block->b_data; + de->d_ino = SWAB32(inode->i_ino); + de->d_u.d_namlen = SWAB16(1); + de->d_reclen = SWAB16(UFS_DIR_REC_LEN(1)); + strcpy (de->d_name, "."); + de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); + de->d_ino = SWAB32(dir->i_ino); + de->d_reclen = SWAB16(SECTOR_SIZE - UFS_DIR_REC_LEN(1)); + de->d_u.d_namlen = SWAB16(2); + strcpy (de->d_name, ".."); + inode->i_nlink = 2; + mark_buffer_dirty(dir_block, 1); + brelse (dir_block); + inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + mark_inode_dirty(inode); + bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) + goto out_no_entry; + de->d_ino = SWAB32(inode->i_ino); + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + dir->i_nlink++; + mark_inode_dirty(dir); + d_instantiate(dentry, inode); + brelse (bh); + err = 0; +out: + return err; + +out_no_entry: + inode->i_nlink = 0; + mark_inode_dirty(inode); + iput (inode); + goto out; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +static int ufs_empty_dir (struct inode * inode) +{ + struct super_block * sb; + unsigned long offset; + struct buffer_head * bh; + struct ufs_dir_entry * de, * de1; + int err; + unsigned swab; + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + + if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) || + !(bh = ufs_bread (inode, 0, 0, &err))) { + ufs_warning (inode->i_sb, "empty_dir", + "bad directory (dir #%lu) - no data block", + inode->i_ino); + return 1; + } + de = (struct ufs_dir_entry *) bh->b_data; + de1 = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); + if (SWAB32(de->d_ino) != inode->i_ino || !SWAB32(de1->d_ino) || + strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) { + ufs_warning (inode->i_sb, "empty_dir", + "bad directory (dir #%lu) - no `.' or `..'", + inode->i_ino); + return 1; + } + offset = SWAB16(de->d_reclen) + SWAB16(de1->d_reclen); + de = (struct ufs_dir_entry *) ((char *) de1 + SWAB16(de1->d_reclen)); + while (offset < inode->i_size ) { + if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { + brelse (bh); + bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err); + if (!bh) { + ufs_error (sb, "empty_dir", + "directory #%lu contains a hole at offset %lu", + inode->i_ino, offset); + offset += sb->s_blocksize; + continue; + } + de = (struct ufs_dir_entry *) bh->b_data; + } + if (!ufs_check_dir_entry ("empty_dir", inode, de, bh, offset)) { + brelse (bh); + return 1; + } + if (SWAB32(de->d_ino)) { + brelse (bh); + return 0; + } + offset += SWAB16(de->d_reclen); + de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); + } + brelse (bh); + return 1; +} + +int ufs_rmdir (struct inode * dir, struct dentry *dentry) +{ + struct super_block *sb; + int retval; + struct inode * inode; + struct buffer_head * bh; + struct ufs_dir_entry * de; + unsigned swab; + + sb = dir->i_sb; + swab = sb->u.ufs_sb.s_swab; + + UFSD(("ENTER\n")) + + retval = -ENAMETOOLONG; + if (dentry->d_name.len > UFS_MAXNAMLEN) + goto out; + + retval = -ENOENT; + bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); + if (!bh) + goto end_rmdir; + + inode = dentry->d_inode; + if (inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize (inode, -1); + + retval = -EPERM; + if ((dir->i_mode & S_ISVTX) && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid && !fsuser()) + goto end_rmdir; + if (inode == dir) /* we may not delete ".", but "../dir" is ok */ + goto end_rmdir; + + retval = -ENOTDIR; + if (!S_ISDIR(inode->i_mode)) + goto end_rmdir; + + retval = -EIO; + if (inode->i_dev != dir->i_dev) + goto end_rmdir; + if (SWAB32(de->d_ino) != inode->i_ino) + goto end_rmdir; + + down(&inode->i_sem); + /* + * Prune any child dentries so that this dentry becomes negative. + */ + if (dentry->d_count > 1) { + ufs_warning (sb, "ufs_rmdir", "d_count=%d, pruning\n", dentry->d_count); + shrink_dcache_parent(dentry); + } + if (!ufs_empty_dir (inode)) + retval = -ENOTEMPTY; + else if (SWAB32(de->d_ino) != inode->i_ino) + retval = -ENOENT; + else { + if (dentry->d_count > 1) { + /* + * Are we deleting the last instance of a busy directory? + * Better clean up if so. + * + * Make directory empty (it will be truncated when finally + * dereferenced). This also inhibits ufs_add_entry. + */ + inode->i_size = 0; + } + retval = ufs_delete_entry (dir, de, bh); + dir->i_version = ++event; + } + up(&inode->i_sem); + if (retval) + goto end_rmdir; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + if (inode->i_nlink != 2) + ufs_warning (inode->i_sb, "ufs_rmdir", + "empty directory has nlink!=2 (%d)", + inode->i_nlink); + inode->i_version = ++event; + inode->i_nlink = 0; + mark_inode_dirty(inode); + dir->i_nlink--; + inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(dir); + d_delete(dentry); + +end_rmdir: + brelse (bh); +out: + UFSD(("EXIT\n")) + + return retval; +} + +int ufs_unlink(struct inode * dir, struct dentry *dentry) +{ + struct super_block * sb; + int retval; + struct inode * inode; + struct buffer_head * bh; + struct ufs_dir_entry * de; + unsigned flags, swab; + + sb = dir->i_sb; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; + + retval = -ENAMETOOLONG; + if (dentry->d_name.len > UFS_MAXNAMLEN) + goto out; + + retval = -ENOENT; + bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); + UFSD(("de: ino %u, reclen %u, namelen %u, name %s\n", SWAB32(de->d_ino), + SWAB16(de->d_reclen), ufs_namlen(de), de->d_name)) + if (!bh) + goto end_unlink; + + inode = dentry->d_inode; + if (inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize (inode, -1); + + retval = -EPERM; + if (S_ISDIR(inode->i_mode)) + goto end_unlink; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + goto end_unlink; + if ((dir->i_mode & S_ISVTX) && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid && !fsuser()) + goto end_unlink; + + retval = -EIO; + if (SWAB32(de->d_ino) != inode->i_ino) + goto end_unlink; + + if (!inode->i_nlink) { + ufs_warning (inode->i_sb, "ufs_unlink", + "Deleting nonexistent file (%lu), %d", + inode->i_ino, inode->i_nlink); + inode->i_nlink = 1; + } + retval = ufs_delete_entry (dir, de, bh); + if (retval) + goto end_unlink; + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(dir); + inode->i_nlink--; + mark_inode_dirty(inode); + inode->i_ctime = dir->i_ctime; + retval = 0; + d_delete(dentry); /* This also frees the inode */ + +end_unlink: + brelse (bh); +out: + return retval; +} + + +int ufs_link (struct dentry * old_dentry, struct inode * dir, + struct dentry *dentry) +{ + struct super_block * sb; + struct inode *inode = old_dentry->d_inode; + struct ufs_dir_entry * de; + struct buffer_head * bh; + int err; + unsigned swab; + + inode = old_dentry->d_inode; + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + + if (S_ISDIR(inode->i_mode)) + return -EPERM; + + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return -EPERM; + + if (inode->i_nlink >= UFS_LINK_MAX) + return -EMLINK; + + bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) + return err; + + de->d_ino = SWAB32(inode->i_ino); + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + inode->i_nlink++; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + inode->i_count++; + d_instantiate(dentry, inode); + return 0; +} + +/* + * Create symbolic link. We use only slow symlinks at this time. + */ +int ufs_symlink (struct inode * dir, struct dentry * dentry, + const char * symname) +{ + struct super_block * sb; + struct ufs_dir_entry * de; + struct inode * inode; + struct buffer_head * bh, * name_block; + char * link; + unsigned i, l; + int err; + char c; + unsigned swab; + + UFSD(("ENTER\n")) + + sb = dir->i_sb; + swab = sb->u.ufs_sb.s_swab; + bh = name_block = NULL; + err = -EIO; + + if (!(inode = ufs_new_inode (dir, S_IFLNK, &err))) { + return err; + } + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &ufs_symlink_inode_operations; + for (l = 0; l < sb->s_blocksize - 1 && symname [l]; l++); + + /***if (l >= sizeof (inode->u.ufs_i.i_data)) {***/ + if (1) { + /* slow symlink */ + name_block = ufs_bread (inode, 0, 1, &err); + if (!name_block) { + inode->i_nlink--; + mark_inode_dirty(inode); + iput (inode); + return err; + } + link = name_block->b_data; + + } else { + /* fast symlink */ + link = (char *) inode->u.ufs_i.i_u1.i_data; + } + i = 0; + while (i < sb->s_blocksize - 1 && (c = *(symname++))) + link[i++] = c; + link[i] = 0; + if (name_block) { + mark_buffer_dirty(name_block, 1); + brelse (name_block); + } + inode->i_size = i; + mark_inode_dirty(inode); + + bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) + goto out_no_entry; + de->d_ino = SWAB32(inode->i_ino); + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + d_instantiate(dentry, inode); + err = 0; +out: + return err; + +out_no_entry: + inode->i_nlink--; + mark_inode_dirty(inode); + iput (inode); + goto out; +} + + +#define PARENT_INO(buffer) \ + ((struct ufs_dir_entry *) ((char *) buffer + \ + SWAB16(((struct ufs_dir_entry *) buffer)->d_reclen)))->d_ino +/* + * 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_ufs_rename (struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry ) +{ + struct super_block * sb; + struct inode * old_inode, * new_inode; + struct buffer_head * old_bh, * new_bh, * dir_bh; + struct ufs_dir_entry * old_de, * new_de; + int retval; + unsigned flags, swab; + + sb = old_dir->i_sb; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; + + UFSD(("ENTER\n")) + + old_inode = new_inode = NULL; + old_bh = new_bh = dir_bh = NULL; + new_de = NULL; + retval = -ENAMETOOLONG; + if (old_dentry->d_name.len > UFS_MAXNAMLEN) + goto end_rename; + + UFSD(("name %s, len %u\n", old_dentry->d_name.name, old_dentry->d_name.len)) + old_bh = ufs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); + UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(old_de->d_ino), + SWAB16(old_de->d_reclen), ufs_namlen(old_de), old_de->d_name)) + + retval = -ENOENT; + if (!old_bh) + goto end_rename; + old_inode = old_dentry->d_inode; + + 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; + if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode)) + goto end_rename; + + new_inode = new_dentry->d_inode; + UFSD(("name %s, len %u\n", new_dentry->d_name.name, new_dentry->d_name.len)) + new_bh = ufs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); + if (new_bh) { + if (!new_inode) { + brelse (new_bh); + new_bh = NULL; + } else { + if (new_inode->i_sb->dq_op) + new_inode->i_sb->dq_op->initialize (new_inode, -1); + } + } + retval = 0; + if (new_inode == old_inode) + goto end_rename; + if (new_inode && S_ISDIR(new_inode->i_mode)) { + retval = -EISDIR; + if (!S_ISDIR(old_inode->i_mode)) + goto end_rename; + retval = -EINVAL; + if (is_subdir(new_dentry, old_dentry)) + goto end_rename; + retval = -ENOTEMPTY; + if (!ufs_empty_dir (new_inode)) + goto end_rename; + retval = -EBUSY; + if (new_dentry->d_count > 1) + goto end_rename; + } + retval = -EPERM; + if (new_inode) { + if ((new_dir->i_mode & S_ISVTX) && + current->fsuid != new_inode->i_uid && + current->fsuid != new_dir->i_uid && !fsuser()) + goto end_rename; + if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode)) + goto end_rename; + } + if (S_ISDIR(old_inode->i_mode)) { + retval = -ENOTDIR; + if (new_inode && !S_ISDIR(new_inode->i_mode)) + goto end_rename; + retval = -EINVAL; + if (is_subdir(new_dentry, old_dentry)) + goto end_rename; + dir_bh = ufs_bread (old_inode, 0, 0, &retval); + if (!dir_bh) + goto end_rename; + if (SWAB32(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino) + goto end_rename; + retval = -EMLINK; + if (!new_inode && new_dir->i_nlink >= UFS_LINK_MAX) + goto end_rename; + } + + if (!new_bh) + new_bh = ufs_add_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de, + &retval); + if (!new_bh) + goto end_rename; + new_dir->i_version = ++event; + + /* + * ok, that's it + */ + new_de->d_ino = SWAB32(old_inode->i_ino); + ufs_delete_entry (old_dir, old_de, old_bh); + + old_dir->i_version = ++event; + if (new_inode) { + new_inode->i_nlink--; + new_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(new_inode); + } + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(old_dir); + if (dir_bh) { + PARENT_INO(dir_bh->b_data) = SWAB32(new_dir->i_ino); + mark_buffer_dirty(dir_bh, 1); + old_dir->i_nlink--; + mark_inode_dirty(old_dir); + if (new_inode) { + new_inode->i_nlink--; + mark_inode_dirty(new_inode); + } else { + new_dir->i_nlink++; + mark_inode_dirty(new_dir); + } + } + mark_buffer_dirty(old_bh, 1); + if (IS_SYNC(old_dir)) { + ll_rw_block (WRITE, 1, &old_bh); + wait_on_buffer (old_bh); + } + + mark_buffer_dirty(new_bh, 1); + if (IS_SYNC(new_dir)) { + ll_rw_block (WRITE, 1, &new_bh); + wait_on_buffer (new_bh); + } + + /* Update the dcache */ + d_move(old_dentry, new_dentry); + retval = 0; +end_rename: + brelse (dir_bh); + brelse (old_bh); + brelse (new_bh); + + UFSD(("EXIT\n")) + + 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. + * + * In the second extended file system, we use a lock flag stored in the memory + * super-block. This way, we really lock other renames only if they occur + * on the same file system + */ +int ufs_rename (struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry ) +{ + int result; + + UFSD(("ENTER\n")) + + while (old_dir->i_sb->u.ufs_sb.s_rename_lock) + sleep_on (&old_dir->i_sb->u.ufs_sb.s_rename_wait); + old_dir->i_sb->u.ufs_sb.s_rename_lock = 1; + result = do_ufs_rename (old_dir, old_dentry, new_dir, new_dentry); + old_dir->i_sb->u.ufs_sb.s_rename_lock = 0; + wake_up (&old_dir->i_sb->u.ufs_sb.s_rename_wait); + + UFSD(("EXIT\n")) + + return result; +} + diff --git a/fs/ufs/super.c b/fs/ufs/super.c new file mode 100644 index 000000000..b68af5771 --- /dev/null +++ b/fs/ufs/super.c @@ -0,0 +1,690 @@ +/* + * linux/fs/ufs/super.c + * + * Copyright (C) 1996 + * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) + * Laboratory for Computer Science Research Computing Facility + * Rutgers, The State University of New Jersey + * + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * + * Kernel module support added on 96/04/26 by + * Stefan Reinauer <stepan@home.culture.mipt.ru> + * + * Module usage counts added on 96/04/29 by + * Gertjan van Wingerde <gertjan@cs.vu.nl> + * + * Clean swab support on 19970406 by + * Francois-Rene Rideau <rideau@ens.fr> + * + * 4.4BSD (FreeBSD) support added on February 1st 1998 by + * Niels Kristian Bech Jensen <nkbj@image.dk> partially based + * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. + * + * NeXTstep support added on February 5th 1998 by + * Niels Kristian Bech Jensen <nkbj@image.dk>. + * + * write support Daniel Pirkl <daniel.pirkl@email.cz> 1998 + * + + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/locks.h> +#include <asm/uaccess.h> +#include <linux/malloc.h> + +#include "swab.h" +#include "util.h" + + +#undef UFS_SUPER_DEBUG +#undef UFS_SUPER_DEBUG_MORE + +#ifdef UFS_SUPER_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +#ifdef UFS_SUPER_DEBUG_MORE +/* + * Print contents of ufs_super_block, useful for debuging + */ +void ufs_print_super_stuff(struct ufs_super_block_first * usb1, + struct ufs_super_block_second * usb2, + struct ufs_super_block_third * usb3, unsigned swab) +{ + printk("\nufs_print_super_stuff\n"); + printk("size of usb: %lu\n", sizeof(struct ufs_super_block)); + printk(" magic: 0x%x\n", SWAB32(usb3->fs_magic)); + printk(" sblkno: %u\n", SWAB32(usb1->fs_sblkno)); + printk(" cblkno: %u\n", SWAB32(usb1->fs_cblkno)); + printk(" iblkno: %u\n", SWAB32(usb1->fs_iblkno)); + printk(" dblkno: %u\n", SWAB32(usb1->fs_dblkno)); + printk(" cgoffset: %u\n", SWAB32(usb1->fs_cgoffset)); + printk(" ~cgmask: 0x%x\n", ~SWAB32(usb1->fs_cgmask)); + printk(" size: %u\n", SWAB32(usb1->fs_size)); + printk(" dsize: %u\n", SWAB32(usb1->fs_dsize)); + printk(" ncg: %u\n", SWAB32(usb1->fs_ncg)); + printk(" bsize: %u\n", SWAB32(usb1->fs_bsize)); + printk(" fsize: %u\n", SWAB32(usb1->fs_fsize)); + printk(" frag: %u\n", SWAB32(usb1->fs_frag)); + printk(" fragshift: %u\n", SWAB32(usb1->fs_fragshift)); + printk(" ~fmask: %u\n", ~SWAB32(usb1->fs_fmask)); + printk(" fshift: %u\n", SWAB32(usb1->fs_fshift)); + printk(" sbsize: %u\n", SWAB32(usb1->fs_sbsize)); + printk(" spc: %u\n", SWAB32(usb1->fs_spc)); + printk(" cpg: %u\n", SWAB32(usb1->fs_cpg)); + printk(" ipg: %u\n", SWAB32(usb1->fs_ipg)); + printk(" fpg: %u\n", SWAB32(usb1->fs_fpg)); + printk(" csaddr: %u\n", SWAB32(usb1->fs_csaddr)); + printk(" cssize: %u\n", SWAB32(usb1->fs_cssize)); + printk(" cgsize: %u\n", SWAB32(usb1->fs_cgsize)); + printk(" fstodb: %u\n", SWAB32(usb1->fs_fsbtodb)); + printk(" postblformat: %u\n", SWAB32(usb3->fs_postblformat)); + printk(" nrpos: %u\n", SWAB32(usb3->fs_nrpos)); + printk(" ndir %u\n", SWAB32(usb1->fs_cstotal.cs_ndir)); + printk(" nifree %u\n", SWAB32(usb1->fs_cstotal.cs_nifree)); + printk(" nbfree %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree)); + printk(" nffree %u\n", SWAB32(usb1->fs_cstotal.cs_nffree)); +} + + +/* + * Print contents of ufs_cylinder_group, useful for debuging + */ +void ufs_print_cylinder_stuff(struct ufs_cylinder_group *cg, unsigned swab) +{ + printk("\nufs_print_cylinder_stuff\n"); + printk("size of ucg: %lu\n", sizeof(struct ufs_cylinder_group)); + printk(" magic: %x\n", SWAB32(cg->cg_magic)); + printk(" time: %u\n", SWAB32(cg->cg_time)); + printk(" cgx: %u\n", SWAB32(cg->cg_cgx)); + printk(" ncyl: %u\n", SWAB16(cg->cg_ncyl)); + printk(" niblk: %u\n", SWAB16(cg->cg_niblk)); + printk(" ndblk: %u\n", SWAB32(cg->cg_ndblk)); + printk(" cs_ndir: %u\n", SWAB32(cg->cg_cs.cs_ndir)); + printk(" cs_nbfree: %u\n", SWAB32(cg->cg_cs.cs_nbfree)); + printk(" cs_nifree: %u\n", SWAB32(cg->cg_cs.cs_nifree)); + printk(" cs_nffree: %u\n", SWAB32(cg->cg_cs.cs_nffree)); + printk(" rotor: %u\n", SWAB32(cg->cg_rotor)); + printk(" frotor: %u\n", SWAB32(cg->cg_frotor)); + printk(" irotor: %u\n", SWAB32(cg->cg_irotor)); + printk(" frsum: %u, %u, %u, %u, %u, %u, %u, %u\n", + SWAB32(cg->cg_frsum[0]), SWAB32(cg->cg_frsum[1]), + SWAB32(cg->cg_frsum[2]), SWAB32(cg->cg_frsum[3]), + SWAB32(cg->cg_frsum[4]), SWAB32(cg->cg_frsum[5]), + SWAB32(cg->cg_frsum[6]), SWAB32(cg->cg_frsum[7])); + printk(" btotoff: %u\n", SWAB32(cg->cg_btotoff)); + printk(" boff: %u\n", SWAB32(cg->cg_boff)); + printk(" iuseoff: %u\n", SWAB32(cg->cg_iusedoff)); + printk(" freeoff: %u\n", SWAB32(cg->cg_freeoff)); + printk(" nextfreeoff: %u\n", SWAB32(cg->cg_nextfreeoff)); +} +#endif /* UFS_SUPER_DEBUG_MORE */ + +/* + * Called while file system is mounted, read super block + * and create important imtermal structures. + */ +struct super_block * ufs_read_super ( + struct super_block * sb, + void * data, + int silent) +{ + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_super_block_second * usb2; + struct ufs_super_block_third * usb3; + struct ufs_buffer_head * ubh; + unsigned char * base, * space; + unsigned size, blks, i; + unsigned block_size, super_block_size; + unsigned flags, swab; + s64 tmp; + static unsigned offsets[] = {0, 96, 160}; /* different superblock locations */ + + UFSD(("ENTER\n")) + + uspi = NULL; + ubh = NULL; + base = space = NULL; + sb->u.ufs_sb.s_ucg = NULL; + flags = 0; + swab = 0; + + /* sb->s_dev and sb->s_flags are set by our caller + * data is the mystery argument to sys_mount() + * + * Our caller also sets s_dev, s_covered, s_rd_only, s_dirt, + * and s_type when we return. + */ + + MOD_INC_USE_COUNT; + lock_super (sb); + + sb->u.ufs_sb.s_uspi = uspi = + kmalloc (sizeof(struct ufs_sb_private_info), GFP_KERNEL); + if (!uspi) + goto failed; + + block_size = BLOCK_SIZE; + super_block_size = BLOCK_SIZE * 2; + + uspi->s_fsize = block_size; + uspi->s_fmask = ~(BLOCK_SIZE - 1); + uspi->s_fshift = BLOCK_SIZE_BITS; + uspi->s_sbsize = super_block_size; + i = 0; + uspi->s_sbbase = offsets[i]; + +again: + set_blocksize (sb->s_dev, block_size); + + /* + * read ufs super block from device + */ + ubh = ubh_bread2 (sb->s_dev, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size); + if (!ubh) + goto failed; + + usb1 = ubh_get_usb_first(USPI_UBH); + usb2 = ubh_get_usb_second(USPI_UBH); + usb3 = ubh_get_usb_third(USPI_UBH); + + /* + * Check ufs magic number + */ + if (usb3->fs_magic != UFS_MAGIC) { + switch (le32_to_cpup(&usb3->fs_magic)) { + case UFS_MAGIC: + swab = UFS_LITTLE_ENDIAN; break; + case UFS_CIGAM: + swab = UFS_BIG_ENDIAN; break; + default: + /* + * Try another super block location + */ + if (++i < sizeof(offsets)/sizeof(unsigned)) { + ubh_brelse2(ubh); + ubh = NULL; + uspi->s_sbbase = offsets[i]; + goto again; + } + else { + printk("ufs_read_super: super block loacation not in { 0, 96, 160} or bad magic number\n"); + goto failed; + } + } + } + + /* + * Check block and fragment sizes + */ + uspi->s_bsize = SWAB32(usb1->fs_bsize); + uspi->s_fsize = SWAB32(usb1->fs_fsize); + uspi->s_sbsize = SWAB32(usb1->fs_sbsize); + + if (uspi->s_bsize != 4096 && uspi->s_bsize != 8192) { + printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n", uspi->s_bsize); + goto failed; + } + if (uspi->s_fsize != 512 && uspi->s_fsize != 1024) { + printk("ufs_read_super: fs_fsize %u != {512, 1024}\n", uspi->s_fsize); + goto failed; + } + + /* + * Block size is not 1024, set block_size to 512, + * free buffers and read it again + */ + if (uspi->s_fsize != block_size || uspi->s_sbsize != super_block_size) { + ubh_brelse2(ubh); + ubh = NULL; + uspi->s_fmask = SWAB32(usb1->fs_fmask); + uspi->s_fshift = SWAB32(usb1->fs_fshift); + goto again; + } + +#ifdef UFS_SUPER_DEBUG_MORE + ufs_print_super_stuff (usb1, usb2, usb3, swab); +#endif + /* + * Check file system type + */ + flags |= UFS_VANILLA; + /* XXX more consistency check */ + UFSD(("ufs_read_super: maxsymlinklen 0x%8.8x\n", usb3->fs_u.fs_44.fs_maxsymlinklen)) + if (usb3->fs_u.fs_44.fs_maxsymlinklen >= 0) { + if (usb3->fs_u.fs_44.fs_inodefmt >= UFS_44INODEFMT) { + UFSD(("44BSD\n")) + flags |= UFS_44BSD; + sb->s_flags |= MS_RDONLY; + } else { + UFSD(("OLD\n")) + sb->s_flags |= UFS_OLD; /* 4.2BSD */ + } + } else if (uspi->s_sbbase > 0) { + UFSD(("NEXT\n")) + flags |= UFS_NEXT; + sb->s_flags |= MS_RDONLY; + } else { + UFSD(("SUN\n")) + flags |= UFS_SUN; + } + + /* + * Check, if file system was correctly unmounted. + * If not, make it read only. + */ + if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) || + ((flags & UFS_ST_MASK) == UFS_ST_OLD) || + ((flags & UFS_ST_MASK) == UFS_ST_NEXT) || + (((flags & UFS_ST_MASK) == UFS_ST_SUN) && + ufs_state(usb3) == UFS_FSOK - usb1->fs_time)) { + switch(usb1->fs_clean) { + case UFS_FSCLEAN: + UFSD(("fs is clean\n")) + break; + case UFS_FSSTABLE: + UFSD(("fs is stable\n")) + break; + case UFS_FSACTIVE: + printk("ufs_read_super: fs is active\n"); + sb->s_flags |= MS_RDONLY; + break; + case UFS_FSBAD: + printk("ufs_read_super: fs is bad\n"); + sb->s_flags |= MS_RDONLY; + break; + default: + printk("ufs_read_super: can't grok fs_clean 0x%x\n", + usb1->fs_clean); + sb->s_flags |= MS_RDONLY; + break; + } + } else { + printk("ufs_read_super: fs needs fsck\n"); + sb->s_flags |= MS_RDONLY; + } + + sb->s_flags &= ~MS_RDONLY; + /* + * Read ufs_super_block into internal data structures + */ + sb->s_blocksize = SWAB32(usb1->fs_fsize); + sb->s_blocksize_bits = SWAB32(usb1->fs_fshift); + sb->s_op = &ufs_super_ops; + sb->dq_op = 0; /* XXX */ + sb->s_magic = SWAB32(usb3->fs_magic); + + uspi->s_sblkno = SWAB32(usb1->fs_sblkno); + uspi->s_cblkno = SWAB32(usb1->fs_cblkno); + uspi->s_iblkno = SWAB32(usb1->fs_iblkno); + uspi->s_dblkno = SWAB32(usb1->fs_dblkno); + uspi->s_cgoffset = SWAB32(usb1->fs_cgoffset); + uspi->s_cgmask = SWAB32(usb1->fs_cgmask); + uspi->s_size = SWAB32(usb1->fs_size); + uspi->s_dsize = SWAB32(usb1->fs_dsize); + uspi->s_ncg = SWAB32(usb1->fs_ncg); + /* s_bsize already set */ + /* s_fsize already set */ + uspi->s_fpb = SWAB32(usb1->fs_frag); + uspi->s_minfree = SWAB32(usb1->fs_minfree); + uspi->s_bmask = SWAB32(usb1->fs_bmask); + uspi->s_fmask = SWAB32(usb1->fs_fmask); + uspi->s_bshift = SWAB32(usb1->fs_bshift); + uspi->s_fshift = SWAB32(usb1->fs_fshift); + uspi->s_fpbshift = SWAB32(usb1->fs_fragshift); + uspi->s_fsbtodb = SWAB32(usb1->fs_fsbtodb); + /* s_sbsize already set */ + uspi->s_csmask = SWAB32(usb1->fs_csmask); + uspi->s_csshift = SWAB32(usb1->fs_csshift); + uspi->s_nindir = SWAB32(usb1->fs_nindir); + uspi->s_inopb = SWAB32(usb1->fs_inopb); + uspi->s_nspf = SWAB32(usb1->fs_nspf); + uspi->s_npsect = SWAB32(usb1->fs_npsect); + uspi->s_interleave = SWAB32(usb1->fs_interleave); + uspi->s_trackskew = SWAB32(usb1->fs_trackskew); + uspi->s_csaddr = SWAB32(usb1->fs_csaddr); + uspi->s_cssize = SWAB32(usb1->fs_cssize); + uspi->s_cgsize = SWAB32(usb1->fs_cgsize); + uspi->s_ntrak = SWAB32(usb1->fs_ntrak); + uspi->s_nsect = SWAB32(usb1->fs_nsect); + uspi->s_spc = SWAB32(usb1->fs_spc); + uspi->s_ipg = SWAB32(usb1->fs_ipg); + uspi->s_fpg = SWAB32(usb1->fs_fpg); + uspi->s_cpc = SWAB32(usb2->fs_cpc); + ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1]; + uspi->s_qbmask = SWAB64(tmp); + ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1]; + uspi->s_qfmask = SWAB64(tmp); + uspi->s_postblformat = SWAB32(usb3->fs_postblformat); + uspi->s_nrpos = SWAB32(usb3->fs_nrpos); + uspi->s_postbloff = SWAB32(usb3->fs_postbloff); + uspi->s_rotbloff = SWAB32(usb3->fs_rotbloff); + + /* + * Compute another fraquently used values + */ + uspi->s_fpbmask = uspi->s_fpb - 1; + uspi->s_apbshift = uspi->s_bshift - 2; + uspi->s_2apbshift = uspi->s_apbshift * 2; + uspi->s_3apbshift = uspi->s_apbshift * 3; + uspi->s_apb = 1 << uspi->s_apbshift; + uspi->s_2apb = 1 << uspi->s_2apbshift; + uspi->s_3apb = 1 << uspi->s_3apbshift; + uspi->s_apbmask = uspi->s_apb - 1; + uspi->s_nspfshift = uspi->s_fshift - SECTOR_BITS; + uspi->s_nspb = uspi->s_nspf << uspi->s_fpbshift; + uspi->s_inopf = uspi->s_inopb >> uspi->s_fpbshift; + + sb->u.ufs_sb.s_flags = flags; + sb->u.ufs_sb.s_swab = swab; + sb->u.ufs_sb.s_rename_lock = 0; + sb->u.ufs_sb.s_rename_wait = NULL; + + sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL); + + /* + * Read cs structures from (usually) first data block + * on the device. + */ + size = uspi->s_cssize; + blks = howmany(size, uspi->s_fsize); + base = space = kmalloc(size, GFP_KERNEL); + if (!base) + goto failed; + for (i = 0; i < blks; i += uspi->s_fpb) { + size = uspi->s_bsize; + if (i + uspi->s_fpb > blks) + size = (blks - i) * uspi->s_fsize; + ubh = ubh_bread(sb->s_dev, uspi->s_csaddr + i, size); + if (!ubh) + goto failed; + ubh_ubhcpymem (space, ubh, size); + sb->u.ufs_sb.s_csp[ufs_fragstoblks(i)] = (struct ufs_csum *)space; + space += size; + ubh_brelse (ubh); + ubh = NULL; + } + + /* + * Read cylinder group (we read only first fragment from block + * at this time) and prepare internal data structures for cg caching. + */ + if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL))) + goto failed; + for (i = 0; i < uspi->s_ncg; i++) + sb->u.ufs_sb.s_ucg[i] = NULL; + for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { + sb->u.ufs_sb.s_ucpi[i] = NULL; + sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY; + } + for (i = 0; i < uspi->s_ncg; i++) { + UFSD(("read cg %u\n", i)) + if (!(sb->u.ufs_sb.s_ucg[i] = bread (sb->s_dev, ufs_cgcmin(i), sb->s_blocksize))) + goto failed; + if (!ufs_cg_chkmagic ((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data)) + goto failed; +#ifdef UFS_SUPER_DEBUG_MORE + ufs_print_cylinder_stuff((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data, swab); +#endif + } + for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { + if (!(sb->u.ufs_sb.s_ucpi[i] = kmalloc (sizeof(struct ufs_cg_private_info), GFP_KERNEL))) + goto failed; + sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY; + } + sb->u.ufs_sb.s_cg_loaded = 0; + + unlock_super(sb); + UFSD(("EXIT\n")) + return(sb); + +failed: + if (ubh) ubh_brelse2 (ubh); + if (uspi) kfree (uspi); + if (base) kfree (base); + + if (sb->u.ufs_sb.s_ucg) { + for (i = 0; i < uspi->s_ncg; i++) + if (sb->u.ufs_sb.s_ucg[i]) brelse (sb->u.ufs_sb.s_ucg[i]); + kfree (sb->u.ufs_sb.s_ucg); + for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) + if (sb->u.ufs_sb.s_ucpi[i]) kfree (sb->u.ufs_sb.s_ucpi[i]); + } + sb->s_dev = 0; + unlock_super (sb); + MOD_DEC_USE_COUNT; + UFSD(("EXIT (FAILED)\n")) + return(NULL); +} + +/* + * Put super block, release internal structures + */ +void ufs_put_super (struct super_block * sb) +{ + struct ufs_sb_private_info * uspi; + struct ufs_buffer_head * ubh; + unsigned char * base, * space; + unsigned size, blks, i; + + UFSD(("ENTER\n")) + + uspi = sb->u.ufs_sb.s_uspi; + size = uspi->s_cssize; + blks = howmany(size, uspi->s_fsize); + base = space = (char*) sb->u.ufs_sb.s_csp[0]; + for (i = 0; i < blks; i += uspi->s_fpb) { + size = uspi->s_bsize; + if (i + uspi->s_fpb > blks) + size = (blks - i) * uspi->s_fsize; + ubh = ubh_bread (sb->s_dev, uspi->s_csaddr + i, size); + if (!ubh) + goto go_on; + ubh_memcpyubh (ubh, space, size); + space += size; + ubh_mark_buffer_uptodate (ubh, 1); + ubh_mark_buffer_dirty (ubh, 0); + ubh_brelse (ubh); + } + +go_on: + for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { + ufs_put_cylinder (sb, i); + kfree (sb->u.ufs_sb.s_ucpi[i]); + } + for (i = 0; i < uspi->s_ncg; i++) + brelse (sb->u.ufs_sb.s_ucg[i]); + kfree (sb->u.ufs_sb.s_ucg); + kfree (base); + ubh_brelse2 (USPI_UBH); + kfree (sb->u.ufs_sb.s_uspi); + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return; +} + +/* + * Write super block to device + */ +void ufs_write_super (struct super_block * sb) { + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct ufs_super_block_third * usb3; + unsigned swab; + + UFSD(("ENTER\n")) + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + usb3 = ubh_get_usb_third(USPI_UBH); + + if (!(sb->s_flags & MS_RDONLY)) { + if (SWAB16(usb3->fs_u.fs_sun.fs_state) & UFS_FSOK) + usb3->fs_u.fs_sun.fs_state = SWAB16(SWAB16(usb3->fs_u.fs_sun.fs_state) & ~UFS_FSOK); + usb1->fs_time = SWAB32(CURRENT_TIME); + usb3->fs_u.fs_sun.fs_state = SWAB32(UFS_FSOK - SWAB32(usb1->fs_time)); + ubh_mark_buffer_dirty (USPI_UBH, 1); + } + sb->s_dirt = 0; + UFSD(("EXIT\n")) +} + +/* + * Copy some info about file system to user + */ +int ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsiz) +{ + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + struct statfs tmp; + struct statfs *sp = &tmp; + unsigned long used, avail; + unsigned swab; + + UFSD(("ENTER\n")) + + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first (USPI_UBH); + + sp->f_type = UFS_MAGIC; + sp->f_bsize = sb->s_blocksize; + sp->f_blocks = uspi->s_dsize; + sp->f_bfree = (SWAB32(usb1->fs_cstotal.cs_nbfree) << uspi->s_fpbshift )+ + SWAB32(usb1->fs_cstotal.cs_nffree); + + avail = sp->f_blocks - (sp->f_blocks / 100) * uspi->s_minfree; + used = sp->f_blocks - sp->f_bfree; + if (avail > used) + sp->f_bavail = avail - used; + else + sp->f_bavail = 0; + sp->f_files = uspi->s_ncg * uspi->s_ipg; + sp->f_ffree = SWAB32(usb1->fs_cstotal.cs_nifree); + sp->f_fsid.val[0] = SWAB32(usb1->fs_id[0]); + sp->f_fsid.val[1] = SWAB32(usb1->fs_id[1]); + sp->f_namelen = UFS_MAXNAMLEN; + + UFSD(("EXIT\n")) + + return copy_to_user(buf, sp, bufsiz) ? -EFAULT : 0; +} + + +static char error_buf[1024]; + +void ufs_warning (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + printk (KERN_WARNING "UFS-fs warning (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +} + +void ufs_error (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + va_list args; + + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + + if (!(sb->s_flags & MS_RDONLY)) { + usb1->fs_clean = UFS_FSBAD; + ubh_mark_buffer_dirty(USPI_UBH, 1); + sb->s_dirt = 1; + sb->s_flags |= MS_RDONLY; + } + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + printk (KERN_CRIT "UFS-fs error (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +} + +void ufs_panic (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + va_list args; + + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + + if (!(sb->s_flags & MS_RDONLY)) { + usb1->fs_clean = UFS_FSBAD; + ubh_mark_buffer_dirty(USPI_UBH, 1); + sb->s_dirt = 1; + } + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + /* this is to prevent panic from syncing this filesystem */ + if (sb->s_lock) + sb->s_lock = 0; + sb->s_flags |= MS_RDONLY; + printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +/* panic ("UFS-fs panic (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +*/ +} + + +static struct super_operations ufs_super_ops = { + ufs_read_inode, + ufs_write_inode, + ufs_put_inode, + ufs_delete_inode, + NULL, /* notify_change() */ + ufs_put_super, + ufs_write_super, + ufs_statfs, + NULL, /* XXX - ufs_remount() */ +}; + +static struct file_system_type ufs_fs_type = { + "ufs", + FS_REQUIRES_DEV, + ufs_read_super, + NULL +}; + + +int init_ufs_fs(void) +{ + return(register_filesystem(&ufs_fs_type)); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + return init_ufs_fs(); +} + +void cleanup_module(void) +{ + unregister_filesystem(&ufs_fs_type); +} +#endif + diff --git a/fs/ufs/swab.h b/fs/ufs/swab.h new file mode 100644 index 000000000..534c26981 --- /dev/null +++ b/fs/ufs/swab.h @@ -0,0 +1,114 @@ +/* + * linux/fs/ufs/ufs_swab.h + * + * Copyright (C) 1997 Francois-Rene Rideau <rideau@ens.fr> + * Copyright (C) 1998 Jakub Jelinek <jj@ultra.linux.cz> + */ + +#ifndef _UFS_SWAB_H +#define _UFS_SWAB_H + +/* + * Notes: + * HERE WE ASSUME EITHER BIG OR LITTLE ENDIAN UFSes + * in case there are ufs implementations that have strange bytesexes, + * you'll need to modify code here as well as in ufs_super.c and ufs_fs.h + * to support them. + */ + +#include <linux/ufs_fs.h> +#include <asm/byteorder.h> + +/* + * These are only valid inside ufs routines, + * after swab has been initialized to sb->u.ufs_sb.s_swab + */ +#define SWAB16(x) ufs_swab16(swab,x) +#define SWAB32(x) ufs_swab32(swab,x) +#define SWAB64(x) ufs_swab64(swab,x) + +/* + * We often use swabing, when we want to increment/decrement some value, so these + * macros might become handy and increase readability. (Daniel) + */ +#define INC_SWAB16(x) x=ufs_swab16_add(swab,x,1) +#define INC_SWAB32(x) x=ufs_swab32_add(swab,x,1) +#define INC_SWAB64(x) x=ufs_swab64_add(swab,x,1) +#define DEC_SWAB16(x) x=ufs_swab16_add(swab,x,-1) +#define DEC_SWAB32(x) x=ufs_swab32_add(swab,x,-1) +#define DEC_SWAB64(x) x=ufs_swab64_add(swab,x,-1) +#define ADD_SWAB16(x,y) x=ufs_swab16_add(swab,x,y) +#define ADD_SWAB32(x,y) x=ufs_swab32_add(swab,x,y) +#define ADD_SWAB64(x,y) x=ufs_swab64_add(swab,x,y) +#define SUB_SWAB16(x,y) x=ufs_swab16_add(swab,x,-(y)) +#define SUB_SWAB32(x,y) x=ufs_swab32_add(swab,x,-(y)) +#define SUB_SWAB64(x,y) x=ufs_swab64_add(swab,x,-(y)) + +#ifndef __PDP_ENDIAN +extern __inline__ __const__ __u16 ufs_swab16(unsigned swab, __u16 x) { + if (swab) + return swab16(x); + else + return x; +} +extern __inline__ __const__ __u32 ufs_swab32(unsigned swab, __u32 x) { + if (swab) + return swab32(x); + else + return x; +} +extern __inline__ __const__ __u64 ufs_swab64(unsigned swab, __u64 x) { + if (swab) + return swab64(x); + else + return x; +} +extern __inline__ __const__ __u16 ufs_swab16_add(unsigned swab, __u16 x, __u16 y) { + if (swab) + return swab16(swab16(x)+y); + else + return x + y; +} +extern __inline__ __const__ __u32 ufs_swab32_add(unsigned swab, __u32 x, __u32 y) { + if (swab) + return swab32(swab32(x)+y); + else + return x + y; +} +extern __inline__ __const__ __u64 ufs_swab64_add(unsigned swab, __u64 x, __u64 y) { + if (swab) + return swab64(swab64(x)+y); + else + return x + y; +} +#else /* __PDP_ENDIAN */ +extern __inline__ __const__ __u16 ufs_swab16(unsigned swab, __u16 x) { + if (swab & UFS_LITTLE_ENDIAN) + return le16_to_cpu(x); + else + return be16_to_cpu(x); +} +extern __inline__ __const__ __u32 ufs_swab32(unsigned swab, __u32 x) { + if (swab & UFS_LITTLE_ENDIAN) + return le32_to_cpu(x); + else + return be32_to_cpu(x); +} +extern __inline__ __const__ __u64 ufs_swab64(unsigned swab, __u64 x) { + if (swab & UFS_LITTLE_ENDIAN) + return le64_to_cpu(x); + else + return be64_to_cpu(x); +} +extern __inline__ __const__ __u16 ufs_swab16_add(unsigned swab, __u16 x, __u16 y) { + return ufs_swab16(swab, ufs_swab16(swab, x) + y); +} +extern __inline__ __const__ __u32 ufs_swab32_add(unsigned swab, __u32 x, __u32 y) { + return ufs_swab32(swab, ufs_swab32(swab, x) + y); +} +extern __inline__ __const__ __u64 ufs_swab64_add(unsigned swab, __u64 x, __u64 y) { + return ufs_swab64(swab, ufs_swab64(swab, x) + y); +} +#endif /* __PDP_ENDIAN */ + +#endif /* _UFS_SWAB_H */ diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c new file mode 100644 index 000000000..9d18c5f53 --- /dev/null +++ b/fs/ufs/symlink.c @@ -0,0 +1,138 @@ +/* + * linux/ufs/ufs/symlink.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@emai.cz> + * Charles University, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/symlink.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/symlink.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext2 symlink handling code + */ + +#include <asm/uaccess.h> + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/ext2_fs.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/stat.h> + + +#undef UFS_SYMLINK_DEBUG + +#ifdef UFS_SYMLINK_DEBUG +#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + + +static struct dentry * ufs_follow_link(struct dentry * dentry, + struct dentry * base) +{ + struct inode * inode; + struct buffer_head * bh; + int error; + char * link; + + UFSD(("ENTER\n")) + + inode = dentry->d_inode; + bh = NULL; + /* slow symlink */ + if (inode->i_blocks) { + if (!(bh = ufs_bread (inode, 0, 0, &error))) { + dput(base); + return ERR_PTR(-EIO); + } + link = bh->b_data; + } + /* fast symlink */ + else { + link = (char *) inode->u.ufs_i.i_u1.i_symlink; + } + UPDATE_ATIME(inode); + base = lookup_dentry(link, base, 1); + if (bh) + brelse(bh); + UFSD(("EXIT\n")) + return base; +} + +static int ufs_readlink (struct dentry * dentry, char * buffer, int buflen) +{ + struct super_block * sb; + struct inode * inode; + struct buffer_head * bh; + char * link; + int i; + + UFSD(("ENTER\n")) + + inode = dentry->d_inode; + sb = inode->i_sb; + bh = NULL; + if (buflen > sb->s_blocksize - 1) + buflen = sb->s_blocksize - 1; + /* slow symlink */ + if (inode->i_blocks) { + int err; + bh = ufs_bread (inode, 0, 0, &err); + if (!bh) { + if(err < 0) /* indicate type of error */ + return err; + return 0; + } + link = bh->b_data; + } + /* fast symlink */ + else { + link = (char *) inode->u.ufs_i.i_u1.i_symlink; + } + i = 0; + while (i < buflen && link[i]) + i++; + if (copy_to_user(buffer, link, i)) + i = -EFAULT; + UPDATE_ATIME(inode); + if (bh) + brelse (bh); + UFSD(("ENTER\n")) + return i; +} + +struct inode_operations ufs_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 */ + ufs_readlink, /* readlink */ + ufs_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c new file mode 100644 index 000000000..c6dca7358 --- /dev/null +++ b/fs/ufs/truncate.c @@ -0,0 +1,473 @@ +/* + * linux/fs/ufs/truncate.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles Uiversity, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/truncate.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/truncate.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 + */ + +/* + * Real random numbers for secure rm added 94/02/18 + * Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr> + */ + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/locks.h> +#include <linux/string.h> + +#include "swab.h" +#include "util.h" + +#undef UFS_TRUNCATE_DEBUG + +#ifdef UFS_TRUNCATE_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +/* + * Secure deletion currently doesn't work. It interacts very badly + * with buffers shared with memory mappings, and for that reason + * can't be done in the truncate() routines. It should instead be + * done separately in "release()" before calling the truncate routines + * that will release the actual file blocks. + * + * Linus + */ + +#define DIRECT_BLOCK howmany (inode->i_size, uspi->s_bsize) +#define DIRECT_FRAGMENT howmany (inode->i_size, uspi->s_fsize) + +static int ufs_trunc_direct (struct inode * inode) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct buffer_head * bh; + u32 * p; + unsigned frag1, frag2, frag3, frag4, block1, block2; + unsigned frag_to_free, free_count; + unsigned i, j, tmp; + int retry; + unsigned swab; + + UFSD(("ENTER\n")) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + + frag_to_free = 0; + free_count = 0; + retry = 0; + + frag1 = DIRECT_FRAGMENT; + frag4 = min (UFS_NDIR_FRAGMENT, inode->u.ufs_i.i_lastfrag); + frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1); + frag3 = frag4 & ~uspi->s_fpbmask; + block1 = block2 = 0; + if (frag2 > frag3) { + frag2 = frag4; + frag3 = frag4 = 0; + } + else if (frag2 < frag3) { + block1 = ufs_fragstoblks (frag2); + block2 = ufs_fragstoblks (frag3); + } + + UFSD(("frag1 %u, frag2 %u, block1 %u, block2 %u, frag3 %u, frag4 %u\n", frag1, frag2, block1, block2, frag3, frag4)) + + if (frag1 >= frag2) + goto next1; + + /* + * Free first free fragments + */ + p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag1); + tmp = SWAB32(*p); + if (!tmp ) + ufs_panic (sb, "ufs_trunc_direct", "internal error"); + frag1 = ufs_fragnum (frag1); + frag2 = ufs_fragnum (frag2); + for (j = frag1; j < frag2; j++) { + bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); + if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + retry = 1; + brelse (bh); + goto next1; + } + bforget (bh); + } + inode->i_blocks -= (frag2-frag1) << uspi->s_nspfshift; + mark_inode_dirty(inode); + ufs_free_fragments (inode, tmp + frag1, frag2 - frag1); + frag_to_free = tmp + frag1; + +next1: + /* + * Free whole blocks + */ + for (i = block1 ; i < block2; i++) { + p = inode->u.ufs_i.i_u1.i_data + i; + tmp = SWAB32(*p); + if (!tmp) + continue; + for (j = 0; j < uspi->s_fpb; j++) { + bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); + if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + retry = 1; + brelse (bh); + goto next2; + } + bforget (bh); + } + *p = SWAB32(0); + inode->i_blocks -= uspi->s_nspb; + mark_inode_dirty(inode); + if (free_count == 0) { + frag_to_free = tmp; + free_count = uspi->s_fpb; + } else if (free_count > 0 && frag_to_free == tmp - free_count) + free_count += uspi->s_fpb; + else { + ufs_free_blocks (inode, frag_to_free, free_count); + frag_to_free = tmp; + free_count = uspi->s_fpb; + } +next2: + } + + if (free_count > 0) + ufs_free_blocks (inode, frag_to_free, free_count); + + if (frag3 >= frag4) + goto next3; + + /* + * Free last free fragments + */ + p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag3); + tmp = SWAB32(*p); + if (!tmp ) + ufs_panic(sb, "ufs_truncate_direct", "internal error"); + frag4 = ufs_fragnum (frag4); + for (j = 0; j < frag4; j++) { + bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); + if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + retry = 1; + brelse (bh); + goto next1; + } + bforget (bh); + } + *p = SWAB32(0); + inode->i_blocks -= frag4 << uspi->s_nspfshift; + mark_inode_dirty(inode); + ufs_free_fragments (inode, tmp, frag4); + next3: + + UFSD(("EXIT\n")) + return retry; +} + + +static int ufs_trunc_indirect (struct inode * inode, unsigned offset, u32 * p) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_buffer_head * ind_ubh; + struct buffer_head * bh; + u32 * ind; + unsigned indirect_block, i, j, tmp; + unsigned frag_to_free, free_count; + int retry; + unsigned swab; + + UFSD(("ENTER\n")) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + + frag_to_free = 0; + free_count = 0; + retry = 0; + + tmp = SWAB32(*p); + if (!tmp) + return 0; + ind_ubh = ubh_bread (sb->s_dev, tmp, uspi->s_bsize); + if (tmp != SWAB32(*p)) { + ubh_brelse (ind_ubh); + return 1; + } + if (!ind_ubh) { + *p = SWAB32(0); + return 0; + } + + indirect_block = (DIRECT_BLOCK > offset) ? (DIRECT_BLOCK - offset) : 0; + for (i = indirect_block; i < uspi->s_apb; i++) { + ind = ubh_get_addr32 (ind_ubh, i); + tmp = SWAB32(*ind); + if (!tmp) + continue; + for (j = 0; j < uspi->s_fpb; j++) { + bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); + if ((bh && bh->b_count != 1) || tmp != SWAB32(*ind)) { + retry = 1; + brelse (bh); + goto next; + } + bforget (bh); + } + *ind = SWAB32(0); + ubh_mark_buffer_dirty(ind_ubh, 1); + if (free_count == 0) { + frag_to_free = tmp; + free_count = uspi->s_fpb; + } else if (free_count > 0 && frag_to_free == tmp - free_count) + free_count += uspi->s_fpb; + else { + ufs_free_blocks (inode, frag_to_free, free_count); + frag_to_free = tmp; + free_count = uspi->s_fpb; + } + inode->i_blocks -= uspi->s_nspb; + mark_inode_dirty(inode); +next: + } + + if (free_count > 0) { + ufs_free_blocks (inode, frag_to_free, free_count); + } + for (i = 0; i < uspi->s_apb; i++) + if (SWAB32(*ubh_get_addr32(ind_ubh,i))) + break; + if (i >= uspi->s_apb) { + if (ubh_max_bcount(ind_ubh) != 1) { + retry = 1; + } + else { + tmp = SWAB32(*p); + *p = SWAB32(0); + inode->i_blocks -= uspi->s_nspb; + mark_inode_dirty(inode); + ufs_free_blocks (inode, tmp, uspi->s_fpb); + ubh_bforget(ind_ubh); + ind_ubh = NULL; + } + } + if (IS_SYNC(inode) && ind_ubh && ubh_buffer_dirty(ind_ubh)) { + ubh_ll_rw_block (WRITE, 1, &ind_ubh); + ubh_wait_on_buffer (ind_ubh); + } + ubh_brelse (ind_ubh); + + UFSD(("EXIT\n")) + + return retry; +} + +static int ufs_trunc_dindirect (struct inode * inode, unsigned offset, u32 * p) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_buffer_head * dind_bh; + unsigned i, tmp, dindirect_block; + u32 * dind; + int retry = 0; + unsigned swab; + + UFSD(("ENTER\n")) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + + dindirect_block = (DIRECT_BLOCK > offset) + ? ((DIRECT_BLOCK - offset) / uspi->s_apb) : 0; + retry = 0; + + tmp = SWAB32(*p); + if (!tmp) + return 0; + dind_bh = ubh_bread (inode->i_dev, tmp, uspi->s_bsize); + if (tmp != SWAB32(*p)) { + ubh_brelse (dind_bh); + return 1; + } + if (!dind_bh) { + *p = SWAB32(0); + return 0; + } + + for (i = dindirect_block ; i < uspi->s_apb ; i++) { + dind = ubh_get_addr32 (dind_bh, i); + tmp = SWAB32(*dind); + if (!tmp) + continue; + retry |= ufs_trunc_indirect (inode, offset + (i << uspi->s_apbshift), dind); + ubh_mark_buffer_dirty(dind_bh, 1); + } + + for (i = 0; i < uspi->s_apb; i++) + if (SWAB32(*ubh_get_addr32 (dind_bh, i))) + break; + if (i >= uspi->s_apb) { + if (ubh_max_bcount(dind_bh) != 1) + retry = 1; + else { + tmp = SWAB32(*p); + *p = SWAB32(0); + inode->i_blocks -= uspi->s_nspb; + mark_inode_dirty(inode); + ufs_free_blocks (inode, tmp, uspi->s_fpb); + ubh_bforget(dind_bh); + dind_bh = NULL; + } + } + if (IS_SYNC(inode) && dind_bh && ubh_buffer_dirty(dind_bh)) { + ubh_ll_rw_block (WRITE, 1, &dind_bh); + ubh_wait_on_buffer (dind_bh); + } + ubh_brelse (dind_bh); + + UFSD(("EXIT\n")) + + return retry; +} + +static int ufs_trunc_tindirect (struct inode * inode) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct ufs_buffer_head * tind_bh; + unsigned tindirect_block, tmp, i; + u32 * tind, * p; + int retry; + unsigned swab; + + UFSD(("ENTER\n")) + + sb = inode->i_sb; + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + retry = 0; + + tindirect_block = (DIRECT_BLOCK > (UFS_NDADDR + uspi->s_apb + uspi->s_2apb)) + ? ((DIRECT_BLOCK - UFS_NDADDR - uspi->s_apb - uspi->s_2apb) / uspi->s_2apb) : 0; + p = inode->u.ufs_i.i_u1.i_data + UFS_TIND_BLOCK; + if (!(tmp = SWAB32(*p))) + return 0; + tind_bh = ubh_bread (sb->s_dev, tmp, uspi->s_bsize); + if (tmp != SWAB32(*p)) { + ubh_brelse (tind_bh); + return 1; + } + if (!tind_bh) { + *p = SWAB32(0); + return 0; + } + + for (i = tindirect_block ; i < uspi->s_apb ; i++) { + tind = ubh_get_addr32 (tind_bh, i); + retry |= ufs_trunc_dindirect(inode, UFS_NDADDR + + uspi->s_apb + ((i + 1) << uspi->s_2apbshift), tind); + ubh_mark_buffer_dirty(tind_bh, 1); + } + for (i = 0; i < uspi->s_apb; i++) + if (SWAB32(*ubh_get_addr32 (tind_bh, i))) + break; + if (i >= uspi->s_apb) { + if (ubh_max_bcount(tind_bh) != 1) + retry = 1; + else { + tmp = SWAB32(*p); + *p = SWAB32(0); + inode->i_blocks -= uspi->s_nspb; + mark_inode_dirty(inode); + ufs_free_blocks (inode, tmp, uspi->s_fpb); + ubh_bforget(tind_bh); + tind_bh = NULL; + } + } + if (IS_SYNC(inode) && tind_bh && ubh_buffer_dirty(tind_bh)) { + ubh_ll_rw_block (WRITE, 1, &tind_bh); + ubh_wait_on_buffer (tind_bh); + } + ubh_brelse (tind_bh); + + UFSD(("EXIT\n")) + return retry; +} + +void ufs_truncate (struct inode * inode) +{ + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct buffer_head * bh; + unsigned offset; + int err, retry; + + UFSD(("ENTER\n")) + sb = inode->i_sb; + uspi = sb->u.ufs_sb.s_uspi; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) + return; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + while (1) { + retry = ufs_trunc_direct(inode); + retry |= ufs_trunc_indirect (inode, UFS_IND_BLOCK, + (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK]); + retry |= ufs_trunc_dindirect (inode, UFS_IND_BLOCK + uspi->s_apb, + (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK]); + retry |= ufs_trunc_tindirect (inode); + if (!retry) + break; + if (IS_SYNC(inode) && (inode->i_state & I_DIRTY)) + ufs_sync_inode (inode); + current->counter = 0; + schedule (); + + + } + offset = inode->i_size & uspi->s_fshift; + if (offset) { + bh = ufs_bread (inode, inode->i_size >> uspi->s_fshift, 0, &err); + if (bh) { + memset (bh->b_data + offset, 0, uspi->s_fsize - offset); + mark_buffer_dirty (bh, 0); + brelse (bh); + } + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->u.ufs_i.i_lastfrag = howmany (inode->i_size, uspi->s_fsize); + mark_inode_dirty(inode); + UFSD(("EXIT\n")) +} diff --git a/fs/ufs/ufs_file.c b/fs/ufs/ufs_file.c deleted file mode 100644 index 7471156a0..000000000 --- a/fs/ufs/ufs_file.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * linux/fs/ufs/ufs_file.c - * - * Copyright (C) 1996 - * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) - * Laboratory for Computer Science Research Computing Facility - * Rutgers, The State University of New Jersey - * - * $Id: ufs_file.c,v 1.9 1997/07/17 02:24:13 davem Exp $ - * - */ - -#include <linux/fs.h> -#include <linux/ufs_fs.h> - -static struct file_operations ufs_file_operations = { - NULL, /* lseek */ - generic_file_read, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* open */ - NULL, /* release */ - file_fsync, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ -}; - -struct inode_operations ufs_file_inode_operations = { - &ufs_file_operations, /* default directory 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 */ - ufs_bmap, /* bmap */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ -}; - diff --git a/fs/ufs/ufs_inode.c b/fs/ufs/ufs_inode.c deleted file mode 100644 index 2011a0be8..000000000 --- a/fs/ufs/ufs_inode.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * linux/fs/ufs/ufs_inode.c - * - * Copyright (C) 1996 - * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) - * Laboratory for Computer Science Research Computing Facility - * Rutgers, The State University of New Jersey - * - * Clean swab support on 19970406 - * by Francois-Rene Rideau <rideau@ens.fr> - * - * 4.4BSD (FreeBSD) support added on February 1st 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk> partially based - * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. - * - * NeXTstep support added on February 5th 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk>. - */ - -#undef DEBUG_UFS_INODE -/*#define DEBUG_UFS_INODE 1*/ -/* Uncomment the line above when hacking ufs inode code */ - -#include <linux/fs.h> -#include <linux/ufs_fs.h> -#include <linux/sched.h> - -#include "ufs_swab.h" - -void ufs_print_inode(struct inode * inode) -{ - printk("ino %lu mode 0%6.6o lk %d uid %d gid %d" - " sz %lu blks %lu cnt %u\n", - inode->i_ino, inode->i_mode, inode->i_nlink, inode->i_uid, - inode->i_gid, inode->i_size, inode->i_blocks, inode->i_count); - printk(" db <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x" - " 0x%x 0x%x 0x%x 0x%x>\n", - inode->u.ufs_i.i_u1.i_data[0], inode->u.ufs_i.i_u1.i_data[1], - inode->u.ufs_i.i_u1.i_data[2], inode->u.ufs_i.i_u1.i_data[3], - inode->u.ufs_i.i_u1.i_data[4], inode->u.ufs_i.i_u1.i_data[5], - inode->u.ufs_i.i_u1.i_data[6], inode->u.ufs_i.i_u1.i_data[7], - inode->u.ufs_i.i_u1.i_data[8], inode->u.ufs_i.i_u1.i_data[9], - inode->u.ufs_i.i_u1.i_data[10], inode->u.ufs_i.i_u1.i_data[11]); - printk(" gen 0x%8.8x ib <0x%x 0x%x 0x%x>\n", - inode->u.ufs_i.i_gen, - inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK], - inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK], - inode->u.ufs_i.i_u1.i_data[UFS_TIND_BLOCK]); -} - -#define inode_bmap(inode, nr) ((inode)->u.ufs_i.i_u1.i_data[(nr)]) - -static inline int block_bmap (struct inode *inode, int block, int nr) -{ - struct buffer_head *bh; - int tmp; - __u32 flags = inode->i_sb->u.ufs_sb.s_flags; - /* XXX Split in fsize big blocks (Can't bread 8Kb). */ - tmp = nr >> (inode->i_sb->u.ufs_sb.s_fshift - 2); - bh = bread (inode->i_dev, inode->i_sb->u.ufs_sb.s_blockbase + block + - tmp, inode->i_sb->s_blocksize); - if (!bh) - return 0; - nr &= ~(inode->i_sb->u.ufs_sb.s_fmask) >> 2; - tmp = SWAB32(((__u32 *)bh->b_data)[nr]); - brelse (bh); - return tmp; -} - -int ufs_bmap (struct inode * inode, int block) -{ - int i; - int addr_per_block = UFS_ADDR_PER_BLOCK(inode->i_sb); - int addr_per_block_bits = UFS_ADDR_PER_BLOCK_BITS(inode->i_sb); - int lbn = ufs_lbn (inode->i_sb, block); - int boff = ufs_boff (inode->i_sb, block); - - if (lbn < 0) { - ufs_warning (inode->i_sb, "ufs_bmap", "block < 0"); - return 0; - } - if (lbn >= UFS_NDADDR + addr_per_block + - (1 << (addr_per_block_bits * 2)) + - ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { - ufs_warning (inode->i_sb, "ufs_bmap", "block > big"); - return 0; - } - if (lbn < UFS_NDADDR) - return (inode->i_sb->u.ufs_sb.s_blockbase + - ufs_dbn (inode->i_sb, inode_bmap (inode, lbn), boff)); - lbn -= UFS_NDADDR; - if (lbn < addr_per_block) { - i = inode_bmap (inode, UFS_IND_BLOCK); - if (!i) - return 0; - return (inode->i_sb->u.ufs_sb.s_blockbase + - ufs_dbn (inode->i_sb, - block_bmap (inode, i, lbn), boff)); - } - lbn -= addr_per_block; - if (lbn < (1 << (addr_per_block_bits * 2))) { - i = inode_bmap (inode, UFS_DIND_BLOCK); - if (!i) - return 0; - i = block_bmap (inode, i, lbn >> addr_per_block_bits); - if (!i) - return 0; - return (inode->i_sb->u.ufs_sb.s_blockbase + - ufs_dbn (inode->i_sb, - block_bmap (inode, i, lbn & (addr_per_block-1)), - boff)); - } - lbn -= (1 << (addr_per_block_bits * 2)); - i = inode_bmap (inode, UFS_TIND_BLOCK); - if (!i) - return 0; - i = block_bmap (inode, i, lbn >> (addr_per_block_bits * 2)); - if (!i) - return 0; - i = block_bmap (inode, i, - (lbn >> addr_per_block_bits) & (addr_per_block - 1)); - if (!i) - return 0; - return (inode->i_sb->u.ufs_sb.s_blockbase + - ufs_dbn (inode->i_sb, - block_bmap (inode, i, lbn & (addr_per_block-1)), boff)); -} - -/* XXX - ufs_read_inode is a mess */ -void ufs_read_inode(struct inode * inode) -{ - struct super_block * sb; - struct ufs_inode * ufsip; - struct buffer_head * bh; - __u32 flags = inode->i_sb->u.ufs_sb.s_flags; - - sb = inode->i_sb; - - if (ufs_ino_ok(inode)) { - printk("ufs_read_inode: bad inum %lu\n", inode->i_ino); - - return; - } - -#ifdef DEBUG_UFS_INODE - printk("ufs_read_inode: ino %lu cg %u cgino %u ipg %u inopb %u\n", - inode->i_ino, ufs_ino2cg(inode), - (inode->i_ino%sb->u.ufs_sb.s_inopb), - sb->u.ufs_sb.s_ipg, sb->u.ufs_sb.s_inopb); -#endif - bh = bread(inode->i_dev, - inode->i_sb->u.ufs_sb.s_blockbase + - ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) + - (inode->i_ino%sb->u.ufs_sb.s_ipg)/ - (sb->u.ufs_sb.s_inopb/sb->u.ufs_sb.s_fsfrag), - sb->s_blocksize); - if (!bh) { - printk("ufs_read_inode: can't read inode %lu from dev %d/%d\n", - inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); - return; - } - - ufsip = (struct ufs_inode *)bh->b_data; - ufsip += (inode->i_ino%(sb->u.ufs_sb.s_inopb/sb->u.ufs_sb.s_fsfrag)); - - /* - * Copy data to the in-core inode. - */ - inode->i_mode = SWAB16(ufsip->ui_mode); - inode->i_nlink = SWAB16(ufsip->ui_nlink); - if (inode->i_nlink == 0) { - /* XXX */ - printk("ufs_read_inode: zero nlink ino %lu dev %u/%u\n", - inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); - inode->i_nlink = 1; - printk("ufs_read_inode: fishy ino %lu pblk %lu dev %u/%u\n", - inode->i_ino, - ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) + - (inode->i_ino%sb->u.ufs_sb.s_ipg)/sb->u.ufs_sb.s_inopb, - MAJOR(inode->i_dev), MINOR(inode->i_dev)); - } - /* XXX - debugging */ - if (SWAB32(ufsip->ui_gen) == 0) { - printk("ufs_read_inode: zero gen ino %lu pblk %lu dev %u/%u\n", - inode->i_ino, - ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) + - (inode->i_ino%sb->u.ufs_sb.s_ipg)/sb->u.ufs_sb.s_inopb, - MAJOR(inode->i_dev), MINOR(inode->i_dev)); - } - /* - * Since Linux currently only has 16-bit uid_t and gid_t, we can't - * really support EFTs. For the moment, we use 0 as the uid and gid - * if an inode has a uid or gid that won't fit in 16 bits. This way - * random users can't get at these files, since they get dynamically - * "chown()ed" to root. - */ - if (UFS_UID(ufsip) >= UFS_USEEFT) { - inode->i_uid = 0; - printk("ufs_read_inode: EFT uid %u ino %lu dev %u/%u, using %u\n", - UFS_UID(ufsip), inode->i_ino, MAJOR(inode->i_dev), - MINOR(inode->i_dev), inode->i_uid); - } else { - inode->i_uid = UFS_UID(ufsip); - } - if (UFS_GID(ufsip) >= UFS_USEEFT) { - inode->i_gid = 0; - printk("ufs_read_inode: EFT gid %u ino %lu dev %u/%u, using %u\n", - UFS_GID(ufsip), inode->i_ino, MAJOR(inode->i_dev), - MINOR(inode->i_dev), inode->i_gid); - } else { - inode->i_gid = UFS_GID(ufsip); - } - - /* - * Linux i_size is 32 bits on most architectures, - * so some files on a UFS filesystem may not - * be readable. I let people access the first 32 bits worth of them. - * for the rw code, we may want to mark these inodes as read-only. - * XXX - bug Linus to make i_size a __u64 instead of a __u32. - */ - inode->u.ufs_i.i_size = SWAB64(ufsip->ui_size); - /* KRR - Just type cast inode->u.ufs_i.i_size into off_t and - * worry about overflow later - */ - inode->i_size = (off_t)inode->u.ufs_i.i_size; - - /* - * Linux doesn't keep tv_usec around in the kernel, so we discard it. - * XXX - I'm not sure what I should do about writing things. I may - * want to keep this data, but for the moment I think I'll just write - * zeros for these fields when writing out inodes. - */ - inode->i_atime = SWAB32(ufsip->ui_atime.tv_sec); - inode->i_mtime = SWAB32(ufsip->ui_mtime.tv_sec); - inode->i_ctime = SWAB32(ufsip->ui_ctime.tv_sec); - inode->i_blksize = sb->u.ufs_sb.s_fsize; - inode->i_blocks = SWAB32(ufsip->ui_blocks); - inode->i_version = ++event; /* see linux/kernel/sched.c */ - - if (S_ISREG(inode->i_mode)) { - inode->i_op = &ufs_file_inode_operations; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &ufs_dir_inode_operations; - } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &ufs_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); - } else { - printk("ufs_read_inode: unknown file type 0%o ino %lu dev %d/%d\n", - inode->i_mode, inode->i_ino, MAJOR(inode->i_dev), - MINOR(inode->i_dev)); - /* XXX - debugging */ - ufs_print_inode(inode); - inode->i_op = &ufs_file_inode_operations; - } - - /* - * ufs_read_super makes sure that UFS_NDADDR and UFS_NINDIR are sane. - */ - if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode)) { - int i; - - if (inode->i_blocks) { - for (i = 0; i < UFS_NDADDR; i++) { - inode->u.ufs_i.i_u1.i_data[i] = - SWAB32(ufsip->ui_u2.ui_addr.ui_db[i]); - } - for (i = 0; i < UFS_NINDIR; i++) { - inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK + i] = - SWAB32(ufsip->ui_u2.ui_addr.ui_ib[i]); - } - } else /* fast symlink */ { - memcpy(inode->u.ufs_i.i_u1.i_symlink, - ufsip->ui_u2.ui_symlink, 60); - } - } - - /* KRR - I need to check the SunOS header files, but for the time - * being, I'm going to tread ui_db[0] and [1] as a __u64 and swab - * them appropriately. This should clean up any real endian problems, - * but we'll still need to add size checks in the write portion of - * the code. - */ - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { - inode->i_rdev = (kdev_t)SWAB64(*(__u64*)&ufsip->ui_u2.ui_addr.ui_db); - } - - inode->u.ufs_i.i_flags = SWAB32(ufsip->ui_flags); - inode->u.ufs_i.i_gen = SWAB32(ufsip->ui_gen); /* XXX - is this i_version? */ - inode->u.ufs_i.i_shadow = SWAB32(ufsip->ui_u3.ui_sun.ui_shadow); /* XXX */ - inode->u.ufs_i.i_uid = SWAB32(ufsip->ui_u3.ui_sun.ui_uid); - inode->u.ufs_i.i_gid = SWAB32(ufsip->ui_u3.ui_sun.ui_gid); - inode->u.ufs_i.i_oeftflag = SWAB32(ufsip->ui_u3.ui_sun.ui_oeftflag); - - brelse(bh); - - if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_INODE)) { - ufs_print_inode(inode); - } - - return; -} - -void ufs_put_inode (struct inode * inode) -{ - if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_INODE)) { - printk("ufs_put_inode:\n"); - ufs_print_inode(inode); - } - - if (inode->i_nlink) - return; - - printk("ufs_put_inode: nlink == 0 for inum %lu on dev %d/%d\n", - inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); - ufs_print_inode(inode); - panic("ufs_put_inode: fs is read only, and nlink == 0"); - - /* XXX - this code goes here eventually - inode->i_size = 0; - if (inode->i_blocks) - ufs_truncate(inode); - ufs_free_inode(inode); - */ - - return; -} diff --git a/fs/ufs/ufs_namei.c b/fs/ufs/ufs_namei.c deleted file mode 100644 index 021b85442..000000000 --- a/fs/ufs/ufs_namei.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * linux/fs/ufs/ufs_namei.c - * - * Copyright (C) 1996 - * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) - * Laboratory for Computer Science Research Computing Facility - * Rutgers, The State University of New Jersey - * - * Clean swab support by Francois-Rene Rideau <rideau@ens.fr> 19970406 - * Ported to 2.1.62 by Francois-Rene Rideau <rideau@ens.fr> 19971109 - * - * 4.4BSD (FreeBSD) support added on February 1st 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk> partially based - * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. - */ - -#include <linux/fs.h> -#include <linux/ufs_fs.h> -#include <linux/string.h> -#include "ufs_swab.h" - -/* - * NOTE1: unlike strncmp, ufs_match returns 1 for success, 0 for failure - * (stolen from ext2fs.) - * NOTE2: flags *is* used, though this is hidden by macros like NAMLEN. - */ -static int ufs_match (int len, const char * const name, struct ufs_direct * d, __u32 flags) -{ - if (!d || len > UFS_MAXNAMLEN) /* XXX - name space */ - return 0; - /* - * "" means "." ---> so paths like "/usr/lib//libc.a" work - */ - if (!len && (NAMLEN(d) == 1) && (d->d_name[0] == '.') && - (d->d_name[1] == '\0')) - return 1; - if (len != NAMLEN(d)) - return 0; - return !memcmp(name, d->d_name, len); -} - -int ufs_lookup (struct inode *dir, struct dentry *dentry) -{ - /* XXX - this is all fucked up! */ - unsigned long int lfragno, fragno; - struct buffer_head * bh; - struct ufs_direct * d; - struct super_block * sb = dir->i_sb; - const char *name = dentry->d_name.name; - int len = dentry->d_name.len; - __u32 flags; - struct inode *inode; - - /* XXX - isn't that already done by the upper layer? */ - if (!dir || !S_ISDIR(dir->i_mode)) - return -EBADF; - - flags = sb->u.ufs_sb.s_flags; - - if (flags & UFS_DEBUG) - printk("Passed name: %s\nPassed length: %d\n", name, len); - - /* debugging hacks: - * Touching /xyzzy in a filesystem toggles debugging messages. - */ - if ((len == 5) && !(memcmp(name, "xyzzy", len)) && - (dir->i_ino == UFS_ROOTINO)) { - sb->u.ufs_sb.s_flags ^= UFS_DEBUG; - printk("UFS debugging %s\n", - (sb->u.ufs_sb.s_flags & UFS_DEBUG) ? - "on": "off"); - goto not_found; - /*return(-ENOENT);*/ - } - - /* - * Touching /xyzzy.i in a filesystem toggles debugging for ufs_inode.c - */ - if ((len == 7) && !(memcmp(name, "xyzzy.i", len)) && - (dir->i_ino == UFS_ROOTINO)) { - sb->u.ufs_sb.s_flags ^= UFS_DEBUG_INODE; - printk("UFS inode debugging %s\n", - (sb->u.ufs_sb.s_flags & UFS_DEBUG_INODE) ? - "on": "off"); - goto not_found; - /*return(-ENOENT);*/ - } - - /* - * Touching /xyzzy.n in a filesystem toggles debugging for ufs_namei.c - */ - if ((len == 7) && !(memcmp(name, "xyzzy.n", len)) && - (dir->i_ino == UFS_ROOTINO)) { - sb->u.ufs_sb.s_flags ^= UFS_DEBUG_NAMEI; - printk("UFS namei debugging %s\n", - (sb->u.ufs_sb.s_flags & UFS_DEBUG_NAMEI) ? - "on": "off"); - goto not_found; - /*return(-ENOENT);*/ - } - - /* - * Touching /xyzzy.l in a filesystem toggles debugging for ufs_symlink.c - */ - if ((len == 7) && !(memcmp(name, "xyzzy.l", len)) && - (dir->i_ino == UFS_ROOTINO)) { - sb->u.ufs_sb.s_flags ^= UFS_DEBUG_LINKS; - printk("UFS symlink debugging %s\n", - (sb->u.ufs_sb.s_flags & UFS_DEBUG_LINKS) ? - "on": "off"); - goto not_found; - /*return(-ENOENT);*/ - } - - /* Now for the real thing */ - - if (flags & (UFS_DEBUG|UFS_DEBUG_NAMEI)) { - printk("ufs_lookup: called for ino %lu name %s\n", - dir->i_ino, name); - } - - for (lfragno = 0; lfragno < dir->i_blocks; lfragno++) { - fragno = ufs_bmap(dir, lfragno); - /* ufs_bmap() reads the block (frag) size in s_blocksize */ - /* XXX - ufs_bmap() call needs error checking */ - if (flags & UFS_DEBUG) { - printk("ufs_lookup: ino %lu lfragno %lu fragno %lu\n", - dir->i_ino, lfragno, fragno); - } - if (fragno == 0) { - /* XXX - bug bug bug */ - goto not_found; - /*return(-ENOENT);*/ - } - bh = bread(dir->i_dev, fragno, sb->s_blocksize); - if (bh == NULL) { - printk("ufs_lookup: bread failed: " - "ino %lu, lfragno %lu", - dir->i_ino, lfragno); - return(-EIO); - } - d = (struct ufs_direct *)(bh->b_data); - while (((char *)d - bh->b_data + SWAB16(d->d_reclen)) <= - sb->s_blocksize) { - /* XXX - skip block if d_reclen or d_namlen is 0 */ - if ((d->d_reclen == 0) || (NAMLEN(d) == 0)) { - /* no need to SWAB16(): test against 0 */ - if (flags & UFS_DEBUG) { - printk("ufs_lookup: skipped space in directory, ino %lu\n", - dir->i_ino); - } - break; - } - if (flags & UFS_DEBUG) { - printk("lfragno 0x%lx " - "direct d 0x%x " - "d_ino %u " - "d_reclen %u " - "d_namlen %u d_name `%s'\n", - lfragno, - (unsigned int)((unsigned long)d), - SWAB32(d->d_ino), - SWAB16(d->d_reclen), - NAMLEN(d),d->d_name); - } - if ((NAMLEN(d) == len) && - /* XXX - don't use strncmp() - see ext2fs */ - (ufs_match(len, name, d, flags))) { - /* We have a match */ -/* XXX - I only superficially understand how things work, - * so use at your own risk... -- Fare' - */ - inode = iget(sb, SWAB32(d->d_ino)); - brelse(bh); - if(!inode) { return -EACCES; } - d_add(dentry,inode); - return(0); - } else { - /* XXX - bounds checking */ - if (flags & UFS_DEBUG) { - printk("ufs_lookup: " - "wanted (%s,%d) got (%s,%d)\n", - name, len, - d->d_name, NAMLEN(d)); - } - } - d = (struct ufs_direct *)((char *)d + - SWAB16(d->d_reclen)); - } - brelse(bh); - } - not_found: - d_add(dentry,NULL); - return(0); -} diff --git a/fs/ufs/ufs_super.c b/fs/ufs/ufs_super.c deleted file mode 100644 index 7fdac8dd6..000000000 --- a/fs/ufs/ufs_super.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * linux/fs/ufs/ufs_super.c - * - * Copyright (C) 1996 - * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) - * Laboratory for Computer Science Research Computing Facility - * Rutgers, The State University of New Jersey - * - * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) - * - */ - -/* - * Kernel module support added on 96/04/26 by - * Stefan Reinauer <stepan@home.culture.mipt.ru> - * - * Module usage counts added on 96/04/29 by - * Gertjan van Wingerde <gertjan@cs.vu.nl> - * - * Clean swab support on 19970406 by - * Francois-Rene Rideau <rideau@ens.fr> - * - * 4.4BSD (FreeBSD) support added on February 1st 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk> partially based - * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. - * - * NeXTstep support added on February 5th 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk>. - */ - -#undef DEBUG_UFS_SUPER -/*#define DEBUG_UFS_SUPER 1*/ -/* Uncomment the line above when hacking ufs superblock code */ - -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/ufs_fs.h> -#include <linux/locks.h> -#include <linux/init.h> -#include <asm/uaccess.h> - -#include "ufs_swab.h" - -struct super_block * ufs_read_super(struct super_block * sb, void * data, int silent); -void ufs_put_super (struct super_block * sb); -int ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsize); - -static struct super_operations ufs_super_ops = { - ufs_read_inode, - NULL, /* XXX - ufs_write_inode() */ - ufs_put_inode, - NULL, /* XXX - ufs_delete_inode() */ - NULL, /* XXX - notify_change() */ - ufs_put_super, - NULL, /* XXX - ufs_write_super() */ - ufs_statfs, - NULL, /* XXX - ufs_remount() */ -}; - -static struct file_system_type ufs_fs_type = { - "ufs", - FS_REQUIRES_DEV, - ufs_read_super, - NULL -}; - -__initfunc(int init_ufs_fs(void)) -{ - return(register_filesystem(&ufs_fs_type)); -} - -#ifdef MODULE -EXPORT_NO_SYMBOLS; - -int init_module(void) -{ - return init_ufs_fs(); -} - -void cleanup_module(void) -{ - unregister_filesystem(&ufs_fs_type); -} -#endif - -static char error_buf[1024]; - -void ufs_warning (struct super_block * sb, const char * function, - const char * fmt, ...) -{ - va_list args; - - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); - printk (KERN_WARNING "UFS warning (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); -} - -#ifdef DEBUG_UFS_SUPER -static void -ufs_print_super_stuff(struct super_block * sb, struct ufs_superblock * usb) -{ - __u32 flags = sb->u.ufs_sb.s_flags; - - printk("fs_sblkno: 0x%8.8x\n", usb->fs_sblkno); - printk("fs_size: 0x%8.8x\n", usb->fs_size); - printk("fs_ncg: 0x%8.8x\n", usb->fs_ncg); - printk("fs_bsize: 0x%8.8x\n", usb->fs_bsize); - printk("fs_fsize: 0x%8.8x\n", usb->fs_fsize); - printk("fs_frag: 0x%8.8x\n", usb->fs_frag); - printk("fs_nindir: 0x%8.8x\n", usb->fs_nindir); - printk("fs_inopb: 0x%8.8x\n", usb->fs_inopb); - printk("fs_optim: 0x%8.8x\n", usb->fs_optim); - printk("fs_ncyl: 0x%8.8x\n", usb->fs_ncyl); - printk("fs_clean: 0x%8.8x\n", usb->fs_clean); - printk("fs_state: 0x%8.8x\n", UFS_STATE(usb)); - printk("fs_magic: 0x%8.8x\n", usb->fs_magic); - printk("fs_fsmnt: `%s'\n", usb->fs_fsmnt); - - return; -} -#endif - -struct super_block * -ufs_read_super(struct super_block * sb, void * data, int silent) -{ - struct ufs_superblock * usb; /* normalized to local byteorder */ - struct buffer_head * bh1, *bh2; - __u32 flags = UFS_DEBUG_INITIAL; /* for sb->u.ufs_sb.s_flags */ - static int offsets[] = { 0, 96, 160 }; /* different superblock locations */ - int i; - - /* sb->s_dev and sb->s_flags are set by our caller - * data is the mystery argument to sys_mount() - * - * Our caller also sets s_dev, s_covered, s_rd_only, s_dirt, - * and s_type when we return. - */ - - MOD_INC_USE_COUNT; - lock_super (sb); - set_blocksize (sb->s_dev, BLOCK_SIZE); - - /* XXX - make everything read only for testing */ - sb->s_flags |= MS_RDONLY; - - for (i = 0; i < sizeof(offsets)/sizeof(offsets[0]); i++) { - if (!(bh1 = bread(sb->s_dev, offsets[i] + UFS_SBLOCK/BLOCK_SIZE, - BLOCK_SIZE)) || - !(bh2 = bread(sb->s_dev, offsets[i] + - UFS_SBLOCK/BLOCK_SIZE + 1, BLOCK_SIZE))) { - brelse(bh1); - printk ("ufs_read_super: unable to read superblock\n"); - goto ufs_read_super_lose; - } - /* XXX - redo this so we can free it later... */ - usb = (struct ufs_superblock *)__get_free_page(GFP_KERNEL); - if (usb == NULL) { - brelse(bh1); - brelse(bh2); - printk ("ufs_read_super: get_free_page() failed\n"); - goto ufs_read_super_lose; - } - - memcpy((char *)usb, bh1->b_data, BLOCK_SIZE); - memcpy((char *)usb + BLOCK_SIZE, bh2->b_data, - sizeof(struct ufs_superblock) - BLOCK_SIZE); - - brelse(bh1); - brelse(bh2); - - switch (le32_to_cpup(&usb->fs_magic)) { - case UFS_MAGIC: - flags |= UFS_LITTLE_ENDIAN; - ufs_superblock_le_to_cpus(usb); - goto found; - case UFS_CIGAM: - flags |= UFS_BIG_ENDIAN; - ufs_superblock_be_to_cpus(usb); - goto found; - /* usb is now normalized to local byteorder */ - default: - } - } - printk ("ufs_read_super: bad magic number 0x%8.8x " - "on dev %d/%d\n", usb->fs_magic, - MAJOR(sb->s_dev), MINOR(sb->s_dev)); - goto ufs_read_super_lose; -found: -#ifdef DEBUG_UFS_SUPER - printk("ufs_read_super: superblock offset 0x%2.2x\n", offsets[i]); -#endif - /* We found a UFS filesystem on this device. */ - - /* XXX - parse args */ - - if ((usb->fs_bsize != 4096) && (usb->fs_bsize != 8192)) { - printk("ufs_read_super: invalid fs_bsize = %d\n", - usb->fs_bsize); - goto ufs_read_super_lose; - } - - if ((usb->fs_fsize != 512) && (usb->fs_fsize != 1024)) { - printk("ufs_read_super: invalid fs_fsize = %d\n", - usb->fs_fsize); - goto ufs_read_super_lose; - } - if (usb->fs_fsize != BLOCK_SIZE) { - set_blocksize (sb->s_dev, usb->fs_fsize); - } - - flags |= UFS_VANILLA; - /* XXX more consistency check */ -#ifdef DEBUG_UFS_SUPER - printk("ufs_read_super: maxsymlinklen 0x%8.8x\n", - usb->fs_u.fs_44.fs_maxsymlinklen); -#endif - if (usb->fs_u.fs_44.fs_maxsymlinklen >= 0) { - if (usb->fs_u.fs_44.fs_inodefmt >= UFS_44INODEFMT) { - flags |= UFS_44BSD; - } else { - flags |= UFS_OLD; /* 4.2BSD */ - } - } else if (offsets[i] > 0) { - flags |= UFS_NEXT; - } else { - flags |= UFS_SUN; - } - -#ifdef DEBUG_UFS_SUPER - ufs_print_super_stuff(sb, usb); -#endif - if ( ((flags&UFS_ST_MASK)==UFS_ST_44BSD) - || ((flags&UFS_ST_MASK)==UFS_ST_OLD) - || ((flags&UFS_ST_MASK)==UFS_ST_NEXT) - || ( ((flags&UFS_ST_MASK)==UFS_ST_SUN) - && UFS_STATE(usb) == UFS_FSOK - usb->fs_time)) { - switch(usb->fs_clean) { - case UFS_FSACTIVE: /* 0x00 */ - printk("ufs_read_super: fs is active\n"); - sb->s_flags |= MS_RDONLY; - break; - case UFS_FSCLEAN: /* 0x01 */ -#ifdef DEBUG_UFS_SUPER - printk("ufs_read_super: fs is clean\n"); -#endif - break; - case UFS_FSSTABLE: /* 0x02 */ -#ifdef DEBUG_UFS_SUPER - printk("ufs_read_super: fs is stable\n"); -#endif - break; - case UFS_FSOSF1: /* 0x03 */ - /* XXX is this correct for DEC OSF/1? */ -#ifdef DEBUG_UFS_SUPER - printk("ufs_read_super: fs is clean and stable (OSF/1)\n"); -#endif - break; - case UFS_FSBAD: /* 0xFF */ - printk("ufs_read_super: fs is bad\n"); - sb->s_flags |= MS_RDONLY; - break; - default: - printk("ufs_read_super: can't grok fs_clean 0x%x\n", - usb->fs_clean); - sb->s_flags |= MS_RDONLY; - break; - } - } else { - printk("ufs_read_super: fs needs fsck\n"); - sb->s_flags |= MS_RDONLY; - /* XXX - make it read only or barf if it's not (/, /usr) */ - } - - /* XXX - sanity check sb fields */ - - /* KRR - Why are we not using fs_bsize for blocksize? */ - sb->s_blocksize = usb->fs_fsize; - sb->s_blocksize_bits = usb->fs_fshift; - /* XXX - sb->s_lock */ - sb->s_op = &ufs_super_ops; - sb->dq_op = 0; /* XXX */ - sb->s_magic = usb->fs_magic; - /* sb->s_time */ - /* sb->s_wait */ - /* XXX - sb->u.ufs_sb */ - sb->u.ufs_sb.s_raw_sb = usb; /* XXX - maybe move this to the top */ - sb->u.ufs_sb.s_flags = flags ; - sb->u.ufs_sb.s_ncg = usb->fs_ncg; - sb->u.ufs_sb.s_ipg = usb->fs_ipg; - sb->u.ufs_sb.s_fpg = usb->fs_fpg; - sb->u.ufs_sb.s_fsize = usb->fs_fsize; - sb->u.ufs_sb.s_fmask = usb->fs_fmask; - sb->u.ufs_sb.s_fshift = usb->fs_fshift; - sb->u.ufs_sb.s_bsize = usb->fs_bsize; - sb->u.ufs_sb.s_bmask = usb->fs_bmask; - sb->u.ufs_sb.s_bshift = usb->fs_bshift; - sb->u.ufs_sb.s_iblkno = usb->fs_iblkno; - sb->u.ufs_sb.s_dblkno = usb->fs_dblkno; - sb->u.ufs_sb.s_cgoffset = usb->fs_cgoffset; - sb->u.ufs_sb.s_cgmask = usb->fs_cgmask; - sb->u.ufs_sb.s_inopb = usb->fs_inopb; - sb->u.ufs_sb.s_lshift = usb->fs_bshift - usb->fs_fshift; - sb->u.ufs_sb.s_lmask = ~((usb->fs_fmask - usb->fs_bmask) - >> usb->fs_fshift); - sb->u.ufs_sb.s_fsfrag = usb->fs_frag; /* XXX - rename this later */ - sb->u.ufs_sb.s_blockbase = offsets[i]; - sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL); - -#ifdef DEBUG_UFS_SUPER - printk("ufs_read_super: inopb %u\n", sb->u.ufs_sb.s_inopb); -#endif - /* - * XXX - read cg structs? - */ - - unlock_super(sb); - return(sb); - -ufs_read_super_lose: - /* XXX - clean up */ - set_blocksize (sb->s_dev, BLOCK_SIZE); - sb->s_dev = 0; - unlock_super (sb); - MOD_DEC_USE_COUNT; - return(NULL); -} - -void ufs_put_super (struct super_block * sb) -{ - if (sb->u.ufs_sb.s_flags & UFS_DEBUG) { - printk("ufs_put_super\n"); /* XXX */ - } - - - /* XXX - sync fs data, set state to ok, and flush buffers */ - set_blocksize (sb->s_dev, BLOCK_SIZE); - - /* XXX - free allocated kernel memory */ - /* includes freeing usb page */ - - MOD_DEC_USE_COUNT; - - return; -} - -int ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsiz) -{ - struct statfs tmp; - struct statfs *sp = &tmp; - struct ufs_superblock *fsb = sb->u.ufs_sb.s_raw_sb; - /* fsb was already normalized during mounting */ - unsigned long used, avail; - - if (sb->u.ufs_sb.s_flags & UFS_DEBUG) { - printk("ufs_statfs\n"); /* XXX */ - } - - sp->f_type = sb->s_magic; - sp->f_bsize = sb->s_blocksize; - sp->f_blocks = fsb->fs_dsize; - sp->f_bfree = fsb->fs_cstotal.cs_nbfree * - fsb->fs_frag + - fsb->fs_cstotal.cs_nffree; - - avail = sp->f_blocks - (sp->f_blocks / 100) * - fsb->fs_minfree; - used = sp->f_blocks - sp->f_bfree; - if (avail > used) - sp->f_bavail = avail - used; - else - sp->f_bavail = 0; - - sp->f_files = sb->u.ufs_sb.s_ncg * sb->u.ufs_sb.s_ipg; - sp->f_ffree = fsb->fs_cstotal.cs_nifree; - sp->f_fsid.val[0] = fsb->fs_id[0]; - sp->f_fsid.val[1] = fsb->fs_id[1]; - sp->f_namelen = UFS_MAXNAMLEN; - - return copy_to_user(buf, sp, bufsiz) ? -EFAULT : 0; -} diff --git a/fs/ufs/ufs_swab.c b/fs/ufs/ufs_swab.c deleted file mode 100644 index 261d16de9..000000000 --- a/fs/ufs/ufs_swab.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * linux/fs/ufs/ufs_swab.c - * - * Copyright (C) 1997 - * Francois-Rene Rideau <rideau@ens.fr> - * - */ - -/* - * For inspiration, you might wanna check sys/ufs/ffs/fs.h from whateverBSD - * - * NOTES - * 19970406 - Fare <rideau@ens.fr> - * 1) I began from old very preliminary 2.0.x sources, - * but it was underfeatured; - * I later saw that 2.1.1 sources had a *global* UFS byteswap flag. - * EVIL: imagine that a swabbed partition be mounted - * while a non-swabbed partition are active (that sucks!) - * I merged that source tree with mine. - * 2) I hope no one is using obNNUUXXIIous byteorder. - * That's the only thing I might have broken, - * though I rather think it's a fix: - * instead of __u64 like BSD, - * the former driver used an explicitly bigendian array of __u32! - * 3) I provide a few macros that use GCC C Extensions. - * Port to other compilers would require avoiding them. - * in any case, 64 bit (long long) support is required, - * unless you're ready to workaround - * 4) the swab routines below depend on the precise name and order - * of the structure elements. Watch out any modification in ufs_fs.h!!! - * 5) putting byteswapping stuff in ufs_swab* seems cleaner to me. - * 6) These sources should work with both 2.0 and 2.1 kernels... - * - * 19971013 - Fare <rideaufr@issy.cnet.fr> - * 1) Ported to 2.1.57 - * 2) instead of byteswapping, use [bl]e_to_cpu: - * it might be that we run on a VAX! - * - * 4.4BSD (FreeBSD) support added on February 1st 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk> partially based - * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. - * - * HOWTO continue adding swab support: - * basically, anywhere metadata is bread() (i.e. mapped to block device), - * data should either be SWAB()ed on the fly, - * or copied to a buffer and globally bswap_ufs_*() there. - * - */ - -#include <linux/fs.h> -#include "ufs_swab.h" - -static __inline__ void n_be16_to_cpus(__u16*p,unsigned n) { -#ifndef __BIG_ENDIAN - unsigned i; - for(i=0;i<n;i++) { - be16_to_cpus(&p[i]); - } -#endif -} -static __inline__ void n_be32_to_cpus(__u32*p,unsigned n) { -#ifndef __BIG_ENDIAN - unsigned i; - for(i=0;i<n;i++) { - be32_to_cpus(&p[i]); - } -#endif -} -static __inline__ void n_le16_to_cpus(__u16*p,unsigned n) { -#ifndef __LITTLE_ENDIAN - unsigned i; - for(i=0;i<n;i++) { - le16_to_cpus(&p[i]); - } -#endif -} -static __inline__ void n_le32_to_cpus(__u32*p,unsigned n) { -#ifndef __LITTLE_ENDIAN - unsigned i; - for(i=0;i<n;i++) { - le32_to_cpus(&p[i]); - } -#endif -} - -#define __length_before(p,member) \ - ((unsigned)(((char*)&((p)->member))-(char*)(p))) -#define __length_since(p,member) \ - ((unsigned)(sizeof(*p)-__length_before(p,member))) -#define __length_between(p,begin,after_end) \ - ((unsigned)(__length_before(p,after_end)-__length_before(p,begin))) -#define be32_to_cpus__between(s,begin,after_end) \ - n_be32_to_cpus((__u32*)&((s).begin), \ - __length_between(&s,begin,after_end)/4) -#define le32_to_cpus__between(s,begin,after_end) \ - n_le32_to_cpus((__u32*)&((s).begin), \ - __length_between(&s,begin,after_end)/4) -#define be32_to_cpus__since(s,begin) \ - n_be32_to_cpus((__u32*)&((s).begin), \ - __length_since(&s,begin)/4) -#define le32_to_cpus__since(s,begin) \ - n_le32_to_cpus((__u32*)&((s).begin), \ - __length_since(&s,begin)/4) -#define be16_to_cpus__between(s,begin,after_end) \ - n_be16_to_cpus((__u16*)&((s).begin), \ - __length_between(&s,begin,after_end)/2) -#define le16_to_cpus__between(s,begin,after_end) \ - n_le16_to_cpus((__u16*)&((s).begin), \ - __length_between(&s,begin,after_end)/2) - -/* - * Here are the whole-structure swabping routines... - * They were fun to design, but I don't understand why we - * need a copy of the superblock, anyway. -- Fare' - */ - -extern void ufs_superblock_be_to_cpus(struct ufs_superblock * usb) { -#ifndef __BIG_ENDIAN - __u16 sb_type = 1; /* SUN type superblock */ - - if (usb->fs_u.fs_44.fs_maxsymlinklen >= 0) - sb_type = 0; /* 4.4BSD (FreeBSD) type superblock */ - - be32_to_cpus__between(*usb,fs_link,fs_fmod); - /* XXX - I dunno what to do w/ fs_csp, - * but it is unused by the current code, so that's ok for now. - */ - be32_to_cpus(&usb->fs_cpc); - if (sb_type) { - be16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_sun.fs_sparecon); - be32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_sun.fs_qbmask); - /* Might fail on strictly aligning 64-bit big-endian - * architectures. Ouch! - */ - be64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qbmask); - be64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qfmask); - } else { - be16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_44.fs_sparecon); - be32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_44.fs_maxfilesize); - be64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_maxfilesize); - be64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qbmask); - be64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qfmask); - be32_to_cpus((__s32 *) &usb->fs_u.fs_44.fs_state); - } - be32_to_cpus__between(*usb,fs_postblformat,fs_magic); -#endif -} -extern void ufs_superblock_le_to_cpus(struct ufs_superblock * usb) { -#ifndef __LITTLE_ENDIAN - __u16 sb_type = 1; /* SUN type superblock */ - - if (usb->fs_u.fs_44.fs_maxsymlinklen >= 0) - sb_type = 0; /* 4.4BSD (FreeBSD) type superblock */ - - le32_to_cpus__between(*usb,fs_link,fs_fmod); - /* XXX - I dunno what to do w/ fs_csp, - * but it is unused by the current code, so that's ok for now. - */ - le32_to_cpus(&usb->fs_cpc); - if (sb_type) { - le16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_sun.fs_sparecon); - le32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_sun.fs_qbmask); - /* Might fail on strictly aligning 64-bit big-endian - * architectures. Ouch! - */ - le64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qbmask); - le64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qfmask); - } else { - le16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_44.fs_sparecon); - le32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_44.fs_maxfilesize); - le64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_maxfilesize); - le64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qbmask); - le64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qfmask); - le32_to_cpus((__s32 *) &usb->fs_u.fs_44.fs_state); - } - le32_to_cpus__between(*usb,fs_postblformat,fs_magic); -#endif -} diff --git a/fs/ufs/ufs_swab.h b/fs/ufs/ufs_swab.h deleted file mode 100644 index f8e9fd898..000000000 --- a/fs/ufs/ufs_swab.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * linux/fs/ufs/ufs_swab.h - * - * Copyright (C) 1997 - * Francois-Rene Rideau <rideau@ens.fr> - * - */ - -#ifndef _UFS_SWAB_H -#define _UFS_SWAB_H - - -/* - * Notes: - * (1) HERE WE ASSUME EITHER BIG OR LITTLE ENDIAN UFSes - * in case there are ufs implementations that have strange bytesexes, - * you'll need to modify code here as well as in ufs_super.c and ufs_fs.h - * to support them. - * (2) for a read/write ufs driver, we should distinguish - * between byteswapping for read or write accesses! - * naming should then be UFS16_TO_CPU and suches. - * - * 4.4BSD (FreeBSD) support added on February 1st 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk> partially based - * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. - */ - -#include <linux/ufs_fs.h> -#include <asm/byteorder.h> - -/* - * These are only valid inside ufs routines, after a variable named flags - * has been made visible in current scope and properly initialized: - __u32 flags = sb->u.ufs_sb.s_flags ; - */ -#define SWAB16(x) ufs_swab16(flags,x) -#define SWAB32(x) ufs_swab32(flags,x) -#define SWAB64(x) ufs_swab64(flags,x) - -extern __inline__ __const__ __u16 ufs_swab16(__u32 flags, __u16 x) { - if ((flags&UFS_BYTESEX) == UFS_LITTLE_ENDIAN) { - return le16_to_cpu(x); - } else { - return be16_to_cpu(x); - } -} -extern __inline__ __const__ __u32 ufs_swab32(__u32 flags, __u32 x) { - if ((flags&UFS_BYTESEX) == UFS_LITTLE_ENDIAN) { - return le32_to_cpu(x); - } else { - return be32_to_cpu(x); - } -} -extern __inline__ __const__ __u64 ufs_swab64(__u32 flags, __u64 x) { - if ((flags&UFS_BYTESEX) == UFS_LITTLE_ENDIAN) { - return le64_to_cpu(x); - } else { - return be64_to_cpu(x); - } -} - - -/* - * These are for in-core superblock normalization. - * It might or not be a bad idea once we go to a read/write driver, - * as all critical info should be copied to the sb info structure anyway. - * So better replace them with a static inline function - * ufs_superblock_to_sb_info() in ufs_super.c - */ -extern void ufs_superblock_le_to_cpus(struct ufs_superblock * usb); -extern void ufs_superblock_be_to_cpus(struct ufs_superblock * usb); - - -/* - * These also implicitly depend on variable flags... - * NAMLEN(foo) is already normalized to local format, so don't SWAB16() it! - */ - -#define NAMLEN(direct) ufs_namlen(flags,direct) -extern __inline__ __u16 ufs_namlen(__u32 flags, struct ufs_direct * direct) { - if ( (flags&UFS_DE_MASK) == UFS_DE_OLD) { - return SWAB16(direct->d_u.d_namlen); - } else /* UFS_DE_44BSD */ { - return direct->d_u.d_44.d_namlen; - } -} - -/* Here is how the uid is computed: - if the file system is 4.2BSD, get it from oldids. - if it has sun extension and oldids is USEEFT, get it from ui_sun. - if it is 4.4 or Hurd, get it from ui_44 (which is the same as ui_hurd). - depends on implicit variable flags being initialized from - __u32 flags = sb->u.ufs_sb.s_flags; -*/ -#define UFS_UID(ino) ufs_uid(flags,ino) -#define UFS_GID(ino) ufs_gid(flags,ino) - -extern __inline__ __u32 ufs_uid(__u32 flags,struct ufs_inode * ino) { - switch(flags&UFS_UID_MASK) { - case UFS_UID_EFT: - return SWAB32(ino->ui_u3.ui_sun.ui_uid) ; - case UFS_UID_44BSD: - return SWAB32(ino->ui_u3.ui_44.ui_uid) ; - case UFS_UID_OLD: - default: - return SWAB16(ino->ui_u1.oldids.suid) ; - } -} -extern __inline__ __u32 ufs_gid(__u32 flags,struct ufs_inode * ino) { - switch(flags&UFS_UID_MASK) { - case UFS_UID_EFT: - return SWAB32(ino->ui_u3.ui_sun.ui_gid) ; - case UFS_UID_44BSD: - return SWAB32(ino->ui_u3.ui_44.ui_gid) ; - case UFS_UID_OLD: - default: - return SWAB16(ino->ui_u1.oldids.sgid) ; - } -} - -#endif /* _UFS_SWAB_H */ diff --git a/fs/ufs/ufs_symlink.c b/fs/ufs/ufs_symlink.c deleted file mode 100644 index e19abe44d..000000000 --- a/fs/ufs/ufs_symlink.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * linux/fs/ufs/ufs_symlink.c - * - * Copyright (C) 1996 - * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) - * Laboratory for Computer Science Research Computing Facility - * Rutgers, The State University of New Jersey - * - * Ported to 2.1.62 by Francois-Rene Rideau <rideau@ens.fr> 19971109 - * - * 4.4BSD (FreeBSD) support added on February 1st 1998 by - * Niels Kristian Bech Jensen <nkbj@image.dk> partially based - * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. - */ - -#include <linux/fs.h> -#include <linux/ufs_fs.h> -#include <linux/sched.h> - -#include <asm/uaccess.h> - -extern int ufs_bmap (struct inode *, int); - -static int -ufs_readlink(struct dentry * dentry, char * buffer, int buflen) -{ - struct inode * inode = dentry->d_inode; - struct super_block * sb = inode->i_sb; - unsigned long int block; - struct buffer_head * bh = NULL; - char * link; - int i; - char c; - - if (sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) { - printk("ufs_readlink: called on ino %lu dev %u/%u\n", - inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); - } - - if (!S_ISLNK(inode->i_mode)) { - return -EINVAL; - } - - if (buflen > sb->s_blocksize - 1) - buflen = sb->s_blocksize - 1; - if (inode->i_blocks) { - /* XXX - error checking */ - block = ufs_bmap(inode, 0); - if (sb->u.ufs_sb.s_flags &(UFS_DEBUG|UFS_DEBUG_LINKS)) { - printk("ufs_readlink: bmap got %lu for ino %lu\n", - block, inode->i_ino); - } - bh = bread(inode->i_dev, block, sb->s_blocksize); - if (!bh) { - printk("ufs_readlink: can't read block 0 for ino %lu on dev %u/%u\n", - inode->i_ino, MAJOR(inode->i_dev), - MINOR(inode->i_dev)); - return 0; - } - link = bh->b_data; - /* no need to bswap */ - } else /* fast symlink */ { - link = (char *)&(inode->u.ufs_i.i_u1.i_symlink[0]); - } - i = 0; - while (i < buflen && (c = link[i])) { - i++; - put_user (c, buffer++); - } - brelse (bh); - return i; -} - -/* - * XXX - blatantly stolen from minix fs - */ -static struct dentry * -ufs_follow_link(struct dentry * dentry, struct dentry * base) -{ - struct inode * inode = dentry->d_inode; - unsigned long int block; - struct buffer_head * bh = NULL; - char * link; - - if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) { - printk("ufs_follow_link: called on ino %lu dev %u/%u\n", - inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); - } - - if (inode->i_blocks) { - /* read the link from disk */ - /* XXX - error checking */ - block = ufs_bmap(inode, 0); - bh = bread(inode->i_dev, block, inode->i_sb->s_blocksize); - if (bh == NULL) { - printk("ufs_follow_link: can't read block 0 for ino %lu on dev %u/%u\n", - inode->i_ino, MAJOR(inode->i_dev), - MINOR(inode->i_dev)); - dput(base); - return ERR_PTR(-EIO); - } - link = bh->b_data; - } else /* fast symlink */ { - link = (char *)&(inode->u.ufs_i.i_u1.i_symlink[0]); - } - base = lookup_dentry(link, base, 1); - brelse (bh); - return base; -} - - -static struct file_operations ufs_symlink_operations = { - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* release */ - NULL, /* fsync */ /* XXX - is this ok? */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ -}; - -struct inode_operations ufs_symlink_inode_operations = { - &ufs_symlink_operations, /* default directory file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - &ufs_readlink, /* readlink */ - &ufs_follow_link, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL, /* permission */ -}; diff --git a/fs/ufs/util.c b/fs/ufs/util.c new file mode 100644 index 000000000..a0348c6f4 --- /dev/null +++ b/fs/ufs/util.c @@ -0,0 +1,197 @@ +/* + * linux/fs/ufs/util.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles Uiversity, Faculty of Mathematics and Physics + */ + +#include <linux/malloc.h> +#include <linux/locks.h> + +#include "swab.h" +#include "util.h" + +#undef UFS_UTILS_DEBUG + +#ifdef UFS_UTILS_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + + +struct ufs_buffer_head * _ubh_bread_ (struct ufs_sb_private_info * uspi, + kdev_t dev, unsigned fragment, unsigned size) +{ + struct ufs_buffer_head * ubh; + unsigned i, j, count; + if (size & ~uspi->s_fmask) + return NULL; + count = size >> uspi->s_fshift; + if (count > UFS_MAXFRAG) + return NULL; + ubh = (struct ufs_buffer_head *) + kmalloc (sizeof (struct ufs_buffer_head), GFP_KERNEL); + if (!ubh) + return NULL; + ubh->fragment = fragment; + ubh->count = count; + for (i = 0; i < count; i++) + if (!(ubh->bh[i] = bread (dev, fragment + i, uspi->s_fsize))) + goto failed; + for (; i < UFS_MAXFRAG; i++) + ubh->bh[i] = NULL; + return ubh; +failed: + for (j = 0; j < i; j++) + brelse (ubh->bh[j]); + return NULL; +} + +struct ufs_buffer_head * _ubh_bread2_ (struct ufs_sb_private_info * uspi, + kdev_t dev, unsigned fragment, unsigned size) +{ + unsigned i, j, count; + if (size & ~uspi->s_fmask) + return NULL; + count = size >> uspi->s_fshift; + if (count <= 0 || count > UFS_MAXFRAG) + return NULL; + USPI_UBH->fragment = fragment; + USPI_UBH->count = count; + for (i = 0; i < count; i++) + if (!(USPI_UBH->bh[i] = bread (dev, fragment + i, uspi->s_fsize))) + goto failed; + for (; i < UFS_MAXFRAG; i++) + USPI_UBH->bh[i] = NULL; + return USPI_UBH; +failed: + for (j = 0; j < i; j++) + brelse (USPI_UBH->bh[j]); + return NULL; +} + +void ubh_brelse (struct ufs_buffer_head * ubh) +{ + unsigned i; + if (!ubh) + return; + for (i = 0; i < ubh->count; i++) + brelse (ubh->bh[i]); + kfree (ubh); +} + +void ubh_brelse2 (struct ufs_buffer_head * ubh) +{ + unsigned i; + if (!ubh) + return; + for ( i = 0; i < ubh->count; i++ ) { + brelse (ubh->bh[i]); + ubh->bh[i] = NULL; + } +} + +void ubh_mark_buffer_dirty (struct ufs_buffer_head * ubh, int flag) +{ + unsigned i; + if (!ubh) + return; + for ( i = 0; i < ubh->count; i++ ) + mark_buffer_dirty (ubh->bh[i], flag); +} + +void ubh_mark_buffer_uptodate (struct ufs_buffer_head * ubh, int flag) +{ + unsigned i; + if (!ubh) + return; + for ( i = 0; i < ubh->count; i++ ) + mark_buffer_uptodate (ubh->bh[i], flag); +} + +void ubh_ll_rw_block (int rw, unsigned nr, struct ufs_buffer_head * ubh[]) +{ + unsigned i; + if (!ubh) + return; + for ( i = 0; i < nr; i++ ) + ll_rw_block (rw, ubh[i]->count, ubh[i]->bh); +} + +void ubh_wait_on_buffer (struct ufs_buffer_head * ubh) +{ + unsigned i; + if (!ubh) + return; + for ( i = 0; i < ubh->count; i++ ) + wait_on_buffer (ubh->bh[i]); +} + +unsigned ubh_max_bcount (struct ufs_buffer_head * ubh) +{ + unsigned i; + unsigned max = 0; + if (!ubh) + return 0; + for ( i = 0; i < ubh->count; i++ ) + if ( ubh->bh[i]->b_count > max ) + max = ubh->bh[i]->b_count; + if (max == 0) + printk("Je cosi shnileho v kralovstvi Danskem!\n"); + return max; +} + +void ubh_bforget (struct ufs_buffer_head * ubh) +{ + unsigned i; + if (!ubh) + return; + for ( i = 0; i < ubh->count; i++ ) if ( ubh->bh[i] ) + bforget (ubh->bh[i]); +} + +int ubh_buffer_dirty (struct ufs_buffer_head * ubh) +{ + unsigned i; + unsigned result = 0; + if (!ubh) + return 0; + for ( i = 0; i < ubh->count; i++ ) + result |= buffer_dirty(ubh->bh[i]); + return result; +} + +void _ubh_ubhcpymem_(struct ufs_sb_private_info * uspi, + unsigned char * mem, struct ufs_buffer_head * ubh, unsigned size) +{ + unsigned len, bhno; + if ( size > (ubh->count << uspi->s_fshift) ) + size = ubh->count << uspi->s_fshift; + bhno = 0; + while ( size ) { + len = min (size, uspi->s_fsize); + memcpy (mem, ubh->bh[bhno]->b_data, len); + mem += uspi->s_fsize; + size -= len; + bhno++; + } +} + +void _ubh_memcpyubh_(struct ufs_sb_private_info * uspi, + struct ufs_buffer_head * ubh, unsigned char * mem, unsigned size) +{ + unsigned len, bhno; + if ( size > (ubh->count << uspi->s_fshift) ) + size = ubh->count << uspi->s_fshift; + bhno = 0; + while ( size ) { + len = min (size, uspi->s_fsize); + memcpy (ubh->bh[bhno]->b_data, mem, len); + mem += uspi->s_fsize; + size -= len; + bhno++; + } +} + diff --git a/fs/ufs/util.h b/fs/ufs/util.h new file mode 100644 index 000000000..8f5e66727 --- /dev/null +++ b/fs/ufs/util.h @@ -0,0 +1,322 @@ +/* + * linux/fs/ufs/util.h + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + */ + +#include <linux/fs.h> +#include "swab.h" + + +/* + * some usefull marcos + */ +#define in_range(b,first,len) ((b)>=(first)&&(b)<(first)+(len)) +#define howmany(x,y) (((x)+(y)-1)/(y)) +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + + +/* + * current filesystem state; method depends on flags + */ +#define ufs_state(usb3) \ + (((flags & UFS_ST_MASK) == UFS_ST_OLD) \ + ? (usb3)->fs_u.fs_sun.fs_state /* old normal way */ \ + : (usb3)->fs_u.fs_44.fs_state /* 4.4BSD way */) + +/* + * namlen, it's format depends of flags + */ +#define ufs_namlen(de) _ufs_namlen_(de,flags,swab) +static inline __u16 _ufs_namlen_(struct ufs_dir_entry * de, unsigned flags, unsigned swab) { + if ((flags & UFS_DE_MASK) == UFS_DE_OLD) { + return SWAB16(de->d_u.d_namlen); + } else /* UFS_DE_44BSD */ { + return de->d_u.d_44.d_namlen; + } +} + +/* + * Here is how the uid is computed: + * if the file system is 4.2BSD, get it from oldids. + * if it has sun extension and oldids is USEEFT, get it from ui_sun. + * if it is 4.4 or Hurd, get it from ui_44 (which is the same as ui_hurd). + */ +#define ufs_uid(inode) _ufs_uid_(inode,flags,swab) +static inline __u32 _ufs_uid_(struct ufs_inode * inode, unsigned flags, unsigned swab) { + switch (flags & UFS_UID_MASK) { + case UFS_UID_EFT: + return SWAB32(inode->ui_u3.ui_sun.ui_uid); + case UFS_UID_44BSD: + return SWAB32(inode->ui_u3.ui_44.ui_uid); + case UFS_UID_OLD: + default: + return SWAB16(inode->ui_u1.oldids.ui_suid); + } +} + +#define ufs_gid(inode) _ufs_gid_(inode,flags,swab) +static inline __u32 _ufs_gid_(struct ufs_inode * inode, unsigned flags, unsigned swab) { + switch (flags & UFS_UID_MASK) { + case UFS_UID_EFT: + return SWAB32(inode->ui_u3.ui_sun.ui_gid); + case UFS_UID_44BSD: + return SWAB32(inode->ui_u3.ui_44.ui_gid); + case UFS_UID_OLD: + default: + return SWAB16(inode->ui_u1.oldids.ui_sgid); + } +} + +/* + * marcros used for retyping + */ +#define UCPI_UBH ((struct ufs_buffer_head *)ucpi) +#define USPI_UBH ((struct ufs_buffer_head *)uspi) + +/* + * This functions manipulate with ufs_buffers + */ +#define ubh_bread(dev,fragment,size) _ubh_bread_(uspi,dev,fragment,size) +extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned); +#define ubh_bread2(dev,fragment,size) _ubh_bread2_(uspi,dev,fragment,size) +extern struct ufs_buffer_head * _ubh_bread2_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned); +extern void ubh_brelse (struct ufs_buffer_head *); +extern void ubh_brelse2 (struct ufs_buffer_head *); +extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *, int); +extern void ubh_mark_buffer_uptodate (struct ufs_buffer_head *, int); +extern void ubh_ll_rw_block (int, unsigned, struct ufs_buffer_head **); +extern void ubh_wait_on_buffer (struct ufs_buffer_head *); +extern unsigned ubh_max_bcount (struct ufs_buffer_head *); +extern void ubh_bforget (struct ufs_buffer_head *); +extern int ubh_buffer_dirty (struct ufs_buffer_head *); +#define ubh_ubhcpymem(mem,ubh,size) _ubh_ubhcpymem_(uspi,mem,ubh,size) +extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struct ufs_buffer_head *, unsigned); +#define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size) +extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned); + +/* + * macros to get important structures from ufs_buffer_head + */ +#define ubh_get_usb_first(ubh) \ + ((struct ufs_super_block_first *)((ubh)->bh[0]->b_data)) + +#define ubh_get_usb_second(ubh) \ + ((struct ufs_super_block_second *)(ubh)-> \ + bh[SECTOR_SIZE >> uspi->s_fshift]->b_data + (SECTOR_SIZE & ~uspi->s_fmask)) + +#define ubh_get_usb_third(ubh) \ + ((struct ufs_super_block_third *)((ubh)-> \ + bh[SECTOR_SIZE*2 >> uspi->s_fshift]->b_data + (SECTOR_SIZE*2 & ~uspi->s_fmask))) + +#define ubh_get_ucg(ubh) \ + ((struct ufs_cylinder_group *)((ubh)->bh[0]->b_data)) + +/* + * Extract byte from ufs_buffer_head + * Extract the bits for a block from a map inside ufs_buffer_head + */ +#define ubh_get_addr8(ubh,begin) \ + ((u8*)(ubh)->bh[(begin) >> uspi->s_fshift]->b_data + ((begin) & ~uspi->s_fmask)) + +#define ubh_get_addr16(ubh,begin) \ + (((u16*)((ubh)->bh[(begin) >> (uspi->s_fshift-1)]->b_data)) + ((begin) & (uspi->fsize>>1) - 1))) + +#define ubh_get_addr32(ubh,begin) \ + (((u32*)((ubh)->bh[(begin) >> (BLOCK_SIZE_BITS-2)]->b_data)) + \ + ((begin) & ((BLOCK_SIZE>>2) - 1))) + +#define ubh_get_addr ubh_get_addr8 + +#define ubh_blkmap(ubh,begin,bit) \ + ((*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) >> ((bit) & 7)) & (0xff >> (UFS_MAXFRAG - uspi->s_fpb))) + +/* + * Macros for access to superblock array structures + */ +#define ubh_postbl(ubh,cylno,i) \ + ((uspi->s_postblformat != UFS_DYNAMICPOSTBLFMT) \ + ? (*(__s16*)(ubh_get_addr(ubh, \ + (unsigned)(&((struct ufs_super_block *)0)->fs_opostbl) \ + + (((cylno) * 16 + (i)) << 1) ) )) \ + : (*(__s16*)(ubh_get_addr(ubh, \ + uspi->s_postbloff + (((cylno) * uspi->s_nrpos + (i)) << 1) )))) + +#define ubh_rotbl(ubh,i) \ + ((uspi->s_postblformat != UFS_DYNAMICPOSTBLFMT) \ + ? (*(__u8*)(ubh_get_addr(ubh, \ + (unsigned)(&((struct ufs_super_block *)0)->fs_space) + (i)))) \ + : (*(__u8*)(ubh_get_addr(ubh, uspi->s_rotbloff + (i))))) + +/* + * Determine the number of available frags given a + * percentage to hold in reserve. + */ +#define ufs_freespace(usb, percentreserved) \ + (ufs_blkstofrags(SWAB32((usb)->fs_cstotal.cs_nbfree)) + \ + SWAB32((usb)->fs_cstotal.cs_nffree) - (uspi->s_dsize * (percentreserved) / 100)) + +/* + * Macros for access to cylinder group array structures + */ +#define ubh_cg_blktot(ucpi,cylno) \ + (*((__u32*)ubh_get_addr(UCPI_UBH, (ucpi)->c_btotoff + ((cylno) << 2)))) + +#define ubh_cg_blks(ucpi,cylno,rpos) \ + (*((__u16*)ubh_get_addr(UCPI_UBH, \ + (ucpi)->c_boff + (((cylno) * uspi->s_nrpos + (rpos)) << 1 )))) + +/* + * Bitmap operation + * This functions work like classical bitmap operations. The diference + * is that we havn't the whole bitmap in one continuous part of memory, + * but in a few buffers. + * The parameter of each function is super_block, ufs_buffer_head and + * position of the begining of the bitmap. + */ +#define ubh_setbit(ubh,begin,bit) \ + (*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) |= (1 << ((bit) & 7))) + +#define ubh_clrbit(ubh,begin,bit) \ + (*ubh_get_addr (ubh, (begin) + ((bit) >> 3)) &= ~(1 << ((bit) & 7))) + +#define ubh_isset(ubh,begin,bit) \ + (*ubh_get_addr (ubh, (begin) + ((bit) >> 3)) & (1 << ((bit) & 7))) + +#define ubh_isclr(ubh,begin,bit) (!ubh_isset(ubh,begin,bit)) + +#define ubh_find_first_zero_bit(ubh,begin,size) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0) +#define ubh_find_next_zero_bit(ubh,begin,size,offset) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset) +static inline unsigned _ubh_find_next_zero_bit_( + struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, + unsigned begin, unsigned size, unsigned offset) +{ + unsigned base, rest; + + begin <<= 3; + size += begin; + offset += begin; + base = offset >> (uspi->s_fshift + 3); + offset &= ((uspi->s_fsize << 3) - 1); + for (;;) { + rest = min (size, uspi->s_fsize << 3); + size -= rest; + offset = ext2_find_next_zero_bit (ubh->bh[base]->b_data, rest, offset); + if (offset < rest || !size) + break; + base++; + offset = 0; + } + return (base << (uspi->s_fshift + 3)) + offset - begin; +} + +#define ubh_isblockclear(ubh,begin,block) (!_ubh_isblockset_(uspi,ubh,begin,block)) +#define ubh_isblockset(ubh,begin,block) _ubh_isblockset_(uspi,ubh,begin,block) +static inline int _ubh_isblockset_(struct ufs_sb_private_info * uspi, + struct ufs_buffer_head * ubh, unsigned begin, unsigned block) +{ + switch (uspi->s_fpb) { + case 8: + return (*ubh_get_addr (ubh, begin + block) == 0xff); + case 4: + return (*ubh_get_addr (ubh, begin + (block >> 1)) == (0x0f << ((block & 0x01) << 2))); + case 2: + return (*ubh_get_addr (ubh, begin + (block >> 2)) == (0x03 << ((block & 0x03) << 1))); + case 1: + return (*ubh_get_addr (ubh, begin + (block >> 3)) == (0x01 << (block & 0x07))); + } + return 0; +} + +#define ubh_clrblock(ubh,begin,block) _ubh_clrblock_(uspi,ubh,begin,block) +static inline void _ubh_clrblock_(struct ufs_sb_private_info * uspi, + struct ufs_buffer_head * ubh, unsigned begin, unsigned block) +{ + switch (uspi->s_fpb) { + case 8: + *ubh_get_addr (ubh, begin + block) = 0x00; + return; + case 4: + *ubh_get_addr (ubh, begin + (block >> 1)) &= ~(0x0f << ((block & 0x01) << 2)); + return; + case 2: + *ubh_get_addr (ubh, begin + (block >> 2)) &= ~(0x03 << ((block & 0x03) << 1)); + return; + case 1: + *ubh_get_addr (ubh, begin + (block >> 3)) &= ~(0x01 << ((block & 0x07))); + return; + } +} + +#define ubh_setblock(ubh,begin,block) _ubh_setblock_(uspi,ubh,begin,block) +static inline void _ubh_setblock_(struct ufs_sb_private_info * uspi, + struct ufs_buffer_head * ubh, unsigned begin, unsigned block) +{ + switch (uspi->s_fpb) { + case 8: + *ubh_get_addr(ubh, begin + block) = 0xff; + return; + case 4: + *ubh_get_addr(ubh, begin + (block >> 1)) |= (0x0f << ((block & 0x01) << 2)); + return; + case 2: + *ubh_get_addr(ubh, begin + (block >> 2)) |= (0x03 << ((block & 0x03) << 1)); + return; + case 1: + *ubh_get_addr(ubh, begin + (block >> 3)) |= (0x01 << ((block & 0x07))); + return; + } +} + +static inline void ufs_fragacct (struct super_block * sb, unsigned blockmap, + unsigned * fraglist, int cnt) +{ + struct ufs_sb_private_info * uspi; + unsigned fragsize, pos; + unsigned swab; + + swab = sb->u.ufs_sb.s_swab; + uspi = sb->u.ufs_sb.s_uspi; + + fragsize = 0; + for (pos = 0; pos < uspi->s_fpb; pos++) { + if (blockmap & (1 << pos)) { + fragsize++; + } + else if (fragsize > 0) { + ADD_SWAB32(fraglist[fragsize], cnt); + fragsize = 0; + } + } + if (fragsize > 0 && fragsize < uspi->s_fpb) + ADD_SWAB32(fraglist[fragsize], cnt); +} + +#define ubh_scanc(ubh,begin,size,table,mask) _ubh_scanc_(uspi,ubh,begin,size,table,mask) +static inline unsigned _ubh_scanc_(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, + unsigned begin, unsigned size, unsigned char * table, unsigned char mask) +{ + unsigned rest, offset; + unsigned char * cp; + + + offset = begin & ~uspi->s_fmask; + begin >>= uspi->s_fshift; + for (;;) { + if ((offset + size) < uspi->s_fsize) + rest = size; + else + rest = uspi->s_fsize - offset; + size -= rest; + cp = ubh->bh[begin]->b_data + offset; + while ((table[*cp++] & mask) == 0 && --rest); + if (rest || !size) + break; + begin++; + offset = 0; + } + return (size + rest); +} |