diff options
| author | Root THC | 2026-02-24 12:42:47 +0000 |
|---|---|---|
| committer | Root THC | 2026-02-24 12:42:47 +0000 |
| commit | c9cbeced5b3f2bdd7407e29c0811e65954132540 (patch) | |
| tree | aefc355416b561111819de159ccbd86c3004cf88 /other/ecfs | |
| parent | 073fe4bf9fca6bf40cef2886d75df832ef4b6fca (diff) | |
initial
Diffstat (limited to 'other/ecfs')
| -rw-r--r-- | other/ecfs/CHANGES | 157 | ||||
| -rw-r--r-- | other/ecfs/Makefile | 23 | ||||
| -rw-r--r-- | other/ecfs/README | 46 | ||||
| -rw-r--r-- | other/ecfs/balloc.c | 805 | ||||
| -rw-r--r-- | other/ecfs/balloc.o | bin | 0 -> 6800 bytes | |||
| -rw-r--r-- | other/ecfs/bitmap.c | 27 | ||||
| -rw-r--r-- | other/ecfs/bitmap.o | bin | 0 -> 1124 bytes | |||
| -rw-r--r-- | other/ecfs/dir.c | 182 | ||||
| -rw-r--r-- | other/ecfs/dir.o | bin | 0 -> 3104 bytes | |||
| -rw-r--r-- | other/ecfs/ecfs.o | bin | 0 -> 48118 bytes | |||
| -rw-r--r-- | other/ecfs/file.c | 233 | ||||
| -rw-r--r-- | other/ecfs/file.o | bin | 0 -> 3168 bytes | |||
| -rw-r--r-- | other/ecfs/fsync.c | 54 | ||||
| -rw-r--r-- | other/ecfs/fsync.o | bin | 0 -> 1308 bytes | |||
| -rw-r--r-- | other/ecfs/ialloc.c | 550 | ||||
| -rw-r--r-- | other/ecfs/ialloc.o | bin | 0 -> 5356 bytes | |||
| -rw-r--r-- | other/ecfs/inode.c | 1329 | ||||
| -rw-r--r-- | other/ecfs/inode.o | bin | 0 -> 11924 bytes | |||
| -rw-r--r-- | other/ecfs/ioctl.c | 91 | ||||
| -rw-r--r-- | other/ecfs/ioctl.o | bin | 0 -> 1972 bytes | |||
| -rw-r--r-- | other/ecfs/namei.c | 824 | ||||
| -rw-r--r-- | other/ecfs/namei.o | bin | 0 -> 9216 bytes | |||
| -rw-r--r-- | other/ecfs/rc4.c | 76 | ||||
| -rw-r--r-- | other/ecfs/rc4.h | 20 | ||||
| -rw-r--r-- | other/ecfs/rc4.o | bin | 0 -> 1272 bytes | |||
| -rw-r--r-- | other/ecfs/repl.pl | 26 | ||||
| -rw-r--r-- | other/ecfs/start-ecfs | 29 | ||||
| -rw-r--r-- | other/ecfs/super.c | 817 | ||||
| -rw-r--r-- | other/ecfs/super.o | bin | 0 -> 12580 bytes | |||
| -rw-r--r-- | other/ecfs/symlink.c | 38 | ||||
| -rw-r--r-- | other/ecfs/symlink.o | bin | 0 -> 1316 bytes | |||
| -rw-r--r-- | other/ecfs/transform | 53 |
32 files changed, 5380 insertions, 0 deletions
diff --git a/other/ecfs/CHANGES b/other/ecfs/CHANGES new file mode 100644 index 0000000..aa5aaf0 --- /dev/null +++ b/other/ecfs/CHANGES | |||
| @@ -0,0 +1,157 @@ | |||
| 1 | Changes from version 0.5a to version 0.5b | ||
| 2 | ========================================= | ||
| 3 | - Now that we have sysctl(), the immutable flag cannot be changed when | ||
| 4 | the system is running at security level > 0. | ||
| 5 | - Some cleanups in the code. | ||
| 6 | - More consistency checks on directories. | ||
| 7 | - The ext2.diff patch from Tom May <ftom@netcom.com> has been | ||
| 8 | integrated. This patch replaces expensive "/" and "%" with | ||
| 9 | cheap ">>" and "&" where possible. | ||
| 10 | |||
| 11 | Changes from version 0.5 to version 0.5a | ||
| 12 | ======================================== | ||
| 13 | - Zero the partial block following the end of the file when a file | ||
| 14 | is truncated. | ||
| 15 | - Dates updated in the copyright. | ||
| 16 | - More checks when the filesystem is mounted: the count of blocks, | ||
| 17 | fragments, and inodes per group is checked against the block size. | ||
| 18 | - The buffers used by the error routines are now static variables, to | ||
| 19 | avoid using space on the kernel stack, as requested by Linus. | ||
| 20 | - Some cleanups in the error messages (some versions of syslog contain | ||
| 21 | a bug which truncates an error message if it contains '\n'). | ||
| 22 | - Check that no data can be written to a file past the 2GB limit. | ||
| 23 | - The famous readdir() bug has been fixed by Stephen Tweedie. | ||
| 24 | - Added a revision level in the superblock. | ||
| 25 | - Full support for O_SYNC flag of the open system call. | ||
| 26 | - New mount options: `resuid=#uid' and `resgid=#gid'. `resuid' causes | ||
| 27 | ext2fs to consider user #uid like root for the reserved blocks. | ||
| 28 | `resgid' acts the same way with group #gid. New fields in the | ||
| 29 | superblock contain default values for resuid and resgid and can | ||
| 30 | be modified by tune2fs. | ||
| 31 | Idea comes from Rene Cougnenc <cougnenc@renux.frmug.fr.net>. | ||
| 32 | - New mount options: `bsddf' and `minixdf'. `bsddf' causes ext2fs | ||
| 33 | to remove the blocks used for FS structures from the total block | ||
| 34 | count in statfs. With `minixdf', ext2fs mimics Minix behavior | ||
| 35 | in statfs (i.e. it returns the total number of blocks on the | ||
| 36 | partition). This is intended to make bde happy :-) | ||
| 37 | - New file attributes: | ||
| 38 | - Immutable files cannot be modified. Data cannot be written to | ||
| 39 | these files. They cannot be removed, renamed and new links cannot | ||
| 40 | be created. Even root cannot modify the files. He has to remove | ||
| 41 | the immutable attribute first. | ||
| 42 | - Append-only files: can only be written in append-mode when writing. | ||
| 43 | They cannot be removed, renamed and new links cannot be created. | ||
| 44 | Note: files may only be added to an append-only directory. | ||
| 45 | - No-dump files: the attribute is not used by the kernel. My port | ||
| 46 | of dump uses it to avoid backing up files which are not important. | ||
| 47 | - New check in ext2_check_dir_entry: the inode number is checked. | ||
| 48 | - Support for big file systems: the copy of the FS descriptor is now | ||
| 49 | dynamically allocated (previous versions used a fixed size array). | ||
| 50 | This allows to mount 2GB+ FS. | ||
| 51 | - Reorganization of the ext2_inode structure to allow other operating | ||
| 52 | systems to create specific fields if they use ext2fs as their native | ||
| 53 | file system. Currently, ext2fs is only implemented in Linux but | ||
| 54 | will soon be part of Gnu Hurd and of Masix. | ||
| 55 | |||
| 56 | Changes from version 0.4b to version 0.5 | ||
| 57 | ======================================== | ||
| 58 | - New superblock fields: s_lastcheck and s_checkinterval added | ||
| 59 | by Uwe Ohse <uwe@tirka.gun.de> to implement timedependent checks | ||
| 60 | of the file system | ||
| 61 | - Real random numbers for secure rm added by Pierre del Perugia | ||
| 62 | <delperug@gla.ecoledoc.ibp.fr> | ||
| 63 | - The mount warnings related to the state of a fs are not printed | ||
| 64 | if the fs is mounted read-only, idea by Nick Holloway | ||
| 65 | <alfie@dcs.warwick.ac.uk> | ||
| 66 | |||
| 67 | Changes from version 0.4a to version 0.4b | ||
| 68 | ========================================= | ||
| 69 | - Copyrights changed to include the name of my laboratory. | ||
| 70 | - Clean up of balloc.c and ialloc.c. | ||
| 71 | - More consistency checks. | ||
| 72 | - Block preallocation added by Stephen Tweedie. | ||
| 73 | - Direct reads of directories disallowed. | ||
| 74 | - Readahead implemented in readdir by Stephen Tweedie. | ||
| 75 | - Bugs in block and inodes allocation fixed. | ||
| 76 | - Readahead implemented in ext2_find_entry by Chip Salzenberg. | ||
| 77 | - New mount options: | ||
| 78 | `check=none|normal|strict' | ||
| 79 | `debug' | ||
| 80 | `errors=continue|remount-ro|panic' | ||
| 81 | `grpid', `bsdgroups' | ||
| 82 | `nocheck' | ||
| 83 | `nogrpid', `sysvgroups' | ||
| 84 | - truncate() now tries to deallocate contiguous blocks in a single call | ||
| 85 | to ext2_free_blocks(). | ||
| 86 | - lots of cosmetic changes. | ||
| 87 | |||
| 88 | Changes from version 0.4 to version 0.4a | ||
| 89 | ======================================== | ||
| 90 | - the `sync' option support is now complete. Version 0.4 was not | ||
| 91 | supporting it when truncating a file. I have tested the synchronous | ||
| 92 | writes and they work but they make the system very slow :-( I have | ||
| 93 | to work again on this to make it faster. | ||
| 94 | - when detecting an error on a mounted filesystem, version 0.4 used | ||
| 95 | to try to write a flag in the super block even if the filesystem had | ||
| 96 | been mounted read-only. This is fixed. | ||
| 97 | - the `sb=#' option now causes the kernel code to use the filesystem | ||
| 98 | descriptors located at block #+1. Version 0.4 used the superblock | ||
| 99 | backup located at block # but used the main copy of the descriptors. | ||
| 100 | - a new file attribute `S' is supported. This attribute causes | ||
| 101 | synchronous writes but is applied to a file not to the entire file | ||
| 102 | system (thanks to Michael Kraehe <kraehe@bakunin.north.de> for | ||
| 103 | suggesting it). | ||
| 104 | - the directory cache is inhibited by default. The cache management | ||
| 105 | code seems to be buggy and I have to look at it carefully before | ||
| 106 | using it again. | ||
| 107 | - deleting a file with the `s' attribute (secure deletion) causes its | ||
| 108 | blocks to be overwritten with random values not with zeros (thanks to | ||
| 109 | Michael A. Griffith <grif@cs.ucr.edu> for suggesting it). | ||
| 110 | - lots of cosmetic changes have been made. | ||
| 111 | |||
| 112 | Changes from version 0.3 to version 0.4 | ||
| 113 | ======================================= | ||
| 114 | - Three new mount options are supported: `check', `sync' and `sb=#'. | ||
| 115 | `check' tells the kernel code to make more consistency checks | ||
| 116 | when the file system is mounted. Currently, the kernel code checks | ||
| 117 | that the blocks and inodes bitmaps are consistent with the free | ||
| 118 | blocks and inodes counts. More checks will be added in future | ||
| 119 | releases. | ||
| 120 | `sync' tells the kernel code to use synchronous writes when updating | ||
| 121 | an inode, a bitmap, a directory entry or an indirect block. This | ||
| 122 | can make the file system much slower but can be a big win for files | ||
| 123 | recovery in case of a crash (and we can now say to the BSD folks | ||
| 124 | that Linux also supports synchronous updates :-). | ||
| 125 | `sb=#' tells the kernel code to use an alternate super block instead | ||
| 126 | of its master copy. `#' is the number of the block (counted in | ||
| 127 | 1024 bytes blocks) which contains the alternate super block. | ||
| 128 | An ext2 file system typically contains backups of the super block | ||
| 129 | at blocks 8193, 16385, and so on. | ||
| 130 | - I have change the meaning of the valid flag used by e2fsck. it | ||
| 131 | now contains the state of the file system. If the kernel code | ||
| 132 | detects an inconsistency while the file system is mounted, it flags | ||
| 133 | it as erroneous and e2fsck will detect that on next run. | ||
| 134 | - The super block now contains a mount counter. This counter is | ||
| 135 | incremented each time the file system is mounted read/write. When | ||
| 136 | this counter becomes bigger than a maximal mount counts (also stored | ||
| 137 | in the super block), e2fsck checks the file system, even if it had | ||
| 138 | been unmounted cleanly, and resets this counter to 0. | ||
| 139 | - File attributes are now supported. One can associate a set of | ||
| 140 | attributes to a file. Three attributes are defined: | ||
| 141 | `c': the file is marked for automatic compression, | ||
| 142 | `s': the file is marked for secure deletion: when the file is | ||
| 143 | deleted, its blocks are zeroed and written back to the disk, | ||
| 144 | `u': the file is marked for undeletion: when the file is deleted, | ||
| 145 | its contents are saved to allow a future undeletion. | ||
| 146 | Currently, only the `s' attribute is implemented in the kernel | ||
| 147 | code. Support for the other attributes will be added in a future | ||
| 148 | release. | ||
| 149 | - a few bugs related to times updates have been fixed by Bruce | ||
| 150 | Evans and me. | ||
| 151 | - a bug related to the links count of deleted inodes has been fixed. | ||
| 152 | Previous versions used to keep the links count set to 1 when a file | ||
| 153 | was deleted. The new version now sets links_count to 0 when deleting | ||
| 154 | the last link. | ||
| 155 | - a race condition when deallocating an inode has been fixed by | ||
| 156 | Stephen Tweedie. | ||
| 157 | |||
diff --git a/other/ecfs/Makefile b/other/ecfs/Makefile new file mode 100644 index 0000000..7623ef0 --- /dev/null +++ b/other/ecfs/Makefile | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | CFLAGS=-m486 -c -O2 -Wall -DMODULE -D__KERNEL__ -I/usr/src/linux/include | ||
| 2 | CC=cc | ||
| 3 | LD=ld | ||
| 4 | |||
| 5 | .SUFFIXES: .o .c | ||
| 6 | |||
| 7 | .c.o: | ||
| 8 | $(CC) $(CFLAGS) $< | ||
| 9 | |||
| 10 | OBJ = balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ | ||
| 11 | ioctl.o namei.o super.o symlink.o rc4.o | ||
| 12 | |||
| 13 | all: ecfs.o | ||
| 14 | |||
| 15 | ecfs.o: ${OBJ} | ||
| 16 | $(LD) -r ${OBJ} -o $@ | ||
| 17 | |||
| 18 | clean: | ||
| 19 | rm -rf *.o | ||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
diff --git a/other/ecfs/README b/other/ecfs/README new file mode 100644 index 0000000..0936411 --- /dev/null +++ b/other/ecfs/README | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | for starters | ||
| 2 | ============ | ||
| 3 | |||
| 4 | !!! | ||
| 5 | |||
| 6 | Do not use ECFS. Use it for testing purposes only! | ||
| 7 | |||
| 8 | !!! | ||
| 9 | |||
| 10 | |||
| 11 | 1. Compile ecfs for your current kernel: | ||
| 12 | |||
| 13 | $ cd /path/to/ecfs; make | ||
| 14 | |||
| 15 | 2. Install | ||
| 16 | |||
| 17 | # cp ecfs.o /lib/modules/`uname -r`/fs | ||
| 18 | |||
| 19 | 3. Start ECFS | ||
| 20 | |||
| 21 | # ./start-ecfs | ||
| 22 | |||
| 23 | Enter your key (at least 8 byte) and ECFS is loaded. | ||
| 24 | |||
| 25 | 4. Use ECFS | ||
| 26 | |||
| 27 | # mount -t ecfs /dev/fd0 /mnt | ||
| 28 | |||
| 29 | All read/write to mnt are now crypted. You can use ext2tools | ||
| 30 | to handle ECFS partitions, like fsck.ext2 etc. Its Ext2 after all! | ||
| 31 | ECFS cant handle mmap(), so its not for your root-FS, its even | ||
| 32 | in development, so send feedback to stealth@segfault.net. | ||
| 33 | |||
| 34 | |||
| 35 | !!! | ||
| 36 | |||
| 37 | Since its in development, your data may be unreadable | ||
| 38 | when using ECFS. | ||
| 39 | Don't blame me, use tcfs or so if you want well-tested | ||
| 40 | CFS. | ||
| 41 | |||
| 42 | !!! | ||
| 43 | |||
| 44 | |||
| 45 | Stealth | ||
| 46 | |||
diff --git a/other/ecfs/balloc.c b/other/ecfs/balloc.c new file mode 100644 index 0000000..401b2c5 --- /dev/null +++ b/other/ecfs/balloc.c | |||
| @@ -0,0 +1,805 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/balloc.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | * | ||
| 9 | * Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 | ||
| 10 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
| 11 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/config.h> | ||
| 15 | #include <linux/fs_ecfs.h> | ||
| 16 | #include <linux/ecfs_fs.h> | ||
| 17 | #include <linux/locks.h> | ||
| 18 | //#include <linux/quotaops.h> | ||
| 19 | |||
| 20 | #define ecfs_find_first_zero_bit find_first_zero_bit | ||
| 21 | #define ecfs_clear_bit __test_and_clear_bit | ||
| 22 | #define ecfs_set_bit __test_and_set_bit | ||
| 23 | #define ecfs_find_next_zero_bit find_next_zero_bit | ||
| 24 | #define ecfs_test_bit test_bit | ||
| 25 | |||
| 26 | // blubber | ||
| 27 | #ifndef KERNEL_VERSION | ||
| 28 | #define KERNEL_VERSION(x,y,z) (x,y,z) | ||
| 29 | #endif | ||
| 30 | |||
| 31 | /* | ||
| 32 | * balloc.c contains the blocks allocation and deallocation routines | ||
| 33 | */ | ||
| 34 | |||
| 35 | /* | ||
| 36 | * The free blocks are managed by bitmaps. A file system contains several | ||
| 37 | * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap | ||
| 38 | * block for inodes, N blocks for the inode table and data blocks. | ||
| 39 | * | ||
| 40 | * The file system contains group descriptors which are located after the | ||
| 41 | * super block. Each descriptor contains the number of the bitmap block and | ||
| 42 | * the free blocks count in the block. The descriptors are loaded in memory | ||
| 43 | * when a file system is mounted (see ecfs_read_super). | ||
| 44 | */ | ||
| 45 | |||
| 46 | |||
| 47 | #define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) | ||
| 48 | |||
| 49 | struct ecfs_group_desc * ecfs_get_group_desc(struct super_block * sb, | ||
| 50 | unsigned int block_group, | ||
| 51 | struct buffer_head ** bh) | ||
| 52 | { | ||
| 53 | unsigned long group_desc; | ||
| 54 | unsigned long desc; | ||
| 55 | struct ecfs_group_desc * gdp; | ||
| 56 | |||
| 57 | if (block_group >= sb->u.ecfs_sb.s_groups_count) { | ||
| 58 | ecfs_error (sb, "ecfs_get_group_desc", | ||
| 59 | "block_group >= groups_count - " | ||
| 60 | "block_group = %d, groups_count = %lu", | ||
| 61 | block_group, sb->u.ecfs_sb.s_groups_count); | ||
| 62 | |||
| 63 | return NULL; | ||
| 64 | } | ||
| 65 | |||
| 66 | group_desc = block_group / ECFS_DESC_PER_BLOCK(sb); | ||
| 67 | desc = block_group % ECFS_DESC_PER_BLOCK(sb); | ||
| 68 | if (!sb->u.ecfs_sb.s_group_desc[group_desc]) { | ||
| 69 | ecfs_error (sb, "ecfs_get_group_desc", | ||
| 70 | "Group descriptor not loaded - " | ||
| 71 | "block_group = %d, group_desc = %lu, desc = %lu", | ||
| 72 | block_group, group_desc, desc); | ||
| 73 | return NULL; | ||
| 74 | } | ||
| 75 | |||
| 76 | gdp = (struct ecfs_group_desc *) | ||
| 77 | sb->u.ecfs_sb.s_group_desc[group_desc]->b_data; | ||
| 78 | if (bh) | ||
| 79 | *bh = sb->u.ecfs_sb.s_group_desc[group_desc]; | ||
| 80 | return gdp + desc; | ||
| 81 | } | ||
| 82 | |||
| 83 | /* | ||
| 84 | * Read the bitmap for a given block_group, reading into the specified | ||
| 85 | * slot in the superblock's bitmap cache. | ||
| 86 | * | ||
| 87 | * Return >=0 on success or a -ve error code. | ||
| 88 | */ | ||
| 89 | |||
| 90 | static int read_block_bitmap (struct super_block * sb, | ||
| 91 | unsigned int block_group, | ||
| 92 | unsigned long bitmap_nr) | ||
| 93 | { | ||
| 94 | struct ecfs_group_desc * gdp; | ||
| 95 | struct buffer_head * bh = NULL; | ||
| 96 | int retval = -EIO; | ||
| 97 | |||
| 98 | gdp = ecfs_get_group_desc (sb, block_group, NULL); | ||
| 99 | if (!gdp) | ||
| 100 | goto error_out; | ||
| 101 | retval = 0; | ||
| 102 | bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize); | ||
| 103 | if (!bh) { | ||
| 104 | ecfs_error (sb, "read_block_bitmap", | ||
| 105 | "Cannot read block bitmap - " | ||
| 106 | "block_group = %d, block_bitmap = %lu", | ||
| 107 | block_group, (unsigned long) gdp->bg_block_bitmap); | ||
| 108 | retval = -EIO; | ||
| 109 | } | ||
| 110 | /* | ||
| 111 | * On IO error, just leave a zero in the superblock's block pointer for | ||
| 112 | * this group. The IO will be retried next time. | ||
| 113 | */ | ||
| 114 | error_out: | ||
| 115 | sb->u.ecfs_sb.s_block_bitmap_number[bitmap_nr] = block_group; | ||
| 116 | sb->u.ecfs_sb.s_block_bitmap[bitmap_nr] = bh; | ||
| 117 | return retval; | ||
| 118 | } | ||
| 119 | |||
| 120 | /* | ||
| 121 | * load_block_bitmap loads the block bitmap for a blocks group | ||
| 122 | * | ||
| 123 | * It maintains a cache for the last bitmaps loaded. This cache is managed | ||
| 124 | * with a LRU algorithm. | ||
| 125 | * | ||
| 126 | * Notes: | ||
| 127 | * 1/ There is one cache per mounted file system. | ||
| 128 | * 2/ If the file system contains less than ECFS_MAX_GROUP_LOADED groups, | ||
| 129 | * this function reads the bitmap without maintaining a LRU cache. | ||
| 130 | * | ||
| 131 | * Return the slot used to store the bitmap, or a -ve error code. | ||
| 132 | */ | ||
| 133 | static int __load_block_bitmap (struct super_block * sb, | ||
| 134 | unsigned int block_group) | ||
| 135 | { | ||
| 136 | int i, j, retval = 0; | ||
| 137 | unsigned long block_bitmap_number; | ||
| 138 | struct buffer_head * block_bitmap; | ||
| 139 | |||
| 140 | if (block_group >= sb->u.ecfs_sb.s_groups_count) | ||
| 141 | ecfs_panic (sb, "load_block_bitmap", | ||
| 142 | "block_group >= groups_count - " | ||
| 143 | "block_group = %d, groups_count = %lu", | ||
| 144 | block_group, sb->u.ecfs_sb.s_groups_count); | ||
| 145 | |||
| 146 | if (sb->u.ecfs_sb.s_groups_count <= ECFS_MAX_GROUP_LOADED) { | ||
| 147 | if (sb->u.ecfs_sb.s_block_bitmap[block_group]) { | ||
| 148 | if (sb->u.ecfs_sb.s_block_bitmap_number[block_group] == | ||
| 149 | block_group) | ||
| 150 | return block_group; | ||
| 151 | ecfs_error (sb, "__load_block_bitmap", | ||
| 152 | "block_group != block_bitmap_number"); | ||
| 153 | } | ||
| 154 | retval = read_block_bitmap (sb, block_group, block_group); | ||
| 155 | if (retval < 0) | ||
| 156 | return retval; | ||
| 157 | return block_group; | ||
| 158 | } | ||
| 159 | |||
| 160 | for (i = 0; i < sb->u.ecfs_sb.s_loaded_block_bitmaps && | ||
| 161 | sb->u.ecfs_sb.s_block_bitmap_number[i] != block_group; i++) | ||
| 162 | ; | ||
| 163 | if (i < sb->u.ecfs_sb.s_loaded_block_bitmaps && | ||
| 164 | sb->u.ecfs_sb.s_block_bitmap_number[i] == block_group) { | ||
| 165 | block_bitmap_number = sb->u.ecfs_sb.s_block_bitmap_number[i]; | ||
| 166 | block_bitmap = sb->u.ecfs_sb.s_block_bitmap[i]; | ||
| 167 | for (j = i; j > 0; j--) { | ||
| 168 | sb->u.ecfs_sb.s_block_bitmap_number[j] = | ||
| 169 | sb->u.ecfs_sb.s_block_bitmap_number[j - 1]; | ||
| 170 | sb->u.ecfs_sb.s_block_bitmap[j] = | ||
| 171 | sb->u.ecfs_sb.s_block_bitmap[j - 1]; | ||
| 172 | } | ||
| 173 | sb->u.ecfs_sb.s_block_bitmap_number[0] = block_bitmap_number; | ||
| 174 | sb->u.ecfs_sb.s_block_bitmap[0] = block_bitmap; | ||
| 175 | |||
| 176 | /* | ||
| 177 | * There's still one special case here --- if block_bitmap == 0 | ||
| 178 | * then our last attempt to read the bitmap failed and we have | ||
| 179 | * just ended up caching that failure. Try again to read it. | ||
| 180 | */ | ||
| 181 | if (!block_bitmap) | ||
| 182 | retval = read_block_bitmap (sb, block_group, 0); | ||
| 183 | } else { | ||
| 184 | if (sb->u.ecfs_sb.s_loaded_block_bitmaps < ECFS_MAX_GROUP_LOADED) | ||
| 185 | sb->u.ecfs_sb.s_loaded_block_bitmaps++; | ||
| 186 | else | ||
| 187 | brelse (sb->u.ecfs_sb.s_block_bitmap[ECFS_MAX_GROUP_LOADED - 1]); | ||
| 188 | for (j = sb->u.ecfs_sb.s_loaded_block_bitmaps - 1; j > 0; j--) { | ||
| 189 | sb->u.ecfs_sb.s_block_bitmap_number[j] = | ||
| 190 | sb->u.ecfs_sb.s_block_bitmap_number[j - 1]; | ||
| 191 | sb->u.ecfs_sb.s_block_bitmap[j] = | ||
| 192 | sb->u.ecfs_sb.s_block_bitmap[j - 1]; | ||
| 193 | } | ||
| 194 | retval = read_block_bitmap (sb, block_group, 0); | ||
| 195 | } | ||
| 196 | return retval; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* | ||
| 200 | * Load the block bitmap for a given block group. First of all do a couple | ||
| 201 | * of fast lookups for common cases and then pass the request onto the guts | ||
| 202 | * of the bitmap loader. | ||
| 203 | * | ||
| 204 | * Return the slot number of the group in the superblock bitmap cache's on | ||
| 205 | * success, or a -ve error code. | ||
| 206 | * | ||
| 207 | * There is still one inconsistency here --- if the number of groups in this | ||
| 208 | * filesystems is <= ECFS_MAX_GROUP_LOADED, then we have no way of | ||
| 209 | * differentiating between a group for which we have never performed a bitmap | ||
| 210 | * IO request, and a group for which the last bitmap read request failed. | ||
| 211 | */ | ||
| 212 | static inline int load_block_bitmap (struct super_block * sb, | ||
| 213 | unsigned int block_group) | ||
| 214 | { | ||
| 215 | int slot; | ||
| 216 | |||
| 217 | /* | ||
| 218 | * Do the lookup for the slot. First of all, check if we're asking | ||
| 219 | * for the same slot as last time, and did we succeed that last time? | ||
| 220 | */ | ||
| 221 | if (sb->u.ecfs_sb.s_loaded_block_bitmaps > 0 && | ||
| 222 | sb->u.ecfs_sb.s_block_bitmap_number[0] == block_group && | ||
| 223 | sb->u.ecfs_sb.s_block_bitmap[0]) { | ||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | /* | ||
| 227 | * Or can we do a fast lookup based on a loaded group on a filesystem | ||
| 228 | * small enough to be mapped directly into the superblock? | ||
| 229 | */ | ||
| 230 | else if (sb->u.ecfs_sb.s_groups_count <= ECFS_MAX_GROUP_LOADED && | ||
| 231 | sb->u.ecfs_sb.s_block_bitmap_number[block_group] == block_group && | ||
| 232 | sb->u.ecfs_sb.s_block_bitmap[block_group]) { | ||
| 233 | slot = block_group; | ||
| 234 | } | ||
| 235 | /* | ||
| 236 | * If not, then do a full lookup for this block group. | ||
| 237 | */ | ||
| 238 | else { | ||
| 239 | slot = __load_block_bitmap (sb, block_group); | ||
| 240 | } | ||
| 241 | |||
| 242 | /* | ||
| 243 | * <0 means we just got an error | ||
| 244 | */ | ||
| 245 | if (slot < 0) | ||
| 246 | return slot; | ||
| 247 | |||
| 248 | /* | ||
| 249 | * If it's a valid slot, we may still have cached a previous IO error, | ||
| 250 | * in which case the bh in the superblock cache will be zero. | ||
| 251 | */ | ||
| 252 | if (!sb->u.ecfs_sb.s_block_bitmap[slot]) | ||
| 253 | return -EIO; | ||
| 254 | |||
| 255 | /* | ||
| 256 | * Must have been read in OK to get this far. | ||
| 257 | */ | ||
| 258 | return slot; | ||
| 259 | } | ||
| 260 | |||
| 261 | void ecfs_free_blocks (struct inode * inode, unsigned long block, | ||
| 262 | unsigned long count) | ||
| 263 | { | ||
| 264 | struct buffer_head * bh; | ||
| 265 | struct buffer_head * bh2; | ||
| 266 | unsigned long block_group; | ||
| 267 | unsigned long bit; | ||
| 268 | unsigned long i; | ||
| 269 | int bitmap_nr; | ||
| 270 | unsigned long overflow; | ||
| 271 | struct super_block * sb; | ||
| 272 | struct ecfs_group_desc * gdp; | ||
| 273 | struct ecfs_super_block * es; | ||
| 274 | |||
| 275 | sb = inode->i_sb; | ||
| 276 | if (!sb) { | ||
| 277 | printk ("ecfs_free_blocks: nonexistent device"); | ||
| 278 | return; | ||
| 279 | } | ||
| 280 | lock_super (sb); | ||
| 281 | es = sb->u.ecfs_sb.s_es; | ||
| 282 | if (block < le32_to_cpu(es->s_first_data_block) || | ||
| 283 | (block + count) > le32_to_cpu(es->s_blocks_count)) { | ||
| 284 | ecfs_error (sb, "ecfs_free_blocks", | ||
| 285 | "Freeing blocks not in datazone - " | ||
| 286 | "block = %lu, count = %lu", block, count); | ||
| 287 | goto error_return; | ||
| 288 | } | ||
| 289 | |||
| 290 | ecfs_debug ("freeing block %lu\n", block); | ||
| 291 | |||
| 292 | do_more: | ||
| 293 | overflow = 0; | ||
| 294 | block_group = (block - le32_to_cpu(es->s_first_data_block)) / | ||
| 295 | ECFS_BLOCKS_PER_GROUP(sb); | ||
| 296 | bit = (block - le32_to_cpu(es->s_first_data_block)) % | ||
| 297 | ECFS_BLOCKS_PER_GROUP(sb); | ||
| 298 | /* | ||
| 299 | * Check to see if we are freeing blocks across a group | ||
| 300 | * boundary. | ||
| 301 | */ | ||
| 302 | if (bit + count > ECFS_BLOCKS_PER_GROUP(sb)) { | ||
| 303 | overflow = bit + count - ECFS_BLOCKS_PER_GROUP(sb); | ||
| 304 | count -= overflow; | ||
| 305 | } | ||
| 306 | bitmap_nr = load_block_bitmap (sb, block_group); | ||
| 307 | if (bitmap_nr < 0) | ||
| 308 | goto error_return; | ||
| 309 | |||
| 310 | bh = sb->u.ecfs_sb.s_block_bitmap[bitmap_nr]; | ||
| 311 | gdp = ecfs_get_group_desc (sb, block_group, &bh2); | ||
| 312 | if (!gdp) | ||
| 313 | goto error_return; | ||
| 314 | |||
| 315 | if (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) || | ||
| 316 | in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) || | ||
| 317 | in_range (block, le32_to_cpu(gdp->bg_inode_table), | ||
| 318 | sb->u.ecfs_sb.s_itb_per_group) || | ||
| 319 | in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table), | ||
| 320 | sb->u.ecfs_sb.s_itb_per_group)) | ||
| 321 | ecfs_error (sb, "ecfs_free_blocks", | ||
| 322 | "Freeing blocks in system zones - " | ||
| 323 | "Block = %lu, count = %lu", | ||
| 324 | block, count); | ||
| 325 | |||
| 326 | for (i = 0; i < count; i++) { | ||
| 327 | if (!ecfs_clear_bit (bit + i, bh->b_data)) | ||
| 328 | ecfs_error (sb, "ecfs_free_blocks", | ||
| 329 | "bit already cleared for block %lu", | ||
| 330 | block); | ||
| 331 | else { | ||
| 332 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) | ||
| 333 | // DQUOT_FREE_BLOCK(sb, inode, 1); | ||
| 334 | #else | ||
| 335 | // DQUOT_FREE_BLOCK(inode, 1); | ||
| 336 | #endif | ||
| 337 | gdp->bg_free_blocks_count = | ||
| 338 | cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1); | ||
| 339 | es->s_free_blocks_count = | ||
| 340 | cpu_to_le32(le32_to_cpu(es->s_free_blocks_count)+1); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | mark_buffer_dirty(bh2); | ||
| 345 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 346 | |||
| 347 | mark_buffer_dirty(bh); | ||
| 348 | if (sb->s_flags & MS_SYNCHRONOUS) { | ||
| 349 | ll_rw_block (WRITE, 1, &bh); | ||
| 350 | wait_on_buffer (bh); | ||
| 351 | } | ||
| 352 | if (overflow) { | ||
| 353 | block += count; | ||
| 354 | count = overflow; | ||
| 355 | goto do_more; | ||
| 356 | } | ||
| 357 | sb->s_dirt = 1; | ||
| 358 | error_return: | ||
| 359 | unlock_super (sb); | ||
| 360 | return; | ||
| 361 | } | ||
| 362 | |||
| 363 | /* | ||
| 364 | * ecfs_new_block uses a goal block to assist allocation. If the goal is | ||
| 365 | * free, or there is a free block within 32 blocks of the goal, that block | ||
| 366 | * is allocated. Otherwise a forward search is made for a free block; within | ||
| 367 | * each block group the search first looks for an entire free byte in the block | ||
| 368 | * bitmap, and then for any free bit if that fails. | ||
| 369 | */ | ||
| 370 | int ecfs_new_block (struct inode * inode, unsigned long goal, | ||
| 371 | u32 * prealloc_count, u32 * prealloc_block, int * err) | ||
| 372 | { | ||
| 373 | struct buffer_head * bh; | ||
| 374 | struct buffer_head * bh2; | ||
| 375 | char * p, * r; | ||
| 376 | int i, j, k, tmp; | ||
| 377 | int bitmap_nr; | ||
| 378 | struct super_block * sb; | ||
| 379 | struct ecfs_group_desc * gdp; | ||
| 380 | struct ecfs_super_block * es; | ||
| 381 | #ifdef ECFSFS_DEBUG | ||
| 382 | static int goal_hits = 0, goal_attempts = 0; | ||
| 383 | #endif | ||
| 384 | *err = -ENOSPC; | ||
| 385 | sb = inode->i_sb; | ||
| 386 | if (!sb) { | ||
| 387 | printk ("ecfs_new_block: nonexistent device"); | ||
| 388 | return 0; | ||
| 389 | } | ||
| 390 | |||
| 391 | lock_super (sb); | ||
| 392 | es = sb->u.ecfs_sb.s_es; | ||
| 393 | if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) && | ||
| 394 | ((sb->u.ecfs_sb.s_resuid != current->fsuid) && | ||
| 395 | (sb->u.ecfs_sb.s_resgid == 0 || | ||
| 396 | !in_group_p (sb->u.ecfs_sb.s_resgid)) && | ||
| 397 | !capable(CAP_SYS_RESOURCE))) | ||
| 398 | goto out; | ||
| 399 | |||
| 400 | ecfs_debug ("goal=%lu.\n", goal); | ||
| 401 | |||
| 402 | repeat: | ||
| 403 | /* | ||
| 404 | * First, test whether the goal block is free. | ||
| 405 | */ | ||
| 406 | if (goal < le32_to_cpu(es->s_first_data_block) || | ||
| 407 | goal >= le32_to_cpu(es->s_blocks_count)) | ||
| 408 | goal = le32_to_cpu(es->s_first_data_block); | ||
| 409 | i = (goal - le32_to_cpu(es->s_first_data_block)) / ECFS_BLOCKS_PER_GROUP(sb); | ||
| 410 | gdp = ecfs_get_group_desc (sb, i, &bh2); | ||
| 411 | if (!gdp) | ||
| 412 | goto io_error; | ||
| 413 | |||
| 414 | if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) { | ||
| 415 | j = ((goal - le32_to_cpu(es->s_first_data_block)) % ECFS_BLOCKS_PER_GROUP(sb)); | ||
| 416 | #ifdef ECFSFS_DEBUG | ||
| 417 | if (j) | ||
| 418 | goal_attempts++; | ||
| 419 | #endif | ||
| 420 | bitmap_nr = load_block_bitmap (sb, i); | ||
| 421 | if (bitmap_nr < 0) | ||
| 422 | goto io_error; | ||
| 423 | |||
| 424 | bh = sb->u.ecfs_sb.s_block_bitmap[bitmap_nr]; | ||
| 425 | |||
| 426 | ecfs_debug ("goal is at %d:%d.\n", i, j); | ||
| 427 | |||
| 428 | if (!ecfs_test_bit(j, bh->b_data)) { | ||
| 429 | #ifdef ECFSFS_DEBUG | ||
| 430 | goal_hits++; | ||
| 431 | ecfs_debug ("goal bit allocated.\n"); | ||
| 432 | #endif | ||
| 433 | goto got_block; | ||
| 434 | } | ||
| 435 | if (j) { | ||
| 436 | /* | ||
| 437 | * The goal was occupied; search forward for a free | ||
| 438 | * block within the next XX blocks. | ||
| 439 | * | ||
| 440 | * end_goal is more or less random, but it has to be | ||
| 441 | * less than ECFS_BLOCKS_PER_GROUP. Aligning up to the | ||
| 442 | * next 64-bit boundary is simple.. | ||
| 443 | */ | ||
| 444 | int end_goal = (j + 63) & ~63; | ||
| 445 | j = ecfs_find_next_zero_bit(bh->b_data, end_goal, j); | ||
| 446 | if (j < end_goal) | ||
| 447 | goto got_block; | ||
| 448 | } | ||
| 449 | |||
| 450 | ecfs_debug ("Bit not found near goal\n"); | ||
| 451 | |||
| 452 | /* | ||
| 453 | * There has been no free block found in the near vicinity | ||
| 454 | * of the goal: do a search forward through the block groups, | ||
| 455 | * searching in each group first for an entire free byte in | ||
| 456 | * the bitmap and then for any free bit. | ||
| 457 | * | ||
| 458 | * Search first in the remainder of the current group; then, | ||
| 459 | * cyclicly search through the rest of the groups. | ||
| 460 | */ | ||
| 461 | p = ((char *) bh->b_data) + (j >> 3); | ||
| 462 | r = memscan(p, 0, (ECFS_BLOCKS_PER_GROUP(sb) - j + 7) >> 3); | ||
| 463 | k = (r - ((char *) bh->b_data)) << 3; | ||
| 464 | if (k < ECFS_BLOCKS_PER_GROUP(sb)) { | ||
| 465 | j = k; | ||
| 466 | goto search_back; | ||
| 467 | } | ||
| 468 | |||
| 469 | k = ecfs_find_next_zero_bit ((unsigned long *) bh->b_data, | ||
| 470 | ECFS_BLOCKS_PER_GROUP(sb), | ||
| 471 | j); | ||
| 472 | if (k < ECFS_BLOCKS_PER_GROUP(sb)) { | ||
| 473 | j = k; | ||
| 474 | goto got_block; | ||
| 475 | } | ||
| 476 | } | ||
| 477 | |||
| 478 | ecfs_debug ("Bit not found in block group %d.\n", i); | ||
| 479 | |||
| 480 | /* | ||
| 481 | * Now search the rest of the groups. We assume that | ||
| 482 | * i and gdp correctly point to the last group visited. | ||
| 483 | */ | ||
| 484 | for (k = 0; k < sb->u.ecfs_sb.s_groups_count; k++) { | ||
| 485 | i++; | ||
| 486 | if (i >= sb->u.ecfs_sb.s_groups_count) | ||
| 487 | i = 0; | ||
| 488 | gdp = ecfs_get_group_desc (sb, i, &bh2); | ||
| 489 | if (!gdp) { | ||
| 490 | *err = -EIO; | ||
| 491 | goto out; | ||
| 492 | } | ||
| 493 | if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) | ||
| 494 | break; | ||
| 495 | } | ||
| 496 | if (k >= sb->u.ecfs_sb.s_groups_count) | ||
| 497 | goto out; | ||
| 498 | bitmap_nr = load_block_bitmap (sb, i); | ||
| 499 | if (bitmap_nr < 0) | ||
| 500 | goto io_error; | ||
| 501 | |||
| 502 | bh = sb->u.ecfs_sb.s_block_bitmap[bitmap_nr]; | ||
| 503 | r = memscan(bh->b_data, 0, ECFS_BLOCKS_PER_GROUP(sb) >> 3); | ||
| 504 | j = (r - bh->b_data) << 3; | ||
| 505 | if (j < ECFS_BLOCKS_PER_GROUP(sb)) | ||
| 506 | goto search_back; | ||
| 507 | else | ||
| 508 | j = ecfs_find_first_zero_bit ((unsigned long *) bh->b_data, | ||
| 509 | ECFS_BLOCKS_PER_GROUP(sb)); | ||
| 510 | if (j >= ECFS_BLOCKS_PER_GROUP(sb)) { | ||
| 511 | ecfs_error (sb, "ecfs_new_block", | ||
| 512 | "Free blocks count corrupted for block group %d", i); | ||
| 513 | goto out; | ||
| 514 | } | ||
| 515 | |||
| 516 | search_back: | ||
| 517 | /* | ||
| 518 | * We have succeeded in finding a free byte in the block | ||
| 519 | * bitmap. Now search backwards up to 7 bits to find the | ||
| 520 | * start of this group of free blocks. | ||
| 521 | */ | ||
| 522 | for (k = 0; k < 7 && j > 0 && !ecfs_test_bit (j - 1, bh->b_data); k++, j--); | ||
| 523 | |||
| 524 | got_block: | ||
| 525 | |||
| 526 | ecfs_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count); | ||
| 527 | |||
| 528 | tmp = j + i * ECFS_BLOCKS_PER_GROUP(sb) + le32_to_cpu(es->s_first_data_block); | ||
| 529 | |||
| 530 | if (tmp == le32_to_cpu(gdp->bg_block_bitmap) || | ||
| 531 | tmp == le32_to_cpu(gdp->bg_inode_bitmap) || | ||
| 532 | in_range (tmp, le32_to_cpu(gdp->bg_inode_table), | ||
| 533 | sb->u.ecfs_sb.s_itb_per_group)) | ||
| 534 | ecfs_error (sb, "ecfs_new_block", | ||
| 535 | "Allocating block in system zone - " | ||
| 536 | "block = %u", tmp); | ||
| 537 | |||
| 538 | if (ecfs_set_bit (j, bh->b_data)) { | ||
| 539 | ecfs_warning (sb, "ecfs_new_block", | ||
| 540 | "bit already set for block %d", j); | ||
| 541 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) | ||
| 542 | //DQUOT_FREE_BLOCK(sb, inode, 1); | ||
| 543 | #else | ||
| 544 | //DQUOT_FREE_BLOCK(inode,1); | ||
| 545 | #endif | ||
| 546 | goto repeat; | ||
| 547 | } | ||
| 548 | |||
| 549 | ecfs_debug ("found bit %d\n", j); | ||
| 550 | |||
| 551 | /* | ||
| 552 | * Do block preallocation now if required. | ||
| 553 | */ | ||
| 554 | #ifdef ECFS_PREALLOCATE | ||
| 555 | /* Writer: ->i_prealloc* */ | ||
| 556 | if (prealloc_count && !*prealloc_count) { | ||
| 557 | int prealloc_goal; | ||
| 558 | unsigned long next_block = tmp + 1; | ||
| 559 | |||
| 560 | prealloc_goal = es->s_prealloc_blocks ? | ||
| 561 | es->s_prealloc_blocks : ECFS_DEFAULT_PREALLOC_BLOCKS; | ||
| 562 | |||
| 563 | *prealloc_block = next_block; | ||
| 564 | /* Writer: end */ | ||
| 565 | for (k = 1; | ||
| 566 | k < prealloc_goal && (j + k) < ECFS_BLOCKS_PER_GROUP(sb); | ||
| 567 | k++, next_block++) { | ||
| 568 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) | ||
| 569 | //if (DQUOT_PREALLOC_BLOCK(sb, inode, 1)) | ||
| 570 | #else | ||
| 571 | //if (DQUOT_PREALLOC_BLOCK(inode,1)) | ||
| 572 | #endif | ||
| 573 | // break; | ||
| 574 | /* Writer: ->i_prealloc* */ | ||
| 575 | if (*prealloc_block + *prealloc_count != next_block || | ||
| 576 | ecfs_set_bit (j + k, bh->b_data)) {} | ||
| 577 | /* Writer: end | ||
| 578 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) | ||
| 579 | DQUOT_FREE_BLOCK(sb, inode, 1); | ||
| 580 | #else | ||
| 581 | DQUOT_FREE_BLOCK(inode,1); | ||
| 582 | #endif | ||
| 583 | break; | ||
| 584 | }*/ | ||
| 585 | (*prealloc_count)++; | ||
| 586 | /* Writer: end */ | ||
| 587 | } | ||
| 588 | /* | ||
| 589 | * As soon as we go for per-group spinlocks we'll need these | ||
| 590 | * done inside the loop above. | ||
| 591 | */ | ||
| 592 | gdp->bg_free_blocks_count = | ||
| 593 | cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - | ||
| 594 | (k - 1)); | ||
| 595 | es->s_free_blocks_count = | ||
| 596 | cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - | ||
| 597 | (k - 1)); | ||
| 598 | ecfs_debug ("Preallocated a further %lu bits.\n", | ||
| 599 | (k - 1)); | ||
| 600 | } | ||
| 601 | #endif | ||
| 602 | |||
| 603 | j = tmp; | ||
| 604 | |||
| 605 | mark_buffer_dirty(bh); | ||
| 606 | if (sb->s_flags & MS_SYNCHRONOUS) { | ||
| 607 | ll_rw_block (WRITE, 1, &bh); | ||
| 608 | wait_on_buffer (bh); | ||
| 609 | } | ||
| 610 | |||
| 611 | if (j >= le32_to_cpu(es->s_blocks_count)) { | ||
| 612 | ecfs_error (sb, "ecfs_new_block", | ||
| 613 | "block(%d) >= blocks count(%d) - " | ||
| 614 | "block_group = %d, es == %p ",j, | ||
| 615 | le32_to_cpu(es->s_blocks_count), i, es); | ||
| 616 | goto out; | ||
| 617 | } | ||
| 618 | |||
| 619 | ecfs_debug ("allocating block %d. " | ||
| 620 | "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); | ||
| 621 | |||
| 622 | gdp->bg_free_blocks_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1); | ||
| 623 | mark_buffer_dirty(bh2); | ||
| 624 | es->s_free_blocks_count = cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - 1); | ||
| 625 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 626 | sb->s_dirt = 1; | ||
| 627 | unlock_super (sb); | ||
| 628 | *err = 0; | ||
| 629 | return j; | ||
| 630 | |||
| 631 | io_error: | ||
| 632 | *err = -EIO; | ||
| 633 | out: | ||
| 634 | unlock_super (sb); | ||
| 635 | return 0; | ||
| 636 | |||
| 637 | } | ||
| 638 | |||
| 639 | unsigned long ecfs_count_free_blocks (struct super_block * sb) | ||
| 640 | { | ||
| 641 | #ifdef ECFSFS_DEBUG | ||
| 642 | struct ecfs_super_block * es; | ||
| 643 | unsigned long desc_count, bitmap_count, x; | ||
| 644 | int bitmap_nr; | ||
| 645 | struct ecfs_group_desc * gdp; | ||
| 646 | int i; | ||
| 647 | |||
| 648 | lock_super (sb); | ||
| 649 | es = sb->u.ecfs_sb.s_es; | ||
| 650 | desc_count = 0; | ||
| 651 | bitmap_count = 0; | ||
| 652 | gdp = NULL; | ||
| 653 | for (i = 0; i < sb->u.ecfs_sb.s_groups_count; i++) { | ||
| 654 | gdp = ecfs_get_group_desc (sb, i, NULL); | ||
| 655 | if (!gdp) | ||
| 656 | continue; | ||
| 657 | desc_count += le16_to_cpu(gdp->bg_free_blocks_count); | ||
| 658 | bitmap_nr = load_block_bitmap (sb, i); | ||
| 659 | if (bitmap_nr < 0) | ||
| 660 | continue; | ||
| 661 | |||
| 662 | x = ecfs_count_free (sb->u.ecfs_sb.s_block_bitmap[bitmap_nr], | ||
| 663 | sb->s_blocksize); | ||
| 664 | printk ("group %d: stored = %d, counted = %lu\n", | ||
| 665 | i, le16_to_cpu(gdp->bg_free_blocks_count), x); | ||
| 666 | bitmap_count += x; | ||
| 667 | } | ||
| 668 | printk("ecfs_count_free_blocks: stored = %lu, computed = %lu, %lu\n", | ||
| 669 | le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count); | ||
| 670 | unlock_super (sb); | ||
| 671 | return bitmap_count; | ||
| 672 | #else | ||
| 673 | return le32_to_cpu(sb->u.ecfs_sb.s_es->s_free_blocks_count); | ||
| 674 | #endif | ||
| 675 | } | ||
| 676 | |||
| 677 | static inline int block_in_use (unsigned long block, | ||
| 678 | struct super_block * sb, | ||
| 679 | unsigned char * map) | ||
| 680 | { | ||
| 681 | return ecfs_test_bit ((block - le32_to_cpu(sb->u.ecfs_sb.s_es->s_first_data_block)) % | ||
| 682 | ECFS_BLOCKS_PER_GROUP(sb), map); | ||
| 683 | } | ||
| 684 | |||
| 685 | static inline int test_root(int a, int b) | ||
| 686 | { | ||
| 687 | if (a == 0) | ||
| 688 | return 1; | ||
| 689 | while (1) { | ||
| 690 | if (a == 1) | ||
| 691 | return 1; | ||
| 692 | if (a % b) | ||
| 693 | return 0; | ||
| 694 | a = a / b; | ||
| 695 | } | ||
| 696 | } | ||
| 697 | |||
| 698 | int ecfs_group_sparse(int group) | ||
| 699 | { | ||
| 700 | return (test_root(group, 3) || test_root(group, 5) || | ||
| 701 | test_root(group, 7)); | ||
| 702 | } | ||
| 703 | |||
| 704 | /** | ||
| 705 | * ecfs_bg_has_super - number of blocks used by the superblock in group | ||
| 706 | * @sb: superblock for filesystem | ||
| 707 | * @group: group number to check | ||
| 708 | * | ||
| 709 | * Return the number of blocks used by the superblock (primary or backup) | ||
| 710 | * in this group. Currently this will be only 0 or 1. | ||
| 711 | */ | ||
| 712 | int ecfs_bg_has_super(struct super_block *sb, int group) | ||
| 713 | { | ||
| 714 | if (ECFS_HAS_RO_COMPAT_FEATURE(sb,ECFS_FEATURE_RO_COMPAT_SPARSE_SUPER)&& | ||
| 715 | !ecfs_group_sparse(group)) | ||
| 716 | return 0; | ||
| 717 | return 1; | ||
| 718 | } | ||
| 719 | |||
| 720 | /** | ||
| 721 | * ecfs_bg_num_gdb - number of blocks used by the group table in group | ||
| 722 | * @sb: superblock for filesystem | ||
| 723 | * @group: group number to check | ||
| 724 | * | ||
| 725 | * Return the number of blocks used by the group descriptor table | ||
| 726 | * (primary or backup) in this group. In the future there may be a | ||
| 727 | * different number of descriptor blocks in each group. | ||
| 728 | */ | ||
| 729 | unsigned long ecfs_bg_num_gdb(struct super_block *sb, int group) | ||
| 730 | { | ||
| 731 | if (ECFS_HAS_RO_COMPAT_FEATURE(sb,ECFS_FEATURE_RO_COMPAT_SPARSE_SUPER)&& | ||
| 732 | !ecfs_group_sparse(group)) | ||
| 733 | return 0; | ||
| 734 | return ECFS_SB(sb)->s_gdb_count; | ||
| 735 | } | ||
| 736 | |||
| 737 | #ifdef CONFIG_ECFS_CHECK | ||
| 738 | /* Called at mount-time, super-block is locked */ | ||
| 739 | void ecfs_check_blocks_bitmap (struct super_block * sb) | ||
| 740 | { | ||
| 741 | struct buffer_head * bh; | ||
| 742 | struct ecfs_super_block * es; | ||
| 743 | unsigned long desc_count, bitmap_count, x, j; | ||
| 744 | unsigned long desc_blocks; | ||
| 745 | int bitmap_nr; | ||
| 746 | struct ecfs_group_desc * gdp; | ||
| 747 | int i; | ||
| 748 | |||
| 749 | es = sb->u.ecfs_sb.s_es; | ||
| 750 | desc_count = 0; | ||
| 751 | bitmap_count = 0; | ||
| 752 | gdp = NULL; | ||
| 753 | for (i = 0; i < sb->u.ecfs_sb.s_groups_count; i++) { | ||
| 754 | gdp = ecfs_get_group_desc (sb, i, NULL); | ||
| 755 | if (!gdp) | ||
| 756 | continue; | ||
| 757 | desc_count += le16_to_cpu(gdp->bg_free_blocks_count); | ||
| 758 | bitmap_nr = load_block_bitmap (sb, i); | ||
| 759 | if (bitmap_nr < 0) | ||
| 760 | continue; | ||
| 761 | |||
| 762 | bh = ECFS_SB(sb)->s_block_bitmap[bitmap_nr]; | ||
| 763 | |||
| 764 | if (ecfs_bg_has_super(sb, i) && !ecfs_test_bit(0, bh->b_data)) | ||
| 765 | ecfs_error(sb, __FUNCTION__, | ||
| 766 | "Superblock in group %d is marked free", i); | ||
| 767 | |||
| 768 | desc_blocks = ecfs_bg_num_gdb(sb, i); | ||
| 769 | for (j = 0; j < desc_blocks; j++) | ||
| 770 | if (!ecfs_test_bit(j + 1, bh->b_data)) | ||
| 771 | ecfs_error(sb, __FUNCTION__, | ||
| 772 | "Descriptor block #%ld in group " | ||
| 773 | "%d is marked free", j, i); | ||
| 774 | |||
| 775 | if (!block_in_use (le32_to_cpu(gdp->bg_block_bitmap), sb, bh->b_data)) | ||
| 776 | ecfs_error (sb, "ecfs_check_blocks_bitmap", | ||
| 777 | "Block bitmap for group %d is marked free", | ||
| 778 | i); | ||
| 779 | |||
| 780 | if (!block_in_use (le32_to_cpu(gdp->bg_inode_bitmap), sb, bh->b_data)) | ||
| 781 | ecfs_error (sb, "ecfs_check_blocks_bitmap", | ||
| 782 | "Inode bitmap for group %d is marked free", | ||
| 783 | i); | ||
| 784 | |||
| 785 | for (j = 0; j < sb->u.ecfs_sb.s_itb_per_group; j++) | ||
| 786 | if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j, sb, bh->b_data)) | ||
| 787 | ecfs_error (sb, "ecfs_check_blocks_bitmap", | ||
| 788 | "Block #%d of the inode table in " | ||
| 789 | "group %d is marked free", j, i); | ||
| 790 | |||
| 791 | x = ecfs_count_free (bh, sb->s_blocksize); | ||
| 792 | if (le16_to_cpu(gdp->bg_free_blocks_count) != x) | ||
| 793 | ecfs_error (sb, "ecfs_check_blocks_bitmap", | ||
| 794 | "Wrong free blocks count for group %d, " | ||
| 795 | "stored = %d, counted = %lu", i, | ||
| 796 | le16_to_cpu(gdp->bg_free_blocks_count), x); | ||
| 797 | bitmap_count += x; | ||
| 798 | } | ||
| 799 | if (le32_to_cpu(es->s_free_blocks_count) != bitmap_count) | ||
| 800 | ecfs_error (sb, "ecfs_check_blocks_bitmap", | ||
| 801 | "Wrong free blocks count in super block, " | ||
| 802 | "stored = %lu, counted = %lu", | ||
| 803 | (unsigned long) le32_to_cpu(es->s_free_blocks_count), bitmap_count); | ||
| 804 | } | ||
| 805 | #endif | ||
diff --git a/other/ecfs/balloc.o b/other/ecfs/balloc.o new file mode 100644 index 0000000..7e78cd9 --- /dev/null +++ b/other/ecfs/balloc.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/bitmap.c b/other/ecfs/bitmap.c new file mode 100644 index 0000000..cb103c8 --- /dev/null +++ b/other/ecfs/bitmap.c | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/bitmap.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/fs_ecfs.h> | ||
| 11 | #include <linux/ecfs_fs.h> | ||
| 12 | |||
| 13 | |||
| 14 | static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; | ||
| 15 | |||
| 16 | unsigned long ecfs_count_free (struct buffer_head * map, unsigned int numchars) | ||
| 17 | { | ||
| 18 | unsigned int i; | ||
| 19 | unsigned long sum = 0; | ||
| 20 | |||
| 21 | if (!map) | ||
| 22 | return (0); | ||
| 23 | for (i = 0; i < numchars; i++) | ||
| 24 | sum += nibblemap[map->b_data[i] & 0xf] + | ||
| 25 | nibblemap[(map->b_data[i] >> 4) & 0xf]; | ||
| 26 | return (sum); | ||
| 27 | } | ||
diff --git a/other/ecfs/bitmap.o b/other/ecfs/bitmap.o new file mode 100644 index 0000000..936e465 --- /dev/null +++ b/other/ecfs/bitmap.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/dir.c b/other/ecfs/dir.c new file mode 100644 index 0000000..0b42038 --- /dev/null +++ b/other/ecfs/dir.c | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/dir.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | * | ||
| 9 | * from | ||
| 10 | * | ||
| 11 | * linux/fs/minix/dir.c | ||
| 12 | * | ||
| 13 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
| 14 | * | ||
| 15 | * ecfs directory handling functions | ||
| 16 | * | ||
| 17 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
| 18 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/fs_ecfs.h> | ||
| 22 | #include <linux/ecfs_fs.h> | ||
| 23 | |||
| 24 | static unsigned char ecfs_filetype_table[] = { | ||
| 25 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK | ||
| 26 | }; | ||
| 27 | |||
| 28 | static int ecfs_readdir(struct file *, void *, filldir_t); | ||
| 29 | |||
| 30 | struct file_operations ecfs_dir_operations = { | ||
| 31 | read: generic_read_dir, | ||
| 32 | readdir: ecfs_readdir, | ||
| 33 | ioctl: ecfs_ioctl, | ||
| 34 | fsync: ecfs_sync_file, | ||
| 35 | }; | ||
| 36 | |||
| 37 | int ecfs_check_dir_entry (const char * function, struct inode * dir, | ||
| 38 | struct ecfs_dir_entry_2 * de, | ||
| 39 | struct buffer_head * bh, | ||
| 40 | unsigned long offset) | ||
| 41 | { | ||
| 42 | const char * error_msg = NULL; | ||
| 43 | |||
| 44 | if (le16_to_cpu(de->rec_len) < ECFS_DIR_REC_LEN(1)) | ||
| 45 | error_msg = "rec_len is smaller than minimal"; | ||
| 46 | else if (le16_to_cpu(de->rec_len) % 4 != 0) | ||
| 47 | error_msg = "rec_len % 4 != 0"; | ||
| 48 | else if (le16_to_cpu(de->rec_len) < ECFS_DIR_REC_LEN(de->name_len)) | ||
| 49 | error_msg = "rec_len is too small for name_len"; | ||
| 50 | else if (dir && ((char *) de - bh->b_data) + le16_to_cpu(de->rec_len) > | ||
| 51 | dir->i_sb->s_blocksize) | ||
| 52 | error_msg = "directory entry across blocks"; | ||
| 53 | else if (dir && le32_to_cpu(de->inode) > le32_to_cpu(dir->i_sb->u.ecfs_sb.s_es->s_inodes_count)) | ||
| 54 | error_msg = "inode out of bounds"; | ||
| 55 | |||
| 56 | if (error_msg != NULL) | ||
| 57 | ecfs_error (dir->i_sb, function, "bad entry in directory #%lu: %s - " | ||
| 58 | "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", | ||
| 59 | dir->i_ino, error_msg, offset, | ||
| 60 | (unsigned long) le32_to_cpu(de->inode), | ||
| 61 | le16_to_cpu(de->rec_len), de->name_len); | ||
| 62 | return error_msg == NULL ? 1 : 0; | ||
| 63 | } | ||
| 64 | |||
| 65 | static int ecfs_readdir(struct file * filp, | ||
| 66 | void * dirent, filldir_t filldir) | ||
| 67 | { | ||
| 68 | int error = 0; | ||
| 69 | unsigned long offset, blk; | ||
| 70 | int i, num, stored; | ||
| 71 | struct buffer_head * bh, * tmp, * bha[16]; | ||
| 72 | struct ecfs_dir_entry_2 * de; | ||
| 73 | struct super_block * sb; | ||
| 74 | int err; | ||
| 75 | struct inode *inode = filp->f_dentry->d_inode; | ||
| 76 | |||
| 77 | sb = inode->i_sb; | ||
| 78 | |||
| 79 | stored = 0; | ||
| 80 | bh = NULL; | ||
| 81 | offset = filp->f_pos & (sb->s_blocksize - 1); | ||
| 82 | |||
| 83 | while (!error && !stored && filp->f_pos < inode->i_size) { | ||
| 84 | blk = (filp->f_pos) >> ECFS_BLOCK_SIZE_BITS(sb); | ||
| 85 | bh = ecfs_bread (inode, blk, 0, &err); | ||
| 86 | if (!bh) { | ||
| 87 | ecfs_error (sb, "ecfs_readdir", | ||
| 88 | "directory #%lu contains a hole at offset %lu", | ||
| 89 | inode->i_ino, (unsigned long)filp->f_pos); | ||
| 90 | filp->f_pos += sb->s_blocksize - offset; | ||
| 91 | continue; | ||
| 92 | } | ||
| 93 | |||
| 94 | /* | ||
| 95 | * Do the readahead | ||
| 96 | */ | ||
| 97 | if (!offset) { | ||
| 98 | for (i = 16 >> (ECFS_BLOCK_SIZE_BITS(sb) - 9), num = 0; | ||
| 99 | i > 0; i--) { | ||
| 100 | tmp = ecfs_getblk (inode, ++blk, 0, &err); | ||
| 101 | if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) | ||
| 102 | bha[num++] = tmp; | ||
| 103 | else | ||
| 104 | brelse (tmp); | ||
| 105 | } | ||
| 106 | if (num) { | ||
| 107 | ll_rw_block (READA, num, bha); | ||
| 108 | for (i = 0; i < num; i++) | ||
| 109 | brelse (bha[i]); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | revalidate: | ||
| 114 | /* If the dir block has changed since the last call to | ||
| 115 | * readdir(2), then we might be pointing to an invalid | ||
| 116 | * dirent right now. Scan from the start of the block | ||
| 117 | * to make sure. */ | ||
| 118 | if (filp->f_version != inode->i_version) { | ||
| 119 | for (i = 0; i < sb->s_blocksize && i < offset; ) { | ||
| 120 | de = (struct ecfs_dir_entry_2 *) | ||
| 121 | (bh->b_data + i); | ||
| 122 | /* It's too expensive to do a full | ||
| 123 | * dirent test each time round this | ||
| 124 | * loop, but we do have to test at | ||
| 125 | * least that it is non-zero. A | ||
| 126 | * failure will be detected in the | ||
| 127 | * dirent test below. */ | ||
| 128 | if (le16_to_cpu(de->rec_len) < ECFS_DIR_REC_LEN(1)) | ||
| 129 | break; | ||
| 130 | i += le16_to_cpu(de->rec_len); | ||
| 131 | } | ||
| 132 | offset = i; | ||
| 133 | filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | ||
| 134 | | offset; | ||
| 135 | filp->f_version = inode->i_version; | ||
| 136 | } | ||
| 137 | |||
| 138 | while (!error && filp->f_pos < inode->i_size | ||
| 139 | && offset < sb->s_blocksize) { | ||
| 140 | de = (struct ecfs_dir_entry_2 *) (bh->b_data + offset); | ||
| 141 | if (!ecfs_check_dir_entry ("ecfs_readdir", inode, de, | ||
| 142 | bh, offset)) { | ||
| 143 | /* On error, skip the f_pos to the | ||
| 144 | next block. */ | ||
| 145 | filp->f_pos = (filp->f_pos | (sb->s_blocksize - 1)) | ||
| 146 | + 1; | ||
| 147 | brelse (bh); | ||
| 148 | return stored; | ||
| 149 | } | ||
| 150 | offset += le16_to_cpu(de->rec_len); | ||
| 151 | if (le32_to_cpu(de->inode)) { | ||
| 152 | /* We might block in the next section | ||
| 153 | * if the data destination is | ||
| 154 | * currently swapped out. So, use a | ||
| 155 | * version stamp to detect whether or | ||
| 156 | * not the directory has been modified | ||
| 157 | * during the copy operation. | ||
| 158 | */ | ||
| 159 | unsigned long version = filp->f_version; | ||
| 160 | unsigned char d_type = DT_UNKNOWN; | ||
| 161 | |||
| 162 | if (ECFS_HAS_INCOMPAT_FEATURE(sb, ECFS_FEATURE_INCOMPAT_FILETYPE) | ||
| 163 | && de->file_type < ECFS_FT_MAX) | ||
| 164 | d_type = ecfs_filetype_table[de->file_type]; | ||
| 165 | error = filldir(dirent, de->name, | ||
| 166 | de->name_len, | ||
| 167 | filp->f_pos, le32_to_cpu(de->inode), | ||
| 168 | d_type); | ||
| 169 | if (error) | ||
| 170 | break; | ||
| 171 | if (version != filp->f_version) | ||
| 172 | goto revalidate; | ||
| 173 | stored ++; | ||
| 174 | } | ||
| 175 | filp->f_pos += le16_to_cpu(de->rec_len); | ||
| 176 | } | ||
| 177 | offset = 0; | ||
| 178 | brelse (bh); | ||
| 179 | } | ||
| 180 | UPDATE_ATIME(inode); | ||
| 181 | return 0; | ||
| 182 | } | ||
diff --git a/other/ecfs/dir.o b/other/ecfs/dir.o new file mode 100644 index 0000000..5d5d416 --- /dev/null +++ b/other/ecfs/dir.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/ecfs.o b/other/ecfs/ecfs.o new file mode 100644 index 0000000..9e68b85 --- /dev/null +++ b/other/ecfs/ecfs.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/file.c b/other/ecfs/file.c new file mode 100644 index 0000000..15bc0e2 --- /dev/null +++ b/other/ecfs/file.c | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/file.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | * | ||
| 9 | * from | ||
| 10 | * | ||
| 11 | * linux/fs/minix/file.c | ||
| 12 | * | ||
| 13 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
| 14 | * | ||
| 15 | * ecfs fs regular file handling primitives | ||
| 16 | * | ||
| 17 | * 64-bit file support on 64-bit platforms by Jakub Jelinek | ||
| 18 | * (jj@sunsite.ms.mff.cuni.cz) | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/fs_ecfs.h> | ||
| 22 | #include <linux/ecfs_fs.h> | ||
| 23 | #include "rc4.h" | ||
| 24 | #include <linux/sched.h> | ||
| 25 | #include <linux/mm.h> | ||
| 26 | #include <asm/uaccess.h> | ||
| 27 | #include <linux/malloc.h> | ||
| 28 | |||
| 29 | static loff_t ecfs_file_lseek(struct file *, loff_t, int); | ||
| 30 | static int ecfs_open_file (struct inode *, struct file *); | ||
| 31 | |||
| 32 | #define ECFS_MAX_SIZE(bits) \ | ||
| 33 | (((ECFS_NDIR_BLOCKS + (1LL << (bits - 2)) + \ | ||
| 34 | (1LL << (bits - 2)) * (1LL << (bits - 2)) + \ | ||
| 35 | (1LL << (bits - 2)) * (1LL << (bits - 2)) * (1LL << (bits - 2))) * \ | ||
| 36 | (1LL << bits)) - 1) | ||
| 37 | |||
| 38 | static long long ecfs_max_sizes[] = { | ||
| 39 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 40 | ECFS_MAX_SIZE(10), ECFS_MAX_SIZE(11), ECFS_MAX_SIZE(12), ECFS_MAX_SIZE(13) | ||
| 41 | }; | ||
| 42 | |||
| 43 | /* | ||
| 44 | * Make sure the offset never goes beyond the 32-bit mark.. | ||
| 45 | */ | ||
| 46 | static loff_t ecfs_file_lseek( | ||
| 47 | struct file *file, | ||
| 48 | loff_t offset, | ||
| 49 | int origin) | ||
| 50 | { | ||
| 51 | struct inode *inode = file->f_dentry->d_inode; | ||
| 52 | |||
| 53 | switch (origin) { | ||
| 54 | case 2: | ||
| 55 | offset += inode->i_size; | ||
| 56 | break; | ||
| 57 | case 1: | ||
| 58 | offset += file->f_pos; | ||
| 59 | } | ||
| 60 | if (offset<0) | ||
| 61 | return -EINVAL; | ||
| 62 | if (((unsigned long long) offset >> 32) != 0) { | ||
| 63 | if (offset > ecfs_max_sizes[ECFS_BLOCK_SIZE_BITS(inode->i_sb)]) | ||
| 64 | return -EINVAL; | ||
| 65 | } | ||
| 66 | if (offset != file->f_pos) { | ||
| 67 | file->f_pos = offset; | ||
| 68 | file->f_reada = 0; | ||
| 69 | file->f_version = ++event; | ||
| 70 | } | ||
| 71 | return offset; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* | ||
| 75 | * Called when an inode is released. Note that this is different | ||
| 76 | * from ecfs_file_open: open gets called at every open, but release | ||
| 77 | * gets called only when /all/ the files are closed. | ||
| 78 | */ | ||
| 79 | static int ecfs_release_file (struct inode * inode, struct file * filp) | ||
| 80 | { | ||
| 81 | if (filp->f_mode & FMODE_WRITE) | ||
| 82 | ecfs_discard_prealloc (inode); | ||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* | ||
| 87 | * Called when an inode is about to be open. | ||
| 88 | * We use this to disallow opening RW large files on 32bit systems if | ||
| 89 | * the caller didn't specify O_LARGEFILE. On 64bit systems we force | ||
| 90 | * on this flag in sys_open. | ||
| 91 | */ | ||
| 92 | static int ecfs_open_file (struct inode * inode, struct file * filp) | ||
| 93 | { | ||
| 94 | if (!(filp->f_flags & O_LARGEFILE) && | ||
| 95 | inode->i_size > 0x7FFFFFFFLL) | ||
| 96 | return -EFBIG; | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | |||
| 101 | unsigned char *ecfs_key = "A"; | ||
| 102 | |||
| 103 | /* page flags 21 .. 29 are unused | ||
| 104 | #define PAGE_PLAIN 22 | ||
| 105 | |||
| 106 | struct page * ecfs_filemap_nopage(struct vm_area_struct *area, | ||
| 107 | unsigned long address, int no_share) | ||
| 108 | { | ||
| 109 | unsigned int from = area->vm_file->f_pos; | ||
| 110 | unsigned char *ptr; | ||
| 111 | unsigned int count = area->vm_end - area->vm_start; | ||
| 112 | struct page *r; | ||
| 113 | rc4_key rc4key; | ||
| 114 | |||
| 115 | if (!orig_ops) | ||
| 116 | return NULL; | ||
| 117 | |||
| 118 | printk("<1> foo\n"); | ||
| 119 | r = orig_ops->nopage(area, address, no_share); | ||
| 120 | |||
| 121 | printk("<1> bar %p %d %d\n", r->virtual, from, r->count); | ||
| 122 | |||
| 123 | ptr = r->virtual; | ||
| 124 | |||
| 125 | if (!ptr)// || test_bit(PAGE_PLAIN, &r->flags)) | ||
| 126 | return r; | ||
| 127 | |||
| 128 | prepare_key(ecfs_key, strlen(ecfs_key), &rc4key); | ||
| 129 | rc4(ptr, PAGE_SIZE, &rc4key, from); | ||
| 130 | |||
| 131 | SetPageReserved(r); | ||
| 132 | return r; | ||
| 133 | } | ||
| 134 | |||
| 135 | |||
| 136 | static struct vm_operations_struct ecfs_nopage = { | ||
| 137 | nopage: ecfs_filemap_nopage, | ||
| 138 | }; | ||
| 139 | |||
| 140 | |||
| 141 | |||
| 142 | int ecfs_file_mmap(struct file *file, struct vm_area_struct *vma) | ||
| 143 | { | ||
| 144 | int r = generic_file_mmap(file, vma); | ||
| 145 | orig_ops = vma->vm_ops; | ||
| 146 | vma->vm_ops = &ecfs_nopage; | ||
| 147 | return r; | ||
| 148 | } | ||
| 149 | |||
| 150 | |||
| 151 | */ | ||
| 152 | ssize_t ecfs_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) | ||
| 153 | { | ||
| 154 | rc4_key rc4key; | ||
| 155 | unsigned int from = file->f_pos; | ||
| 156 | unsigned char *ptr; | ||
| 157 | ssize_t r; | ||
| 158 | char file_key[1024+100]; /* to avoid keystream-reuse */ | ||
| 159 | |||
| 160 | memset(file_key, 0, sizeof(file_key)); | ||
| 161 | |||
| 162 | /* Should not overflow as init() checked for > 1024 */ | ||
| 163 | sprintf(file_key, "%s%lx", ecfs_key, file->f_dentry->d_inode->i_ino); | ||
| 164 | |||
| 165 | r = generic_file_read(file, buf, count, ppos); | ||
| 166 | if (r < 0) | ||
| 167 | return r; | ||
| 168 | |||
| 169 | if ((ptr = kmalloc(count, GFP_KERNEL)) == NULL) | ||
| 170 | return r; | ||
| 171 | |||
| 172 | copy_from_user(ptr, buf, r); | ||
| 173 | |||
| 174 | prepare_key(file_key, strlen(file_key), &rc4key); | ||
| 175 | rc4(ptr, r, &rc4key, from); | ||
| 176 | |||
| 177 | copy_to_user(buf, ptr, r); | ||
| 178 | kfree(ptr); | ||
| 179 | return r; | ||
| 180 | } | ||
| 181 | |||
| 182 | |||
| 183 | ssize_t | ||
| 184 | ecfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) | ||
| 185 | { | ||
| 186 | rc4_key rc4key; | ||
| 187 | unsigned int from = file->f_pos; | ||
| 188 | unsigned char *ptr; | ||
| 189 | mm_segment_t orig_fs; | ||
| 190 | ssize_t r; | ||
| 191 | char file_key[1024+100]; /* to avoid keystream-reuse */ | ||
| 192 | |||
| 193 | memset(file_key, 0, sizeof(file_key)); | ||
| 194 | |||
| 195 | /* Should not overflow as init() checked for > 1024 */ | ||
| 196 | sprintf(file_key, "%s%lx", ecfs_key, file->f_dentry->d_inode->i_ino); | ||
| 197 | |||
| 198 | |||
| 199 | if ((ptr = kmalloc(count, GFP_KERNEL)) == NULL) | ||
| 200 | return -1; | ||
| 201 | |||
| 202 | copy_from_user(ptr, buf, count); | ||
| 203 | |||
| 204 | prepare_key(file_key, strlen(file_key), &rc4key); | ||
| 205 | rc4(ptr, count, &rc4key, from); | ||
| 206 | |||
| 207 | orig_fs = get_fs(); | ||
| 208 | set_fs(KERNEL_DS); | ||
| 209 | r = generic_file_write(file, ptr, count, ppos); | ||
| 210 | set_fs(orig_fs); | ||
| 211 | kfree(ptr); | ||
| 212 | return r; | ||
| 213 | } | ||
| 214 | |||
| 215 | |||
| 216 | /* | ||
| 217 | * We have mostly NULL's here: the current defaults are ok for | ||
| 218 | * the ecfs filesystem. | ||
| 219 | */ | ||
| 220 | struct file_operations ecfs_file_operations = { | ||
| 221 | llseek: ecfs_file_lseek, | ||
| 222 | read: ecfs_file_read, | ||
| 223 | write: ecfs_file_write, | ||
| 224 | ioctl: ecfs_ioctl, | ||
| 225 | mmap: generic_file_mmap, | ||
| 226 | open: ecfs_open_file, | ||
| 227 | release: ecfs_release_file, | ||
| 228 | fsync: ecfs_sync_file, | ||
| 229 | }; | ||
| 230 | |||
| 231 | struct inode_operations ecfs_file_inode_operations = { | ||
| 232 | truncate: ecfs_truncate, | ||
| 233 | }; | ||
diff --git a/other/ecfs/file.o b/other/ecfs/file.o new file mode 100644 index 0000000..63ca536 --- /dev/null +++ b/other/ecfs/file.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/fsync.c b/other/ecfs/fsync.c new file mode 100644 index 0000000..4079de1 --- /dev/null +++ b/other/ecfs/fsync.c | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/fsync.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk) | ||
| 5 | * from | ||
| 6 | * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) | ||
| 7 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 8 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 9 | * from | ||
| 10 | * linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds | ||
| 11 | * | ||
| 12 | * ecfsfs fsync primitive | ||
| 13 | * | ||
| 14 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
| 15 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
| 16 | * | ||
| 17 | * Removed unnecessary code duplication for little endian machines | ||
| 18 | * and excessive __inline__s. | ||
| 19 | * Andi Kleen, 1997 | ||
| 20 | * | ||
| 21 | * Major simplications and cleanup - we only need to do the metadata, because | ||
| 22 | * we can depend on generic_block_fdatasync() to sync the data blocks. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include <linux/fs_ecfs.h> | ||
| 26 | #include <linux/ecfs_fs.h> | ||
| 27 | #include <linux/locks.h> | ||
| 28 | #include <linux/smp_lock.h> | ||
| 29 | |||
| 30 | |||
| 31 | /* | ||
| 32 | * File may be NULL when we are called. Perhaps we shouldn't | ||
| 33 | * even pass file to fsync ? | ||
| 34 | */ | ||
| 35 | |||
| 36 | int ecfs_sync_file(struct file * file, struct dentry *dentry, int datasync) | ||
| 37 | { | ||
| 38 | struct inode *inode = dentry->d_inode; | ||
| 39 | return ecfs_fsync_inode(inode, datasync); | ||
| 40 | } | ||
| 41 | |||
| 42 | int ecfs_fsync_inode(struct inode *inode, int datasync) | ||
| 43 | { | ||
| 44 | int err; | ||
| 45 | |||
| 46 | err = fsync_inode_buffers(inode); | ||
| 47 | if (!(inode->i_state & I_DIRTY)) | ||
| 48 | return err; | ||
| 49 | if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) | ||
| 50 | return err; | ||
| 51 | |||
| 52 | err |= ecfs_sync_inode(inode); | ||
| 53 | return err ? -EIO : 0; | ||
| 54 | } | ||
diff --git a/other/ecfs/fsync.o b/other/ecfs/fsync.o new file mode 100644 index 0000000..4d0a339 --- /dev/null +++ b/other/ecfs/fsync.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/ialloc.c b/other/ecfs/ialloc.c new file mode 100644 index 0000000..9649fba --- /dev/null +++ b/other/ecfs/ialloc.c | |||
| @@ -0,0 +1,550 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/ialloc.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | * | ||
| 9 | * BSD ufs-inspired inode and directory allocation by | ||
| 10 | * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 | ||
| 11 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
| 12 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/config.h> | ||
| 16 | #include <linux/fs_ecfs.h> | ||
| 17 | #include <linux/ecfs_fs.h> | ||
| 18 | #include <linux/locks.h> | ||
| 19 | //#include <linux/quotaops.h> | ||
| 20 | |||
| 21 | #define ecfs_find_first_zero_bit find_first_zero_bit | ||
| 22 | #define ecfs_clear_bit __test_and_clear_bit | ||
| 23 | #define ecfs_set_bit __test_and_set_bit | ||
| 24 | #define ecfs_find_next_zero_bit find_next_zero_bit | ||
| 25 | #define ecfs_test_bit test_bit | ||
| 26 | |||
| 27 | /* | ||
| 28 | * ialloc.c contains the inodes allocation and deallocation routines | ||
| 29 | */ | ||
| 30 | |||
| 31 | /* | ||
| 32 | * The free inodes are managed by bitmaps. A file system contains several | ||
| 33 | * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap | ||
| 34 | * block for inodes, N blocks for the inode table and data blocks. | ||
| 35 | * | ||
| 36 | * The file system contains group descriptors which are located after the | ||
| 37 | * super block. Each descriptor contains the number of the bitmap block and | ||
| 38 | * the free blocks count in the block. The descriptors are loaded in memory | ||
| 39 | * when a file system is mounted (see ecfs_read_super). | ||
| 40 | */ | ||
| 41 | |||
| 42 | |||
| 43 | /* | ||
| 44 | * Read the inode allocation bitmap for a given block_group, reading | ||
| 45 | * into the specified slot in the superblock's bitmap cache. | ||
| 46 | * | ||
| 47 | * Return >=0 on success or a -ve error code. | ||
| 48 | */ | ||
| 49 | static int read_inode_bitmap (struct super_block * sb, | ||
| 50 | unsigned long block_group, | ||
| 51 | unsigned int bitmap_nr) | ||
| 52 | { | ||
| 53 | struct ecfs_group_desc * gdp; | ||
| 54 | struct buffer_head * bh = NULL; | ||
| 55 | int retval = 0; | ||
| 56 | |||
| 57 | gdp = ecfs_get_group_desc (sb, block_group, NULL); | ||
| 58 | if (!gdp) { | ||
| 59 | retval = -EIO; | ||
| 60 | goto error_out; | ||
| 61 | } | ||
| 62 | bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_inode_bitmap), sb->s_blocksize); | ||
| 63 | if (!bh) { | ||
| 64 | ecfs_error (sb, "read_inode_bitmap", | ||
| 65 | "Cannot read inode bitmap - " | ||
| 66 | "block_group = %lu, inode_bitmap = %lu", | ||
| 67 | block_group, (unsigned long) gdp->bg_inode_bitmap); | ||
| 68 | retval = -EIO; | ||
| 69 | } | ||
| 70 | /* | ||
| 71 | * On IO error, just leave a zero in the superblock's block pointer for | ||
| 72 | * this group. The IO will be retried next time. | ||
| 73 | */ | ||
| 74 | error_out: | ||
| 75 | sb->u.ecfs_sb.s_inode_bitmap_number[bitmap_nr] = block_group; | ||
| 76 | sb->u.ecfs_sb.s_inode_bitmap[bitmap_nr] = bh; | ||
| 77 | return retval; | ||
| 78 | } | ||
| 79 | |||
| 80 | /* | ||
| 81 | * load_inode_bitmap loads the inode bitmap for a blocks group | ||
| 82 | * | ||
| 83 | * It maintains a cache for the last bitmaps loaded. This cache is managed | ||
| 84 | * with a LRU algorithm. | ||
| 85 | * | ||
| 86 | * Notes: | ||
| 87 | * 1/ There is one cache per mounted file system. | ||
| 88 | * 2/ If the file system contains less than ECFS_MAX_GROUP_LOADED groups, | ||
| 89 | * this function reads the bitmap without maintaining a LRU cache. | ||
| 90 | * | ||
| 91 | * Return the slot used to store the bitmap, or a -ve error code. | ||
| 92 | */ | ||
| 93 | static int load_inode_bitmap (struct super_block * sb, | ||
| 94 | unsigned int block_group) | ||
| 95 | { | ||
| 96 | int i, j, retval = 0; | ||
| 97 | unsigned long inode_bitmap_number; | ||
| 98 | struct buffer_head * inode_bitmap; | ||
| 99 | |||
| 100 | if (block_group >= sb->u.ecfs_sb.s_groups_count) | ||
| 101 | ecfs_panic (sb, "load_inode_bitmap", | ||
| 102 | "block_group >= groups_count - " | ||
| 103 | "block_group = %d, groups_count = %lu", | ||
| 104 | block_group, sb->u.ecfs_sb.s_groups_count); | ||
| 105 | if (sb->u.ecfs_sb.s_loaded_inode_bitmaps > 0 && | ||
| 106 | sb->u.ecfs_sb.s_inode_bitmap_number[0] == block_group && | ||
| 107 | sb->u.ecfs_sb.s_inode_bitmap[0] != NULL) | ||
| 108 | return 0; | ||
| 109 | if (sb->u.ecfs_sb.s_groups_count <= ECFS_MAX_GROUP_LOADED) { | ||
| 110 | if (sb->u.ecfs_sb.s_inode_bitmap[block_group]) { | ||
| 111 | if (sb->u.ecfs_sb.s_inode_bitmap_number[block_group] != block_group) | ||
| 112 | ecfs_panic (sb, "load_inode_bitmap", | ||
| 113 | "block_group != inode_bitmap_number"); | ||
| 114 | else | ||
| 115 | return block_group; | ||
| 116 | } else { | ||
| 117 | retval = read_inode_bitmap (sb, block_group, | ||
| 118 | block_group); | ||
| 119 | if (retval < 0) | ||
| 120 | return retval; | ||
| 121 | return block_group; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | for (i = 0; i < sb->u.ecfs_sb.s_loaded_inode_bitmaps && | ||
| 126 | sb->u.ecfs_sb.s_inode_bitmap_number[i] != block_group; | ||
| 127 | i++) | ||
| 128 | ; | ||
| 129 | if (i < sb->u.ecfs_sb.s_loaded_inode_bitmaps && | ||
| 130 | sb->u.ecfs_sb.s_inode_bitmap_number[i] == block_group) { | ||
| 131 | inode_bitmap_number = sb->u.ecfs_sb.s_inode_bitmap_number[i]; | ||
| 132 | inode_bitmap = sb->u.ecfs_sb.s_inode_bitmap[i]; | ||
| 133 | for (j = i; j > 0; j--) { | ||
| 134 | sb->u.ecfs_sb.s_inode_bitmap_number[j] = | ||
| 135 | sb->u.ecfs_sb.s_inode_bitmap_number[j - 1]; | ||
| 136 | sb->u.ecfs_sb.s_inode_bitmap[j] = | ||
| 137 | sb->u.ecfs_sb.s_inode_bitmap[j - 1]; | ||
| 138 | } | ||
| 139 | sb->u.ecfs_sb.s_inode_bitmap_number[0] = inode_bitmap_number; | ||
| 140 | sb->u.ecfs_sb.s_inode_bitmap[0] = inode_bitmap; | ||
| 141 | |||
| 142 | /* | ||
| 143 | * There's still one special case here --- if inode_bitmap == 0 | ||
| 144 | * then our last attempt to read the bitmap failed and we have | ||
| 145 | * just ended up caching that failure. Try again to read it. | ||
| 146 | */ | ||
| 147 | if (!inode_bitmap) | ||
| 148 | retval = read_inode_bitmap (sb, block_group, 0); | ||
| 149 | |||
| 150 | } else { | ||
| 151 | if (sb->u.ecfs_sb.s_loaded_inode_bitmaps < ECFS_MAX_GROUP_LOADED) | ||
| 152 | sb->u.ecfs_sb.s_loaded_inode_bitmaps++; | ||
| 153 | else | ||
| 154 | brelse (sb->u.ecfs_sb.s_inode_bitmap[ECFS_MAX_GROUP_LOADED - 1]); | ||
| 155 | for (j = sb->u.ecfs_sb.s_loaded_inode_bitmaps - 1; j > 0; j--) { | ||
| 156 | sb->u.ecfs_sb.s_inode_bitmap_number[j] = | ||
| 157 | sb->u.ecfs_sb.s_inode_bitmap_number[j - 1]; | ||
| 158 | sb->u.ecfs_sb.s_inode_bitmap[j] = | ||
| 159 | sb->u.ecfs_sb.s_inode_bitmap[j - 1]; | ||
| 160 | } | ||
| 161 | retval = read_inode_bitmap (sb, block_group, 0); | ||
| 162 | } | ||
| 163 | return retval; | ||
| 164 | } | ||
| 165 | |||
| 166 | /* | ||
| 167 | * NOTE! When we get the inode, we're the only people | ||
| 168 | * that have access to it, and as such there are no | ||
| 169 | * race conditions we have to worry about. The inode | ||
| 170 | * is not on the hash-lists, and it cannot be reached | ||
| 171 | * through the filesystem because the directory entry | ||
| 172 | * has been deleted earlier. | ||
| 173 | * | ||
| 174 | * HOWEVER: we must make sure that we get no aliases, | ||
| 175 | * which means that we have to call "clear_inode()" | ||
| 176 | * _before_ we mark the inode not in use in the inode | ||
| 177 | * bitmaps. Otherwise a newly created file might use | ||
| 178 | * the same inode number (not actually the same pointer | ||
| 179 | * though), and then we'd have two inodes sharing the | ||
| 180 | * same inode number and space on the harddisk. | ||
| 181 | */ | ||
| 182 | void ecfs_free_inode (struct inode * inode) | ||
| 183 | { | ||
| 184 | struct super_block * sb = inode->i_sb; | ||
| 185 | int is_directory; | ||
| 186 | unsigned long ino; | ||
| 187 | struct buffer_head * bh; | ||
| 188 | struct buffer_head * bh2; | ||
| 189 | unsigned long block_group; | ||
| 190 | unsigned long bit; | ||
| 191 | int bitmap_nr; | ||
| 192 | struct ecfs_group_desc * gdp; | ||
| 193 | struct ecfs_super_block * es; | ||
| 194 | |||
| 195 | ino = inode->i_ino; | ||
| 196 | ecfs_debug ("freeing inode %lu\n", ino); | ||
| 197 | |||
| 198 | /* | ||
| 199 | * Note: we must free any quota before locking the superblock, | ||
| 200 | * as writing the quota to disk may need the lock as well. | ||
| 201 | */ | ||
| 202 | //DQUOT_FREE_INODE(sb, inode); | ||
| 203 | //DQUOT_DROP(inode); | ||
| 204 | |||
| 205 | lock_super (sb); | ||
| 206 | es = sb->u.ecfs_sb.s_es; | ||
| 207 | if (ino < ECFS_FIRST_INO(sb) || | ||
| 208 | ino > le32_to_cpu(es->s_inodes_count)) { | ||
| 209 | ecfs_error (sb, "free_inode", | ||
| 210 | "reserved inode or nonexistent inode"); | ||
| 211 | goto error_return; | ||
| 212 | } | ||
| 213 | block_group = (ino - 1) / ECFS_INODES_PER_GROUP(sb); | ||
| 214 | bit = (ino - 1) % ECFS_INODES_PER_GROUP(sb); | ||
| 215 | bitmap_nr = load_inode_bitmap (sb, block_group); | ||
| 216 | if (bitmap_nr < 0) | ||
| 217 | goto error_return; | ||
| 218 | |||
| 219 | bh = sb->u.ecfs_sb.s_inode_bitmap[bitmap_nr]; | ||
| 220 | |||
| 221 | is_directory = S_ISDIR(inode->i_mode); | ||
| 222 | |||
| 223 | /* Do this BEFORE marking the inode not in use */ | ||
| 224 | clear_inode (inode); | ||
| 225 | |||
| 226 | /* Ok, now we can actually update the inode bitmaps.. */ | ||
| 227 | if (!ecfs_clear_bit (bit, bh->b_data)) | ||
| 228 | ecfs_error (sb, "ecfs_free_inode", | ||
| 229 | "bit already cleared for inode %lu", ino); | ||
| 230 | else { | ||
| 231 | gdp = ecfs_get_group_desc (sb, block_group, &bh2); | ||
| 232 | if (gdp) { | ||
| 233 | gdp->bg_free_inodes_count = | ||
| 234 | cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1); | ||
| 235 | if (is_directory) | ||
| 236 | gdp->bg_used_dirs_count = | ||
| 237 | cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); | ||
| 238 | } | ||
| 239 | mark_buffer_dirty(bh2); | ||
| 240 | es->s_free_inodes_count = | ||
| 241 | cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1); | ||
| 242 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 243 | } | ||
| 244 | mark_buffer_dirty(bh); | ||
| 245 | if (sb->s_flags & MS_SYNCHRONOUS) { | ||
| 246 | ll_rw_block (WRITE, 1, &bh); | ||
| 247 | wait_on_buffer (bh); | ||
| 248 | } | ||
| 249 | sb->s_dirt = 1; | ||
| 250 | error_return: | ||
| 251 | unlock_super (sb); | ||
| 252 | } | ||
| 253 | |||
| 254 | /* | ||
| 255 | * There are two policies for allocating an inode. If the new inode is | ||
| 256 | * a directory, then a forward search is made for a block group with both | ||
| 257 | * free space and a low directory-to-inode ratio; if that fails, then of | ||
| 258 | * the groups with above-average free space, that group with the fewest | ||
| 259 | * directories already is chosen. | ||
| 260 | * | ||
| 261 | * For other inodes, search forward from the parent directory\'s block | ||
| 262 | * group to find a free inode. | ||
| 263 | */ | ||
| 264 | struct inode * ecfs_new_inode (const struct inode * dir, int mode) | ||
| 265 | { | ||
| 266 | struct super_block * sb; | ||
| 267 | struct buffer_head * bh; | ||
| 268 | struct buffer_head * bh2; | ||
| 269 | int i, j, avefreei; | ||
| 270 | struct inode * inode; | ||
| 271 | int bitmap_nr; | ||
| 272 | struct ecfs_group_desc * gdp; | ||
| 273 | struct ecfs_group_desc * tmp; | ||
| 274 | struct ecfs_super_block * es; | ||
| 275 | int err; | ||
| 276 | |||
| 277 | /* Cannot create files in a deleted directory */ | ||
| 278 | if (!dir || !dir->i_nlink) | ||
| 279 | return ERR_PTR(-EPERM); | ||
| 280 | |||
| 281 | sb = dir->i_sb; | ||
| 282 | inode = new_inode(sb); | ||
| 283 | if (!inode) | ||
| 284 | return ERR_PTR(-ENOMEM); | ||
| 285 | |||
| 286 | lock_super (sb); | ||
| 287 | es = sb->u.ecfs_sb.s_es; | ||
| 288 | repeat: | ||
| 289 | gdp = NULL; i=0; | ||
| 290 | |||
| 291 | if (S_ISDIR(mode)) { | ||
| 292 | avefreei = le32_to_cpu(es->s_free_inodes_count) / | ||
| 293 | sb->u.ecfs_sb.s_groups_count; | ||
| 294 | /* I am not yet convinced that this next bit is necessary. | ||
| 295 | i = dir->u.ecfs_i.i_block_group; | ||
| 296 | for (j = 0; j < sb->u.ecfs_sb.s_groups_count; j++) { | ||
| 297 | tmp = ecfs_get_group_desc (sb, i, &bh2); | ||
| 298 | if (tmp && | ||
| 299 | (le16_to_cpu(tmp->bg_used_dirs_count) << 8) < | ||
| 300 | le16_to_cpu(tmp->bg_free_inodes_count)) { | ||
| 301 | gdp = tmp; | ||
| 302 | break; | ||
| 303 | } | ||
| 304 | else | ||
| 305 | i = ++i % sb->u.ecfs_sb.s_groups_count; | ||
| 306 | } | ||
| 307 | */ | ||
| 308 | if (!gdp) { | ||
| 309 | for (j = 0; j < sb->u.ecfs_sb.s_groups_count; j++) { | ||
| 310 | tmp = ecfs_get_group_desc (sb, j, &bh2); | ||
| 311 | if (tmp && | ||
| 312 | le16_to_cpu(tmp->bg_free_inodes_count) && | ||
| 313 | le16_to_cpu(tmp->bg_free_inodes_count) >= avefreei) { | ||
| 314 | if (!gdp || | ||
| 315 | (le16_to_cpu(tmp->bg_free_blocks_count) > | ||
| 316 | le16_to_cpu(gdp->bg_free_blocks_count))) { | ||
| 317 | i = j; | ||
| 318 | gdp = tmp; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | } | ||
| 322 | } | ||
| 323 | } | ||
| 324 | else | ||
| 325 | { | ||
| 326 | /* | ||
| 327 | * Try to place the inode in its parent directory | ||
| 328 | */ | ||
| 329 | i = dir->u.ecfs_i.i_block_group; | ||
| 330 | tmp = ecfs_get_group_desc (sb, i, &bh2); | ||
| 331 | if (tmp && le16_to_cpu(tmp->bg_free_inodes_count)) | ||
| 332 | gdp = tmp; | ||
| 333 | else | ||
| 334 | { | ||
| 335 | /* | ||
| 336 | * Use a quadratic hash to find a group with a | ||
| 337 | * free inode | ||
| 338 | */ | ||
| 339 | for (j = 1; j < sb->u.ecfs_sb.s_groups_count; j <<= 1) { | ||
| 340 | i += j; | ||
| 341 | if (i >= sb->u.ecfs_sb.s_groups_count) | ||
| 342 | i -= sb->u.ecfs_sb.s_groups_count; | ||
| 343 | tmp = ecfs_get_group_desc (sb, i, &bh2); | ||
| 344 | if (tmp && | ||
| 345 | le16_to_cpu(tmp->bg_free_inodes_count)) { | ||
| 346 | gdp = tmp; | ||
| 347 | break; | ||
| 348 | } | ||
| 349 | } | ||
| 350 | } | ||
| 351 | if (!gdp) { | ||
| 352 | /* | ||
| 353 | * That failed: try linear search for a free inode | ||
| 354 | */ | ||
| 355 | i = dir->u.ecfs_i.i_block_group + 1; | ||
| 356 | for (j = 2; j < sb->u.ecfs_sb.s_groups_count; j++) { | ||
| 357 | if (++i >= sb->u.ecfs_sb.s_groups_count) | ||
| 358 | i = 0; | ||
| 359 | tmp = ecfs_get_group_desc (sb, i, &bh2); | ||
| 360 | if (tmp && | ||
| 361 | le16_to_cpu(tmp->bg_free_inodes_count)) { | ||
| 362 | gdp = tmp; | ||
| 363 | break; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | err = -ENOSPC; | ||
| 370 | if (!gdp) | ||
| 371 | goto fail; | ||
| 372 | |||
| 373 | err = -EIO; | ||
| 374 | bitmap_nr = load_inode_bitmap (sb, i); | ||
| 375 | if (bitmap_nr < 0) | ||
| 376 | goto fail; | ||
| 377 | |||
| 378 | bh = sb->u.ecfs_sb.s_inode_bitmap[bitmap_nr]; | ||
| 379 | if ((j = ecfs_find_first_zero_bit ((unsigned long *) bh->b_data, | ||
| 380 | ECFS_INODES_PER_GROUP(sb))) < | ||
| 381 | ECFS_INODES_PER_GROUP(sb)) { | ||
| 382 | if (ecfs_set_bit (j, bh->b_data)) { | ||
| 383 | ecfs_error (sb, "ecfs_new_inode", | ||
| 384 | "bit already set for inode %d", j); | ||
| 385 | goto repeat; | ||
| 386 | } | ||
| 387 | mark_buffer_dirty(bh); | ||
| 388 | if (sb->s_flags & MS_SYNCHRONOUS) { | ||
| 389 | ll_rw_block (WRITE, 1, &bh); | ||
| 390 | wait_on_buffer (bh); | ||
| 391 | } | ||
| 392 | } else { | ||
| 393 | if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) { | ||
| 394 | ecfs_error (sb, "ecfs_new_inode", | ||
| 395 | "Free inodes count corrupted in group %d", | ||
| 396 | i); | ||
| 397 | /* Is it really ENOSPC? */ | ||
| 398 | err = -ENOSPC; | ||
| 399 | if (sb->s_flags & MS_RDONLY) | ||
| 400 | goto fail; | ||
| 401 | |||
| 402 | gdp->bg_free_inodes_count = 0; | ||
| 403 | mark_buffer_dirty(bh2); | ||
| 404 | } | ||
| 405 | goto repeat; | ||
| 406 | } | ||
| 407 | j += i * ECFS_INODES_PER_GROUP(sb) + 1; | ||
| 408 | if (j < ECFS_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) { | ||
| 409 | ecfs_error (sb, "ecfs_new_inode", | ||
| 410 | "reserved inode or inode > inodes count - " | ||
| 411 | "block_group = %d,inode=%d", i, j); | ||
| 412 | err = -EIO; | ||
| 413 | goto fail; | ||
| 414 | } | ||
| 415 | gdp->bg_free_inodes_count = | ||
| 416 | cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1); | ||
| 417 | if (S_ISDIR(mode)) | ||
| 418 | gdp->bg_used_dirs_count = | ||
| 419 | cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1); | ||
| 420 | mark_buffer_dirty(bh2); | ||
| 421 | es->s_free_inodes_count = | ||
| 422 | cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1); | ||
| 423 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 424 | sb->s_dirt = 1; | ||
| 425 | inode->i_mode = mode; | ||
| 426 | inode->i_uid = current->fsuid; | ||
| 427 | if (test_opt (sb, GRPID)) | ||
| 428 | inode->i_gid = dir->i_gid; | ||
| 429 | else if (dir->i_mode & S_ISGID) { | ||
| 430 | inode->i_gid = dir->i_gid; | ||
| 431 | if (S_ISDIR(mode)) | ||
| 432 | mode |= S_ISGID; | ||
| 433 | } else | ||
| 434 | inode->i_gid = current->fsgid; | ||
| 435 | |||
| 436 | inode->i_ino = j; | ||
| 437 | inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ | ||
| 438 | inode->i_blocks = 0; | ||
| 439 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | ||
| 440 | inode->u.ecfs_i.i_new_inode = 1; | ||
| 441 | inode->u.ecfs_i.i_flags = dir->u.ecfs_i.i_flags; | ||
| 442 | if (S_ISLNK(mode)) | ||
| 443 | inode->u.ecfs_i.i_flags &= ~(ECFS_IMMUTABLE_FL | ECFS_APPEND_FL); | ||
| 444 | inode->u.ecfs_i.i_faddr = 0; | ||
| 445 | inode->u.ecfs_i.i_frag_no = 0; | ||
| 446 | inode->u.ecfs_i.i_frag_size = 0; | ||
| 447 | inode->u.ecfs_i.i_file_acl = 0; | ||
| 448 | inode->u.ecfs_i.i_dir_acl = 0; | ||
| 449 | inode->u.ecfs_i.i_dtime = 0; | ||
| 450 | inode->u.ecfs_i.i_block_group = i; | ||
| 451 | if (inode->u.ecfs_i.i_flags & ECFS_SYNC_FL) | ||
| 452 | inode->i_flags |= S_SYNC; | ||
| 453 | insert_inode_hash(inode); | ||
| 454 | inode->i_generation = event++; | ||
| 455 | mark_inode_dirty(inode); | ||
| 456 | |||
| 457 | unlock_super (sb); | ||
| 458 | /*if(DQUOT_ALLOC_INODE(sb, inode)) { | ||
| 459 | sb->dq_op->drop(inode); | ||
| 460 | inode->i_nlink = 0; | ||
| 461 | iput(inode); | ||
| 462 | return ERR_PTR(-EDQUOT); | ||
| 463 | }*/ | ||
| 464 | ecfs_debug ("allocating inode %lu\n", inode->i_ino); | ||
| 465 | return inode; | ||
| 466 | |||
| 467 | fail: | ||
| 468 | unlock_super(sb); | ||
| 469 | iput(inode); | ||
| 470 | return ERR_PTR(err); | ||
| 471 | } | ||
| 472 | |||
| 473 | unsigned long ecfs_count_free_inodes (struct super_block * sb) | ||
| 474 | { | ||
| 475 | #ifdef ECFSFS_DEBUG | ||
| 476 | struct ecfs_super_block * es; | ||
| 477 | unsigned long desc_count, bitmap_count, x; | ||
| 478 | int bitmap_nr; | ||
| 479 | struct ecfs_group_desc * gdp; | ||
| 480 | int i; | ||
| 481 | |||
| 482 | lock_super (sb); | ||
| 483 | es = sb->u.ecfs_sb.s_es; | ||
| 484 | desc_count = 0; | ||
| 485 | bitmap_count = 0; | ||
| 486 | gdp = NULL; | ||
| 487 | for (i = 0; i < sb->u.ecfs_sb.s_groups_count; i++) { | ||
| 488 | gdp = ecfs_get_group_desc (sb, i, NULL); | ||
| 489 | if (!gdp) | ||
| 490 | continue; | ||
| 491 | desc_count += le16_to_cpu(gdp->bg_free_inodes_count); | ||
| 492 | bitmap_nr = load_inode_bitmap (sb, i); | ||
| 493 | if (bitmap_nr < 0) | ||
| 494 | continue; | ||
| 495 | |||
| 496 | x = ecfs_count_free (sb->u.ecfs_sb.s_inode_bitmap[bitmap_nr], | ||
| 497 | ECFS_INODES_PER_GROUP(sb) / 8); | ||
| 498 | printk ("group %d: stored = %d, counted = %lu\n", | ||
| 499 | i, le16_to_cpu(gdp->bg_free_inodes_count), x); | ||
| 500 | bitmap_count += x; | ||
| 501 | } | ||
| 502 | printk("ecfs_count_free_inodes: stored = %lu, computed = %lu, %lu\n", | ||
| 503 | le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count); | ||
| 504 | unlock_super (sb); | ||
| 505 | return desc_count; | ||
| 506 | #else | ||
| 507 | return le32_to_cpu(sb->u.ecfs_sb.s_es->s_free_inodes_count); | ||
| 508 | #endif | ||
| 509 | } | ||
| 510 | |||
| 511 | #ifdef CONFIG_ECFS_CHECK | ||
| 512 | /* Called at mount-time, super-block is locked */ | ||
| 513 | void ecfs_check_inodes_bitmap (struct super_block * sb) | ||
| 514 | { | ||
| 515 | struct ecfs_super_block * es; | ||
| 516 | unsigned long desc_count, bitmap_count, x; | ||
| 517 | int bitmap_nr; | ||
| 518 | struct ecfs_group_desc * gdp; | ||
| 519 | int i; | ||
| 520 | |||
| 521 | es = sb->u.ecfs_sb.s_es; | ||
| 522 | desc_count = 0; | ||
| 523 | bitmap_count = 0; | ||
| 524 | gdp = NULL; | ||
| 525 | for (i = 0; i < sb->u.ecfs_sb.s_groups_count; i++) { | ||
| 526 | gdp = ecfs_get_group_desc (sb, i, NULL); | ||
| 527 | if (!gdp) | ||
| 528 | continue; | ||
| 529 | desc_count += le16_to_cpu(gdp->bg_free_inodes_count); | ||
| 530 | bitmap_nr = load_inode_bitmap (sb, i); | ||
| 531 | if (bitmap_nr < 0) | ||
| 532 | continue; | ||
| 533 | |||
| 534 | x = ecfs_count_free (sb->u.ecfs_sb.s_inode_bitmap[bitmap_nr], | ||
| 535 | ECFS_INODES_PER_GROUP(sb) / 8); | ||
| 536 | if (le16_to_cpu(gdp->bg_free_inodes_count) != x) | ||
| 537 | ecfs_error (sb, "ecfs_check_inodes_bitmap", | ||
| 538 | "Wrong free inodes count in group %d, " | ||
| 539 | "stored = %d, counted = %lu", i, | ||
| 540 | le16_to_cpu(gdp->bg_free_inodes_count), x); | ||
| 541 | bitmap_count += x; | ||
| 542 | } | ||
| 543 | if (le32_to_cpu(es->s_free_inodes_count) != bitmap_count) | ||
| 544 | ecfs_error (sb, "ecfs_check_inodes_bitmap", | ||
| 545 | "Wrong free inodes count in super block, " | ||
| 546 | "stored = %lu, counted = %lu", | ||
| 547 | (unsigned long) le32_to_cpu(es->s_free_inodes_count), | ||
| 548 | bitmap_count); | ||
| 549 | } | ||
| 550 | #endif | ||
diff --git a/other/ecfs/ialloc.o b/other/ecfs/ialloc.o new file mode 100644 index 0000000..a6f00d8 --- /dev/null +++ b/other/ecfs/ialloc.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/inode.c b/other/ecfs/inode.c new file mode 100644 index 0000000..2be65ad --- /dev/null +++ b/other/ecfs/inode.c | |||
| @@ -0,0 +1,1329 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/inode.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | * | ||
| 9 | * from | ||
| 10 | * | ||
| 11 | * linux/fs/minix/inode.c | ||
| 12 | * | ||
| 13 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
| 14 | * | ||
| 15 | * Goal-directed block allocation by Stephen Tweedie | ||
| 16 | * (sct@dcs.ed.ac.uk), 1993, 1998 | ||
| 17 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
| 18 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
| 19 | * 64-bit file support on 64-bit platforms by Jakub Jelinek | ||
| 20 | * (jj@sunsite.ms.mff.cuni.cz) | ||
| 21 | * | ||
| 22 | * Assorted race fixes, rewrite of ecfs_get_block() by Al Viro, 2000 | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include <linux/fs_ecfs.h> | ||
| 26 | #include <linux/ecfs_fs.h> | ||
| 27 | #include "rc4.h" | ||
| 28 | #include <linux/locks.h> | ||
| 29 | #include <linux/smp_lock.h> | ||
| 30 | #include <linux/sched.h> | ||
| 31 | #include <linux/highuid.h> | ||
| 32 | |||
| 33 | static int ecfs_update_inode(struct inode * inode, int do_sync); | ||
| 34 | |||
| 35 | extern unsigned char *ecfs_key; | ||
| 36 | |||
| 37 | /* | ||
| 38 | * Called at each iput() | ||
| 39 | */ | ||
| 40 | void ecfs_put_inode (struct inode * inode) | ||
| 41 | { | ||
| 42 | ecfs_discard_prealloc (inode); | ||
| 43 | } | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Called at the last iput() if i_nlink is zero. | ||
| 47 | */ | ||
| 48 | void ecfs_delete_inode (struct inode * inode) | ||
| 49 | { | ||
| 50 | lock_kernel(); | ||
| 51 | |||
| 52 | if (is_bad_inode(inode) || | ||
| 53 | inode->i_ino == ECFS_ACL_IDX_INO || | ||
| 54 | inode->i_ino == ECFS_ACL_DATA_INO) | ||
| 55 | goto no_delete; | ||
| 56 | inode->u.ecfs_i.i_dtime = CURRENT_TIME; | ||
| 57 | mark_inode_dirty(inode); | ||
| 58 | ecfs_update_inode(inode, IS_SYNC(inode)); | ||
| 59 | inode->i_size = 0; | ||
| 60 | if (inode->i_blocks) | ||
| 61 | ecfs_truncate (inode); | ||
| 62 | ecfs_free_inode (inode); | ||
| 63 | |||
| 64 | unlock_kernel(); | ||
| 65 | return; | ||
| 66 | no_delete: | ||
| 67 | unlock_kernel(); | ||
| 68 | clear_inode(inode); /* We must guarantee clearing of inode... */ | ||
| 69 | } | ||
| 70 | |||
| 71 | void ecfs_discard_prealloc (struct inode * inode) | ||
| 72 | { | ||
| 73 | #ifdef ECFS_PREALLOCATE | ||
| 74 | lock_kernel(); | ||
| 75 | /* Writer: ->i_prealloc* */ | ||
| 76 | if (inode->u.ecfs_i.i_prealloc_count) { | ||
| 77 | unsigned short total = inode->u.ecfs_i.i_prealloc_count; | ||
| 78 | unsigned long block = inode->u.ecfs_i.i_prealloc_block; | ||
| 79 | inode->u.ecfs_i.i_prealloc_count = 0; | ||
| 80 | inode->u.ecfs_i.i_prealloc_block = 0; | ||
| 81 | /* Writer: end */ | ||
| 82 | ecfs_free_blocks (inode, block, total); | ||
| 83 | } | ||
| 84 | unlock_kernel(); | ||
| 85 | #endif | ||
| 86 | } | ||
| 87 | |||
| 88 | static int ecfs_alloc_block (struct inode * inode, unsigned long goal, int *err) | ||
| 89 | { | ||
| 90 | #ifdef ECFSFS_DEBUG | ||
| 91 | static unsigned long alloc_hits = 0, alloc_attempts = 0; | ||
| 92 | #endif | ||
| 93 | unsigned long result; | ||
| 94 | |||
| 95 | |||
| 96 | #ifdef ECFS_PREALLOCATE | ||
| 97 | /* Writer: ->i_prealloc* */ | ||
| 98 | if (inode->u.ecfs_i.i_prealloc_count && | ||
| 99 | (goal == inode->u.ecfs_i.i_prealloc_block || | ||
| 100 | goal + 1 == inode->u.ecfs_i.i_prealloc_block)) | ||
| 101 | { | ||
| 102 | result = inode->u.ecfs_i.i_prealloc_block++; | ||
| 103 | inode->u.ecfs_i.i_prealloc_count--; | ||
| 104 | /* Writer: end */ | ||
| 105 | #ifdef ECFSFS_DEBUG | ||
| 106 | ecfs_debug ("preallocation hit (%lu/%lu).\n", | ||
| 107 | ++alloc_hits, ++alloc_attempts); | ||
| 108 | #endif | ||
| 109 | } else { | ||
| 110 | ecfs_discard_prealloc (inode); | ||
| 111 | #ifdef ECFSFS_DEBUG | ||
| 112 | ecfs_debug ("preallocation miss (%lu/%lu).\n", | ||
| 113 | alloc_hits, ++alloc_attempts); | ||
| 114 | #endif | ||
| 115 | if (S_ISREG(inode->i_mode)) | ||
| 116 | result = ecfs_new_block (inode, goal, | ||
| 117 | &inode->u.ecfs_i.i_prealloc_count, | ||
| 118 | &inode->u.ecfs_i.i_prealloc_block, err); | ||
| 119 | else | ||
| 120 | result = ecfs_new_block (inode, goal, 0, 0, err); | ||
| 121 | } | ||
| 122 | #else | ||
| 123 | result = ecfs_new_block (inode, goal, 0, 0, err); | ||
| 124 | #endif | ||
| 125 | return result; | ||
| 126 | } | ||
| 127 | |||
| 128 | typedef struct { | ||
| 129 | u32 *p; | ||
| 130 | u32 key; | ||
| 131 | struct buffer_head *bh; | ||
| 132 | } Indirect; | ||
| 133 | |||
| 134 | static inline void add_chain(Indirect *p, struct buffer_head *bh, u32 *v) | ||
| 135 | { | ||
| 136 | p->key = *(p->p = v); | ||
| 137 | p->bh = bh; | ||
| 138 | } | ||
| 139 | |||
| 140 | static inline int verify_chain(Indirect *from, Indirect *to) | ||
| 141 | { | ||
| 142 | while (from <= to && from->key == *from->p) | ||
| 143 | from++; | ||
| 144 | return (from > to); | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * ecfs_block_to_path - parse the block number into array of offsets | ||
| 149 | * @inode: inode in question (we are only interested in its superblock) | ||
| 150 | * @i_block: block number to be parsed | ||
| 151 | * @offsets: array to store the offsets in | ||
| 152 | * | ||
| 153 | * To store the locations of file's data ecfs uses a data structure common | ||
| 154 | * for UNIX filesystems - tree of pointers anchored in the inode, with | ||
| 155 | * data blocks at leaves and indirect blocks in intermediate nodes. | ||
| 156 | * This function translates the block number into path in that tree - | ||
| 157 | * return value is the path length and @offsets[n] is the offset of | ||
| 158 | * pointer to (n+1)th node in the nth one. If @block is out of range | ||
| 159 | * (negative or too large) warning is printed and zero returned. | ||
| 160 | * | ||
| 161 | * Note: function doesn't find node addresses, so no IO is needed. All | ||
| 162 | * we need to know is the capacity of indirect blocks (taken from the | ||
| 163 | * inode->i_sb). | ||
| 164 | */ | ||
| 165 | |||
| 166 | /* | ||
| 167 | * Portability note: the last comparison (check that we fit into triple | ||
| 168 | * indirect block) is spelled differently, because otherwise on an | ||
| 169 | * architecture with 32-bit longs and 8Kb pages we might get into trouble | ||
| 170 | * if our filesystem had 8Kb blocks. We might use long long, but that would | ||
| 171 | * kill us on x86. Oh, well, at least the sign propagation does not matter - | ||
| 172 | * i_block would have to be negative in the very beginning, so we would not | ||
| 173 | * get there at all. | ||
| 174 | */ | ||
| 175 | |||
| 176 | static int ecfs_block_to_path(struct inode *inode, long i_block, int offsets[4]) | ||
| 177 | { | ||
| 178 | int ptrs = ECFS_ADDR_PER_BLOCK(inode->i_sb); | ||
| 179 | int ptrs_bits = ECFS_ADDR_PER_BLOCK_BITS(inode->i_sb); | ||
| 180 | const long direct_blocks = ECFS_NDIR_BLOCKS, | ||
| 181 | indirect_blocks = ptrs, | ||
| 182 | double_blocks = (1 << (ptrs_bits * 2)); | ||
| 183 | int n = 0; | ||
| 184 | |||
| 185 | if (i_block < 0) { | ||
| 186 | ecfs_warning (inode->i_sb, "ecfs_block_to_path", "block < 0"); | ||
| 187 | } else if (i_block < direct_blocks) { | ||
| 188 | offsets[n++] = i_block; | ||
| 189 | } else if ( (i_block -= direct_blocks) < indirect_blocks) { | ||
| 190 | offsets[n++] = ECFS_IND_BLOCK; | ||
| 191 | offsets[n++] = i_block; | ||
| 192 | } else if ((i_block -= indirect_blocks) < double_blocks) { | ||
| 193 | offsets[n++] = ECFS_DIND_BLOCK; | ||
| 194 | offsets[n++] = i_block >> ptrs_bits; | ||
| 195 | offsets[n++] = i_block & (ptrs - 1); | ||
| 196 | } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) { | ||
| 197 | offsets[n++] = ECFS_TIND_BLOCK; | ||
| 198 | offsets[n++] = i_block >> (ptrs_bits * 2); | ||
| 199 | offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1); | ||
| 200 | offsets[n++] = i_block & (ptrs - 1); | ||
| 201 | } else { | ||
| 202 | ecfs_warning (inode->i_sb, "ecfs_block_to_path", "block > big"); | ||
| 203 | } | ||
| 204 | return n; | ||
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * ecfs_get_branch - read the chain of indirect blocks leading to data | ||
| 209 | * @inode: inode in question | ||
| 210 | * @depth: depth of the chain (1 - direct pointer, etc.) | ||
| 211 | * @offsets: offsets of pointers in inode/indirect blocks | ||
| 212 | * @chain: place to store the result | ||
| 213 | * @err: here we store the error value | ||
| 214 | * | ||
| 215 | * Function fills the array of triples <key, p, bh> and returns %NULL | ||
| 216 | * if everything went OK or the pointer to the last filled triple | ||
| 217 | * (incomplete one) otherwise. Upon the return chain[i].key contains | ||
| 218 | * the number of (i+1)-th block in the chain (as it is stored in memory, | ||
| 219 | * i.e. little-endian 32-bit), chain[i].p contains the address of that | ||
| 220 | * number (it points into struct inode for i==0 and into the bh->b_data | ||
| 221 | * for i>0) and chain[i].bh points to the buffer_head of i-th indirect | ||
| 222 | * block for i>0 and NULL for i==0. In other words, it holds the block | ||
| 223 | * numbers of the chain, addresses they were taken from (and where we can | ||
| 224 | * verify that chain did not change) and buffer_heads hosting these | ||
| 225 | * numbers. | ||
| 226 | * | ||
| 227 | * Function stops when it stumbles upon zero pointer (absent block) | ||
| 228 | * (pointer to last triple returned, *@err == 0) | ||
| 229 | * or when it gets an IO error reading an indirect block | ||
| 230 | * (ditto, *@err == -EIO) | ||
| 231 | * or when it notices that chain had been changed while it was reading | ||
| 232 | * (ditto, *@err == -EAGAIN) | ||
| 233 | * or when it reads all @depth-1 indirect blocks successfully and finds | ||
| 234 | * the whole chain, all way to the data (returns %NULL, *err == 0). | ||
| 235 | */ | ||
| 236 | static inline Indirect *ecfs_get_branch(struct inode *inode, | ||
| 237 | int depth, | ||
| 238 | int *offsets, | ||
| 239 | Indirect chain[4], | ||
| 240 | int *err) | ||
| 241 | { | ||
| 242 | kdev_t dev = inode->i_dev; | ||
| 243 | int size = inode->i_sb->s_blocksize; | ||
| 244 | Indirect *p = chain; | ||
| 245 | struct buffer_head *bh; | ||
| 246 | |||
| 247 | *err = 0; | ||
| 248 | /* i_data is not going away, no lock needed */ | ||
| 249 | add_chain (chain, NULL, inode->u.ecfs_i.i_data + *offsets); | ||
| 250 | if (!p->key) | ||
| 251 | goto no_block; | ||
| 252 | while (--depth) { | ||
| 253 | bh = bread(dev, le32_to_cpu(p->key), size); | ||
| 254 | if (!bh) | ||
| 255 | goto failure; | ||
| 256 | /* Reader: pointers */ | ||
| 257 | if (!verify_chain(chain, p)) | ||
| 258 | goto changed; | ||
| 259 | add_chain(++p, bh, (u32*)bh->b_data + *++offsets); | ||
| 260 | /* Reader: end */ | ||
| 261 | if (!p->key) | ||
| 262 | goto no_block; | ||
| 263 | } | ||
| 264 | return NULL; | ||
| 265 | |||
| 266 | changed: | ||
| 267 | *err = -EAGAIN; | ||
| 268 | goto no_block; | ||
| 269 | failure: | ||
| 270 | *err = -EIO; | ||
| 271 | no_block: | ||
| 272 | return p; | ||
| 273 | } | ||
| 274 | |||
| 275 | /** | ||
| 276 | * ecfs_find_near - find a place for allocation with sufficient locality | ||
| 277 | * @inode: owner | ||
| 278 | * @ind: descriptor of indirect block. | ||
| 279 | * | ||
| 280 | * This function returns the prefered place for block allocation. | ||
| 281 | * It is used when heuristic for sequential allocation fails. | ||
| 282 | * Rules are: | ||
| 283 | * + if there is a block to the left of our position - allocate near it. | ||
| 284 | * + if pointer will live in indirect block - allocate near that block. | ||
| 285 | * + if pointer will live in inode - allocate in the same cylinder group. | ||
| 286 | * Caller must make sure that @ind is valid and will stay that way. | ||
| 287 | */ | ||
| 288 | |||
| 289 | static inline unsigned long ecfs_find_near(struct inode *inode, Indirect *ind) | ||
| 290 | { | ||
| 291 | u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ecfs_i.i_data; | ||
| 292 | u32 *p; | ||
| 293 | |||
| 294 | /* Try to find previous block */ | ||
| 295 | for (p = ind->p - 1; p >= start; p--) | ||
| 296 | if (*p) | ||
| 297 | return le32_to_cpu(*p); | ||
| 298 | |||
| 299 | /* No such thing, so let's try location of indirect block */ | ||
| 300 | if (ind->bh) | ||
| 301 | return ind->bh->b_blocknr; | ||
| 302 | |||
| 303 | /* | ||
| 304 | * It is going to be refered from inode itself? OK, just put it into | ||
| 305 | * the same cylinder group then. | ||
| 306 | */ | ||
| 307 | return (inode->u.ecfs_i.i_block_group * | ||
| 308 | ECFS_BLOCKS_PER_GROUP(inode->i_sb)) + | ||
| 309 | le32_to_cpu(inode->i_sb->u.ecfs_sb.s_es->s_first_data_block); | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * ecfs_find_goal - find a prefered place for allocation. | ||
| 314 | * @inode: owner | ||
| 315 | * @block: block we want | ||
| 316 | * @chain: chain of indirect blocks | ||
| 317 | * @partial: pointer to the last triple within a chain | ||
| 318 | * @goal: place to store the result. | ||
| 319 | * | ||
| 320 | * Normally this function find the prefered place for block allocation, | ||
| 321 | * stores it in *@goal and returns zero. If the branch had been changed | ||
| 322 | * under us we return -EAGAIN. | ||
| 323 | */ | ||
| 324 | |||
| 325 | static inline int ecfs_find_goal(struct inode *inode, | ||
| 326 | long block, | ||
| 327 | Indirect chain[4], | ||
| 328 | Indirect *partial, | ||
| 329 | unsigned long *goal) | ||
| 330 | { | ||
| 331 | /* Writer: ->i_next_alloc* */ | ||
| 332 | if (block == inode->u.ecfs_i.i_next_alloc_block + 1) { | ||
| 333 | inode->u.ecfs_i.i_next_alloc_block++; | ||
| 334 | inode->u.ecfs_i.i_next_alloc_goal++; | ||
| 335 | } | ||
| 336 | /* Writer: end */ | ||
| 337 | /* Reader: pointers, ->i_next_alloc* */ | ||
| 338 | if (verify_chain(chain, partial)) { | ||
| 339 | /* | ||
| 340 | * try the heuristic for sequential allocation, | ||
| 341 | * failing that at least try to get decent locality. | ||
| 342 | */ | ||
| 343 | if (block == inode->u.ecfs_i.i_next_alloc_block) | ||
| 344 | *goal = inode->u.ecfs_i.i_next_alloc_goal; | ||
| 345 | if (!*goal) | ||
| 346 | *goal = ecfs_find_near(inode, partial); | ||
| 347 | return 0; | ||
| 348 | } | ||
| 349 | /* Reader: end */ | ||
| 350 | return -EAGAIN; | ||
| 351 | } | ||
| 352 | |||
| 353 | /** | ||
| 354 | * ecfs_alloc_branch - allocate and set up a chain of blocks. | ||
| 355 | * @inode: owner | ||
| 356 | * @num: depth of the chain (number of blocks to allocate) | ||
| 357 | * @offsets: offsets (in the blocks) to store the pointers to next. | ||
| 358 | * @branch: place to store the chain in. | ||
| 359 | * | ||
| 360 | * This function allocates @num blocks, zeroes out all but the last one, | ||
| 361 | * links them into chain and (if we are synchronous) writes them to disk. | ||
| 362 | * In other words, it prepares a branch that can be spliced onto the | ||
| 363 | * inode. It stores the information about that chain in the branch[], in | ||
| 364 | * the same format as ecfs_get_branch() would do. We are calling it after | ||
| 365 | * we had read the existing part of chain and partial points to the last | ||
| 366 | * triple of that (one with zero ->key). Upon the exit we have the same | ||
| 367 | * picture as after the successful ecfs_get_block(), excpet that in one | ||
| 368 | * place chain is disconnected - *branch->p is still zero (we did not | ||
| 369 | * set the last link), but branch->key contains the number that should | ||
| 370 | * be placed into *branch->p to fill that gap. | ||
| 371 | * | ||
| 372 | * If allocation fails we free all blocks we've allocated (and forget | ||
| 373 | * ther buffer_heads) and return the error value the from failed | ||
| 374 | * ecfs_alloc_block() (normally -ENOSPC). Otherwise we set the chain | ||
| 375 | * as described above and return 0. | ||
| 376 | */ | ||
| 377 | |||
| 378 | static int ecfs_alloc_branch(struct inode *inode, | ||
| 379 | int num, | ||
| 380 | unsigned long goal, | ||
| 381 | int *offsets, | ||
| 382 | Indirect *branch) | ||
| 383 | { | ||
| 384 | int blocksize = inode->i_sb->s_blocksize; | ||
| 385 | int n = 0; | ||
| 386 | int err; | ||
| 387 | int i; | ||
| 388 | int parent = ecfs_alloc_block(inode, goal, &err); | ||
| 389 | |||
| 390 | branch[0].key = cpu_to_le32(parent); | ||
| 391 | if (parent) for (n = 1; n < num; n++) { | ||
| 392 | struct buffer_head *bh; | ||
| 393 | /* Allocate the next block */ | ||
| 394 | int nr = ecfs_alloc_block(inode, parent, &err); | ||
| 395 | if (!nr) | ||
| 396 | break; | ||
| 397 | branch[n].key = cpu_to_le32(nr); | ||
| 398 | /* | ||
| 399 | * Get buffer_head for parent block, zero it out and set | ||
| 400 | * the pointer to new one, then send parent to disk. | ||
| 401 | */ | ||
| 402 | bh = getblk(inode->i_dev, parent, blocksize); | ||
| 403 | if (!buffer_uptodate(bh)) | ||
| 404 | wait_on_buffer(bh); | ||
| 405 | memset(bh->b_data, 0, blocksize); | ||
| 406 | branch[n].bh = bh; | ||
| 407 | branch[n].p = (u32*) bh->b_data + offsets[n]; | ||
| 408 | *branch[n].p = branch[n].key; | ||
| 409 | mark_buffer_uptodate(bh, 1); | ||
| 410 | mark_buffer_dirty_inode(bh, inode); | ||
| 411 | if (IS_SYNC(inode) || inode->u.ecfs_i.i_osync) { | ||
| 412 | ll_rw_block (WRITE, 1, &bh); | ||
| 413 | wait_on_buffer (bh); | ||
| 414 | } | ||
| 415 | parent = nr; | ||
| 416 | } | ||
| 417 | if (n == num) | ||
| 418 | return 0; | ||
| 419 | |||
| 420 | /* Allocation failed, free what we already allocated */ | ||
| 421 | for (i = 1; i < n; i++) | ||
| 422 | bforget(branch[i].bh); | ||
| 423 | for (i = 0; i < n; i++) | ||
| 424 | ecfs_free_blocks(inode, le32_to_cpu(branch[i].key), 1); | ||
| 425 | return err; | ||
| 426 | } | ||
| 427 | |||
| 428 | /** | ||
| 429 | * ecfs_splice_branch - splice the allocated branch onto inode. | ||
| 430 | * @inode: owner | ||
| 431 | * @block: (logical) number of block we are adding | ||
| 432 | * @chain: chain of indirect blocks (with a missing link - see | ||
| 433 | * ecfs_alloc_branch) | ||
| 434 | * @where: location of missing link | ||
| 435 | * @num: number of blocks we are adding | ||
| 436 | * | ||
| 437 | * This function verifies that chain (up to the missing link) had not | ||
| 438 | * changed, fills the missing link and does all housekeeping needed in | ||
| 439 | * inode (->i_blocks, etc.). In case of success we end up with the full | ||
| 440 | * chain to new block and return 0. Otherwise (== chain had been changed) | ||
| 441 | * we free the new blocks (forgetting their buffer_heads, indeed) and | ||
| 442 | * return -EAGAIN. | ||
| 443 | */ | ||
| 444 | |||
| 445 | static inline int ecfs_splice_branch(struct inode *inode, | ||
| 446 | long block, | ||
| 447 | Indirect chain[4], | ||
| 448 | Indirect *where, | ||
| 449 | int num) | ||
| 450 | { | ||
| 451 | int i; | ||
| 452 | |||
| 453 | /* Verify that place we are splicing to is still there and vacant */ | ||
| 454 | |||
| 455 | /* Writer: pointers, ->i_next_alloc*, ->i_blocks */ | ||
| 456 | if (!verify_chain(chain, where-1) || *where->p) | ||
| 457 | /* Writer: end */ | ||
| 458 | goto changed; | ||
| 459 | |||
| 460 | /* That's it */ | ||
| 461 | |||
| 462 | *where->p = where->key; | ||
| 463 | inode->u.ecfs_i.i_next_alloc_block = block; | ||
| 464 | inode->u.ecfs_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key); | ||
| 465 | inode->i_blocks += num * inode->i_sb->s_blocksize/512; | ||
| 466 | |||
| 467 | /* Writer: end */ | ||
| 468 | |||
| 469 | /* We are done with atomic stuff, now do the rest of housekeeping */ | ||
| 470 | |||
| 471 | inode->i_ctime = CURRENT_TIME; | ||
| 472 | |||
| 473 | /* had we spliced it onto indirect block? */ | ||
| 474 | if (where->bh) { | ||
| 475 | mark_buffer_dirty_inode(where->bh, inode); | ||
| 476 | if (IS_SYNC(inode) || inode->u.ecfs_i.i_osync) { | ||
| 477 | ll_rw_block (WRITE, 1, &where->bh); | ||
| 478 | wait_on_buffer(where->bh); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | if (IS_SYNC(inode) || inode->u.ecfs_i.i_osync) | ||
| 483 | ecfs_sync_inode (inode); | ||
| 484 | else | ||
| 485 | mark_inode_dirty(inode); | ||
| 486 | return 0; | ||
| 487 | |||
| 488 | changed: | ||
| 489 | for (i = 1; i < num; i++) | ||
| 490 | bforget(where[i].bh); | ||
| 491 | for (i = 0; i < num; i++) | ||
| 492 | ecfs_free_blocks(inode, le32_to_cpu(where[i].key), 1); | ||
| 493 | return -EAGAIN; | ||
| 494 | } | ||
| 495 | |||
| 496 | /* | ||
| 497 | * Allocation strategy is simple: if we have to allocate something, we will | ||
| 498 | * have to go the whole way to leaf. So let's do it before attaching anything | ||
| 499 | * to tree, set linkage between the newborn blocks, write them if sync is | ||
| 500 | * required, recheck the path, free and repeat if check fails, otherwise | ||
| 501 | * set the last missing link (that will protect us from any truncate-generated | ||
| 502 | * removals - all blocks on the path are immune now) and possibly force the | ||
| 503 | * write on the parent block. | ||
| 504 | * That has a nice additional property: no special recovery from the failed | ||
| 505 | * allocations is needed - we simply release blocks and do not touch anything | ||
| 506 | * reachable from inode. | ||
| 507 | */ | ||
| 508 | |||
| 509 | static int ecfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) | ||
| 510 | { | ||
| 511 | int err = -EIO; | ||
| 512 | int offsets[4]; | ||
| 513 | Indirect chain[4]; | ||
| 514 | Indirect *partial; | ||
| 515 | unsigned long goal; | ||
| 516 | int left; | ||
| 517 | int depth = ecfs_block_to_path(inode, iblock, offsets); | ||
| 518 | |||
| 519 | if (depth == 0) | ||
| 520 | goto out; | ||
| 521 | |||
| 522 | lock_kernel(); | ||
| 523 | reread: | ||
| 524 | partial = ecfs_get_branch(inode, depth, offsets, chain, &err); | ||
| 525 | |||
| 526 | /* Simplest case - block found, no allocation needed */ | ||
| 527 | if (!partial) { | ||
| 528 | got_it: | ||
| 529 | bh_result->b_dev = inode->i_dev; | ||
| 530 | bh_result->b_blocknr = le32_to_cpu(chain[depth-1].key); | ||
| 531 | bh_result->b_state |= (1UL << BH_Mapped); | ||
| 532 | /* Clean up and exit */ | ||
| 533 | partial = chain+depth-1; /* the whole chain */ | ||
| 534 | goto cleanup; | ||
| 535 | } | ||
| 536 | |||
| 537 | /* Next simple case - plain lookup or failed read of indirect block */ | ||
| 538 | if (!create || err == -EIO) { | ||
| 539 | cleanup: | ||
| 540 | while (partial > chain) { | ||
| 541 | brelse(partial->bh); | ||
| 542 | partial--; | ||
| 543 | } | ||
| 544 | unlock_kernel(); | ||
| 545 | out: | ||
| 546 | return err; | ||
| 547 | } | ||
| 548 | |||
| 549 | /* | ||
| 550 | * Indirect block might be removed by truncate while we were | ||
| 551 | * reading it. Handling of that case (forget what we've got and | ||
| 552 | * reread) is taken out of the main path. | ||
| 553 | */ | ||
| 554 | if (err == -EAGAIN) | ||
| 555 | goto changed; | ||
| 556 | |||
| 557 | if (ecfs_find_goal(inode, iblock, chain, partial, &goal) < 0) | ||
| 558 | goto changed; | ||
| 559 | |||
| 560 | left = (chain + depth) - partial; | ||
| 561 | err = ecfs_alloc_branch(inode, left, goal, | ||
| 562 | offsets+(partial-chain), partial); | ||
| 563 | if (err) | ||
| 564 | goto cleanup; | ||
| 565 | |||
| 566 | if (ecfs_splice_branch(inode, iblock, chain, partial, left) < 0) | ||
| 567 | goto changed; | ||
| 568 | |||
| 569 | bh_result->b_state |= (1UL << BH_New); | ||
| 570 | goto got_it; | ||
| 571 | |||
| 572 | changed: | ||
| 573 | while (partial > chain) { | ||
| 574 | bforget(partial->bh); | ||
| 575 | partial--; | ||
| 576 | } | ||
| 577 | goto reread; | ||
| 578 | } | ||
| 579 | |||
| 580 | struct buffer_head * ecfs_getblk(struct inode * inode, long block, int create, int * err) | ||
| 581 | { | ||
| 582 | struct buffer_head dummy; | ||
| 583 | int error; | ||
| 584 | |||
| 585 | dummy.b_state = 0; | ||
| 586 | dummy.b_blocknr = -1000; | ||
| 587 | error = ecfs_get_block(inode, block, &dummy, create); | ||
| 588 | *err = error; | ||
| 589 | if (!error && buffer_mapped(&dummy)) { | ||
| 590 | struct buffer_head *bh; | ||
| 591 | bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); | ||
| 592 | |||
| 593 | if (buffer_new(&dummy)) { | ||
| 594 | if (!buffer_uptodate(bh)) | ||
| 595 | wait_on_buffer(bh); | ||
| 596 | memset(bh->b_data, 0, inode->i_sb->s_blocksize); | ||
| 597 | mark_buffer_uptodate(bh, 1); | ||
| 598 | mark_buffer_dirty_inode(bh, inode); | ||
| 599 | } | ||
| 600 | return bh; | ||
| 601 | } | ||
| 602 | return NULL; | ||
| 603 | } | ||
| 604 | |||
| 605 | struct buffer_head * ecfs_bread (struct inode * inode, int block, | ||
| 606 | int create, int *err) | ||
| 607 | { | ||
| 608 | struct buffer_head * bh; | ||
| 609 | int prev_blocks; | ||
| 610 | |||
| 611 | prev_blocks = inode->i_blocks; | ||
| 612 | |||
| 613 | bh = ecfs_getblk (inode, block, create, err); | ||
| 614 | if (!bh) | ||
| 615 | return bh; | ||
| 616 | |||
| 617 | /* | ||
| 618 | * If the inode has grown, and this is a directory, then perform | ||
| 619 | * preallocation of a few more blocks to try to keep directory | ||
| 620 | * fragmentation down. | ||
| 621 | */ | ||
| 622 | if (create && | ||
| 623 | S_ISDIR(inode->i_mode) && | ||
| 624 | inode->i_blocks > prev_blocks && | ||
| 625 | ECFS_HAS_COMPAT_FEATURE(inode->i_sb, | ||
| 626 | ECFS_FEATURE_COMPAT_DIR_PREALLOC)) { | ||
| 627 | int i; | ||
| 628 | struct buffer_head *tmp_bh; | ||
| 629 | |||
| 630 | for (i = 1; | ||
| 631 | i < ECFS_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks; | ||
| 632 | i++) { | ||
| 633 | /* | ||
| 634 | * ecfs_getblk will zero out the contents of the | ||
| 635 | * directory for us | ||
| 636 | */ | ||
| 637 | tmp_bh = ecfs_getblk(inode, block+i, create, err); | ||
| 638 | if (!tmp_bh) { | ||
| 639 | brelse (bh); | ||
| 640 | return 0; | ||
| 641 | } | ||
| 642 | brelse (tmp_bh); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | |||
| 646 | if (buffer_uptodate(bh)) | ||
| 647 | return bh; | ||
| 648 | ll_rw_block (READ, 1, &bh); | ||
| 649 | wait_on_buffer (bh); | ||
| 650 | if (buffer_uptodate(bh)) | ||
| 651 | return bh; | ||
| 652 | brelse (bh); | ||
| 653 | *err = -EIO; | ||
| 654 | return NULL; | ||
| 655 | } | ||
| 656 | |||
| 657 | static int ecfs_writepage(struct page *page) | ||
| 658 | { | ||
| 659 | return block_write_full_page(page,ecfs_get_block); | ||
| 660 | } | ||
| 661 | |||
| 662 | |||
| 663 | static int ecfs_readpage(struct file *file, struct page *page) | ||
| 664 | { | ||
| 665 | return block_read_full_page(page,ecfs_get_block); | ||
| 666 | |||
| 667 | /* | ||
| 668 | ptr = page->virtual; | ||
| 669 | |||
| 670 | #define PAGE_PLAIN 22 | ||
| 671 | |||
| 672 | printk("<1> ecfs_readpage() %d\n", from); | ||
| 673 | if (!ptr || !test_bit(PAGE_PLAIN, &page->flags)) | ||
| 674 | return r; | ||
| 675 | |||
| 676 | prepare_key(ecfs_key, strlen(ecfs_key), &rc4key); | ||
| 677 | rc4(ptr, PAGE_SIZE, &rc4key, from); | ||
| 678 | clear_bit(PAGE_PLAIN, &page->flags); | ||
| 679 | |||
| 680 | printk("<1>HHHH ecfs_readpage() %d\n", from); | ||
| 681 | |||
| 682 | return r; | ||
| 683 | */ | ||
| 684 | |||
| 685 | } | ||
| 686 | |||
| 687 | |||
| 688 | static int ecfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) | ||
| 689 | { | ||
| 690 | return block_prepare_write(page,from,to,ecfs_get_block); | ||
| 691 | } | ||
| 692 | |||
| 693 | |||
| 694 | static int ecfs_bmap(struct address_space *mapping, long block) | ||
| 695 | { | ||
| 696 | return generic_block_bmap(mapping,block,ecfs_get_block); | ||
| 697 | } | ||
| 698 | |||
| 699 | |||
| 700 | struct address_space_operations ecfs_aops = { | ||
| 701 | readpage: ecfs_readpage, | ||
| 702 | writepage: ecfs_writepage, | ||
| 703 | sync_page: block_sync_page, | ||
| 704 | prepare_write: ecfs_prepare_write, | ||
| 705 | commit_write: generic_commit_write, | ||
| 706 | bmap: ecfs_bmap | ||
| 707 | }; | ||
| 708 | |||
| 709 | /* | ||
| 710 | * Probably it should be a library function... search for first non-zero word | ||
| 711 | * or memcmp with zero_page, whatever is better for particular architecture. | ||
| 712 | * Linus? | ||
| 713 | */ | ||
| 714 | static inline int all_zeroes(u32 *p, u32 *q) | ||
| 715 | { | ||
| 716 | while (p < q) | ||
| 717 | if (*p++) | ||
| 718 | return 0; | ||
| 719 | return 1; | ||
| 720 | } | ||
| 721 | |||
| 722 | /** | ||
| 723 | * ecfs_find_shared - find the indirect blocks for partial truncation. | ||
| 724 | * @inode: inode in question | ||
| 725 | * @depth: depth of the affected branch | ||
| 726 | * @offsets: offsets of pointers in that branch (see ecfs_block_to_path) | ||
| 727 | * @chain: place to store the pointers to partial indirect blocks | ||
| 728 | * @top: place to the (detached) top of branch | ||
| 729 | * | ||
| 730 | * This is a helper function used by ecfs_truncate(). | ||
| 731 | * | ||
| 732 | * When we do truncate() we may have to clean the ends of several indirect | ||
| 733 | * blocks but leave the blocks themselves alive. Block is partially | ||
| 734 | * truncated if some data below the new i_size is refered from it (and | ||
| 735 | * it is on the path to the first completely truncated data block, indeed). | ||
| 736 | * We have to free the top of that path along with everything to the right | ||
| 737 | * of the path. Since no allocation past the truncation point is possible | ||
| 738 | * until ecfs_truncate() finishes, we may safely do the latter, but top | ||
| 739 | * of branch may require special attention - pageout below the truncation | ||
| 740 | * point might try to populate it. | ||
| 741 | * | ||
| 742 | * We atomically detach the top of branch from the tree, store the block | ||
| 743 | * number of its root in *@top, pointers to buffer_heads of partially | ||
| 744 | * truncated blocks - in @chain[].bh and pointers to their last elements | ||
| 745 | * that should not be removed - in @chain[].p. Return value is the pointer | ||
| 746 | * to last filled element of @chain. | ||
| 747 | * | ||
| 748 | * The work left to caller to do the actual freeing of subtrees: | ||
| 749 | * a) free the subtree starting from *@top | ||
| 750 | * b) free the subtrees whose roots are stored in | ||
| 751 | * (@chain[i].p+1 .. end of @chain[i].bh->b_data) | ||
| 752 | * c) free the subtrees growing from the inode past the @chain[0].p | ||
| 753 | * (no partially truncated stuff there). | ||
| 754 | */ | ||
| 755 | |||
| 756 | static Indirect *ecfs_find_shared(struct inode *inode, | ||
| 757 | int depth, | ||
| 758 | int offsets[4], | ||
| 759 | Indirect chain[4], | ||
| 760 | u32 *top) | ||
| 761 | { | ||
| 762 | Indirect *partial, *p; | ||
| 763 | int k, err; | ||
| 764 | |||
| 765 | *top = 0; | ||
| 766 | for (k = depth; k > 1 && !offsets[k-1]; k--) | ||
| 767 | ; | ||
| 768 | partial = ecfs_get_branch(inode, k, offsets, chain, &err); | ||
| 769 | /* Writer: pointers */ | ||
| 770 | if (!partial) | ||
| 771 | partial = chain + k-1; | ||
| 772 | /* | ||
| 773 | * If the branch acquired continuation since we've looked at it - | ||
| 774 | * fine, it should all survive and (new) top doesn't belong to us. | ||
| 775 | */ | ||
| 776 | if (!partial->key && *partial->p) | ||
| 777 | /* Writer: end */ | ||
| 778 | goto no_top; | ||
| 779 | for (p=partial; p>chain && all_zeroes((u32*)p->bh->b_data,p->p); p--) | ||
| 780 | ; | ||
| 781 | /* | ||
| 782 | * OK, we've found the last block that must survive. The rest of our | ||
| 783 | * branch should be detached before unlocking. However, if that rest | ||
| 784 | * of branch is all ours and does not grow immediately from the inode | ||
| 785 | * it's easier to cheat and just decrement partial->p. | ||
| 786 | */ | ||
| 787 | if (p == chain + k - 1 && p > chain) { | ||
| 788 | p->p--; | ||
| 789 | } else { | ||
| 790 | *top = *p->p; | ||
| 791 | *p->p = 0; | ||
| 792 | } | ||
| 793 | /* Writer: end */ | ||
| 794 | |||
| 795 | while(partial > p) | ||
| 796 | { | ||
| 797 | brelse(partial->bh); | ||
| 798 | partial--; | ||
| 799 | } | ||
| 800 | no_top: | ||
| 801 | return partial; | ||
| 802 | } | ||
| 803 | |||
| 804 | /** | ||
| 805 | * ecfs_free_data - free a list of data blocks | ||
| 806 | * @inode: inode we are dealing with | ||
| 807 | * @p: array of block numbers | ||
| 808 | * @q: points immediately past the end of array | ||
| 809 | * | ||
| 810 | * We are freeing all blocks refered from that array (numbers are | ||
| 811 | * stored as little-endian 32-bit) and updating @inode->i_blocks | ||
| 812 | * appropriately. | ||
| 813 | */ | ||
| 814 | static inline void ecfs_free_data(struct inode *inode, u32 *p, u32 *q) | ||
| 815 | { | ||
| 816 | int blocks = inode->i_sb->s_blocksize / 512; | ||
| 817 | unsigned long block_to_free = 0, count = 0; | ||
| 818 | unsigned long nr; | ||
| 819 | |||
| 820 | for ( ; p < q ; p++) { | ||
| 821 | nr = le32_to_cpu(*p); | ||
| 822 | if (nr) { | ||
| 823 | *p = 0; | ||
| 824 | /* accumulate blocks to free if they're contiguous */ | ||
| 825 | if (count == 0) | ||
| 826 | goto free_this; | ||
| 827 | else if (block_to_free == nr - count) | ||
| 828 | count++; | ||
| 829 | else { | ||
| 830 | /* Writer: ->i_blocks */ | ||
| 831 | inode->i_blocks -= blocks * count; | ||
| 832 | /* Writer: end */ | ||
| 833 | ecfs_free_blocks (inode, block_to_free, count); | ||
| 834 | mark_inode_dirty(inode); | ||
| 835 | free_this: | ||
| 836 | block_to_free = nr; | ||
| 837 | count = 1; | ||
| 838 | } | ||
| 839 | } | ||
| 840 | } | ||
| 841 | if (count > 0) { | ||
| 842 | /* Writer: ->i_blocks */ | ||
| 843 | inode->i_blocks -= blocks * count; | ||
| 844 | /* Writer: end */ | ||
| 845 | ecfs_free_blocks (inode, block_to_free, count); | ||
| 846 | mark_inode_dirty(inode); | ||
| 847 | } | ||
| 848 | } | ||
| 849 | |||
| 850 | /** | ||
| 851 | * ecfs_free_branches - free an array of branches | ||
| 852 | * @inode: inode we are dealing with | ||
| 853 | * @p: array of block numbers | ||
| 854 | * @q: pointer immediately past the end of array | ||
| 855 | * @depth: depth of the branches to free | ||
| 856 | * | ||
| 857 | * We are freeing all blocks refered from these branches (numbers are | ||
| 858 | * stored as little-endian 32-bit) and updating @inode->i_blocks | ||
| 859 | * appropriately. | ||
| 860 | */ | ||
| 861 | static void ecfs_free_branches(struct inode *inode, u32 *p, u32 *q, int depth) | ||
| 862 | { | ||
| 863 | struct buffer_head * bh; | ||
| 864 | unsigned long nr; | ||
| 865 | |||
| 866 | if (depth--) { | ||
| 867 | int addr_per_block = ECFS_ADDR_PER_BLOCK(inode->i_sb); | ||
| 868 | for ( ; p < q ; p++) { | ||
| 869 | nr = le32_to_cpu(*p); | ||
| 870 | if (!nr) | ||
| 871 | continue; | ||
| 872 | *p = 0; | ||
| 873 | bh = bread (inode->i_dev, nr, inode->i_sb->s_blocksize); | ||
| 874 | /* | ||
| 875 | * A read failure? Report error and clear slot | ||
| 876 | * (should be rare). | ||
| 877 | */ | ||
| 878 | if (!bh) { | ||
| 879 | ecfs_error(inode->i_sb, "ecfs_free_branches", | ||
| 880 | "Read failure, inode=%ld, block=%ld", | ||
| 881 | inode->i_ino, nr); | ||
| 882 | continue; | ||
| 883 | } | ||
| 884 | ecfs_free_branches(inode, | ||
| 885 | (u32*)bh->b_data, | ||
| 886 | (u32*)bh->b_data + addr_per_block, | ||
| 887 | depth); | ||
| 888 | bforget(bh); | ||
| 889 | /* Writer: ->i_blocks */ | ||
| 890 | inode->i_blocks -= inode->i_sb->s_blocksize / 512; | ||
| 891 | /* Writer: end */ | ||
| 892 | ecfs_free_blocks(inode, nr, 1); | ||
| 893 | mark_inode_dirty(inode); | ||
| 894 | } | ||
| 895 | } else | ||
| 896 | ecfs_free_data(inode, p, q); | ||
| 897 | } | ||
| 898 | |||
| 899 | void ecfs_truncate (struct inode * inode) | ||
| 900 | { | ||
| 901 | u32 *i_data = inode->u.ecfs_i.i_data; | ||
| 902 | int addr_per_block = ECFS_ADDR_PER_BLOCK(inode->i_sb); | ||
| 903 | int offsets[4]; | ||
| 904 | Indirect chain[4]; | ||
| 905 | Indirect *partial; | ||
| 906 | int nr = 0; | ||
| 907 | int n; | ||
| 908 | long iblock; | ||
| 909 | unsigned blocksize; | ||
| 910 | |||
| 911 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | ||
| 912 | S_ISLNK(inode->i_mode))) | ||
| 913 | return; | ||
| 914 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
| 915 | return; | ||
| 916 | |||
| 917 | ecfs_discard_prealloc(inode); | ||
| 918 | |||
| 919 | blocksize = inode->i_sb->s_blocksize; | ||
| 920 | iblock = (inode->i_size + blocksize-1) | ||
| 921 | >> ECFS_BLOCK_SIZE_BITS(inode->i_sb); | ||
| 922 | |||
| 923 | block_truncate_page(inode->i_mapping, inode->i_size, ecfs_get_block); | ||
| 924 | |||
| 925 | n = ecfs_block_to_path(inode, iblock, offsets); | ||
| 926 | if (n == 0) | ||
| 927 | return; | ||
| 928 | |||
| 929 | if (n == 1) { | ||
| 930 | ecfs_free_data(inode, i_data+offsets[0], | ||
| 931 | i_data + ECFS_NDIR_BLOCKS); | ||
| 932 | goto do_indirects; | ||
| 933 | } | ||
| 934 | |||
| 935 | partial = ecfs_find_shared(inode, n, offsets, chain, &nr); | ||
| 936 | /* Kill the top of shared branch (already detached) */ | ||
| 937 | if (nr) { | ||
| 938 | if (partial == chain) | ||
| 939 | mark_inode_dirty(inode); | ||
| 940 | else | ||
| 941 | mark_buffer_dirty_inode(partial->bh, inode); | ||
| 942 | ecfs_free_branches(inode, &nr, &nr+1, (chain+n-1) - partial); | ||
| 943 | } | ||
| 944 | /* Clear the ends of indirect blocks on the shared branch */ | ||
| 945 | while (partial > chain) { | ||
| 946 | ecfs_free_branches(inode, | ||
| 947 | partial->p + 1, | ||
| 948 | (u32*)partial->bh->b_data + addr_per_block, | ||
| 949 | (chain+n-1) - partial); | ||
| 950 | mark_buffer_dirty_inode(partial->bh, inode); | ||
| 951 | if (IS_SYNC(inode)) { | ||
| 952 | ll_rw_block (WRITE, 1, &partial->bh); | ||
| 953 | wait_on_buffer (partial->bh); | ||
| 954 | } | ||
| 955 | brelse (partial->bh); | ||
| 956 | partial--; | ||
| 957 | } | ||
| 958 | do_indirects: | ||
| 959 | /* Kill the remaining (whole) subtrees */ | ||
| 960 | switch (offsets[0]) { | ||
| 961 | default: | ||
| 962 | nr = i_data[ECFS_IND_BLOCK]; | ||
| 963 | if (nr) { | ||
| 964 | i_data[ECFS_IND_BLOCK] = 0; | ||
| 965 | mark_inode_dirty(inode); | ||
| 966 | ecfs_free_branches(inode, &nr, &nr+1, 1); | ||
| 967 | } | ||
| 968 | case ECFS_IND_BLOCK: | ||
| 969 | nr = i_data[ECFS_DIND_BLOCK]; | ||
| 970 | if (nr) { | ||
| 971 | i_data[ECFS_DIND_BLOCK] = 0; | ||
| 972 | mark_inode_dirty(inode); | ||
| 973 | ecfs_free_branches(inode, &nr, &nr+1, 2); | ||
| 974 | } | ||
| 975 | case ECFS_DIND_BLOCK: | ||
| 976 | nr = i_data[ECFS_TIND_BLOCK]; | ||
| 977 | if (nr) { | ||
| 978 | i_data[ECFS_TIND_BLOCK] = 0; | ||
| 979 | mark_inode_dirty(inode); | ||
| 980 | ecfs_free_branches(inode, &nr, &nr+1, 3); | ||
| 981 | } | ||
| 982 | case ECFS_TIND_BLOCK: | ||
| 983 | ; | ||
| 984 | } | ||
| 985 | inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
| 986 | if (IS_SYNC(inode)) | ||
| 987 | ecfs_sync_inode (inode); | ||
| 988 | else | ||
| 989 | mark_inode_dirty(inode); | ||
| 990 | } | ||
| 991 | |||
| 992 | void ecfs_read_inode (struct inode * inode) | ||
| 993 | { | ||
| 994 | struct buffer_head * bh; | ||
| 995 | struct ecfs_inode * raw_inode; | ||
| 996 | unsigned long block_group; | ||
| 997 | unsigned long group_desc; | ||
| 998 | unsigned long desc; | ||
| 999 | unsigned long block; | ||
| 1000 | unsigned long offset; | ||
| 1001 | struct ecfs_group_desc * gdp; | ||
| 1002 | |||
| 1003 | if ((inode->i_ino != ECFS_ROOT_INO && inode->i_ino != ECFS_ACL_IDX_INO && | ||
| 1004 | inode->i_ino != ECFS_ACL_DATA_INO && | ||
| 1005 | inode->i_ino < ECFS_FIRST_INO(inode->i_sb)) || | ||
| 1006 | inode->i_ino > le32_to_cpu(inode->i_sb->u.ecfs_sb.s_es->s_inodes_count)) { | ||
| 1007 | ecfs_error (inode->i_sb, "ecfs_read_inode", | ||
| 1008 | "bad inode number: %lu", inode->i_ino); | ||
| 1009 | goto bad_inode; | ||
| 1010 | } | ||
| 1011 | block_group = (inode->i_ino - 1) / ECFS_INODES_PER_GROUP(inode->i_sb); | ||
| 1012 | if (block_group >= inode->i_sb->u.ecfs_sb.s_groups_count) { | ||
| 1013 | ecfs_error (inode->i_sb, "ecfs_read_inode", | ||
| 1014 | "group >= groups count"); | ||
| 1015 | goto bad_inode; | ||
| 1016 | } | ||
| 1017 | group_desc = block_group >> ECFS_DESC_PER_BLOCK_BITS(inode->i_sb); | ||
| 1018 | desc = block_group & (ECFS_DESC_PER_BLOCK(inode->i_sb) - 1); | ||
| 1019 | bh = inode->i_sb->u.ecfs_sb.s_group_desc[group_desc]; | ||
| 1020 | if (!bh) { | ||
| 1021 | ecfs_error (inode->i_sb, "ecfs_read_inode", | ||
| 1022 | "Descriptor not loaded"); | ||
| 1023 | goto bad_inode; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | gdp = (struct ecfs_group_desc *) bh->b_data; | ||
| 1027 | /* | ||
| 1028 | * Figure out the offset within the block group inode table | ||
| 1029 | */ | ||
| 1030 | offset = ((inode->i_ino - 1) % ECFS_INODES_PER_GROUP(inode->i_sb)) * | ||
| 1031 | ECFS_INODE_SIZE(inode->i_sb); | ||
| 1032 | block = le32_to_cpu(gdp[desc].bg_inode_table) + | ||
| 1033 | (offset >> ECFS_BLOCK_SIZE_BITS(inode->i_sb)); | ||
| 1034 | if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) { | ||
| 1035 | ecfs_error (inode->i_sb, "ecfs_read_inode", | ||
| 1036 | "unable to read inode block - " | ||
| 1037 | "inode=%lu, block=%lu", inode->i_ino, block); | ||
| 1038 | goto bad_inode; | ||
| 1039 | } | ||
| 1040 | offset &= (ECFS_BLOCK_SIZE(inode->i_sb) - 1); | ||
| 1041 | raw_inode = (struct ecfs_inode *) (bh->b_data + offset); | ||
| 1042 | |||
| 1043 | inode->i_mode = le16_to_cpu(raw_inode->i_mode); | ||
| 1044 | inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); | ||
| 1045 | inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); | ||
| 1046 | if(!(test_opt (inode->i_sb, NO_UID32))) { | ||
| 1047 | inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16; | ||
| 1048 | inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16; | ||
| 1049 | } | ||
| 1050 | inode->i_nlink = le16_to_cpu(raw_inode->i_links_count); | ||
| 1051 | inode->i_size = le32_to_cpu(raw_inode->i_size); | ||
| 1052 | inode->i_atime = le32_to_cpu(raw_inode->i_atime); | ||
| 1053 | inode->i_ctime = le32_to_cpu(raw_inode->i_ctime); | ||
| 1054 | inode->i_mtime = le32_to_cpu(raw_inode->i_mtime); | ||
| 1055 | inode->u.ecfs_i.i_dtime = le32_to_cpu(raw_inode->i_dtime); | ||
| 1056 | /* We now have enough fields to check if the inode was active or not. | ||
| 1057 | * This is needed because nfsd might try to access dead inodes | ||
| 1058 | * the test is that same one that e2fsck uses | ||
| 1059 | * NeilBrown 1999oct15 | ||
| 1060 | */ | ||
| 1061 | if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ecfs_i.i_dtime)) { | ||
| 1062 | /* this inode is deleted */ | ||
| 1063 | brelse (bh); | ||
| 1064 | goto bad_inode; | ||
| 1065 | } | ||
| 1066 | inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ | ||
| 1067 | inode->i_blocks = le32_to_cpu(raw_inode->i_blocks); | ||
| 1068 | inode->i_version = ++event; | ||
| 1069 | inode->u.ecfs_i.i_flags = le32_to_cpu(raw_inode->i_flags); | ||
| 1070 | inode->u.ecfs_i.i_faddr = le32_to_cpu(raw_inode->i_faddr); | ||
| 1071 | inode->u.ecfs_i.i_frag_no = raw_inode->i_frag; | ||
| 1072 | inode->u.ecfs_i.i_frag_size = raw_inode->i_fsize; | ||
| 1073 | inode->u.ecfs_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl); | ||
| 1074 | if (S_ISDIR(inode->i_mode)) | ||
| 1075 | inode->u.ecfs_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); | ||
| 1076 | else { | ||
| 1077 | inode->u.ecfs_i.i_high_size = le32_to_cpu(raw_inode->i_size_high); | ||
| 1078 | inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32; | ||
| 1079 | } | ||
| 1080 | inode->i_generation = le32_to_cpu(raw_inode->i_generation); | ||
| 1081 | inode->u.ecfs_i.i_block_group = block_group; | ||
| 1082 | |||
| 1083 | /* | ||
| 1084 | * NOTE! The in-memory inode i_data array is in little-endian order | ||
| 1085 | * even on big-endian machines: we do NOT byteswap the block numbers! | ||
| 1086 | */ | ||
| 1087 | for (block = 0; block < ECFS_N_BLOCKS; block++) | ||
| 1088 | inode->u.ecfs_i.i_data[block] = raw_inode->i_block[block]; | ||
| 1089 | |||
| 1090 | if (inode->i_ino == ECFS_ACL_IDX_INO || | ||
| 1091 | inode->i_ino == ECFS_ACL_DATA_INO) | ||
| 1092 | /* Nothing to do */ ; | ||
| 1093 | else if (S_ISREG(inode->i_mode)) { | ||
| 1094 | inode->i_op = &ecfs_file_inode_operations; | ||
| 1095 | inode->i_fop = &ecfs_file_operations; | ||
| 1096 | inode->i_mapping->a_ops = &ecfs_aops; | ||
| 1097 | } else if (S_ISDIR(inode->i_mode)) { | ||
| 1098 | inode->i_op = &ecfs_dir_inode_operations; | ||
| 1099 | inode->i_fop = &ecfs_dir_operations; | ||
| 1100 | } else if (S_ISLNK(inode->i_mode)) { | ||
| 1101 | if (!inode->i_blocks) | ||
| 1102 | inode->i_op = &ecfs_fast_symlink_inode_operations; | ||
| 1103 | else { | ||
| 1104 | inode->i_op = &page_symlink_inode_operations; | ||
| 1105 | inode->i_mapping->a_ops = &ecfs_aops; | ||
| 1106 | } | ||
| 1107 | } else | ||
| 1108 | init_special_inode(inode, inode->i_mode, | ||
| 1109 | le32_to_cpu(raw_inode->i_block[0])); | ||
| 1110 | brelse (bh); | ||
| 1111 | inode->i_attr_flags = 0; | ||
| 1112 | if (inode->u.ecfs_i.i_flags & ECFS_SYNC_FL) { | ||
| 1113 | inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; | ||
| 1114 | inode->i_flags |= S_SYNC; | ||
| 1115 | } | ||
| 1116 | if (inode->u.ecfs_i.i_flags & ECFS_APPEND_FL) { | ||
| 1117 | inode->i_attr_flags |= ATTR_FLAG_APPEND; | ||
| 1118 | inode->i_flags |= S_APPEND; | ||
| 1119 | } | ||
| 1120 | if (inode->u.ecfs_i.i_flags & ECFS_IMMUTABLE_FL) { | ||
| 1121 | inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE; | ||
| 1122 | inode->i_flags |= S_IMMUTABLE; | ||
| 1123 | } | ||
| 1124 | if (inode->u.ecfs_i.i_flags & ECFS_NOATIME_FL) { | ||
| 1125 | inode->i_attr_flags |= ATTR_FLAG_NOATIME; | ||
| 1126 | inode->i_flags |= S_NOATIME; | ||
| 1127 | } | ||
| 1128 | return; | ||
| 1129 | |||
| 1130 | bad_inode: | ||
| 1131 | make_bad_inode(inode); | ||
| 1132 | return; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | static int ecfs_update_inode(struct inode * inode, int do_sync) | ||
| 1136 | { | ||
| 1137 | struct buffer_head * bh; | ||
| 1138 | struct ecfs_inode * raw_inode; | ||
| 1139 | unsigned long block_group; | ||
| 1140 | unsigned long group_desc; | ||
| 1141 | unsigned long desc; | ||
| 1142 | unsigned long block; | ||
| 1143 | unsigned long offset; | ||
| 1144 | int err = 0; | ||
| 1145 | struct ecfs_group_desc * gdp; | ||
| 1146 | |||
| 1147 | if ((inode->i_ino != ECFS_ROOT_INO && | ||
| 1148 | inode->i_ino < ECFS_FIRST_INO(inode->i_sb)) || | ||
| 1149 | inode->i_ino > le32_to_cpu(inode->i_sb->u.ecfs_sb.s_es->s_inodes_count)) { | ||
| 1150 | ecfs_error (inode->i_sb, "ecfs_write_inode", | ||
| 1151 | "bad inode number: %lu", inode->i_ino); | ||
| 1152 | return -EIO; | ||
| 1153 | } | ||
| 1154 | block_group = (inode->i_ino - 1) / ECFS_INODES_PER_GROUP(inode->i_sb); | ||
| 1155 | if (block_group >= inode->i_sb->u.ecfs_sb.s_groups_count) { | ||
| 1156 | ecfs_error (inode->i_sb, "ecfs_write_inode", | ||
| 1157 | "group >= groups count"); | ||
| 1158 | return -EIO; | ||
| 1159 | } | ||
| 1160 | group_desc = block_group >> ECFS_DESC_PER_BLOCK_BITS(inode->i_sb); | ||
| 1161 | desc = block_group & (ECFS_DESC_PER_BLOCK(inode->i_sb) - 1); | ||
| 1162 | bh = inode->i_sb->u.ecfs_sb.s_group_desc[group_desc]; | ||
| 1163 | if (!bh) { | ||
| 1164 | ecfs_error (inode->i_sb, "ecfs_write_inode", | ||
| 1165 | "Descriptor not loaded"); | ||
| 1166 | return -EIO; | ||
| 1167 | } | ||
| 1168 | gdp = (struct ecfs_group_desc *) bh->b_data; | ||
| 1169 | /* | ||
| 1170 | * Figure out the offset within the block group inode table | ||
| 1171 | */ | ||
| 1172 | offset = ((inode->i_ino - 1) % ECFS_INODES_PER_GROUP(inode->i_sb)) * | ||
| 1173 | ECFS_INODE_SIZE(inode->i_sb); | ||
| 1174 | block = le32_to_cpu(gdp[desc].bg_inode_table) + | ||
| 1175 | (offset >> ECFS_BLOCK_SIZE_BITS(inode->i_sb)); | ||
| 1176 | if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) { | ||
| 1177 | ecfs_error (inode->i_sb, "ecfs_write_inode", | ||
| 1178 | "unable to read inode block - " | ||
| 1179 | "inode=%lu, block=%lu", inode->i_ino, block); | ||
| 1180 | return -EIO; | ||
| 1181 | } | ||
| 1182 | offset &= ECFS_BLOCK_SIZE(inode->i_sb) - 1; | ||
| 1183 | raw_inode = (struct ecfs_inode *) (bh->b_data + offset); | ||
| 1184 | |||
| 1185 | raw_inode->i_mode = cpu_to_le16(inode->i_mode); | ||
| 1186 | if(!(test_opt(inode->i_sb, NO_UID32))) { | ||
| 1187 | raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid)); | ||
| 1188 | raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid)); | ||
| 1189 | /* | ||
| 1190 | * Fix up interoperability with old kernels. Otherwise, old inodes get | ||
| 1191 | * re-used with the upper 16 bits of the uid/gid intact | ||
| 1192 | */ | ||
| 1193 | if(!inode->u.ecfs_i.i_dtime) { | ||
| 1194 | raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid)); | ||
| 1195 | raw_inode->i_gid_high = cpu_to_le16(high_16_bits(inode->i_gid)); | ||
| 1196 | } else { | ||
| 1197 | raw_inode->i_uid_high = 0; | ||
| 1198 | raw_inode->i_gid_high = 0; | ||
| 1199 | } | ||
| 1200 | } else { | ||
| 1201 | raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(inode->i_uid)); | ||
| 1202 | raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(inode->i_gid)); | ||
| 1203 | raw_inode->i_uid_high = 0; | ||
| 1204 | raw_inode->i_gid_high = 0; | ||
| 1205 | } | ||
| 1206 | raw_inode->i_links_count = cpu_to_le16(inode->i_nlink); | ||
| 1207 | raw_inode->i_size = cpu_to_le32(inode->i_size); | ||
| 1208 | raw_inode->i_atime = cpu_to_le32(inode->i_atime); | ||
| 1209 | raw_inode->i_ctime = cpu_to_le32(inode->i_ctime); | ||
| 1210 | raw_inode->i_mtime = cpu_to_le32(inode->i_mtime); | ||
| 1211 | raw_inode->i_blocks = cpu_to_le32(inode->i_blocks); | ||
| 1212 | raw_inode->i_dtime = cpu_to_le32(inode->u.ecfs_i.i_dtime); | ||
| 1213 | raw_inode->i_flags = cpu_to_le32(inode->u.ecfs_i.i_flags); | ||
| 1214 | raw_inode->i_faddr = cpu_to_le32(inode->u.ecfs_i.i_faddr); | ||
| 1215 | raw_inode->i_frag = inode->u.ecfs_i.i_frag_no; | ||
| 1216 | raw_inode->i_fsize = inode->u.ecfs_i.i_frag_size; | ||
| 1217 | raw_inode->i_file_acl = cpu_to_le32(inode->u.ecfs_i.i_file_acl); | ||
| 1218 | if (S_ISDIR(inode->i_mode)) | ||
| 1219 | raw_inode->i_dir_acl = cpu_to_le32(inode->u.ecfs_i.i_dir_acl); | ||
| 1220 | else { | ||
| 1221 | raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32); | ||
| 1222 | if (raw_inode->i_size_high) { | ||
| 1223 | struct super_block *sb = inode->i_sb; | ||
| 1224 | if (!ECFS_HAS_RO_COMPAT_FEATURE(sb, | ||
| 1225 | ECFS_FEATURE_RO_COMPAT_LARGE_FILE) || | ||
| 1226 | ECFS_SB(sb)->s_es->s_rev_level == | ||
| 1227 | cpu_to_le32(ECFS_GOOD_OLD_REV)) { | ||
| 1228 | /* If this is the first large file | ||
| 1229 | * created, add a flag to the superblock. | ||
| 1230 | */ | ||
| 1231 | lock_kernel(); | ||
| 1232 | ecfs_update_dynamic_rev(sb); | ||
| 1233 | ECFS_SET_RO_COMPAT_FEATURE(sb, | ||
| 1234 | ECFS_FEATURE_RO_COMPAT_LARGE_FILE); | ||
| 1235 | unlock_kernel(); | ||
| 1236 | ecfs_write_super(sb); | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | raw_inode->i_generation = cpu_to_le32(inode->i_generation); | ||
| 1242 | if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) | ||
| 1243 | raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); | ||
| 1244 | else for (block = 0; block < ECFS_N_BLOCKS; block++) | ||
| 1245 | raw_inode->i_block[block] = inode->u.ecfs_i.i_data[block]; | ||
| 1246 | mark_buffer_dirty(bh); | ||
| 1247 | if (do_sync) { | ||
| 1248 | ll_rw_block (WRITE, 1, &bh); | ||
| 1249 | wait_on_buffer (bh); | ||
| 1250 | if (buffer_req(bh) && !buffer_uptodate(bh)) { | ||
| 1251 | printk ("IO error syncing ecfs inode [" | ||
| 1252 | "%s:%08lx]\n", | ||
| 1253 | bdevname(inode->i_dev), inode->i_ino); | ||
| 1254 | err = -EIO; | ||
| 1255 | } | ||
| 1256 | } | ||
| 1257 | brelse (bh); | ||
| 1258 | return err; | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | void ecfs_write_inode (struct inode * inode, int wait) | ||
| 1262 | { | ||
| 1263 | lock_kernel(); | ||
| 1264 | ecfs_update_inode (inode, wait); | ||
| 1265 | unlock_kernel(); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | int ecfs_sync_inode (struct inode *inode) | ||
| 1269 | { | ||
| 1270 | return ecfs_update_inode (inode, 1); | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | int ecfs_notify_change(struct dentry *dentry, struct iattr *iattr) | ||
| 1274 | { | ||
| 1275 | struct inode *inode = dentry->d_inode; | ||
| 1276 | int retval; | ||
| 1277 | unsigned int flags; | ||
| 1278 | |||
| 1279 | retval = -EPERM; | ||
| 1280 | if (iattr->ia_valid & ATTR_ATTR_FLAG && | ||
| 1281 | ((!(iattr->ia_attr_flags & ATTR_FLAG_APPEND) != | ||
| 1282 | !(inode->u.ecfs_i.i_flags & ECFS_APPEND_FL)) || | ||
| 1283 | (!(iattr->ia_attr_flags & ATTR_FLAG_IMMUTABLE) != | ||
| 1284 | !(inode->u.ecfs_i.i_flags & ECFS_IMMUTABLE_FL)))) { | ||
| 1285 | if (!capable(CAP_LINUX_IMMUTABLE)) | ||
| 1286 | goto out; | ||
| 1287 | } else if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||
| 1288 | goto out; | ||
| 1289 | |||
| 1290 | retval = inode_change_ok(inode, iattr); | ||
| 1291 | if (retval != 0) | ||
| 1292 | goto out; | ||
| 1293 | |||
| 1294 | inode_setattr(inode, iattr); | ||
| 1295 | |||
| 1296 | flags = iattr->ia_attr_flags; | ||
| 1297 | if (flags & ATTR_FLAG_SYNCRONOUS) { | ||
| 1298 | inode->i_flags |= S_SYNC; | ||
| 1299 | inode->u.ecfs_i.i_flags |= ECFS_SYNC_FL; | ||
| 1300 | } else { | ||
| 1301 | inode->i_flags &= ~S_SYNC; | ||
| 1302 | inode->u.ecfs_i.i_flags &= ~ECFS_SYNC_FL; | ||
| 1303 | } | ||
| 1304 | if (flags & ATTR_FLAG_NOATIME) { | ||
| 1305 | inode->i_flags |= S_NOATIME; | ||
| 1306 | inode->u.ecfs_i.i_flags |= ECFS_NOATIME_FL; | ||
| 1307 | } else { | ||
| 1308 | inode->i_flags &= ~S_NOATIME; | ||
| 1309 | inode->u.ecfs_i.i_flags &= ~ECFS_NOATIME_FL; | ||
| 1310 | } | ||
| 1311 | if (flags & ATTR_FLAG_APPEND) { | ||
| 1312 | inode->i_flags |= S_APPEND; | ||
| 1313 | inode->u.ecfs_i.i_flags |= ECFS_APPEND_FL; | ||
| 1314 | } else { | ||
| 1315 | inode->i_flags &= ~S_APPEND; | ||
| 1316 | inode->u.ecfs_i.i_flags &= ~ECFS_APPEND_FL; | ||
| 1317 | } | ||
| 1318 | if (flags & ATTR_FLAG_IMMUTABLE) { | ||
| 1319 | inode->i_flags |= S_IMMUTABLE; | ||
| 1320 | inode->u.ecfs_i.i_flags |= ECFS_IMMUTABLE_FL; | ||
| 1321 | } else { | ||
| 1322 | inode->i_flags &= ~S_IMMUTABLE; | ||
| 1323 | inode->u.ecfs_i.i_flags &= ~ECFS_IMMUTABLE_FL; | ||
| 1324 | } | ||
| 1325 | mark_inode_dirty(inode); | ||
| 1326 | out: | ||
| 1327 | return retval; | ||
| 1328 | } | ||
| 1329 | |||
diff --git a/other/ecfs/inode.o b/other/ecfs/inode.o new file mode 100644 index 0000000..8b5b3a8 --- /dev/null +++ b/other/ecfs/inode.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/ioctl.c b/other/ecfs/ioctl.c new file mode 100644 index 0000000..6fca75d --- /dev/null +++ b/other/ecfs/ioctl.c | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/ioctl.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/fs_ecfs.h> | ||
| 11 | #include <linux/ecfs_fs.h> | ||
| 12 | #include <linux/sched.h> | ||
| 13 | #include <asm/uaccess.h> | ||
| 14 | |||
| 15 | |||
| 16 | int ecfs_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, | ||
| 17 | unsigned long arg) | ||
| 18 | { | ||
| 19 | unsigned int flags; | ||
| 20 | |||
| 21 | ecfs_debug ("cmd = %u, arg = %lu\n", cmd, arg); | ||
| 22 | |||
| 23 | switch (cmd) { | ||
| 24 | case ECFS_IOC_GETFLAGS: | ||
| 25 | flags = inode->u.ecfs_i.i_flags & ECFS_FL_USER_VISIBLE; | ||
| 26 | return put_user(flags, (int *) arg); | ||
| 27 | case ECFS_IOC_SETFLAGS: { | ||
| 28 | unsigned int oldflags; | ||
| 29 | |||
| 30 | if (IS_RDONLY(inode)) | ||
| 31 | return -EROFS; | ||
| 32 | |||
| 33 | if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||
| 34 | return -EPERM; | ||
| 35 | |||
| 36 | if (get_user(flags, (int *) arg)) | ||
| 37 | return -EFAULT; | ||
| 38 | |||
| 39 | oldflags = inode->u.ecfs_i.i_flags; | ||
| 40 | |||
| 41 | /* | ||
| 42 | * The IMMUTABLE and APPEND_ONLY flags can only be changed by | ||
| 43 | * the relevant capability. | ||
| 44 | * | ||
| 45 | * This test looks nicer. Thanks to Pauline Middelink | ||
| 46 | */ | ||
| 47 | if ((flags ^ oldflags) & (ECFS_APPEND_FL | ECFS_IMMUTABLE_FL)) { | ||
| 48 | if (!capable(CAP_LINUX_IMMUTABLE)) | ||
| 49 | return -EPERM; | ||
| 50 | } | ||
| 51 | |||
| 52 | flags = flags & ECFS_FL_USER_MODIFIABLE; | ||
| 53 | flags |= oldflags & ~ECFS_FL_USER_MODIFIABLE; | ||
| 54 | inode->u.ecfs_i.i_flags = flags; | ||
| 55 | |||
| 56 | if (flags & ECFS_SYNC_FL) | ||
| 57 | inode->i_flags |= S_SYNC; | ||
| 58 | else | ||
| 59 | inode->i_flags &= ~S_SYNC; | ||
| 60 | if (flags & ECFS_APPEND_FL) | ||
| 61 | inode->i_flags |= S_APPEND; | ||
| 62 | else | ||
| 63 | inode->i_flags &= ~S_APPEND; | ||
| 64 | if (flags & ECFS_IMMUTABLE_FL) | ||
| 65 | inode->i_flags |= S_IMMUTABLE; | ||
| 66 | else | ||
| 67 | inode->i_flags &= ~S_IMMUTABLE; | ||
| 68 | if (flags & ECFS_NOATIME_FL) | ||
| 69 | inode->i_flags |= S_NOATIME; | ||
| 70 | else | ||
| 71 | inode->i_flags &= ~S_NOATIME; | ||
| 72 | inode->i_ctime = CURRENT_TIME; | ||
| 73 | mark_inode_dirty(inode); | ||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | case ECFS_IOC_GETVERSION: | ||
| 77 | return put_user(inode->i_generation, (int *) arg); | ||
| 78 | case ECFS_IOC_SETVERSION: | ||
| 79 | if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||
| 80 | return -EPERM; | ||
| 81 | if (IS_RDONLY(inode)) | ||
| 82 | return -EROFS; | ||
| 83 | if (get_user(inode->i_generation, (int *) arg)) | ||
| 84 | return -EFAULT; | ||
| 85 | inode->i_ctime = CURRENT_TIME; | ||
| 86 | mark_inode_dirty(inode); | ||
| 87 | return 0; | ||
| 88 | default: | ||
| 89 | return -ENOTTY; | ||
| 90 | } | ||
| 91 | } | ||
diff --git a/other/ecfs/ioctl.o b/other/ecfs/ioctl.o new file mode 100644 index 0000000..d55bf55 --- /dev/null +++ b/other/ecfs/ioctl.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/namei.c b/other/ecfs/namei.c new file mode 100644 index 0000000..c66e6c1 --- /dev/null +++ b/other/ecfs/namei.c | |||
| @@ -0,0 +1,824 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/namei.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | * | ||
| 9 | * from | ||
| 10 | * | ||
| 11 | * linux/fs/minix/namei.c | ||
| 12 | * | ||
| 13 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
| 14 | * | ||
| 15 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
| 16 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
| 17 | * Directory entry file type support and forward compatibility hooks | ||
| 18 | * for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998 | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/fs_ecfs.h> | ||
| 22 | #include <linux/ecfs_fs.h> | ||
| 23 | #include <linux/locks.h> | ||
| 24 | #include <linux/quotaops.h> | ||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | /* | ||
| 29 | * define how far ahead to read directories while searching them. | ||
| 30 | */ | ||
| 31 | #define NAMEI_RA_CHUNKS 2 | ||
| 32 | #define NAMEI_RA_BLOCKS 4 | ||
| 33 | #define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) | ||
| 34 | #define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) | ||
| 35 | |||
| 36 | /* | ||
| 37 | * NOTE! unlike strncmp, ecfs_match returns 1 for success, 0 for failure. | ||
| 38 | * | ||
| 39 | * `len <= ECFS_NAME_LEN' is guaranteed by caller. | ||
| 40 | * `de != NULL' is guaranteed by caller. | ||
| 41 | */ | ||
| 42 | static inline int ecfs_match (int len, const char * const name, | ||
| 43 | struct ecfs_dir_entry_2 * de) | ||
| 44 | { | ||
| 45 | if (len != de->name_len) | ||
| 46 | return 0; | ||
| 47 | if (!de->inode) | ||
| 48 | return 0; | ||
| 49 | return !memcmp(name, de->name, len); | ||
| 50 | } | ||
| 51 | |||
| 52 | /* | ||
| 53 | * ecfs_find_entry() | ||
| 54 | * | ||
| 55 | * finds an entry in the specified directory with the wanted name. It | ||
| 56 | * returns the cache buffer in which the entry was found, and the entry | ||
| 57 | * itself (as a parameter - res_dir). It does NOT read the inode of the | ||
| 58 | * entry - you'll have to do that yourself if you want to. | ||
| 59 | */ | ||
| 60 | static struct buffer_head * ecfs_find_entry (struct inode * dir, | ||
| 61 | const char * const name, int namelen, | ||
| 62 | struct ecfs_dir_entry_2 ** res_dir) | ||
| 63 | { | ||
| 64 | struct super_block * sb; | ||
| 65 | struct buffer_head * bh_use[NAMEI_RA_SIZE]; | ||
| 66 | struct buffer_head * bh_read[NAMEI_RA_SIZE]; | ||
| 67 | unsigned long offset; | ||
| 68 | int block, toread, i, err; | ||
| 69 | |||
| 70 | *res_dir = NULL; | ||
| 71 | sb = dir->i_sb; | ||
| 72 | |||
| 73 | if (namelen > ECFS_NAME_LEN) | ||
| 74 | return NULL; | ||
| 75 | |||
| 76 | memset (bh_use, 0, sizeof (bh_use)); | ||
| 77 | toread = 0; | ||
| 78 | for (block = 0; block < NAMEI_RA_SIZE; ++block) { | ||
| 79 | struct buffer_head * bh; | ||
| 80 | |||
| 81 | if ((block << ECFS_BLOCK_SIZE_BITS (sb)) >= dir->i_size) | ||
| 82 | break; | ||
| 83 | bh = ecfs_getblk (dir, block, 0, &err); | ||
| 84 | bh_use[block] = bh; | ||
| 85 | if (bh && !buffer_uptodate(bh)) | ||
| 86 | bh_read[toread++] = bh; | ||
| 87 | } | ||
| 88 | |||
| 89 | for (block = 0, offset = 0; offset < dir->i_size; block++) { | ||
| 90 | struct buffer_head * bh; | ||
| 91 | struct ecfs_dir_entry_2 * de; | ||
| 92 | char * dlimit; | ||
| 93 | |||
| 94 | if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { | ||
| 95 | ll_rw_block (READ, toread, bh_read); | ||
| 96 | toread = 0; | ||
| 97 | } | ||
| 98 | bh = bh_use[block % NAMEI_RA_SIZE]; | ||
| 99 | if (!bh) { | ||
| 100 | #if 0 | ||
| 101 | ecfs_error (sb, "ecfs_find_entry", | ||
| 102 | "directory #%lu contains a hole at offset %lu", | ||
| 103 | dir->i_ino, offset); | ||
| 104 | #endif | ||
| 105 | offset += sb->s_blocksize; | ||
| 106 | continue; | ||
| 107 | } | ||
| 108 | wait_on_buffer (bh); | ||
| 109 | if (!buffer_uptodate(bh)) { | ||
| 110 | /* | ||
| 111 | * read error: all bets are off | ||
| 112 | */ | ||
| 113 | break; | ||
| 114 | } | ||
| 115 | |||
| 116 | de = (struct ecfs_dir_entry_2 *) bh->b_data; | ||
| 117 | dlimit = bh->b_data + sb->s_blocksize; | ||
| 118 | while ((char *) de < dlimit) { | ||
| 119 | /* this code is executed quadratically often */ | ||
| 120 | /* do minimal checking `by hand' */ | ||
| 121 | int de_len; | ||
| 122 | |||
| 123 | if ((char *) de + namelen <= dlimit && | ||
| 124 | ecfs_match (namelen, name, de)) { | ||
| 125 | /* found a match - | ||
| 126 | just to be sure, do a full check */ | ||
| 127 | if (!ecfs_check_dir_entry("ecfs_find_entry", | ||
| 128 | dir, de, bh, offset)) | ||
| 129 | goto failure; | ||
| 130 | for (i = 0; i < NAMEI_RA_SIZE; ++i) { | ||
| 131 | if (bh_use[i] != bh) | ||
| 132 | brelse (bh_use[i]); | ||
| 133 | } | ||
| 134 | *res_dir = de; | ||
| 135 | return bh; | ||
| 136 | } | ||
| 137 | /* prevent looping on a bad block */ | ||
| 138 | de_len = le16_to_cpu(de->rec_len); | ||
| 139 | if (de_len <= 0) | ||
| 140 | goto failure; | ||
| 141 | offset += de_len; | ||
| 142 | de = (struct ecfs_dir_entry_2 *) | ||
| 143 | ((char *) de + de_len); | ||
| 144 | } | ||
| 145 | |||
| 146 | brelse (bh); | ||
| 147 | if (((block + NAMEI_RA_SIZE) << ECFS_BLOCK_SIZE_BITS (sb)) >= | ||
| 148 | dir->i_size) | ||
| 149 | bh = NULL; | ||
| 150 | else | ||
| 151 | bh = ecfs_getblk (dir, block + NAMEI_RA_SIZE, 0, &err); | ||
| 152 | bh_use[block % NAMEI_RA_SIZE] = bh; | ||
| 153 | if (bh && !buffer_uptodate(bh)) | ||
| 154 | bh_read[toread++] = bh; | ||
| 155 | } | ||
| 156 | |||
| 157 | failure: | ||
| 158 | for (i = 0; i < NAMEI_RA_SIZE; ++i) | ||
| 159 | brelse (bh_use[i]); | ||
| 160 | return NULL; | ||
| 161 | } | ||
| 162 | |||
| 163 | static struct dentry *ecfs_lookup(struct inode * dir, struct dentry *dentry) | ||
| 164 | { | ||
| 165 | struct inode * inode; | ||
| 166 | struct ecfs_dir_entry_2 * de; | ||
| 167 | struct buffer_head * bh; | ||
| 168 | |||
| 169 | if (dentry->d_name.len > ECFS_NAME_LEN) | ||
| 170 | return ERR_PTR(-ENAMETOOLONG); | ||
| 171 | |||
| 172 | bh = ecfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); | ||
| 173 | inode = NULL; | ||
| 174 | if (bh) { | ||
| 175 | unsigned long ino = le32_to_cpu(de->inode); | ||
| 176 | brelse (bh); | ||
| 177 | inode = iget(dir->i_sb, ino); | ||
| 178 | |||
| 179 | if (!inode) | ||
| 180 | return ERR_PTR(-EACCES); | ||
| 181 | } | ||
| 182 | d_add(dentry, inode); | ||
| 183 | return NULL; | ||
| 184 | } | ||
| 185 | |||
| 186 | #define S_SHIFT 12 | ||
| 187 | static unsigned char ecfs_type_by_mode[S_IFMT >> S_SHIFT] = { | ||
| 188 | [S_IFREG >> S_SHIFT] ECFS_FT_REG_FILE, | ||
| 189 | [S_IFDIR >> S_SHIFT] ECFS_FT_DIR, | ||
| 190 | [S_IFCHR >> S_SHIFT] ECFS_FT_CHRDEV, | ||
| 191 | [S_IFBLK >> S_SHIFT] ECFS_FT_BLKDEV, | ||
| 192 | [S_IFIFO >> S_SHIFT] ECFS_FT_FIFO, | ||
| 193 | [S_IFSOCK >> S_SHIFT] ECFS_FT_SOCK, | ||
| 194 | [S_IFLNK >> S_SHIFT] ECFS_FT_SYMLINK, | ||
| 195 | }; | ||
| 196 | |||
| 197 | static inline void ecfs_set_de_type(struct super_block *sb, | ||
| 198 | struct ecfs_dir_entry_2 *de, | ||
| 199 | umode_t mode) { | ||
| 200 | if (ECFS_HAS_INCOMPAT_FEATURE(sb, ECFS_FEATURE_INCOMPAT_FILETYPE)) | ||
| 201 | de->file_type = ecfs_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* | ||
| 205 | * ecfs_add_entry() | ||
| 206 | * | ||
| 207 | * adds a file entry to the specified directory. | ||
| 208 | */ | ||
| 209 | int ecfs_add_entry (struct inode * dir, const char * name, int namelen, | ||
| 210 | struct inode *inode) | ||
| 211 | { | ||
| 212 | unsigned long offset; | ||
| 213 | unsigned short rec_len; | ||
| 214 | struct buffer_head * bh; | ||
| 215 | struct ecfs_dir_entry_2 * de, * de1; | ||
| 216 | struct super_block * sb; | ||
| 217 | int retval; | ||
| 218 | |||
| 219 | sb = dir->i_sb; | ||
| 220 | |||
| 221 | if (!namelen) | ||
| 222 | return -EINVAL; | ||
| 223 | bh = ecfs_bread (dir, 0, 0, &retval); | ||
| 224 | if (!bh) | ||
| 225 | return retval; | ||
| 226 | rec_len = ECFS_DIR_REC_LEN(namelen); | ||
| 227 | offset = 0; | ||
| 228 | de = (struct ecfs_dir_entry_2 *) bh->b_data; | ||
| 229 | while (1) { | ||
| 230 | if ((char *)de >= sb->s_blocksize + bh->b_data) { | ||
| 231 | brelse (bh); | ||
| 232 | bh = NULL; | ||
| 233 | bh = ecfs_bread (dir, offset >> ECFS_BLOCK_SIZE_BITS(sb), 1, &retval); | ||
| 234 | if (!bh) | ||
| 235 | return retval; | ||
| 236 | if (dir->i_size <= offset) { | ||
| 237 | if (dir->i_size == 0) { | ||
| 238 | return -ENOENT; | ||
| 239 | } | ||
| 240 | |||
| 241 | ecfs_debug ("creating next block\n"); | ||
| 242 | |||
| 243 | de = (struct ecfs_dir_entry_2 *) bh->b_data; | ||
| 244 | de->inode = 0; | ||
| 245 | de->rec_len = le16_to_cpu(sb->s_blocksize); | ||
| 246 | dir->i_size = offset + sb->s_blocksize; | ||
| 247 | dir->u.ecfs_i.i_flags &= ~ECFS_BTREE_FL; | ||
| 248 | mark_inode_dirty(dir); | ||
| 249 | } else { | ||
| 250 | |||
| 251 | ecfs_debug ("skipping to next block\n"); | ||
| 252 | |||
| 253 | de = (struct ecfs_dir_entry_2 *) bh->b_data; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | if (!ecfs_check_dir_entry ("ecfs_add_entry", dir, de, bh, | ||
| 257 | offset)) { | ||
| 258 | brelse (bh); | ||
| 259 | return -ENOENT; | ||
| 260 | } | ||
| 261 | if (ecfs_match (namelen, name, de)) { | ||
| 262 | brelse (bh); | ||
| 263 | return -EEXIST; | ||
| 264 | } | ||
| 265 | if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) || | ||
| 266 | (le16_to_cpu(de->rec_len) >= ECFS_DIR_REC_LEN(de->name_len) + rec_len)) { | ||
| 267 | offset += le16_to_cpu(de->rec_len); | ||
| 268 | if (le32_to_cpu(de->inode)) { | ||
| 269 | de1 = (struct ecfs_dir_entry_2 *) ((char *) de + | ||
| 270 | ECFS_DIR_REC_LEN(de->name_len)); | ||
| 271 | de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) - | ||
| 272 | ECFS_DIR_REC_LEN(de->name_len)); | ||
| 273 | de->rec_len = cpu_to_le16(ECFS_DIR_REC_LEN(de->name_len)); | ||
| 274 | de = de1; | ||
| 275 | } | ||
| 276 | de->file_type = ECFS_FT_UNKNOWN; | ||
| 277 | if (inode) { | ||
| 278 | de->inode = cpu_to_le32(inode->i_ino); | ||
| 279 | ecfs_set_de_type(dir->i_sb, de, inode->i_mode); | ||
| 280 | } else | ||
| 281 | de->inode = 0; | ||
| 282 | de->name_len = namelen; | ||
| 283 | memcpy (de->name, name, namelen); | ||
| 284 | /* | ||
| 285 | * XXX shouldn't update any times until successful | ||
| 286 | * completion of syscall, but too many callers depend | ||
| 287 | * on this. | ||
| 288 | * | ||
| 289 | * XXX similarly, too many callers depend on | ||
| 290 | * ecfs_new_inode() setting the times, but error | ||
| 291 | * recovery deletes the inode, so the worst that can | ||
| 292 | * happen is that the times are slightly out of date | ||
| 293 | * and/or different from the directory change time. | ||
| 294 | */ | ||
| 295 | dir->i_mtime = dir->i_ctime = CURRENT_TIME; | ||
| 296 | dir->u.ecfs_i.i_flags &= ~ECFS_BTREE_FL; | ||
| 297 | mark_inode_dirty(dir); | ||
| 298 | dir->i_version = ++event; | ||
| 299 | mark_buffer_dirty_inode(bh, dir); | ||
| 300 | if (IS_SYNC(dir)) { | ||
| 301 | ll_rw_block (WRITE, 1, &bh); | ||
| 302 | wait_on_buffer (bh); | ||
| 303 | } | ||
| 304 | brelse(bh); | ||
| 305 | return 0; | ||
| 306 | } | ||
| 307 | offset += le16_to_cpu(de->rec_len); | ||
| 308 | de = (struct ecfs_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); | ||
| 309 | } | ||
| 310 | brelse (bh); | ||
| 311 | return -ENOSPC; | ||
| 312 | } | ||
| 313 | |||
| 314 | /* | ||
| 315 | * ecfs_delete_entry deletes a directory entry by merging it with the | ||
| 316 | * previous entry | ||
| 317 | */ | ||
| 318 | static int ecfs_delete_entry (struct inode * dir, | ||
| 319 | struct ecfs_dir_entry_2 * de_del, | ||
| 320 | struct buffer_head * bh) | ||
| 321 | { | ||
| 322 | struct ecfs_dir_entry_2 * de, * pde; | ||
| 323 | int i; | ||
| 324 | |||
| 325 | i = 0; | ||
| 326 | pde = NULL; | ||
| 327 | de = (struct ecfs_dir_entry_2 *) bh->b_data; | ||
| 328 | while (i < bh->b_size) { | ||
| 329 | if (!ecfs_check_dir_entry ("ecfs_delete_entry", NULL, | ||
| 330 | de, bh, i)) | ||
| 331 | return -EIO; | ||
| 332 | if (de == de_del) { | ||
| 333 | if (pde) | ||
| 334 | pde->rec_len = | ||
| 335 | cpu_to_le16(le16_to_cpu(pde->rec_len) + | ||
| 336 | le16_to_cpu(de->rec_len)); | ||
| 337 | else | ||
| 338 | de->inode = 0; | ||
| 339 | dir->i_version = ++event; | ||
| 340 | mark_buffer_dirty_inode(bh, dir); | ||
| 341 | if (IS_SYNC(dir)) { | ||
| 342 | ll_rw_block (WRITE, 1, &bh); | ||
| 343 | wait_on_buffer (bh); | ||
| 344 | } | ||
| 345 | return 0; | ||
| 346 | } | ||
| 347 | i += le16_to_cpu(de->rec_len); | ||
| 348 | pde = de; | ||
| 349 | de = (struct ecfs_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); | ||
| 350 | } | ||
| 351 | return -ENOENT; | ||
| 352 | } | ||
| 353 | |||
| 354 | /* | ||
| 355 | * By the time this is called, we already have created | ||
| 356 | * the directory cache entry for the new file, but it | ||
| 357 | * is so far negative - it has no inode. | ||
| 358 | * | ||
| 359 | * If the create succeeds, we fill in the inode information | ||
| 360 | * with d_instantiate(). | ||
| 361 | */ | ||
| 362 | static int ecfs_create (struct inode * dir, struct dentry * dentry, int mode) | ||
| 363 | { | ||
| 364 | struct inode * inode = ecfs_new_inode (dir, mode); | ||
| 365 | int err = PTR_ERR(inode); | ||
| 366 | if (IS_ERR(inode)) | ||
| 367 | return err; | ||
| 368 | |||
| 369 | inode->i_op = &ecfs_file_inode_operations; | ||
| 370 | inode->i_fop = &ecfs_file_operations; | ||
| 371 | inode->i_mapping->a_ops = &ecfs_aops; | ||
| 372 | inode->i_mode = mode; | ||
| 373 | mark_inode_dirty(inode); | ||
| 374 | err = ecfs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, | ||
| 375 | inode); | ||
| 376 | if (err) { | ||
| 377 | inode->i_nlink--; | ||
| 378 | mark_inode_dirty(inode); | ||
| 379 | iput (inode); | ||
| 380 | return err; | ||
| 381 | } | ||
| 382 | d_instantiate(dentry, inode); | ||
| 383 | return 0; | ||
| 384 | } | ||
| 385 | |||
| 386 | static int ecfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) | ||
| 387 | { | ||
| 388 | struct inode * inode = ecfs_new_inode (dir, mode); | ||
| 389 | int err = PTR_ERR(inode); | ||
| 390 | |||
| 391 | if (IS_ERR(inode)) | ||
| 392 | return err; | ||
| 393 | |||
| 394 | inode->i_uid = current->fsuid; | ||
| 395 | init_special_inode(inode, mode, rdev); | ||
| 396 | err = ecfs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, | ||
| 397 | inode); | ||
| 398 | if (err) | ||
| 399 | goto out_no_entry; | ||
| 400 | mark_inode_dirty(inode); | ||
| 401 | d_instantiate(dentry, inode); | ||
| 402 | return 0; | ||
| 403 | |||
| 404 | out_no_entry: | ||
| 405 | inode->i_nlink--; | ||
| 406 | mark_inode_dirty(inode); | ||
| 407 | iput(inode); | ||
| 408 | return err; | ||
| 409 | } | ||
| 410 | |||
| 411 | static int ecfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) | ||
| 412 | { | ||
| 413 | struct inode * inode; | ||
| 414 | struct buffer_head * dir_block; | ||
| 415 | struct ecfs_dir_entry_2 * de; | ||
| 416 | int err; | ||
| 417 | |||
| 418 | if (dir->i_nlink >= ECFS_LINK_MAX) | ||
| 419 | return -EMLINK; | ||
| 420 | |||
| 421 | inode = ecfs_new_inode (dir, S_IFDIR); | ||
| 422 | err = PTR_ERR(inode); | ||
| 423 | if (IS_ERR(inode)) | ||
| 424 | return err; | ||
| 425 | |||
| 426 | inode->i_op = &ecfs_dir_inode_operations; | ||
| 427 | inode->i_fop = &ecfs_dir_operations; | ||
| 428 | inode->i_size = inode->i_sb->s_blocksize; | ||
| 429 | inode->i_blocks = 0; | ||
| 430 | dir_block = ecfs_bread (inode, 0, 1, &err); | ||
| 431 | if (!dir_block) { | ||
| 432 | inode->i_nlink--; /* is this nlink == 0? */ | ||
| 433 | mark_inode_dirty(inode); | ||
| 434 | iput (inode); | ||
| 435 | return err; | ||
| 436 | } | ||
| 437 | de = (struct ecfs_dir_entry_2 *) dir_block->b_data; | ||
| 438 | de->inode = cpu_to_le32(inode->i_ino); | ||
| 439 | de->name_len = 1; | ||
| 440 | de->rec_len = cpu_to_le16(ECFS_DIR_REC_LEN(de->name_len)); | ||
| 441 | strcpy (de->name, "."); | ||
| 442 | ecfs_set_de_type(dir->i_sb, de, S_IFDIR); | ||
| 443 | de = (struct ecfs_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); | ||
| 444 | de->inode = cpu_to_le32(dir->i_ino); | ||
| 445 | de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - ECFS_DIR_REC_LEN(1)); | ||
| 446 | de->name_len = 2; | ||
| 447 | strcpy (de->name, ".."); | ||
| 448 | ecfs_set_de_type(dir->i_sb, de, S_IFDIR); | ||
| 449 | inode->i_nlink = 2; | ||
| 450 | mark_buffer_dirty_inode(dir_block, dir); | ||
| 451 | brelse (dir_block); | ||
| 452 | inode->i_mode = S_IFDIR | mode; | ||
| 453 | if (dir->i_mode & S_ISGID) | ||
| 454 | inode->i_mode |= S_ISGID; | ||
| 455 | mark_inode_dirty(inode); | ||
| 456 | err = ecfs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, | ||
| 457 | inode); | ||
| 458 | if (err) | ||
| 459 | goto out_no_entry; | ||
| 460 | dir->i_nlink++; | ||
| 461 | dir->u.ecfs_i.i_flags &= ~ECFS_BTREE_FL; | ||
| 462 | mark_inode_dirty(dir); | ||
| 463 | d_instantiate(dentry, inode); | ||
| 464 | return 0; | ||
| 465 | |||
| 466 | out_no_entry: | ||
| 467 | inode->i_nlink = 0; | ||
| 468 | mark_inode_dirty(inode); | ||
| 469 | iput (inode); | ||
| 470 | return err; | ||
| 471 | } | ||
| 472 | |||
| 473 | /* | ||
| 474 | * routine to check that the specified directory is empty (for rmdir) | ||
| 475 | */ | ||
| 476 | static int empty_dir (struct inode * inode) | ||
| 477 | { | ||
| 478 | unsigned long offset; | ||
| 479 | struct buffer_head * bh; | ||
| 480 | struct ecfs_dir_entry_2 * de, * de1; | ||
| 481 | struct super_block * sb; | ||
| 482 | int err; | ||
| 483 | |||
| 484 | sb = inode->i_sb; | ||
| 485 | if (inode->i_size < ECFS_DIR_REC_LEN(1) + ECFS_DIR_REC_LEN(2) || | ||
| 486 | !(bh = ecfs_bread (inode, 0, 0, &err))) { | ||
| 487 | ecfs_warning (inode->i_sb, "empty_dir", | ||
| 488 | "bad directory (dir #%lu) - no data block", | ||
| 489 | inode->i_ino); | ||
| 490 | return 1; | ||
| 491 | } | ||
| 492 | de = (struct ecfs_dir_entry_2 *) bh->b_data; | ||
| 493 | de1 = (struct ecfs_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); | ||
| 494 | if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) || | ||
| 495 | strcmp (".", de->name) || strcmp ("..", de1->name)) { | ||
| 496 | ecfs_warning (inode->i_sb, "empty_dir", | ||
| 497 | "bad directory (dir #%lu) - no `.' or `..'", | ||
| 498 | inode->i_ino); | ||
| 499 | brelse (bh); | ||
| 500 | return 1; | ||
| 501 | } | ||
| 502 | offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); | ||
| 503 | de = (struct ecfs_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len)); | ||
| 504 | while (offset < inode->i_size ) { | ||
| 505 | if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { | ||
| 506 | brelse (bh); | ||
| 507 | bh = ecfs_bread (inode, offset >> ECFS_BLOCK_SIZE_BITS(sb), 0, &err); | ||
| 508 | if (!bh) { | ||
| 509 | #if 0 | ||
| 510 | ecfs_error (sb, "empty_dir", | ||
| 511 | "directory #%lu contains a hole at offset %lu", | ||
| 512 | inode->i_ino, offset); | ||
| 513 | #endif | ||
| 514 | offset += sb->s_blocksize; | ||
| 515 | continue; | ||
| 516 | } | ||
| 517 | de = (struct ecfs_dir_entry_2 *) bh->b_data; | ||
| 518 | } | ||
| 519 | if (!ecfs_check_dir_entry ("empty_dir", inode, de, bh, | ||
| 520 | offset)) { | ||
| 521 | brelse (bh); | ||
| 522 | return 1; | ||
| 523 | } | ||
| 524 | if (le32_to_cpu(de->inode)) { | ||
| 525 | brelse (bh); | ||
| 526 | return 0; | ||
| 527 | } | ||
| 528 | offset += le16_to_cpu(de->rec_len); | ||
| 529 | de = (struct ecfs_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); | ||
| 530 | } | ||
| 531 | brelse (bh); | ||
| 532 | return 1; | ||
| 533 | } | ||
| 534 | |||
| 535 | static int ecfs_rmdir (struct inode * dir, struct dentry *dentry) | ||
| 536 | { | ||
| 537 | int retval; | ||
| 538 | struct inode * inode; | ||
| 539 | struct buffer_head * bh; | ||
| 540 | struct ecfs_dir_entry_2 * de; | ||
| 541 | |||
| 542 | retval = -ENOENT; | ||
| 543 | bh = ecfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); | ||
| 544 | if (!bh) | ||
| 545 | goto end_rmdir; | ||
| 546 | |||
| 547 | inode = dentry->d_inode; | ||
| 548 | DQUOT_INIT(inode); | ||
| 549 | |||
| 550 | retval = -EIO; | ||
| 551 | if (le32_to_cpu(de->inode) != inode->i_ino) | ||
| 552 | goto end_rmdir; | ||
| 553 | |||
| 554 | retval = -ENOTEMPTY; | ||
| 555 | if (!empty_dir (inode)) | ||
| 556 | goto end_rmdir; | ||
| 557 | |||
| 558 | retval = ecfs_delete_entry(dir, de, bh); | ||
| 559 | if (retval) | ||
| 560 | goto end_rmdir; | ||
| 561 | if (inode->i_nlink != 2) | ||
| 562 | ecfs_warning (inode->i_sb, "ecfs_rmdir", | ||
| 563 | "empty directory has nlink!=2 (%d)", | ||
| 564 | inode->i_nlink); | ||
| 565 | inode->i_version = ++event; | ||
| 566 | inode->i_nlink = 0; | ||
| 567 | inode->i_size = 0; | ||
| 568 | mark_inode_dirty(inode); | ||
| 569 | dir->i_nlink--; | ||
| 570 | inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; | ||
| 571 | dir->u.ecfs_i.i_flags &= ~ECFS_BTREE_FL; | ||
| 572 | mark_inode_dirty(dir); | ||
| 573 | |||
| 574 | end_rmdir: | ||
| 575 | brelse (bh); | ||
| 576 | return retval; | ||
| 577 | } | ||
| 578 | |||
| 579 | static int ecfs_unlink(struct inode * dir, struct dentry *dentry) | ||
| 580 | { | ||
| 581 | int retval; | ||
| 582 | struct inode * inode; | ||
| 583 | struct buffer_head * bh; | ||
| 584 | struct ecfs_dir_entry_2 * de; | ||
| 585 | |||
| 586 | retval = -ENOENT; | ||
| 587 | bh = ecfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); | ||
| 588 | if (!bh) | ||
| 589 | goto end_unlink; | ||
| 590 | |||
| 591 | inode = dentry->d_inode; | ||
| 592 | DQUOT_INIT(inode); | ||
| 593 | |||
| 594 | retval = -EIO; | ||
| 595 | if (le32_to_cpu(de->inode) != inode->i_ino) | ||
| 596 | goto end_unlink; | ||
| 597 | |||
| 598 | if (!inode->i_nlink) { | ||
| 599 | ecfs_warning (inode->i_sb, "ecfs_unlink", | ||
| 600 | "Deleting nonexistent file (%lu), %d", | ||
| 601 | inode->i_ino, inode->i_nlink); | ||
| 602 | inode->i_nlink = 1; | ||
| 603 | } | ||
| 604 | retval = ecfs_delete_entry(dir, de, bh); | ||
| 605 | if (retval) | ||
| 606 | goto end_unlink; | ||
| 607 | dir->i_ctime = dir->i_mtime = CURRENT_TIME; | ||
| 608 | dir->u.ecfs_i.i_flags &= ~ECFS_BTREE_FL; | ||
| 609 | mark_inode_dirty(dir); | ||
| 610 | inode->i_nlink--; | ||
| 611 | mark_inode_dirty(inode); | ||
| 612 | inode->i_ctime = dir->i_ctime; | ||
| 613 | retval = 0; | ||
| 614 | |||
| 615 | end_unlink: | ||
| 616 | brelse (bh); | ||
| 617 | return retval; | ||
| 618 | } | ||
| 619 | |||
| 620 | static int ecfs_symlink (struct inode * dir, struct dentry *dentry, const char * symname) | ||
| 621 | { | ||
| 622 | struct inode * inode; | ||
| 623 | int l, err; | ||
| 624 | |||
| 625 | l = strlen(symname)+1; | ||
| 626 | if (l > dir->i_sb->s_blocksize) | ||
| 627 | return -ENAMETOOLONG; | ||
| 628 | |||
| 629 | inode = ecfs_new_inode (dir, S_IFLNK); | ||
| 630 | err = PTR_ERR(inode); | ||
| 631 | if (IS_ERR(inode)) | ||
| 632 | return err; | ||
| 633 | |||
| 634 | inode->i_mode = S_IFLNK | S_IRWXUGO; | ||
| 635 | |||
| 636 | if (l > sizeof (inode->u.ecfs_i.i_data)) { | ||
| 637 | inode->i_op = &page_symlink_inode_operations; | ||
| 638 | inode->i_mapping->a_ops = &ecfs_aops; | ||
| 639 | err = block_symlink(inode, symname, l); | ||
| 640 | if (err) | ||
| 641 | goto out_no_entry; | ||
| 642 | } else { | ||
| 643 | inode->i_op = &ecfs_fast_symlink_inode_operations; | ||
| 644 | memcpy((char*)&inode->u.ecfs_i.i_data,symname,l); | ||
| 645 | inode->i_size = l-1; | ||
| 646 | } | ||
| 647 | mark_inode_dirty(inode); | ||
| 648 | |||
| 649 | err = ecfs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, | ||
| 650 | inode); | ||
| 651 | if (err) | ||
| 652 | goto out_no_entry; | ||
| 653 | d_instantiate(dentry, inode); | ||
| 654 | return 0; | ||
| 655 | |||
| 656 | out_no_entry: | ||
| 657 | inode->i_nlink--; | ||
| 658 | mark_inode_dirty(inode); | ||
| 659 | iput (inode); | ||
| 660 | return err; | ||
| 661 | } | ||
| 662 | |||
| 663 | static int ecfs_link (struct dentry * old_dentry, | ||
| 664 | struct inode * dir, struct dentry *dentry) | ||
| 665 | { | ||
| 666 | struct inode *inode = old_dentry->d_inode; | ||
| 667 | int err; | ||
| 668 | |||
| 669 | if (S_ISDIR(inode->i_mode)) | ||
| 670 | return -EPERM; | ||
| 671 | |||
| 672 | if (inode->i_nlink >= ECFS_LINK_MAX) | ||
| 673 | return -EMLINK; | ||
| 674 | |||
| 675 | err = ecfs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, | ||
| 676 | inode); | ||
| 677 | if (err) | ||
| 678 | return err; | ||
| 679 | |||
| 680 | inode->i_nlink++; | ||
| 681 | inode->i_ctime = CURRENT_TIME; | ||
| 682 | mark_inode_dirty(inode); | ||
| 683 | atomic_inc(&inode->i_count); | ||
| 684 | d_instantiate(dentry, inode); | ||
| 685 | return 0; | ||
| 686 | } | ||
| 687 | |||
| 688 | #define PARENT_INO(buffer) \ | ||
| 689 | ((struct ecfs_dir_entry_2 *) ((char *) buffer + \ | ||
| 690 | le16_to_cpu(((struct ecfs_dir_entry_2 *) buffer)->rec_len)))->inode | ||
| 691 | |||
| 692 | /* | ||
| 693 | * Anybody can rename anything with this: the permission checks are left to the | ||
| 694 | * higher-level routines. | ||
| 695 | */ | ||
| 696 | static int ecfs_rename (struct inode * old_dir, struct dentry *old_dentry, | ||
| 697 | struct inode * new_dir,struct dentry *new_dentry) | ||
| 698 | { | ||
| 699 | struct inode * old_inode, * new_inode; | ||
| 700 | struct buffer_head * old_bh, * new_bh, * dir_bh; | ||
| 701 | struct ecfs_dir_entry_2 * old_de, * new_de; | ||
| 702 | int retval; | ||
| 703 | |||
| 704 | old_bh = new_bh = dir_bh = NULL; | ||
| 705 | |||
| 706 | old_bh = ecfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); | ||
| 707 | /* | ||
| 708 | * Check for inode number is _not_ due to possible IO errors. | ||
| 709 | * We might rmdir the source, keep it as pwd of some process | ||
| 710 | * and merrily kill the link to whatever was created under the | ||
| 711 | * same name. Goodbye sticky bit ;-< | ||
| 712 | */ | ||
| 713 | old_inode = old_dentry->d_inode; | ||
| 714 | retval = -ENOENT; | ||
| 715 | if (!old_bh || le32_to_cpu(old_de->inode) != old_inode->i_ino) | ||
| 716 | goto end_rename; | ||
| 717 | |||
| 718 | new_inode = new_dentry->d_inode; | ||
| 719 | new_bh = ecfs_find_entry (new_dir, new_dentry->d_name.name, | ||
| 720 | new_dentry->d_name.len, &new_de); | ||
| 721 | if (new_bh) { | ||
| 722 | if (!new_inode) { | ||
| 723 | brelse (new_bh); | ||
| 724 | new_bh = NULL; | ||
| 725 | } else { | ||
| 726 | DQUOT_INIT(new_inode); | ||
| 727 | } | ||
| 728 | } | ||
| 729 | if (S_ISDIR(old_inode->i_mode)) { | ||
| 730 | if (new_inode) { | ||
| 731 | retval = -ENOTEMPTY; | ||
| 732 | if (!empty_dir (new_inode)) | ||
| 733 | goto end_rename; | ||
| 734 | } | ||
| 735 | retval = -EIO; | ||
| 736 | dir_bh = ecfs_bread (old_inode, 0, 0, &retval); | ||
| 737 | if (!dir_bh) | ||
| 738 | goto end_rename; | ||
| 739 | if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino) | ||
| 740 | goto end_rename; | ||
| 741 | retval = -EMLINK; | ||
| 742 | if (!new_inode && new_dir!=old_dir && | ||
| 743 | new_dir->i_nlink >= ECFS_LINK_MAX) | ||
| 744 | goto end_rename; | ||
| 745 | } | ||
| 746 | if (!new_bh) { | ||
| 747 | retval = ecfs_add_entry (new_dir, new_dentry->d_name.name, | ||
| 748 | new_dentry->d_name.len, | ||
| 749 | old_inode); | ||
| 750 | if (retval) | ||
| 751 | goto end_rename; | ||
| 752 | } else { | ||
| 753 | new_de->inode = le32_to_cpu(old_inode->i_ino); | ||
| 754 | if (ECFS_HAS_INCOMPAT_FEATURE(new_dir->i_sb, | ||
| 755 | ECFS_FEATURE_INCOMPAT_FILETYPE)) | ||
| 756 | new_de->file_type = old_de->file_type; | ||
| 757 | new_dir->i_version = ++event; | ||
| 758 | mark_buffer_dirty_inode(new_bh, new_dir); | ||
| 759 | if (IS_SYNC(new_dir)) { | ||
| 760 | ll_rw_block (WRITE, 1, &new_bh); | ||
| 761 | wait_on_buffer (new_bh); | ||
| 762 | } | ||
| 763 | brelse(new_bh); | ||
| 764 | new_bh = NULL; | ||
| 765 | } | ||
| 766 | |||
| 767 | /* | ||
| 768 | * Like most other Unix systems, set the ctime for inodes on a | ||
| 769 | * rename. | ||
| 770 | */ | ||
| 771 | old_inode->i_ctime = CURRENT_TIME; | ||
| 772 | mark_inode_dirty(old_inode); | ||
| 773 | |||
| 774 | /* | ||
| 775 | * ok, that's it | ||
| 776 | */ | ||
| 777 | ecfs_delete_entry(old_dir, old_de, old_bh); | ||
| 778 | |||
| 779 | if (new_inode) { | ||
| 780 | new_inode->i_nlink--; | ||
| 781 | new_inode->i_ctime = CURRENT_TIME; | ||
| 782 | mark_inode_dirty(new_inode); | ||
| 783 | } | ||
| 784 | old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; | ||
| 785 | old_dir->u.ecfs_i.i_flags &= ~ECFS_BTREE_FL; | ||
| 786 | mark_inode_dirty(old_dir); | ||
| 787 | if (dir_bh) { | ||
| 788 | PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); | ||
| 789 | mark_buffer_dirty_inode(dir_bh, old_inode); | ||
| 790 | old_dir->i_nlink--; | ||
| 791 | mark_inode_dirty(old_dir); | ||
| 792 | if (new_inode) { | ||
| 793 | new_inode->i_nlink--; | ||
| 794 | mark_inode_dirty(new_inode); | ||
| 795 | } else { | ||
| 796 | new_dir->i_nlink++; | ||
| 797 | new_dir->u.ecfs_i.i_flags &= ~ECFS_BTREE_FL; | ||
| 798 | mark_inode_dirty(new_dir); | ||
| 799 | } | ||
| 800 | } | ||
| 801 | |||
| 802 | retval = 0; | ||
| 803 | |||
| 804 | end_rename: | ||
| 805 | brelse (dir_bh); | ||
| 806 | brelse (old_bh); | ||
| 807 | brelse (new_bh); | ||
| 808 | return retval; | ||
| 809 | } | ||
| 810 | |||
| 811 | /* | ||
| 812 | * directories can handle most operations... | ||
| 813 | */ | ||
| 814 | struct inode_operations ecfs_dir_inode_operations = { | ||
| 815 | create: ecfs_create, | ||
| 816 | lookup: ecfs_lookup, | ||
| 817 | link: ecfs_link, | ||
| 818 | unlink: ecfs_unlink, | ||
| 819 | symlink: ecfs_symlink, | ||
| 820 | mkdir: ecfs_mkdir, | ||
| 821 | rmdir: ecfs_rmdir, | ||
| 822 | mknod: ecfs_mknod, | ||
| 823 | rename: ecfs_rename, | ||
| 824 | }; | ||
diff --git a/other/ecfs/namei.o b/other/ecfs/namei.o new file mode 100644 index 0000000..c567acd --- /dev/null +++ b/other/ecfs/namei.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/rc4.c b/other/ecfs/rc4.c new file mode 100644 index 0000000..a922b75 --- /dev/null +++ b/other/ecfs/rc4.c | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #include <linux/types.h> | ||
| 2 | #include "rc4.h" | ||
| 3 | |||
| 4 | |||
| 5 | /* originally implemented by <unknown> */ | ||
| 6 | void | ||
| 7 | prepare_key(unsigned char *key_data_ptr, size_t key_data_len, rc4_key *key) | ||
| 8 | { | ||
| 9 | unsigned char index1; | ||
| 10 | unsigned char index2; | ||
| 11 | unsigned char* state; | ||
| 12 | short counter; | ||
| 13 | |||
| 14 | state = &key->state[0]; | ||
| 15 | for(counter = 0; counter < 256; counter++) | ||
| 16 | state[counter] = counter; | ||
| 17 | key->x = 0; | ||
| 18 | key->y = 0; | ||
| 19 | index1 = 0; | ||
| 20 | index2 = 0; | ||
| 21 | for (counter = 0; counter < 256; counter++) { | ||
| 22 | index2 = (key_data_ptr[index1] + state[counter] + | ||
| 23 | index2) % 256; | ||
| 24 | my_swap_byte(&state[counter], &state[index2]); | ||
| 25 | |||
| 26 | index1 = (index1 + 1) % key_data_len; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | |||
| 31 | void | ||
| 32 | rc4(unsigned char *buffer_ptr, size_t buffer_len, rc4_key *key, unsigned int from) | ||
| 33 | { | ||
| 34 | unsigned char x; | ||
| 35 | unsigned char y; | ||
| 36 | unsigned char *state; | ||
| 37 | unsigned char xorIndex; | ||
| 38 | unsigned int counter; | ||
| 39 | unsigned int j; | ||
| 40 | |||
| 41 | x = key->x; | ||
| 42 | y = key->y; | ||
| 43 | |||
| 44 | state = &key->state[0]; | ||
| 45 | |||
| 46 | for (counter = 0; counter < from; ++counter) { | ||
| 47 | x = (x + 1) % 256; | ||
| 48 | y = (state[x] + y) % 256; | ||
| 49 | my_swap_byte(&state[x], &state[y]); | ||
| 50 | } | ||
| 51 | |||
| 52 | |||
| 53 | for (j = 0; j < buffer_len; ++j) { | ||
| 54 | x = (x + 1) % 256; | ||
| 55 | y = (state[x] + y) % 256; | ||
| 56 | my_swap_byte(&state[x], &state[y]); | ||
| 57 | |||
| 58 | xorIndex = (state[x] + state[y]) % 256; | ||
| 59 | buffer_ptr[j] ^= state[xorIndex]; | ||
| 60 | } | ||
| 61 | |||
| 62 | key->x = x; | ||
| 63 | key->y = y; | ||
| 64 | } | ||
| 65 | |||
| 66 | |||
| 67 | void my_swap_byte(unsigned char *a, unsigned char *b) | ||
| 68 | { | ||
| 69 | unsigned char swapByte; | ||
| 70 | |||
| 71 | swapByte = *a; | ||
| 72 | *a = *b; | ||
| 73 | *b = swapByte; | ||
| 74 | } | ||
| 75 | |||
| 76 | |||
diff --git a/other/ecfs/rc4.h b/other/ecfs/rc4.h new file mode 100644 index 0000000..7915285 --- /dev/null +++ b/other/ecfs/rc4.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #ifndef __rc4_h__ | ||
| 2 | #define __rc4_h__ | ||
| 3 | |||
| 4 | typedef struct { | ||
| 5 | unsigned char state[256]; | ||
| 6 | unsigned char x; | ||
| 7 | unsigned char y; | ||
| 8 | } rc4_key; | ||
| 9 | |||
| 10 | typedef struct { | ||
| 11 | unsigned char *ptr; | ||
| 12 | int stream_len; | ||
| 13 | } rc4_stream; | ||
| 14 | |||
| 15 | void prepare_key(unsigned char *, unsigned int, rc4_key *); | ||
| 16 | void rc4(unsigned char *, unsigned int, rc4_key *, unsigned int); | ||
| 17 | void my_swap_byte(unsigned char *,unsigned char *); | ||
| 18 | |||
| 19 | #endif | ||
| 20 | |||
diff --git a/other/ecfs/rc4.o b/other/ecfs/rc4.o new file mode 100644 index 0000000..9144b8c --- /dev/null +++ b/other/ecfs/rc4.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/repl.pl b/other/ecfs/repl.pl new file mode 100644 index 0000000..46e5b5b --- /dev/null +++ b/other/ecfs/repl.pl | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | #!/usr/bin/perl | ||
| 2 | |||
| 3 | # Written to transform ext2 functions and structs | ||
| 4 | # to ecfs. | ||
| 5 | # | ||
| 6 | |||
| 7 | my $f= shift; | ||
| 8 | open I, "<$f" or die "$!"; | ||
| 9 | open O, ">$f.new" or die "$!"; | ||
| 10 | |||
| 11 | while (<I>) { | ||
| 12 | s/ext2/ecfs/g; | ||
| 13 | s/EXT2/ECFS/g; | ||
| 14 | |||
| 15 | s/const// if (/ecfs_free_blocks/); | ||
| 16 | s/const// if (/ecfs_new_block/); | ||
| 17 | |||
| 18 | print O; | ||
| 19 | } | ||
| 20 | |||
| 21 | close O; | ||
| 22 | close I; | ||
| 23 | |||
| 24 | |||
| 25 | unlink $f; | ||
| 26 | rename "$f.new", $f; | ||
diff --git a/other/ecfs/start-ecfs b/other/ecfs/start-ecfs new file mode 100644 index 0000000..1fc39ab --- /dev/null +++ b/other/ecfs/start-ecfs | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #!/usr/bin/perl | ||
| 2 | |||
| 3 | use Term::ReadKey; | ||
| 4 | |||
| 5 | print "This program is used to read in the key for ECFS\n". | ||
| 6 | "This avoids that your key shows up in .history-file\n". | ||
| 7 | "since ECFS is started like 'insmod ecfs_key=blah'\n\n". | ||
| 8 | "You are now prompted for the key. Mount ECFS partitions like\n". | ||
| 9 | "'mount -t ecfs /dev/fd0 /mnt' afterwards. Remember that mmap()\n". | ||
| 10 | "is not supported by ECFS.\n\n". | ||
| 11 | "Also make sure ecfs.o is in apropriate /lib/modules/... location.\n\n"; | ||
| 12 | |||
| 13 | $p1 = "X"; | ||
| 14 | $p2 = "Y"; | ||
| 15 | |||
| 16 | ReadMode('noecho'); | ||
| 17 | while ($p1 ne $p2) { | ||
| 18 | print "Key: "; | ||
| 19 | $p1 = ReadLine(0); | ||
| 20 | print "\n(again): "; | ||
| 21 | $p2 = ReadLine(0); | ||
| 22 | print "\n"; | ||
| 23 | chop($p1); chop($p2); | ||
| 24 | } | ||
| 25 | |||
| 26 | ReadMode('normal'); | ||
| 27 | |||
| 28 | exec "insmod", "ecfs", "ecfs_key=$p1"; | ||
| 29 | |||
diff --git a/other/ecfs/super.c b/other/ecfs/super.c new file mode 100644 index 0000000..abd8fb5 --- /dev/null +++ b/other/ecfs/super.c | |||
| @@ -0,0 +1,817 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/super.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 5 | * Remy Card (card@masi.ibp.fr) | ||
| 6 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 7 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 8 | * | ||
| 9 | * from | ||
| 10 | * | ||
| 11 | * linux/fs/minix/inode.c | ||
| 12 | * | ||
| 13 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
| 14 | * | ||
| 15 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
| 16 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/config.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/string.h> | ||
| 22 | #include <linux/fs_ecfs.h> | ||
| 23 | #include <linux/ecfs_fs.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/locks.h> | ||
| 27 | #include <asm/uaccess.h> | ||
| 28 | |||
| 29 | #include <linux/blkdev.h> | ||
| 30 | |||
| 31 | extern unsigned char *ecfs_key; | ||
| 32 | |||
| 33 | static char error_buf[1024]; | ||
| 34 | |||
| 35 | void ecfs_error (struct super_block * sb, const char * function, | ||
| 36 | const char * fmt, ...) | ||
| 37 | { | ||
| 38 | va_list args; | ||
| 39 | |||
| 40 | if (!(sb->s_flags & MS_RDONLY)) { | ||
| 41 | sb->u.ecfs_sb.s_mount_state |= ECFS_ERROR_FS; | ||
| 42 | sb->u.ecfs_sb.s_es->s_state = | ||
| 43 | cpu_to_le16(le16_to_cpu(sb->u.ecfs_sb.s_es->s_state) | ECFS_ERROR_FS); | ||
| 44 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 45 | sb->s_dirt = 1; | ||
| 46 | } | ||
| 47 | va_start (args, fmt); | ||
| 48 | vsprintf (error_buf, fmt, args); | ||
| 49 | va_end (args); | ||
| 50 | if (test_opt (sb, ERRORS_PANIC) || | ||
| 51 | (le16_to_cpu(sb->u.ecfs_sb.s_es->s_errors) == ECFS_ERRORS_PANIC && | ||
| 52 | !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO))) | ||
| 53 | panic ("ECFS-fs panic (device %s): %s: %s\n", | ||
| 54 | bdevname(sb->s_dev), function, error_buf); | ||
| 55 | printk (KERN_CRIT "ECFS-fs error (device %s): %s: %s\n", | ||
| 56 | bdevname(sb->s_dev), function, error_buf); | ||
| 57 | if (test_opt (sb, ERRORS_RO) || | ||
| 58 | (le16_to_cpu(sb->u.ecfs_sb.s_es->s_errors) == ECFS_ERRORS_RO && | ||
| 59 | !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) { | ||
| 60 | printk ("Remounting filesystem read-only\n"); | ||
| 61 | sb->s_flags |= MS_RDONLY; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | NORET_TYPE void ecfs_panic (struct super_block * sb, const char * function, | ||
| 66 | const char * fmt, ...) | ||
| 67 | { | ||
| 68 | va_list args; | ||
| 69 | |||
| 70 | if (!(sb->s_flags & MS_RDONLY)) { | ||
| 71 | sb->u.ecfs_sb.s_mount_state |= ECFS_ERROR_FS; | ||
| 72 | sb->u.ecfs_sb.s_es->s_state = | ||
| 73 | cpu_to_le16(le16_to_cpu(sb->u.ecfs_sb.s_es->s_state) | ECFS_ERROR_FS); | ||
| 74 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 75 | sb->s_dirt = 1; | ||
| 76 | } | ||
| 77 | va_start (args, fmt); | ||
| 78 | vsprintf (error_buf, fmt, args); | ||
| 79 | va_end (args); | ||
| 80 | /* this is to prevent panic from syncing this filesystem */ | ||
| 81 | #if 0 | ||
| 82 | if (sb->s_lock) | ||
| 83 | sb->s_lock=0; | ||
| 84 | #endif | ||
| 85 | sb->s_flags |= MS_RDONLY; | ||
| 86 | panic ("ECFS-fs panic (device %s): %s: %s\n", | ||
| 87 | bdevname(sb->s_dev), function, error_buf); | ||
| 88 | } | ||
| 89 | |||
| 90 | void ecfs_warning (struct super_block * sb, const char * function, | ||
| 91 | const char * fmt, ...) | ||
| 92 | { | ||
| 93 | va_list args; | ||
| 94 | |||
| 95 | va_start (args, fmt); | ||
| 96 | vsprintf (error_buf, fmt, args); | ||
| 97 | va_end (args); | ||
| 98 | printk (KERN_WARNING "ECFS-fs warning (device %s): %s: %s\n", | ||
| 99 | bdevname(sb->s_dev), function, error_buf); | ||
| 100 | } | ||
| 101 | |||
| 102 | void ecfs_update_dynamic_rev(struct super_block *sb) | ||
| 103 | { | ||
| 104 | struct ecfs_super_block *es = ECFS_SB(sb)->s_es; | ||
| 105 | |||
| 106 | if (le32_to_cpu(es->s_rev_level) > ECFS_GOOD_OLD_REV) | ||
| 107 | return; | ||
| 108 | |||
| 109 | ecfs_warning(sb, __FUNCTION__, | ||
| 110 | "updating to rev %d because of new feature flag, " | ||
| 111 | "running e2fsck is recommended", | ||
| 112 | ECFS_DYNAMIC_REV); | ||
| 113 | |||
| 114 | es->s_first_ino = cpu_to_le32(ECFS_GOOD_OLD_FIRST_INO); | ||
| 115 | es->s_inode_size = cpu_to_le16(ECFS_GOOD_OLD_INODE_SIZE); | ||
| 116 | es->s_rev_level = cpu_to_le32(ECFS_DYNAMIC_REV); | ||
| 117 | /* leave es->s_feature_*compat flags alone */ | ||
| 118 | /* es->s_uuid will be set by e2fsck if empty */ | ||
| 119 | |||
| 120 | /* | ||
| 121 | * The rest of the superblock fields should be zero, and if not it | ||
| 122 | * means they are likely already in use, so leave them alone. We | ||
| 123 | * can leave it up to e2fsck to clean up any inconsistencies there. | ||
| 124 | */ | ||
| 125 | } | ||
| 126 | |||
| 127 | void ecfs_put_super (struct super_block * sb) | ||
| 128 | { | ||
| 129 | int db_count; | ||
| 130 | int i; | ||
| 131 | |||
| 132 | if (!(sb->s_flags & MS_RDONLY)) { | ||
| 133 | sb->u.ecfs_sb.s_es->s_state = le16_to_cpu(sb->u.ecfs_sb.s_mount_state); | ||
| 134 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 135 | } | ||
| 136 | db_count = ECFS_SB(sb)->s_gdb_count; | ||
| 137 | for (i = 0; i < db_count; i++) | ||
| 138 | if (sb->u.ecfs_sb.s_group_desc[i]) | ||
| 139 | brelse (sb->u.ecfs_sb.s_group_desc[i]); | ||
| 140 | kfree(sb->u.ecfs_sb.s_group_desc); | ||
| 141 | for (i = 0; i < ECFS_MAX_GROUP_LOADED; i++) | ||
| 142 | if (sb->u.ecfs_sb.s_inode_bitmap[i]) | ||
| 143 | brelse (sb->u.ecfs_sb.s_inode_bitmap[i]); | ||
| 144 | for (i = 0; i < ECFS_MAX_GROUP_LOADED; i++) | ||
| 145 | if (sb->u.ecfs_sb.s_block_bitmap[i]) | ||
| 146 | brelse (sb->u.ecfs_sb.s_block_bitmap[i]); | ||
| 147 | brelse (sb->u.ecfs_sb.s_sbh); | ||
| 148 | |||
| 149 | return; | ||
| 150 | } | ||
| 151 | |||
| 152 | static struct super_operations ecfs_sops = { | ||
| 153 | read_inode: ecfs_read_inode, | ||
| 154 | write_inode: ecfs_write_inode, | ||
| 155 | put_inode: ecfs_put_inode, | ||
| 156 | delete_inode: ecfs_delete_inode, | ||
| 157 | put_super: ecfs_put_super, | ||
| 158 | write_super: ecfs_write_super, | ||
| 159 | statfs: ecfs_statfs, | ||
| 160 | remount_fs: ecfs_remount, | ||
| 161 | }; | ||
| 162 | |||
| 163 | /* | ||
| 164 | * This function has been shamelessly adapted from the msdos fs | ||
| 165 | */ | ||
| 166 | static int parse_options (char * options, unsigned long * sb_block, | ||
| 167 | unsigned short *resuid, unsigned short * resgid, | ||
| 168 | unsigned long * mount_options) | ||
| 169 | { | ||
| 170 | char * this_char; | ||
| 171 | char * value; | ||
| 172 | |||
| 173 | if (!options) | ||
| 174 | return 1; | ||
| 175 | for (this_char = strtok (options, ","); | ||
| 176 | this_char != NULL; | ||
| 177 | this_char = strtok (NULL, ",")) { | ||
| 178 | if ((value = strchr (this_char, '=')) != NULL) | ||
| 179 | *value++ = 0; | ||
| 180 | if (!strcmp (this_char, "bsddf")) | ||
| 181 | clear_opt (*mount_options, MINIX_DF); | ||
| 182 | else if (!strcmp (this_char, "nouid32")) { | ||
| 183 | set_opt (*mount_options, NO_UID32); | ||
| 184 | } | ||
| 185 | else if (!strcmp (this_char, "check")) { | ||
| 186 | if (!value || !*value || !strcmp (value, "none")) | ||
| 187 | clear_opt (*mount_options, CHECK); | ||
| 188 | else | ||
| 189 | #ifdef CONFIG_ECFS_CHECK | ||
| 190 | set_opt (*mount_options, CHECK); | ||
| 191 | #else | ||
| 192 | printk("ECFS Check option not supported\n"); | ||
| 193 | #endif | ||
| 194 | } | ||
| 195 | else if (!strcmp (this_char, "debug")) | ||
| 196 | set_opt (*mount_options, DEBUG); | ||
| 197 | else if (!strcmp (this_char, "errors")) { | ||
| 198 | if (!value || !*value) { | ||
| 199 | printk ("ECFS-fs: the errors option requires " | ||
| 200 | "an argument\n"); | ||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | if (!strcmp (value, "continue")) { | ||
| 204 | clear_opt (*mount_options, ERRORS_RO); | ||
| 205 | clear_opt (*mount_options, ERRORS_PANIC); | ||
| 206 | set_opt (*mount_options, ERRORS_CONT); | ||
| 207 | } | ||
| 208 | else if (!strcmp (value, "remount-ro")) { | ||
| 209 | clear_opt (*mount_options, ERRORS_CONT); | ||
| 210 | clear_opt (*mount_options, ERRORS_PANIC); | ||
| 211 | set_opt (*mount_options, ERRORS_RO); | ||
| 212 | } | ||
| 213 | else if (!strcmp (value, "panic")) { | ||
| 214 | clear_opt (*mount_options, ERRORS_CONT); | ||
| 215 | clear_opt (*mount_options, ERRORS_RO); | ||
| 216 | set_opt (*mount_options, ERRORS_PANIC); | ||
| 217 | } | ||
| 218 | else { | ||
| 219 | printk ("ECFS-fs: Invalid errors option: %s\n", | ||
| 220 | value); | ||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | else if (!strcmp (this_char, "grpid") || | ||
| 225 | !strcmp (this_char, "bsdgroups")) | ||
| 226 | set_opt (*mount_options, GRPID); | ||
| 227 | else if (!strcmp (this_char, "minixdf")) | ||
| 228 | set_opt (*mount_options, MINIX_DF); | ||
| 229 | else if (!strcmp (this_char, "nocheck")) | ||
| 230 | clear_opt (*mount_options, CHECK); | ||
| 231 | else if (!strcmp (this_char, "nogrpid") || | ||
| 232 | !strcmp (this_char, "sysvgroups")) | ||
| 233 | clear_opt (*mount_options, GRPID); | ||
| 234 | else if (!strcmp (this_char, "resgid")) { | ||
| 235 | if (!value || !*value) { | ||
| 236 | printk ("ECFS-fs: the resgid option requires " | ||
| 237 | "an argument\n"); | ||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | *resgid = simple_strtoul (value, &value, 0); | ||
| 241 | if (*value) { | ||
| 242 | printk ("ECFS-fs: Invalid resgid option: %s\n", | ||
| 243 | value); | ||
| 244 | return 0; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | else if (!strcmp (this_char, "resuid")) { | ||
| 248 | if (!value || !*value) { | ||
| 249 | printk ("ECFS-fs: the resuid option requires " | ||
| 250 | "an argument"); | ||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | *resuid = simple_strtoul (value, &value, 0); | ||
| 254 | if (*value) { | ||
| 255 | printk ("ECFS-fs: Invalid resuid option: %s\n", | ||
| 256 | value); | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | else if (!strcmp (this_char, "sb")) { | ||
| 261 | if (!value || !*value) { | ||
| 262 | printk ("ECFS-fs: the sb option requires " | ||
| 263 | "an argument"); | ||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | *sb_block = simple_strtoul (value, &value, 0); | ||
| 267 | if (*value) { | ||
| 268 | printk ("ECFS-fs: Invalid sb option: %s\n", | ||
| 269 | value); | ||
| 270 | return 0; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | /* Silently ignore the quota options */ | ||
| 274 | else if (!strcmp (this_char, "grpquota") | ||
| 275 | || !strcmp (this_char, "noquota") | ||
| 276 | || !strcmp (this_char, "quota") | ||
| 277 | || !strcmp (this_char, "usrquota")) | ||
| 278 | /* Don't do anything ;-) */ ; | ||
| 279 | else { | ||
| 280 | printk ("ECFS-fs: Unrecognized mount option %s\n", this_char); | ||
| 281 | return 0; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | return 1; | ||
| 285 | } | ||
| 286 | |||
| 287 | static int ecfs_setup_super (struct super_block * sb, | ||
| 288 | struct ecfs_super_block * es, | ||
| 289 | int read_only) | ||
| 290 | { | ||
| 291 | int res = 0; | ||
| 292 | if (le32_to_cpu(es->s_rev_level) > ECFS_MAX_SUPP_REV) { | ||
| 293 | printk ("ECFS-fs warning: revision level too high, " | ||
| 294 | "forcing read-only mode\n"); | ||
| 295 | res = MS_RDONLY; | ||
| 296 | } | ||
| 297 | if (read_only) | ||
| 298 | return res; | ||
| 299 | if (!(sb->u.ecfs_sb.s_mount_state & ECFS_VALID_FS)) | ||
| 300 | printk ("ECFS-fs warning: mounting unchecked fs, " | ||
| 301 | "running e2fsck is recommended\n"); | ||
| 302 | else if ((sb->u.ecfs_sb.s_mount_state & ECFS_ERROR_FS)) | ||
| 303 | printk ("ECFS-fs warning: mounting fs with errors, " | ||
| 304 | "running e2fsck is recommended\n"); | ||
| 305 | else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 && | ||
| 306 | le16_to_cpu(es->s_mnt_count) >= | ||
| 307 | (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count)) | ||
| 308 | printk ("ECFS-fs warning: maximal mount count reached, " | ||
| 309 | "running e2fsck is recommended\n"); | ||
| 310 | else if (le32_to_cpu(es->s_checkinterval) && | ||
| 311 | (le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= CURRENT_TIME)) | ||
| 312 | printk ("ECFS-fs warning: checktime reached, " | ||
| 313 | "running e2fsck is recommended\n"); | ||
| 314 | es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~ECFS_VALID_FS); | ||
| 315 | if (!(__s16) le16_to_cpu(es->s_max_mnt_count)) | ||
| 316 | es->s_max_mnt_count = (__s16) cpu_to_le16(ECFS_DFL_MAX_MNT_COUNT); | ||
| 317 | es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1); | ||
| 318 | es->s_mtime = cpu_to_le32(CURRENT_TIME); | ||
| 319 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 320 | sb->s_dirt = 1; | ||
| 321 | if (test_opt (sb, DEBUG)) | ||
| 322 | printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, " | ||
| 323 | "bpg=%lu, ipg=%lu, mo=%04lx]\n", | ||
| 324 | ECFSFS_VERSION, ECFSFS_DATE, sb->s_blocksize, | ||
| 325 | sb->u.ecfs_sb.s_frag_size, | ||
| 326 | sb->u.ecfs_sb.s_groups_count, | ||
| 327 | ECFS_BLOCKS_PER_GROUP(sb), | ||
| 328 | ECFS_INODES_PER_GROUP(sb), | ||
| 329 | sb->u.ecfs_sb.s_mount_opt); | ||
| 330 | #ifdef CONFIG_ECFS_CHECK | ||
| 331 | if (test_opt (sb, CHECK)) { | ||
| 332 | ecfs_check_blocks_bitmap (sb); | ||
| 333 | ecfs_check_inodes_bitmap (sb); | ||
| 334 | } | ||
| 335 | #endif | ||
| 336 | return res; | ||
| 337 | } | ||
| 338 | |||
| 339 | static int ecfs_check_descriptors (struct super_block * sb) | ||
| 340 | { | ||
| 341 | int i; | ||
| 342 | int desc_block = 0; | ||
| 343 | unsigned long block = le32_to_cpu(sb->u.ecfs_sb.s_es->s_first_data_block); | ||
| 344 | struct ecfs_group_desc * gdp = NULL; | ||
| 345 | |||
| 346 | ecfs_debug ("Checking group descriptors"); | ||
| 347 | |||
| 348 | for (i = 0; i < sb->u.ecfs_sb.s_groups_count; i++) | ||
| 349 | { | ||
| 350 | if ((i % ECFS_DESC_PER_BLOCK(sb)) == 0) | ||
| 351 | gdp = (struct ecfs_group_desc *) sb->u.ecfs_sb.s_group_desc[desc_block++]->b_data; | ||
| 352 | if (le32_to_cpu(gdp->bg_block_bitmap) < block || | ||
| 353 | le32_to_cpu(gdp->bg_block_bitmap) >= block + ECFS_BLOCKS_PER_GROUP(sb)) | ||
| 354 | { | ||
| 355 | ecfs_error (sb, "ecfs_check_descriptors", | ||
| 356 | "Block bitmap for group %d" | ||
| 357 | " not in group (block %lu)!", | ||
| 358 | i, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap)); | ||
| 359 | return 0; | ||
| 360 | } | ||
| 361 | if (le32_to_cpu(gdp->bg_inode_bitmap) < block || | ||
| 362 | le32_to_cpu(gdp->bg_inode_bitmap) >= block + ECFS_BLOCKS_PER_GROUP(sb)) | ||
| 363 | { | ||
| 364 | ecfs_error (sb, "ecfs_check_descriptors", | ||
| 365 | "Inode bitmap for group %d" | ||
| 366 | " not in group (block %lu)!", | ||
| 367 | i, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap)); | ||
| 368 | return 0; | ||
| 369 | } | ||
| 370 | if (le32_to_cpu(gdp->bg_inode_table) < block || | ||
| 371 | le32_to_cpu(gdp->bg_inode_table) + sb->u.ecfs_sb.s_itb_per_group >= | ||
| 372 | block + ECFS_BLOCKS_PER_GROUP(sb)) | ||
| 373 | { | ||
| 374 | ecfs_error (sb, "ecfs_check_descriptors", | ||
| 375 | "Inode table for group %d" | ||
| 376 | " not in group (block %lu)!", | ||
| 377 | i, (unsigned long) le32_to_cpu(gdp->bg_inode_table)); | ||
| 378 | return 0; | ||
| 379 | } | ||
| 380 | block += ECFS_BLOCKS_PER_GROUP(sb); | ||
| 381 | gdp++; | ||
| 382 | } | ||
| 383 | return 1; | ||
| 384 | } | ||
| 385 | |||
| 386 | #define log2(n) ffz(~(n)) | ||
| 387 | |||
| 388 | struct super_block * ecfs_read_super (struct super_block * sb, void * data, | ||
| 389 | int silent) | ||
| 390 | { | ||
| 391 | struct buffer_head * bh; | ||
| 392 | struct ecfs_super_block * es; | ||
| 393 | unsigned long sb_block = 1; | ||
| 394 | unsigned short resuid = ECFS_DEF_RESUID; | ||
| 395 | unsigned short resgid = ECFS_DEF_RESGID; | ||
| 396 | unsigned long logic_sb_block = 1; | ||
| 397 | unsigned long offset = 0; | ||
| 398 | kdev_t dev = sb->s_dev; | ||
| 399 | int blocksize = BLOCK_SIZE; | ||
| 400 | int hblock; | ||
| 401 | int db_count; | ||
| 402 | int i, j; | ||
| 403 | |||
| 404 | |||
| 405 | |||
| 406 | |||
| 407 | /* | ||
| 408 | * See what the current blocksize for the device is, and | ||
| 409 | * use that as the blocksize. Otherwise (or if the blocksize | ||
| 410 | * is smaller than the default) use the default. | ||
| 411 | * This is important for devices that have a hardware | ||
| 412 | * sectorsize that is larger than the default. | ||
| 413 | */ | ||
| 414 | // blocksize = get_hardblocksize(dev); | ||
| 415 | blocksize = get_hardsect_size(dev); | ||
| 416 | if( blocksize == 0 || blocksize < BLOCK_SIZE ) | ||
| 417 | { | ||
| 418 | blocksize = BLOCK_SIZE; | ||
| 419 | } | ||
| 420 | |||
| 421 | sb->u.ecfs_sb.s_mount_opt = 0; | ||
| 422 | if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, | ||
| 423 | &sb->u.ecfs_sb.s_mount_opt)) { | ||
| 424 | return NULL; | ||
| 425 | } | ||
| 426 | |||
| 427 | set_blocksize (dev, blocksize); | ||
| 428 | |||
| 429 | /* | ||
| 430 | * If the superblock doesn't start on a sector boundary, | ||
| 431 | * calculate the offset. FIXME(eric) this doesn't make sense | ||
| 432 | * that we would have to do this. | ||
| 433 | */ | ||
| 434 | if (blocksize != BLOCK_SIZE) { | ||
| 435 | logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; | ||
| 436 | offset = (sb_block*BLOCK_SIZE) % blocksize; | ||
| 437 | } | ||
| 438 | |||
| 439 | if (!(bh = bread (dev, logic_sb_block, blocksize))) { | ||
| 440 | printk ("ECFS-fs: unable to read superblock\n"); | ||
| 441 | return NULL; | ||
| 442 | } | ||
| 443 | /* | ||
| 444 | * Note: s_es must be initialized s_es as soon as possible because | ||
| 445 | * some ecfs macro-instructions depend on its value | ||
| 446 | */ | ||
| 447 | es = (struct ecfs_super_block *) (((char *)bh->b_data) + offset); | ||
| 448 | sb->u.ecfs_sb.s_es = es; | ||
| 449 | sb->s_magic = le16_to_cpu(es->s_magic); | ||
| 450 | if (sb->s_magic != ECFS_SUPER_MAGIC) { | ||
| 451 | if (!silent) | ||
| 452 | printk ("VFS: Can't find an ecfs filesystem on dev " | ||
| 453 | "%s.\n", bdevname(dev)); | ||
| 454 | failed_mount: | ||
| 455 | if (bh) | ||
| 456 | brelse(bh); | ||
| 457 | return NULL; | ||
| 458 | } | ||
| 459 | if (le32_to_cpu(es->s_rev_level) == ECFS_GOOD_OLD_REV && | ||
| 460 | (ECFS_HAS_COMPAT_FEATURE(sb, ~0U) || | ||
| 461 | ECFS_HAS_RO_COMPAT_FEATURE(sb, ~0U) || | ||
| 462 | ECFS_HAS_INCOMPAT_FEATURE(sb, ~0U))) | ||
| 463 | printk("ECFS-fs warning: feature flags set on rev 0 fs, " | ||
| 464 | "running e2fsck is recommended\n"); | ||
| 465 | /* | ||
| 466 | * Check feature flags regardless of the revision level, since we | ||
| 467 | * previously didn't change the revision level when setting the flags, | ||
| 468 | * so there is a chance incompat flags are set on a rev 0 filesystem. | ||
| 469 | */ | ||
| 470 | if ((i = ECFS_HAS_INCOMPAT_FEATURE(sb, ~ECFS_FEATURE_INCOMPAT_SUPP))) { | ||
| 471 | printk("ECFS-fs: %s: couldn't mount because of " | ||
| 472 | "unsupported optional features (%x).\n", | ||
| 473 | bdevname(dev), i); | ||
| 474 | goto failed_mount; | ||
| 475 | } | ||
| 476 | if (!(sb->s_flags & MS_RDONLY) && | ||
| 477 | (i = ECFS_HAS_RO_COMPAT_FEATURE(sb, ~ECFS_FEATURE_RO_COMPAT_SUPP))){ | ||
| 478 | printk("ECFS-fs: %s: couldn't mount RDWR because of " | ||
| 479 | "unsupported optional features (%x).\n", | ||
| 480 | bdevname(dev), i); | ||
| 481 | goto failed_mount; | ||
| 482 | } | ||
| 483 | sb->s_blocksize_bits = | ||
| 484 | le32_to_cpu(ECFS_SB(sb)->s_es->s_log_block_size) + 10; | ||
| 485 | sb->s_blocksize = 1 << sb->s_blocksize_bits; | ||
| 486 | if (sb->s_blocksize != BLOCK_SIZE && | ||
| 487 | (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || | ||
| 488 | sb->s_blocksize == 4096)) { | ||
| 489 | /* | ||
| 490 | * Make sure the blocksize for the filesystem is larger | ||
| 491 | * than the hardware sectorsize for the machine. | ||
| 492 | */ | ||
| 493 | // hblock = get_hardblocksize(dev); | ||
| 494 | hblock = get_hardsect_size(dev); | ||
| 495 | if( (hblock != 0) | ||
| 496 | && (sb->s_blocksize < hblock) ) | ||
| 497 | { | ||
| 498 | printk("ECFS-fs: blocksize too small for device.\n"); | ||
| 499 | goto failed_mount; | ||
| 500 | } | ||
| 501 | |||
| 502 | brelse (bh); | ||
| 503 | set_blocksize (dev, sb->s_blocksize); | ||
| 504 | logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize; | ||
| 505 | offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize; | ||
| 506 | bh = bread (dev, logic_sb_block, sb->s_blocksize); | ||
| 507 | if(!bh) { | ||
| 508 | printk("ECFS-fs: Couldn't read superblock on " | ||
| 509 | "2nd try.\n"); | ||
| 510 | goto failed_mount; | ||
| 511 | } | ||
| 512 | es = (struct ecfs_super_block *) (((char *)bh->b_data) + offset); | ||
| 513 | sb->u.ecfs_sb.s_es = es; | ||
| 514 | if (es->s_magic != le16_to_cpu(ECFS_SUPER_MAGIC)) { | ||
| 515 | printk ("ECFS-fs: Magic mismatch, very weird !\n"); | ||
| 516 | goto failed_mount; | ||
| 517 | } | ||
| 518 | } | ||
| 519 | if (le32_to_cpu(es->s_rev_level) == ECFS_GOOD_OLD_REV) { | ||
| 520 | sb->u.ecfs_sb.s_inode_size = ECFS_GOOD_OLD_INODE_SIZE; | ||
| 521 | sb->u.ecfs_sb.s_first_ino = ECFS_GOOD_OLD_FIRST_INO; | ||
| 522 | } else { | ||
| 523 | sb->u.ecfs_sb.s_inode_size = le16_to_cpu(es->s_inode_size); | ||
| 524 | sb->u.ecfs_sb.s_first_ino = le32_to_cpu(es->s_first_ino); | ||
| 525 | if (sb->u.ecfs_sb.s_inode_size != ECFS_GOOD_OLD_INODE_SIZE) { | ||
| 526 | printk ("ECFS-fs: unsupported inode size: %d\n", | ||
| 527 | sb->u.ecfs_sb.s_inode_size); | ||
| 528 | goto failed_mount; | ||
| 529 | } | ||
| 530 | } | ||
| 531 | sb->u.ecfs_sb.s_frag_size = ECFS_MIN_FRAG_SIZE << | ||
| 532 | le32_to_cpu(es->s_log_frag_size); | ||
| 533 | if (sb->u.ecfs_sb.s_frag_size) | ||
| 534 | sb->u.ecfs_sb.s_frags_per_block = sb->s_blocksize / | ||
| 535 | sb->u.ecfs_sb.s_frag_size; | ||
| 536 | else | ||
| 537 | sb->s_magic = 0; | ||
| 538 | sb->u.ecfs_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); | ||
| 539 | sb->u.ecfs_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group); | ||
| 540 | sb->u.ecfs_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); | ||
| 541 | sb->u.ecfs_sb.s_inodes_per_block = sb->s_blocksize / | ||
| 542 | ECFS_INODE_SIZE(sb); | ||
| 543 | sb->u.ecfs_sb.s_itb_per_group = sb->u.ecfs_sb.s_inodes_per_group / | ||
| 544 | sb->u.ecfs_sb.s_inodes_per_block; | ||
| 545 | sb->u.ecfs_sb.s_desc_per_block = sb->s_blocksize / | ||
| 546 | sizeof (struct ecfs_group_desc); | ||
| 547 | sb->u.ecfs_sb.s_sbh = bh; | ||
| 548 | if (resuid != ECFS_DEF_RESUID) | ||
| 549 | sb->u.ecfs_sb.s_resuid = resuid; | ||
| 550 | else | ||
| 551 | sb->u.ecfs_sb.s_resuid = le16_to_cpu(es->s_def_resuid); | ||
| 552 | if (resgid != ECFS_DEF_RESGID) | ||
| 553 | sb->u.ecfs_sb.s_resgid = resgid; | ||
| 554 | else | ||
| 555 | sb->u.ecfs_sb.s_resgid = le16_to_cpu(es->s_def_resgid); | ||
| 556 | sb->u.ecfs_sb.s_mount_state = le16_to_cpu(es->s_state); | ||
| 557 | sb->u.ecfs_sb.s_addr_per_block_bits = | ||
| 558 | log2 (ECFS_ADDR_PER_BLOCK(sb)); | ||
| 559 | sb->u.ecfs_sb.s_desc_per_block_bits = | ||
| 560 | log2 (ECFS_DESC_PER_BLOCK(sb)); | ||
| 561 | if (sb->s_magic != ECFS_SUPER_MAGIC) { | ||
| 562 | if (!silent) | ||
| 563 | printk ("VFS: Can't find an ecfs filesystem on dev " | ||
| 564 | "%s.\n", | ||
| 565 | bdevname(dev)); | ||
| 566 | goto failed_mount; | ||
| 567 | } | ||
| 568 | if (sb->s_blocksize != bh->b_size) { | ||
| 569 | if (!silent) | ||
| 570 | printk ("VFS: Unsupported blocksize on dev " | ||
| 571 | "%s.\n", bdevname(dev)); | ||
| 572 | goto failed_mount; | ||
| 573 | } | ||
| 574 | |||
| 575 | if (sb->s_blocksize != sb->u.ecfs_sb.s_frag_size) { | ||
| 576 | printk ("ECFS-fs: fragsize %lu != blocksize %lu (not supported yet)\n", | ||
| 577 | sb->u.ecfs_sb.s_frag_size, sb->s_blocksize); | ||
| 578 | goto failed_mount; | ||
| 579 | } | ||
| 580 | |||
| 581 | if (sb->u.ecfs_sb.s_blocks_per_group > sb->s_blocksize * 8) { | ||
| 582 | printk ("ECFS-fs: #blocks per group too big: %lu\n", | ||
| 583 | sb->u.ecfs_sb.s_blocks_per_group); | ||
| 584 | goto failed_mount; | ||
| 585 | } | ||
| 586 | if (sb->u.ecfs_sb.s_frags_per_group > sb->s_blocksize * 8) { | ||
| 587 | printk ("ECFS-fs: #fragments per group too big: %lu\n", | ||
| 588 | sb->u.ecfs_sb.s_frags_per_group); | ||
| 589 | goto failed_mount; | ||
| 590 | } | ||
| 591 | if (sb->u.ecfs_sb.s_inodes_per_group > sb->s_blocksize * 8) { | ||
| 592 | printk ("ECFS-fs: #inodes per group too big: %lu\n", | ||
| 593 | sb->u.ecfs_sb.s_inodes_per_group); | ||
| 594 | goto failed_mount; | ||
| 595 | } | ||
| 596 | |||
| 597 | sb->u.ecfs_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) - | ||
| 598 | le32_to_cpu(es->s_first_data_block) + | ||
| 599 | ECFS_BLOCKS_PER_GROUP(sb) - 1) / | ||
| 600 | ECFS_BLOCKS_PER_GROUP(sb); | ||
| 601 | db_count = (sb->u.ecfs_sb.s_groups_count + ECFS_DESC_PER_BLOCK(sb) - 1) / | ||
| 602 | ECFS_DESC_PER_BLOCK(sb); | ||
| 603 | sb->u.ecfs_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); | ||
| 604 | if (sb->u.ecfs_sb.s_group_desc == NULL) { | ||
| 605 | printk ("ECFS-fs: not enough memory\n"); | ||
| 606 | goto failed_mount; | ||
| 607 | } | ||
| 608 | for (i = 0; i < db_count; i++) { | ||
| 609 | sb->u.ecfs_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1, | ||
| 610 | sb->s_blocksize); | ||
| 611 | if (!sb->u.ecfs_sb.s_group_desc[i]) { | ||
| 612 | for (j = 0; j < i; j++) | ||
| 613 | brelse (sb->u.ecfs_sb.s_group_desc[j]); | ||
| 614 | kfree(sb->u.ecfs_sb.s_group_desc); | ||
| 615 | printk ("ECFS-fs: unable to read group descriptors\n"); | ||
| 616 | goto failed_mount; | ||
| 617 | } | ||
| 618 | } | ||
| 619 | if (!ecfs_check_descriptors (sb)) { | ||
| 620 | for (j = 0; j < db_count; j++) | ||
| 621 | brelse (sb->u.ecfs_sb.s_group_desc[j]); | ||
| 622 | kfree(sb->u.ecfs_sb.s_group_desc); | ||
| 623 | printk ("ECFS-fs: group descriptors corrupted !\n"); | ||
| 624 | goto failed_mount; | ||
| 625 | } | ||
| 626 | for (i = 0; i < ECFS_MAX_GROUP_LOADED; i++) { | ||
| 627 | sb->u.ecfs_sb.s_inode_bitmap_number[i] = 0; | ||
| 628 | sb->u.ecfs_sb.s_inode_bitmap[i] = NULL; | ||
| 629 | sb->u.ecfs_sb.s_block_bitmap_number[i] = 0; | ||
| 630 | sb->u.ecfs_sb.s_block_bitmap[i] = NULL; | ||
| 631 | } | ||
| 632 | sb->u.ecfs_sb.s_loaded_inode_bitmaps = 0; | ||
| 633 | sb->u.ecfs_sb.s_loaded_block_bitmaps = 0; | ||
| 634 | sb->u.ecfs_sb.s_gdb_count = db_count; | ||
| 635 | /* | ||
| 636 | * set up enough so that it can read an inode | ||
| 637 | */ | ||
| 638 | sb->s_op = &ecfs_sops; | ||
| 639 | sb->s_root = d_alloc_root(iget(sb, ECFS_ROOT_INO)); | ||
| 640 | if (!sb->s_root) { | ||
| 641 | for (i = 0; i < db_count; i++) | ||
| 642 | if (sb->u.ecfs_sb.s_group_desc[i]) | ||
| 643 | brelse (sb->u.ecfs_sb.s_group_desc[i]); | ||
| 644 | kfree(sb->u.ecfs_sb.s_group_desc); | ||
| 645 | brelse (bh); | ||
| 646 | printk ("ECFS-fs: get root inode failed\n"); | ||
| 647 | return NULL; | ||
| 648 | } | ||
| 649 | ecfs_setup_super (sb, es, sb->s_flags & MS_RDONLY); | ||
| 650 | return sb; | ||
| 651 | } | ||
| 652 | |||
| 653 | static void ecfs_commit_super (struct super_block * sb, | ||
| 654 | struct ecfs_super_block * es) | ||
| 655 | { | ||
| 656 | es->s_wtime = cpu_to_le32(CURRENT_TIME); | ||
| 657 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 658 | sb->s_dirt = 0; | ||
| 659 | } | ||
| 660 | |||
| 661 | /* | ||
| 662 | * In the second extended file system, it is not necessary to | ||
| 663 | * write the super block since we use a mapping of the | ||
| 664 | * disk super block in a buffer. | ||
| 665 | * | ||
| 666 | * However, this function is still used to set the fs valid | ||
| 667 | * flags to 0. We need to set this flag to 0 since the fs | ||
| 668 | * may have been checked while mounted and e2fsck may have | ||
| 669 | * set s_state to ECFS_VALID_FS after some corrections. | ||
| 670 | */ | ||
| 671 | |||
| 672 | void ecfs_write_super (struct super_block * sb) | ||
| 673 | { | ||
| 674 | struct ecfs_super_block * es; | ||
| 675 | |||
| 676 | if (!(sb->s_flags & MS_RDONLY)) { | ||
| 677 | es = sb->u.ecfs_sb.s_es; | ||
| 678 | |||
| 679 | ecfs_debug ("setting valid to 0\n"); | ||
| 680 | |||
| 681 | if (le16_to_cpu(es->s_state) & ECFS_VALID_FS) { | ||
| 682 | es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~ECFS_VALID_FS); | ||
| 683 | es->s_mtime = cpu_to_le32(CURRENT_TIME); | ||
| 684 | } | ||
| 685 | ecfs_commit_super (sb, es); | ||
| 686 | } | ||
| 687 | sb->s_dirt = 0; | ||
| 688 | } | ||
| 689 | |||
| 690 | int ecfs_remount (struct super_block * sb, int * flags, char * data) | ||
| 691 | { | ||
| 692 | struct ecfs_super_block * es; | ||
| 693 | unsigned short resuid = sb->u.ecfs_sb.s_resuid; | ||
| 694 | unsigned short resgid = sb->u.ecfs_sb.s_resgid; | ||
| 695 | unsigned long new_mount_opt; | ||
| 696 | unsigned long tmp; | ||
| 697 | |||
| 698 | /* | ||
| 699 | * Allow the "check" option to be passed as a remount option. | ||
| 700 | */ | ||
| 701 | new_mount_opt = sb->u.ecfs_sb.s_mount_opt; | ||
| 702 | if (!parse_options (data, &tmp, &resuid, &resgid, | ||
| 703 | &new_mount_opt)) | ||
| 704 | return -EINVAL; | ||
| 705 | |||
| 706 | sb->u.ecfs_sb.s_mount_opt = new_mount_opt; | ||
| 707 | sb->u.ecfs_sb.s_resuid = resuid; | ||
| 708 | sb->u.ecfs_sb.s_resgid = resgid; | ||
| 709 | es = sb->u.ecfs_sb.s_es; | ||
| 710 | if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) | ||
| 711 | return 0; | ||
| 712 | if (*flags & MS_RDONLY) { | ||
| 713 | if (le16_to_cpu(es->s_state) & ECFS_VALID_FS || | ||
| 714 | !(sb->u.ecfs_sb.s_mount_state & ECFS_VALID_FS)) | ||
| 715 | return 0; | ||
| 716 | /* | ||
| 717 | * OK, we are remounting a valid rw partition rdonly, so set | ||
| 718 | * the rdonly flag and then mark the partition as valid again. | ||
| 719 | */ | ||
| 720 | es->s_state = cpu_to_le16(sb->u.ecfs_sb.s_mount_state); | ||
| 721 | es->s_mtime = cpu_to_le32(CURRENT_TIME); | ||
| 722 | mark_buffer_dirty(sb->u.ecfs_sb.s_sbh); | ||
| 723 | sb->s_dirt = 1; | ||
| 724 | ecfs_commit_super (sb, es); | ||
| 725 | } | ||
| 726 | else { | ||
| 727 | int ret; | ||
| 728 | if ((ret = ECFS_HAS_RO_COMPAT_FEATURE(sb, | ||
| 729 | ~ECFS_FEATURE_RO_COMPAT_SUPP))) { | ||
| 730 | printk("ECFS-fs: %s: couldn't remount RDWR because of " | ||
| 731 | "unsupported optional features (%x).\n", | ||
| 732 | bdevname(sb->s_dev), ret); | ||
| 733 | return -EROFS; | ||
| 734 | } | ||
| 735 | /* | ||
| 736 | * Mounting a RDONLY partition read-write, so reread and | ||
| 737 | * store the current valid flag. (It may have been changed | ||
| 738 | * by e2fsck since we originally mounted the partition.) | ||
| 739 | */ | ||
| 740 | sb->u.ecfs_sb.s_mount_state = le16_to_cpu(es->s_state); | ||
| 741 | if (!ecfs_setup_super (sb, es, 0)) | ||
| 742 | sb->s_flags &= ~MS_RDONLY; | ||
| 743 | } | ||
| 744 | return 0; | ||
| 745 | } | ||
| 746 | |||
| 747 | int ecfs_statfs (struct super_block * sb, struct statfs * buf) | ||
| 748 | { | ||
| 749 | unsigned long overhead; | ||
| 750 | int i; | ||
| 751 | |||
| 752 | if (test_opt (sb, MINIX_DF)) | ||
| 753 | overhead = 0; | ||
| 754 | else { | ||
| 755 | /* | ||
| 756 | * Compute the overhead (FS structures) | ||
| 757 | */ | ||
| 758 | |||
| 759 | /* | ||
| 760 | * All of the blocks before first_data_block are | ||
| 761 | * overhead | ||
| 762 | */ | ||
| 763 | overhead = le32_to_cpu(sb->u.ecfs_sb.s_es->s_first_data_block); | ||
| 764 | |||
| 765 | /* | ||
| 766 | * Add the overhead attributed to the superblock and | ||
| 767 | * block group descriptors. If the sparse superblocks | ||
| 768 | * feature is turned on, then not all groups have this. | ||
| 769 | */ | ||
| 770 | for (i = 0; i < ECFS_SB(sb)->s_groups_count; i++) | ||
| 771 | overhead += ecfs_bg_has_super(sb, i) + | ||
| 772 | ecfs_bg_num_gdb(sb, i); | ||
| 773 | |||
| 774 | /* | ||
| 775 | * Every block group has an inode bitmap, a block | ||
| 776 | * bitmap, and an inode table. | ||
| 777 | */ | ||
| 778 | overhead += (sb->u.ecfs_sb.s_groups_count * | ||
| 779 | (2 + sb->u.ecfs_sb.s_itb_per_group)); | ||
| 780 | } | ||
| 781 | |||
| 782 | buf->f_type = ECFS_SUPER_MAGIC; | ||
| 783 | buf->f_bsize = sb->s_blocksize; | ||
| 784 | buf->f_blocks = le32_to_cpu(sb->u.ecfs_sb.s_es->s_blocks_count) - overhead; | ||
| 785 | buf->f_bfree = ecfs_count_free_blocks (sb); | ||
| 786 | buf->f_bavail = buf->f_bfree - le32_to_cpu(sb->u.ecfs_sb.s_es->s_r_blocks_count); | ||
| 787 | if (buf->f_bfree < le32_to_cpu(sb->u.ecfs_sb.s_es->s_r_blocks_count)) | ||
| 788 | buf->f_bavail = 0; | ||
| 789 | buf->f_files = le32_to_cpu(sb->u.ecfs_sb.s_es->s_inodes_count); | ||
| 790 | buf->f_ffree = ecfs_count_free_inodes (sb); | ||
| 791 | buf->f_namelen = ECFS_NAME_LEN; | ||
| 792 | return 0; | ||
| 793 | } | ||
| 794 | |||
| 795 | static DECLARE_FSTYPE_DEV(ecfs_fs_type, "ecfs", ecfs_read_super); | ||
| 796 | |||
| 797 | MODULE_PARM(ecfs_key, "s"); | ||
| 798 | |||
| 799 | static int __init init_ecfs_fs(void) | ||
| 800 | { | ||
| 801 | if (strlen(ecfs_key) < 8 || strlen(ecfs_key) > 1024) { | ||
| 802 | printk("<1> You need a key of 8 <= len <= 1024. Please rmmod and\n" | ||
| 803 | "'insmod ecfs_key=<key>' again.\n\n"); | ||
| 804 | return -1; | ||
| 805 | } | ||
| 806 | return register_filesystem(&ecfs_fs_type); | ||
| 807 | } | ||
| 808 | |||
| 809 | static void __exit exit_ecfs_fs(void) | ||
| 810 | { | ||
| 811 | unregister_filesystem(&ecfs_fs_type); | ||
| 812 | } | ||
| 813 | |||
| 814 | EXPORT_NO_SYMBOLS; | ||
| 815 | |||
| 816 | module_init(init_ecfs_fs) | ||
| 817 | module_exit(exit_ecfs_fs) | ||
diff --git a/other/ecfs/super.o b/other/ecfs/super.o new file mode 100644 index 0000000..a2367ef --- /dev/null +++ b/other/ecfs/super.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/symlink.c b/other/ecfs/symlink.c new file mode 100644 index 0000000..1108793 --- /dev/null +++ b/other/ecfs/symlink.c | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/ecfs/symlink.c | ||
| 3 | * | ||
| 4 | * Only fast symlinks left here - the rest is done by generic code. AV, 1999 | ||
| 5 | * | ||
| 6 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
| 7 | * Remy Card (card@masi.ibp.fr) | ||
| 8 | * Laboratoire MASI - Institut Blaise Pascal | ||
| 9 | * Universite Pierre et Marie Curie (Paris VI) | ||
| 10 | * | ||
| 11 | * from | ||
| 12 | * | ||
| 13 | * linux/fs/minix/symlink.c | ||
| 14 | * | ||
| 15 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
| 16 | * | ||
| 17 | * ecfs symlink handling code | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/fs_ecfs.h> | ||
| 21 | #include <linux/ecfs_fs.h> | ||
| 22 | |||
| 23 | static int ecfs_readlink(struct dentry *dentry, char *buffer, int buflen) | ||
| 24 | { | ||
| 25 | char *s = (char *)dentry->d_inode->u.ecfs_i.i_data; | ||
| 26 | return vfs_readlink(dentry, buffer, buflen, s); | ||
| 27 | } | ||
| 28 | |||
| 29 | static int ecfs_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
| 30 | { | ||
| 31 | char *s = (char *)dentry->d_inode->u.ecfs_i.i_data; | ||
| 32 | return vfs_follow_link(nd, s); | ||
| 33 | } | ||
| 34 | |||
| 35 | struct inode_operations ecfs_fast_symlink_inode_operations = { | ||
| 36 | readlink: ecfs_readlink, | ||
| 37 | follow_link: ecfs_follow_link, | ||
| 38 | }; | ||
diff --git a/other/ecfs/symlink.o b/other/ecfs/symlink.o new file mode 100644 index 0000000..9bca096 --- /dev/null +++ b/other/ecfs/symlink.o | |||
| Binary files differ | |||
diff --git a/other/ecfs/transform b/other/ecfs/transform new file mode 100644 index 0000000..9071929 --- /dev/null +++ b/other/ecfs/transform | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #!/usr/bin/perl | ||
| 2 | |||
| 3 | # Takes kernel Ext2 headers and rewrites | ||
| 4 | # them to Ecfs headers in separate files. | ||
| 5 | # No kernel-headers are touched. | ||
| 6 | |||
| 7 | @cp = ( | ||
| 8 | "cp ". | ||
| 9 | "/usr/src/linux/include/linux/ext2_fs.h ". | ||
| 10 | "/usr/src/linux/include/linux/ecfs_fs.h", | ||
| 11 | |||
| 12 | "cp ". | ||
| 13 | "/usr/src/linux/include/linux/ext2_fs_sb.h ". | ||
| 14 | "/usr/src/linux/include/linux/ecfs_fs_sb.h", | ||
| 15 | |||
| 16 | "cp ". | ||
| 17 | "/usr/src/linux/include/linux/ext2_fs_i.h ". | ||
| 18 | "/usr/src/linux/include/linux/ecfs_fs_i.h", | ||
| 19 | |||
| 20 | "cp ". | ||
| 21 | "/usr/src/linux/include/linux/fs.h ". | ||
| 22 | "/usr/src/linux/include/linux/fs_ecfs.h " | ||
| 23 | ); | ||
| 24 | |||
| 25 | |||
| 26 | @sed = ( | ||
| 27 | "./repl.pl ". | ||
| 28 | "/usr/src/linux/include/linux/ecfs_fs.h", | ||
| 29 | |||
| 30 | "./repl.pl ". | ||
| 31 | "/usr/src/linux/include/linux/ecfs_fs_sb.h", | ||
| 32 | |||
| 33 | "./repl.pl ". | ||
| 34 | "/usr/src/linux/include/linux/ecfs_fs_i.h", | ||
| 35 | |||
| 36 | "./repl.pl ". | ||
| 37 | "/usr/src/linux/include/linux/fs_ecfs.h " | ||
| 38 | ); | ||
| 39 | |||
| 40 | |||
| 41 | foreach (@cp) { | ||
| 42 | print "$_\n"; | ||
| 43 | system($_); | ||
| 44 | } | ||
| 45 | |||
| 46 | foreach (@sed) { | ||
| 47 | print "$_\n"; | ||
| 48 | system($_); | ||
| 49 | } | ||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
