diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/sysv |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/sysv')
-rw-r--r-- | fs/sysv/CHANGES | 60 | ||||
-rw-r--r-- | fs/sysv/ChangeLog | 106 | ||||
-rw-r--r-- | fs/sysv/INTRO | 182 | ||||
-rw-r--r-- | fs/sysv/Makefile | 8 | ||||
-rw-r--r-- | fs/sysv/balloc.c | 239 | ||||
-rw-r--r-- | fs/sysv/dir.c | 388 | ||||
-rw-r--r-- | fs/sysv/file.c | 49 | ||||
-rw-r--r-- | fs/sysv/ialloc.c | 240 | ||||
-rw-r--r-- | fs/sysv/inode.c | 354 | ||||
-rw-r--r-- | fs/sysv/itree.c | 475 | ||||
-rw-r--r-- | fs/sysv/namei.c | 318 | ||||
-rw-r--r-- | fs/sysv/super.c | 572 | ||||
-rw-r--r-- | fs/sysv/symlink.c | 20 | ||||
-rw-r--r-- | fs/sysv/sysv.h | 244 |
14 files changed, 3255 insertions, 0 deletions
diff --git a/fs/sysv/CHANGES b/fs/sysv/CHANGES new file mode 100644 index 00000000000..66ea6e92be6 --- /dev/null +++ b/fs/sysv/CHANGES @@ -0,0 +1,60 @@ +Mon, 15 Dec 1997 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> + * namei.c: struct sysv_dir_inode_operations updated to use dentries. + +Fri, 23 Jan 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> + * inode.c: corrected 1 track offset setting (in sb->sv_block_base). + Originally it was overridden (by setting to zero) + in detected_[xenix,sysv4,sysv2,coherent]. Thanks + to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl> + for identifying the problem. + +Tue, 27 Jan 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> + * inode.c: added 2048-byte block support to SystemV FS. + Merged detected_bs[512,1024,2048]() into one function: + void detected_bs (u_char type, struct super_block *sb). + Thanks to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl> + for the patch. + +Wed, 4 Feb 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> + * namei.c: removed static subdir(); is_subdir() from dcache.c + is used instead. Cosmetic changes. + +Thu, 3 Dec 1998 Al Viro (viro@parcelfarce.linux.theplanet.co.uk) + * namei.c (sysv_rmdir): + Bugectomy: old check for victim being busy + (inode->i_count) wasn't replaced (with checking + dentry->d_count) and escaped Linus in the last round + of changes. Shot and buried. + +Wed, 9 Dec 1998 AV + * namei.c (do_sysv_rename): + Fixed incorrect check for other owners + race. + Removed checks that went to VFS. + * namei.c (sysv_unlink): + Removed checks that went to VFS. + +Thu, 10 Dec 1998 AV + * namei.c (do_mknod): + Removed dead code - mknod is never asked to + create a symlink or directory. Incidentially, + it wouldn't do it right if it would be called. + +Sat, 26 Dec 1998 KGB + * inode.c (detect_sysv4): + Added detection of expanded s_type field (0x10, + 0x20 and 0x30). Forced read-only access in this case. + +Sun, 21 Mar 1999 AV + * namei.c (sysv_link): + Fixed i_count usage that resulted in dcache corruption. + * inode.c: + Filled ->delete_inode() method with sysv_delete_inode(). + sysv_put_inode() is gone, as it tried to do ->delete_ + _inode()'s job. + * ialloc.c: (sysv_free_inode): + Fixed race. + +Sun, 30 Apr 1999 AV + * namei.c (sysv_mknod): + Removed dead code (S_IFREG case is now passed to + ->create() by VFS). diff --git a/fs/sysv/ChangeLog b/fs/sysv/ChangeLog new file mode 100644 index 00000000000..18e3487debd --- /dev/null +++ b/fs/sysv/ChangeLog @@ -0,0 +1,106 @@ +Thu Feb 14 2002 Andrew Morton <akpm@zip.com.au> + + * dir_commit_chunk(): call writeout_one_page() as well as + waitfor_one_page() for IS_SYNC directories, so that we + actually do sync the directory. (forward-port from 2.4). + +Thu Feb 7 2002 Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk> + + * super.c: switched to ->get_sb() + * ChangeLog: fixed dates ;-) + +2002-01-24 David S. Miller <davem@redhat.com> + + * inode.c: Include linux/init.h + +Mon Jan 21 2002 Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk> + * ialloc.c (sysv_new_inode): zero SYSV_I(inode)->i_data out. + * i_vnode renamed to vfs_inode. Sorry, but let's keep that + consistent. + +Sat Jan 19 2002 Christoph Hellwig <hch@infradead.org> + + * include/linux/sysv_fs.h (SYSV_I): Get fs-private inode data using + list_entry() instead of inode->u. + * include/linux/sysv_fs_i.h: Add 'struct inode i_vnode' field to + sysv_inode_info structure. + * inode.c: Include <linux/slab.h>, implement alloc_inode/destroy_inode + sop methods, add infrastructure for per-fs inode slab cache. + * super.c (init_sysv_fs): Initialize inode cache, recover properly + in the case of failed register_filesystem for V7. + (exit_sysv_fs): Destroy inode cache. + +Sat Jan 19 2002 Christoph Hellwig <hch@infradead.org> + + * include/linux/sysv_fs.h: Include <linux/sysv_fs_i.h>, declare SYSV_I(). + * dir.c (sysv_find_entry): Use SYSV_I() instead of ->u.sysv_i to + access fs-private inode data. + * ialloc.c (sysv_new_inode): Likewise. + * inode.c (sysv_read_inode): Likewise. + (sysv_update_inode): Likewise. + * itree.c (get_branch): Likewise. + (sysv_truncate): Likewise. + * symlink.c (sysv_readlink): Likewise. + (sysv_follow_link): Likewise. + +Fri Jan 4 2002 Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk> + + * ialloc.c (sysv_free_inode): Use sb->s_id instead of bdevname(). + * inode.c (sysv_read_inode): Likewise. + (sysv_update_inode): Likewise. + (sysv_sync_inode): Likewise. + * super.c (detect_sysv): Likewise. + (complete_read_super): Likewise. + (sysv_read_super): Likewise. + (v7_read_super): Likewise. + +Sun Dec 30 2001 Manfred Spraul <manfreds@colorfullife.com> + + * dir.c (dir_commit_chunk): Do not set dir->i_version. + (sysv_readdir): Likewise. + +Thu Dec 27 2001 Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk> + + * itree.c (get_block): Use map_bh() to fill out bh_result. + +Tue Dec 25 2001 Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk> + + * super.c (sysv_read_super): Use sb_set_blocksize() to set blocksize. + (v7_read_super): Likewise. + +Tue Nov 27 2001 Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk> + + * itree.c (get_block): Change type for iblock argument to sector_t. + * super.c (sysv_read_super): Set s_blocksize early. + (v7_read_super): Likewise. + * balloc.c (sysv_new_block): Use sb_bread(). instead of bread(). + (sysv_count_free_blocks): Likewise. + * ialloc.c (sysv_raw_inode): Likewise. + * itree.c (get_branch): Likewise. + (free_branches): Likewise. + * super.c (sysv_read_super): Likewise. + (v7_read_super): Likewise. + +Sat Dec 15 2001 Christoph Hellwig <hch@infradead.org> + + * inode.c (sysv_read_inode): Mark inode as bad in case of failure. + * super.c (complete_read_super): Check for bad root inode. + +Wed Nov 21 2001 Andrew Morton <andrewm@uow.edu.au> + + * file.c (sysv_sync_file): Call fsync_inode_data_buffers. + +Fri Oct 26 2001 Christoph Hellwig <hch@infradead.org> + + * dir.c, ialloc.c, namei.c, include/linux/sysv_fs_i.h: + Implement per-Inode lookup offset cache. + Modelled after Ted's ext2 patch. + +Fri Oct 26 2001 Christoph Hellwig <hch@infradead.org> + + * inode.c, super.c, include/linux/sysv_fs.h, + include/linux/sysv_fs_sb.h: + Remove symlink faking. Noone really wants to use these as + linux filesystems and native OSes don't support it anyway. + + diff --git a/fs/sysv/INTRO b/fs/sysv/INTRO new file mode 100644 index 00000000000..de4e4d17cac --- /dev/null +++ b/fs/sysv/INTRO @@ -0,0 +1,182 @@ +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 and 2048) + - 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 00000000000..3591f9d7a48 --- /dev/null +++ b/fs/sysv/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Linux SystemV/Coherent filesystem routines. +# + +obj-$(CONFIG_SYSV_FS) += sysv.o + +sysv-objs := ialloc.o balloc.o inode.o itree.o file.o dir.o \ + namei.o super.o symlink.o diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c new file mode 100644 index 00000000000..9a6ad96acf2 --- /dev/null +++ b/fs/sysv/balloc.c @@ -0,0 +1,239 @@ +/* + * 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/buffer_head.h> +#include <linux/string.h> +#include "sysv.h" + +/* We don't trust the value of + sb->sv_sbd2->s_tfree = *sb->sv_free_blocks + but we nevertheless keep it up to date. */ + +static inline sysv_zone_t *get_chunk(struct super_block *sb, struct buffer_head *bh) +{ + char *bh_data = bh->b_data; + + if (SYSV_SB(sb)->s_type == FSTYPE_SYSV4) + return (sysv_zone_t*)(bh_data+4); + else + return (sysv_zone_t*)(bh_data+2); +} + +/* NOTE NOTE NOTE: nr is a block number _as_ _stored_ _on_ _disk_ */ + +void sysv_free_block(struct super_block * sb, sysv_zone_t nr) +{ + struct sysv_sb_info * sbi = SYSV_SB(sb); + struct buffer_head * bh; + sysv_zone_t *blocks = sbi->s_bcache; + unsigned count; + unsigned block = fs32_to_cpu(sbi, nr); + + /* + * This code does not work at all for AFS (it has a bitmap + * free list). As AFS is supposed to be read-only no one + * should call this for an AFS filesystem anyway... + */ + if (sbi->s_type == FSTYPE_AFS) + return; + + if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) { + printk("sysv_free_block: trying to free block not in datazone\n"); + return; + } + + lock_super(sb); + count = fs16_to_cpu(sbi, *sbi->s_bcache_count); + + if (count > sbi->s_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, ditto if it's completely empty + * (applies only on Coherent). + */ + if (count == sbi->s_flc_size || count == 0) { + block += sbi->s_block_base; + bh = sb_getblk(sb, block); + if (!bh) { + printk("sysv_free_block: getblk() failed\n"); + unlock_super(sb); + return; + } + memset(bh->b_data, 0, sb->s_blocksize); + *(__fs16*)bh->b_data = cpu_to_fs16(sbi, count); + memcpy(get_chunk(sb,bh), blocks, count * sizeof(sysv_zone_t)); + mark_buffer_dirty(bh); + set_buffer_uptodate(bh); + brelse(bh); + count = 0; + } + sbi->s_bcache[count++] = nr; + + *sbi->s_bcache_count = cpu_to_fs16(sbi, count); + fs32_add(sbi, sbi->s_free_blocks, 1); + dirty_sb(sb); + unlock_super(sb); +} + +sysv_zone_t sysv_new_block(struct super_block * sb) +{ + struct sysv_sb_info *sbi = SYSV_SB(sb); + unsigned int block; + sysv_zone_t nr; + struct buffer_head * bh; + unsigned count; + + lock_super(sb); + count = fs16_to_cpu(sbi, *sbi->s_bcache_count); + + if (count == 0) /* Applies only to Coherent FS */ + goto Enospc; + nr = sbi->s_bcache[--count]; + if (nr == 0) /* Applies only to Xenix FS, SystemV FS */ + goto Enospc; + + block = fs32_to_cpu(sbi, nr); + + *sbi->s_bcache_count = cpu_to_fs16(sbi, count); + + if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) { + printk("sysv_new_block: new block %d is not in data zone\n", + block); + goto Enospc; + } + + if (count == 0) { /* the last block continues the free list */ + unsigned count; + + block += sbi->s_block_base; + if (!(bh = sb_bread(sb, block))) { + printk("sysv_new_block: cannot read free-list block\n"); + /* retry this same block next time */ + *sbi->s_bcache_count = cpu_to_fs16(sbi, 1); + goto Enospc; + } + count = fs16_to_cpu(sbi, *(__fs16*)bh->b_data); + if (count > sbi->s_flc_size) { + printk("sysv_new_block: free-list block with >flc_size entries\n"); + brelse(bh); + goto Enospc; + } + *sbi->s_bcache_count = cpu_to_fs16(sbi, count); + memcpy(sbi->s_bcache, get_chunk(sb, bh), + count * sizeof(sysv_zone_t)); + brelse(bh); + } + /* Now the free list head in the superblock is valid again. */ + fs32_add(sbi, sbi->s_free_blocks, -1); + dirty_sb(sb); + unlock_super(sb); + return nr; + +Enospc: + unlock_super(sb); + return 0; +} + +unsigned long sysv_count_free_blocks(struct super_block * sb) +{ + struct sysv_sb_info * sbi = SYSV_SB(sb); + int sb_count; + int count; + struct buffer_head * bh = NULL; + sysv_zone_t *blocks; + unsigned block; + int n; + + /* + * This code does not work at all for AFS (it has a bitmap + * free list). As AFS is supposed to be read-only we just + * lie and say it has no free block at all. + */ + if (sbi->s_type == FSTYPE_AFS) + return 0; + + lock_super(sb); + sb_count = fs32_to_cpu(sbi, *sbi->s_free_blocks); + + if (0) + goto trust_sb; + + /* this causes a lot of disk traffic ... */ + count = 0; + n = fs16_to_cpu(sbi, *sbi->s_bcache_count); + blocks = sbi->s_bcache; + while (1) { + sysv_zone_t zone; + if (n > sbi->s_flc_size) + goto E2big; + zone = 0; + while (n && (zone = blocks[--n]) != 0) + count++; + if (zone == 0) + break; + + block = fs32_to_cpu(sbi, zone); + if (bh) + brelse(bh); + + if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) + goto Einval; + block += sbi->s_block_base; + bh = sb_bread(sb, block); + if (!bh) + goto Eio; + n = fs16_to_cpu(sbi, *(__fs16*)bh->b_data); + blocks = get_chunk(sb, bh); + } + if (bh) + brelse(bh); + if (count != sb_count) + goto Ecount; +done: + unlock_super(sb); + return count; + +Einval: + printk("sysv_count_free_blocks: new block %d is not in data zone\n", + block); + goto trust_sb; +Eio: + printk("sysv_count_free_blocks: cannot read free-list block\n"); + goto trust_sb; +E2big: + printk("sysv_count_free_blocks: >flc_size entries in free-list block\n"); + if (bh) + brelse(bh); +trust_sb: + count = sb_count; + goto done; +Ecount: + printk("sysv_count_free_blocks: free block count was %d, " + "correcting to %d\n", sb_count, count); + if (!(sb->s_flags & MS_RDONLY)) { + *sbi->s_free_blocks = cpu_to_fs32(sbi, count); + dirty_sb(sb); + } + goto done; +} diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c new file mode 100644 index 00000000000..69a085abad6 --- /dev/null +++ b/fs/sysv/dir.c @@ -0,0 +1,388 @@ +/* + * 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 <linux/pagemap.h> +#include <linux/highmem.h> +#include <linux/smp_lock.h> +#include "sysv.h" + +static int sysv_readdir(struct file *, void *, filldir_t); + +struct file_operations sysv_dir_operations = { + .read = generic_read_dir, + .readdir = sysv_readdir, + .fsync = sysv_sync_file, +}; + +static inline void dir_put_page(struct page *page) +{ + kunmap(page); + page_cache_release(page); +} + +static inline unsigned long dir_pages(struct inode *inode) +{ + return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; +} + +static int dir_commit_chunk(struct page *page, unsigned from, unsigned to) +{ + struct inode *dir = (struct inode *)page->mapping->host; + int err = 0; + + page->mapping->a_ops->commit_write(NULL, page, from, to); + if (IS_DIRSYNC(dir)) + err = write_one_page(page, 1); + else + unlock_page(page); + return err; +} + +static struct page * dir_get_page(struct inode *dir, unsigned long n) +{ + struct address_space *mapping = dir->i_mapping; + struct page *page = read_cache_page(mapping, n, + (filler_t*)mapping->a_ops->readpage, NULL); + if (!IS_ERR(page)) { + wait_on_page_locked(page); + kmap(page); + if (!PageUptodate(page)) + goto fail; + } + return page; + +fail: + dir_put_page(page); + return ERR_PTR(-EIO); +} + +static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + unsigned long pos = filp->f_pos; + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + unsigned offset = pos & ~PAGE_CACHE_MASK; + unsigned long n = pos >> PAGE_CACHE_SHIFT; + unsigned long npages = dir_pages(inode); + + lock_kernel(); + + pos = (pos + SYSV_DIRSIZE-1) & ~(SYSV_DIRSIZE-1); + if (pos >= inode->i_size) + goto done; + + for ( ; n < npages; n++, offset = 0) { + char *kaddr, *limit; + struct sysv_dir_entry *de; + struct page *page = dir_get_page(inode, n); + + if (IS_ERR(page)) + continue; + kaddr = (char *)page_address(page); + de = (struct sysv_dir_entry *)(kaddr+offset); + limit = kaddr + PAGE_CACHE_SIZE - SYSV_DIRSIZE; + for ( ;(char*)de <= limit; de++) { + char *name = de->name; + int over; + + if (!de->inode) + continue; + + offset = (char *)de - kaddr; + + over = filldir(dirent, name, strnlen(name,SYSV_NAMELEN), + (n<<PAGE_CACHE_SHIFT) | offset, + fs16_to_cpu(SYSV_SB(sb), de->inode), + DT_UNKNOWN); + if (over) { + dir_put_page(page); + goto done; + } + } + dir_put_page(page); + } + +done: + filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; + unlock_kernel(); + return 0; +} + +/* 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 && buffer[len]) + return 0; + return !memcmp(name, buffer, len); +} + +/* + * 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. + */ +struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_page) +{ + const char * name = dentry->d_name.name; + int namelen = dentry->d_name.len; + struct inode * dir = dentry->d_parent->d_inode; + unsigned long start, n; + unsigned long npages = dir_pages(dir); + struct page *page = NULL; + struct sysv_dir_entry *de; + + *res_page = NULL; + + start = SYSV_I(dir)->i_dir_start_lookup; + if (start >= npages) + start = 0; + n = start; + + do { + char *kaddr; + page = dir_get_page(dir, n); + if (!IS_ERR(page)) { + kaddr = (char*)page_address(page); + de = (struct sysv_dir_entry *) kaddr; + kaddr += PAGE_CACHE_SIZE - SYSV_DIRSIZE; + for ( ; (char *) de <= kaddr ; de++) { + if (!de->inode) + continue; + if (namecompare(namelen, SYSV_NAMELEN, + name, de->name)) + goto found; + } + } + dir_put_page(page); + + if (++n >= npages) + n = 0; + } while (n != start); + + return NULL; + +found: + SYSV_I(dir)->i_dir_start_lookup = n; + *res_page = page; + return de; +} + +int sysv_add_link(struct dentry *dentry, struct inode *inode) +{ + struct inode *dir = dentry->d_parent->d_inode; + const char * name = dentry->d_name.name; + int namelen = dentry->d_name.len; + struct page *page = NULL; + struct sysv_dir_entry * de; + unsigned long npages = dir_pages(dir); + unsigned long n; + char *kaddr; + unsigned from, to; + int err; + + /* We take care of directory expansion in the same loop */ + for (n = 0; n <= npages; n++) { + page = dir_get_page(dir, n); + err = PTR_ERR(page); + if (IS_ERR(page)) + goto out; + kaddr = (char*)page_address(page); + de = (struct sysv_dir_entry *)kaddr; + kaddr += PAGE_CACHE_SIZE - SYSV_DIRSIZE; + while ((char *)de <= kaddr) { + if (!de->inode) + goto got_it; + err = -EEXIST; + if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) + goto out_page; + de++; + } + dir_put_page(page); + } + BUG(); + return -EINVAL; + +got_it: + from = (char*)de - (char*)page_address(page); + to = from + SYSV_DIRSIZE; + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + goto out_unlock; + memcpy (de->name, name, namelen); + memset (de->name + namelen, 0, SYSV_DIRSIZE - namelen - 2); + de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino); + err = dir_commit_chunk(page, from, to); + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(dir); +out_page: + dir_put_page(page); +out: + return err; +out_unlock: + unlock_page(page); + goto out_page; +} + +int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = (struct inode*)mapping->host; + char *kaddr = (char*)page_address(page); + unsigned from = (char*)de - kaddr; + unsigned to = from + SYSV_DIRSIZE; + int err; + + lock_page(page); + err = mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + BUG(); + de->inode = 0; + err = dir_commit_chunk(page, from, to); + dir_put_page(page); + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + return err; +} + +int sysv_make_empty(struct inode *inode, struct inode *dir) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page = grab_cache_page(mapping, 0); + struct sysv_dir_entry * de; + char *base; + int err; + + if (!page) + return -ENOMEM; + kmap(page); + err = mapping->a_ops->prepare_write(NULL, page, 0, 2 * SYSV_DIRSIZE); + if (err) { + unlock_page(page); + goto fail; + } + + base = (char*)page_address(page); + memset(base, 0, PAGE_CACHE_SIZE); + + de = (struct sysv_dir_entry *) base; + de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino); + strcpy(de->name,"."); + de++; + de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), dir->i_ino); + strcpy(de->name,".."); + + err = dir_commit_chunk(page, 0, 2 * SYSV_DIRSIZE); +fail: + kunmap(page); + page_cache_release(page); + return err; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +int sysv_empty_dir(struct inode * inode) +{ + struct super_block *sb = inode->i_sb; + struct page *page = NULL; + unsigned long i, npages = dir_pages(inode); + + for (i = 0; i < npages; i++) { + char *kaddr; + struct sysv_dir_entry * de; + page = dir_get_page(inode, i); + + if (IS_ERR(page)) + continue; + + kaddr = (char *)page_address(page); + de = (struct sysv_dir_entry *)kaddr; + kaddr += PAGE_CACHE_SIZE-SYSV_DIRSIZE; + + for ( ;(char *)de <= kaddr; de++) { + if (!de->inode) + continue; + /* check for . and .. */ + if (de->name[0] != '.') + goto not_empty; + if (!de->name[1]) { + if (de->inode == cpu_to_fs16(SYSV_SB(sb), + inode->i_ino)) + continue; + goto not_empty; + } + if (de->name[1] != '.' || de->name[2]) + goto not_empty; + } + dir_put_page(page); + } + return 1; + +not_empty: + dir_put_page(page); + return 0; +} + +/* Releases the page */ +void sysv_set_link(struct sysv_dir_entry *de, struct page *page, + struct inode *inode) +{ + struct inode *dir = (struct inode*)page->mapping->host; + unsigned from = (char *)de-(char*)page_address(page); + unsigned to = from + SYSV_DIRSIZE; + int err; + + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + BUG(); + de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino); + err = dir_commit_chunk(page, from, to); + dir_put_page(page); + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(dir); +} + +struct sysv_dir_entry * sysv_dotdot (struct inode *dir, struct page **p) +{ + struct page *page = dir_get_page(dir, 0); + struct sysv_dir_entry *de = NULL; + + if (!IS_ERR(page)) { + de = (struct sysv_dir_entry*) page_address(page) + 1; + *p = page; + } + return de; +} + +ino_t sysv_inode_by_name(struct dentry *dentry) +{ + struct page *page; + struct sysv_dir_entry *de = sysv_find_entry (dentry, &page); + ino_t res = 0; + + if (de) { + res = fs16_to_cpu(SYSV_SB(dentry->d_sb), de->inode); + dir_put_page(page); + } + return res; +} diff --git a/fs/sysv/file.c b/fs/sysv/file.c new file mode 100644 index 00000000000..da69abc0624 --- /dev/null +++ b/fs/sysv/file.c |