diff -uNr linux.orig/Documentation/Configure.help linux/Documentation/Configure.help --- linux.orig/Documentation/Configure.help Fri Nov 6 20:07:06 1998 +++ linux/Documentation/Configure.help Wed Sep 30 22:22:42 1998 @@ -3377,6 +3377,14 @@ sunsite.unc.edu:/pub/Linux/docs/faqs. This option will enlarge your kernel by about 41 kB. Default is Y. +Second extended filesystem support with 32 bit UIDs +CONFIG_EXT32_FS + This is a horribly hacked version of the ext2 filesystem which has the + ability to use UIDs and GIDs exceeding 65535 for any file or directory. + It will mount ordinary ext2 filesystems and use them in this way. If you + mount a ext32 filesystem with the ext2 driver, all files/directories + owned by a user with a high UID will appear to be owned by UID 65534. + xiafs filesystem support CONFIG_XIA_FS This is an old filesystem (= method to organize files on a harddisk diff -uNr linux.orig/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- linux.orig/arch/i386/kernel/entry.S Fri Nov 6 20:07:17 1998 +++ linux/arch/i386/kernel/entry.S Thu Oct 1 17:46:07 1998 @@ -550,15 +550,15 @@ .long SYMBOL_NAME(sys_time) .long SYMBOL_NAME(sys_mknod) .long SYMBOL_NAME(sys_chmod) /* 15 */ - .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_chown16) .long SYMBOL_NAME(sys_break) .long SYMBOL_NAME(sys_stat) .long SYMBOL_NAME(sys_lseek) .long SYMBOL_NAME(sys_getpid) /* 20 */ .long SYMBOL_NAME(sys_mount) .long SYMBOL_NAME(sys_umount) - .long SYMBOL_NAME(sys_setuid) - .long SYMBOL_NAME(sys_getuid) + .long SYMBOL_NAME(sys_setuid16) + .long SYMBOL_NAME(sys_getuid16) .long SYMBOL_NAME(sys_stime) /* 25 */ .long SYMBOL_NAME(sys_ptrace) .long SYMBOL_NAME(sys_alarm) @@ -580,11 +580,11 @@ .long SYMBOL_NAME(sys_times) .long SYMBOL_NAME(sys_prof) .long SYMBOL_NAME(sys_brk) /* 45 */ - .long SYMBOL_NAME(sys_setgid) - .long SYMBOL_NAME(sys_getgid) + .long SYMBOL_NAME(sys_setgid16) + .long SYMBOL_NAME(sys_getgid16) .long SYMBOL_NAME(sys_signal) - .long SYMBOL_NAME(sys_geteuid) - .long SYMBOL_NAME(sys_getegid) /* 50 */ + .long SYMBOL_NAME(sys_geteuid16) + .long SYMBOL_NAME(sys_getegid16) /* 50 */ .long SYMBOL_NAME(sys_acct) .long SYMBOL_NAME(sys_phys) .long SYMBOL_NAME(sys_lock) @@ -604,8 +604,8 @@ .long SYMBOL_NAME(sys_sigaction) .long SYMBOL_NAME(sys_sgetmask) .long SYMBOL_NAME(sys_ssetmask) - .long SYMBOL_NAME(sys_setreuid) /* 70 */ - .long SYMBOL_NAME(sys_setregid) + .long SYMBOL_NAME(sys_setreuid16) /* 70 */ + .long SYMBOL_NAME(sys_setregid16) .long SYMBOL_NAME(sys_sigsuspend) .long SYMBOL_NAME(sys_sigpending) .long SYMBOL_NAME(sys_sethostname) @@ -614,8 +614,8 @@ .long SYMBOL_NAME(sys_getrusage) .long SYMBOL_NAME(sys_gettimeofday) .long SYMBOL_NAME(sys_settimeofday) - .long SYMBOL_NAME(sys_getgroups) /* 80 */ - .long SYMBOL_NAME(sys_setgroups) + .long SYMBOL_NAME(sys_getgroups16) /* 80 */ + .long SYMBOL_NAME(sys_setgroups16) .long SYMBOL_NAME(old_select) .long SYMBOL_NAME(sys_symlink) .long SYMBOL_NAME(sys_lstat) @@ -629,7 +629,7 @@ .long SYMBOL_NAME(sys_truncate) .long SYMBOL_NAME(sys_ftruncate) .long SYMBOL_NAME(sys_fchmod) - .long SYMBOL_NAME(sys_fchown) /* 95 */ + .long SYMBOL_NAME(sys_fchown16) /* 95 */ .long SYMBOL_NAME(sys_getpriority) .long SYMBOL_NAME(sys_setpriority) .long SYMBOL_NAME(sys_profil) @@ -640,9 +640,9 @@ .long SYMBOL_NAME(sys_syslog) .long SYMBOL_NAME(sys_setitimer) .long SYMBOL_NAME(sys_getitimer) /* 105 */ - .long SYMBOL_NAME(sys_newstat) - .long SYMBOL_NAME(sys_newlstat) - .long SYMBOL_NAME(sys_newfstat) + .long SYMBOL_NAME(sys_newstat16) + .long SYMBOL_NAME(sys_newlstat16) + .long SYMBOL_NAME(sys_newfstat16) .long SYMBOL_NAME(sys_uname) .long SYMBOL_NAME(sys_iopl) /* 110 */ .long SYMBOL_NAME(sys_vhangup) @@ -665,15 +665,15 @@ .long SYMBOL_NAME(sys_init_module) .long SYMBOL_NAME(sys_delete_module) .long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */ - .long SYMBOL_NAME(sys_quotactl) + .long SYMBOL_NAME(sys_quotactl16) .long SYMBOL_NAME(sys_getpgid) .long SYMBOL_NAME(sys_fchdir) .long SYMBOL_NAME(sys_bdflush) .long SYMBOL_NAME(sys_sysfs) /* 135 */ .long SYMBOL_NAME(sys_personality) .long 0 /* for afs_syscall */ - .long SYMBOL_NAME(sys_setfsuid) - .long SYMBOL_NAME(sys_setfsgid) + .long SYMBOL_NAME(sys_setfsuid16) + .long SYMBOL_NAME(sys_setfsgid16) .long SYMBOL_NAME(sys_llseek) /* 140 */ .long SYMBOL_NAME(sys_getdents) .long SYMBOL_NAME(sys_select) @@ -698,6 +698,35 @@ .long SYMBOL_NAME(sys_sched_rr_get_interval) .long SYMBOL_NAME(sys_nanosleep) .long SYMBOL_NAME(sys_mremap) - .long 0,0 + .long 0,0 /* 165 */ .long SYMBOL_NAME(sys_vm86) - .space (NR_syscalls-166)*4 + .long 0,0,0 + +/* Leave space for the Linux 2.1 syscalls */ + + .long 0,0,0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0,0,0 + +/* here are where I've moved the 32 bit UID syscalls */ +/* Chris Wing #include +#include + #include #define __DQUOT_VERSION__ "dquot_5.6.0" @@ -1033,6 +1035,8 @@ struct inode *ino; kdev_t dev; +/* printk(KERN_DEBUG "32bit syscall: sys_quotactl #215\n"); */ + cmds = cmd >> SUBCMDSHIFT; type = cmd & SUBCMDMASK; @@ -1096,4 +1100,19 @@ if (has_quota_enabled(dev, type)) return(set_dqblk(dev, id, type, flags, (struct dqblk *) addr)); return(-ESRCH); +} + +/* 16 bit UID compatibility syscall */ + +/* is this even necessary? did the original int imply a 32 bit value + anyway? */ + +asmlinkage int sys_quotactl16(int cmd, const char *special, int id, caddr_t addr) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_quotactl16 #131\n"); */ + + if(id < 65536) + return( sys_quotactl( cmd, special, id, addr ) ); + else + return(-EINVAL); } diff -uNr linux.orig/fs/ext2/inode.c linux/fs/ext2/inode.c --- linux.orig/fs/ext2/inode.c Fri Nov 6 20:08:12 1998 +++ linux/fs/ext2/inode.c Fri Nov 6 19:07:30 1998 @@ -27,6 +27,8 @@ #include #include +#include + static int ext2_update_inode(struct inode * inode, int do_sync); void ext2_put_inode (struct inode * inode) @@ -577,8 +579,23 @@ raw_inode = (struct ext2_inode *) (bh->b_data + offset); raw_inode->i_mode = inode->i_mode; - raw_inode->i_uid = inode->i_uid; - raw_inode->i_gid = inode->i_gid; + +/* High UIDs will be represented by DEF_HIGHUID when written to an ext2 + filesystem. This prevents getting weird IDs due to the mod 65536 action + + Chris Wing wingc@umich.edu +*/ + + if(inode->i_uid > 65535) + raw_inode->i_uid = DEF_HIGHUID; + else + raw_inode->i_uid = inode->i_uid; + + if(inode->i_gid > 65535) + raw_inode->i_gid = DEF_HIGHUID; + else + raw_inode->i_gid = inode->i_gid; + raw_inode->i_links_count = inode->i_nlink; raw_inode->i_size = inode->i_size; raw_inode->i_atime = inode->i_atime; diff -uNr linux.orig/fs/ext32/CHANGES linux/fs/ext32/CHANGES --- linux.orig/fs/ext32/CHANGES Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/CHANGES Fri Sep 25 18:44:19 1998 @@ -0,0 +1,161 @@ +Recent changes +========================================= + - Hacked by Chris Wing to support 32-bit UIDs + +Changes from version 0.5a to version 0.5b +========================================= + - Now that we have sysctl(), the immutable flag cannot be changed when + the system is running at security level > 0. + - Some cleanups in the code. + - More consistency checks on directories. + - The ext2.diff patch from Tom May has been + integrated. This patch replaces expensive "/" and "%" with + cheap ">>" and "&" where possible. + +Changes from version 0.5 to version 0.5a +======================================== + - Zero the partial block following the end of the file when a file + is truncated. + - Dates updated in the copyright. + - More checks when the filesystem is mounted: the count of blocks, + fragments, and inodes per group is checked against the block size. + - The buffers used by the error routines are now static variables, to + avoid using space on the kernel stack, as requested by Linus. + - Some cleanups in the error messages (some versions of syslog contain + a bug which truncates an error message if it contains '\n'). + - Check that no data can be written to a file past the 2GB limit. + - The famous readdir() bug has been fixed by Stephen Tweedie. + - Added a revision level in the superblock. + - Full support for O_SYNC flag of the open system call. + - New mount options: `resuid=#uid' and `resgid=#gid'. `resuid' causes + ext2fs to consider user #uid like root for the reserved blocks. + `resgid' acts the same way with group #gid. New fields in the + superblock contain default values for resuid and resgid and can + be modified by tune2fs. + Idea comes from Rene Cougnenc . + - New mount options: `bsddf' and `minixdf'. `bsddf' causes ext2fs + to remove the blocks used for FS structures from the total block + count in statfs. With `minixdf', ext2fs mimics Minix behavior + in statfs (i.e. it returns the total number of blocks on the + partition). This is intended to make bde happy :-) + - New file attributes: + - Immutable files cannot be modified. Data cannot be written to + these files. They cannot be removed, renamed and new links cannot + be created. Even root cannot modify the files. He has to remove + the immutable attribute first. + - Append-only files: can only be written in append-mode when writing. + They cannot be removed, renamed and new links cannot be created. + Note: files may only be added to an append-only directory. + - No-dump files: the attribute is not used by the kernel. My port + of dump uses it to avoid backing up files which are not important. + - New check in ext2_check_dir_entry: the inode number is checked. + - Support for big file systems: the copy of the FS descriptor is now + dynamically allocated (previous versions used a fixed size array). + This allows to mount 2GB+ FS. + - Reorganization of the ext2_inode structure to allow other operating + systems to create specific fields if they use ext2fs as their native + file system. Currently, ext2fs is only implemented in Linux but + will soon be part of Gnu Hurd and of Masix. + +Changes from version 0.4b to version 0.5 +======================================== + - New superblock fields: s_lastcheck and s_checkinterval added + by Uwe Ohse to implement timedependent checks + of the file system + - Real random numbers for secure rm added by Pierre del Perugia + + - The mount warnings related to the state of a fs are not printed + if the fs is mounted read-only, idea by Nick Holloway + + +Changes from version 0.4a to version 0.4b +========================================= + - Copyrights changed to include the name of my laboratory. + - Clean up of balloc.c and ialloc.c. + - More consistency checks. + - Block preallocation added by Stephen Tweedie. + - Direct reads of directories disallowed. + - Readahead implemented in readdir by Stephen Tweedie. + - Bugs in block and inodes allocation fixed. + - Readahead implemented in ext2_find_entry by Chip Salzenberg. + - New mount options: + `check=none|normal|strict' + `debug' + `errors=continue|remount-ro|panic' + `grpid', `bsdgroups' + `nocheck' + `nogrpid', `sysvgroups' + - truncate() now tries to deallocate contiguous blocks in a single call + to ext2_free_blocks(). + - lots of cosmetic changes. + +Changes from version 0.4 to version 0.4a +======================================== + - the `sync' option support is now complete. Version 0.4 was not + supporting it when truncating a file. I have tested the synchronous + writes and they work but they make the system very slow :-( I have + to work again on this to make it faster. + - when detecting an error on a mounted filesystem, version 0.4 used + to try to write a flag in the super block even if the filesystem had + been mounted read-only. This is fixed. + - the `sb=#' option now causes the kernel code to use the filesystem + descriptors located at block #+1. Version 0.4 used the superblock + backup located at block # but used the main copy of the descriptors. + - a new file attribute `S' is supported. This attribute causes + synchronous writes but is applied to a file not to the entire file + system (thanks to Michael Kraehe for + suggesting it). + - the directory cache is inhibited by default. The cache management + code seems to be buggy and I have to look at it carefully before + using it again. + - deleting a file with the `s' attribute (secure deletion) causes its + blocks to be overwritten with random values not with zeros (thanks to + Michael A. Griffith for suggesting it). + - lots of cosmetic changes have been made. + +Changes from version 0.3 to version 0.4 +======================================= + - Three new mount options are supported: `check', `sync' and `sb=#'. + `check' tells the kernel code to make more consistency checks + when the file system is mounted. Currently, the kernel code checks + that the blocks and inodes bitmaps are consistent with the free + blocks and inodes counts. More checks will be added in future + releases. + `sync' tells the kernel code to use synchronous writes when updating + an inode, a bitmap, a directory entry or an indirect block. This + can make the file system much slower but can be a big win for files + recovery in case of a crash (and we can now say to the BSD folks + that Linux also supports synchronous updates :-). + `sb=#' tells the kernel code to use an alternate super block instead + of its master copy. `#' is the number of the block (counted in + 1024 bytes blocks) which contains the alternate super block. + An ext2 file system typically contains backups of the super block + at blocks 8193, 16385, and so on. + - I have change the meaning of the valid flag used by e2fsck. it + now contains the state of the file system. If the kernel code + detects an inconsistency while the file system is mounted, it flags + it as erroneous and e2fsck will detect that on next run. + - The super block now contains a mount counter. This counter is + incremented each time the file system is mounted read/write. When + this counter becomes bigger than a maximal mount counts (also stored + in the super block), e2fsck checks the file system, even if it had + been unmounted cleanly, and resets this counter to 0. + - File attributes are now supported. One can associate a set of + attributes to a file. Three attributes are defined: + `c': the file is marked for automatic compression, + `s': the file is marked for secure deletion: when the file is + deleted, its blocks are zeroed and written back to the disk, + `u': the file is marked for undeletion: when the file is deleted, + its contents are saved to allow a future undeletion. + Currently, only the `s' attribute is implemented in the kernel + code. Support for the other attributes will be added in a future + release. + - a few bugs related to times updates have been fixed by Bruce + Evans and me. + - a bug related to the links count of deleted inodes has been fixed. + Previous versions used to keep the links count set to 1 when a file + was deleted. The new version now sets links_count to 0 when deleting + the last link. + - a race condition when deallocating an inode has been fixed by + Stephen Tweedie. + diff -uNr linux.orig/fs/ext32/Makefile linux/fs/ext32/Makefile --- linux.orig/fs/ext32/Makefile Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/Makefile Fri Sep 25 18:47:14 1998 @@ -0,0 +1,15 @@ +# +# Makefile for the linux ext32-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... + +O_TARGET := ext32.o +O_OBJS := acl.o balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ + ioctl.o namei.o super.o symlink.o truncate.o +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -uNr linux.orig/fs/ext32/acl.c linux/fs/ext32/acl.c --- linux.orig/fs/ext32/acl.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/acl.c Fri Sep 25 18:58:56 1998 @@ -0,0 +1,58 @@ +/* + * linux/fs/ext32/acl.c + * + * Copyright (C) 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + */ + +/* + * This file will contain the Access Control Lists management for the + * second extended file system. + */ + +#include +#include +#include +#include +#include + +/* + * ext32_permission () + * + * Check for access rights + */ +int ext32_permission (struct inode * inode, int mask) +{ + unsigned short mode = inode->i_mode; + + /* + * Nobody gets write access to a file on a readonly-fs + */ + if ((mask & S_IWOTH) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) && + IS_RDONLY(inode)) + return -EROFS; + /* + * Nobody gets write access to an immutable file + */ + if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) + return -EACCES; + /* + * Special case, access is always granted for root + */ + if (fsuser()) + return 0; + /* + * If no ACL, checks using the file mode + */ + else if (current->fsuid == inode->i_uid) + mode >>= 6; + else if (in_group_p (inode->i_gid)) + mode >>= 3; + if (((mode & mask & S_IRWXO) == mask)) + return 0; + else + return -EACCES; +} diff -uNr linux.orig/fs/ext32/balloc.c linux/fs/ext32/balloc.c --- linux.orig/fs/ext32/balloc.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/balloc.c Fri Nov 6 21:07:39 1998 @@ -0,0 +1,714 @@ +/* + * linux/fs/ext32/balloc.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + */ + +/* + * balloc.c contains the blocks allocation and deallocation routines + */ + +/* + * The free blocks are managed by bitmaps. A file system contains several + * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap + * block for inodes, N blocks for the inode table and data blocks. + * + * The file system contains group descriptors which are located after the + * super block. Each descriptor contains the number of the bitmap block and + * the free blocks count in the block. The descriptors are loaded in memory + * when a file system is mounted (see ext32_read_super). + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) + +static struct ext32_group_desc * get_group_desc (struct super_block * sb, + unsigned int block_group, + struct buffer_head ** bh) +{ + unsigned long group_desc; + unsigned long desc; + struct ext32_group_desc * gdp; + + /* + * This panic should never trigger on a bad filesystem: the caller + * should have verified the block/group number already. + */ + if (block_group >= sb->u.ext32_sb.s_groups_count) + ext32_panic (sb, "get_group_desc", + "block_group >= groups_count - " + "block_group = %d, groups_count = %lu", + block_group, sb->u.ext32_sb.s_groups_count); + + group_desc = block_group / EXT32_DESC_PER_BLOCK(sb); + desc = block_group % EXT32_DESC_PER_BLOCK(sb); + if (!sb->u.ext32_sb.s_group_desc[group_desc]) + ext32_panic (sb, "get_group_desc", + "Group descriptor not loaded - " + "block_group = %d, group_desc = %lu, desc = %lu", + block_group, group_desc, desc); + gdp = (struct ext32_group_desc *) + sb->u.ext32_sb.s_group_desc[group_desc]->b_data; + if (bh) + *bh = sb->u.ext32_sb.s_group_desc[group_desc]; + return gdp + desc; +} + +/* + * Read the bitmap for a given block_group, reading into the specified + * slot in the superblock's bitmap cache. + * + * Return >=0 on success or a -ve error code. + */ + +static int read_block_bitmap (struct super_block * sb, + unsigned int block_group, + unsigned long bitmap_nr) +{ + struct ext32_group_desc * gdp; + struct buffer_head * bh; + int retval = 0; + + gdp = get_group_desc (sb, block_group, NULL); + bh = bread (sb->s_dev, gdp->bg_block_bitmap, sb->s_blocksize); + if (!bh) { + ext32_error (sb, "read_block_bitmap", + "Cannot read block bitmap - " + "block_group = %d, block_bitmap = %lu", + block_group, (unsigned long) gdp->bg_block_bitmap); + retval = -EIO; + } + /* + * On IO error, just leave a zero in the superblock's block pointer for + * this group. The IO will be retried next time. + */ + sb->u.ext32_sb.s_block_bitmap_number[bitmap_nr] = block_group; + sb->u.ext32_sb.s_block_bitmap[bitmap_nr] = bh; + return retval; +} + +/* + * load_block_bitmap loads the block bitmap for a blocks group + * + * It maintains a cache for the last bitmaps loaded. This cache is managed + * with a LRU algorithm. + * + * Notes: + * 1/ There is one cache per mounted file system. + * 2/ If the file system contains less than EXT32_MAX_GROUP_LOADED groups, + * this function reads the bitmap without maintaining a LRU cache. + * + * Return the slot used to store the bitmap, or a -ve error code. + */ +static int load__block_bitmap (struct super_block * sb, + unsigned int block_group) +{ + int i, j, retval = 0; + unsigned long block_bitmap_number; + struct buffer_head * block_bitmap; + + if (block_group >= sb->u.ext32_sb.s_groups_count) + ext32_panic (sb, "load_block_bitmap", + "block_group >= groups_count - " + "block_group = %d, groups_count = %lu", + block_group, sb->u.ext32_sb.s_groups_count); + + if (sb->u.ext32_sb.s_groups_count <= EXT32_MAX_GROUP_LOADED) { + if (sb->u.ext32_sb.s_block_bitmap[block_group]) { + if (sb->u.ext32_sb.s_block_bitmap_number[block_group] != + block_group) + ext32_panic (sb, "load_block_bitmap", + "block_group != block_bitmap_number"); + else + return block_group; + } else { + retval = read_block_bitmap (sb, block_group, block_group); + if (retval < 0) + return retval; + return block_group; + } + } + + for (i = 0; i < sb->u.ext32_sb.s_loaded_block_bitmaps && + sb->u.ext32_sb.s_block_bitmap_number[i] != block_group; i++) + ; + if (i < sb->u.ext32_sb.s_loaded_block_bitmaps && + sb->u.ext32_sb.s_block_bitmap_number[i] == block_group) { + block_bitmap_number = sb->u.ext32_sb.s_block_bitmap_number[i]; + block_bitmap = sb->u.ext32_sb.s_block_bitmap[i]; + for (j = i; j > 0; j--) { + sb->u.ext32_sb.s_block_bitmap_number[j] = + sb->u.ext32_sb.s_block_bitmap_number[j - 1]; + sb->u.ext32_sb.s_block_bitmap[j] = + sb->u.ext32_sb.s_block_bitmap[j - 1]; + } + sb->u.ext32_sb.s_block_bitmap_number[0] = block_bitmap_number; + sb->u.ext32_sb.s_block_bitmap[0] = block_bitmap; + + /* + * There's still one special case here --- if block_bitmap == 0 + * then our last attempt to read the bitmap failed and we have + * just ended up caching that failure. Try again to read it. + */ + if (!block_bitmap) + retval = read_block_bitmap (sb, block_group, 0); + } else { + if (sb->u.ext32_sb.s_loaded_block_bitmaps < EXT32_MAX_GROUP_LOADED) + sb->u.ext32_sb.s_loaded_block_bitmaps++; + else + brelse (sb->u.ext32_sb.s_block_bitmap[EXT32_MAX_GROUP_LOADED - 1]); + for (j = sb->u.ext32_sb.s_loaded_block_bitmaps - 1; j > 0; j--) { + sb->u.ext32_sb.s_block_bitmap_number[j] = + sb->u.ext32_sb.s_block_bitmap_number[j - 1]; + sb->u.ext32_sb.s_block_bitmap[j] = + sb->u.ext32_sb.s_block_bitmap[j - 1]; + } + retval = read_block_bitmap (sb, block_group, 0); + } + return retval; +} + +/* + * Load the block bitmap for a given block group. First of all do a couple + * of fast lookups for common cases and then pass the request onto the guts + * of the bitmap loader. + * + * Return the slot number of the group in the superblock bitmap cache's on + * success, or a -ve error code. + * + * There is still one inconsistancy here --- if the number of groups in this + * filesystems is <= EXT32_MAX_GROUP_LOADED, then we have no way of + * differentiating between a group for which we have never performed a bitmap + * IO request, and a group for which the last bitmap read request failed. + */ +static inline int load_block_bitmap (struct super_block * sb, + unsigned int block_group) +{ + int slot; + + /* + * Do the lookup for the slot. First of all, check if we're asking + * for the same slot as last time, and did we succeed that last time? + */ + if (sb->u.ext32_sb.s_loaded_block_bitmaps > 0 && + sb->u.ext32_sb.s_block_bitmap_number[0] == block_group && + sb->u.ext32_sb.s_block_bitmap[block_group]) { + slot = 0; + } + /* + * Or can we do a fast lookup based on a loaded group on a filesystem + * small enough to be mapped directly into the superblock? + */ + else if (sb->u.ext32_sb.s_groups_count <= EXT32_MAX_GROUP_LOADED && + sb->u.ext32_sb.s_block_bitmap_number[block_group] == block_group && + sb->u.ext32_sb.s_block_bitmap[block_group]) { + slot = block_group; + } + /* + * If not, then do a full lookup for this block group. + */ + else { + slot = load__block_bitmap (sb, block_group); + } + + /* + * <0 means we just got an error + */ + if (slot < 0) + return slot; + + /* + * If it's a valid slot, we may still have cached a previous IO error, + * in which case the bh in the superblock cache will be zero. + */ + if (!sb->u.ext32_sb.s_block_bitmap[slot]) + return -EIO; + + /* + * Must have been read in OK to get this far. + */ + return slot; +} + +void ext32_free_blocks (const struct inode * inode, unsigned long block, + unsigned long count) +{ + struct buffer_head * bh; + struct buffer_head * bh2; + unsigned long block_group; + unsigned long bit; + unsigned long i; + int bitmap_nr; + struct super_block * sb; + struct ext32_group_desc * gdp; + struct ext32_super_block * es; + + sb = inode->i_sb; + if (!sb) { + printk ("ext32_free_blocks: nonexistent device"); + return; + } + lock_super (sb); + es = sb->u.ext32_sb.s_es; + if (block < es->s_first_data_block || + (block + count) > es->s_blocks_count) { + ext32_error (sb, "ext32_free_blocks", + "Freeing blocks not in datazone - " + "block = %lu, count = %lu", block, count); + unlock_super (sb); + return; + } + + ext32_debug ("freeing block %lu\n", block); + + block_group = (block - es->s_first_data_block) / + EXT32_BLOCKS_PER_GROUP(sb); + bit = (block - es->s_first_data_block) % EXT32_BLOCKS_PER_GROUP(sb); + if (bit + count > EXT32_BLOCKS_PER_GROUP(sb)) { + ext32_error (sb, "ext32_free_blocks", + "Freeing blocks across group boundary - " + "Block = %lu, count = %lu", + block, count); + unlock_super (sb); + return; + } + + bitmap_nr = load_block_bitmap (sb, block_group); + if (bitmap_nr < 0) + goto error_return; + + bh = sb->u.ext32_sb.s_block_bitmap[bitmap_nr]; + gdp = get_group_desc (sb, block_group, &bh2); + + if (ext32_test_opt (sb, CHECK_STRICT) && + (in_range (gdp->bg_block_bitmap, block, count) || + in_range (gdp->bg_inode_bitmap, block, count) || + in_range (block, gdp->bg_inode_table, + sb->u.ext32_sb.s_itb_per_group) || + in_range (block + count - 1, gdp->bg_inode_table, + sb->u.ext32_sb.s_itb_per_group))) { + ext32_error (sb, "ext32_free_blocks", + "Freeing blocks in system zones - " + "Block = %lu, count = %lu", + block, count); + unlock_super (sb); + return; + } + + for (i = 0; i < count; i++) { + if (!clear_bit (bit + i, bh->b_data)) + ext32_warning (sb, "ext32_free_blocks", + "bit already cleared for block %lu", + block); + else { + if (sb->dq_op) + sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)); + gdp->bg_free_blocks_count++; + es->s_free_blocks_count++; + } + } + + mark_buffer_dirty(bh2, 1); + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + + mark_buffer_dirty(bh, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + sb->s_dirt = 1; +error_return: + unlock_super (sb); + return; +} + +/* + * ext32_new_block uses a goal block to assist allocation. If the goal is + * free, or there is a free block within 32 blocks of the goal, that block + * is allocated. Otherwise a forward search is made for a free block; within + * each block group the search first looks for an entire free byte in the block + * bitmap, and then for any free bit if that fails. + */ +int ext32_new_block (const struct inode * inode, unsigned long goal, + u32 * prealloc_count, u32 * prealloc_block, int * err) +{ + struct buffer_head * bh; + struct buffer_head * bh2; + char * p, * r; + int i, j, k, tmp; + int bitmap_nr; + struct super_block * sb; + struct ext32_group_desc * gdp; + struct ext32_super_block * es; + + *err = -ENOSPC; +#ifdef EXT32FS_DEBUG + static int goal_hits = 0, goal_attempts = 0; +#endif + sb = inode->i_sb; + if (!sb) { + printk ("ext32_new_block: nonexistent device"); + return 0; + } + lock_super (sb); + es = sb->u.ext32_sb.s_es; + if (es->s_free_blocks_count <= es->s_r_blocks_count && + (!fsuser() && (sb->u.ext32_sb.s_resuid != current->fsuid) && + (sb->u.ext32_sb.s_resgid == 0 || + !in_group_p (sb->u.ext32_sb.s_resgid)))) { + unlock_super (sb); + return 0; + } + + ext32_debug ("goal=%lu.\n", goal); + +repeat: + /* + * First, test whether the goal block is free. + */ + if (goal < es->s_first_data_block || goal >= es->s_blocks_count) + goal = es->s_first_data_block; + i = (goal - es->s_first_data_block) / EXT32_BLOCKS_PER_GROUP(sb); + gdp = get_group_desc (sb, i, &bh2); + if (gdp->bg_free_blocks_count > 0) { + j = ((goal - es->s_first_data_block) % EXT32_BLOCKS_PER_GROUP(sb)); +#ifdef EXT32FS_DEBUG + if (j) + goal_attempts++; +#endif + bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) { + *err = -EIO; + unlock_super (sb); + return 0; + } + + bh = sb->u.ext32_sb.s_block_bitmap[bitmap_nr]; + + ext32_debug ("goal is at %d:%d.\n", i, j); + + if (!test_bit(j, bh->b_data)) { +#ifdef EXT32FS_DEBUG + goal_hits++; + ext32_debug ("goal bit allocated.\n"); +#endif + goto got_block; + } + if (j) { + /* + * The goal was occupied; search forward for a free + * block within the next XX blocks. + * + * end_goal is more or less random, but it has to be + * less than EXT32_BLOCKS_PER_GROUP. Aligning up to the + * next 64-bit boundary is simple.. + */ + int end_goal = (j + 63) & ~63; + j = find_next_zero_bit(bh->b_data, end_goal, j); + if (j < end_goal) + goto got_block; + } + + ext32_debug ("Bit not found near goal\n"); + + /* + * There has been no free block found in the near vicinity + * of the goal: do a search forward through the block groups, + * searching in each group first for an entire free byte in + * the bitmap and then for any free bit. + * + * Search first in the remainder of the current group; then, + * cyclicly search through the rest of the groups. + */ + p = ((char *) bh->b_data) + (j >> 3); + r = memscan(p, 0, (EXT32_BLOCKS_PER_GROUP(sb) - j + 7) >> 3); + k = (r - ((char *) bh->b_data)) << 3; + if (k < EXT32_BLOCKS_PER_GROUP(sb)) { + j = k; + goto search_back; + } + k = find_next_zero_bit ((unsigned long *) bh->b_data, + EXT32_BLOCKS_PER_GROUP(sb), + j); + if (k < EXT32_BLOCKS_PER_GROUP(sb)) { + j = k; + goto got_block; + } + } + + ext32_debug ("Bit not found in block group %d.\n", i); + + /* + * Now search the rest of the groups. We assume that + * i and gdp correctly point to the last group visited. + */ + for (k = 0; k < sb->u.ext32_sb.s_groups_count; k++) { + i++; + if (i >= sb->u.ext32_sb.s_groups_count) + i = 0; + gdp = get_group_desc (sb, i, &bh2); + if (gdp->bg_free_blocks_count > 0) + break; + } + if (k >= sb->u.ext32_sb.s_groups_count) { + unlock_super (sb); + return 0; + } + + bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) { + *err = -EIO; + unlock_super (sb); + return 0; + } + + bh = sb->u.ext32_sb.s_block_bitmap[bitmap_nr]; + r = memscan(bh->b_data, 0, EXT32_BLOCKS_PER_GROUP(sb) >> 3); + j = (r - bh->b_data) << 3; + if (j < EXT32_BLOCKS_PER_GROUP(sb)) + goto search_back; + else + j = find_first_zero_bit ((unsigned long *) bh->b_data, + EXT32_BLOCKS_PER_GROUP(sb)); + if (j >= EXT32_BLOCKS_PER_GROUP(sb)) { + ext32_error (sb, "ext32_new_block", + "Free blocks count corrupted for block group %d", i); + unlock_super (sb); + return 0; + } + +search_back: + /* + * We have succeeded in finding a free byte in the block + * bitmap. Now search backwards up to 7 bits to find the + * start of this group of free blocks. + */ + for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh->b_data); k++, j--); + +got_block: + + ext32_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count); + + /* + * Check quota for allocation of this block. + */ + if (sb->dq_op) + if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, sb->s_blocksize))) { + unlock_super (sb); + *err = -EDQUOT; + return 0; + } + + tmp = j + i * EXT32_BLOCKS_PER_GROUP(sb) + es->s_first_data_block; + + if (ext32_test_opt (sb, CHECK_STRICT) && + (tmp == gdp->bg_block_bitmap || + tmp == gdp->bg_inode_bitmap || + in_range (tmp, gdp->bg_inode_table, sb->u.ext32_sb.s_itb_per_group))) { + ext32_error (sb, "ext32_new_block", + "Allocating block in system zone - " + "block = %u", tmp); + unlock_super (sb); + *err = -EIO; + return 0; + } + + if (set_bit (j, bh->b_data)) { + ext32_warning (sb, "ext32_new_block", + "bit already set for block %d", j); + if (sb->dq_op) + sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)); + goto repeat; + } + + ext32_debug ("found bit %d\n", j); + + /* + * Do block preallocation now if required. + */ +#ifdef EXT32_PREALLOCATE + if (prealloc_block) { + *prealloc_count = 0; + *prealloc_block = tmp + 1; + for (k = 1; + k < 8 && (j + k) < EXT32_BLOCKS_PER_GROUP(sb); k++) { + if (sb->dq_op) + if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize))) + break; + if (set_bit (j + k, bh->b_data)) { + if (sb->dq_op) + sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)); + break; + } + (*prealloc_count)++; + } + gdp->bg_free_blocks_count -= *prealloc_count; + es->s_free_blocks_count -= *prealloc_count; + ext32_debug ("Preallocated a further %lu bits.\n", + *prealloc_count); + } +#endif + + j = tmp; + + mark_buffer_dirty(bh, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + + if (j >= es->s_blocks_count) { + ext32_error (sb, "ext32_new_block", + "block >= blocks count - " + "block_group = %d, block=%d", i, j); + unlock_super (sb); + return 0; + } + if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) { + ext32_error (sb, "ext32_new_block", "cannot get block %d", j); + unlock_super (sb); + return 0; + } + memset(bh->b_data, 0, sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + brelse (bh); + + ext32_debug ("allocating block %d. " + "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); + + gdp->bg_free_blocks_count--; + mark_buffer_dirty(bh2, 1); + es->s_free_blocks_count--; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + sb->s_dirt = 1; + unlock_super (sb); + *err = 0; + return j; +} + +unsigned long ext32_count_free_blocks (struct super_block * sb) +{ +#ifdef EXT32FS_DEBUG + struct ext32_super_block * es; + unsigned long desc_count, bitmap_count, x; + int bitmap_nr; + struct ext32_group_desc * gdp; + int i; + + lock_super (sb); + es = sb->u.ext32_sb.s_es; + desc_count = 0; + bitmap_count = 0; + gdp = NULL; + for (i = 0; i < sb->u.ext32_sb.s_groups_count; i++) { + gdp = get_group_desc (sb, i, NULL); + desc_count += gdp->bg_free_blocks_count; + + bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + + x = ext32_count_free (sb->u.ext32_sb.s_block_bitmap[bitmap_nr], + sb->s_blocksize); + printk ("group %d: stored = %d, counted = %lu\n", + i, gdp->bg_free_blocks_count, x); + bitmap_count += x; + } + printk("ext32_count_free_blocks: stored = %lu, computed = %lu, %lu\n", + es->s_free_blocks_count, desc_count, bitmap_count); + unlock_super (sb); + return bitmap_count; +#else + return sb->u.ext32_sb.s_es->s_free_blocks_count; +#endif +} + +static inline int block_in_use (unsigned long block, + struct super_block * sb, + unsigned char * map) +{ + return test_bit ((block - sb->u.ext32_sb.s_es->s_first_data_block) % + EXT32_BLOCKS_PER_GROUP(sb), map); +} + +void ext32_check_blocks_bitmap (struct super_block * sb) +{ + struct buffer_head * bh; + struct ext32_super_block * es; + unsigned long desc_count, bitmap_count, x; + unsigned long desc_blocks; + int bitmap_nr; + struct ext32_group_desc * gdp; + int i, j; + + lock_super (sb); + es = sb->u.ext32_sb.s_es; + desc_count = 0; + bitmap_count = 0; + gdp = NULL; + desc_blocks = (sb->u.ext32_sb.s_groups_count + EXT32_DESC_PER_BLOCK(sb) - 1) / + EXT32_DESC_PER_BLOCK(sb); + for (i = 0; i < sb->u.ext32_sb.s_groups_count; i++) { + gdp = get_group_desc (sb, i, NULL); + desc_count += gdp->bg_free_blocks_count; + bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + + bh = sb->u.ext32_sb.s_block_bitmap[bitmap_nr]; + + if (!test_bit (0, bh->b_data)) + ext32_error (sb, "ext32_check_blocks_bitmap", + "Superblock in group %d is marked free", i); + + for (j = 0; j < desc_blocks; j++) + if (!test_bit (j + 1, bh->b_data)) + ext32_error (sb, "ext32_check_blocks_bitmap", + "Descriptor block #%d in group " + "%d is marked free", j, i); + + if (!block_in_use (gdp->bg_block_bitmap, sb, bh->b_data)) + ext32_error (sb, "ext32_check_blocks_bitmap", + "Block bitmap for group %d is marked free", + i); + + if (!block_in_use (gdp->bg_inode_bitmap, sb, bh->b_data)) + ext32_error (sb, "ext32_check_blocks_bitmap", + "Inode bitmap for group %d is marked free", + i); + + for (j = 0; j < sb->u.ext32_sb.s_itb_per_group; j++) + if (!block_in_use (gdp->bg_inode_table + j, sb, bh->b_data)) + ext32_error (sb, "ext32_check_blocks_bitmap", + "Block #%d of the inode table in " + "group %d is marked free", j, i); + + x = ext32_count_free (bh, sb->s_blocksize); + if (gdp->bg_free_blocks_count != x) + ext32_error (sb, "ext32_check_blocks_bitmap", + "Wrong free blocks count for group %d, " + "stored = %d, counted = %lu", i, + gdp->bg_free_blocks_count, x); + bitmap_count += x; + } + if (es->s_free_blocks_count != bitmap_count) + ext32_error (sb, "ext32_check_blocks_bitmap", + "Wrong free blocks count in super block, " + "stored = %lu, counted = %lu", + (unsigned long) es->s_free_blocks_count, bitmap_count); + unlock_super (sb); +} diff -uNr linux.orig/fs/ext32/bitmap.c linux/fs/ext32/bitmap.c --- linux.orig/fs/ext32/bitmap.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/bitmap.c Fri Sep 25 18:47:39 1998 @@ -0,0 +1,26 @@ +/* + * linux/fs/ext32/bitmap.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + */ + +#include +#include + +static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; + +unsigned long ext32_count_free (struct buffer_head * map, unsigned int numchars) +{ + unsigned int i; + unsigned long sum = 0; + + if (!map) + return (0); + for (i = 0; i < numchars; i++) + sum += nibblemap[map->b_data[i] & 0xf] + + nibblemap[(map->b_data[i] >> 4) & 0xf]; + return (sum); +} diff -uNr linux.orig/fs/ext32/dir.c linux/fs/ext32/dir.c --- linux.orig/fs/ext32/dir.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/dir.c Fri Sep 25 18:47:46 1998 @@ -0,0 +1,211 @@ +/* + * linux/fs/ext32/dir.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/dir.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext32 directory handling functions + */ + +#include + +#include +#include +#include +#include +#include + +static int ext32_dir_read (struct inode * inode, struct file * filp, + char * buf, int count) +{ + return -EISDIR; +} + +static int ext32_readdir (struct inode *, struct file *, void *, filldir_t); + +static struct file_operations ext32_dir_operations = { + NULL, /* lseek - default */ + ext32_dir_read, /* read */ + NULL, /* write - bad */ + ext32_readdir, /* readdir */ + NULL, /* select - default */ + ext32_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + ext32_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +/* + * directories can handle most operations... + */ +struct inode_operations ext32_dir_inode_operations = { + &ext32_dir_operations, /* default directory file-ops */ + ext32_create, /* create */ + ext32_lookup, /* lookup */ + ext32_link, /* link */ + ext32_unlink, /* unlink */ + ext32_symlink, /* symlink */ + ext32_mkdir, /* mkdir */ + ext32_rmdir, /* rmdir */ + ext32_mknod, /* mknod */ + ext32_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + ext32_truncate, /* truncate */ + ext32_permission, /* permission */ + NULL /* smap */ +}; + +int ext32_check_dir_entry (const char * function, struct inode * dir, + struct ext32_dir_entry * de, struct buffer_head * bh, + unsigned long offset) +{ + const char * error_msg = NULL; + + if (de->rec_len < EXT32_DIR_REC_LEN(1)) + error_msg = "rec_len is smaller than minimal"; + else if (de->rec_len % 4 != 0) + error_msg = "rec_len % 4 != 0"; + else if (de->rec_len < EXT32_DIR_REC_LEN(de->name_len)) + error_msg = "rec_len is too small for name_len"; + else if (dir && ((char *) de - bh->b_data) + de->rec_len > + dir->i_sb->s_blocksize) + error_msg = "directory entry across blocks"; + else if (dir && de->inode > dir->i_sb->u.ext32_sb.s_es->s_inodes_count) + error_msg = "inode out of bounds"; + + if (error_msg != NULL) + ext32_error (dir->i_sb, function, "bad entry in directory #%lu: %s - " + "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", + dir->i_ino, error_msg, offset, (unsigned long) de->inode, + de->rec_len, de->name_len); + return error_msg == NULL ? 1 : 0; +} + +static int ext32_readdir (struct inode * inode, struct file * filp, + void * dirent, filldir_t filldir) +{ + int error = 0; + unsigned long offset, blk; + int i, num, stored; + struct buffer_head * bh, * tmp, * bha[16]; + struct ext32_dir_entry * de; + struct super_block * sb; + int err; + + if (!inode || !S_ISDIR(inode->i_mode)) + return -EBADF; + sb = inode->i_sb; + + stored = 0; + bh = NULL; + offset = filp->f_pos & (sb->s_blocksize - 1); + + while (!error && !stored && filp->f_pos < inode->i_size) { + blk = (filp->f_pos) >> EXT32_BLOCK_SIZE_BITS(sb); + bh = ext32_bread (inode, blk, 0, &err); + if (!bh) { + ext32_error (sb, "ext32_readdir", + "directory #%lu contains a hole at offset %lu", + inode->i_ino, (unsigned long)filp->f_pos); + filp->f_pos += sb->s_blocksize - offset; + continue; + } + + /* + * Do the readahead + */ + if (!offset) { + for (i = 16 >> (EXT32_BLOCK_SIZE_BITS(sb) - 9), num = 0; + i > 0; i--) { + tmp = ext32_getblk (inode, ++blk, 0, &err); + if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) + bha[num++] = tmp; + else + brelse (tmp); + } + if (num) { + ll_rw_block (READA, num, bha); + for (i = 0; i < num; i++) + brelse (bha[i]); + } + } + +revalidate: + /* If the dir block has changed since the last call to + * readdir(2), then we might be pointing to an invalid + * dirent right now. Scan from the start of the block + * to make sure. */ + if (filp->f_version != inode->i_version) { + for (i = 0; i < sb->s_blocksize && i < offset; ) { + de = (struct ext32_dir_entry *) + (bh->b_data + i); + /* It's too expensive to do a full + * dirent test each time round this + * loop, but we do have to test at + * least that it is non-zero. A + * failure will be detected in the + * dirent test below. */ + if (de->rec_len < EXT32_DIR_REC_LEN(1)) + break; + i += de->rec_len; + } + offset = i; + filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) + | offset; + filp->f_version = inode->i_version; + } + + while (!error && filp->f_pos < inode->i_size + && offset < sb->s_blocksize) { + de = (struct ext32_dir_entry *) (bh->b_data + offset); + if (!ext32_check_dir_entry ("ext32_readdir", inode, de, + bh, offset)) { + /* On error, skip the f_pos to the + next block. */ + filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + + sb->s_blocksize; + brelse (bh); + return stored; + } + offset += de->rec_len; + if (de->inode) { + /* We might block in the next section + * if the data destination is + * currently swapped out. So, use a + * version stamp to detect whether or + * not the directory has been modified + * during the copy operation. */ + unsigned long version; + dcache_add(inode, de->name, de->name_len, de->inode); + version = inode->i_version; + error = filldir(dirent, de->name, de->name_len, filp->f_pos, de->inode); + if (error) + break; + if (version != inode->i_version) + goto revalidate; + stored ++; + } + filp->f_pos += de->rec_len; + } + offset = 0; + brelse (bh); + } + UPDATE_ATIME(inode); + return 0; +} diff -uNr linux.orig/fs/ext32/file.c linux/fs/ext32/file.c --- linux.orig/fs/ext32/file.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/file.c Fri Sep 25 18:47:52 1998 @@ -0,0 +1,209 @@ +/* + * linux/fs/ext32/file.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/file.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext32 fs regular file handling primitives + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NBUF 32 + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +static int ext32_file_write (struct inode *, struct file *, const char *, int); +static void ext32_release_file (struct inode *, struct file *); + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ext32 filesystem. + */ +static struct file_operations ext32_file_operations = { + NULL, /* lseek - default */ + generic_file_read, /* read */ + ext32_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + ext32_ioctl, /* ioctl */ + generic_file_mmap, /* mmap */ + NULL, /* no special open is needed */ + ext32_release_file, /* release */ + ext32_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +struct inode_operations ext32_file_inode_operations = { + &ext32_file_operations,/* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + generic_readpage, /* readpage */ + NULL, /* writepage */ + ext32_bmap, /* bmap */ + ext32_truncate, /* truncate */ + ext32_permission, /* permission */ + NULL /* smap */ +}; + +static int ext32_file_write (struct inode * inode, struct file * filp, + const char * buf, int count) +{ + const loff_t two_gb = 2147483647; + loff_t pos; + off_t pos2; + long block; + int offset; + int written, c; + struct buffer_head * bh, *bufferlist[NBUF]; + struct super_block * sb; + int err; + int i,buffercount,write_error; + + write_error = buffercount = 0; + if (!inode) { + printk("ext32_file_write: inode = NULL\n"); + return -EINVAL; + } + sb = inode->i_sb; + if (sb->s_flags & MS_RDONLY) + /* + * This fs has been automatically remounted ro because of errors + */ + return -ENOSPC; + + if (!S_ISREG(inode->i_mode)) { + ext32_warning (sb, "ext32_file_write", "mode = %07o", + inode->i_mode); + return -EINVAL; + } + if (filp->f_flags & O_APPEND) + pos = inode->i_size; + else + pos = filp->f_pos; + pos2 = (off_t) pos; + /* + * If a file has been opened in synchronous mode, we have to ensure + * that meta-data will also be written synchronously. Thus, we + * set the i_osync field. This field is tested by the allocation + * routines. + */ + if (filp->f_flags & O_SYNC) + inode->u.ext32_i.i_osync++; + block = pos2 >> EXT32_BLOCK_SIZE_BITS(sb); + offset = pos2 & (sb->s_blocksize - 1); + c = sb->s_blocksize - offset; + written = 0; + while (count > 0) { + if (pos > two_gb) { + if (!written) + written = -EFBIG; + break; + } + bh = ext32_getblk (inode, block, 1, &err); + if (!bh) { + if (!written) + written = err; + break; + } + count -= c; + if (count < 0) + c += count; + if (c != sb->s_blocksize && !buffer_uptodate(bh)) { + ll_rw_block (READ, 1, &bh); + wait_on_buffer (bh); + if (!buffer_uptodate(bh)) { + brelse (bh); + if (!written) + written = -EIO; + break; + } + } + memcpy_fromfs (bh->b_data + offset, buf, c); + update_vm_cache(inode, pos, bh->b_data + offset, c); + pos2 += c; + pos += c; + written += c; + buf += c; + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 0); + if (filp->f_flags & O_SYNC) + bufferlist[buffercount++] = bh; + else + brelse(bh); + if (buffercount == NBUF){ + ll_rw_block(WRITE, buffercount, bufferlist); + for(i=0; is_blocksize; + } + if ( buffercount ){ + ll_rw_block(WRITE, buffercount, bufferlist); + for(i=0; i inode->i_size) + inode->i_size = pos; + if (filp->f_flags & O_SYNC) + inode->u.ext32_i.i_osync--; + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + filp->f_pos = pos; + inode->i_dirt = 1; + return written; +} + +/* + * Called when a inode is released. Note that this is different + * from ext32_open: open gets called at every open, but release + * gets called only when /all/ the files are closed. + */ +static void ext32_release_file (struct inode * inode, struct file * filp) +{ + if (filp->f_mode & 2) + ext32_discard_prealloc (inode); +} diff -uNr linux.orig/fs/ext32/fsync.c linux/fs/ext32/fsync.c --- linux.orig/fs/ext32/fsync.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/fsync.c Fri Sep 25 18:47:58 1998 @@ -0,0 +1,192 @@ +/* + * linux/fs/ext32/fsync.c + * + * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk) + * from + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * from + * linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds + * + * ext32fs fsync primitive + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#define blocksize (EXT32_BLOCK_SIZE(inode->i_sb)) +#define addr_per_block (EXT32_ADDR_PER_BLOCK(inode->i_sb)) + +static int sync_block (struct inode * inode, u32 * block, int wait) +{ + struct buffer_head * bh; + int tmp; + + if (!*block) + return 0; + tmp = *block; + bh = get_hash_table (inode->i_dev, *block, blocksize); + if (!bh) + return 0; + if (*block != tmp) { + brelse (bh); + return 1; + } + if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { + brelse (bh); + return -1; + } + if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { + brelse (bh); + return 0; + } + ll_rw_block (WRITE, 1, &bh); + bh->b_count--; + return 0; +} + +static int sync_iblock (struct inode * inode, u32 * iblock, + struct buffer_head ** bh, int wait) +{ + int rc, tmp; + + *bh = NULL; + tmp = *iblock; + if (!tmp) + return 0; + rc = sync_block (inode, iblock, wait); + if (rc) + return rc; + *bh = bread (inode->i_dev, tmp, blocksize); + if (tmp != *iblock) { + brelse (*bh); + *bh = NULL; + return 1; + } + if (!*bh) + return -1; + return 0; +} + + +static int sync_direct (struct inode * inode, int wait) +{ + int i; + int rc, err = 0; + + for (i = 0; i < EXT32_NDIR_BLOCKS; i++) { + rc = sync_block (inode, inode->u.ext32_i.i_data + i, wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + return err; +} + +static int sync_indirect (struct inode * inode, u32 * iblock, int wait) +{ + int i; + struct buffer_head * ind_bh; + int rc, err = 0; + + rc = sync_iblock (inode, iblock, &ind_bh, wait); + if (rc || !ind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_block (inode, + ((u32 *) ind_bh->b_data) + i, + wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + brelse (ind_bh); + return err; +} + +static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) +{ + int i; + struct buffer_head * dind_bh; + int rc, err = 0; + + rc = sync_iblock (inode, diblock, &dind_bh, wait); + if (rc || !dind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_indirect (inode, + ((u32 *) dind_bh->b_data) + i, + wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + brelse (dind_bh); + return err; +} + +static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) +{ + int i; + struct buffer_head * tind_bh; + int rc, err = 0; + + rc = sync_iblock (inode, tiblock, &tind_bh, wait); + if (rc || !tind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_dindirect (inode, + ((u32 *) tind_bh->b_data) + i, + wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + brelse (tind_bh); + return err; +} + +int ext32_sync_file (struct inode * inode, struct file * file) +{ + int wait, err = 0; + + if (S_ISLNK(inode->i_mode) && !(inode->i_blocks)) + /* + * Don't sync fast links! + */ + goto skip; + + for (wait=0; wait<=1; wait++) + { + err |= sync_direct (inode, wait); + err |= sync_indirect (inode, + inode->u.ext32_i.i_data+EXT32_IND_BLOCK, + wait); + err |= sync_dindirect (inode, + inode->u.ext32_i.i_data+EXT32_DIND_BLOCK, + wait); + err |= sync_tindirect (inode, + inode->u.ext32_i.i_data+EXT32_TIND_BLOCK, + wait); + } +skip: + err |= ext32_sync_inode (inode); + return (err < 0) ? -EIO : 0; +} diff -uNr linux.orig/fs/ext32/ialloc.c linux/fs/ext32/ialloc.c --- linux.orig/fs/ext32/ialloc.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/ialloc.c Fri Nov 6 21:07:51 1998 @@ -0,0 +1,563 @@ +/* + * linux/fs/ext32/ialloc.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * BSD ufs-inspired inode and directory allocation by + * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + */ + +/* + * ialloc.c contains the inodes allocation and deallocation routines + */ + +/* + * The free inodes are managed by bitmaps. A file system contains several + * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap + * block for inodes, N blocks for the inode table and data blocks. + * + * The file system contains group descriptors which are located after the + * super block. Each descriptor contains the number of the bitmap block and + * the free blocks count in the block. The descriptors are loaded in memory + * when a file system is mounted (see ext32_read_super). + */ + +#include +#include +#include +#include +#include +#include + +#include + +static struct ext32_group_desc * get_group_desc (struct super_block * sb, + unsigned int block_group, + struct buffer_head ** bh) +{ + unsigned long group_desc; + unsigned long desc; + struct ext32_group_desc * gdp; + + if (block_group >= sb->u.ext32_sb.s_groups_count) + ext32_panic (sb, "get_group_desc", + "block_group >= groups_count - " + "block_group = %d, groups_count = %lu", + block_group, sb->u.ext32_sb.s_groups_count); + + group_desc = block_group / EXT32_DESC_PER_BLOCK(sb); + desc = block_group % EXT32_DESC_PER_BLOCK(sb); + if (!sb->u.ext32_sb.s_group_desc[group_desc]) + ext32_panic (sb, "get_group_desc", + "Group descriptor not loaded - " + "block_group = %d, group_desc = %lu, desc = %lu", + block_group, group_desc, desc); + gdp = (struct ext32_group_desc *) + sb->u.ext32_sb.s_group_desc[group_desc]->b_data; + if (bh) + *bh = sb->u.ext32_sb.s_group_desc[group_desc]; + return gdp + desc; +} + +/* + * Read the inode allocation bitmap for a given block_group, reading + * into the specified slot in the superblock's bitmap cache. + * + * Return >=0 on success or a -ve error code. + */ + +static int read_inode_bitmap (struct super_block * sb, + unsigned long block_group, + unsigned int bitmap_nr) +{ + struct ext32_group_desc * gdp; + struct buffer_head * bh; + int retval = 0; + + gdp = get_group_desc (sb, block_group, NULL); + bh = bread (sb->s_dev, gdp->bg_inode_bitmap, sb->s_blocksize); + if (!bh) { + ext32_error (sb, "read_inode_bitmap", + "Cannot read inode bitmap - " + "block_group = %lu, inode_bitmap = %lu", + block_group, (unsigned long) gdp->bg_inode_bitmap); + retval = -EIO; + } + /* + * On IO error, just leave a zero in the superblock's block pointer for + * this group. The IO will be retried next time. + */ + sb->u.ext32_sb.s_inode_bitmap_number[bitmap_nr] = block_group; + sb->u.ext32_sb.s_inode_bitmap[bitmap_nr] = bh; + return retval; +} + +/* + * load_inode_bitmap loads the inode bitmap for a blocks group + * + * It maintains a cache for the last bitmaps loaded. This cache is managed + * with a LRU algorithm. + * + * Notes: + * 1/ There is one cache per mounted file system. + * 2/ If the file system contains less than EXT32_MAX_GROUP_LOADED groups, + * this function reads the bitmap without maintaining a LRU cache. + * + * Return the slot used to store the bitmap, or a -ve error code. + */ +static int load_inode_bitmap (struct super_block * sb, + unsigned int block_group) +{ + int i, j, retval = 0; + unsigned long inode_bitmap_number; + struct buffer_head * inode_bitmap; + + if (block_group >= sb->u.ext32_sb.s_groups_count) + ext32_panic (sb, "load_inode_bitmap", + "block_group >= groups_count - " + "block_group = %d, groups_count = %lu", + block_group, sb->u.ext32_sb.s_groups_count); + if (sb->u.ext32_sb.s_loaded_inode_bitmaps > 0 && + sb->u.ext32_sb.s_inode_bitmap_number[0] == block_group) + return 0; + if (sb->u.ext32_sb.s_groups_count <= EXT32_MAX_GROUP_LOADED) { + if (sb->u.ext32_sb.s_inode_bitmap[block_group]) { + if (sb->u.ext32_sb.s_inode_bitmap_number[block_group] != block_group) + ext32_panic (sb, "load_inode_bitmap", + "block_group != inode_bitmap_number"); + else + return block_group; + } else { + retval = read_inode_bitmap (sb, block_group, block_group); + if (retval < 0) + return retval; + return block_group; + } + } + + for (i = 0; i < sb->u.ext32_sb.s_loaded_inode_bitmaps && + sb->u.ext32_sb.s_inode_bitmap_number[i] != block_group; + i++) + ; + if (i < sb->u.ext32_sb.s_loaded_inode_bitmaps && + sb->u.ext32_sb.s_inode_bitmap_number[i] == block_group) { + inode_bitmap_number = sb->u.ext32_sb.s_inode_bitmap_number[i]; + inode_bitmap = sb->u.ext32_sb.s_inode_bitmap[i]; + for (j = i; j > 0; j--) { + sb->u.ext32_sb.s_inode_bitmap_number[j] = + sb->u.ext32_sb.s_inode_bitmap_number[j - 1]; + sb->u.ext32_sb.s_inode_bitmap[j] = + sb->u.ext32_sb.s_inode_bitmap[j - 1]; + } + sb->u.ext32_sb.s_inode_bitmap_number[0] = inode_bitmap_number; + sb->u.ext32_sb.s_inode_bitmap[0] = inode_bitmap; + + /* + * There's still one special case here --- if inode_bitmap == 0 + * then our last attempt to read the bitmap failed and we have + * just ended up caching that failure. Try again to read it. + */ + if (!inode_bitmap) + retval = read_inode_bitmap (sb, block_group, 0); + } else { + if (sb->u.ext32_sb.s_loaded_inode_bitmaps < EXT32_MAX_GROUP_LOADED) + sb->u.ext32_sb.s_loaded_inode_bitmaps++; + else + brelse (sb->u.ext32_sb.s_inode_bitmap[EXT32_MAX_GROUP_LOADED - 1]); + for (j = sb->u.ext32_sb.s_loaded_inode_bitmaps - 1; j > 0; j--) { + sb->u.ext32_sb.s_inode_bitmap_number[j] = + sb->u.ext32_sb.s_inode_bitmap_number[j - 1]; + sb->u.ext32_sb.s_inode_bitmap[j] = + sb->u.ext32_sb.s_inode_bitmap[j - 1]; + } + retval = read_inode_bitmap (sb, block_group, 0); + } + return retval; +} + +void ext32_free_inode (struct inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + struct buffer_head * bh2; + unsigned long block_group; + unsigned long bit; + int bitmap_nr; + struct ext32_group_desc * gdp; + struct ext32_super_block * es; + + if (!inode) + return; + if (!inode->i_dev) { + printk ("ext32_free_inode: inode has no device\n"); + return; + } + if (inode->i_count > 1) { + printk ("ext32_free_inode: inode has count=%ld\n", + inode->i_count); + return; + } + if (inode->i_nlink) { + printk ("ext32_free_inode: inode has nlink=%d\n", + inode->i_nlink); + return; + } + if (!inode->i_sb) { + printk("ext32_free_inode: inode on nonexistent device\n"); + return; + } + + ext32_debug ("freeing inode %lu\n", inode->i_ino); + + sb = inode->i_sb; + lock_super (sb); + if (inode->i_ino < EXT32_FIRST_INO(sb) || + inode->i_ino > sb->u.ext32_sb.s_es->s_inodes_count) { + ext32_error (sb, "free_inode", + "reserved inode or nonexistent inode"); + unlock_super (sb); + return; + } + es = sb->u.ext32_sb.s_es; + block_group = (inode->i_ino - 1) / EXT32_INODES_PER_GROUP(sb); + bit = (inode->i_ino - 1) % EXT32_INODES_PER_GROUP(sb); + bitmap_nr = load_inode_bitmap (sb, block_group); + if (bitmap_nr < 0) { + unlock_super (sb); + return; + } + + bh = sb->u.ext32_sb.s_inode_bitmap[bitmap_nr]; + if (!clear_bit (bit, bh->b_data)) + ext32_warning (sb, "ext32_free_inode", + "bit already cleared for inode %lu", inode->i_ino); + else { + gdp = get_group_desc (sb, block_group, &bh2); + gdp->bg_free_inodes_count++; + if (S_ISDIR(inode->i_mode)) + gdp->bg_used_dirs_count--; + mark_buffer_dirty(bh2, 1); + es->s_free_inodes_count++; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + inode->i_dirt = 0; + } + mark_buffer_dirty(bh, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + if (sb->dq_op) + sb->dq_op->free_inode (inode, 1); + sb->s_dirt = 1; + clear_inode (inode); + unlock_super (sb); +} + +/* + * This function increments the inode version number + * + * This may be used one day by the NFS server + */ +static void inc_inode_version (struct inode * inode, + struct ext32_group_desc *gdp, + int mode) +{ + inode->u.ext32_i.i_version++; + inode->i_dirt = 1; + + return; +} + +/* + * There are two policies for allocating an inode. If the new inode is + * a directory, then a forward search is made for a block group with both + * free space and a low directory-to-inode ratio; if that fails, then of + * the groups with above-average free space, that group with the fewest + * directories already is chosen. + * + * For other inodes, search forward from the parent directory\'s block + * group to find a free inode. + */ +struct inode * ext32_new_inode (const struct inode * dir, int mode, int * err) +{ + struct super_block * sb; + struct buffer_head * bh; + struct buffer_head * bh2; + int i, j, avefreei; + struct inode * inode; + int bitmap_nr; + struct ext32_group_desc * gdp; + struct ext32_group_desc * tmp; + struct ext32_super_block * es; + + if (!dir || !(inode = get_empty_inode ())) + { + *err=-ENOMEM; + return NULL; + } + sb = dir->i_sb; + inode->i_sb = sb; + inode->i_flags = sb->s_flags; + lock_super (sb); + es = sb->u.ext32_sb.s_es; +repeat: + gdp = NULL; i=0; + + *err = -ENOSPC; + if (S_ISDIR(mode)) { + avefreei = es->s_free_inodes_count / + sb->u.ext32_sb.s_groups_count; +/* I am not yet convinced that this next bit is necessary. + i = dir->u.ext32_i.i_block_group; + for (j = 0; j < sb->u.ext32_sb.s_groups_count; j++) { + tmp = get_group_desc (sb, i, &bh2); + if ((tmp->bg_used_dirs_count << 8) < + tmp->bg_free_inodes_count) { + gdp = tmp; + break; + } + else + i = ++i % sb->u.ext32_sb.s_groups_count; + } +*/ + if (!gdp) { + for (j = 0; j < sb->u.ext32_sb.s_groups_count; j++) { + tmp = get_group_desc (sb, j, &bh2); + if (tmp->bg_free_inodes_count && + tmp->bg_free_inodes_count >= avefreei) { + if (!gdp || + (tmp->bg_free_blocks_count > + gdp->bg_free_blocks_count)) { + i = j; + gdp = tmp; + } + } + } + } + } + else + { + /* + * Try to place the inode in its parent directory + */ + i = dir->u.ext32_i.i_block_group; + tmp = get_group_desc (sb, i, &bh2); + if (tmp->bg_free_inodes_count) + gdp = tmp; + else + { + /* + * Use a quadratic hash to find a group with a + * free inode + */ + for (j = 1; j < sb->u.ext32_sb.s_groups_count; j <<= 1) { + i += j; + if (i >= sb->u.ext32_sb.s_groups_count) + i -= sb->u.ext32_sb.s_groups_count; + tmp = get_group_desc (sb, i, &bh2); + if (tmp->bg_free_inodes_count) { + gdp = tmp; + break; + } + } + } + if (!gdp) { + /* + * That failed: try linear search for a free inode + */ + i = dir->u.ext32_i.i_block_group + 1; + for (j = 2; j < sb->u.ext32_sb.s_groups_count; j++) { + if (++i >= sb->u.ext32_sb.s_groups_count) + i = 0; + tmp = get_group_desc (sb, i, &bh2); + if (tmp->bg_free_inodes_count) { + gdp = tmp; + break; + } + } + } + } + + if (!gdp) { + unlock_super (sb); + iput(inode); + return NULL; + } + bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) { + unlock_super (sb); + iput(inode); + *err = -EIO; + return NULL; + } + + bh = sb->u.ext32_sb.s_inode_bitmap[bitmap_nr]; + if ((j = find_first_zero_bit ((unsigned long *) bh->b_data, + EXT32_INODES_PER_GROUP(sb))) < + EXT32_INODES_PER_GROUP(sb)) { + if (set_bit (j, bh->b_data)) { + ext32_warning (sb, "ext32_new_inode", + "bit already set for inode %d", j); + goto repeat; + } + mark_buffer_dirty(bh, 1); + if (sb->s_flags & MS_SYNCHRONOUS) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + } else { + if (gdp->bg_free_inodes_count != 0) { + ext32_error (sb, "ext32_new_inode", + "Free inodes count corrupted in group %d", + i); + unlock_super (sb); + iput (inode); + return NULL; + } + goto repeat; + } + j += i * EXT32_INODES_PER_GROUP(sb) + 1; + if (j < EXT32_FIRST_INO(sb) || j > es->s_inodes_count) { + ext32_error (sb, "ext32_new_inode", + "reserved inode or inode > inodes count - " + "block_group = %d,inode=%d", i, j); + unlock_super (sb); + iput (inode); + return NULL; + } + gdp->bg_free_inodes_count--; + if (S_ISDIR(mode)) + gdp->bg_used_dirs_count++; + mark_buffer_dirty(bh2, 1); + es->s_free_inodes_count--; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + sb->s_dirt = 1; + inode->i_mode = mode; + inode->i_sb = sb; + inode->i_count = 1; + inode->i_nlink = 1; + inode->i_dev = sb->s_dev; + inode->i_uid = current->fsuid; + if (ext32_test_opt (sb, GRPID)) + inode->i_gid = dir->i_gid; + else if (dir->i_mode & S_ISGID) { + inode->i_gid = dir->i_gid; + if (S_ISDIR(mode)) + mode |= S_ISGID; + } else + inode->i_gid = current->fsgid; + inode->i_dirt = 1; + inode->i_ino = j; + inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ + inode->i_blocks = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->u.ext32_i.i_new_inode = 1; + inode->u.ext32_i.i_flags = dir->u.ext32_i.i_flags; + if (S_ISLNK(mode)) + inode->u.ext32_i.i_flags &= ~(EXT32_IMMUTABLE_FL | EXT32_APPEND_FL); + inode->u.ext32_i.i_faddr = 0; + inode->u.ext32_i.i_frag_no = 0; + inode->u.ext32_i.i_frag_size = 0; + inode->u.ext32_i.i_file_acl = 0; + inode->u.ext32_i.i_dir_acl = 0; + inode->u.ext32_i.i_dtime = 0; + inode->u.ext32_i.i_block_group = i; + inode->i_op = NULL; + if (inode->u.ext32_i.i_flags & EXT32_SYNC_FL) + inode->i_flags |= MS_SYNCHRONOUS; + insert_inode_hash(inode); + inc_inode_version (inode, gdp, mode); + + unlock_super (sb); + if (sb->dq_op) { + sb->dq_op->initialize (inode, -1); + if (sb->dq_op->alloc_inode (inode, 1)) { + sb->dq_op->drop (inode); + inode->i_nlink = 0; + iput (inode); + *err = -EDQUOT; + return NULL; + } + inode->i_flags |= S_WRITE; + } + ext32_debug ("allocating inode %lu\n", inode->i_ino); + + *err = 0; + return inode; +} + +unsigned long ext32_count_free_inodes (struct super_block * sb) +{ +#ifdef EXT32FS_DEBUG + struct ext32_super_block * es; + unsigned long desc_count, bitmap_count, x; + int bitmap_nr; + struct ext32_group_desc * gdp; + int i; + + lock_super (sb); + es = sb->u.ext32_sb.s_es; + desc_count = 0; + bitmap_count = 0; + gdp = NULL; + for (i = 0; i < sb->u.ext32_sb.s_groups_count; i++) { + gdp = get_group_desc (sb, i, NULL); + desc_count += gdp->bg_free_inodes_count; + bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + + x = ext32_count_free (sb->u.ext32_sb.s_inode_bitmap[bitmap_nr], + EXT32_INODES_PER_GROUP(sb) / 8); + printk ("group %d: stored = %d, counted = %lu\n", + i, gdp->bg_free_inodes_count, x); + bitmap_count += x; + } + printk("ext32_count_free_inodes: stored = %lu, computed = %lu, %lu\n", + es->s_free_inodes_count, desc_count, bitmap_count); + unlock_super (sb); + return desc_count; +#else + return sb->u.ext32_sb.s_es->s_free_inodes_count; +#endif +} + +void ext32_check_inodes_bitmap (struct super_block * sb) +{ + struct ext32_super_block * es; + unsigned long desc_count, bitmap_count, x; + int bitmap_nr; + struct ext32_group_desc * gdp; + int i; + + lock_super (sb); + es = sb->u.ext32_sb.s_es; + desc_count = 0; + bitmap_count = 0; + gdp = NULL; + for (i = 0; i < sb->u.ext32_sb.s_groups_count; i++) { + gdp = get_group_desc (sb, i, NULL); + desc_count += gdp->bg_free_inodes_count; + bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + + x = ext32_count_free (sb->u.ext32_sb.s_inode_bitmap[bitmap_nr], + EXT32_INODES_PER_GROUP(sb) / 8); + if (gdp->bg_free_inodes_count != x) + ext32_error (sb, "ext32_check_inodes_bitmap", + "Wrong free inodes count in group %d, " + "stored = %d, counted = %lu", i, + gdp->bg_free_inodes_count, x); + bitmap_count += x; + } + if (es->s_free_inodes_count != bitmap_count) + ext32_error (sb, "ext32_check_inodes_bitmap", + "Wrong free inodes count in super block, " + "stored = %lu, counted = %lu", + (unsigned long) es->s_free_inodes_count, bitmap_count); + unlock_super (sb); +} diff -uNr linux.orig/fs/ext32/inode.c linux/fs/ext32/inode.c --- linux.orig/fs/ext32/inode.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/inode.c Wed Nov 25 18:42:48 1998 @@ -0,0 +1,671 @@ +/* + * linux/fs/ext32/inode.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/inode.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int ext32_update_inode(struct inode * inode, int do_sync); + +void ext32_put_inode (struct inode * inode) +{ + ext32_discard_prealloc (inode); + if (inode->i_nlink || inode->i_ino == EXT32_ACL_IDX_INO || + inode->i_ino == EXT32_ACL_DATA_INO) + return; + inode->u.ext32_i.i_dtime = CURRENT_TIME; + inode->i_dirt = 1; + ext32_update_inode(inode, IS_SYNC(inode)); + inode->i_size = 0; + if (inode->i_blocks) + ext32_truncate (inode); + ext32_free_inode (inode); +} + +#define inode_bmap(inode, nr) ((inode)->u.ext32_i.i_data[(nr)]) + +static inline int block_bmap (struct buffer_head * bh, int nr) +{ + int tmp; + + if (!bh) + return 0; + tmp = ((u32 *) bh->b_data)[nr]; + brelse (bh); + return tmp; +} + +/* + * ext32_discard_prealloc and ext32_alloc_block are atomic wrt. the + * superblock in the same manner as are ext32_free_blocks and + * ext32_new_block. We just wait on the super rather than locking it + * here, since ext32_new_block will do the necessary locking and we + * can't block until then. + */ +void ext32_discard_prealloc (struct inode * inode) +{ +#ifdef EXT32_PREALLOCATE + unsigned short total; + + if (inode->u.ext32_i.i_prealloc_count) { + total = inode->u.ext32_i.i_prealloc_count; + inode->u.ext32_i.i_prealloc_count = 0; + ext32_free_blocks (inode, inode->u.ext32_i.i_prealloc_block, total); + } +#endif +} + +static int ext32_alloc_block (struct inode * inode, unsigned long goal, int * err) +{ +#ifdef EXT32FS_DEBUG + static unsigned long alloc_hits = 0, alloc_attempts = 0; +#endif + unsigned long result; + struct buffer_head * bh; + + wait_on_super (inode->i_sb); + +#ifdef EXT32_PREALLOCATE + if (inode->u.ext32_i.i_prealloc_count && + (goal == inode->u.ext32_i.i_prealloc_block || + goal + 1 == inode->u.ext32_i.i_prealloc_block)) + { + result = inode->u.ext32_i.i_prealloc_block++; + inode->u.ext32_i.i_prealloc_count--; + ext32_debug ("preallocation hit (%lu/%lu).\n", + ++alloc_hits, ++alloc_attempts); + + /* It doesn't matter if we block in getblk() since + we have already atomically allocated the block, and + are only clearing it now. */ + if (!(bh = getblk (inode->i_sb->s_dev, result, + inode->i_sb->s_blocksize))) { + ext32_error (inode->i_sb, "ext32_alloc_block", + "cannot get block %lu", result); + return 0; + } + memset(bh->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + brelse (bh); + } else { + ext32_discard_prealloc (inode); + ext32_debug ("preallocation miss (%lu/%lu).\n", + alloc_hits, ++alloc_attempts); + if (S_ISREG(inode->i_mode)) + result = ext32_new_block (inode, goal, + &inode->u.ext32_i.i_prealloc_count, + &inode->u.ext32_i.i_prealloc_block, err); + else + result = ext32_new_block (inode, goal, 0, 0, err); + } +#else + result = ext32_new_block (inode, goal, 0, 0, err); +#endif + + return result; +} + + +int ext32_bmap (struct inode * inode, int block) +{ + int i; + int addr_per_block = EXT32_ADDR_PER_BLOCK(inode->i_sb); + int addr_per_block_bits = EXT32_ADDR_PER_BLOCK_BITS(inode->i_sb); + + if (block < 0) { + ext32_warning (inode->i_sb, "ext32_bmap", "block < 0"); + return 0; + } + if (block >= EXT32_NDIR_BLOCKS + addr_per_block + + (1 << (addr_per_block_bits * 2)) + + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { + ext32_warning (inode->i_sb, "ext32_bmap", "block > big"); + return 0; + } + if (block < EXT32_NDIR_BLOCKS) + return inode_bmap (inode, block); + block -= EXT32_NDIR_BLOCKS; + if (block < addr_per_block) { + i = inode_bmap (inode, EXT32_IND_BLOCK); + if (!i) + return 0; + return block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), block); + } + block -= addr_per_block; + if (block < (1 << (addr_per_block_bits * 2))) { + i = inode_bmap (inode, EXT32_DIND_BLOCK); + if (!i) + return 0; + i = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block >> addr_per_block_bits); + if (!i) + return 0; + return block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); + } + block -= (1 << (addr_per_block_bits * 2)); + i = inode_bmap (inode, EXT32_TIND_BLOCK); + if (!i) + return 0; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block >> (addr_per_block_bits * 2)); + if (!i) + return 0; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + (block >> addr_per_block_bits) & (addr_per_block - 1)); + if (!i) + return 0; + return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); +} + +static struct buffer_head * inode_getblk (struct inode * inode, int nr, + int create, int new_block, int * err) +{ + u32 * p; + int tmp, goal = 0; + struct buffer_head * result; + int blocks = inode->i_sb->s_blocksize / 512; + + p = inode->u.ext32_i.i_data + nr; +repeat: + tmp = *p; + if (tmp) { + result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (tmp == *p) + return result; + brelse (result); + goto repeat; + } + if (!create || new_block >= + (current->rlim[RLIMIT_FSIZE].rlim_cur >> + EXT32_BLOCK_SIZE_BITS(inode->i_sb))) { + *err = -EFBIG; + return NULL; + } + if (inode->u.ext32_i.i_next_alloc_block == new_block) + goal = inode->u.ext32_i.i_next_alloc_goal; + + ext32_debug ("hint = %d,", goal); + + if (!goal) { + for (tmp = nr - 1; tmp >= 0; tmp--) { + if (inode->u.ext32_i.i_data[tmp]) { + goal = inode->u.ext32_i.i_data[tmp]; + break; + } + } + if (!goal) + goal = (inode->u.ext32_i.i_block_group * + EXT32_BLOCKS_PER_GROUP(inode->i_sb)) + + inode->i_sb->u.ext32_sb.s_es->s_first_data_block; + } + + ext32_debug ("goal = %d.\n", goal); + + tmp = ext32_alloc_block (inode, goal, err); + if (!tmp) + return NULL; + result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (*p) { + ext32_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + *p = tmp; + inode->u.ext32_i.i_next_alloc_block = new_block; + inode->u.ext32_i.i_next_alloc_goal = tmp; + inode->i_ctime = CURRENT_TIME; + inode->i_blocks += blocks; + if (IS_SYNC(inode) || inode->u.ext32_i.i_osync) + ext32_sync_inode (inode); + else + inode->i_dirt = 1; + return result; +} + +static struct buffer_head * block_getblk (struct inode * inode, + struct buffer_head * bh, int nr, + int create, int blocksize, + int new_block, int * err) +{ + int tmp, goal = 0; + u32 * p; + struct buffer_head * result; + int blocks = inode->i_sb->s_blocksize / 512; + + if (!bh) + return NULL; + if (!buffer_uptodate(bh)) { + ll_rw_block (READ, 1, &bh); + wait_on_buffer (bh); + if (!buffer_uptodate(bh)) { + brelse (bh); + return NULL; + } + } + p = (u32 *) bh->b_data + nr; +repeat: + tmp = *p; + if (tmp) { + result = getblk (bh->b_dev, tmp, blocksize); + if (tmp == *p) { + brelse (bh); + return result; + } + brelse (result); + goto repeat; + } + if (!create || new_block >= + (current->rlim[RLIMIT_FSIZE].rlim_cur >> + EXT32_BLOCK_SIZE_BITS(inode->i_sb))) { + brelse (bh); + *err = -EFBIG; + return NULL; + } + if (inode->u.ext32_i.i_next_alloc_block == new_block) + goal = inode->u.ext32_i.i_next_alloc_goal; + if (!goal) { + for (tmp = nr - 1; tmp >= 0; tmp--) { + if (((u32 *) bh->b_data)[tmp]) { + goal = ((u32 *)bh->b_data)[tmp]; + break; + } + } + if (!goal) + goal = bh->b_blocknr; + } + tmp = ext32_alloc_block (inode, goal, err); + if (!tmp) { + brelse (bh); + return NULL; + } + result = getblk (bh->b_dev, tmp, blocksize); + if (*p) { + ext32_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + *p = tmp; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(inode) || inode->u.ext32_i.i_osync) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + inode->i_ctime = CURRENT_TIME; + inode->i_blocks += blocks; + inode->i_dirt = 1; + inode->u.ext32_i.i_next_alloc_block = new_block; + inode->u.ext32_i.i_next_alloc_goal = tmp; + brelse (bh); + return result; +} + +struct buffer_head * ext32_getblk (struct inode * inode, long block, + int create, int * err) +{ + struct buffer_head * bh; + unsigned long b; + unsigned long addr_per_block = EXT32_ADDR_PER_BLOCK(inode->i_sb); + int addr_per_block_bits = EXT32_ADDR_PER_BLOCK_BITS(inode->i_sb); + + *err = -EIO; + if (block < 0) { + ext32_warning (inode->i_sb, "ext32_getblk", "block < 0"); + return NULL; + } + if (block > EXT32_NDIR_BLOCKS + addr_per_block + + (1 << (addr_per_block_bits * 2)) + + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { + ext32_warning (inode->i_sb, "ext32_getblk", "block > big"); + return NULL; + } + /* + * If this is a sequential block allocation, set the next_alloc_block + * to this block now so that all the indblock and data block + * allocations use the same goal zone + */ + + ext32_debug ("block %lu, next %lu, goal %lu.\n", block, + inode->u.ext32_i.i_next_alloc_block, + inode->u.ext32_i.i_next_alloc_goal); + + if (block == inode->u.ext32_i.i_next_alloc_block + 1) { + inode->u.ext32_i.i_next_alloc_block++; + inode->u.ext32_i.i_next_alloc_goal++; + } + + *err = -ENOSPC; + b = block; + if (block < EXT32_NDIR_BLOCKS) + return inode_getblk (inode, block, create, b, err); + block -= EXT32_NDIR_BLOCKS; + if (block < addr_per_block) { + bh = inode_getblk (inode, EXT32_IND_BLOCK, create, b, err); + return block_getblk (inode, bh, block, create, + inode->i_sb->s_blocksize, b, err); + } + block -= addr_per_block; + if (block < (1 << (addr_per_block_bits * 2))) { + bh = inode_getblk (inode, EXT32_DIND_BLOCK, create, b, err); + bh = block_getblk (inode, bh, block >> addr_per_block_bits, + create, inode->i_sb->s_blocksize, b, err); + return block_getblk (inode, bh, block & (addr_per_block - 1), + create, inode->i_sb->s_blocksize, b, err); + } + block -= (1 << (addr_per_block_bits * 2)); + bh = inode_getblk (inode, EXT32_TIND_BLOCK, create, b, err); + bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2), + create, inode->i_sb->s_blocksize, b, err); + bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1), + create, inode->i_sb->s_blocksize, b, err); + return block_getblk (inode, bh, block & (addr_per_block - 1), create, + inode->i_sb->s_blocksize, b, err); +} + +struct buffer_head * ext32_bread (struct inode * inode, int block, + int create, int *err) +{ + struct buffer_head * bh; + + bh = ext32_getblk (inode, block, create, err); + if (!bh || buffer_uptodate(bh)) + return bh; + ll_rw_block (READ, 1, &bh); + wait_on_buffer (bh); + if (buffer_uptodate(bh)) + return bh; + brelse (bh); + *err = -EIO; + return NULL; +} + +void ext32_read_inode (struct inode * inode) +{ + struct buffer_head * bh; + struct ext32_inode * raw_inode; + unsigned long block_group; + unsigned long group_desc; + unsigned long desc; + unsigned long block; + unsigned long offset; + struct ext32_group_desc * gdp; + + if ((inode->i_ino != EXT32_ROOT_INO && inode->i_ino != EXT32_ACL_IDX_INO && + inode->i_ino != EXT32_ACL_DATA_INO && + inode->i_ino < EXT32_FIRST_INO(inode->i_sb)) || + inode->i_ino > inode->i_sb->u.ext32_sb.s_es->s_inodes_count) { + ext32_error (inode->i_sb, "ext32_read_inode", + "bad inode number: %lu", inode->i_ino); + return; + } + block_group = (inode->i_ino - 1) / EXT32_INODES_PER_GROUP(inode->i_sb); + if (block_group >= inode->i_sb->u.ext32_sb.s_groups_count) + ext32_panic (inode->i_sb, "ext32_read_inode", + "group >= groups count"); + group_desc = block_group >> EXT32_DESC_PER_BLOCK_BITS(inode->i_sb); + desc = block_group & (EXT32_DESC_PER_BLOCK(inode->i_sb) - 1); + bh = inode->i_sb->u.ext32_sb.s_group_desc[group_desc]; + if (!bh) { + ext32_error (inode->i_sb, "ext32_read_inode", + "Descriptor not loaded"); + goto bad_inode; + } + + gdp = (struct ext32_group_desc *) bh->b_data; + /* + * Figure out the offset within the block group inode table + */ + offset = ((inode->i_ino - 1) % EXT32_INODES_PER_GROUP(inode->i_sb)) * + EXT32_INODE_SIZE(inode->i_sb); + block = gdp[desc].bg_inode_table + + (offset >> EXT32_BLOCK_SIZE_BITS(inode->i_sb)); + if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) { + ext32_error (inode->i_sb, "ext32_read_inode", + "unable to read inode block - " + "inode=%lu, block=%lu", inode->i_ino, block); + goto bad_inode; + } + + offset &= (EXT32_BLOCK_SIZE(inode->i_sb) - 1); + raw_inode = (struct ext32_inode *) (bh->b_data + offset); + + inode->i_mode = raw_inode->i_mode; + +/* If the raw UID is MAGIC_HIGHUID, then a high UID is hiding here... */ + + if(raw_inode->i_uid == MAGIC_HIGHUID) { + if(raw_inode->i_reserved2[0] < 65536) +/* they must really mean user 65534 */ + inode->i_uid = MAGIC_HIGHUID; /* oops */ + else + inode->i_uid = raw_inode->i_reserved2[0]; + } + else { + inode->i_uid = raw_inode->i_uid; + } + +/* Same for the GID */ + + if(raw_inode->i_gid == MAGIC_HIGHUID) { + if(raw_inode->i_reserved2[1] < 65536) + inode->i_gid = MAGIC_HIGHUID; /* oops */ + else + inode->i_gid = raw_inode->i_reserved2[1]; + } + else { + inode->i_gid = raw_inode->i_gid; + } + + inode->i_nlink = raw_inode->i_links_count; + inode->i_size = raw_inode->i_size; + inode->i_atime = raw_inode->i_atime; + inode->i_ctime = raw_inode->i_ctime; + inode->i_mtime = raw_inode->i_mtime; + inode->u.ext32_i.i_dtime = raw_inode->i_dtime; + inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ + inode->i_blocks = raw_inode->i_blocks; + inode->i_version = ++event; + inode->u.ext32_i.i_new_inode = 0; + inode->u.ext32_i.i_flags = raw_inode->i_flags; + inode->u.ext32_i.i_faddr = raw_inode->i_faddr; + inode->u.ext32_i.i_frag_no = raw_inode->i_frag; + inode->u.ext32_i.i_frag_size = raw_inode->i_fsize; + inode->u.ext32_i.i_osync = 0; + inode->u.ext32_i.i_file_acl = raw_inode->i_file_acl; + inode->u.ext32_i.i_dir_acl = raw_inode->i_dir_acl; + inode->u.ext32_i.i_version = raw_inode->i_version; + inode->u.ext32_i.i_block_group = block_group; + inode->u.ext32_i.i_next_alloc_block = 0; + inode->u.ext32_i.i_next_alloc_goal = 0; + if (inode->u.ext32_i.i_prealloc_count) + ext32_error (inode->i_sb, "ext32_read_inode", + "New inode has non-zero prealloc count!"); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + inode->i_rdev = to_kdev_t(raw_inode->i_block[0]); + else for (block = 0; block < EXT32_N_BLOCKS; block++) + inode->u.ext32_i.i_data[block] = raw_inode->i_block[block]; + brelse (bh); + inode->i_op = NULL; + if (inode->i_ino == EXT32_ACL_IDX_INO || + inode->i_ino == EXT32_ACL_DATA_INO) + /* Nothing to do */ ; + else if (S_ISREG(inode->i_mode)) + inode->i_op = &ext32_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &ext32_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &ext32_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 (inode->u.ext32_i.i_flags & EXT32_SYNC_FL) + inode->i_flags |= MS_SYNCHRONOUS; + if (inode->u.ext32_i.i_flags & EXT32_APPEND_FL) + inode->i_flags |= S_APPEND; + if (inode->u.ext32_i.i_flags & EXT32_IMMUTABLE_FL) + inode->i_flags |= S_IMMUTABLE; + if (inode->u.ext32_i.i_flags & EXT32_NOATIME_FL) + inode->i_flags |= MS_NOATIME; + return; + +bad_inode: + make_bad_inode(inode); + return; +} + +static int ext32_update_inode(struct inode * inode, int do_sync) +{ + struct buffer_head * bh; + struct ext32_inode * raw_inode; + unsigned long block_group; + unsigned long group_desc; + unsigned long desc; + unsigned long block; + unsigned long offset; + int err = 0; + struct ext32_group_desc * gdp; + + if ((inode->i_ino != EXT32_ROOT_INO && + inode->i_ino < EXT32_FIRST_INO(inode->i_sb)) || + inode->i_ino > inode->i_sb->u.ext32_sb.s_es->s_inodes_count) { + ext32_error (inode->i_sb, "ext32_write_inode", + "bad inode number: %lu", inode->i_ino); + return 0; + } + block_group = (inode->i_ino - 1) / EXT32_INODES_PER_GROUP(inode->i_sb); + if (block_group >= inode->i_sb->u.ext32_sb.s_groups_count) + ext32_panic (inode->i_sb, "ext32_write_inode", + "group >= groups count"); + group_desc = block_group >> EXT32_DESC_PER_BLOCK_BITS(inode->i_sb); + desc = block_group & (EXT32_DESC_PER_BLOCK(inode->i_sb) - 1); + bh = inode->i_sb->u.ext32_sb.s_group_desc[group_desc]; + if (!bh) + ext32_panic (inode->i_sb, "ext32_write_inode", + "Descriptor not loaded"); + gdp = (struct ext32_group_desc *) bh->b_data; + /* + * Figure out the offset within the block group inode table + */ + offset = ((inode->i_ino - 1) % EXT32_INODES_PER_GROUP(inode->i_sb)) * + EXT32_INODE_SIZE(inode->i_sb); + block = gdp[desc].bg_inode_table + + (offset >> EXT32_BLOCK_SIZE_BITS(inode->i_sb)); + if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) { + ext32_error (inode->i_sb, "ext32_write_inode", + "unable to read inode block - " + "inode=%lu, block=%lu", inode->i_ino, block); + /* + * Unfortunately we're in a lose-lose situation. I think that + * keeping the inode in-core with the dirty bit set is + * the worse option, since that will soak up inodes until + * the end of the world. Clearing the dirty bit is nasty if + * we haven't succeeded in writing out, but it's less nasty + * than the alternative. -- sct + */ + inode->i_dirt = 0; + + return -EIO; + } + + offset &= EXT32_BLOCK_SIZE(inode->i_sb) - 1; + raw_inode = (struct ext32_inode *) (bh->b_data + offset); + + raw_inode->i_mode = inode->i_mode; + +/* Encode high UIDs with 16-bit UID of MAGIC_HIGHUID */ + + if((inode->i_uid > 65535) || (inode->i_uid == MAGIC_HIGHUID)) { + raw_inode->i_uid = MAGIC_HIGHUID; + raw_inode->i_reserved2[0] = inode->i_uid; + } + else { + raw_inode->i_uid = inode->i_uid; + } + +/* Same for the GID */ + + if((inode->i_gid > 65535) || (inode->i_gid == MAGIC_HIGHUID)) { + raw_inode->i_gid = MAGIC_HIGHUID; + raw_inode->i_reserved2[1] = inode->i_gid; + } + else { + raw_inode->i_gid = inode->i_gid; + } + + raw_inode->i_links_count = inode->i_nlink; + raw_inode->i_size = inode->i_size; + raw_inode->i_atime = inode->i_atime; + raw_inode->i_ctime = inode->i_ctime; + raw_inode->i_mtime = inode->i_mtime; + raw_inode->i_blocks = inode->i_blocks; + raw_inode->i_dtime = inode->u.ext32_i.i_dtime; + raw_inode->i_flags = inode->u.ext32_i.i_flags; + raw_inode->i_faddr = inode->u.ext32_i.i_faddr; + raw_inode->i_frag = inode->u.ext32_i.i_frag_no; + raw_inode->i_fsize = inode->u.ext32_i.i_frag_size; + raw_inode->i_file_acl = inode->u.ext32_i.i_file_acl; + raw_inode->i_dir_acl = inode->u.ext32_i.i_dir_acl; + raw_inode->i_version = inode->u.ext32_i.i_version; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + raw_inode->i_block[0] = kdev_t_to_nr(inode->i_rdev); + else for (block = 0; block < EXT32_N_BLOCKS; block++) + raw_inode->i_block[block] = inode->u.ext32_i.i_data[block]; + mark_buffer_dirty(bh, 1); + inode->i_dirt = 0; + if (do_sync) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) { + ext32_error (inode->i_sb, + "IO error syncing ext32 inode [" + "%s:%08lx]\n", + kdevname(inode->i_dev), inode->i_ino); + err = -EIO; + } + } + brelse (bh); + return err; +} + +void ext32_write_inode (struct inode * inode) +{ + ext32_update_inode (inode, 0); +} + +int ext32_sync_inode (struct inode *inode) +{ + return ext32_update_inode (inode, 1); +} + diff -uNr linux.orig/fs/ext32/ioctl.c linux/fs/ext32/ioctl.c --- linux.orig/fs/ext32/ioctl.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/ioctl.c Fri Sep 25 18:48:16 1998 @@ -0,0 +1,91 @@ +/* + * linux/fs/ext32/ioctl.c + * + * Copyright (C) 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + */ + +#include + +#include +#include +#include +#include +#include +#include + +int ext32_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, + unsigned long arg) +{ + int err; + unsigned long flags; + + ext32_debug ("cmd = %u, arg = %lu\n", cmd, arg); + + switch (cmd) { + case EXT32_IOC_GETFLAGS: + err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int)); + if (err) + return err; + put_user(inode->u.ext32_i.i_flags, (int *) arg); + return 0; + case EXT32_IOC_SETFLAGS: + err = verify_area(VERIFY_READ, (int *) arg, sizeof(int)); + if (err) + return err; + flags = get_user((int *) arg); + /* + * The IMMUTABLE and APPEND_ONLY flags can only be changed by + * the super user when the security level is zero. + */ + if ((flags & (EXT32_APPEND_FL | EXT32_IMMUTABLE_FL)) ^ + (inode->u.ext32_i.i_flags & + (EXT32_APPEND_FL | EXT32_IMMUTABLE_FL))) { + /* This test looks nicer. Thanks to Pauline Middelink */ + if (!fsuser() || securelevel > 0) + return -EPERM; + } else + if ((current->fsuid != inode->i_uid) && !fsuser()) + return -EPERM; + if (IS_RDONLY(inode)) + return -EROFS; + inode->u.ext32_i.i_flags = flags; + if (flags & EXT32_APPEND_FL) + inode->i_flags |= S_APPEND; + else + inode->i_flags &= ~S_APPEND; + if (flags & EXT32_IMMUTABLE_FL) + inode->i_flags |= S_IMMUTABLE; + else + inode->i_flags &= ~S_IMMUTABLE; + if (flags & EXT32_NOATIME_FL) + inode->i_flags |= MS_NOATIME; + else + inode->i_flags &= ~MS_NOATIME; + inode->i_ctime = CURRENT_TIME; + inode->i_dirt = 1; + return 0; + case EXT32_IOC_GETVERSION: + err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int)); + if (err) + return err; + put_user(inode->u.ext32_i.i_version, (int *) arg); + return 0; + case EXT32_IOC_SETVERSION: + if ((current->fsuid != inode->i_uid) && !fsuser()) + return -EPERM; + if (IS_RDONLY(inode)) + return -EROFS; + err = verify_area(VERIFY_READ, (int *) arg, sizeof(int)); + if (err) + return err; + inode->u.ext32_i.i_version = get_user((int *) arg); + inode->i_ctime = CURRENT_TIME; + inode->i_dirt = 1; + return 0; + default: + return -ENOTTY; + } +} diff -uNr linux.orig/fs/ext32/namei.c linux/fs/ext32/namei.c --- linux.orig/fs/ext32/namei.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/namei.c Fri Sep 25 18:48:21 1998 @@ -0,0 +1,1140 @@ +/* + * linux/fs/ext32/namei.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/namei.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * define how far ahead to read directories while searching them. + */ +#define NAMEI_RA_CHUNKS 2 +#define NAMEI_RA_BLOCKS 4 +#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) +#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) + +/* + * NOTE! unlike strncmp, ext32_match returns 1 for success, 0 for failure. + */ +static int ext32_match (int len, const char * const name, + struct ext32_dir_entry * de) +{ + if (!de || !de->inode || len > EXT32_NAME_LEN) + return 0; + /* + * "" means "." ---> so paths like "/usr/lib//libc.a" work + */ + if (!len && de->name_len == 1 && (de->name[0] == '.') && + (de->name[1] == '\0')) + return 1; + if (len != de->name_len) + return 0; + return !memcmp(name, de->name, len); +} + +/* + * ext32_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 * ext32_find_entry (struct inode * dir, + const char * const name, int namelen, + struct ext32_dir_entry ** res_dir) +{ + struct super_block * sb; + struct buffer_head * bh_use[NAMEI_RA_SIZE]; + struct buffer_head * bh_read[NAMEI_RA_SIZE]; + unsigned long offset; + int block, toread, i, err; + + *res_dir = NULL; + if (!dir) + return NULL; + sb = dir->i_sb; + + if (namelen > EXT32_NAME_LEN) + return NULL; + + memset (bh_use, 0, sizeof (bh_use)); + toread = 0; + for (block = 0; block < NAMEI_RA_SIZE; ++block) { + struct buffer_head * bh; + + if ((block << EXT32_BLOCK_SIZE_BITS (sb)) >= dir->i_size) + break; + bh = ext32_getblk (dir, block, 0, &err); + bh_use[block] = bh; + if (bh && !buffer_uptodate(bh)) + bh_read[toread++] = bh; + } + + for (block = 0, offset = 0; offset < dir->i_size; block++) { + struct buffer_head * bh; + struct ext32_dir_entry * de; + char * dlimit; + + if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { + ll_rw_block (READ, toread, bh_read); + toread = 0; + } + bh = bh_use[block % NAMEI_RA_SIZE]; + if (!bh) { + ext32_error (sb, "ext32_find_entry", + "directory #%lu contains a hole at offset %lu", + dir->i_ino, offset); + offset += sb->s_blocksize; + continue; + } + wait_on_buffer (bh); + if (!buffer_uptodate(bh)) { + /* + * read error: all bets are off + */ + break; + } + + de = (struct ext32_dir_entry *) bh->b_data; + dlimit = bh->b_data + sb->s_blocksize; + while ((char *) de < dlimit) { + if (!ext32_check_dir_entry ("ext32_find_entry", dir, + de, bh, offset)) + goto failure; + if (de->inode != 0 && ext32_match (namelen, name, de)) { + for (i = 0; i < NAMEI_RA_SIZE; ++i) { + if (bh_use[i] != bh) + brelse (bh_use[i]); + } + *res_dir = de; + return bh; + } + offset += de->rec_len; + de = (struct ext32_dir_entry *) + ((char *) de + de->rec_len); + } + + brelse (bh); + if (((block + NAMEI_RA_SIZE) << EXT32_BLOCK_SIZE_BITS (sb)) >= + dir->i_size) + bh = NULL; + else + bh = ext32_getblk (dir, block + NAMEI_RA_SIZE, 0, &err); + bh_use[block % NAMEI_RA_SIZE] = bh; + if (bh && !buffer_uptodate(bh)) + bh_read[toread++] = bh; + } + +failure: + for (i = 0; i < NAMEI_RA_SIZE; ++i) + brelse (bh_use[i]); + return NULL; +} + +int ext32_lookup (struct inode * dir, const char * name, int len, + struct inode ** result) +{ + unsigned long ino; + struct ext32_dir_entry * de; + struct buffer_head * bh; + + *result = NULL; + if (!dir) + return -ENOENT; + if (!S_ISDIR(dir->i_mode)) { + iput (dir); + return -ENOTDIR; + } + if (len > EXT32_NAME_LEN) { + iput (dir); + return -ENAMETOOLONG; + } + if (dcache_lookup(dir, name, len, &ino)) { + if (!ino) { + iput(dir); + return -ENOENT; + } + if (!(*result = iget (dir->i_sb, ino))) { + iput (dir); + return -EACCES; + } + iput (dir); + return 0; + } + ino = dir->i_version; + if (!(bh = ext32_find_entry (dir, name, len, &de))) { + if (ino == dir->i_version) + dcache_add(dir, name, len, 0); + iput (dir); + return -ENOENT; + } + ino = de->inode; + dcache_add(dir, name, len, ino); + brelse (bh); + if (!(*result = iget (dir->i_sb, ino))) { + iput (dir); + return -EACCES; + } + iput (dir); + return 0; +} + +/* + * ext32_add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as ext32_find_entry(). It returns NULL if it failed. + * + * NOTE!! The inode part of 'de' is left at 0 - which means you + * may not sleep between calling this and putting something into + * the entry, as someone else might have used it while you slept. + */ +static struct buffer_head * ext32_add_entry (struct inode * dir, + const char * name, int namelen, + struct ext32_dir_entry ** res_dir, + int *err) +{ + unsigned long offset; + unsigned short rec_len; + struct buffer_head * bh; + struct ext32_dir_entry * de, * de1; + struct super_block * sb; + + *err = -EINVAL; + *res_dir = NULL; + if (!dir) + return NULL; + sb = dir->i_sb; + + if (namelen > EXT32_NAME_LEN) + { + *err = -ENAMETOOLONG; + return NULL; + } + + if (!namelen) + return NULL; + /* + * Is this a busy deleted directory? Can't create new files if so + */ + if (dir->i_size == 0) + { + *err = -ENOENT; + return NULL; + } + bh = ext32_bread (dir, 0, 0, err); + if (!bh) + return NULL; + rec_len = EXT32_DIR_REC_LEN(namelen); + offset = 0; + de = (struct ext32_dir_entry *) bh->b_data; + *err = -ENOSPC; + while (1) { + if ((char *)de >= sb->s_blocksize + bh->b_data) { + brelse (bh); + bh = NULL; + bh = ext32_bread (dir, offset >> EXT32_BLOCK_SIZE_BITS(sb), 1, err); + if (!bh) + return NULL; + if (dir->i_size <= offset) { + if (dir->i_size == 0) { + *err = -ENOENT; + return NULL; + } + + ext32_debug ("creating next block\n"); + + de = (struct ext32_dir_entry *) bh->b_data; + de->inode = 0; + de->rec_len = sb->s_blocksize; + dir->i_size = offset + sb->s_blocksize; + dir->i_dirt = 1; + } else { + + ext32_debug ("skipping to next block\n"); + + de = (struct ext32_dir_entry *) bh->b_data; + } + } + if (!ext32_check_dir_entry ("ext32_add_entry", dir, de, bh, + offset)) { + *err = -ENOENT; + brelse (bh); + return NULL; + } + if (de->inode != 0 && ext32_match (namelen, name, de)) { + *err = -EEXIST; + brelse (bh); + return NULL; + } + if ((de->inode == 0 && de->rec_len >= rec_len) || + (de->rec_len >= EXT32_DIR_REC_LEN(de->name_len) + rec_len)) { + offset += de->rec_len; + if (de->inode) { + de1 = (struct ext32_dir_entry *) ((char *) de + + EXT32_DIR_REC_LEN(de->name_len)); + de1->rec_len = de->rec_len - + EXT32_DIR_REC_LEN(de->name_len); + de->rec_len = EXT32_DIR_REC_LEN(de->name_len); + de = de1; + } + de->inode = 0; + de->name_len = namelen; + memcpy (de->name, name, namelen); + /* + * XXX shouldn't update any times until successful + * completion of syscall, but too many callers depend + * on this. + * + * XXX similarly, too many callers depend on + * ext32_new_inode() setting the times, but error + * recovery deletes the inode, so the worst that can + * happen is that the times are slightly out of date + * and/or different from the directory change time. + */ + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_dirt = 1; + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + *res_dir = de; + *err = 0; + return bh; + } + offset += de->rec_len; + de = (struct ext32_dir_entry *) ((char *) de + de->rec_len); + } + brelse (bh); + return NULL; +} + +/* + * ext32_delete_entry deletes a directory entry by merging it with the + * previous entry + */ +static int ext32_delete_entry (struct ext32_dir_entry * dir, + struct buffer_head * bh) +{ + struct ext32_dir_entry * de, * pde; + int i; + + i = 0; + pde = NULL; + de = (struct ext32_dir_entry *) bh->b_data; + while (i < bh->b_size) { + if (!ext32_check_dir_entry ("ext32_delete_entry", NULL, + de, bh, i)) + return -EIO; + if (de == dir) { + if (pde) + pde->rec_len += dir->rec_len; + dir->inode = 0; + return 0; + } + i += de->rec_len; + pde = de; + de = (struct ext32_dir_entry *) ((char *) de + de->rec_len); + } + return -ENOENT; +} + +int ext32_create (struct inode * dir,const char * name, int len, int mode, + struct inode ** result) +{ + struct inode * inode; + struct buffer_head * bh; + struct ext32_dir_entry * de; + int err; + + *result = NULL; + if (!dir) + return -ENOENT; + inode = ext32_new_inode (dir, mode, &err); + if (!inode) { + iput (dir); + return err; + } + inode->i_op = &ext32_file_inode_operations; + inode->i_mode = mode; + inode->i_dirt = 1; + bh = ext32_add_entry (dir, name, len, &de, &err); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput (inode); + iput (dir); + return err; + } + de->inode = inode->i_ino; + dir->i_version = ++event; + dcache_add(dir, de->name, de->name_len, de->inode); + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + iput (dir); + *result = inode; + return 0; +} + +int ext32_mknod (struct inode * dir, const char * name, int len, int mode, + int rdev) +{ + struct inode * inode; + struct buffer_head * bh; + struct ext32_dir_entry * de; + int err; + + if (!dir) + return -ENOENT; + + if (len > EXT32_NAME_LEN) { + iput (dir); + return -ENAMETOOLONG; + } + bh = ext32_find_entry (dir, name, len, &de); + if (bh) { + brelse (bh); + iput (dir); + return -EEXIST; + } + inode = ext32_new_inode (dir, mode, &err); + if (!inode) { + iput (dir); + return err; + } + inode->i_uid = current->fsuid; + inode->i_mode = mode; + inode->i_op = NULL; + if (S_ISREG(inode->i_mode)) + inode->i_op = &ext32_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &ext32_dir_inode_operations; + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + } + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &ext32_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) + inode->i_op = &chrdev_inode_operations; + else if (S_ISBLK(inode->i_mode)) + inode->i_op = &blkdev_inode_operations; + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); + if (S_ISBLK(mode) || S_ISCHR(mode)) + inode->i_rdev = to_kdev_t(rdev); + inode->i_dirt = 1; + bh = ext32_add_entry (dir, name, len, &de, &err); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput (inode); + iput (dir); + return err; + } + de->inode = inode->i_ino; + dir->i_version = ++event; + dcache_add(dir, de->name, de->name_len, de->inode); + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + iput (dir); + iput (inode); + return 0; +} + +int ext32_mkdir (struct inode * dir, const char * name, int len, int mode) +{ + struct inode * inode; + struct buffer_head * bh, * dir_block; + struct ext32_dir_entry * de; + int err; + + if (!dir) + return -ENOENT; + if (len > EXT32_NAME_LEN) { + iput (dir); + return -ENAMETOOLONG; + } + bh = ext32_find_entry (dir, name, len, &de); + if (bh) { + brelse (bh); + iput (dir); + return -EEXIST; + } + if (dir->i_nlink >= EXT32_LINK_MAX) { + iput (dir); + return -EMLINK; + } + inode = ext32_new_inode (dir, S_IFDIR, &err); + if (!inode) { + iput (dir); + return err; + } + inode->i_op = &ext32_dir_inode_operations; + inode->i_size = inode->i_sb->s_blocksize; + dir_block = ext32_bread (inode, 0, 1, &err); + if (!dir_block) { + iput (dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput (inode); + return err; + } + inode->i_blocks = inode->i_sb->s_blocksize / 512; + de = (struct ext32_dir_entry *) dir_block->b_data; + de->inode = inode->i_ino; + de->name_len = 1; + de->rec_len = EXT32_DIR_REC_LEN(de->name_len); + strcpy (de->name, "."); + de = (struct ext32_dir_entry *) ((char *) de + de->rec_len); + de->inode = dir->i_ino; + de->rec_len = inode->i_sb->s_blocksize - EXT32_DIR_REC_LEN(1); + de->name_len = 2; + strcpy (de->name, ".."); + inode->i_nlink = 2; + mark_buffer_dirty(dir_block, 1); + brelse (dir_block); + inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + inode->i_dirt = 1; + bh = ext32_add_entry (dir, name, len, &de, &err); + if (!bh) { + iput (dir); + inode->i_nlink = 0; + inode->i_dirt = 1; + iput (inode); + return err; + } + de->inode = inode->i_ino; + dir->i_version = ++event; + dcache_add(dir, de->name, de->name_len, de->inode); + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + 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) +{ + unsigned long offset; + struct buffer_head * bh; + struct ext32_dir_entry * de, * de1; + struct super_block * sb; + int err; + + sb = inode->i_sb; + if (inode->i_size < EXT32_DIR_REC_LEN(1) + EXT32_DIR_REC_LEN(2) || + !(bh = ext32_bread (inode, 0, 0, &err))) { + ext32_warning (inode->i_sb, "empty_dir", + "bad directory (dir #%lu) - no data block", + inode->i_ino); + return 1; + } + de = (struct ext32_dir_entry *) bh->b_data; + de1 = (struct ext32_dir_entry *) ((char *) de + de->rec_len); + if (de->inode != inode->i_ino || !de1->inode || + strcmp (".", de->name) || strcmp ("..", de1->name)) { + ext32_warning (inode->i_sb, "empty_dir", + "bad directory (dir #%lu) - no `.' or `..'", + inode->i_ino); + brelse (bh); + return 1; + } + offset = de->rec_len + de1->rec_len; + de = (struct ext32_dir_entry *) ((char *) de1 + de1->rec_len); + while (offset < inode->i_size ) { + if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { + brelse (bh); + bh = ext32_bread (inode, offset >> EXT32_BLOCK_SIZE_BITS(sb), 1, &err); + if (!bh) { + ext32_error (sb, "empty_dir", + "directory #%lu contains a hole at offset %lu", + inode->i_ino, offset); + offset += sb->s_blocksize; + continue; + } + de = (struct ext32_dir_entry *) bh->b_data; + } + if (!ext32_check_dir_entry ("empty_dir", inode, de, bh, + offset)) { + brelse (bh); + return 1; + } + if (de->inode) { + brelse (bh); + return 0; + } + offset += de->rec_len; + de = (struct ext32_dir_entry *) ((char *) de + de->rec_len); + } + brelse (bh); + return 1; +} + +int ext32_rmdir (struct inode * dir, const char * name, int len) +{ + int retval; + struct inode * inode; + struct buffer_head * bh; + struct ext32_dir_entry * de; + +repeat: + if (!dir) + return -ENOENT; + inode = NULL; + if (len > EXT32_NAME_LEN) { + iput (dir); + return -ENAMETOOLONG; + } + bh = ext32_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 (inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize (inode, -1); + if (inode->i_dev != dir->i_dev) { + retval = -EBUSY; + goto end_rmdir; + } + 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_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; + } + down(&inode->i_sem); + if (!empty_dir (inode)) + retval = -ENOTEMPTY; + else if (de->inode != inode->i_ino) + retval = -ENOENT; + else { + if (inode->i_count > 1) { + /* + * Are we deleting the last instance of a busy directory? + * Better clean up if so. + * + * Make directory empty (it will be truncated when finally + * dereferenced). This also inhibits ext32_add_entry. + */ + inode->i_size = 0; + } + retval = ext32_delete_entry (de, bh); + dir->i_version = ++event; + } + up(&inode->i_sem); + if (retval) + goto end_rmdir; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + if (inode->i_nlink != 2) + ext32_warning (inode->i_sb, "ext32_rmdir", + "empty directory has nlink!=2 (%d)", + inode->i_nlink); + inode->i_version = ++event; + 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; +end_rmdir: + iput (dir); + iput (inode); + brelse (bh); + return retval; +} + +int ext32_unlink (struct inode * dir, const char * name, int len) +{ + int retval; + struct inode * inode; + struct buffer_head * bh; + struct ext32_dir_entry * de; + +repeat: + if (!dir) + return -ENOENT; + retval = -ENOENT; + inode = NULL; + if (len > EXT32_NAME_LEN) { + iput (dir); + return -ENAMETOOLONG; + } + bh = ext32_find_entry (dir, name, len, &de); + if (!bh) + goto end_unlink; + if (!(inode = iget (dir->i_sb, de->inode))) + goto end_unlink; + if (inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize (inode, -1); + retval = -EPERM; + if (S_ISDIR(inode->i_mode)) + goto end_unlink; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + goto end_unlink; + if (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 (!inode->i_nlink) { + ext32_warning (inode->i_sb, "ext32_unlink", + "Deleting nonexistent file (%lu), %d", + inode->i_ino, inode->i_nlink); + inode->i_nlink = 1; + } + retval = ext32_delete_entry (de, bh); + if (retval) + goto end_unlink; + dir->i_version = ++event; + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt = 1; + inode->i_nlink--; + inode->i_dirt = 1; + inode->i_ctime = dir->i_ctime; + retval = 0; +end_unlink: + brelse (bh); + iput (inode); + iput (dir); + return retval; +} + +int ext32_symlink (struct inode * dir, const char * name, int len, + const char * symname) +{ + struct ext32_dir_entry * de; + struct inode * inode = NULL; + struct buffer_head * bh = NULL, * name_block = NULL; + char * link; + int i, err; + int l; + char c; + + if (!(inode = ext32_new_inode (dir, S_IFLNK, &err))) { + iput (dir); + return err; + } + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &ext32_symlink_inode_operations; + for (l = 0; l < inode->i_sb->s_blocksize - 1 && + symname [l]; l++) + ; + if (l >= sizeof (inode->u.ext32_i.i_data)) { + + ext32_debug ("l=%d, normal symlink\n", l); + + name_block = ext32_bread (inode, 0, 1, &err); + if (!name_block) { + iput (dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput (inode); + return err; + } + link = name_block->b_data; + } else { + link = (char *) inode->u.ext32_i.i_data; + + ext32_debug ("l=%d, fast symlink\n", l); + + } + i = 0; + while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++))) + link[i++] = c; + link[i] = 0; + if (name_block) { + mark_buffer_dirty(name_block, 1); + brelse (name_block); + } + inode->i_size = i; + inode->i_dirt = 1; + + bh = ext32_find_entry (dir, name, len, &de); + if (bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput (inode); + brelse (bh); + iput (dir); + return -EEXIST; + } + bh = ext32_add_entry (dir, name, len, &de, &err); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput (inode); + iput (dir); + return err; + } + de->inode = inode->i_ino; + dir->i_version = ++event; + dcache_add(dir, de->name, de->name_len, de->inode); + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + iput (dir); + iput (inode); + return 0; +} + +int ext32_link (struct inode * oldinode, struct inode * dir, + const char * name, int len) +{ + struct ext32_dir_entry * de; + struct buffer_head * bh; + int err; + + if (S_ISDIR(oldinode->i_mode)) { + iput (oldinode); + iput (dir); + return -EPERM; + } + if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) { + iput (oldinode); + iput (dir); + return -EPERM; + } + if (oldinode->i_nlink >= EXT32_LINK_MAX) { + iput (oldinode); + iput (dir); + return -EMLINK; + } + bh = ext32_find_entry (dir, name, len, &de); + if (bh) { + brelse (bh); + iput (dir); + iput (oldinode); + return -EEXIST; + } + bh = ext32_add_entry (dir, name, len, &de, &err); + if (!bh) { + iput (dir); + iput (oldinode); + return err; + } + de->inode = oldinode->i_ino; + dir->i_version = ++event; + dcache_add(dir, de->name, de->name_len, de->inode); + mark_buffer_dirty(bh, 1); + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + iput (dir); + oldinode->i_nlink++; + oldinode->i_ctime = CURRENT_TIME; + oldinode->i_dirt = 1; + iput (oldinode); + return 0; +} + +static int subdir (struct inode * new_inode, struct inode * old_inode) +{ + int ino; + int result; + + new_inode->i_count++; + result = 0; + for (;;) { + if (new_inode == old_inode) { + result = 1; + break; + } + if (new_inode->i_dev != old_inode->i_dev) + break; + ino = new_inode->i_ino; + if (ext32_lookup (new_inode, "..", 2, &new_inode)) + break; + if (new_inode->i_ino == ino) + break; + } + iput (new_inode); + return result; +} + +#define PARENT_INO(buffer) \ + ((struct ext32_dir_entry *) ((char *) buffer + \ + ((struct ext32_dir_entry *) buffer)->rec_len))->inode + +#define PARENT_NAME(buffer) \ + ((struct ext32_dir_entry *) ((char *) buffer + \ + ((struct ext32_dir_entry *) buffer)->rec_len))->name + +/* + * 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_ext32_rename (struct inode * old_dir, const char * old_name, + int old_len, struct inode * new_dir, + const char * new_name, int new_len, + int must_be_dir) +{ + struct inode * old_inode, * new_inode; + struct buffer_head * old_bh, * new_bh, * dir_bh; + struct ext32_dir_entry * old_de, * new_de; + int retval; + + goto start_up; +try_again: + if (new_bh && new_de) { + ext32_delete_entry(new_de, new_bh); + new_dir->i_version = ++event; + } + 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; + new_de = NULL; + retval = -ENAMETOOLONG; + if (old_len > EXT32_NAME_LEN) + goto end_rename; + + old_bh = ext32_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; + if (must_be_dir && !S_ISDIR(old_inode->i_mode)) + 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; + if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode)) + goto end_rename; + new_bh = ext32_find_entry (new_dir, new_name, new_len, &new_de); + if (new_bh) { + new_inode = __iget (new_dir->i_sb, new_de->inode, 0); /* no mntp cross */ + if (!new_inode) { + brelse (new_bh); + new_bh = NULL; + } else { + if (new_inode->i_sb->dq_op) + new_inode->i_sb->dq_op->initialize (new_inode, -1); + } + } + 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) { + if ((new_dir->i_mode & S_ISVTX) && + current->fsuid != new_inode->i_uid && + current->fsuid != new_dir->i_uid && !fsuser()) + goto end_rename; + if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode)) + goto end_rename; + } + + if (S_ISDIR(old_inode->i_mode)) { + retval = -ENOTDIR; + if (new_inode && !S_ISDIR(new_inode->i_mode)) + goto end_rename; + retval = -EINVAL; + if (subdir (new_dir, old_inode)) + goto end_rename; + dir_bh = ext32_bread (old_inode, 0, 0, &retval); + 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 >= EXT32_LINK_MAX) + goto end_rename; + } + if (!new_bh) + new_bh = ext32_add_entry (new_dir, new_name, new_len, &new_de, + &retval); + if (!new_bh) + goto end_rename; + new_dir->i_version = ++event; + /* + * 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 + */ + new_de->inode = old_inode->i_ino; + dcache_add(new_dir, new_de->name, new_de->name_len, new_de->inode); + retval = ext32_delete_entry (old_de, old_bh); + if (retval == -ENOENT) + goto try_again; + if (retval) + goto end_rename; + old_dir->i_version = ++event; + if (new_inode) { + new_inode->i_nlink--; + new_inode->i_ctime = CURRENT_TIME; + new_inode->i_dirt = 1; + } + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + old_dir->i_dirt = 1; + if (dir_bh) { + PARENT_INO(dir_bh->b_data) = new_dir->i_ino; + dcache_add(old_inode, "..", 2, 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; + } + } + mark_buffer_dirty(old_bh, 1); + if (IS_SYNC(old_dir)) { + ll_rw_block (WRITE, 1, &old_bh); + wait_on_buffer (old_bh); + } + mark_buffer_dirty(new_bh, 1); + if (IS_SYNC(new_dir)) { + ll_rw_block (WRITE, 1, &new_bh); + wait_on_buffer (new_bh); + } + 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. + * + * In the second extended file system, we use a lock flag stored in the memory + * super-block. This way, we really lock other renames only if they occur + * on the same file system + */ +int ext32_rename (struct inode * old_dir, const char * old_name, int old_len, + struct inode * new_dir, const char * new_name, int new_len, + int must_be_dir) +{ + int result; + + while (old_dir->i_sb->u.ext32_sb.s_rename_lock) + sleep_on (&old_dir->i_sb->u.ext32_sb.s_rename_wait); + old_dir->i_sb->u.ext32_sb.s_rename_lock = 1; + result = do_ext32_rename (old_dir, old_name, old_len, new_dir, + new_name, new_len, must_be_dir); + old_dir->i_sb->u.ext32_sb.s_rename_lock = 0; + wake_up (&old_dir->i_sb->u.ext32_sb.s_rename_wait); + return result; +} diff -uNr linux.orig/fs/ext32/super.c linux/fs/ext32/super.c --- linux.orig/fs/ext32/super.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/super.c Fri Nov 6 21:06:53 1998 @@ -0,0 +1,740 @@ +/* + * linux/fs/ext32/super.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/inode.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static char error_buf[1024]; + +void ext32_error (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + va_list args; + + if (!(sb->s_flags & MS_RDONLY)) { + sb->u.ext32_sb.s_mount_state |= EXT32_ERROR_FS; + sb->u.ext32_sb.s_es->s_state |= EXT32_ERROR_FS; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + sb->s_dirt = 1; + } + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + if (ext32_test_opt (sb, ERRORS_PANIC) || + (sb->u.ext32_sb.s_es->s_errors == EXT32_ERRORS_PANIC && + !ext32_test_opt (sb, ERRORS_CONT) && !ext32_test_opt (sb, ERRORS_RO))) + panic ("EXT32-fs panic (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); + printk (KERN_CRIT "EXT32-fs error (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); + if (ext32_test_opt (sb, ERRORS_RO) || + (sb->u.ext32_sb.s_es->s_errors == EXT32_ERRORS_RO && + !ext32_test_opt (sb, ERRORS_CONT) && !ext32_test_opt (sb, ERRORS_PANIC))) { + printk ("Remounting filesystem read-only\n"); + sb->s_flags |= MS_RDONLY; + } +} + +NORET_TYPE void ext32_panic (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + va_list args; + + if (!(sb->s_flags & MS_RDONLY)) { + sb->u.ext32_sb.s_mount_state |= EXT32_ERROR_FS; + sb->u.ext32_sb.s_es->s_state |= EXT32_ERROR_FS; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + sb->s_dirt = 1; + } + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + /* this is to prevent panic from syncing this filesystem */ + if (sb->s_lock) + sb->s_lock=0; + sb->s_flags |= MS_RDONLY; + panic ("EXT32-fs panic (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +} + +void ext32_warning (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + printk (KERN_WARNING "EXT32-fs warning (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +} + +void ext32_put_super (struct super_block * sb) +{ + int db_count; + int i; + + lock_super (sb); + if (!(sb->s_flags & MS_RDONLY)) { + sb->u.ext32_sb.s_es->s_state = sb->u.ext32_sb.s_mount_state; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + } + sb->s_dev = 0; + db_count = sb->u.ext32_sb.s_db_per_group; + for (i = 0; i < db_count; i++) + if (sb->u.ext32_sb.s_group_desc[i]) + brelse (sb->u.ext32_sb.s_group_desc[i]); + kfree_s (sb->u.ext32_sb.s_group_desc, + db_count * sizeof (struct buffer_head *)); + for (i = 0; i < EXT32_MAX_GROUP_LOADED; i++) + if (sb->u.ext32_sb.s_inode_bitmap[i]) + brelse (sb->u.ext32_sb.s_inode_bitmap[i]); + for (i = 0; i < EXT32_MAX_GROUP_LOADED; i++) + if (sb->u.ext32_sb.s_block_bitmap[i]) + brelse (sb->u.ext32_sb.s_block_bitmap[i]); + brelse (sb->u.ext32_sb.s_sbh); + unlock_super (sb); + MOD_DEC_USE_COUNT; + return; +} + +static struct super_operations ext32_sops = { + ext32_read_inode, + NULL, + ext32_write_inode, + ext32_put_inode, + ext32_put_super, + ext32_write_super, + ext32_statfs, + ext32_remount +}; + +/* + * This function has been shamelessly adapted from the msdos fs + */ +static int parse_options (char * options, unsigned long * sb_block, + unsigned short *resuid, unsigned short * resgid, + unsigned long * mount_options) +{ + char * this_char; + char * value; + + if (!options) + return 1; + for (this_char = strtok (options, ","); + this_char != NULL; + this_char = strtok (NULL, ",")) { + if ((value = strchr (this_char, '=')) != NULL) + *value++ = 0; + if (!strcmp (this_char, "bsddf")) + ext32_clear_opt (*mount_options, MINIX_DF); + else if (!strcmp (this_char, "check")) { + if (!value || !*value) + ext32_set_opt (*mount_options, CHECK_NORMAL); + else if (!strcmp (value, "none")) { + ext32_clear_opt (*mount_options, CHECK_NORMAL); + ext32_clear_opt (*mount_options, CHECK_STRICT); + } + else if (!strcmp (value, "normal")) + ext32_set_opt (*mount_options, CHECK_NORMAL); + else if (!strcmp (value, "strict")) { + ext32_set_opt (*mount_options, CHECK_NORMAL); + ext32_set_opt (*mount_options, CHECK_STRICT); + } + else { + printk ("EXT32-fs: Invalid check option: %s\n", + value); + return 0; + } + } + else if (!strcmp (this_char, "debug")) + ext32_set_opt (*mount_options, DEBUG); + else if (!strcmp (this_char, "errors")) { + if (!value || !*value) { + printk ("EXT32-fs: the errors option requires " + "an argument"); + return 0; + } + if (!strcmp (value, "continue")) { + ext32_clear_opt (*mount_options, ERRORS_RO); + ext32_clear_opt (*mount_options, ERRORS_PANIC); + ext32_set_opt (*mount_options, ERRORS_CONT); + } + else if (!strcmp (value, "remount-ro")) { + ext32_clear_opt (*mount_options, ERRORS_CONT); + ext32_clear_opt (*mount_options, ERRORS_PANIC); + ext32_set_opt (*mount_options, ERRORS_RO); + } + else if (!strcmp (value, "panic")) { + ext32_clear_opt (*mount_options, ERRORS_CONT); + ext32_clear_opt (*mount_options, ERRORS_RO); + ext32_set_opt (*mount_options, ERRORS_PANIC); + } + else { + printk ("EXT32-fs: Invalid errors option: %s\n", + value); + return 0; + } + } + else if (!strcmp (this_char, "grpid") || + !strcmp (this_char, "bsdgroups")) + ext32_set_opt (*mount_options, GRPID); + else if (!strcmp (this_char, "minixdf")) + ext32_set_opt (*mount_options, MINIX_DF); + else if (!strcmp (this_char, "nocheck")) { + ext32_clear_opt (*mount_options, CHECK_NORMAL); + ext32_clear_opt (*mount_options, CHECK_STRICT); + } + else if (!strcmp (this_char, "nogrpid") || + !strcmp (this_char, "sysvgroups")) + ext32_clear_opt (*mount_options, GRPID); + else if (!strcmp (this_char, "resgid")) { + if (!value || !*value) { + printk ("EXT32-fs: the resgid option requires " + "an argument"); + return 0; + } + *resgid = simple_strtoul (value, &value, 0); + if (*value) { + printk ("EXT32-fs: Invalid resgid option: %s\n", + value); + return 0; + } + } + else if (!strcmp (this_char, "resuid")) { + if (!value || !*value) { + printk ("EXT32-fs: the resuid option requires " + "an argument"); + return 0; + } + *resuid = simple_strtoul (value, &value, 0); + if (*value) { + printk ("EXT32-fs: Invalid resuid option: %s\n", + value); + return 0; + } + } + else if (!strcmp (this_char, "sb")) { + if (!value || !*value) { + printk ("EXT32-fs: the sb option requires " + "an argument"); + return 0; + } + *sb_block = simple_strtoul (value, &value, 0); + if (*value) { + printk ("EXT32-fs: Invalid sb option: %s\n", + value); + return 0; + } + } + /* Silently ignore the quota options */ + else if (!strcmp (this_char, "grpquota") + || !strcmp (this_char, "noquota") + || !strcmp (this_char, "quota") + || !strcmp (this_char, "usrquota")) + /* Don't do anything ;-) */ ; + else { + printk ("EXT32-fs: Unrecognized mount option %s\n", this_char); + return 0; + } + } + return 1; +} + +static void ext32_setup_super (struct super_block * sb, + struct ext32_super_block * es) +{ + if (es->s_rev_level > EXT32_MAX_SUPP_REV) { + printk ("EXT32-fs warning: revision level too high, " + "forcing read/only mode\n"); + sb->s_flags |= MS_RDONLY; + } + if (!(sb->s_flags & MS_RDONLY)) { + if (!(sb->u.ext32_sb.s_mount_state & EXT32_VALID_FS)) + printk ("EXT32-fs warning: mounting unchecked fs, " + "running e2fsck is recommended\n"); + else if ((sb->u.ext32_sb.s_mount_state & EXT32_ERROR_FS)) + printk ("EXT32-fs warning: mounting fs with errors, " + "running e2fsck is recommended\n"); + else if (es->s_max_mnt_count >= 0 && + es->s_mnt_count >= (unsigned short) es->s_max_mnt_count) + printk ("EXT32-fs warning: maximal mount count reached, " + "running e2fsck is recommended\n"); + else if (es->s_checkinterval && + (es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME)) + printk ("EXT32-fs warning: checktime reached, " + "running e2fsck is recommended\n"); + es->s_state &= ~EXT32_VALID_FS; + if (!es->s_max_mnt_count) + es->s_max_mnt_count = EXT32_DFL_MAX_MNT_COUNT; + es->s_mnt_count++; + es->s_mtime = CURRENT_TIME; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + sb->s_dirt = 1; + if (ext32_test_opt (sb, DEBUG)) + printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, " + "bpg=%lu, ipg=%lu, mo=%04lx]\n", + EXT32FS_VERSION, EXT32FS_DATE, sb->s_blocksize, + sb->u.ext32_sb.s_frag_size, + sb->u.ext32_sb.s_groups_count, + EXT32_BLOCKS_PER_GROUP(sb), + EXT32_INODES_PER_GROUP(sb), + sb->u.ext32_sb.s_mount_opt); + if (ext32_test_opt (sb, CHECK)) { + ext32_check_blocks_bitmap (sb); + ext32_check_inodes_bitmap (sb); + } + } +} + +static int ext32_check_descriptors (struct super_block * sb) +{ + int i; + int desc_block = 0; + unsigned long block = sb->u.ext32_sb.s_es->s_first_data_block; + struct ext32_group_desc * gdp = NULL; + + ext32_debug ("Checking group descriptors"); + + for (i = 0; i < sb->u.ext32_sb.s_groups_count; i++) + { + if ((i % EXT32_DESC_PER_BLOCK(sb)) == 0) + gdp = (struct ext32_group_desc *) sb->u.ext32_sb.s_group_desc[desc_block++]->b_data; + if (gdp->bg_block_bitmap < block || + gdp->bg_block_bitmap >= block + EXT32_BLOCKS_PER_GROUP(sb)) + { + ext32_error (sb, "ext32_check_descriptors", + "Block bitmap for group %d" + " not in group (block %lu)!", + i, (unsigned long) gdp->bg_block_bitmap); + return 0; + } + if (gdp->bg_inode_bitmap < block || + gdp->bg_inode_bitmap >= block + EXT32_BLOCKS_PER_GROUP(sb)) + { + ext32_error (sb, "ext32_check_descriptors", + "Inode bitmap for group %d" + " not in group (block %lu)!", + i, (unsigned long) gdp->bg_inode_bitmap); + return 0; + } + if (gdp->bg_inode_table < block || + gdp->bg_inode_table + sb->u.ext32_sb.s_itb_per_group >= + block + EXT32_BLOCKS_PER_GROUP(sb)) + { + ext32_error (sb, "ext32_check_descriptors", + "Inode table for group %d" + " not in group (block %lu)!", + i, (unsigned long) gdp->bg_inode_table); + return 0; + } + block += EXT32_BLOCKS_PER_GROUP(sb); + gdp++; + } + return 1; +} + +#define log2(n) ffz(~(n)) + +struct super_block * ext32_read_super (struct super_block * sb, void * data, + int silent) +{ + struct buffer_head * bh; + struct ext32_super_block * es; + unsigned long sb_block = 1; + unsigned short resuid = EXT32_DEF_RESUID; + unsigned short resgid = EXT32_DEF_RESGID; + unsigned long logic_sb_block = 1; + kdev_t dev = sb->s_dev; + int db_count; + int i, j; + + sb->u.ext32_sb.s_mount_opt = 0; + ext32_set_opt (sb->u.ext32_sb.s_mount_opt, CHECK_NORMAL); + if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, + &sb->u.ext32_sb.s_mount_opt)) { + sb->s_dev = 0; + return NULL; + } + + MOD_INC_USE_COUNT; + lock_super (sb); + set_blocksize (dev, BLOCK_SIZE); + if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) { + sb->s_dev = 0; + unlock_super (sb); + printk ("EXT32-fs: unable to read superblock\n"); + MOD_DEC_USE_COUNT; + return NULL; + } + /* + * Note: s_es must be initialized s_es as soon as possible because + * some ext32 macro-instructions depend on its value + */ + es = (struct ext32_super_block *) bh->b_data; + sb->u.ext32_sb.s_es = es; + sb->s_magic = es->s_magic; + if (sb->s_magic != EXT32_SUPER_MAGIC) { + if (!silent) + printk ("VFS: Can't find an ext32 filesystem on dev " + "%s.\n", kdevname(dev)); + failed_mount: + sb->s_dev = 0; + unlock_super (sb); + if (bh) + brelse(bh); + MOD_DEC_USE_COUNT; + return NULL; + } + if (es->s_rev_level > EXT32_GOOD_OLD_REV) { + if (es->s_feature_incompat & ~EXT32_FEATURE_INCOMPAT_SUPP) { + printk("EXT32-fs: %s: couldn't mount because of " + "unsupported optional features.\n", + kdevname(dev)); + goto failed_mount; + } + if (!(sb->s_flags & MS_RDONLY) && + (es->s_feature_ro_compat & ~EXT32_FEATURE_RO_COMPAT_SUPP)) { + printk("EXT32-fs: %s: couldn't mount RDWR because of " + "unsupported optional features.\n", + kdevname(dev)); + goto failed_mount; + } + } + sb->s_blocksize_bits = sb->u.ext32_sb.s_es->s_log_block_size + 10; + sb->s_blocksize = 1 << sb->s_blocksize_bits; + if (sb->s_blocksize != BLOCK_SIZE && + (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || + sb->s_blocksize == 4096)) { + unsigned long offset; + + brelse (bh); + set_blocksize (dev, sb->s_blocksize); + logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize; + offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize; + bh = bread (dev, logic_sb_block, sb->s_blocksize); + if(!bh) { + printk("EXT32-fs: Couldn't read superblock on " + "2nd try.\n"); + goto failed_mount; + } + es = (struct ext32_super_block *) (((char *)bh->b_data) + offset); + sb->u.ext32_sb.s_es = es; + if (es->s_magic != EXT32_SUPER_MAGIC) { + printk ("EXT32-fs: Magic mismatch, very weird !\n"); + goto failed_mount; + } + } + if (es->s_rev_level == EXT32_GOOD_OLD_REV) { + sb->u.ext32_sb.s_inode_size = EXT32_GOOD_OLD_INODE_SIZE; + sb->u.ext32_sb.s_first_ino = EXT32_GOOD_OLD_FIRST_INO; + } else { + sb->u.ext32_sb.s_inode_size = es->s_inode_size; + sb->u.ext32_sb.s_first_ino = es->s_first_ino; + if (sb->u.ext32_sb.s_inode_size != EXT32_GOOD_OLD_INODE_SIZE) { + printk ("EXT32-fs: unsupported inode size: %d\n", + sb->u.ext32_sb.s_inode_size); + goto failed_mount; + } + } + sb->u.ext32_sb.s_frag_size = EXT32_MIN_FRAG_SIZE << + es->s_log_frag_size; + if (sb->u.ext32_sb.s_frag_size) + sb->u.ext32_sb.s_frags_per_block = sb->s_blocksize / + sb->u.ext32_sb.s_frag_size; + else + sb->s_magic = 0; + sb->u.ext32_sb.s_blocks_per_group = es->s_blocks_per_group; + sb->u.ext32_sb.s_frags_per_group = es->s_frags_per_group; + sb->u.ext32_sb.s_inodes_per_group = es->s_inodes_per_group; + sb->u.ext32_sb.s_inodes_per_block = sb->s_blocksize / + EXT32_INODE_SIZE(sb); + sb->u.ext32_sb.s_itb_per_group = sb->u.ext32_sb.s_inodes_per_group / + sb->u.ext32_sb.s_inodes_per_block; + sb->u.ext32_sb.s_desc_per_block = sb->s_blocksize / + sizeof (struct ext32_group_desc); + sb->u.ext32_sb.s_sbh = bh; + if (resuid != EXT32_DEF_RESUID) + sb->u.ext32_sb.s_resuid = resuid; + else + sb->u.ext32_sb.s_resuid = es->s_def_resuid; + if (resgid != EXT32_DEF_RESGID) + sb->u.ext32_sb.s_resgid = resgid; + else + sb->u.ext32_sb.s_resgid = es->s_def_resgid; + sb->u.ext32_sb.s_mount_state = es->s_state; + sb->u.ext32_sb.s_rename_lock = 0; + sb->u.ext32_sb.s_rename_wait = NULL; + sb->u.ext32_sb.s_addr_per_block_bits = + log2 (EXT32_ADDR_PER_BLOCK(sb)); + sb->u.ext32_sb.s_desc_per_block_bits = + log2 (EXT32_DESC_PER_BLOCK(sb)); + if (sb->s_magic != EXT32_SUPER_MAGIC) { + if (!silent) + printk ("VFS: Can't find an ext32 filesystem on dev " + "%s.\n", + kdevname(dev)); + goto failed_mount; + } + if (sb->s_blocksize != bh->b_size) { + if (!silent) + printk ("VFS: Unsupported blocksize on dev " + "%s.\n", kdevname(dev)); + goto failed_mount; + } + + if (sb->s_blocksize != sb->u.ext32_sb.s_frag_size) { + printk ("EXT32-fs: fragsize %lu != blocksize %lu (not supported yet)\n", + sb->u.ext32_sb.s_frag_size, sb->s_blocksize); + goto failed_mount; + } + + if (sb->u.ext32_sb.s_blocks_per_group > sb->s_blocksize * 8) { + printk ("EXT32-fs: #blocks per group too big: %lu\n", + sb->u.ext32_sb.s_blocks_per_group); + goto failed_mount; + } + if (sb->u.ext32_sb.s_frags_per_group > sb->s_blocksize * 8) { + printk ("EXT32-fs: #fragments per group too big: %lu\n", + sb->u.ext32_sb.s_frags_per_group); + goto failed_mount; + } + if (sb->u.ext32_sb.s_inodes_per_group > sb->s_blocksize * 8) { + printk ("EXT32-fs: #inodes per group too big: %lu\n", + sb->u.ext32_sb.s_inodes_per_group); + goto failed_mount; + } + + sb->u.ext32_sb.s_groups_count = (es->s_blocks_count - + es->s_first_data_block + + EXT32_BLOCKS_PER_GROUP(sb) - 1) / + EXT32_BLOCKS_PER_GROUP(sb); + db_count = (sb->u.ext32_sb.s_groups_count + EXT32_DESC_PER_BLOCK(sb) - 1) / + EXT32_DESC_PER_BLOCK(sb); + sb->u.ext32_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); + if (sb->u.ext32_sb.s_group_desc == NULL) { + printk ("EXT32-fs: not enough memory\n"); + goto failed_mount; + } + for (i = 0; i < db_count; i++) { + sb->u.ext32_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1, + sb->s_blocksize); + if (!sb->u.ext32_sb.s_group_desc[i]) { + for (j = 0; j < i; j++) + brelse (sb->u.ext32_sb.s_group_desc[j]); + kfree_s (sb->u.ext32_sb.s_group_desc, + db_count * sizeof (struct buffer_head *)); + printk ("EXT32-fs: unable to read group descriptors\n"); + goto failed_mount; + } + } + if (!ext32_check_descriptors (sb)) { + for (j = 0; j < db_count; j++) + brelse (sb->u.ext32_sb.s_group_desc[j]); + kfree_s (sb->u.ext32_sb.s_group_desc, + db_count * sizeof (struct buffer_head *)); + printk ("EXT32-fs: group descriptors corrupted !\n"); + goto failed_mount; + } + for (i = 0; i < EXT32_MAX_GROUP_LOADED; i++) { + sb->u.ext32_sb.s_inode_bitmap_number[i] = 0; + sb->u.ext32_sb.s_inode_bitmap[i] = NULL; + sb->u.ext32_sb.s_block_bitmap_number[i] = 0; + sb->u.ext32_sb.s_block_bitmap[i] = NULL; + } + sb->u.ext32_sb.s_loaded_inode_bitmaps = 0; + sb->u.ext32_sb.s_loaded_block_bitmaps = 0; + sb->u.ext32_sb.s_db_per_group = db_count; + unlock_super (sb); + /* + * set up enough so that it can read an inode + */ + sb->s_dev = dev; + sb->s_op = &ext32_sops; + if (!(sb->s_mounted = iget (sb, EXT32_ROOT_INO))) { + sb->s_dev = 0; + for (i = 0; i < db_count; i++) + if (sb->u.ext32_sb.s_group_desc[i]) + brelse (sb->u.ext32_sb.s_group_desc[i]); + kfree_s (sb->u.ext32_sb.s_group_desc, + db_count * sizeof (struct buffer_head *)); + brelse (bh); + printk ("EXT32-fs: get root inode failed\n"); + MOD_DEC_USE_COUNT; + return NULL; + } + ext32_setup_super (sb, es); + return sb; +} + +static void ext32_commit_super (struct super_block * sb, + struct ext32_super_block * es) +{ + es->s_wtime = CURRENT_TIME; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + sb->s_dirt = 0; +} + +/* + * In the second extended file system, it is not necessary to + * write the super block since we use a mapping of the + * disk super block in a buffer. + * + * However, this function is still used to set the fs valid + * flags to 0. We need to set this flag to 0 since the fs + * may have been checked while mounted and e2fsck may have + * set s_state to EXT32_VALID_FS after some corrections. + */ + +void ext32_write_super (struct super_block * sb) +{ + struct ext32_super_block * es; + + if (!(sb->s_flags & MS_RDONLY)) { + es = sb->u.ext32_sb.s_es; + + ext32_debug ("setting valid to 0\n"); + + if (es->s_state & EXT32_VALID_FS) { + es->s_state &= ~EXT32_VALID_FS; + es->s_mtime = CURRENT_TIME; + } + ext32_commit_super (sb, es); + } + sb->s_dirt = 0; +} + +int ext32_remount (struct super_block * sb, int * flags, char * data) +{ + struct ext32_super_block * es; + unsigned short resuid = sb->u.ext32_sb.s_resuid; + unsigned short resgid = sb->u.ext32_sb.s_resgid; + unsigned long new_mount_opt; + unsigned long tmp; + + /* + * Allow the "check" option to be passed as a remount option. + */ + new_mount_opt = EXT32_MOUNT_CHECK_NORMAL; + if (!parse_options (data, &tmp, &resuid, &resgid, + &new_mount_opt)) + return -EINVAL; + + sb->u.ext32_sb.s_mount_opt = new_mount_opt; + sb->u.ext32_sb.s_resuid = resuid; + sb->u.ext32_sb.s_resgid = resgid; + es = sb->u.ext32_sb.s_es; + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + if (*flags & MS_RDONLY) { + if (es->s_state & EXT32_VALID_FS || + !(sb->u.ext32_sb.s_mount_state & EXT32_VALID_FS)) + return 0; + /* + * OK, we are remounting a valid rw partition rdonly, so set + * the rdonly flag and then mark the partition as valid again. + */ + es->s_state = sb->u.ext32_sb.s_mount_state; + es->s_mtime = CURRENT_TIME; + mark_buffer_dirty(sb->u.ext32_sb.s_sbh, 1); + sb->s_dirt = 1; + ext32_commit_super (sb, es); + } + else { + /* + * Mounting a RDONLY partition read-write, so reread and + * store the current valid flag. (It may have been changed + * by e2fsck since we originally mounted the partition.) + */ + sb->u.ext32_sb.s_mount_state = es->s_state; + sb->s_flags &= ~MS_RDONLY; + ext32_setup_super (sb, es); + } + return 0; +} + +static struct file_system_type ext32_fs_type = { + ext32_read_super, "ext32", 1, NULL +}; + +int init_ext32_fs(void) +{ + return register_filesystem(&ext32_fs_type); +} + +#ifdef MODULE +int init_module(void) +{ + int status; + + if ((status = init_ext32_fs()) == 0) + register_symtab(0); + return status; +} + +void cleanup_module(void) +{ + unregister_filesystem(&ext32_fs_type); +} + +#endif + +void ext32_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) +{ + unsigned long overhead; + unsigned long overhead_per_group; + struct statfs tmp; + + if (ext32_test_opt (sb, MINIX_DF)) + overhead = 0; + else { + /* + * Compute the overhead (FS structures) + */ + overhead_per_group = 1 /* super block */ + + sb->u.ext32_sb.s_db_per_group /* descriptors */ + + 1 /* block bitmap */ + + 1 /* inode bitmap */ + + sb->u.ext32_sb.s_itb_per_group /* inode table */; + overhead = sb->u.ext32_sb.s_es->s_first_data_block + + sb->u.ext32_sb.s_groups_count * overhead_per_group; + } + + tmp.f_type = EXT32_SUPER_MAGIC; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = sb->u.ext32_sb.s_es->s_blocks_count - overhead; + tmp.f_bfree = ext32_count_free_blocks (sb); + tmp.f_bavail = tmp.f_bfree - sb->u.ext32_sb.s_es->s_r_blocks_count; + if (tmp.f_bfree < sb->u.ext32_sb.s_es->s_r_blocks_count) + tmp.f_bavail = 0; + tmp.f_files = sb->u.ext32_sb.s_es->s_inodes_count; + tmp.f_ffree = ext32_count_free_inodes (sb); + tmp.f_namelen = EXT32_NAME_LEN; + memcpy_tofs(buf, &tmp, bufsiz); +} diff -uNr linux.orig/fs/ext32/symlink.c linux/fs/ext32/symlink.c --- linux.orig/fs/ext32/symlink.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/symlink.c Fri Sep 25 18:48:35 1998 @@ -0,0 +1,132 @@ +/* + * linux/fs/ext32/symlink.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/symlink.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext32 symlink handling code + */ + +#include + +#include +#include +#include +#include +#include + +static int ext32_readlink (struct inode *, char *, int); +static int ext32_follow_link (struct inode *, struct inode *, int, int, + struct inode **); + +/* + * symlinks can't do much... + */ +struct inode_operations ext32_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 */ + ext32_readlink, /* readlink */ + ext32_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; + +static int ext32_follow_link(struct inode * dir, struct inode * inode, + int flag, int mode, struct inode ** res_inode) +{ + int error; + struct buffer_head * bh = NULL; + char * link; + + *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 (dir); + iput (inode); + return -ELOOP; + } + if (inode->i_blocks) { + if (!(bh = ext32_bread (inode, 0, 0, &error))) { + iput (dir); + iput (inode); + return -EIO; + } + link = bh->b_data; + } else + link = (char *) inode->u.ext32_i.i_data; + UPDATE_ATIME(inode); + current->link_count++; + error = open_namei (link, flag, mode, res_inode, dir); + current->link_count--; + iput (inode); + if (bh) + brelse (bh); + return error; +} + +static int ext32_readlink (struct inode * inode, char * buffer, int buflen) +{ + struct buffer_head * bh = NULL; + char * link; + int i, err; + char c; + + if (!S_ISLNK(inode->i_mode)) { + iput (inode); + return -EINVAL; + } + if (buflen > inode->i_sb->s_blocksize - 1) + buflen = inode->i_sb->s_blocksize - 1; + if (inode->i_blocks) { + bh = ext32_bread (inode, 0, 0, &err); + if (!bh) { + iput (inode); + return 0; + } + link = bh->b_data; + } + else + link = (char *) inode->u.ext32_i.i_data; + i = 0; + while (i < buflen && (c = link[i])) { + i++; + put_user (c, buffer++); + } + UPDATE_ATIME(inode); + iput (inode); + if (bh) + brelse (bh); + return i; +} diff -uNr linux.orig/fs/ext32/truncate.c linux/fs/ext32/truncate.c --- linux.orig/fs/ext32/truncate.c Wed Dec 31 19:00:00 1969 +++ linux/fs/ext32/truncate.c Fri Sep 25 18:48:42 1998 @@ -0,0 +1,365 @@ +/* + * linux/fs/ext32/truncate.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/truncate.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * Real random numbers for secure rm added 94/02/18 + * Idea from Pierre del Perugia + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 + +/* + * Secure deletion currently doesn't work. It interacts very badly + * with buffers shared with memory mappings, and for that reason + * can't be done in the truncate() routines. It should instead be + * done separately in "release()" before calling the truncate routines + * that will release the actual file blocks. + * + * Linus + */ +static int ext32_secrm_seed = 152; /* Random generator base */ + +#define RANDOM_INT (ext32_secrm_seed = ext32_secrm_seed * 69069l +1) +#endif + +/* + * 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. + */ + +static int trunc_direct (struct inode * inode) +{ + u32 * p; + int i, tmp; + struct buffer_head * bh; + unsigned long block_to_free = 0; + unsigned long free_count = 0; + int retry = 0; + int blocks = inode->i_sb->s_blocksize / 512; +#define DIRECT_BLOCK ((inode->i_size + inode->i_sb->s_blocksize - 1) / \ + inode->i_sb->s_blocksize) + int direct_block = DIRECT_BLOCK; + +repeat: + for (i = direct_block ; i < EXT32_NDIR_BLOCKS ; i++) { + p = inode->u.ext32_i.i_data + i; + tmp = *p; + if (!tmp) + continue; + bh = get_hash_table (inode->i_dev, tmp, + inode->i_sb->s_blocksize); + if (i < direct_block) { + brelse (bh); + goto repeat; + } + if ((bh && bh->b_count != 1) || tmp != *p) { + retry = 1; + brelse (bh); + continue; + } + *p = 0; + inode->i_blocks -= blocks; + inode->i_dirt = 1; + bforget(bh); + if (free_count == 0) { + block_to_free = tmp; + free_count++; + } else if (free_count > 0 && block_to_free == tmp - free_count) + free_count++; + else { + ext32_free_blocks (inode, block_to_free, free_count); + block_to_free = tmp; + free_count = 1; + } +/* ext32_free_blocks (inode, tmp, 1); */ + } + if (free_count > 0) + ext32_free_blocks (inode, block_to_free, free_count); + return retry; +} + +static int trunc_indirect (struct inode * inode, int offset, u32 * p) +{ + int i, tmp; + struct buffer_head * bh; + struct buffer_head * ind_bh; + u32 * ind; + unsigned long block_to_free = 0; + unsigned long free_count = 0; + int retry = 0; + int addr_per_block = EXT32_ADDR_PER_BLOCK(inode->i_sb); + int blocks = inode->i_sb->s_blocksize / 512; +#define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset) + int indirect_block = INDIRECT_BLOCK; + + tmp = *p; + if (!tmp) + return 0; + ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (tmp != *p) { + brelse (ind_bh); + return 1; + } + if (!ind_bh) { + *p = 0; + return 0; + } +repeat: + for (i = indirect_block ; i < addr_per_block ; i++) { + if (i < 0) + i = 0; + if (i < indirect_block) + goto repeat; + ind = i + (u32 *) ind_bh->b_data; + tmp = *ind; + if (!tmp) + continue; + bh = get_hash_table (inode->i_dev, tmp, + inode->i_sb->s_blocksize); + if (i < indirect_block) { + brelse (bh); + goto repeat; + } + if ((bh && bh->b_count != 1) || tmp != *ind) { + retry = 1; + brelse (bh); + continue; + } + *ind = 0; + mark_buffer_dirty(ind_bh, 1); + bforget(bh); + if (free_count == 0) { + block_to_free = tmp; + free_count++; + } else if (free_count > 0 && block_to_free == tmp - free_count) + free_count++; + else { + ext32_free_blocks (inode, block_to_free, free_count); + block_to_free = tmp; + free_count = 1; + } +/* ext32_free_blocks (inode, tmp, 1); */ + inode->i_blocks -= blocks; + inode->i_dirt = 1; + } + if (free_count > 0) + ext32_free_blocks (inode, block_to_free, free_count); + ind = (u32 *) ind_bh->b_data; + for (i = 0; i < addr_per_block; i++) + if (*(ind++)) + break; + if (i >= addr_per_block) + if (ind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; + *p = 0; + inode->i_blocks -= blocks; + inode->i_dirt = 1; + ext32_free_blocks (inode, tmp, 1); + } + if (IS_SYNC(inode) && buffer_dirty(ind_bh)) { + ll_rw_block (WRITE, 1, &ind_bh); + wait_on_buffer (ind_bh); + } + brelse (ind_bh); + return retry; +} + +static int trunc_dindirect (struct inode * inode, int offset, + u32 * p) +{ + int i, tmp; + struct buffer_head * dind_bh; + u32 * dind; + int retry = 0; + int addr_per_block = EXT32_ADDR_PER_BLOCK(inode->i_sb); + int blocks = inode->i_sb->s_blocksize / 512; +#define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block) + int dindirect_block = DINDIRECT_BLOCK; + + tmp = *p; + if (!tmp) + return 0; + dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (tmp != *p) { + brelse (dind_bh); + return 1; + } + if (!dind_bh) { + *p = 0; + return 0; + } +repeat: + for (i = dindirect_block ; i < addr_per_block ; i++) { + if (i < 0) + i = 0; + if (i < dindirect_block) + goto repeat; + dind = i + (u32 *) dind_bh->b_data; + tmp = *dind; + if (!tmp) + continue; + retry |= trunc_indirect (inode, offset + (i * addr_per_block), + dind); + mark_buffer_dirty(dind_bh, 1); + } + dind = (u32 *) dind_bh->b_data; + for (i = 0; i < addr_per_block; i++) + if (*(dind++)) + break; + if (i >= addr_per_block) + if (dind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; + *p = 0; + inode->i_blocks -= blocks; + inode->i_dirt = 1; + ext32_free_blocks (inode, tmp, 1); + } + if (IS_SYNC(inode) && buffer_dirty(dind_bh)) { + ll_rw_block (WRITE, 1, &dind_bh); + wait_on_buffer (dind_bh); + } + brelse (dind_bh); + return retry; +} + +static int trunc_tindirect (struct inode * inode) +{ + int i, tmp; + struct buffer_head * tind_bh; + u32 * tind, * p; + int retry = 0; + int addr_per_block = EXT32_ADDR_PER_BLOCK(inode->i_sb); + int blocks = inode->i_sb->s_blocksize / 512; +#define TINDIRECT_BLOCK (((int)DIRECT_BLOCK - (addr_per_block * addr_per_block + \ + addr_per_block + EXT32_NDIR_BLOCKS)) / \ + (addr_per_block * addr_per_block)) + int tindirect_block = TINDIRECT_BLOCK; + + p = inode->u.ext32_i.i_data + EXT32_TIND_BLOCK; + if (!(tmp = *p)) + return 0; + tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (tmp != *p) { + brelse (tind_bh); + return 1; + } + if (!tind_bh) { + *p = 0; + return 0; + } +repeat: + for (i = tindirect_block ; i < addr_per_block ; i++) { + if (i < 0) + i = 0; + if (i < tindirect_block) + goto repeat; + tind = i + (u32 *) tind_bh->b_data; + retry |= trunc_dindirect(inode, EXT32_NDIR_BLOCKS + + addr_per_block + (i + 1) * addr_per_block * addr_per_block, + tind); + mark_buffer_dirty(tind_bh, 1); + } + tind = (u32 *) tind_bh->b_data; + for (i = 0; i < addr_per_block; i++) + if (*(tind++)) + break; + if (i >= addr_per_block) + if (tind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; + *p = 0; + inode->i_blocks -= blocks; + inode->i_dirt = 1; + ext32_free_blocks (inode, tmp, 1); + } + if (IS_SYNC(inode) && buffer_dirty(tind_bh)) { + ll_rw_block (WRITE, 1, &tind_bh); + wait_on_buffer (tind_bh); + } + brelse (tind_bh); + return retry; +} + +void ext32_truncate (struct inode * inode) +{ + int retry; + struct buffer_head * bh; + int err; + int offset; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + ext32_discard_prealloc(inode); + while (1) { + retry = trunc_direct(inode); + retry |= trunc_indirect (inode, EXT32_IND_BLOCK, + (u32 *) &inode->u.ext32_i.i_data[EXT32_IND_BLOCK]); + retry |= trunc_dindirect (inode, EXT32_IND_BLOCK + + EXT32_ADDR_PER_BLOCK(inode->i_sb), + (u32 *) &inode->u.ext32_i.i_data[EXT32_DIND_BLOCK]); + retry |= trunc_tindirect (inode); + if (!retry) + break; + if (IS_SYNC(inode) && inode->i_dirt) + ext32_sync_inode (inode); + current->counter = 0; + schedule (); + } + /* + * If the file is not being truncated to a block boundary, the + * contents of the partial block following the end of the file must be + * zeroed in case it ever becomes accessible again because of + * subsequent file growth. + */ + offset = inode->i_size & (inode->i_sb->s_blocksize - 1); + if (offset) { + bh = ext32_bread (inode, + inode->i_size >> EXT32_BLOCK_SIZE_BITS(inode->i_sb), + 0, &err); + if (bh) { + memset (bh->b_data + offset, 0, + inode->i_sb->s_blocksize - offset); + mark_buffer_dirty (bh, 0); + brelse (bh); + } + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_dirt = 1; +} diff -uNr linux.orig/fs/filesystems.c linux/fs/filesystems.c --- linux.orig/fs/filesystems.c Fri Nov 6 20:07:52 1998 +++ linux/fs/filesystems.c Wed Sep 30 22:03:23 1998 @@ -9,12 +9,15 @@ #include #include +#include + #ifdef CONFIG_NLS #include #endif #include #include #include +#include #include #include #include @@ -56,6 +59,10 @@ #ifdef CONFIG_EXT2_FS init_ext2_fs(); +#endif + +#ifdef CONFIG_EXT32_FS + init_ext32_fs(); #endif #ifdef CONFIG_XIA_FS diff -uNr linux.orig/fs/open.c linux/fs/open.c --- linux.orig/fs/open.c Fri Nov 6 20:07:57 1998 +++ linux/fs/open.c Mon Nov 23 21:50:13 1998 @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -383,6 +385,8 @@ struct iattr newattrs; int error; +/* printk(KERN_DEBUG "32bit syscall: sys_fchown #211\n"); */ + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) return -EBADF; if (!(inode = file->f_inode)) @@ -429,12 +433,37 @@ return error; } +/* 16 bit UID compatibility call */ + +asmlinkage int sys_fchown16(unsigned int fd, old_uid_t user, old_gid_t group) +{ + uid_t new_user; + gid_t new_group; + +/* printk(KERN_DEBUG "16bit syscall: sys_fchown16 #95 uid: %d gid: %d\n",user,group); */ + + if(user == (old_uid_t)-1) + new_user = (uid_t)-1; + else + new_user = (uid_t)user; + + if(group == (old_gid_t)-1) + new_group = (gid_t)-1; + else + new_group = (gid_t)group; + + return( sys_fchown( fd, new_user, new_group ) ); +} + + asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) { struct inode * inode; int error; struct iattr newattrs; +/* printk(KERN_DEBUG "32bit syscall: sys_chown #200\n"); */ + error = lnamei(filename,&inode); if (error) return error; @@ -484,6 +513,28 @@ iput(inode); return(error); } + +/* 16 bit UID compatibility call */ +asmlinkage int sys_chown16(const char * filename, old_uid_t user, old_gid_t group) +{ + uid_t new_user; + gid_t new_group; + +/* printk(KERN_DEBUG "16bit syscall: sys_chown16 #16 uid: %d gid: %d\n",user,group); */ + + if(user == (old_uid_t)-1) + new_user = (uid_t)-1; + else + new_user = (uid_t)user; + + if(group == (old_gid_t)-1) + new_group = (gid_t)-1; + else + new_group = (gid_t)group; + + return( sys_chown( filename, new_user, new_group ) ); +} + /* * Note that while the flag value (low two bits) for sys_open means: diff -uNr linux.orig/fs/stat.c linux/fs/stat.c --- linux.orig/fs/stat.c Fri Nov 6 20:08:01 1998 +++ linux/fs/stat.c Mon Nov 23 21:50:49 1998 @@ -12,6 +12,8 @@ #include #include +#include + #include #ifndef __alpha__ @@ -20,6 +22,11 @@ * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ + +/* fixed to return 16 bit uids under UID32 */ +/* The old_stat structure was hardcoded to 16 bit uids, so that + doesn't need to be changed */ + static void cp_old_stat(struct inode * inode, struct old_stat * statbuf) { struct old_stat tmp; @@ -34,8 +41,17 @@ tmp.st_ino = inode->i_ino; tmp.st_mode = inode->i_mode; tmp.st_nlink = inode->i_nlink; - tmp.st_uid = inode->i_uid; - tmp.st_gid = inode->i_gid; + + if(inode->i_uid > 65535) + tmp.st_uid = DEF_HIGHUID; + else + tmp.st_uid = (old_uid_t)inode->i_uid; + + if(inode->i_gid > 65535) + tmp.st_gid = DEF_HIGHUID; + else + tmp.st_gid = (old_gid_t)inode->i_gid; + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); tmp.st_size = inode->i_size; if (inode->i_pipe) @@ -53,6 +69,8 @@ struct new_stat tmp; unsigned int blocks, indirect; +/* printk(KERN_DEBUG "32bit internal function: cp_new_stat\n"); */ + memset(&tmp, 0, sizeof(tmp)); tmp.st_dev = kdev_t_to_nr(inode->i_dev); tmp.st_ino = inode->i_ino; @@ -106,11 +124,67 @@ memcpy_tofs(statbuf,&tmp,sizeof(tmp)); } +/* 16 bit UID compatibility syscall */ +static void cp_new_stat16(struct inode * inode, struct uid16_new_stat * statbuf) +{ + struct uid16_new_stat tmp; + unsigned int blocks, indirect; + + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = kdev_t_to_nr(inode->i_dev); + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + + if(inode->i_uid > 65535) + tmp.st_uid = DEF_HIGHUID; + else + tmp.st_uid = (old_uid_t)inode->i_uid; + + if(inode->i_gid > 65535) + tmp.st_gid = DEF_HIGHUID; + else + tmp.st_gid = (old_gid_t)inode->i_gid; + + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); + tmp.st_size = inode->i_size; + if (inode->i_pipe) + tmp.st_size = PIPE_SIZE(*inode); + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + + if (!inode->i_blksize) { + blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (blocks > D_B) { + indirect = (blocks - D_B + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) + blocks++; + } + } + tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; + tmp.st_blksize = BLOCK_SIZE; + } else { + tmp.st_blocks = inode->i_blocks; + tmp.st_blksize = inode->i_blksize; + } + memcpy_tofs(statbuf,&tmp,sizeof(tmp)); +} + + + #ifndef __alpha__ /* * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ + +/* This should work properly under UID32 */ + asmlinkage int sys_stat(char * filename, struct old_stat * statbuf) { struct inode * inode; @@ -133,6 +207,8 @@ struct inode * inode; int error; +/* printk(KERN_DEBUG "32bit syscall: sys_newstat #212\n"); */ + error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); if (error) return error; @@ -144,12 +220,35 @@ return 0; } +/* 16 bit UID compatibility syscall */ +asmlinkage int sys_newstat16(char * filename, struct uid16_new_stat * statbuf) +{ + struct inode * inode; + int error; + +/* printk(KERN_DEBUG "16bit syscall: sys_newstat16 #106\n"); */ + + error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); + if (error) + return error; + error = namei(filename,&inode); + if (error) + return error; + cp_new_stat16(inode,statbuf); + iput(inode); + return 0; +} + + #ifndef __alpha__ /* * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ + +/* This should still work under UID32 */ + asmlinkage int sys_lstat(char * filename, struct old_stat * statbuf) { struct inode * inode; @@ -173,6 +272,8 @@ struct inode * inode; int error; +/* printk(KERN_DEBUG "32bit syscall: sys_newlstat #213\n"); */ + error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); if (error) return error; @@ -184,12 +285,34 @@ return 0; } +/* 16 bit UID compatbility syscall */ +asmlinkage int sys_newlstat16(char * filename, struct uid16_new_stat * statbuf) +{ + struct inode * inode; + int error; + +/* printk(KERN_DEBUG "16bit syscall: sys_newlstat16 #107\n"); */ + + error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); + if (error) + return error; + error = lnamei(filename,&inode); + if (error) + return error; + cp_new_stat16(inode,statbuf); + iput(inode); + return 0; +} + + #ifndef __alpha__ /* * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ + +/* This should still work under UID32 */ asmlinkage int sys_fstat(unsigned int fd, struct old_stat * statbuf) { struct file * f; @@ -213,6 +336,8 @@ struct inode * inode; int error; +/* printk(KERN_DEBUG "32bit syscall: sys_newfstat #214\n"); */ + error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); if (error) return error; @@ -221,6 +346,25 @@ cp_new_stat(inode,statbuf); return 0; } + +/* 16 bit UID compatibility syscall */ +asmlinkage int sys_newfstat16(unsigned int fd, struct uid16_new_stat * statbuf) +{ + struct file * f; + struct inode * inode; + int error; + +/* printk(KERN_DEBUG "16bit syscall: sys_newfstat16 #108\n"); */ + + error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); + if (error) + return error; + if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode)) + return -EBADF; + cp_new_stat16(inode,statbuf); + return 0; +} + asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz) { diff -uNr linux.orig/include/asm-i386/posix_types.h linux/include/asm-i386/posix_types.h --- linux.orig/include/asm-i386/posix_types.h Fri Nov 6 20:08:28 1998 +++ linux/include/asm-i386/posix_types.h Fri Sep 25 19:06:59 1998 @@ -13,8 +13,8 @@ typedef unsigned short __kernel_nlink_t; typedef long __kernel_off_t; typedef int __kernel_pid_t; -typedef unsigned short __kernel_uid_t; -typedef unsigned short __kernel_gid_t; +typedef unsigned int __kernel_uid_t; +typedef unsigned int __kernel_gid_t; typedef unsigned int __kernel_size_t; typedef int __kernel_ssize_t; typedef int __kernel_ptrdiff_t; @@ -22,6 +22,9 @@ typedef long __kernel_clock_t; typedef int __kernel_daddr_t; typedef char * __kernel_caddr_t; + +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; #ifdef __GNUC__ typedef long long __kernel_loff_t; diff -uNr linux.orig/include/asm-i386/stat.h linux/include/asm-i386/stat.h --- linux.orig/include/asm-i386/stat.h Fri Nov 6 20:08:34 1998 +++ linux/include/asm-i386/stat.h Wed Sep 30 22:41:12 1998 @@ -21,6 +21,31 @@ unsigned long st_ino; unsigned short st_mode; unsigned short st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long __unused1; + unsigned long st_mtime; + unsigned long __unused2; + unsigned long st_ctime; + unsigned long __unused3; + unsigned long __unused4; + unsigned long __unused5; +}; + +/* compatibility for the 16bit UID stat syscalls */ + +struct uid16_new_stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; unsigned short st_uid; unsigned short st_gid; unsigned short st_rdev; @@ -37,5 +62,6 @@ unsigned long __unused4; unsigned long __unused5; }; + #endif diff -uNr linux.orig/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h --- linux.orig/include/asm-i386/unistd.h Fri Nov 6 20:08:40 1998 +++ linux/include/asm-i386/unistd.h Thu Oct 1 17:41:02 1998 @@ -21,19 +21,19 @@ #define __NR_time 13 #define __NR_mknod 14 #define __NR_chmod 15 -#define __NR_chown 16 +#define __NR_chown16 16 /* fixed to work with uid16 */ #define __NR_break 17 -#define __NR_oldstat 18 +#define __NR_oldstat 18 /* fixed to work with uid16 */ #define __NR_lseek 19 #define __NR_getpid 20 #define __NR_mount 21 #define __NR_umount 22 -#define __NR_setuid 23 -#define __NR_getuid 24 +#define __NR_setuid16 23 /* fixed to work with uid16 */ +#define __NR_getuid16 24 /* fixed to work with uid16 */ #define __NR_stime 25 #define __NR_ptrace 26 #define __NR_alarm 27 -#define __NR_oldfstat 28 +#define __NR_oldfstat 28 /* fixed to work with uid16 */ #define __NR_pause 29 #define __NR_utime 30 #define __NR_stty 31 @@ -51,11 +51,11 @@ #define __NR_times 43 #define __NR_prof 44 #define __NR_brk 45 -#define __NR_setgid 46 -#define __NR_getgid 47 +#define __NR_setgid16 46 /* fixed to work with uid16 */ +#define __NR_getgid16 47 /* fixed to work with uid16 */ #define __NR_signal 48 -#define __NR_geteuid 49 -#define __NR_getegid 50 +#define __NR_geteuid16 49 /* fixed to work with uid16 */ +#define __NR_getegid16 50 /* fixed to work with uid16 */ #define __NR_acct 51 #define __NR_phys 52 #define __NR_lock 53 @@ -75,8 +75,8 @@ #define __NR_sigaction 67 #define __NR_sgetmask 68 #define __NR_ssetmask 69 -#define __NR_setreuid 70 -#define __NR_setregid 71 +#define __NR_setreuid16 70 /* fixed to work with uid16 */ +#define __NR_setregid16 71 /* fixed to work with uid16 */ #define __NR_sigsuspend 72 #define __NR_sigpending 73 #define __NR_sethostname 74 @@ -85,11 +85,11 @@ #define __NR_getrusage 77 #define __NR_gettimeofday 78 #define __NR_settimeofday 79 -#define __NR_getgroups 80 -#define __NR_setgroups 81 +#define __NR_getgroups16 80 /* fixed to work with uid16 */ +#define __NR_setgroups16 81 /* fixed to work with uid16 */ #define __NR_select 82 #define __NR_symlink 83 -#define __NR_oldlstat 84 +#define __NR_oldlstat 84 /* fixed to work with uid16 */ #define __NR_readlink 85 #define __NR_uselib 86 #define __NR_swapon 87 @@ -100,7 +100,7 @@ #define __NR_truncate 92 #define __NR_ftruncate 93 #define __NR_fchmod 94 -#define __NR_fchown 95 +#define __NR_fchown16 95 /* fixed to work with uid16 */ #define __NR_getpriority 96 #define __NR_setpriority 97 #define __NR_profil 98 @@ -111,9 +111,9 @@ #define __NR_syslog 103 #define __NR_setitimer 104 #define __NR_getitimer 105 -#define __NR_stat 106 -#define __NR_lstat 107 -#define __NR_fstat 108 +#define __NR_stat16 106 /* fixed to work with uid16 */ +#define __NR_lstat16 107 /* fixed to work with uid16 */ +#define __NR_fstat16 108 /* fixed to work with uid16 */ #define __NR_olduname 109 #define __NR_iopl 110 #define __NR_vhangup 111 @@ -136,15 +136,15 @@ #define __NR_init_module 128 #define __NR_delete_module 129 #define __NR_get_kernel_syms 130 -#define __NR_quotactl 131 +#define __NR_quotactl16 131 /* fixed to work with uid16 */ #define __NR_getpgid 132 #define __NR_fchdir 133 #define __NR_bdflush 134 #define __NR_sysfs 135 #define __NR_personality 136 #define __NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 +#define __NR_setfsuid16 138 /* fixed to work with uid16 */ +#define __NR_setfsgid16 139 /* fixed to work with uid16 */ #define __NR__llseek 140 #define __NR_getdents 141 #define __NR__newselect 142 @@ -169,6 +169,39 @@ #define __NR_sched_rr_get_interval 161 #define __NR_nanosleep 162 #define __NR_mremap 163 + +/* new 32 bit UID calls */ + +/* the kernel symbols all refer to 32 bit calls. the syscall numbers are +unchanged for 16 bit calls, introducing these new numbers for the 32 bit +calls. that way, all old code calls the 16 bit functions, but kernel +modules, glibc, anything important, will use the 32 bit calls. +as it should be. */ + +/* chris wing wingc@umich.edu */ + +/* moved these way down, because linux 2.1 has a lot of syscalls + where these used to be. */ + +#define __NR_chown 200 +#define __NR_setuid 201 +#define __NR_getuid 202 +#define __NR_setgid 203 +#define __NR_getgid 204 +#define __NR_geteuid 205 +#define __NR_getegid 206 +#define __NR_setreuid 207 +#define __NR_setregid 208 +#define __NR_getgroups 209 +#define __NR_setgroups 210 +#define __NR_fchown 211 +#define __NR_stat 212 +#define __NR_lstat 213 +#define __NR_fstat 214 +#define __NR_quotactl 215 +#define __NR_setfsuid 216 +#define __NR_setfsgid 217 + /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ #define _syscall0(type,name) \ diff -uNr linux.orig/include/linux/ext32_fs.h linux/include/linux/ext32_fs.h --- linux.orig/include/linux/ext32_fs.h Wed Dec 31 19:00:00 1969 +++ linux/include/linux/ext32_fs.h Fri Nov 6 21:09:56 1998 @@ -0,0 +1,540 @@ +/* + * linux/include/linux/ext32_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _LINUX_EXT32_FS_H +#define _LINUX_EXT32_FS_H + +#include + +/* + * The second extended filesystem constants/structures + */ + +/* + * Define EXT32FS_DEBUG to produce debug messages + */ +#undef EXT32FS_DEBUG + +/* + * Define EXT32_PREALLOCATE to preallocate data blocks for expanding files + */ +#define EXT32_PREALLOCATE + +/* + * The second extended file system version + */ +#define EXT32FS_DATE "95/08/09" +#define EXT32FS_VERSION "0.5b" + +/* + * Debug code + */ +#ifdef EXT32FS_DEBUG +# define ext32_debug(f, a...) { \ + printk ("EXT32-fs DEBUG (%s, %d): %s:", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } +#else +# define ext32_debug(f, a...) /**/ +#endif + +/* + * Special inodes numbers + */ +#define EXT32_BAD_INO 1 /* Bad blocks inode */ +#define EXT32_ROOT_INO 2 /* Root inode */ +#define EXT32_ACL_IDX_INO 3 /* ACL inode */ +#define EXT32_ACL_DATA_INO 4 /* ACL inode */ +#define EXT32_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT32_UNDEL_DIR_INO 6 /* Undelete directory inode */ + +/* First non-reserved inode for old ext32 filesystems */ +#define EXT32_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT32_SUPER_MAGIC 0xEF53 + +/* + * Maximal count of links to a file + */ +#define EXT32_LINK_MAX 32000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT32_MIN_BLOCK_SIZE 1024 +#define EXT32_MAX_BLOCK_SIZE 4096 +#define EXT32_MIN_BLOCK_LOG_SIZE 10 +#ifdef __KERNEL__ +# define EXT32_BLOCK_SIZE(s) ((s)->s_blocksize) +#else +# define EXT32_BLOCK_SIZE(s) (EXT32_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#endif +#define EXT32_ACLE_PER_BLOCK(s) (EXT32_BLOCK_SIZE(s) / sizeof (struct ext32_acl_entry)) +#define EXT32_ADDR_PER_BLOCK(s) (EXT32_BLOCK_SIZE(s) / sizeof (__u32)) +#ifdef __KERNEL__ +# define EXT32_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) +#else +# define EXT32_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#endif +#ifdef __KERNEL__ +#define EXT32_ADDR_PER_BLOCK_BITS(s) ((s)->u.ext32_sb.s_addr_per_block_bits) +#define EXT32_INODE_SIZE(s) ((s)->u.ext32_sb.s_inode_size) +#define EXT32_FIRST_INO(s) ((s)->u.ext32_sb.s_first_ino) +#else +#define EXT32_INODE_SIZE(s) (((s)->s_rev_level == EXT32_GOOD_OLD_REV) ? \ + EXT32_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) +#define EXT32_FIRST_INO(s) (((s)->s_rev_level == EXT32_GOOD_OLD_REV) ? \ + EXT32_GOOD_OLD_FIRST_INO : \ + (s)->s_first_ino) +#endif + +/* + * Macro-instructions used to manage fragments + */ +#define EXT32_MIN_FRAG_SIZE 1024 +#define EXT32_MAX_FRAG_SIZE 4096 +#define EXT32_MIN_FRAG_LOG_SIZE 10 +#ifdef __KERNEL__ +# define EXT32_FRAG_SIZE(s) ((s)->u.ext32_sb.s_frag_size) +# define EXT32_FRAGS_PER_BLOCK(s) ((s)->u.ext32_sb.s_frags_per_block) +#else +# define EXT32_FRAG_SIZE(s) (EXT32_MIN_FRAG_SIZE << (s)->s_log_frag_size) +# define EXT32_FRAGS_PER_BLOCK(s) (EXT32_BLOCK_SIZE(s) / EXT32_FRAG_SIZE(s)) +#endif + +/* + * ACL structures + */ +struct ext32_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; +}; + +struct ext32_acl_entry /* Access Control List Entry */ +{ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext32_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_pad; + __u32 bg_reserved[3]; +}; + +/* + * Macro-instructions used to manage group descriptors + */ +#ifdef __KERNEL__ +# define EXT32_BLOCKS_PER_GROUP(s) ((s)->u.ext32_sb.s_blocks_per_group) +# define EXT32_DESC_PER_BLOCK(s) ((s)->u.ext32_sb.s_desc_per_block) +# define EXT32_INODES_PER_GROUP(s) ((s)->u.ext32_sb.s_inodes_per_group) +# define EXT32_DESC_PER_BLOCK_BITS(s) ((s)->u.ext32_sb.s_desc_per_block_bits) +#else +# define EXT32_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) +# define EXT32_DESC_PER_BLOCK(s) (EXT32_BLOCK_SIZE(s) / sizeof (struct ext32_group_desc)) +# define EXT32_INODES_PER_GROUP(s) ((s)->s_inodes_per_group) +#endif + +/* + * Constants relative to the data blocks + */ +#define EXT32_NDIR_BLOCKS 12 +#define EXT32_IND_BLOCK EXT32_NDIR_BLOCKS +#define EXT32_DIND_BLOCK (EXT32_IND_BLOCK + 1) +#define EXT32_TIND_BLOCK (EXT32_DIND_BLOCK + 1) +#define EXT32_N_BLOCKS (EXT32_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT32_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT32_UNRM_FL 0x00000002 /* Undelete */ +#define EXT32_COMPR_FL 0x00000004 /* Compress file */ +#define EXT32_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT32_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT32_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT32_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT32_NOATIME_FL 0x00000080 /* do not update atime */ +#define EXT32_RESERVED_FL 0x80000000 /* reserved for ext32 lib */ + +/* + * ioctl commands + */ +#define EXT32_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT32_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT32_IOC_GETVERSION _IOR('v', 1, long) +#define EXT32_IOC_SETVERSION _IOW('v', 2, long) + +/* + * Structure of an inode on the disk + */ +struct ext32_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_reserved1; + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + struct { + __u32 m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT32_N_BLOCKS];/* Pointers to blocks */ + __u32 i_version; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u32 l_i_reserved2[2]; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + struct { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +#if defined(__KERNEL__) || defined(__linux__) +#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_frag osd2.linux2.l_i_frag +#define i_fsize osd2.linux2.l_i_fsize +#define i_reserved2 osd2.linux2.l_i_reserved2 +#endif + +#ifdef __hurd__ +#define i_translator osd1.hurd1.h_i_translator +#define i_frag osd2.hurd2.h_i_frag; +#define i_fsize osd2.hurd2.h_i_fsize; +#define i_uid_high osd2.hurd2.h_i_uid_high +#define i_gid_high osd2.hurd2.h_i_gid_high +#define i_author osd2.hurd2.h_i_author +#endif + +#ifdef __masix__ +#define i_reserved1 osd1.masix1.m_i_reserved1 +#define i_frag osd2.masix2.m_i_frag +#define i_fsize osd2.masix2.m_i_fsize +#define i_reserved2 osd2.masix2.m_i_reserved2 +#endif + +/* + * File system states + */ +#define EXT32_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT32_ERROR_FS 0x0002 /* Errors detected */ + +/* + * Mount flags + */ +#define EXT32_MOUNT_CHECK_NORMAL 0x0001 /* Do some more checks */ +#define EXT32_MOUNT_CHECK_STRICT 0x0002 /* Do again more checks */ +#define EXT32_MOUNT_CHECK (EXT32_MOUNT_CHECK_NORMAL | \ + EXT32_MOUNT_CHECK_STRICT) +#define EXT32_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT32_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT32_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT32_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT32_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT32_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT32_MOUNT_NO_ATIME 0x0100 /* Don't update the atime */ + +/* oooh. hey, you're defining things in the global namespace which are + used only by the ext2 driver. + that's not nice.... will have to add 32 to everything now +*/ + +#define ext32_clear_opt(o, opt) o &= ~EXT32_MOUNT_##opt +#define ext32_set_opt(o, opt) o |= EXT32_MOUNT_##opt +#define ext32_test_opt(sb, opt) ((sb)->u.ext32_sb.s_mount_opt & \ + EXT32_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT32_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT32_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT32_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT32_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT32_ERRORS_PANIC 3 /* Panic */ +#define EXT32_ERRORS_DEFAULT EXT32_ERRORS_CONTINUE + +/* + * Structure of the super block + */ +struct ext32_super_block { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT32_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u32 s_reserved[230]; /* Padding to the end of the block */ +}; + +/* + * Codes for operating systems + */ +#define EXT32_OS_LINUX 0 +#define EXT32_OS_HURD 1 +#define EXT32_OS_MASIX 2 +#define EXT32_OS_FREEBSD 3 +#define EXT32_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT32_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT32_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT32_CURRENT_REV EXT32_GOOD_OLD_REV +#define EXT32_MAX_SUPP_REV EXT32_DYNAMIC_REV + +#define EXT32_GOOD_OLD_INODE_SIZE 128 + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT32_DEF_RESUID 0 +#define EXT32_DEF_RESGID 0 + +/* + * Structure of a directory entry + */ +#define EXT32_NAME_LEN 255 + +struct ext32_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT32_NAME_LEN]; /* File name */ +}; + +/* + * EXT32_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT32_DIR_PAD 4 +#define EXT32_DIR_ROUND (EXT32_DIR_PAD - 1) +#define EXT32_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT32_DIR_ROUND) & \ + ~EXT32_DIR_ROUND) + +/* + * Feature set definitions --- none are defined as of now + */ +#define EXT32_FEATURE_COMPAT_SUPP 0 +#define EXT32_FEATURE_INCOMPAT_SUPP 0 +#define EXT32_FEATURE_RO_COMPAT_SUPP 0 + +#ifdef __KERNEL__ +/* + * Function prototypes + */ + +/* + * Ok, these declarations are also in but none of the + * ext32 source programs needs to include it so they are duplicated here. + */ +# define NORET_TYPE /**/ +# define ATTRIB_NORET __attribute__((noreturn)) +# define NORET_AND noreturn, + +/* acl.c */ +extern int ext32_permission (struct inode *, int); + +/* balloc.c */ +extern int ext32_new_block (const struct inode *, unsigned long, + __u32 *, __u32 *, int *); +extern void ext32_free_blocks (const struct inode *, unsigned long, + unsigned long); +extern unsigned long ext32_count_free_blocks (struct super_block *); +extern void ext32_check_blocks_bitmap (struct super_block *); + +/* bitmap.c */ +extern unsigned long ext32_count_free (struct buffer_head *, unsigned); + +/* dir.c */ +extern int ext32_check_dir_entry (const char *, struct inode *, + struct ext32_dir_entry *, struct buffer_head *, + unsigned long); + +/* file.c */ +extern int ext32_read (struct inode *, struct file *, char *, int); +extern int ext32_write (struct inode *, struct file *, char *, int); + +/* fsync.c */ +extern int ext32_sync_file (struct inode *, struct file *); + +/* ialloc.c */ +extern struct inode * ext32_new_inode (const struct inode *, int, int *); +extern void ext32_free_inode (struct inode *); +extern unsigned long ext32_count_free_inodes (struct super_block *); +extern void ext32_check_inodes_bitmap (struct super_block *); + +/* inode.c */ +extern int ext32_bmap (struct inode *, int); + +extern struct buffer_head * ext32_getblk (struct inode *, long, int, int *); +extern struct buffer_head * ext32_bread (struct inode *, int, int, int *); + +extern int ext32_getcluster (struct inode * inode, long block); +extern void ext32_read_inode (struct inode *); +extern void ext32_write_inode (struct inode *); +extern void ext32_put_inode (struct inode *); +extern int ext32_sync_inode (struct inode *); +extern void ext32_discard_prealloc (struct inode *); + +/* ioctl.c */ +extern int ext32_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); + +/* namei.c */ +extern void ext32_release (struct inode *, struct file *); +extern int ext32_lookup (struct inode *,const char *, int, struct inode **); +extern int ext32_create (struct inode *,const char *, int, int, + struct inode **); +extern int ext32_mkdir (struct inode *, const char *, int, int); +extern int ext32_rmdir (struct inode *, const char *, int); +extern int ext32_unlink (struct inode *, const char *, int); +extern int ext32_symlink (struct inode *, const char *, int, const char *); +extern int ext32_link (struct inode *, struct inode *, const char *, int); +extern int ext32_mknod (struct inode *, const char *, int, int, int); +extern int ext32_rename (struct inode *, const char *, int, + struct inode *, const char *, int, int); + +/* super.c */ +extern void ext32_error (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern NORET_TYPE void ext32_panic (struct super_block *, const char *, + const char *, ...) + __attribute__ ((NORET_AND format (printf, 3, 4))); +extern void ext32_warning (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern void ext32_put_super (struct super_block *); +extern void ext32_write_super (struct super_block *); +extern int ext32_remount (struct super_block *, int *, char *); +extern struct super_block * ext32_read_super (struct super_block *,void *,int); +extern int init_ext32_fs(void); +extern void ext32_statfs (struct super_block *, struct statfs *, int); + +/* truncate.c */ +extern void ext32_truncate (struct inode *); + +/* + * Inodes and files operations + */ + +/* dir.c */ +extern struct inode_operations ext32_dir_inode_operations; + +/* file.c */ +extern struct inode_operations ext32_file_inode_operations; + +/* symlink.c */ +extern struct inode_operations ext32_symlink_inode_operations; + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_EXT32_FS_H */ diff -uNr linux.orig/include/linux/ext32_fs_i.h linux/include/linux/ext32_fs_i.h --- linux.orig/include/linux/ext32_fs_i.h Wed Dec 31 19:00:00 1969 +++ linux/include/linux/ext32_fs_i.h Fri Sep 25 18:42:34 1998 @@ -0,0 +1,41 @@ +/* + * linux/include/linux/ext32_fs_i.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs_i.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _LINUX_EXT32_FS_I +#define _LINUX_EXT32_FS_I + +/* + * second extended file system inode data in memory + */ +struct ext32_inode_info { + __u32 i_data[15]; + __u32 i_flags; + __u32 i_faddr; + __u8 i_frag_no; + __u8 i_frag_size; + __u16 i_osync; + __u32 i_file_acl; + __u32 i_dir_acl; + __u32 i_dtime; + __u32 i_version; + __u32 i_block_group; + __u32 i_next_alloc_block; + __u32 i_next_alloc_goal; + __u32 i_prealloc_block; + __u32 i_prealloc_count; + int i_new_inode:1; /* Is a freshly allocated inode */ +}; + +#endif /* _LINUX_EXT32_FS_I */ diff -uNr linux.orig/include/linux/ext32_fs_sb.h linux/include/linux/ext32_fs_sb.h --- linux.orig/include/linux/ext32_fs_sb.h Wed Dec 31 19:00:00 1969 +++ linux/include/linux/ext32_fs_sb.h Fri Sep 25 18:43:20 1998 @@ -0,0 +1,65 @@ +/* + * linux/include/linux/ext32_fs_sb.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs_sb.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _LINUX_EXT32_FS_SB +#define _LINUX_EXT32_FS_SB + +#include + +/* + * The following is not needed anymore since the descriptors buffer + * heads are now dynamically allocated + */ +/* #define EXT32_MAX_GROUP_DESC 8 */ + +#define EXT32_MAX_GROUP_LOADED 8 + +/* + * second extended-fs super-block data in memory + */ +struct ext32_sb_info { + unsigned long s_frag_size; /* Size of a fragment in bytes */ + unsigned long s_frags_per_block;/* Number of fragments per block */ + unsigned long s_inodes_per_block;/* Number of inodes per block */ + unsigned long s_frags_per_group;/* Number of fragments in a group */ + unsigned long s_blocks_per_group;/* Number of blocks in a group */ + unsigned long s_inodes_per_group;/* Number of inodes in a group */ + unsigned long s_itb_per_group; /* Number of inode table blocks per group */ + unsigned long s_db_per_group; /* Number of descriptor blocks per group */ + unsigned long s_desc_per_block; /* Number of group descriptors per block */ + unsigned long s_groups_count; /* Number of groups in the fs */ + struct buffer_head * s_sbh; /* Buffer containing the super block */ + struct ext32_super_block * s_es; /* Pointer to the super block in the buffer */ + struct buffer_head ** s_group_desc; + unsigned short s_loaded_inode_bitmaps; + unsigned short s_loaded_block_bitmaps; + unsigned long s_inode_bitmap_number[EXT32_MAX_GROUP_LOADED]; + struct buffer_head * s_inode_bitmap[EXT32_MAX_GROUP_LOADED]; + unsigned long s_block_bitmap_number[EXT32_MAX_GROUP_LOADED]; + struct buffer_head * s_block_bitmap[EXT32_MAX_GROUP_LOADED]; + int s_rename_lock; + struct wait_queue * s_rename_wait; + unsigned long s_mount_opt; + unsigned short s_resuid; + unsigned short s_resgid; + unsigned short s_mount_state; + unsigned short s_pad; + int s_addr_per_block_bits; + int s_desc_per_block_bits; + int s_inode_size; + int s_first_ino; +}; + +#endif /* _LINUX_EXT32_FS_SB */ diff -uNr linux.orig/include/linux/fs.h linux/include/linux/fs.h --- linux.orig/include/linux/fs.h Fri Nov 6 20:08:58 1998 +++ linux/include/linux/fs.h Wed Sep 30 22:13:02 1998 @@ -237,6 +237,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +328,7 @@ struct minix_inode_info minix_i; struct ext_inode_info ext_i; struct ext2_inode_info ext2_i; + struct ext32_inode_info ext32_i; struct hpfs_inode_info hpfs_i; struct msdos_inode_info msdos_i; struct umsdos_inode_info umsdos_i; @@ -433,6 +435,7 @@ #include #include #include +#include #include #include #include @@ -462,6 +465,7 @@ struct minix_sb_info minix_sb; struct ext_sb_info ext_sb; struct ext2_sb_info ext2_sb; + struct ext32_sb_info ext32_sb; struct hpfs_sb_info hpfs_sb; struct msdos_sb_info msdos_sb; struct isofs_sb_info isofs_sb; diff -uNr linux.orig/include/linux/highuid.h linux/include/linux/highuid.h --- linux.orig/include/linux/highuid.h Wed Dec 31 19:00:00 1969 +++ linux/include/linux/highuid.h Fri Dec 4 17:40:49 1998 @@ -0,0 +1,26 @@ +/* simple constants for the uid32 hack for Linux + Chris Wing +*/ + +/* This is the number used to indicate on disk that a high UID/GID will + be used. When you mount a magic ext32 filesystem on an ordinary Linux + machine, all of your highuid files will appear to be owned by this uid. +*/ + +#define MAGIC_HIGHUID 65534 + +/* This is the "default" high uid. It is used to signify uid overflow to + old programs when they request uid/gid information but are using the + old 16 bit interfaces. + When you run a libc5 program, it will think that all highuid files or + processes are owned by this uid. +*/ + +#define DEF_HIGHUID 65534 + +/* These used to be different, and then, I decided to make them the same. + Can anyone think of a compelling reason to pick one or the other? + (Since I picked MAGIC_HIGHUID to be 65534, that probably shouldn't + change or else the filesystem will lose high UIDs) + I didn't want to use 65535 just in case somebody took it to mean -1 +*/ diff -uNr linux.orig/include/linux/ipc.h linux/include/linux/ipc.h --- linux.orig/include/linux/ipc.h Mon Nov 23 21:00:20 1998 +++ linux/include/linux/ipc.h Mon Nov 23 20:59:12 1998 @@ -8,6 +8,19 @@ struct ipc_perm { key_t key; + uid_t uid; /* owner euid and egid */ + gid_t gid; + uid_t cuid; /* creator euid and egid */ + gid_t cgid; + ushort mode; /* access modes see mode flags below */ + ushort seq; /* sequence number */ +}; + +/* compatibility structure for old applications */ + +struct uid16_ipc_perm +{ + key_t key; ushort uid; /* owner euid and egid */ ushort gid; ushort cuid; /* creator euid and egid */ diff -uNr linux.orig/include/linux/msg.h linux/include/linux/msg.h --- linux.orig/include/linux/msg.h Mon Nov 23 21:00:20 1998 +++ linux/include/linux/msg.h Mon Nov 23 20:55:57 1998 @@ -23,6 +23,24 @@ ushort msg_lrpid; /* last receive pid */ }; +/* compatibility structure for old applications */ + +struct uid16_msqid_ds { + struct uid16_ipc_perm msg_perm; + struct msg *msg_first; /* first message on queue */ + struct msg *msg_last; /* last message in queue */ + time_t msg_stime; /* last msgsnd time */ + time_t msg_rtime; /* last msgrcv time */ + time_t msg_ctime; /* last change time */ + struct wait_queue *wwait; + struct wait_queue *rwait; + ushort msg_cbytes; /* current number of bytes on queue */ + ushort msg_qnum; /* number of messages in queue */ + ushort msg_qbytes; /* max number of bytes on queue */ + ushort msg_lspid; /* pid of last msgsnd */ + ushort msg_lrpid; /* last receive pid */ +}; + /* message buffer for msgsnd and msgrcv calls */ struct msgbuf { long mtype; /* type of message */ @@ -72,7 +90,7 @@ asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); -asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf); +asmlinkage int sys_msgctl (int msqid, int cmd, struct uid16_msqid_ds *buf); #endif /* __KERNEL__ */ diff -uNr linux.orig/include/linux/sched.h linux/include/linux/sched.h --- linux.orig/include/linux/sched.h Tue Nov 10 04:22:18 1998 +++ linux/include/linux/sched.h Tue Nov 10 04:22:10 1998 @@ -208,8 +208,9 @@ */ struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr; struct wait_queue *wait_chldexit; /* for wait4() */ - unsigned short uid,euid,suid,fsuid; - unsigned short gid,egid,sgid,fsgid; +/* why were these originally hard-coded to unsigned short? an oversight? */ + uid_t uid,euid,suid,fsuid; + gid_t gid,egid,sgid,fsgid; unsigned long timeout, policy, rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; diff -uNr linux.orig/include/linux/sem.h linux/include/linux/sem.h --- linux.orig/include/linux/sem.h Mon Nov 23 21:00:20 1998 +++ linux/include/linux/sem.h Mon Nov 23 20:57:53 1998 @@ -26,6 +26,19 @@ ushort sem_nsems; /* no. of semaphores in array */ }; +/* compatibility structure passed to old applications */ + +struct uid16_semid_ds { + struct uid16_ipc_perm sem_perm; /* permissions .. see ipc.h */ + time_t sem_otime; /* last semop time */ + time_t sem_ctime; /* last change time */ + struct sem *sem_base; /* ptr to first semaphore in array */ + struct sem_queue *sem_pending; /* pending operations to be processed */ + struct sem_queue **sem_pending_last; /* last pending operation */ + struct sem_undo *undo; /* undo requests on this array */ + ushort sem_nsems; /* no. of semaphores in array */ +}; + /* semop system calls takes an array of these. */ struct sembuf { ushort sem_num; /* semaphore index in array */ @@ -36,7 +49,19 @@ /* arg for semctl system calls. */ union semun { int val; /* value for SETVAL */ - struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ + struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET +*/ + ushort *array; /* array for GETALL & SETALL */ + struct seminfo *__buf; /* buffer for IPC_INFO */ + void *__pad; +}; + +/* compatibility structure for old applications */ + +union uid16_semun { + int val; /* value for SETVAL */ + struct uid16_semid_ds *buf; /* buffer for IPC_STAT & IPC_SET +*/ ushort *array; /* array for GETALL & SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ void *__pad; @@ -105,7 +130,7 @@ asmlinkage int sys_semget (key_t key, int nsems, int semflg); asmlinkage int sys_semop (int semid, struct sembuf *sops, unsigned nsops); -asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg); +asmlinkage int sys_semctl (int semid, int semnum, int cmd, union uid16_semun arg); #endif /* __KERNEL__ */ diff -uNr linux.orig/include/linux/shm.h linux/include/linux/shm.h --- linux.orig/include/linux/shm.h Mon Nov 23 21:00:20 1998 +++ linux/include/linux/shm.h Mon Nov 23 20:58:06 1998 @@ -20,6 +20,23 @@ struct vm_area_struct *attaches; /* descriptors for attaches */ }; +/* compatibility structure passed to old applications */ + +struct uid16_shmid_ds { + struct uid16_ipc_perm shm_perm; /* operation perms */ + int shm_segsz; /* size of segment (bytes) */ + time_t shm_atime; /* last attach time */ + time_t shm_dtime; /* last detach time */ + time_t shm_ctime; /* last change time */ + unsigned short shm_cpid; /* pid of creator */ + unsigned short shm_lpid; /* pid of last operator */ + short shm_nattch; /* no. of current attaches */ + /* the following are private */ + unsigned short shm_npages; /* size of segment (pages) */ + unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */ + struct vm_area_struct *attaches; /* descriptors for attaches */ +}; + /* permission flag for shmget */ #define SHM_R 0400 /* or S_IRUGO from */ #define SHM_W 0200 /* or S_IWUGO from */ @@ -62,7 +79,7 @@ asmlinkage int sys_shmget (key_t key, int size, int flag); asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr); asmlinkage int sys_shmdt (char *shmaddr); -asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf); +asmlinkage int sys_shmctl (int shmid, int cmd, struct uid16_shmid_ds *buf); #endif /* __KERNEL__ */ diff -uNr linux.orig/include/linux/types.h linux/include/linux/types.h --- linux.orig/include/linux/types.h Fri Nov 6 20:09:03 1998 +++ linux/include/linux/types.h Wed Sep 30 22:32:58 1998 @@ -17,6 +17,9 @@ typedef __kernel_gid_t gid_t; typedef __kernel_daddr_t daddr_t; +typedef __kernel_old_uid_t old_uid_t; +typedef __kernel_old_gid_t old_gid_t; + #if defined(__GNUC__) && !defined(__STRICT_ANSI__) typedef __kernel_loff_t loff_t; #endif diff -uNr linux.orig/init/version.c linux/init/version.c --- linux.orig/init/version.c Fri Nov 6 20:52:23 1998 +++ linux/init/version.c Fri Nov 6 20:52:59 1998 @@ -26,5 +26,5 @@ }; const char *linux_banner = - "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" + "Linux/UID32 version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n"; diff -uNr linux.orig/ipc/msg.c linux/ipc/msg.c --- linux.orig/ipc/msg.c Sat Nov 7 22:50:43 1998 +++ linux/ipc/msg.c Mon Nov 23 21:23:08 1998 @@ -18,6 +18,8 @@ #include +#include + extern int ipcperms (struct ipc_perm *ipcp, short msgflg); static void freeque (int id); @@ -534,18 +536,23 @@ kfree(msq); } -asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) +/* this function is modified to convert between the old 16-bit UID + IPC structure and the 32-bit UID IPC structure used internally + by the kernel. */ + +asmlinkage int sys_msgctl (int msqid, int cmd, struct uid16_msqid_ds *buf) { int id, err; struct msqid_ds *msq; - struct msqid_ds tbuf; + struct uid16_msqid_ds tbuf; struct ipc_perm *ipcp; if (msqid < 0 || cmd < 0) return -EINVAL; switch (cmd) { case IPC_INFO: - case MSG_INFO: + case MSG_INFO: +/* doesn't involve UIDs so this is safe */ if (!buf) return -EFAULT; { @@ -583,7 +590,30 @@ if (ipcperms (&msq->msg_perm, S_IRUGO)) return -EACCES; id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid; - tbuf.msg_perm = msq->msg_perm; + tbuf.msg_perm.key = msq->msg_perm.key; + + if(msq->msg_perm.uid > 65535) + tbuf.msg_perm.uid = DEF_HIGHUID; + else + tbuf.msg_perm.uid = (ushort)msq->msg_perm.uid; + + if(msq->msg_perm.gid > 65535) + tbuf.msg_perm.gid = DEF_HIGHUID; + else + tbuf.msg_perm.gid = (ushort)msq->msg_perm.gid; + + if(msq->msg_perm.cuid > 65535) + tbuf.msg_perm.cuid = DEF_HIGHUID; + else + tbuf.msg_perm.cuid = (ushort)msq->msg_perm.cuid; + + if(msq->msg_perm.cgid > 65535) + tbuf.msg_perm.cgid = DEF_HIGHUID; + else + tbuf.msg_perm.cgid = (ushort)msq->msg_perm.cgid; + + tbuf.msg_perm.mode = msq->msg_perm.mode; + tbuf.msg_perm.seq = msq->msg_perm.seq; tbuf.msg_stime = msq->msg_stime; tbuf.msg_rtime = msq->msg_rtime; tbuf.msg_ctime = msq->msg_ctime; @@ -623,7 +653,30 @@ case IPC_STAT: if (ipcperms (ipcp, S_IRUGO)) return -EACCES; - tbuf.msg_perm = msq->msg_perm; + tbuf.msg_perm.key = msq->msg_perm.key; + + if(msq->msg_perm.uid > 65535) + tbuf.msg_perm.uid = DEF_HIGHUID; + else + tbuf.msg_perm.uid = (ushort)msq->msg_perm.uid; + + if(msq->msg_perm.gid > 65535) + tbuf.msg_perm.gid = DEF_HIGHUID; + else + tbuf.msg_perm.gid = (ushort)msq->msg_perm.gid; + + if(msq->msg_perm.cuid > 65535) + tbuf.msg_perm.cuid = DEF_HIGHUID; + else + tbuf.msg_perm.cuid = (ushort)msq->msg_perm.cuid; + + if(msq->msg_perm.cgid > 65535) + tbuf.msg_perm.cgid = DEF_HIGHUID; + else + tbuf.msg_perm.cgid = (ushort)msq->msg_perm.cgid; + + tbuf.msg_perm.mode = msq->msg_perm.mode; + tbuf.msg_perm.seq = msq->msg_perm.seq; tbuf.msg_stime = msq->msg_stime; tbuf.msg_rtime = msq->msg_rtime; tbuf.msg_ctime = msq->msg_ctime; @@ -641,8 +694,11 @@ if (tbuf.msg_qbytes > MSGMNB && !suser()) return -EPERM; msq->msg_qbytes = tbuf.msg_qbytes; - ipcp->uid = tbuf.msg_perm.uid; - ipcp->gid = tbuf.msg_perm.gid; + +/* if any UID or GID is passed mod 65536 this may break horribly... */ + + ipcp->uid = (uid_t)tbuf.msg_perm.uid; + ipcp->gid = (gid_t)tbuf.msg_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (S_IRWXUGO & tbuf.msg_perm.mode); msq->msg_ctime = CURRENT_TIME; diff -uNr linux.orig/ipc/sem.c linux/ipc/sem.c --- linux.orig/ipc/sem.c Sat Nov 7 22:50:43 1998 +++ linux/ipc/sem.c Wed Nov 25 16:40:22 1998 @@ -40,6 +40,8 @@ #include #include +#include + extern int ipcperms (struct ipc_perm *ipcp, short semflg); static int newary (key_t, int, int); static int findkey (key_t key); @@ -355,10 +357,14 @@ kfree(sma); } -asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg) +asmlinkage int sys_semctl (int semid, int semnum, int cmd, union uid16_semun arg) { - struct semid_ds *buf = NULL; - struct semid_ds tbuf; +/* buf must be the right size, else we will copy wrong structure size + and corrupt data. because they used a union at this point they have + to use this intermediate pointer +*/ + struct uid16_semid_ds *buf = NULL; + struct uid16_semid_ds tbuf; int i, id, val = 0; struct semid_ds *sma; struct ipc_perm *ipcp; @@ -410,7 +416,30 @@ if (ipcperms (&sma->sem_perm, S_IRUGO)) return -EACCES; id = (unsigned int) sma->sem_perm.seq * SEMMNI + semid; - tbuf.sem_perm = sma->sem_perm; + tbuf.sem_perm.key = sma->sem_perm.key; + + if(sma->sem_perm.uid > 65535) + tbuf.sem_perm.uid = DEF_HIGHUID; + else + tbuf.sem_perm.uid = (ushort)sma->sem_perm.uid; + + if(sma->sem_perm.gid > 65535) + tbuf.sem_perm.gid = DEF_HIGHUID; + else + tbuf.sem_perm.gid = (ushort)sma->sem_perm.gid; + + if(sma->sem_perm.cuid > 65535) + tbuf.sem_perm.cuid = DEF_HIGHUID; + else + tbuf.sem_perm.cuid = (ushort)sma->sem_perm.cuid; + + if(sma->sem_perm.cgid > 65535) + tbuf.sem_perm.cgid = DEF_HIGHUID; + else + tbuf.sem_perm.cgid = (ushort)sma->sem_perm.cgid; + + tbuf.sem_perm.mode = sma->sem_perm.mode; + tbuf.sem_perm.seq = sma->sem_perm.seq; tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; @@ -517,8 +546,9 @@ break; case IPC_SET: if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) { - ipcp->uid = tbuf.sem_perm.uid; - ipcp->gid = tbuf.sem_perm.gid; + /* this has the potential to break horribly */ + ipcp->uid = (uid_t)tbuf.sem_perm.uid; + ipcp->gid = (gid_t)tbuf.sem_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (tbuf.sem_perm.mode & S_IRWXUGO); sma->sem_ctime = CURRENT_TIME; @@ -528,7 +558,30 @@ case IPC_STAT: if (ipcperms (ipcp, S_IRUGO)) return -EACCES; - tbuf.sem_perm = sma->sem_perm; + tbuf.sem_perm.key = sma->sem_perm.key; + + if(sma->sem_perm.uid > 65535) + tbuf.sem_perm.uid = DEF_HIGHUID; + else + tbuf.sem_perm.uid = (ushort)sma->sem_perm.uid; + + if(sma->sem_perm.gid > 65535) + tbuf.sem_perm.gid = DEF_HIGHUID; + else + tbuf.sem_perm.gid = (ushort)sma->sem_perm.gid; + + if(sma->sem_perm.cuid > 65535) + tbuf.sem_perm.cuid = DEF_HIGHUID; + else + tbuf.sem_perm.cuid = (ushort)sma->sem_perm.cuid; + + if(sma->sem_perm.cgid > 65535) + tbuf.sem_perm.cgid = DEF_HIGHUID; + else + tbuf.sem_perm.cgid = (ushort)sma->sem_perm.cgid; + + tbuf.sem_perm.mode = sma->sem_perm.mode; + tbuf.sem_perm.seq = sma->sem_perm.seq; tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; diff -uNr linux.orig/ipc/shm.c linux/ipc/shm.c --- linux.orig/ipc/shm.c Sat Nov 7 22:50:43 1998 +++ linux/ipc/shm.c Mon Nov 23 21:46:54 1998 @@ -18,6 +18,8 @@ #include #include +#include + extern int ipcperms (struct ipc_perm *ipcp, short shmflg); extern unsigned long get_swap_page (void); static int findkey (key_t key); @@ -198,9 +200,9 @@ return; } -asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) +asmlinkage int sys_shmctl (int shmid, int cmd, struct uid16_shmid_ds *buf) { - struct shmid_ds tbuf; + struct uid16_shmid_ds tbuf; struct shmid_ds *shp; struct ipc_perm *ipcp; int id, err; @@ -264,7 +266,31 @@ if (ipcperms (&shp->shm_perm, S_IRUGO)) return -EACCES; id = (unsigned int) shp->shm_perm.seq * SHMMNI + shmid; - tbuf.shm_perm = shp->shm_perm; + + tbuf.shm_perm.key = shp->shm_perm.key; + + if(shp->shm_perm.uid > 65535) + tbuf.shm_perm.uid = DEF_HIGHUID; + else + tbuf.shm_perm.uid = (ushort)shp->shm_perm.uid; + + if(shp->shm_perm.gid > 65535) + tbuf.shm_perm.gid = DEF_HIGHUID; + else + tbuf.shm_perm.gid = (ushort)shp->shm_perm.gid; + + if(shp->shm_perm.cuid > 65535) + tbuf.shm_perm.cuid = DEF_HIGHUID; + else + tbuf.shm_perm.cuid = (ushort)shp->shm_perm.cuid; + + if(shp->shm_perm.cgid > 65535) + tbuf.shm_perm.cgid = DEF_HIGHUID; + else + tbuf.shm_perm.cgid = (ushort)shp->shm_perm.cgid; + + tbuf.shm_perm.mode = shp->shm_perm.mode; + tbuf.shm_perm.seq = shp->shm_perm.seq; tbuf.shm_segsz = shp->shm_segsz; tbuf.shm_atime = shp->shm_atime; tbuf.shm_dtime = shp->shm_dtime; @@ -309,7 +335,31 @@ err = verify_area (VERIFY_WRITE, buf, sizeof (*buf)); if (err) return err; - tbuf.shm_perm = shp->shm_perm; + + tbuf.shm_perm.key = shp->shm_perm.key; + + if(shp->shm_perm.uid > 65535) + tbuf.shm_perm.uid = DEF_HIGHUID; + else + tbuf.shm_perm.uid = (ushort)shp->shm_perm.uid; + + if(shp->shm_perm.gid > 65535) + tbuf.shm_perm.gid = DEF_HIGHUID; + else + tbuf.shm_perm.gid = (ushort)shp->shm_perm.gid; + + if(shp->shm_perm.cuid > 65535) + tbuf.shm_perm.cuid = DEF_HIGHUID; + else + tbuf.shm_perm.cuid = (ushort)shp->shm_perm.cuid; + + if(shp->shm_perm.cgid > 65535) + tbuf.shm_perm.cgid = DEF_HIGHUID; + else + tbuf.shm_perm.cgid = (ushort)shp->shm_perm.cgid; + + tbuf.shm_perm.mode = shp->shm_perm.mode; + tbuf.shm_perm.seq = shp->shm_perm.seq; tbuf.shm_segsz = shp->shm_segsz; tbuf.shm_atime = shp->shm_atime; tbuf.shm_dtime = shp->shm_dtime; @@ -322,8 +372,9 @@ case IPC_SET: if (suser() || current->euid == shp->shm_perm.uid || current->euid == shp->shm_perm.cuid) { - ipcp->uid = tbuf.shm_perm.uid; - ipcp->gid = tbuf.shm_perm.gid; +/* yet another opportunity for disaster */ + ipcp->uid = (uid_t)tbuf.shm_perm.uid; + ipcp->gid = (gid_t)tbuf.shm_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (tbuf.shm_perm.mode & S_IRWXUGO); shp->shm_ctime = CURRENT_TIME; diff -uNr linux.orig/ipc/util.c linux/ipc/util.c --- linux.orig/ipc/util.c Sat Nov 7 22:50:43 1998 +++ linux/ipc/util.c Mon Nov 23 21:03:09 1998 @@ -72,7 +72,7 @@ return -ENOSYS; } -asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg) +asmlinkage int sys_semctl (int semid, int semnum, int cmd, union uid16_semun arg) { return -ENOSYS; } @@ -93,7 +93,7 @@ return -ENOSYS; } -asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) +asmlinkage int sys_msgctl (int msqid, int cmd, struct uid16_msqid_ds *buf) { return -ENOSYS; } @@ -113,7 +113,7 @@ return -ENOSYS; } -asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) +asmlinkage int sys_shmctl (int shmid, int cmd, struct uid16_shmid_ds *buf) { return -ENOSYS; } diff -uNr linux.orig/kernel/ksyms.c linux/kernel/ksyms.c --- linux.orig/kernel/ksyms.c Fri Nov 6 20:09:12 1998 +++ linux/kernel/ksyms.c Wed Sep 30 22:16:28 1998 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff -uNr linux.orig/kernel/sched.c linux/kernel/sched.c --- linux.orig/kernel/sched.c Fri Nov 6 20:09:16 1998 +++ linux/kernel/sched.c Mon Nov 23 21:51:40 1998 @@ -34,6 +34,8 @@ #include #include +#include + #include #include #include @@ -1370,22 +1372,75 @@ asmlinkage int sys_getuid(void) { +/* printk(KERN_DEBUG "32bit syscall: sys_getuid #202\n"); */ + return current->uid; } +/* 16 bit UID compatibility syscall */ +asmlinkage int sys_getuid16(void) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_getuid16 #24\n"); */ + + if(current->uid > 65535) + return DEF_HIGHUID; + else + return (old_uid_t)current->uid; +} + + asmlinkage int sys_geteuid(void) { +/* printk(KERN_DEBUG "32bit syscall: sys_geteuid #205\n"); */ + return current->euid; } +/* 16 bit UID compatibility syscall */ +asmlinkage int sys_geteuid16(void) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_geteuid16 #49\n"); */ + + if(current->euid > 65535) + return DEF_HIGHUID; + else + return (old_uid_t)current->euid; +} + asmlinkage int sys_getgid(void) { +/* printk(KERN_DEBUG "32bit syscall: sys_getgid #204\n"); */ + return current->gid; } +/* 16 bit UID compatibility syscall */ +asmlinkage int sys_getgid16(void) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_getgid16 #47\n"); */ + + if(current->gid > 65535) + return DEF_HIGHUID; + else + return (old_gid_t)current->gid; +} + asmlinkage int sys_getegid(void) { +/* printk(KERN_DEBUG "32bit syscall: sys_getegid #206\n"); */ + return current->egid; +} + +/* 16 bit UID compatibility syscall */ +asmlinkage int sys_getegid16(void) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_getegid16 #50\n"); */ + + if(current->egid > 65535) + return DEF_HIGHUID; + else + return current->egid; } /* diff -uNr linux.orig/kernel/sys.c linux/kernel/sys.c --- linux.orig/kernel/sys.c Fri Nov 6 20:09:21 1998 +++ linux/kernel/sys.c Mon Nov 23 21:53:00 1998 @@ -25,6 +25,8 @@ #include #endif +#include + #include #include @@ -245,6 +247,8 @@ int old_rgid = current->gid; int old_egid = current->egid; +/* printk(KERN_DEBUG "32bit syscall: sys_setregid #208\n"); */ + if (rgid != (gid_t) -1) { if ((old_rgid == rgid) || (current->egid==rgid) || @@ -273,6 +277,34 @@ return 0; } +/* 16 bit UID compatibility syscall */ + +/* xterm didn't work under the new kernel. hmm... it uses setruid()... + wait a minute, -1 is a different thing in 16 and 32 bits!! +*/ + +asmlinkage int sys_setregid16(old_gid_t rgid, old_gid_t egid) +{ + gid_t new_rgid; + gid_t new_egid; + +/* printk(KERN_DEBUG "16bit syscall: sys_setregid16 #71 rgid: %d egid: %d\n", rgid, egid); */ + + if(rgid == (old_gid_t)-1) + new_rgid = (gid_t)-1; + else + new_rgid = (gid_t)rgid; + + if(egid == (old_gid_t)-1) + new_egid = (gid_t)-1; + else + new_egid = (gid_t)egid; + + return ( sys_setregid( new_rgid, new_egid ) ); +} + + + /* * setgid() is implemented like SysV w/ SAVED_IDS */ @@ -280,6 +312,8 @@ { int old_egid = current->egid; +/* printk(KERN_DEBUG "32bit syscall: sys_setgid #203\n"); */ + if (suser()) current->gid = current->egid = current->sgid = current->fsgid = gid; else if ((gid == current->gid) || (gid == current->sgid)) @@ -290,6 +324,14 @@ current->dumpable = 0; return 0; } + +/* 16 bit compatibility syscall */ +asmlinkage int sys_setgid16(old_gid_t gid) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_setgid16 #46 gid: %d\n",gid); */ + + return( sys_setgid( (gid_t)gid ) ); +} static char acct_active = 0; static struct file acct_file; @@ -454,6 +496,8 @@ int old_ruid = current->uid; int old_euid = current->euid; +/* printk(KERN_DEBUG "32bit syscall: sys_reuid #207\n"); */ + if (ruid != (uid_t) -1) { if ((old_ruid == ruid) || (current->euid==ruid) || @@ -482,6 +526,28 @@ return 0; } +/* 16 bit compatibility syscall */ + +asmlinkage int sys_setreuid16(old_uid_t ruid, old_uid_t euid) +{ + uid_t new_ruid; + uid_t new_euid; + +/* printk(KERN_DEBUG "16bit syscall: sys_setreuid16 #70 ruid: %d euid: %d\n",ruid,euid); */ + + if(ruid == (old_uid_t)-1) + new_ruid = (uid_t)-1; + else + new_ruid = (uid_t)ruid; + + if(euid == (old_uid_t)-1) + new_euid = (uid_t)-1; + else + new_euid = (uid_t)euid; + + return( sys_setreuid( new_ruid, new_euid ) ); +} + /* * setuid() is implemented like SysV w/ SAVED_IDS * @@ -497,6 +563,8 @@ { int old_euid = current->euid; +/* printk(KERN_DEBUG "32bit syscall: sys_setuid #201\n"); */ + if (suser()) current->uid = current->euid = current->suid = current->fsuid = uid; else if ((uid == current->uid) || (uid == current->suid)) @@ -508,6 +576,15 @@ return(0); } +/* 16 bit UID compatibility syscall */ + +asmlinkage int sys_setuid16(old_uid_t uid) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_setuid16 #23 uid: %d\n",uid); */ + + return( sys_setuid( (uid_t)uid ) ); +} + /* * "setfsuid()" sets the fsuid - the uid used for filesystem checks. This * is used for "access()" and for the NFS daemon (letting nfsd stay at @@ -518,6 +595,8 @@ { int old_fsuid = current->fsuid; +/* printk(KERN_DEBUG "32bit syscall: sys_setfsuid #216\n"); */ + if (uid == current->uid || uid == current->euid || uid == current->suid || uid == current->fsuid || suser()) current->fsuid = uid; @@ -526,6 +605,15 @@ return old_fsuid; } +/* 16 bit UID compatibility syscall */ + +asmlinkage int sys_setfsuid16(old_uid_t uid) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_setfsuid16 #138 uid: %d\n",uid); */ + + return( sys_setfsuid( (uid_t)uid ) ); +} + /* * Samma på svenska.. */ @@ -533,6 +621,8 @@ { int old_fsgid = current->fsgid; +/* printk(KERN_DEBUG "32bit syscall: sys_setfsgid #217\n"); */ + if (gid == current->gid || gid == current->egid || gid == current->sgid || gid == current->fsgid || suser()) current->fsgid = gid; @@ -541,6 +631,14 @@ return old_fsgid; } +/* 16 bit UID compatibility syscall */ +asmlinkage int sys_setfsgid16(old_gid_t gid) +{ +/* printk(KERN_DEBUG "16bit syscall: sys_setfsgid16 #139 gid: %d\n",gid); */ + + return( sys_setfsgid( (gid_t)gid ) ); +} + asmlinkage long sys_times(struct tms * tbuf) { if (tbuf) { @@ -663,6 +761,8 @@ int i; int * groups; +/* printk(KERN_DEBUG "32bit syscall: sys_getgroups #209\n"); */ + /* Avoid an integer overflow on systems with 32 bit gid_t (Alpha) */ if (gidsetsize & ~0x3FFFFFFF) return -EINVAL; @@ -689,10 +789,56 @@ return i; } +/* 16 bit UID compatibility syscall + Can't just call the old function now ! */ + +asmlinkage int sys_getgroups16(int gidsetsize, old_gid_t *grouplist) +{ + int i; + int * groups; + +/* printk(KERN_DEBUG "16bit syscall: sys_getgroups16 #80\n"); */ + + /* Avoid an integer overflow on systems with 32 bit gid_t (Alpha) */ + if (gidsetsize & ~0x3FFFFFFF) + return -EINVAL; + groups = current->groups; + for (i = 0 ; i < NGROUPS ; i++) { + if (groups[i] == NOGROUP) + break; + } + if (gidsetsize) { + int error; + error = verify_area(VERIFY_WRITE, grouplist, sizeof(old_gid_t) * gidsetsize); + if (error) + return error; + if (i > gidsetsize) + return -EINVAL; + + for (i = 0 ; i < NGROUPS ; i++) { + if (groups[i] == NOGROUP) + break; + +/* return DEF_HIGHUID if gid is greater than 65535. */ +/* confused about put_user, looked at it. automatically sizes + proper length to write. Nifty! */ + + if (groups[i] > 65535) + put_user(DEF_HIGHUID, grouplist); + else + put_user((old_gid_t)groups[i], grouplist); + grouplist++; + } + } + return i; +} + asmlinkage int sys_setgroups(int gidsetsize, gid_t *grouplist) { int i; +/* printk(KERN_DEBUG "32bit syscall: sys_setgroups #210\n"); */ + if (!suser()) return -EPERM; if (gidsetsize > NGROUPS) @@ -707,6 +853,30 @@ current->groups[i] = NOGROUP; return 0; } + +/* 16 bit UID compatibility syscall */ + +asmlinkage int sys_setgroups16(int gidsetsize, old_gid_t *grouplist) +{ + int i; + +/* printk(KERN_DEBUG "16bit syscall: sys_setgroups16 #81\n"); */ + + if (!suser()) + return -EPERM; + if (gidsetsize > NGROUPS) + return -EINVAL; + i = verify_area(VERIFY_READ, grouplist, sizeof(old_gid_t) * gidsetsize); + if (i) + return i; + for (i = 0; i < gidsetsize; i++, grouplist++) { + current->groups[i] = (gid_t)get_user(grouplist); + } + if (i < NGROUPS) + current->groups[i] = NOGROUP; + return 0; +} + int in_group_p(gid_t grp) {