summaryrefslogtreecommitdiffstats
path: root/fs/sysv
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
committer <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
commit1513ff9b7899ab588401c89db0e99903dbf5f886 (patch)
treef69cc81a940a502ea23d664c3ffb2d215a479667 /fs/sysv
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/sysv')
-rw-r--r--fs/sysv/INTRO183
-rw-r--r--fs/sysv/Makefile31
-rw-r--r--fs/sysv/README37
-rw-r--r--fs/sysv/balloc.c329
-rw-r--r--fs/sysv/dir.c144
-rw-r--r--fs/sysv/file.c263
-rw-r--r--fs/sysv/fsync.c197
-rw-r--r--fs/sysv/ialloc.c218
-rw-r--r--fs/sysv/inode.c951
-rw-r--r--fs/sysv/mmap.c85
-rw-r--r--fs/sysv/namei.c822
-rw-r--r--fs/sysv/symlink.c110
-rw-r--r--fs/sysv/truncate.c283
13 files changed, 3653 insertions, 0 deletions
diff --git a/fs/sysv/INTRO b/fs/sysv/INTRO
new file mode 100644
index 000000000..9e53cb317
--- /dev/null
+++ b/fs/sysv/INTRO
@@ -0,0 +1,183 @@
+This is the implementation of the SystemV/Coherent filesystem for Linux.
+It grew out of separate filesystem implementations
+
+ Xenix FS Doug Evans <dje@cygnus.com> June 1992
+ SystemV FS Paul B. Monday <pmonday@eecs.wsu.edu> March-June 1993
+ Coherent FS B. Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> June 1993
+
+and was merged together in July 1993.
+
+These filesystems are rather similar. Here is a comparison with Minix FS:
+
+* Linux fdisk reports on partitions
+ - Minix FS 0x81 Linux/Minix
+ - Xenix FS ??
+ - SystemV FS ??
+ - Coherent FS 0x08 AIX bootable
+
+* Size of a block or zone (data allocation unit on disk)
+ - Minix FS 1024
+ - Xenix FS 1024 (also 512 ??)
+ - SystemV FS 1024 (also 512)
+ - Coherent FS 512
+
+* General layout: all have one boot block, one super block and
+ separate areas for inodes and for directories/data.
+ On SystemV Release 2 FS (e.g. Microport) the first track is reserved and
+ all the block numbers (including the super block) are offset by one track.
+
+* Byte ordering of "short" (16 bit entities) on disk:
+ - Minix FS little endian 0 1
+ - Xenix FS little endian 0 1
+ - SystemV FS little endian 0 1
+ - Coherent FS little endian 0 1
+ Of course, this affects only the file system, not the data of files on it!
+
+* Byte ordering of "long" (32 bit entities) on disk:
+ - Minix FS little endian 0 1 2 3
+ - Xenix FS little endian 0 1 2 3
+ - SystemV FS little endian 0 1 2 3
+ - Coherent FS PDP-11 2 3 0 1
+ Of course, this affects only the file system, not the data of files on it!
+
+* Inode on disk: "short", 0 means non-existent, the root dir ino is:
+ - Minix FS 1
+ - Xenix FS, SystemV FS, Coherent FS 2
+
+* Maximum number of hard links to a file:
+ - Minix FS 250
+ - Xenix FS ??
+ - SystemV FS ??
+ - Coherent FS >=10000
+
+* Free inode management:
+ - Minix FS a bitmap
+ - Xenix FS, SystemV FS, Coherent FS
+ There is a cache of a certain number of free inodes in the super-block.
+ When it is exhausted, new free inodes are found using a linear search.
+
+* Free block management:
+ - Minix FS a bitmap
+ - Xenix FS, SystemV FS, Coherent FS
+ Free blocks are organized in a "free list". Maybe a misleading term,
+ since it is not true that every free block contains a pointer to
+ the next free block. Rather, the free blocks are organized in chunks
+ of limited size, and every now and then a free block contains pointers
+ to the free blocks pertaining to the next chunk; the first of these
+ contains pointers and so on. The list terminates with a "block number"
+ 0 on Xenix FS and SystemV FS, with a block zeroed out on Coherent FS.
+
+* Super-block location:
+ - Minix FS block 1 = bytes 1024..2047
+ - Xenix FS block 1 = bytes 1024..2047
+ - SystemV FS bytes 512..1023
+ - Coherent FS block 1 = bytes 512..1023
+
+* Super-block layout:
+ - Minix FS
+ unsigned short s_ninodes;
+ unsigned short s_nzones;
+ unsigned short s_imap_blocks;
+ unsigned short s_zmap_blocks;
+ unsigned short s_firstdatazone;
+ unsigned short s_log_zone_size;
+ unsigned long s_max_size;
+ unsigned short s_magic;
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short s_firstdatazone;
+ unsigned long s_nzones;
+ unsigned short s_fzone_count;
+ unsigned long s_fzones[NICFREE];
+ unsigned short s_finode_count;
+ unsigned short s_finodes[NICINOD];
+ char s_flock;
+ char s_ilock;
+ char s_modified;
+ char s_rdonly;
+ unsigned long s_time;
+ short s_dinfo[4]; -- SystemV FS only
+ unsigned long s_free_zones;
+ unsigned short s_free_inodes;
+ short s_dinfo[4]; -- Xenix FS only
+ unsigned short s_interleave_m,s_interleave_n; -- Coherent FS only
+ char s_fname[6];
+ char s_fpack[6];
+ then they differ considerably:
+ Xenix FS
+ char s_clean;
+ char s_fill[371];
+ long s_magic;
+ long s_type;
+ SystemV FS
+ long s_fill[12 or 14];
+ long s_state;
+ long s_magic;
+ long s_type;
+ Coherent FS
+ unsigned long s_unique;
+ Note that Coherent FS has no magic.
+
+* Inode layout:
+ - Minix FS
+ unsigned short i_mode;
+ unsigned short i_uid;
+ unsigned long i_size;
+ unsigned long i_time;
+ unsigned char i_gid;
+ unsigned char i_nlinks;
+ unsigned short i_zone[7+1+1];
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short i_mode;
+ unsigned short i_nlink;
+ unsigned short i_uid;
+ unsigned short i_gid;
+ unsigned long i_size;
+ unsigned char i_zone[3*(10+1+1+1)];
+ unsigned long i_atime;
+ unsigned long i_mtime;
+ unsigned long i_ctime;
+
+* Regular file data blocks are organized as
+ - Minix FS
+ 7 direct blocks
+ 1 indirect block (pointers to blocks)
+ 1 double-indirect block (pointer to pointers to blocks)
+ - Xenix FS, SystemV FS, Coherent FS
+ 10 direct blocks
+ 1 indirect block (pointers to blocks)
+ 1 double-indirect block (pointer to pointers to blocks)
+ 1 triple-indirect block (pointer to pointers to pointers to blocks)
+
+* Inode size, inodes per block
+ - Minix FS 32 32
+ - Xenix FS 64 16
+ - SystemV FS 64 16
+ - Coherent FS 64 8
+
+* Directory entry on disk
+ - Minix FS
+ unsigned short inode;
+ char name[14/30];
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short inode;
+ char name[14];
+
+* Dir entry size, dir entries per block
+ - Minix FS 16/32 64/32
+ - Xenix FS 16 64
+ - SystemV FS 16 64
+ - Coherent FS 16 32
+
+* How to implement symbolic links such that the host fsck doesn't scream:
+ - Minix FS normal
+ - Xenix FS kludge: as regular files with chmod 1000
+ - SystemV FS ??
+ - Coherent FS kludge: as regular files with chmod 1000
+
+
+Notation: We often speak of a "block" but mean a zone (the allocation unit)
+and not the disk driver's notion of "block".
+
+
+Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>
+
diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile
new file mode 100644
index 000000000..d4a6ecbd4
--- /dev/null
+++ b/fs/sysv/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the Linux SystemV/Coherent-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= ialloc.o balloc.o inode.o file.o dir.o symlink.o namei.o \
+ fsync.o truncate.o
+
+sysv.o: $(OBJS)
+ $(LD) -r -o sysv.o $(OBJS)
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/sysv/README b/fs/sysv/README
new file mode 100644
index 000000000..d318eb64b
--- /dev/null
+++ b/fs/sysv/README
@@ -0,0 +1,37 @@
+This is the implementation of the SystemV/Coherent filesystem for Linux.
+It implements all of
+ - Xenix FS,
+ - SystemV/386 FS,
+ - Coherent FS.
+
+This is version beta 4.
+
+To install:
+* Answer the 'System V and Coherent filesystem support' question with 'y'
+ when configuring the kernel.
+* To mount a disk or a partition, use
+ mount [-r] -t sysv device mountpoint
+ The file system type names
+ -t sysv
+ -t xenix
+ -t coherent
+ may be used interchangeably, but the last two will eventually disappear.
+
+Bugs in the present implementation:
+- Coherent FS:
+ - The "free list interleave" n:m is currently ignored.
+ - Only file systems with no filesystem name and no pack name are recognized.
+ (See Coherent "man mkfs" for a description of these features.)
+- SystemV Release 2 FS:
+ The superblock is only searched in the blocks 9, 15, 18, which corresponds to the
+ beginning of track 1 on floppy disks. No support for this FS on hard disk yet.
+
+
+Please report any bugs and suggestions to
+ Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhde.de> or
+ Pascal Haible <haible@izfm.uni-stuttgart.de> .
+
+
+Bruno Haible
+<haible@ma2s2.mathematik.uni-karlsruhe.de>
+
diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c
new file mode 100644
index 000000000..f0fb850be
--- /dev/null
+++ b/fs/sysv/balloc.c
@@ -0,0 +1,329 @@
+/*
+ * linux/fs/sysv/balloc.c
+ *
+ * minix/bitmap.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext/freelists.c
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * xenix/alloc.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/alloc.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/balloc.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing blocks.
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+/* We don't trust the value of
+ sb->sv_sbd2->s_tfree = *sb->sv_sb_total_free_blocks
+ but we nevertheless keep it up to date. */
+
+void sysv_free_block(struct super_block * sb, unsigned int block)
+{
+ struct buffer_head * bh;
+ char * bh_data;
+
+ if (!sb) {
+ printk("sysv_free_block: trying to free block on nonexistent device\n");
+ return;
+ }
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_free_block: trying to free block not in datazone\n");
+ return;
+ }
+ lock_super(sb);
+ if (*sb->sv_sb_flc_count > sb->sv_flc_size) {
+ printk("sysv_free_block: flc_count > flc_size\n");
+ unlock_super(sb);
+ return;
+ }
+ /* If the free list head in super-block is full, it is copied
+ * into this block being freed:
+ */
+ if (*sb->sv_sb_flc_count == sb->sv_flc_size) {
+ unsigned short * flc_count;
+ unsigned long * flc_blocks;
+
+ bh = sv_getblk(sb, sb->s_dev, block);
+ if (!bh) {
+ printk("sysv_free_block: getblk() failed\n");
+ unlock_super(sb);
+ return;
+ }
+ bh_data = bh->b_data;
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV4:
+ flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV2:
+ flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_COH:
+ flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ default: panic("sysv_free_block: invalid fs type\n");
+ }
+ *flc_count = *sb->sv_sb_flc_count; /* = sb->sv_flc_size */
+ memcpy(flc_blocks, sb->sv_sb_flc_blocks, *flc_count * sizeof(sysv_zone_t));
+ mark_buffer_dirty(bh, 1);
+ bh->b_uptodate = 1;
+ brelse(bh);
+ *sb->sv_sb_flc_count = 0;
+ } else
+ /* If the free list head in super-block is empty, create a new head
+ * in this block being freed:
+ */
+ if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */
+ bh = sv_getblk(sb, sb->s_dev, block);
+ if (!bh) {
+ printk("sysv_free_block: getblk() failed\n");
+ unlock_super(sb);
+ return;
+ }
+ memset(bh->b_data, 0, sb->sv_block_size);
+ /* this implies ((struct ..._freelist_chunk *) bh->b_data)->flc_count = 0; */
+ mark_buffer_dirty(bh, 1);
+ bh->b_uptodate = 1;
+ brelse(bh);
+ /* still *sb->sv_sb_flc_count = 0 */
+ } else {
+ /* Throw away block's contents */
+ bh = sv_get_hash_table(sb, sb->s_dev, block);
+ if (bh)
+ bh->b_dirt = 0;
+ brelse(bh);
+ }
+ if (sb->sv_convert)
+ block = to_coh_ulong(block);
+ sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)++] = block;
+ if (sb->sv_convert)
+ *sb->sv_sb_total_free_blocks =
+ to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) + 1);
+ else
+ *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks + 1;
+ mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
+ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
+ sb->s_dirt = 1; /* and needs time stamp */
+ unlock_super(sb);
+}
+
+int sysv_new_block(struct super_block * sb)
+{
+ unsigned int block;
+ struct buffer_head * bh;
+ char * bh_data;
+
+ if (!sb) {
+ printk("sysv_new_block: trying to get new block from nonexistent device\n");
+ return 0;
+ }
+ lock_super(sb);
+ if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */
+ unlock_super(sb);
+ return 0; /* no blocks available */
+ }
+ block = sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)-1];
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (block == 0) { /* Applies only to Xenix FS, SystemV FS */
+ unlock_super(sb);
+ return 0; /* no blocks available */
+ }
+ (*sb->sv_sb_flc_count)--;
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_new_block: new block %d is not in data zone\n",block);
+ unlock_super(sb);
+ return 0;
+ }
+ if (*sb->sv_sb_flc_count == 0) { /* the last block continues the free list */
+ unsigned short * flc_count;
+ unsigned long * flc_blocks;
+
+ if (!(bh = sv_bread(sb, sb->s_dev, block))) {
+ printk("sysv_new_block: cannot read free-list block\n");
+ /* retry this same block next time */
+ (*sb->sv_sb_flc_count)++;
+ unlock_super(sb);
+ return 0;
+ }
+ bh_data = bh->b_data;
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV4:
+ flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV2:
+ flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_COH:
+ flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ default: panic("sysv_new_block: invalid fs type\n");
+ }
+ if (*flc_count > sb->sv_flc_size) {
+ printk("sysv_new_block: free-list block with >flc_size entries\n");
+ brelse(bh);
+ unlock_super(sb);
+ return 0;
+ }
+ *sb->sv_sb_flc_count = *flc_count;
+ memcpy(sb->sv_sb_flc_blocks, flc_blocks, *flc_count * sizeof(sysv_zone_t));
+ brelse(bh);
+ }
+ /* Now the free list head in the superblock is valid again. */
+ bh = sv_getblk(sb, sb->s_dev, block);
+ if (!bh) {
+ printk("sysv_new_block: getblk() failed\n");
+ unlock_super(sb);
+ return 0;
+ }
+ if (bh->b_count != 1) {
+ printk("sysv_new_block: block already in use\n");
+ unlock_super(sb);
+ return 0;
+ }
+ memset(bh->b_data, 0, sb->sv_block_size);
+ mark_buffer_dirty(bh, 1);
+ bh->b_uptodate = 1;
+ brelse(bh);
+ if (sb->sv_convert)
+ *sb->sv_sb_total_free_blocks =
+ to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) - 1);
+ else
+ *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks - 1;
+ mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
+ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
+ sb->s_dirt = 1; /* and needs time stamp */
+ unlock_super(sb);
+ return block;
+}
+
+unsigned long sysv_count_free_blocks(struct super_block * sb)
+{
+#if 1 /* test */
+ int count, old_count;
+ unsigned int block;
+ struct buffer_head * bh;
+ char * bh_data;
+ int i;
+
+ /* this causes a lot of disk traffic ... */
+ count = 0;
+ lock_super(sb);
+ if (*sb->sv_sb_flc_count > 0) {
+ for (i = *sb->sv_sb_flc_count ; /* i > 0 */ ; ) {
+ block = sb->sv_sb_flc_blocks[--i];
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (block == 0) /* block 0 terminates list */
+ goto done;
+ count++;
+ if (i == 0)
+ break;
+ }
+ /* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */
+ while (1) {
+ unsigned short * flc_count;
+ unsigned long * flc_blocks;
+
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_count_free_blocks: new block %d is not in data zone\n",block);
+ break;
+ }
+ if (!(bh = sv_bread(sb, sb->s_dev, block))) {
+ printk("sysv_count_free_blocks: cannot read free-list block\n");
+ break;
+ }
+ bh_data = bh->b_data;
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV4:
+ flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV2:
+ flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_COH:
+ flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ default: panic("sysv_count_free_blocks: invalid fs type\n");
+ }
+ if (*flc_count > sb->sv_flc_size) {
+ printk("sysv_count_free_blocks: free-list block with >flc_size entries\n");
+ brelse(bh);
+ break;
+ }
+ if (*flc_count == 0) { /* Applies only to Coherent FS */
+ brelse(bh);
+ break;
+ }
+ for (i = *flc_count ; /* i > 0 */ ; ) {
+ block = flc_blocks[--i];
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (block == 0) /* block 0 terminates list */
+ break;
+ count++;
+ if (i == 0)
+ break;
+ }
+ /* block = flc_blocks[0], the last block continues the free list */
+ brelse(bh);
+ if (block == 0) /* Applies only to Xenix FS and SystemV FS */
+ break;
+ }
+ done: ;
+ }
+ old_count = *sb->sv_sb_total_free_blocks;
+ if (sb->sv_convert)
+ old_count = from_coh_ulong(old_count);
+ if (count != old_count) {
+ printk("sysv_count_free_blocks: free block count was %d, correcting to %d\n",old_count,count);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ *sb->sv_sb_total_free_blocks = (sb->sv_convert ? to_coh_ulong(count) : count);
+ mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ }
+ }
+ unlock_super(sb);
+ return count;
+#else
+ int count;
+
+ count = *sb->sv_sb_total_free_blocks;
+ if (sb->sv_convert)
+ count = from_coh_ulong(count);
+ return count;
+#endif
+}
+
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c
new file mode 100644
index 000000000..a4b019228
--- /dev/null
+++ b/fs/sysv/dir.c
@@ -0,0 +1,144 @@
+/*
+ * linux/fs/sysv/dir.c
+ *
+ * minix/dir.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/dir.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/dir.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent directory handling functions
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+3) & ~3)
+
+static int sysv_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ return -EISDIR;
+}
+
+static int sysv_readdir(struct inode *, struct file *, struct dirent *, int);
+
+static struct file_operations sysv_dir_operations = {
+ NULL, /* lseek - default */
+ sysv_dir_read, /* read */
+ NULL, /* write - bad */
+ sysv_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync /* default fsync */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations sysv_dir_inode_operations = {
+ &sysv_dir_operations, /* default directory file-ops */
+ sysv_create, /* create */
+ sysv_lookup, /* lookup */
+ sysv_link, /* link */
+ sysv_unlink, /* unlink */
+ sysv_symlink, /* symlink */
+ sysv_mkdir, /* mkdir */
+ sysv_rmdir, /* rmdir */
+ sysv_mknod, /* mknod */
+ sysv_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ sysv_truncate, /* truncate */
+ NULL /* permission */
+};
+
+static int sysv_readdir1 (struct inode * inode, struct file * filp,
+ struct dirent * dirent)
+{
+ struct super_block * sb;
+ unsigned int offset,i;
+ char c;
+ struct buffer_head * bh;
+ char* bh_data;
+ struct sysv_dir_entry * de;
+
+ if (!inode || !(sb = inode->i_sb) || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ if ((unsigned long)(filp->f_pos) % SYSV_DIRSIZE)
+ return -EBADF;
+ while (filp->f_pos < inode->i_size) {
+ offset = filp->f_pos & sb->sv_block_size_1;
+ bh = sysv_file_bread(inode, filp->f_pos >> sb->sv_block_size_bits, 0);
+ if (!bh) {
+ filp->f_pos += sb->sv_block_size - offset;
+ continue;
+ }
+ bh_data = bh->b_data;
+ while (offset < sb->sv_block_size && filp->f_pos < inode->i_size) {
+ de = (struct sysv_dir_entry *) (offset + bh_data);
+ offset += SYSV_DIRSIZE;
+ filp->f_pos += SYSV_DIRSIZE;
+ if (de->inode) {
+ struct sysv_dir_entry sde;
+
+ /* Copy the directory entry first, because the directory
+ * might be modified while we sleep in put_fs_byte...
+ */
+ memcpy(&sde, de, sizeof(struct sysv_dir_entry));
+
+ for (i = 0; i < SYSV_NAMELEN; i++)
+ if ((c = sde.name[i]) != 0)
+ put_fs_byte(c,i+dirent->d_name);
+ else
+ break;
+ if (i) {
+ if (sde.inode > inode->i_sb->sv_ninodes)
+ printk("sysv_readdir: Bad inode number on dev 0x%04x, ino %ld, offset 0x%04lx: %d is out of range\n",
+ inode->i_dev, inode->i_ino, (off_t) filp->f_pos - SYSV_DIRSIZE, sde.inode);
+ put_fs_long(sde.inode,&dirent->d_ino);
+ put_fs_byte(0,i+dirent->d_name);
+ put_fs_word(i,&dirent->d_reclen);
+ brelse(bh);
+ return ROUND_UP(NAME_OFFSET(dirent)+i+1);
+ }
+ }
+ }
+ brelse(bh);
+ }
+ return 0;
+}
+
+static int sysv_readdir(struct inode * inode, struct file * filp,
+ struct dirent * dirent, int count)
+{
+ int retval, stored;
+
+ /* compatibility */
+ if (count==1)
+ return sysv_readdir1(inode,filp,dirent);
+
+ stored = 0;
+ while (count >= sizeof(struct dirent)) {
+ retval = sysv_readdir1(inode,filp,dirent);
+ if (retval < 0)
+ return retval;
+ if (!retval)
+ return stored;
+ dirent = (struct dirent *)((char *) dirent + retval);
+ stored += retval;
+ count -= retval;
+ }
+ return stored;
+}
diff --git a/fs/sysv/file.c b/fs/sysv/file.c
new file mode 100644
index 000000000..27f82d51a
--- /dev/null
+++ b/fs/sysv/file.c
@@ -0,0 +1,263 @@
+/*
+ * linux/fs/sysv/file.c
+ *
+ * minix/file.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/file.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/file.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent regular file handling primitives
+ */
+
+#include <asm/segment.h>
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.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/sysv_fs.h>
+
+static int sysv_file_write(struct inode *, struct file *, char *, int);
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the coh filesystem.
+ */
+static struct file_operations sysv_file_operations = {
+ NULL, /* lseek - default */
+ sysv_file_read, /* read */
+ sysv_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ sysv_sync_file /* fsync */
+};
+
+struct inode_operations sysv_file_inode_operations = {
+ &sysv_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 */
+ sysv_bmap, /* bmap */
+ sysv_truncate, /* truncate */
+ NULL /* permission */
+};
+
+int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ struct super_block * sb = inode->i_sb;
+ int read,left,chars;
+ unsigned int block;
+ int blocks, offset;
+ int bhrequest, uptodate;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct buffer_head * buflist[NBUF];
+ unsigned int size;
+
+ if (!inode) {
+ printk("sysv_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("sysv_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ offset = filp->f_pos;
+ size = inode->i_size;
+ if (offset > size)
+ left = 0;
+ else
+ left = size - offset;
+ if (left > count)
+ left = count;
+ if (left <= 0)
+ return 0;
+ read = 0;
+ block = offset >> sb->sv_block_size_bits;
+ offset &= sb->sv_block_size_1;
+ size = (size + sb->sv_block_size_1) >> sb->sv_block_size_bits;
+ blocks = (left + offset + sb->sv_block_size_1) >> sb->sv_block_size_bits;
+ bhb = bhe = buflist;
+ if (filp->f_reada) {
+ blocks += read_ahead[MAJOR(inode->i_dev)] >> (sb->sv_block_size_bits - 9);
+ if (block + blocks > size)
+ blocks = size - block;
+ }
+
+ /* We do this in a two stage process. We first try and request
+ as many blocks as we can, then we wait for the first one to
+ complete, and then we try and wrap up as many as are actually
+ done. This routine is rather generic, in that it can be used
+ in a filesystem by substituting the appropriate function in
+ for getblk.
+
+ This routine is optimized to make maximum use of the various
+ buffers and caches.
+ */
+
+ do {
+ bhrequest = 0;
+ uptodate = 1;
+ while (blocks) {
+ --blocks;
+ *bhb = sysv_getblk(inode, block++, 0);
+ if (*bhb && !(*bhb)->b_uptodate) {
+ uptodate = 0;
+ bhreq[bhrequest++] = *bhb;
+ }
+
+ if (++bhb == &buflist[NBUF])
+ bhb = buflist;
+
+ /* If the block we have on hand is uptodate, go ahead
+ and complete processing. */
+ if (uptodate)
+ break;
+ if (bhb == bhe)
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest)
+ ll_rw_block(READ, bhrequest, bhreq);
+
+ do { /* Finish off all I/O that has actually completed */
+ if (*bhe) {
+ wait_on_buffer(*bhe);
+ if (!(*bhe)->b_uptodate) { /* read error? */
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ left = 0;
+ break;
+ }
+ }
+ if (left < sb->sv_block_size - offset)
+ chars = left;
+ else
+ chars = sb->sv_block_size - offset;
+ filp->f_pos += chars;
+ left -= chars;
+ read += chars;
+ if (*bhe) {
+ memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ brelse(*bhe);
+ buf += chars;
+ } else {
+ while (chars-- > 0)
+ put_fs_byte(0,buf++);
+ }
+ offset = 0;
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0);
+
+/* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ };
+ if (!read)
+ return -EIO;
+ filp->f_reada = 1;
+ if (!IS_RDONLY(inode))
+ inode->i_atime = CURRENT_TIME;
+ return read;
+}
+
+static int sysv_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ struct super_block * sb = inode->i_sb;
+ off_t pos;
+ int written,c;
+ struct buffer_head * bh;
+ char * p;
+
+ if (!inode) {
+ printk("sysv_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("sysv_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ * But we need to protect against simultaneous truncate as we may end up
+ * writing our data into blocks that have meanwhile been incorporated into
+ * the freelist, thereby trashing the freelist.
+ */
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+ written = 0;
+ while (written<count) {
+ bh = sysv_getblk (inode, pos >> sb->sv_block_size_bits, 1);
+ if (!bh) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = sb->sv_block_size - (pos & sb->sv_block_size_1);
+ if (c > count-written)
+ c = count-written;
+ if (c != sb->sv_block_size && !bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ /* now either c==sb->sv_block_size or bh->b_uptodate */
+ p = (pos & sb->sv_block_size_1) + bh->b_data;
+ pos += c;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_dirt = 1;
+ }
+ written += c;
+ memcpy_fromfs(p,buf,c);
+ buf += c;
+ bh->b_uptodate = 1;
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ filp->f_pos = pos;
+ inode->i_dirt = 1;
+ return written;
+}
diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c
new file mode 100644
index 000000000..9e105077d
--- /dev/null
+++ b/fs/sysv/fsync.c
@@ -0,0 +1,197 @@
+/*
+ * linux/fs/sysv/fsync.c
+ *
+ * minix/fsync.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ * coh/fsync.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/fsync.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent fsync primitive
+ */
+
+#include <linux/errno.h>
+#include <linux/stat.h>
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+
+
+/* return values: 0 means OK/done, 1 means redo, -1 means I/O error. */
+
+/* Sync one block. The block number is
+ * from_coh_ulong(*blockp) if convert=1, *blockp if convert=0.
+ */
+static int sync_block (struct inode * inode, unsigned long * blockp, int convert, int wait)
+{
+ struct buffer_head * bh;
+ unsigned long tmp, block;
+ struct super_block * sb;
+
+ block = tmp = *blockp;
+ if (convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ return 0;
+ sb = inode->i_sb;
+ bh = sv_get_hash_table(sb, inode->i_dev, block);
+ if (!bh)
+ return 0;
+ if (*blockp != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && bh->b_req && !bh->b_uptodate) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !bh->b_uptodate || !bh->b_dirt) {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ return 0;
+}
+
+/* Sync one block full of indirect pointers and read it because we'll need it. */
+static int sync_iblock (struct inode * inode, unsigned long * iblockp, int convert,
+ struct buffer_head * *bh, int wait)
+{
+ int rc;
+ unsigned long tmp, block;
+
+ *bh = NULL;
+ block = tmp = *iblockp;
+ if (convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ return 0;
+ rc = sync_block (inode, iblockp, convert, wait);
+ if (rc)
+ return rc;
+ *bh = sv_bread(inode->i_sb, inode->i_dev, block);
+ if (tmp != *iblockp) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < 10; i++) {
+ rc = sync_block (inode, inode->u.sysv_i.i_data + i, 0, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned long *iblockp, int convert, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ int rc, err = 0;
+ struct super_block * sb;
+
+ rc = sync_iblock (inode, iblockp, convert, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ sb = inode->i_sb;
+ for (i = 0; i < sb->sv_ind_per_block; i++) {
+ rc = sync_block (inode,
+ ((unsigned long *) ind_bh->b_data) + i, sb->sv_convert,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int convert,
+ int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ int rc, err = 0;
+ struct super_block * sb;
+
+ rc = sync_iblock (inode, diblockp, convert, &dind_bh, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ sb = inode->i_sb;
+ for (i = 0; i < sb->sv_ind_per_block; i++) {
+ rc = sync_indirect (inode,
+ ((unsigned long *) dind_bh->b_data) + i, sb->sv_convert,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int convert,
+ int wait)
+{
+ int i;
+ struct buffer_head * tind_bh;
+ int rc, err = 0;
+ struct super_block * sb;
+
+ rc = sync_iblock (inode, tiblockp, convert, &tind_bh, wait);
+ if (rc || !tind_bh)
+ return rc;
+
+ sb = inode->i_sb;
+ for (i = 0; i < sb->sv_ind_per_block; i++) {
+ rc = sync_dindirect (inode,
+ ((unsigned long *) tind_bh->b_data) + i, sb->sv_convert,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(tind_bh);
+ return err;
+}
+
+int sysv_sync_file(struct inode * inode, struct file * file)
+{
+ int wait, err = 0;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+
+ for (wait=0; wait<=1; wait++) {
+ err |= sync_direct(inode, wait);
+ err |= sync_indirect(inode, inode->u.sysv_i.i_data+10, 0, wait);
+ err |= sync_dindirect(inode, inode->u.sysv_i.i_data+11, 0, wait);
+ err |= sync_tindirect(inode, inode->u.sysv_i.i_data+12, 0, wait);
+ }
+ err |= sysv_sync_inode (inode);
+ return (err < 0) ? -EIO : 0;
+}
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
new file mode 100644
index 000000000..f87009100
--- /dev/null
+++ b/fs/sysv/ialloc.c
@@ -0,0 +1,218 @@
+/*
+ * linux/fs/sysv/ialloc.c
+ *
+ * minix/bitmap.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext/freelists.c
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * xenix/alloc.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/alloc.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/ialloc.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing inodes.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stddef.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+/* We don't trust the value of
+ sb->sv_sbd2->s_tinode = *sb->sv_sb_total_free_inodes
+ but we nevertheless keep it up to date. */
+
+/* An inode on disk is considered free if both i_mode == 0 and i_nlink == 0. */
+
+/* return &sb->sv_sb_fic_inodes[i] = &sbd->s_inode[i]; */
+static inline sysv_ino_t * sv_sb_fic_inode (struct super_block * sb, unsigned int i)
+{
+ if (sb->sv_bh1 == sb->sv_bh2)
+ return &sb->sv_sb_fic_inodes[i];
+ else {
+ /* 512 byte Xenix FS */
+ unsigned int offset = offsetof(struct xenix_super_block, s_inode[i]);
+ if (offset < 512)
+ return (sysv_ino_t*)(sb->sv_sbd1 + offset);
+ else
+ return (sysv_ino_t*)(sb->sv_sbd2 + offset);
+ }
+}
+
+void sysv_free_inode(struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned int ino;
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+
+ if (!inode)
+ return;
+ if (!inode->i_dev) {
+ printk("sysv_free_inode: inode has no device\n");
+ return;
+ }
+ if (inode->i_count != 1) {
+ printk("sysv_free_inode: inode has count=%d\n", inode->i_count);
+ return;
+ }
+ if (inode->i_nlink) {
+ printk("sysv_free_inode: inode has nlink=%d\n", inode->i_nlink);
+ return;
+ }
+ if (!(sb = inode->i_sb)) {
+ printk("sysv_free_inode: inode on nonexistent device\n");
+ return;
+ }
+ ino = inode->i_ino;
+ if (ino <= SYSV_ROOT_INO || ino > sb->sv_ninodes) {
+ printk("sysv_free_inode: inode 0,1,2 or nonexistent inode\n");
+ return;
+ }
+ if (!(bh = sv_bread(sb, inode->i_dev, sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits)))) {
+ printk("sysv_free_inode: unable to read inode block on device %d/%d\n",MAJOR(inode->i_dev),MINOR(inode->i_dev));
+ clear_inode(inode);
+ return;
+ }
+ raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1);
+ lock_super(sb);
+ if (*sb->sv_sb_fic_count < sb->sv_fic_size)
+ *sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)++) = ino;
+ (*sb->sv_sb_total_free_inodes)++;
+ mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
+ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
+ sb->s_dirt = 1; /* and needs time stamp */
+ memset(raw_inode, 0, sizeof(struct sysv_inode));
+ mark_buffer_dirty(bh, 1);
+ unlock_super(sb);
+ brelse(bh);
+ clear_inode(inode);
+}
+
+struct inode * sysv_new_inode(const struct inode * dir)
+{
+ struct inode * inode;
+ struct super_block * sb;
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ int i,j,ino,block;
+
+ if (!dir || !(inode = get_empty_inode()))
+ return NULL;
+ sb = dir->i_sb;
+ inode->i_sb = sb;
+ inode->i_flags = inode->i_sb->s_flags;
+ lock_super(sb); /* protect against task switches */
+ if ((*sb->sv_sb_fic_count == 0)
+ || (*sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)-1) == 0) /* Applies only to SystemV2 FS */
+ ) {
+ /* Rebuild cache of free inodes: */
+ /* i : index into cache slot being filled */
+ /* ino : inode we are trying */
+ /* block : firstinodezone + (ino-1)/inodes_per_block */
+ /* j : (ino-1)%inodes_per_block */
+ /* bh : buffer for block */
+ /* raw_inode : pointer to inode ino in the block */
+ for (i = 0, ino = SYSV_ROOT_INO+1, block = sb->sv_firstinodezone, j = SYSV_ROOT_INO ; i < sb->sv_fic_size && block < sb->sv_firstdatazone ; block++, j = 0) {
+ if (!(bh = sv_bread(sb, sb->s_dev, block))) {
+ printk("sysv_new_inode: unable to read inode table\n");
+ break; /* go with what we've got */
+ /* FIXME: Perhaps try the next block? */
+ }
+ raw_inode = (struct sysv_inode *) bh->b_data + j;
+ for (; j < sb->sv_inodes_per_block && i < sb->sv_fic_size; ino++, j++, raw_inode++) {
+ if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0)
+ *sv_sb_fic_inode(sb,i++) = ino;
+ }
+ brelse(bh);
+ }
+ if (i == 0) {
+ iput(inode);
+ unlock_super(sb);
+ return NULL; /* no inodes available */
+ }
+ *sb->sv_sb_fic_count = i;
+ }
+ /* Now *sb->sv_sb_fic_count > 0. */
+ ino = *sv_sb_fic_inode(sb,--(*sb->sv_sb_fic_count));
+ mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
+ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
+ sb->s_dirt = 1; /* and needs time stamp */
+ inode->i_count = 1;
+ inode->i_nlink = 1;
+ inode->i_dev = sb->s_dev;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ inode->i_dirt = 1;
+ inode->i_ino = ino;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_op = NULL;
+ inode->i_blocks = inode->i_blksize = 0;
+ insert_inode_hash(inode);
+ /* Change directory entry: */
+ inode->i_mode = 0; /* for sysv_write_inode() */
+ inode->i_size = 0; /* ditto */
+ sysv_write_inode(inode); /* ensure inode not allocated again */
+ /* FIXME: caller may call this too. */
+ inode->i_dirt = 1; /* cleared by sysv_write_inode() */
+ /* That's it. */
+ (*sb->sv_sb_total_free_inodes)--;
+ mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */
+ sb->s_dirt = 1; /* and needs time stamp again */
+ unlock_super(sb);
+ return inode;
+}
+
+unsigned long sysv_count_free_inodes(struct super_block * sb)
+{
+#if 1 /* test */
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ int j,block,count;
+
+ /* this causes a lot of disk traffic ... */
+ count = 0;
+ lock_super(sb);
+ /* i : index into cache slot being filled */
+ /* ino : inode we are trying */
+ /* block : firstinodezone + (ino-1)/inodes_per_block */
+ /* j : (ino-1)%inodes_per_block */
+ /* bh : buffer for block */
+ /* raw_inode : pointer to inode ino in the block */
+ for (block = sb->sv_firstinodezone, j = SYSV_ROOT_INO ; block < sb->sv_firstdatazone ; block++, j = 0) {
+ if (!(bh = sv_bread(sb, sb->s_dev, block))) {
+ printk("sysv_count_free_inodes: unable to read inode table\n");
+ break; /* go with what we've got */
+ /* FIXME: Perhaps try the next block? */
+ }
+ raw_inode = (struct sysv_inode *) bh->b_data + j;
+ for (; j < sb->sv_inodes_per_block ; j++, raw_inode++)
+ if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0)
+ count++;
+ brelse(bh);
+ }
+ if (count != *sb->sv_sb_total_free_inodes) {
+ printk("sysv_count_free_inodes: free inode count was %d, correcting to %d\n",(short)(*sb->sv_sb_total_free_inodes),count);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ *sb->sv_sb_total_free_inodes = count;
+ mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ }
+ }
+ unlock_super(sb);
+ return count;
+#else
+ return *sb->sv_sb_total_free_inodes;
+#endif
+}
+
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
new file mode 100644
index 000000000..0b61ae7c7
--- /dev/null
+++ b/fs/sysv/inode.c
@@ -0,0 +1,951 @@
+/*
+ * linux/fs/sysv/inode.c
+ *
+ * minix/inode.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * xenix/inode.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/inode.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Paul B. Monday
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing inodes and for read/writing
+ * the superblock.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#include <asm/segment.h>
+
+void sysv_put_inode(struct inode *inode)
+{
+ if (inode->i_nlink)
+ return;
+ inode->i_size = 0;
+ sysv_truncate(inode);
+ sysv_free_inode(inode);
+}
+
+
+static struct super_operations sysv_sops = {
+ sysv_read_inode,
+ sysv_notify_change,
+ sysv_write_inode,
+ sysv_put_inode,
+ sysv_put_super,
+ sysv_write_super,
+ sysv_statfs,
+ NULL
+};
+
+/* The following functions try to recognize specific filesystems.
+ * We recognize:
+ * - Xenix FS by its magic number.
+ * - SystemV FS by its magic number.
+ * - Coherent FS by its funny fname/fpack field.
+ * We discriminate among SystemV4 and SystemV2 FS by the assumption that
+ * the time stamp is not < 01-01-1980.
+ */
+
+static void detected_bs512 (struct super_block *sb)
+{
+ sb->sv_block_size = 512;
+ sb->sv_block_size_1 = 512-1;
+ sb->sv_block_size_bits = 9;
+ sb->sv_block_size_ratio = 2;
+ sb->sv_block_size_ratio_bits = 1;
+ sb->sv_inodes_per_block = 512/64;
+ sb->sv_inodes_per_block_1 = 512/64-1;
+ sb->sv_inodes_per_block_bits = 9-6;
+ sb->sv_toobig_block = 10 +
+ (sb->sv_ind_per_block = 512/4) +
+ (sb->sv_ind_per_block_2 = (512/4)*(512/4)) +
+ (sb->sv_ind_per_block_3 = (512/4)*(512/4)*(512/4));
+ sb->sv_ind_per_block_1 = 512/4-1;
+ sb->sv_ind_per_block_2_1 = (512/4)*(512/4)-1;
+ sb->sv_ind_per_block_2_bits = 2 *
+ (sb->sv_ind_per_block_bits = 9-2);
+ sb->sv_ind_per_block_block_size_1 = (512/4)*512-1;
+ sb->sv_ind_per_block_block_size_bits = (9-2)+9;
+ sb->sv_ind_per_block_2_block_size_1 = (512/4)*(512/4)*512-1;
+ sb->sv_ind_per_block_2_block_size_bits = (9-2)+(9-2)+9;
+ sb->sv_ind0_size = 10 * 512;
+ sb->sv_ind1_size = (10 + (512/4))* 512;
+ sb->sv_ind2_size = (10 + (512/4) + (512/4)*(512/4)) * 512;
+}
+
+static void detected_bs1024 (struct super_block *sb)
+{
+ sb->sv_block_size = 1024;
+ sb->sv_block_size_1 = 1024-1;
+ sb->sv_block_size_bits = 10;
+ sb->sv_block_size_ratio = 1;
+ sb->sv_block_size_ratio_bits = 0;
+ sb->sv_inodes_per_block = 1024/64;
+ sb->sv_inodes_per_block_1 = 1024/64-1;
+ sb->sv_inodes_per_block_bits = 10-6;
+ sb->sv_toobig_block = 10 +
+ (sb->sv_ind_per_block = 1024/4) +
+ (sb->sv_ind_per_block_2 = (1024/4)*(1024/4)) +
+ (sb->sv_ind_per_block_3 = (1024/4)*(1024/4)*(1024/4));
+ sb->sv_ind_per_block_1 = 1024/4-1;
+ sb->sv_ind_per_block_2_1 = (1024/4)*(1024/4)-1;
+ sb->sv_ind_per_block_2_bits = 2 *
+ (sb->sv_ind_per_block_bits = 10-2);
+ sb->sv_ind_per_block_block_size_1 = (1024/4)*1024-1;
+ sb->sv_ind_per_block_block_size_bits = (10-2)+10;
+ sb->sv_ind_per_block_2_block_size_1 = (1024/4)*(1024/4)*1024-1;
+ sb->sv_ind_per_block_2_block_size_bits = (10-2)+(10-2)+10;
+ sb->sv_ind0_size = 10 * 1024;
+ sb->sv_ind1_size = (10 + (1024/4))* 1024;
+ sb->sv_ind2_size = (10 + (1024/4) + (1024/4)*(1024/4)) * 1024;
+}
+
+static const char* detect_xenix (struct super_block *sb, struct buffer_head *bh)
+{
+ struct xenix_super_block * sbd;
+
+ sbd = (struct xenix_super_block *) bh->b_data;
+ if (sbd->s_magic != 0x2b5544)
+ return NULL;
+ switch (sbd->s_type) {
+ case 1: detected_bs512(sb); break;
+ case 2: detected_bs1024(sb); break;
+ default: return NULL;
+ }
+ sb->sv_type = FSTYPE_XENIX;
+ return "Xenix";
+}
+static struct super_block * detected_xenix (struct super_block *sb, struct buffer_head *bh1, struct buffer_head *bh2)
+{
+ struct xenix_super_block * sbd1;
+ struct xenix_super_block * sbd2;
+
+ if (sb->sv_block_size == BLOCK_SIZE)
+ /* block size = 1024, so bh1 = bh2 */
+ sbd1 = sbd2 = (struct xenix_super_block *) bh1->b_data;
+ else {
+ /* block size = 512, so bh1 != bh2 */
+ sbd1 = (struct xenix_super_block *) bh1->b_data;
+ sbd2 = (struct xenix_super_block *) (bh2->b_data - BLOCK_SIZE/2);
+ /* sanity check */
+ if (sbd2->s_magic != 0x2b5544)
+ return NULL;
+ }
+
+ sb->sv_convert = 0;
+ sb->sv_kludge_symlinks = 1;
+ sb->sv_truncate = 1;
+ sb->sv_link_max = XENIX_LINK_MAX;
+ sb->sv_fic_size = XENIX_NICINOD;
+ sb->sv_flc_size = XENIX_NICFREE;
+ sb->sv_bh1 = bh1;
+ sb->sv_bh2 = bh2;
+ sb->sv_sbd1 = (char *) sbd1;
+ sb->sv_sbd2 = (char *) sbd2;
+ sb->sv_sb_fic_count = &sbd1->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd1->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd2->s_tinode;
+ sb->sv_sb_flc_count = &sbd1->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd1->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd2->s_tfree;
+ sb->sv_sb_time = &sbd2->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd1->s_isize;
+ sb->sv_nzones = sbd1->s_fsize;
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ return sb;
+}
+
+static const char* detect_sysv4 (struct super_block *sb, struct buffer_head *bh)
+{
+ struct sysv4_super_block * sbd;
+
+ sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if (sbd->s_magic != 0xfd187e20)
+ return NULL;
+ if (sbd->s_time < 315532800) /* this is likely to happen on SystemV2 FS */
+ return NULL;
+ switch (sbd->s_type) {
+ case 1: detected_bs512(sb); break;
+ case 2: detected_bs1024(sb); break;
+ default: return NULL;
+ }
+ sb->sv_type = FSTYPE_SYSV4;
+ return "SystemV";
+}
+static struct super_block * detected_sysv4 (struct super_block *sb, struct buffer_head *bh)
+{
+ struct sysv4_super_block * sbd;
+
+ if (sb->sv_block_size == BLOCK_SIZE)
+ sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ else {
+ sbd = (struct sysv4_super_block *) bh->b_data;
+ /* sanity check */
+ if (sbd->s_magic != 0xfd187e20)
+ return NULL;
+ if (sbd->s_time < 315532800)
+ return NULL;
+ }
+
+ sb->sv_convert = 0;
+ sb->sv_kludge_symlinks = 0; /* ?? */
+ sb->sv_truncate = 1;
+ sb->sv_link_max = SYSV_LINK_MAX;
+ sb->sv_fic_size = SYSV_NICINOD;
+ sb->sv_flc_size = SYSV_NICFREE;
+ sb->sv_bh1 = bh;
+ sb->sv_bh2 = bh;
+ sb->sv_sbd1 = (char *) sbd;
+ sb->sv_sbd2 = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_sb_flc_count = &sbd->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd->s_isize;
+ sb->sv_nzones = sbd->s_fsize;
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ return sb;
+}
+
+static const char* detect_sysv2 (struct super_block *sb, struct buffer_head *bh)
+{
+ struct sysv2_super_block * sbd;
+
+ sbd = (struct sysv2_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if (sbd->s_magic != 0xfd187e20)
+ return NULL;
+ if (sbd->s_time < 315532800) /* this is likely to happen on SystemV4 FS */
+ return NULL;
+ switch (sbd->s_type) {
+ case 1: detected_bs512(sb); break;
+ case 2: detected_bs1024(sb); break;
+ default: return NULL;
+ }
+ sb->sv_type = FSTYPE_SYSV2;
+ return "SystemV Release 2";
+}
+static struct super_block * detected_sysv2 (struct super_block *sb, struct buffer_head *bh)
+{
+ struct sysv2_super_block * sbd;
+
+ if (sb->sv_block_size == BLOCK_SIZE)
+ sbd = (struct sysv2_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ else {
+ sbd = (struct sysv2_super_block *) bh->b_data;
+ /* sanity check */
+ if (sbd->s_magic != 0xfd187e20)
+ return NULL;
+ if (sbd->s_time < 315532800)
+ return NULL;
+ }
+
+ sb->sv_convert = 0;
+ sb->sv_kludge_symlinks = 0; /* ?? */
+ sb->sv_truncate = 1;
+ sb->sv_link_max = SYSV_LINK_MAX;
+ sb->sv_fic_size = SYSV_NICINOD;
+ sb->sv_flc_size = SYSV_NICFREE;
+ sb->sv_bh1 = bh;
+ sb->sv_bh2 = bh;
+ sb->sv_sbd1 = (char *) sbd;
+ sb->sv_sbd2 = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_sb_flc_count = &sbd->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd->s_isize;
+ sb->sv_nzones = sbd->s_fsize;
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ return sb;
+}
+
+static const char* detect_coherent (struct super_block *sb, struct buffer_head *bh)
+{
+ struct coh_super_block * sbd;
+
+ sbd = (struct coh_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
+ || (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
+ return NULL;
+ detected_bs512(sb);
+ sb->sv_type = FSTYPE_COH;
+ return "Coherent";
+}
+static struct super_block * detected_coherent (struct super_block *sb, struct buffer_head *bh)
+{
+ struct coh_super_block * sbd;
+
+ sbd = (struct coh_super_block *) bh->b_data;
+ /* sanity check */
+ if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
+ || (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
+ return NULL;
+
+ sb->sv_convert = 1;
+ sb->sv_kludge_symlinks = 1;
+ sb->sv_truncate = 1;
+ sb->sv_link_max = COH_LINK_MAX;
+ sb->sv_fic_size = COH_NICINOD;
+ sb->sv_flc_size = COH_NICFREE;
+ sb->sv_bh1 = bh;
+ sb->sv_bh2 = bh;
+ sb->sv_sbd1 = (char *) sbd;
+ sb->sv_sbd2 = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_sb_flc_count = &sbd->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd->s_isize;
+ sb->sv_nzones = from_coh_ulong(sbd->s_fsize);
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ return sb;
+}
+
+struct super_block *sysv_read_super(struct super_block *sb,void *data,
+ int silent)
+{
+ struct buffer_head *bh;
+ const char *found;
+ int dev = sb->s_dev;
+
+ if (1024 != sizeof (struct xenix_super_block))
+ panic("Xenix FS: bad super-block size");
+ if ((512 != sizeof (struct sysv4_super_block))
+ || (512 != sizeof (struct sysv2_super_block)))
+ panic("SystemV FS: bad super-block size");
+ if (500 != sizeof (struct coh_super_block))
+ panic("Coherent FS: bad super-block size");
+ if (64 != sizeof (struct sysv_inode))
+ panic("sysv fs: bad i-node size");
+ lock_super(sb);
+ set_blocksize(dev,BLOCK_SIZE);
+
+ /* Try to read Xenix superblock */
+ if ((bh = bread(dev, 1, BLOCK_SIZE)) != NULL) {
+ if ((found = detect_xenix(sb,bh)) != NULL)
+ goto ok;
+ brelse(bh);
+ }
+ if ((bh = bread(dev, 0, BLOCK_SIZE)) != NULL) {
+ /* Try to recognize SystemV superblock */
+ if ((found = detect_sysv4(sb,bh)) != NULL)
+ goto ok;
+ if ((found = detect_sysv2(sb,bh)) != NULL)
+ goto ok;
+ /* Try to recognize Coherent superblock */
+ if ((found = detect_coherent(sb,bh)) != NULL)
+ goto ok;
+ brelse(bh);
+ }
+ /* Try to recognize SystemV superblock */
+ /* Offset by 1 track, i.e. most probably 9, 15, or 18 kilobytes. */
+ { static int offsets[] = { 9, 15, 18, };
+ int i;
+ for (i = 0; i < sizeof(offsets)/sizeof(offsets[0]); i++)
+ if ((bh = bread(dev, offsets[i], BLOCK_SIZE)) != NULL) {
+ /* Try to recognize SystemV superblock */
+ if ((found = detect_sysv4(sb,bh)) != NULL) {
+ sb->sv_block_base = offsets[i] << sb->sv_block_size_ratio_bits;
+ goto ok;
+ }
+ if ((found = detect_sysv2(sb,bh)) != NULL) {
+ sb->sv_block_base = offsets[i] << sb->sv_block_size_ratio_bits;
+ goto ok;
+ }
+ brelse(bh);
+ }
+ }
+ sb->s_dev=0;
+ unlock_super(sb);
+ if (!silent)
+ printk("VFS: unable to read Xenix/SystemV/Coherent superblock on device %d/%d\n",MAJOR(dev),MINOR(dev));
+ return NULL;
+
+ ok:
+ if (sb->sv_block_size == BLOCK_SIZE) {
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ if (!detected_xenix(sb,bh,bh))
+ goto bad_superblock;
+ break;
+ case FSTYPE_SYSV4:
+ if (!detected_sysv4(sb,bh))
+ goto bad_superblock;
+ break;
+ case FSTYPE_SYSV2:
+ if (!detected_sysv2(sb,bh))
+ goto bad_superblock;
+ break;
+ default:
+ bad_superblock:
+ brelse(bh);
+ sb->s_dev = 0;
+ unlock_super(sb);
+ printk("SysV FS: cannot read superblock in 1024 byte mode\n");
+ return NULL;
+ }
+ } else {
+ /* Switch to another block size. Unfortunately, we have to
+ release the 1 KB block bh and read it in two parts again. */
+ struct buffer_head *bh1, *bh2;
+ unsigned long blocknr = bh->b_blocknr << sb->sv_block_size_ratio_bits;
+
+ brelse(bh);
+ set_blocksize(dev,sb->sv_block_size);
+ bh1 = NULL; bh2 = NULL;
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ if ((bh1 = bread(dev, blocknr, sb->sv_block_size)) == NULL)
+ goto bad_superblock2;
+ if ((bh2 = bread(dev, blocknr+1, sb->sv_block_size)) == NULL)
+ goto bad_superblock2;
+ if (!detected_xenix(sb,bh1,bh2))
+ goto bad_superblock2;
+ break;
+ case FSTYPE_SYSV4:
+ if ((bh2 = bread(dev, blocknr+1, sb->sv_block_size)) == NULL)
+ goto bad_superblock2;
+ if (!detected_sysv4(sb,bh2))
+ goto bad_superblock2;
+ break;
+ case FSTYPE_SYSV2:
+ if ((bh2 = bread(dev, blocknr+1, sb->sv_block_size)) == NULL)
+ goto bad_superblock2;
+ if (!detected_sysv2(sb,bh2))
+ goto bad_superblock2;
+ break;
+ case FSTYPE_COH:
+ if ((bh2 = bread(dev, blocknr+1, sb->sv_block_size)) == NULL)
+ goto bad_superblock2;
+ if (!detected_coherent(sb,bh2))
+ goto bad_superblock2;
+ break;
+ default:
+ bad_superblock2:
+ brelse(bh1);
+ brelse(bh2);
+ set_blocksize(sb->s_dev,BLOCK_SIZE);
+ sb->s_dev = 0;
+ unlock_super(sb);
+ printk("SysV FS: cannot read superblock in 512 byte mode\n");
+ return NULL;
+ }
+ }
+ sb->sv_ninodes = (sb->sv_firstdatazone - sb->sv_firstinodezone) << sb->sv_inodes_per_block_bits;
+ if (!silent)
+ printk("VFS: Found a %s FS (block size = %d) on device %d/%d\n",found,sb->sv_block_size,MAJOR(dev),MINOR(dev));
+ sb->s_magic = SYSV_MAGIC_BASE + sb->sv_type;
+ /* The buffer code now supports block size 512 as well as 1024. */
+ sb->s_blocksize = sb->sv_block_size;
+ sb->s_blocksize_bits = sb->sv_block_size_bits;
+ /* set up enough so that it can read an inode */
+ sb->s_dev = dev;
+ sb->s_op = &sysv_sops;
+ sb->s_mounted = iget(sb,SYSV_ROOT_INO);
+ unlock_super(sb);
+ if (!sb->s_mounted) {
+ sysv_put_super(sb);
+ printk("SysV FS: get root inode failed\n");
+ return NULL;
+ }
+ sb->s_dirt = 1;
+ /* brelse(bh); resp. brelse(bh1); brelse(bh2);
+ occurs when the disk is unmounted. */
+ return sb;
+}
+
+/* This is only called on sync() and umount(), when s_dirt=1. */
+void sysv_write_super (struct super_block *sb)
+{
+ lock_super(sb);
+ if (sb->sv_bh1->b_dirt || sb->sv_bh2->b_dirt) {
+ /* If we are going to write out the super block,
+ then attach current time stamp. */
+ unsigned long time = CURRENT_TIME;
+ if (sb->sv_convert)
+ time = to_coh_ulong(time);
+ *sb->sv_sb_time = time;
+ mark_buffer_dirty(sb->sv_bh2, 1);
+ }
+ sb->s_dirt = 0;
+ unlock_super(sb);
+}
+
+void sysv_put_super(struct super_block *sb)
+{
+ /* we can assume sysv_write_super() has already been called */
+ lock_super(sb);
+ brelse(sb->sv_bh1);
+ if (sb->sv_bh1 != sb->sv_bh2) brelse(sb->sv_bh2);
+ /* switch back to default block size */
+ if (sb->s_blocksize != BLOCK_SIZE)
+ set_blocksize(sb->s_dev,BLOCK_SIZE);
+ sb->s_dev = 0;
+ unlock_super(sb);
+}
+
+void sysv_statfs(struct super_block *sb, struct statfs *buf)
+{
+ long tmp;
+
+ put_fs_long(sb->s_magic, &buf->f_type); /* type of filesystem */
+ put_fs_long(sb->sv_block_size, &buf->f_bsize); /* block size */
+ put_fs_long(sb->sv_ndatazones, &buf->f_blocks); /* total data blocks in file system */
+ tmp = sysv_count_free_blocks(sb);
+ put_fs_long(tmp, &buf->f_bfree); /* free blocks in fs */
+ put_fs_long(tmp, &buf->f_bavail); /* free blocks available to non-superuser */
+ put_fs_long(sb->sv_ninodes, &buf->f_files); /* total file nodes in file system */
+ put_fs_long(sysv_count_free_inodes(sb), &buf->f_ffree); /* free file nodes in fs */
+ put_fs_long(SYSV_NAMELEN, &buf->f_namelen);
+ /* Don't know what value to put in buf->f_fsid */ /* file system id */
+}
+
+
+/* bmap support for running executables and shared libraries. */
+
+static inline int inode_bmap(struct super_block * sb, struct inode * inode, int nr)
+{
+ int tmp = inode->u.sysv_i.i_data[nr];
+ if (!tmp)
+ return 0;
+ return tmp + sb->sv_block_base;
+}
+
+static int block_bmap(struct super_block * sb, struct buffer_head * bh, int nr, int convert)
+{
+ int tmp;
+
+ if (!bh)
+ return 0;
+ tmp = ((sysv_zone_t *) bh->b_data) [nr];
+ if (convert)
+ tmp = from_coh_ulong(tmp);
+ brelse(bh);
+ if (!tmp)
+ return 0;
+ return tmp + sb->sv_block_base;
+}
+
+int sysv_bmap(struct inode * inode,int block_nr)
+{
+ unsigned int block = block_nr;
+ struct super_block * sb = inode->i_sb;
+ int convert;
+ int i;
+ struct buffer_head * bh;
+
+ if (block < 10)
+ return inode_bmap(sb,inode,block);
+ block -= 10;
+ convert = sb->sv_convert;
+ if (block < sb->sv_ind_per_block) {
+ i = inode_bmap(sb,inode,10);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,sb->sv_block_size);
+ return block_bmap(sb, bh, block, convert);
+ }
+ block -= sb->sv_ind_per_block;
+ if (block < sb->sv_ind_per_block_2) {
+ i = inode_bmap(sb,inode,11);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,sb->sv_block_size);
+ i = block_bmap(sb, bh, block >> sb->sv_ind_per_block_bits, convert);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,sb->sv_block_size);
+ return block_bmap(sb, bh, block & sb->sv_ind_per_block_1, convert);
+ }
+ block -= sb->sv_ind_per_block_2;
+ if (block < sb->sv_ind_per_block_3) {
+ i = inode_bmap(sb,inode,12);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,sb->sv_block_size);
+ i = block_bmap(sb, bh, block >> sb->sv_ind_per_block_2_bits, convert);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,sb->sv_block_size);
+ i = block_bmap(sb, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1,convert);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,sb->sv_block_size);
+ return block_bmap(sb, bh, block & sb->sv_ind_per_block_1, convert);
+ }
+ if ((int)block<0) {
+ printk("sysv_bmap: block<0");
+ return 0;
+ }
+ printk("sysv_bmap: block>big");
+ return 0;
+}
+
+/* End of bmap support. */
+
+
+/* Access selected blocks of regular files (or directories) */
+
+static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create)
+{
+ struct super_block *sb;
+ unsigned long tmp;
+ unsigned long *p;
+ struct buffer_head * result;
+
+ sb = inode->i_sb;
+ p = inode->u.sysv_i.i_data + nr;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = sv_getblk(sb, inode->i_dev, tmp);
+ if (tmp == *p)
+ return result;
+ brelse(result);
+ goto repeat;
+ }
+ if (!create)
+ return NULL;
+ tmp = sysv_new_block(sb);
+ if (!tmp)
+ return NULL;
+ result = sv_getblk(sb, inode->i_dev, tmp);
+ if (*p) {
+ sysv_free_block(sb,tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *p = tmp;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ return result;
+}
+
+static struct buffer_head * block_getblk(struct inode * inode,
+ struct buffer_head * bh, int nr, int create)
+{
+ struct super_block *sb;
+ unsigned long tmp, block;
+ sysv_zone_t *p;
+ struct buffer_head * result;
+
+ if (!bh)
+ return NULL;
+ if (!bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ return NULL;
+ }
+ }
+ sb = inode->i_sb;
+ p = nr + (sysv_zone_t *) bh->b_data;
+repeat:
+ block = tmp = *p;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (tmp) {
+ result = sv_getblk(sb, bh->b_dev, block);
+ if (tmp == *p) {
+ brelse(bh);
+ return result;
+ }
+ brelse(result);
+ goto repeat;
+ }
+ if (!create) {
+ brelse(bh);
+ return NULL;
+ }
+ block = sysv_new_block(sb);
+ if (!block) {
+ brelse(bh);
+ return NULL;
+ }
+ result = sv_getblk(sb, bh->b_dev, block);
+ if (*p) {
+ sysv_free_block(sb,block);
+ brelse(result);
+ goto repeat;
+ }
+ *p = (sb->sv_convert ? to_coh_ulong(block) : block);
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ return result;
+}
+
+struct buffer_head * sysv_getblk(struct inode * inode, unsigned int block, int create)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+
+ if (block < 10)
+ return inode_getblk(inode,block,create);
+ block -= 10;
+ if (block < sb->sv_ind_per_block) {
+ bh = inode_getblk(inode,10,create);
+ return block_getblk(inode, bh, block, create);
+ }
+ block -= sb->sv_ind_per_block;
+ if (block < sb->sv_ind_per_block_2) {
+ bh = inode_getblk(inode,11,create);
+ bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create);
+ return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create);
+ }
+ block -= sb->sv_ind_per_block_2;
+ if (block < sb->sv_ind_per_block_3) {
+ bh = inode_getblk(inode,12,create);
+ bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create);
+ bh = block_getblk(inode, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, create);
+ return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create);
+ }
+ if ((int)block<0) {
+ printk("sysv_getblk: block<0");
+ return NULL;
+ }
+ printk("sysv_getblk: block>big");
+ return NULL;
+}
+
+struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create)
+{
+ struct buffer_head * bh;
+
+ bh = sysv_getblk(inode,block,create);
+ if (!bh || bh->b_uptodate)
+ return bh;
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_uptodate)
+ return bh;
+ brelse(bh);
+ return NULL;
+}
+
+
+static inline unsigned long read3byte (char * p)
+{
+ return (unsigned long)(*(unsigned short *)p)
+ | (unsigned long)(*(unsigned char *)(p+2)) << 16;
+}
+
+static inline void write3byte (char * p, unsigned long val)
+{
+ *(unsigned short *)p = (unsigned short) val;
+ *(unsigned char *)(p+2) = val >> 16;
+}
+
+static inline unsigned long coh_read3byte (char * p)
+{
+ return (unsigned long)(*(unsigned char *)p) << 16
+ | (unsigned long)(*(unsigned short *)(p+1));
+}
+
+static inline void coh_write3byte (char * p, unsigned long val)
+{
+ *(unsigned char *)p = val >> 16;
+ *(unsigned short *)(p+1) = (unsigned short) val;
+}
+
+void sysv_read_inode(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ unsigned int block, ino;
+ umode_t mode;
+
+ ino = inode->i_ino;
+ inode->i_op = NULL;
+ inode->i_mode = 0;
+ if (!ino || ino > sb->sv_ninodes) {
+ printk("Bad inode number on dev 0x%04x: %d is out of range\n",
+ inode->i_dev, ino);
+ return;
+ }
+ block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits);
+ if (!(bh = sv_bread(sb,inode->i_dev,block))) {
+ printk("Major problem: unable to read inode from dev 0x%04x\n",
+ inode->i_dev);
+ return;
+ }
+ raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1);
+ mode = raw_inode->i_mode;
+ if (sb->sv_kludge_symlinks)
+ mode = from_coh_imode(mode);
+ /* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
+ inode->i_mode = mode;
+ inode->i_uid = raw_inode->i_uid;
+ inode->i_gid = raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_nlink;
+ if (sb->sv_convert) {
+ inode->i_size = from_coh_ulong(raw_inode->i_size);
+ inode->i_atime = from_coh_ulong(raw_inode->i_atime);
+ inode->i_mtime = from_coh_ulong(raw_inode->i_mtime);
+ inode->i_ctime = from_coh_ulong(raw_inode->i_ctime);
+ } else {
+ inode->i_size = raw_inode->i_size;
+ inode->i_atime = raw_inode->i_atime;
+ inode->i_mtime = raw_inode->i_mtime;
+ inode->i_ctime = raw_inode->i_ctime;
+ }
+ inode->i_blocks = inode->i_blksize = 0;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = raw_inode->i_a.i_rdev;
+ else
+ if (sb->sv_convert)
+ for (block = 0; block < 10+1+1+1; block++)
+ inode->u.sysv_i.i_data[block] =
+ coh_read3byte(&raw_inode->i_a.i_addb[3*block]);
+ else
+ for (block = 0; block < 10+1+1+1; block++)
+ inode->u.sysv_i.i_data[block] =
+ read3byte(&raw_inode->i_a.i_addb[3*block]);
+ brelse(bh);
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &sysv_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &sysv_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &sysv_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);
+}
+
+/* To avoid inconsistencies between inodes in memory and inodes on disk. */
+extern int sysv_notify_change(struct inode *inode, struct iattr *attr)
+{
+ int error;
+
+ if ((error = inode_change_ok(inode, attr)) != 0)
+ return error;
+
+ if (attr->ia_valid & ATTR_MODE)
+ if (inode->i_sb->sv_kludge_symlinks)
+ if (attr->ia_mode == COH_KLUDGE_SYMLINK_MODE)
+ attr->ia_mode = COH_KLUDGE_NOT_SYMLINK;
+
+ inode_setattr(inode, attr);
+
+ return 0;
+}
+
+static struct buffer_head * sysv_update_inode(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ unsigned int ino, block;
+ umode_t mode;
+
+ ino = inode->i_ino;
+ if (!ino || ino > sb->sv_ninodes) {
+ printk("Bad inode number on dev 0x%04x: %d is out of range\n",
+ inode->i_dev, ino);
+ inode->i_dirt = 0;
+ return 0;
+ }
+ block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits);
+ if (!(bh = sv_bread(sb,inode->i_dev,block))) {
+ printk("unable to read i-node block\n");
+ inode->i_dirt = 0;
+ return 0;
+ }
+ raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1);
+ mode = inode->i_mode;
+ if (sb->sv_kludge_symlinks)
+ mode = to_coh_imode(mode);
+ raw_inode->i_mode = mode;
+ raw_inode->i_uid = inode->i_uid;
+ raw_inode->i_gid = inode->i_gid;
+ raw_inode->i_nlink = inode->i_nlink;
+ if (sb->sv_convert) {
+ raw_inode->i_size = to_coh_ulong(inode->i_size);
+ raw_inode->i_atime = to_coh_ulong(inode->i_atime);
+ raw_inode->i_mtime = to_coh_ulong(inode->i_mtime);
+ raw_inode->i_ctime = to_coh_ulong(inode->i_ctime);
+ } else {
+ raw_inode->i_size = inode->i_size;
+ raw_inode->i_atime = inode->i_atime;
+ raw_inode->i_mtime = inode->i_mtime;
+ raw_inode->i_ctime = inode->i_ctime;
+ }
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_a.i_rdev = inode->i_rdev; /* write 2 or 3 bytes ?? */
+ else
+ if (sb->sv_convert)
+ for (block = 0; block < 10+1+1+1; block++)
+ coh_write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]);
+ else
+ for (block = 0; block < 10+1+1+1; block++)
+ write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]);
+ inode->i_dirt=0;
+ mark_buffer_dirty(bh, 1);
+ return bh;
+}
+
+void sysv_write_inode(struct inode * inode)
+{
+ struct buffer_head *bh;
+ bh = sysv_update_inode(inode);
+ brelse(bh);
+}
+
+int sysv_sync_inode(struct inode * inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = sysv_update_inode(inode);
+ if (bh && bh->b_dirt) {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_req && !bh->b_uptodate)
+ {
+ printk ("IO error syncing sysv inode [%04x:%08lx]\n",
+ inode->i_dev, inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
+
diff --git a/fs/sysv/mmap.c b/fs/sysv/mmap.c
new file mode 100644
index 000000000..3ec3867a9
--- /dev/null
+++ b/fs/sysv/mmap.c
@@ -0,0 +1,85 @@
+/*
+ * linux/fs/sysv/mmap.c
+ *
+ * mm/memory.c, mm/mmap.c
+ * Copyright (C) 1991, 1992, 1993 Linus Torvalds
+ *
+ * nfs/mmap.c
+ * Copyright (C) 1993 Jon Tombs
+ *
+ * fs/msdos/mmap.c
+ * Copyright (C) 1994 Jacques Gelinas
+ *
+ * fs/sysv/mmap.c
+ * Copyright (C) 1994 Bruno Haible
+ *
+ * SystemV/Coherent mmap handling
+ */
+
+#include <asm/segment.h>
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long sysv_file_mmap_nopage (struct vm_area_struct * area,
+ unsigned long address, unsigned long page, int no_share)
+{
+ int remaining, count, old_fs;
+ struct file filp;
+
+ address &= PAGE_MASK;
+ /* prepare a file pointer */
+ filp.f_pos = address - area->vm_start + area->vm_offset;
+ filp.f_reada = 0;
+ remaining = area->vm_end - address;
+ if (remaining > PAGE_SIZE)
+ remaining = PAGE_SIZE;
+ /* read from the file. page is in kernel space, not user space. */
+ old_fs = get_fs(); set_fs(get_ds());
+ count = sysv_file_read (area->vm_inode, &filp, (char *)page, remaining);
+ set_fs(old_fs);
+ if (count < 0)
+ count = 0; /* do nothing on I/O error ?? */
+ else
+ remaining -= count;
+ if (remaining > 0)
+ memset((char *)page + count, 0, remaining);
+ return page;
+}
+
+static struct vm_operations_struct sysv_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ sysv_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* share */
+ NULL, /* unmap */
+};
+
+int sysv_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+ if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ vma->vm_inode = inode;
+ inode->i_count++;
+ vma->vm_ops = &sysv_file_mmap;
+ return 0;
+}
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
new file mode 100644
index 000000000..c9fd77158
--- /dev/null
+++ b/fs/sysv/namei.c
@@ -0,0 +1,822 @@
+/*
+ * linux/fs/sysv/namei.c
+ *
+ * minix/namei.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/namei.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/namei.c
+ * Copyright (C) 1993 Bruno Haible
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+
+/* compare strings: name[0..len-1] (not zero-terminated) and
+ * buffer[0..] (filled with zeroes up to buffer[0..maxlen-1])
+ */
+static inline int namecompare(int len, int maxlen,
+ const char * name, const char * buffer)
+{
+ if (len > maxlen)
+ return 0;
+ if (len < maxlen && buffer[len])
+ return 0;
+ return !memcmp(name, buffer, len);
+}
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space. [Now it is!]
+ * Thus we'll have to use sysv_match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, sysv_match returns 1 for success, 0 for failure.
+ */
+static int sysv_match(int len, const char * name, struct sysv_dir_entry * de)
+{
+ if (!de->inode || len > SYSV_NAMELEN)
+ return 0;
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
+ return 1;
+ return namecompare(len,SYSV_NAMELEN,name,de->name);
+}
+
+/*
+ * sysv_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 * sysv_find_entry(struct inode * dir,
+ const char * name, int namelen, struct sysv_dir_entry ** res_dir)
+{
+ struct super_block * sb;
+ unsigned long pos, block, offset; /* pos = block * block_size + offset */
+ struct buffer_head * bh;
+
+ *res_dir = NULL;
+ if (!dir)
+ return NULL;
+ sb = dir->i_sb;
+ if (namelen > SYSV_NAMELEN)
+ if (sb->sv_truncate)
+ namelen = SYSV_NAMELEN;
+ else
+ return NULL;
+ bh = NULL;
+ pos = block = offset = 0;
+ while (pos < dir->i_size) {
+ if (!bh) {
+ bh = sysv_file_bread(dir,block,0);
+ if (!bh) {
+ /* offset = 0; */ block++;
+ pos += sb->sv_block_size;
+ continue;
+ }
+ }
+ if (sysv_match(namelen, name,
+ *res_dir = (struct sysv_dir_entry *) (bh->b_data + offset) ))
+ return bh;
+ pos += SYSV_DIRSIZE;
+ offset += SYSV_DIRSIZE;
+ if (offset < sb->sv_block_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0; block++;
+ }
+ brelse(bh);
+ *res_dir = NULL;
+ return NULL;
+}
+
+int sysv_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result)
+{
+ int ino;
+ struct sysv_dir_entry * de;
+ struct buffer_head * bh;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!(bh = sysv_find_entry(dir,name,len,&de))) {
+ iput(dir);
+ return -ENOENT;
+ }
+ ino = de->inode;
+ brelse(bh);
+ if (!(*result = iget(dir->i_sb,ino))) {
+ iput(dir);
+ return -EACCES;
+ }
+ iput(dir);
+ return 0;
+}
+
+/*
+ * sysv_add_entry()
+ *
+ * adds a file entry to the specified directory, returning a possible
+ * error value if it fails.
+ *
+ * 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 int sysv_add_entry(struct inode * dir,
+ const char * name, int namelen,
+ struct buffer_head ** res_buf,
+ struct sysv_dir_entry ** res_dir)
+{
+ struct super_block * sb;
+ int i;
+ unsigned long pos, block, offset; /* pos = block * block_size + offset */
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ *res_buf = NULL;
+ *res_dir = NULL;
+ if (!dir)
+ return -ENOENT;
+ sb = dir->i_sb;
+ if (namelen > SYSV_NAMELEN)
+ if (sb->sv_truncate)
+ namelen = SYSV_NAMELEN;
+ else
+ return -ENAMETOOLONG;
+ if (!namelen)
+ return -ENOENT;
+ bh = NULL;
+ pos = block = offset = 0;
+ while (1) {
+ if (!bh) {
+ bh = sysv_file_bread(dir,block,1);
+ if (!bh)
+ return -ENOSPC;
+ }
+ de = (struct sysv_dir_entry *) (bh->b_data + offset);
+ pos += SYSV_DIRSIZE;
+ offset += SYSV_DIRSIZE;
+ if (pos > dir->i_size) {
+ de->inode = 0;
+ dir->i_size = pos;
+ dir->i_dirt = 1;
+ }
+ if (de->inode) {
+ if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) {
+ brelse(bh);
+ return -EEXIST;
+ }
+ } else {
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ for (i = 0; i < SYSV_NAMELEN ; i++)
+ de->name[i] = (i < namelen) ? name[i] : 0;
+ mark_buffer_dirty(bh, 1);
+ *res_dir = de;
+ break;
+ }
+ if (offset < sb->sv_block_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0; block++;
+ }
+ *res_buf = bh;
+ return 0;
+}
+
+int sysv_create(struct inode * dir,const char * name, int len, int mode,
+ struct inode ** result)
+{
+ int error;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ inode = sysv_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &sysv_file_inode_operations;
+ inode->i_mode = mode;
+ inode->i_dirt = 1;
+ error = sysv_add_entry(dir,name,len, &bh ,&de);
+ if (error) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return error;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ *result = inode;
+ return 0;
+}
+
+int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)
+{
+ int error;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ if (!dir)
+ return -ENOENT;
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ inode = sysv_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_uid = current->fsuid;
+ inode->i_mode = mode;
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &sysv_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &sysv_dir_inode_operations;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ }
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &sysv_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+ if (S_ISBLK(mode) || S_ISCHR(mode))
+ inode->i_rdev = rdev;
+ inode->i_dirt = 1;
+ error = sysv_add_entry(dir, name, len, &bh, &de);
+ if (error) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return error;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int sysv_mkdir(struct inode * dir, const char * name, int len, int mode)
+{
+ int error;
+ struct inode * inode;
+ struct buffer_head * bh, *dir_block;
+ struct sysv_dir_entry * de;
+
+ if (!dir) {
+ iput(dir);
+ return -EINVAL;
+ }
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ if (dir->i_nlink >= dir->i_sb->sv_link_max) {
+ iput(dir);
+ return -EMLINK;
+ }
+ inode = sysv_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &sysv_dir_inode_operations;
+ inode->i_size = 2 * SYSV_DIRSIZE;
+ dir_block = sysv_file_bread(inode,0,1);
+ if (!dir_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ de = (struct sysv_dir_entry *) (dir_block->b_data + 0*SYSV_DIRSIZE);
+ de->inode = inode->i_ino;
+ strcpy(de->name,"."); /* rest of de->name is zero, see sysv_new_block */
+ de = (struct sysv_dir_entry *) (dir_block->b_data + 1*SYSV_DIRSIZE);
+ de->inode = dir->i_ino;
+ strcpy(de->name,".."); /* rest of de->name is zero, see sysv_new_block */
+ inode->i_nlink = 2;
+ mark_buffer_dirty(dir_block, 1);
+ brelse(dir_block);
+ inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ inode->i_dirt = 1;
+ error = sysv_add_entry(dir, name, len, &bh, &de);
+ if (error) {
+ iput(dir);
+ inode->i_nlink=0;
+ iput(inode);
+ return error;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ dir->i_nlink++;
+ dir->i_dirt = 1;
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir(struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned long pos, block, offset; /* pos = block * block_size + offset */
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ if (!inode)
+ return 1;
+ block = 0;
+ bh = NULL;
+ pos = offset = 2*SYSV_DIRSIZE;
+ if (inode->i_size % SYSV_DIRSIZE)
+ goto bad_dir;
+ if (inode->i_size < pos)
+ goto bad_dir;
+ bh = sysv_file_bread(inode,0,0);
+ if (!bh)
+ goto bad_dir;
+ de = (struct sysv_dir_entry *) (bh->b_data + 0*SYSV_DIRSIZE);
+ if (!de->inode || strcmp(de->name,"."))
+ goto bad_dir;
+ de = (struct sysv_dir_entry *) (bh->b_data + 1*SYSV_DIRSIZE);
+ if (!de->inode || strcmp(de->name,".."))
+ goto bad_dir;
+ sb = inode->i_sb;
+ while (pos < inode->i_size) {
+ if (!bh) {
+ bh = sysv_file_bread(inode,block,0);
+ if (!bh) {
+ /* offset = 0; */ block++;
+ pos += sb->sv_block_size;
+ continue;
+ }
+ }
+ de = (struct sysv_dir_entry *) (bh->b_data + offset);
+ pos += SYSV_DIRSIZE;
+ offset += SYSV_DIRSIZE;
+ if (de->inode) {
+ brelse(bh);
+ return 0;
+ }
+ if (offset < sb->sv_block_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0; block++;
+ }
+ brelse(bh);
+ return 1;
+bad_dir:
+ brelse(bh);
+ printk("Bad directory on device %04x\n",inode->i_dev);
+ return 1;
+}
+
+int sysv_rmdir(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ inode = NULL;
+ bh = sysv_find_entry(dir,name,len,&de);
+ retval = -ENOENT;
+ if (!bh)
+ goto end_rmdir;
+ retval = -EPERM;
+ if (!(inode = iget(dir->i_sb, de->inode)))
+ goto end_rmdir;
+ if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ current->fsuid != inode->i_uid &&
+ current->fsuid != dir->i_uid)
+ goto end_rmdir;
+ if (inode->i_dev != dir->i_dev)
+ goto end_rmdir;
+ if (inode == dir) /* we may not delete ".", but "../dir" is ok */
+ goto end_rmdir;
+ if (!S_ISDIR(inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto end_rmdir;
+ }
+ if (!empty_dir(inode)) {
+ retval = -ENOTEMPTY;
+ goto end_rmdir;
+ }
+ if (de->inode != inode->i_ino) {
+ retval = -ENOENT;
+ goto end_rmdir;
+ }
+ if (inode->i_count > 1) {
+ retval = -EBUSY;
+ goto end_rmdir;
+ }
+ if (inode->i_nlink != 2)
+ printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
+ de->inode = 0;
+ mark_buffer_dirty(bh, 1);
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ dir->i_nlink--;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt=1;
+ retval = 0;
+end_rmdir:
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return retval;
+}
+
+int sysv_unlink(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+repeat:
+ retval = -ENOENT;
+ inode = NULL;
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (!bh)
+ goto end_unlink;
+ if (!(inode = iget(dir->i_sb, de->inode)))
+ goto end_unlink;
+ retval = -EPERM;
+ if (S_ISDIR(inode->i_mode))
+ goto end_unlink;
+ if (de->inode != inode->i_ino) {
+ iput(inode);
+ brelse(bh);
+ current->counter = 0;
+ schedule();
+ goto repeat;
+ }
+ if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ current->fsuid != inode->i_uid &&
+ current->fsuid != dir->i_uid)
+ goto end_unlink;
+ if (de->inode != inode->i_ino) {
+ retval = -ENOENT;
+ goto end_unlink;
+ }
+ if (!inode->i_nlink) {
+ printk("Deleting nonexistent file (%04x:%lu), %d\n",
+ inode->i_dev,inode->i_ino,inode->i_nlink);
+ inode->i_nlink=1;
+ }
+ de->inode = 0;
+ mark_buffer_dirty(bh, 1);
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ inode->i_nlink--;
+ inode->i_ctime = dir->i_ctime;
+ inode->i_dirt = 1;
+ retval = 0;
+end_unlink:
+ brelse(bh);
+ iput(inode);
+ iput(dir);
+ return retval;
+}
+
+int sysv_symlink(struct inode * dir, const char * name, int len, const char * symname)
+{
+ struct sysv_dir_entry * de;
+ struct inode * inode;
+ struct buffer_head * name_block;
+ char * name_block_data;
+ struct super_block * sb;
+ int i;
+ char c;
+ struct buffer_head * bh;
+
+ if (!(inode = sysv_new_inode(dir))) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = S_IFLNK | 0777;
+ inode->i_op = &sysv_symlink_inode_operations;
+ name_block = sysv_file_bread(inode,0,1);
+ if (!name_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ sb = inode->i_sb;
+ name_block_data = name_block->b_data;
+ i = 0;
+ while (i < sb->sv_block_size_1 && (c = *(symname++)))
+ name_block_data[i++] = c;
+ name_block_data[i] = 0;
+ mark_buffer_dirty(name_block, 1);
+ brelse(name_block);
+ inode->i_size = i;
+ inode->i_dirt = 1;
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ i = sysv_add_entry(dir, name, len, &bh, &de);
+ if (i) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return i;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, int len)
+{
+ int error;
+ struct sysv_dir_entry * de;
+ struct buffer_head * bh;
+
+ if (S_ISDIR(oldinode->i_mode)) {
+ iput(oldinode);
+ iput(dir);
+ return -EPERM;
+ }
+ if (oldinode->i_nlink >= oldinode->i_sb->sv_link_max) {
+ iput(oldinode);
+ iput(dir);
+ return -EMLINK;
+ }
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ iput(oldinode);
+ return -EEXIST;
+ }
+ error = sysv_add_entry(dir, name, len, &bh, &de);
+ if (error) {
+ iput(dir);
+ iput(oldinode);
+ return error;
+ }
+ de->inode = oldinode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ oldinode->i_nlink++;
+ oldinode->i_ctime = CURRENT_TIME;
+ oldinode->i_dirt = 1;
+ iput(oldinode);
+ return 0;
+}
+
+/* return 1 if `new' is a subdir of `old' on the same device */
+static int subdir(struct inode * new_inode, struct inode * old_inode)
+{
+ int ino;
+ int result;
+
+ new_inode->i_count++;
+ result = 0;
+ for (;;) {
+ if (new_inode == old_inode) {
+ result = 1;
+ break;
+ }
+ if (new_inode->i_dev != old_inode->i_dev)
+ break;
+ ino = new_inode->i_ino;
+ if (sysv_lookup(new_inode,"..",2,&new_inode))
+ break;
+ if (new_inode->i_ino == ino) /* root dir reached ? */
+ break;
+ }
+ iput(new_inode);
+ return result;
+}
+
+#define PARENT_INO(buffer) \
+(((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode)
+
+/*
+ * 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_sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ struct inode * old_inode, * new_inode;
+ struct buffer_head * old_bh, * new_bh, * dir_bh;
+ struct sysv_dir_entry * old_de, * new_de;
+ int retval;
+
+ goto start_up;
+try_again:
+ brelse(old_bh);
+ brelse(new_bh);
+ brelse(dir_bh);
+ iput(old_inode);
+ iput(new_inode);
+ current->counter = 0;
+ schedule();
+start_up:
+ old_inode = new_inode = NULL;
+ old_bh = new_bh = dir_bh = NULL;
+ old_bh = sysv_find_entry(old_dir,old_name,old_len,&old_de);
+ retval = -ENOENT;
+ if (!old_bh)
+ goto end_rename;
+ old_inode = __iget(old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */
+ if (!old_inode)
+ goto end_rename;
+ retval = -EPERM;
+ if ((old_dir->i_mode & S_ISVTX) &&
+ current->fsuid != old_inode->i_uid &&
+ current->fsuid != old_dir->i_uid && !fsuser())
+ goto end_rename;
+ new_bh = sysv_find_entry(new_dir,new_name,new_len,&new_de);
+ if (new_bh) {
+ new_inode = __iget(new_dir->i_sb, new_de->inode, 0);
+ if (!new_inode) {
+ brelse(new_bh);
+ new_bh = NULL;
+ }
+ }
+ if (new_inode == old_inode) {
+ retval = 0;
+ goto end_rename;
+ }
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ retval = -EISDIR;
+ if (!S_ISDIR(old_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir, old_inode))
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_inode))
+ goto end_rename;
+ retval = -EBUSY;
+ if (new_inode->i_count > 1)
+ goto end_rename;
+ }
+ retval = -EPERM;
+ if (new_inode && (new_dir->i_mode & S_ISVTX) &&
+ current->fsuid != new_inode->i_uid &&
+ current->fsuid != new_dir->i_uid && !fsuser())
+ goto end_rename;
+ if (S_ISDIR(old_inode->i_mode)) {
+ retval = -ENOTDIR;
+ if (new_inode && !S_ISDIR(new_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir, old_inode))
+ goto end_rename;
+ retval = -EIO;
+ dir_bh = sysv_file_bread(old_inode,0,0);
+ if (!dir_bh)
+ goto end_rename;
+ if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
+ goto end_rename;
+ retval = -EMLINK;
+ if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
+ goto end_rename;
+ }
+ if (!new_bh) {
+ retval = sysv_add_entry(new_dir,new_name,new_len,&new_bh,&new_de);
+ if (retval)
+ goto end_rename;
+ }
+/* sanity checking before doing the rename - avoid races */
+ if (new_inode && (new_de->inode != new_inode->i_ino))
+ goto try_again;
+ if (new_de->inode && !new_inode)
+ goto try_again;
+ if (old_de->inode != old_inode->i_ino)
+ goto try_again;
+/* ok, that's it */
+ old_de->inode = 0;
+ new_de->inode = old_inode->i_ino;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ old_dir->i_dirt = 1;
+ new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
+ new_dir->i_dirt = 1;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime = CURRENT_TIME;
+ new_inode->i_dirt = 1;
+ }
+ mark_buffer_dirty(old_bh, 1);
+ mark_buffer_dirty(new_bh, 1);
+ if (dir_bh) {
+ PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
+ mark_buffer_dirty(dir_bh, 1);
+ old_dir->i_nlink--;
+ old_dir->i_dirt = 1;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_dirt = 1;
+ } else {
+ new_dir->i_nlink++;
+ new_dir->i_dirt = 1;
+ }
+ }
+ retval = 0;
+end_rename:
+ brelse(dir_bh);
+ brelse(old_bh);
+ brelse(new_bh);
+ iput(old_inode);
+ iput(new_inode);
+ iput(old_dir);
+ iput(new_dir);
+ return retval;
+}
+
+/*
+ * Ok, rename also locks out other renames, as they can change the parent of
+ * a directory, and we don't want any races. Other races are checked for by
+ * "do_rename()", which restarts if there are inconsistencies.
+ *
+ * Note that there is no race between different filesystems: it's only within
+ * the same device that races occur: many renames can happen at once, as long
+ * as they are on different partitions.
+ */
+int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ static struct wait_queue * wait = NULL;
+ static int lock = 0;
+ int result;
+
+ while (lock)
+ sleep_on(&wait);
+ lock = 1;
+ result = do_sysv_rename(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+ lock = 0;
+ wake_up(&wait);
+ return result;
+}
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
new file mode 100644
index 000000000..d392816bc
--- /dev/null
+++ b/fs/sysv/symlink.c
@@ -0,0 +1,110 @@
+/*
+ * linux/fs/sysv/symlink.c
+ *
+ * minix/symlink.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/symlink.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/symlink.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent symlink handling code
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+
+static int sysv_readlink(struct inode *, char *, int);
+static int sysv_follow_link(struct inode *, struct inode *, int, int, struct inode **);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations sysv_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 */
+ sysv_readlink, /* readlink */
+ sysv_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int sysv_follow_link(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ int error;
+ struct buffer_head * bh;
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->fs->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if (current->link_count > 5) {
+ iput(inode);
+ iput(dir);
+ return -ELOOP;
+ }
+ if (!(bh = sysv_file_bread(inode, 0, 0))) { /* is reading 1 block enough ?? */
+ iput(inode);
+ iput(dir);
+ return -EIO;
+ }
+ iput(inode);
+ current->link_count++;
+ error = open_namei(bh->b_data,flag,mode,res_inode,dir);
+ current->link_count--;
+ brelse(bh);
+ return error;
+}
+
+static int sysv_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ struct buffer_head * bh;
+ char * bh_data;
+ int i;
+ char c;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+ if (buflen > inode->i_sb->sv_block_size_1)
+ buflen = inode->i_sb->sv_block_size_1;
+ bh = sysv_file_bread(inode, 0, 0);
+ iput(inode);
+ if (!bh)
+ return 0;
+ bh_data = bh->b_data;
+ i = 0;
+ while (i<buflen && (c = bh_data[i])) {
+ i++;
+ put_fs_byte(c,buffer++);
+ }
+ brelse(bh);
+ return i;
+}
diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c
new file mode 100644
index 000000000..21451e6dd
--- /dev/null
+++ b/fs/sysv/truncate.c
@@ -0,0 +1,283 @@
+/*
+ * linux/fs/sysv/truncate.c
+ *
+ * minix/truncate.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/truncate.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/truncate.c
+ * Copyright (C) 1993 Bruno Haible
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+
+
+/* Linus' implementation of truncate.
+ * It doesn't need locking because it can tell from looking at bh->b_count
+ * whether a given block is in use elsewhere.
+ */
+
+/*
+ * Truncate has the most races in the whole filesystem: coding it is
+ * a pain in the a**. Especially as I don't do any locking...
+ *
+ * The code may look a bit weird, but that's just because I've tried to
+ * handle things like file-size changes in a somewhat graceful manner.
+ * Anyway, truncating a file at the same time somebody else writes to it
+ * is likely to result in pretty weird behaviour...
+ *
+ * The new code handles normal truncates (size = 0) as well as the more
+ * general case (size = XXX). I hope.
+ */
+
+/* We throw away any data beyond inode->i_size. */
+
+static int trunc_direct(struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned int i;
+ unsigned long * p;
+ unsigned long block;
+ struct buffer_head * bh;
+ int retry = 0;
+
+ sb = inode->i_sb;
+repeat:
+ for (i = ((unsigned long) inode->i_size + sb->sv_block_size_1) >> sb->sv_block_size_bits; i < 10; i++) {
+ p = inode->u.sysv_i.i_data + i;
+ block = *p;
+ if (!block)
+ continue;
+ bh = sv_get_hash_table(sb, inode->i_dev, block);
+ if ((i << sb->sv_block_size_bits) < inode->i_size) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || (block != *p)) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *p = 0;
+ inode->i_dirt = 1;
+ brelse(bh);
+ sysv_free_block(sb,block);
+ }
+ return retry;
+}
+
+static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+{
+ unsigned long indtmp, indblock;
+ struct super_block * sb;
+ struct buffer_head * indbh;
+ unsigned int i;
+ sysv_zone_t * ind;
+ unsigned long tmp, block;
+ struct buffer_head * bh;
+ int retry = 0;
+
+ indblock = indtmp = *p;
+ if (convert)
+ indblock = from_coh_ulong(indblock);
+ if (!indblock)
+ return 0;
+ sb = inode->i_sb;
+ indbh = sv_bread(sb, inode->i_dev, indblock);
+ if (indtmp != *p) {
+ brelse(indbh);
+ return 1;
+ }
+ if (!indbh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+repeat:
+ if (inode->i_size < offset)
+ i = 0;
+ else
+ i = (inode->i_size - offset + sb->sv_block_size_1) >> sb->sv_block_size_bits;
+ for (; i < sb->sv_ind_per_block; i++) {
+ ind = ((sysv_zone_t *) indbh->b_data) + i;
+ block = tmp = *ind;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ continue;
+ bh = sv_get_hash_table(sb, inode->i_dev, block);
+ if ((i << sb->sv_block_size_bits) + offset < inode->i_size) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || (tmp != *ind)) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *ind = 0;
+ mark_buffer_dirty(indbh, 1);
+ brelse(bh);
+ sysv_free_block(sb,block);
+ }
+ for (i = 0; i < sb->sv_ind_per_block; i++)
+ if (((sysv_zone_t *) indbh->b_data)[i])
+ goto done;
+ if ((indbh->b_count != 1) || (indtmp != *p)) {
+ brelse(indbh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,indblock);
+done:
+ brelse(indbh);
+ return retry;
+}
+
+static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+{
+ unsigned long indtmp, indblock;
+ struct super_block * sb;
+ struct buffer_head * indbh;
+ unsigned int i;
+ sysv_zone_t * ind;
+ unsigned long tmp, block;
+ int retry = 0;
+
+ indblock = indtmp = *p;
+ if (convert)
+ indblock = from_coh_ulong(indblock);
+ if (!indblock)
+ return 0;
+ sb = inode->i_sb;
+ indbh = sv_bread(sb, inode->i_dev, indblock);
+ if (indtmp != *p) {
+ brelse(indbh);
+ return 1;
+ }
+ if (!indbh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+ if (inode->i_size < offset)
+ i = 0;
+ else
+ i = (inode->i_size - offset + sb->sv_ind_per_block_block_size_1) >> sb->sv_ind_per_block_block_size_bits;
+ for (; i < sb->sv_ind_per_block; i++) {
+ ind = ((sysv_zone_t *) indbh->b_data) + i;
+ block = tmp = *ind;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ continue;
+ retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&indbh->b_dirt);
+ }
+ for (i = 0; i < sb->sv_ind_per_block; i++)
+ if (((sysv_zone_t *) indbh->b_data)[i])
+ goto done;
+ if ((indbh->b_count != 1) || (indtmp != *p)) {
+ brelse(indbh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,indblock);
+done:
+ brelse(indbh);
+ return retry;
+}
+
+static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+{
+ unsigned long indtmp, indblock;
+ struct super_block * sb;
+ struct buffer_head * indbh;
+ unsigned int i;
+ sysv_zone_t * ind;
+ unsigned long tmp, block;
+ int retry = 0;
+
+ indblock = indtmp = *p;
+ if (convert)
+ indblock = from_coh_ulong(indblock);
+ if (!indblock)
+ return 0;
+ sb = inode->i_sb;
+ indbh = sv_bread(sb, inode->i_dev, indblock);
+ if (indtmp != *p) {
+ brelse(indbh);
+ return 1;
+ }
+ if (!indbh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+ if (inode->i_size < offset)
+ i = 0;
+ else
+ i = (inode->i_size - offset + sb->sv_ind_per_block_2_block_size_1) >> sb->sv_ind_per_block_2_block_size_bits;
+ for (; i < sb->sv_ind_per_block; i++) {
+ ind = ((sysv_zone_t *) indbh->b_data) + i;
+ block = tmp = *ind;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ continue;
+ retry |= trunc_dindirect(inode,offset+(i<<sb->sv_ind_per_block_2_bits),ind,sb->sv_convert,&indbh->b_dirt);
+ }
+ for (i = 0; i < sb->sv_ind_per_block; i++)
+ if (((sysv_zone_t *) indbh->b_data)[i])
+ goto done;
+ if ((indbh->b_count != 1) || (indtmp != *p)) {
+ brelse(indbh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,indblock);
+done:
+ brelse(indbh);
+ return retry;
+}
+
+static int trunc_all(struct inode * inode)
+{
+ struct super_block * sb;
+
+ sb = inode->i_sb;
+ return trunc_direct(inode)
+ | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt)
+ | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt)
+ | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt);
+}
+
+
+void sysv_truncate(struct inode * inode)
+{
+ /* If this is called from sysv_put_inode, we needn't worry about
+ * races as we are just losing the last reference to the inode.
+ * If this is called from another place, let's hope it's a regular
+ * file.
+ * Truncating symbolic links is strange. We assume we don't truncate
+ * a directory we are just modifying. We ensure we don't truncate
+ * a regular file we are just writing to, by use of a lock.
+ */
+ if (S_ISLNK(inode->i_mode))
+ printk("sysv_truncate: truncating symbolic link\n");
+ else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
+ return;
+ while (trunc_all(inode)) {
+ current->counter = 0;
+ schedule();
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+}