From 4296e978da0ab4198aa7f3d823690ee452e300c6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 21 Sep 2008 11:25:17 +0000 Subject: rename generic-2.6/patches to generic-2.6/patches-2.6.21 SVN-Revision: 12636 --- .../generic-2.6/patches-2.6.21/001-squashfs.patch | 4180 +++++++++++ .../patches-2.6.21/002-lzma_decompress.patch | 794 ++ .../patches-2.6.21/003-squashfs_lzma.patch | 109 + .../patches-2.6.21/004-extra_optimization.patch | 14 + .../patches-2.6.21/006-gcc4_inline_fix.patch | 13 + .../patches-2.6.21/007-samsung_flash.patch | 38 + .../009-revert_intel_flash_breakage.patch | 171 + .../010-disable_old_squashfs_compatibility.patch | 23 + .../generic-2.6/patches-2.6.21/011-mips_boot.patch | 21 + .../patches-2.6.21/012-mips_cpu_tlb.patch | 20 + .../013-mips_generic_gpio_support.patch | 38 + .../patches-2.6.21/060-rootfs_split.patch | 492 ++ .../patches-2.6.21/065-block2mtd_init.patch | 114 + .../patches-2.6.21/100-netfilter_layer7_2.17.patch | 2123 ++++++ .../101-netfilter_layer7_pktmatch.patch | 113 + .../patches-2.6.21/110-ipp2p_0.8.1rc1.patch | 952 +++ .../120-openswan-2.4.0.kernel-2.6-natt.patch | 172 + .../patches-2.6.21/130-netfilter_ipset.patch | 7714 +++++++++++++++++++ .../patches-2.6.21/140-netfilter_time.patch | 245 + .../patches-2.6.21/150-netfilter_imq.patch | 904 +++ .../patches-2.6.21/160-netfilter_route.patch | 965 +++ .../170-netfilter_chaostables_0.8.patch | 853 +++ .../patches-2.6.21/171-netfilter_tarpit.patch | 325 + .../patches-2.6.21/180-netfilter_depends.patch | 22 + .../patches-2.6.21/200-sched_esfq.patch | 797 ++ .../201-multiple_default_gateways.patch | 360 + .../patches-2.6.21/202-mips-freestanding.patch | 14 + .../patches-2.6.21/204-jffs2_eofdetect.patch | 60 + .../patches-2.6.21/207-powerpc_asm_segment_h.patch | 11 + .../patches-2.6.21/208-rtl8110sb_fix.patch | 26 + .../generic-2.6/patches-2.6.21/209-mini_fo.patch | 7817 ++++++++++++++++++++ .../patches-2.6.21/210-d80211_compat.patch | 13 + .../211-no_block2mtd_readahead.patch | 13 + .../patches-2.6.21/212-block2mtd_erase_scan.patch | 13 + .../patches-2.6.21/213-kobject_uevent.patch | 39 + .../generic-2.6/patches-2.6.21/420-gpiodev.patch | 26 + .../patches-2.6.21/510-yaffs_support.patch | 21 + .../511-yaffs_reduce_compiler_warnings.patch | 80 + .../patches-2.6.21/900-headers_type_and_time.patch | 50 + .../902-darwin_scripts_include.patch | 154 + .../patches-2.6.21/903-hostap_txpower.patch | 166 + .../patches-2.6.21/903-stddef_include.patch | 19 + .../patches-2.6.21/904-ls_time_locale.patch | 22 + .../999-backport_ip_hdr_and_friends.patch | 58 + .../linux/generic-2.6/patches/001-squashfs.patch | 4180 ----------- .../generic-2.6/patches/002-lzma_decompress.patch | 794 -- .../generic-2.6/patches/003-squashfs_lzma.patch | 109 - .../patches/004-extra_optimization.patch | 14 - .../generic-2.6/patches/006-gcc4_inline_fix.patch | 13 - .../generic-2.6/patches/007-samsung_flash.patch | 38 - .../patches/009-revert_intel_flash_breakage.patch | 171 - .../010-disable_old_squashfs_compatibility.patch | 23 - .../linux/generic-2.6/patches/011-mips_boot.patch | 21 - .../generic-2.6/patches/012-mips_cpu_tlb.patch | 20 - .../patches/013-mips_generic_gpio_support.patch | 38 - .../generic-2.6/patches/060-rootfs_split.patch | 492 -- .../generic-2.6/patches/065-block2mtd_init.patch | 114 - .../patches/100-netfilter_layer7_2.17.patch | 2123 ------ .../patches/101-netfilter_layer7_pktmatch.patch | 113 - .../generic-2.6/patches/110-ipp2p_0.8.1rc1.patch | 952 --- .../120-openswan-2.4.0.kernel-2.6-natt.patch | 172 - .../generic-2.6/patches/130-netfilter_ipset.patch | 7714 ------------------- .../generic-2.6/patches/140-netfilter_time.patch | 245 - .../generic-2.6/patches/150-netfilter_imq.patch | 904 --- .../generic-2.6/patches/160-netfilter_route.patch | 965 --- .../patches/170-netfilter_chaostables_0.8.patch | 853 --- .../generic-2.6/patches/171-netfilter_tarpit.patch | 325 - .../patches/180-netfilter_depends.patch | 22 - .../linux/generic-2.6/patches/200-sched_esfq.patch | 797 -- .../patches/201-multiple_default_gateways.patch | 360 - .../patches/202-mips-freestanding.patch | 14 - .../generic-2.6/patches/204-jffs2_eofdetect.patch | 60 - .../patches/207-powerpc_asm_segment_h.patch | 11 - .../generic-2.6/patches/208-rtl8110sb_fix.patch | 26 - target/linux/generic-2.6/patches/209-mini_fo.patch | 7817 -------------------- .../generic-2.6/patches/210-d80211_compat.patch | 13 - .../patches/211-no_block2mtd_readahead.patch | 13 - .../patches/212-block2mtd_erase_scan.patch | 13 - .../generic-2.6/patches/213-kobject_uevent.patch | 39 - target/linux/generic-2.6/patches/420-gpiodev.patch | 26 - .../generic-2.6/patches/510-yaffs_support.patch | 21 - .../511-yaffs_reduce_compiler_warnings.patch | 80 - .../patches/900-headers_type_and_time.patch | 50 - .../patches/902-darwin_scripts_include.patch | 154 - .../generic-2.6/patches/903-hostap_txpower.patch | 166 - .../generic-2.6/patches/903-stddef_include.patch | 19 - .../generic-2.6/patches/904-ls_time_locale.patch | 22 - .../patches/999-backport_ip_hdr_and_friends.patch | 58 - 88 files changed, 30174 insertions(+), 30174 deletions(-) create mode 100644 target/linux/generic-2.6/patches-2.6.21/001-squashfs.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/002-lzma_decompress.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/003-squashfs_lzma.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/004-extra_optimization.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/006-gcc4_inline_fix.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/007-samsung_flash.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/009-revert_intel_flash_breakage.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/010-disable_old_squashfs_compatibility.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/011-mips_boot.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/012-mips_cpu_tlb.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/013-mips_generic_gpio_support.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/060-rootfs_split.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/065-block2mtd_init.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/100-netfilter_layer7_2.17.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/101-netfilter_layer7_pktmatch.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/110-ipp2p_0.8.1rc1.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/120-openswan-2.4.0.kernel-2.6-natt.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/130-netfilter_ipset.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/140-netfilter_time.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/150-netfilter_imq.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/160-netfilter_route.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/170-netfilter_chaostables_0.8.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/171-netfilter_tarpit.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/180-netfilter_depends.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/200-sched_esfq.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/201-multiple_default_gateways.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/202-mips-freestanding.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/204-jffs2_eofdetect.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/207-powerpc_asm_segment_h.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/208-rtl8110sb_fix.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/209-mini_fo.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/210-d80211_compat.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/211-no_block2mtd_readahead.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/212-block2mtd_erase_scan.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/213-kobject_uevent.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/420-gpiodev.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/510-yaffs_support.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/511-yaffs_reduce_compiler_warnings.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/900-headers_type_and_time.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/902-darwin_scripts_include.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/903-hostap_txpower.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/903-stddef_include.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/904-ls_time_locale.patch create mode 100644 target/linux/generic-2.6/patches-2.6.21/999-backport_ip_hdr_and_friends.patch delete mode 100644 target/linux/generic-2.6/patches/001-squashfs.patch delete mode 100644 target/linux/generic-2.6/patches/002-lzma_decompress.patch delete mode 100644 target/linux/generic-2.6/patches/003-squashfs_lzma.patch delete mode 100644 target/linux/generic-2.6/patches/004-extra_optimization.patch delete mode 100644 target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch delete mode 100644 target/linux/generic-2.6/patches/007-samsung_flash.patch delete mode 100644 target/linux/generic-2.6/patches/009-revert_intel_flash_breakage.patch delete mode 100644 target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch delete mode 100644 target/linux/generic-2.6/patches/011-mips_boot.patch delete mode 100644 target/linux/generic-2.6/patches/012-mips_cpu_tlb.patch delete mode 100644 target/linux/generic-2.6/patches/013-mips_generic_gpio_support.patch delete mode 100644 target/linux/generic-2.6/patches/060-rootfs_split.patch delete mode 100644 target/linux/generic-2.6/patches/065-block2mtd_init.patch delete mode 100644 target/linux/generic-2.6/patches/100-netfilter_layer7_2.17.patch delete mode 100644 target/linux/generic-2.6/patches/101-netfilter_layer7_pktmatch.patch delete mode 100644 target/linux/generic-2.6/patches/110-ipp2p_0.8.1rc1.patch delete mode 100644 target/linux/generic-2.6/patches/120-openswan-2.4.0.kernel-2.6-natt.patch delete mode 100644 target/linux/generic-2.6/patches/130-netfilter_ipset.patch delete mode 100644 target/linux/generic-2.6/patches/140-netfilter_time.patch delete mode 100644 target/linux/generic-2.6/patches/150-netfilter_imq.patch delete mode 100644 target/linux/generic-2.6/patches/160-netfilter_route.patch delete mode 100644 target/linux/generic-2.6/patches/170-netfilter_chaostables_0.8.patch delete mode 100644 target/linux/generic-2.6/patches/171-netfilter_tarpit.patch delete mode 100644 target/linux/generic-2.6/patches/180-netfilter_depends.patch delete mode 100644 target/linux/generic-2.6/patches/200-sched_esfq.patch delete mode 100644 target/linux/generic-2.6/patches/201-multiple_default_gateways.patch delete mode 100644 target/linux/generic-2.6/patches/202-mips-freestanding.patch delete mode 100644 target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch delete mode 100644 target/linux/generic-2.6/patches/207-powerpc_asm_segment_h.patch delete mode 100644 target/linux/generic-2.6/patches/208-rtl8110sb_fix.patch delete mode 100644 target/linux/generic-2.6/patches/209-mini_fo.patch delete mode 100644 target/linux/generic-2.6/patches/210-d80211_compat.patch delete mode 100644 target/linux/generic-2.6/patches/211-no_block2mtd_readahead.patch delete mode 100644 target/linux/generic-2.6/patches/212-block2mtd_erase_scan.patch delete mode 100644 target/linux/generic-2.6/patches/213-kobject_uevent.patch delete mode 100644 target/linux/generic-2.6/patches/420-gpiodev.patch delete mode 100644 target/linux/generic-2.6/patches/510-yaffs_support.patch delete mode 100644 target/linux/generic-2.6/patches/511-yaffs_reduce_compiler_warnings.patch delete mode 100644 target/linux/generic-2.6/patches/900-headers_type_and_time.patch delete mode 100644 target/linux/generic-2.6/patches/902-darwin_scripts_include.patch delete mode 100644 target/linux/generic-2.6/patches/903-hostap_txpower.patch delete mode 100644 target/linux/generic-2.6/patches/903-stddef_include.patch delete mode 100644 target/linux/generic-2.6/patches/904-ls_time_locale.patch delete mode 100644 target/linux/generic-2.6/patches/999-backport_ip_hdr_and_friends.patch (limited to 'target/linux') diff --git a/target/linux/generic-2.6/patches-2.6.21/001-squashfs.patch b/target/linux/generic-2.6/patches-2.6.21/001-squashfs.patch new file mode 100644 index 0000000000..e64db466cf --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/001-squashfs.patch @@ -0,0 +1,4180 @@ +Index: linux-2.6.21.7/fs/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/fs/Kconfig ++++ linux-2.6.21.7/fs/Kconfig +@@ -1371,6 +1371,71 @@ config CRAMFS + + If unsure, say N. + ++config SQUASHFS ++ tristate "SquashFS 3.0 - Squashed file system support" ++ select ZLIB_INFLATE ++ help ++ Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File ++ System). Squashfs is a highly compressed read-only filesystem for Linux. ++ It uses zlib compression to compress both files, inodes and directories. ++ Inodes in the system are very small and all blocks are packed to minimise ++ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. ++ SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full ++ uid/gid information, hard links and timestamps. ++ ++ Squashfs is intended for general read-only filesystem use, for archival ++ use (i.e. in cases where a .tar.gz file may be used), and in embedded ++ systems where low overhead is needed. Further information and filesystem tools ++ are available from http://squashfs.sourceforge.net. ++ ++ If you want to compile this as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read . The module ++ will be called squashfs. Note that the root file system (the one ++ containing the directory /) cannot be compiled as a module. ++ ++ If unsure, say N. ++ ++config SQUASHFS_EMBEDDED ++ ++ bool "Additional options for memory-constrained systems" ++ depends on SQUASHFS ++ default n ++ help ++ Saying Y here allows you to specify cache sizes and how Squashfs ++ allocates memory. This is only intended for memory constrained ++ systems. ++ ++ If unsure, say N. ++ ++config SQUASHFS_FRAGMENT_CACHE_SIZE ++ int "Number of fragments cached" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default "3" ++ help ++ By default SquashFS caches the last 3 fragments read from ++ the filesystem. Increasing this amount may mean SquashFS ++ has to re-read fragments less often from disk, at the expense ++ of extra system memory. Decreasing this amount will mean ++ SquashFS uses less memory at the expense of extra reads from disk. ++ ++ Note there must be at least one cached fragment. Anything ++ much more than three will probably not make much difference. ++ ++config SQUASHFS_VMALLOC ++ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default n ++ help ++ By default SquashFS uses kmalloc to obtain fragment cache memory. ++ Kmalloc memory is the standard kernel allocator, but it can fail ++ on memory constrained systems. Because of the way Vmalloc works, ++ Vmalloc can succeed when kmalloc fails. Specifying this option ++ will make SquashFS always use Vmalloc to allocate the ++ fragment cache memory. ++ ++ If unsure, say N. ++ + config VXFS_FS + tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + depends on BLOCK +Index: linux-2.6.21.7/fs/Makefile +=================================================================== +--- linux-2.6.21.7.orig/fs/Makefile ++++ linux-2.6.21.7/fs/Makefile +@@ -68,6 +68,7 @@ obj-$(CONFIG_JBD) += jbd/ + obj-$(CONFIG_JBD2) += jbd2/ + obj-$(CONFIG_EXT2_FS) += ext2/ + obj-$(CONFIG_CRAMFS) += cramfs/ ++obj-$(CONFIG_SQUASHFS) += squashfs/ + obj-$(CONFIG_RAMFS) += ramfs/ + obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ + obj-$(CONFIG_CODA_FS) += coda/ +Index: linux-2.6.21.7/fs/squashfs/inode.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/squashfs/inode.c +@@ -0,0 +1,2124 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * inode.c ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs.h" ++ ++static void squashfs_put_super(struct super_block *); ++static int squashfs_statfs(struct dentry *, struct kstatfs *); ++static int squashfs_symlink_readpage(struct file *file, struct page *page); ++static int squashfs_readpage(struct file *file, struct page *page); ++static int squashfs_readpage4K(struct file *file, struct page *page); ++static int squashfs_readdir(struct file *, void *, filldir_t); ++static struct inode *squashfs_alloc_inode(struct super_block *sb); ++static void squashfs_destroy_inode(struct inode *inode); ++static int init_inodecache(void); ++static void destroy_inodecache(void); ++static struct dentry *squashfs_lookup(struct inode *, struct dentry *, ++ struct nameidata *); ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode); ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize); ++static int squashfs_get_sb(struct file_system_type *, int, ++ const char *, void *, struct vfsmount *); ++ ++ ++static z_stream stream; ++ ++static struct file_system_type squashfs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "squashfs", ++ .get_sb = squashfs_get_sb, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static struct super_operations squashfs_ops = { ++ .alloc_inode = squashfs_alloc_inode, ++ .destroy_inode = squashfs_destroy_inode, ++ .statfs = squashfs_statfs, ++ .put_super = squashfs_put_super, ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { ++ .readpage = squashfs_symlink_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops = { ++ .readpage = squashfs_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops_4K = { ++ .readpage = squashfs_readpage4K ++}; ++ ++static struct file_operations squashfs_dir_ops = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir ++}; ++ ++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { ++ .lookup = squashfs_lookup ++}; ++ ++ ++static struct buffer_head *get_block_length(struct super_block *s, ++ int *cur_index, int *offset, int *c_byte) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned short temp; ++ struct buffer_head *bh; ++ ++ if (!(bh = sb_bread(s, *cur_index))) ++ goto out; ++ ++ if (msblk->devblksize - *offset == 1) { ++ if (msblk->swap) ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ else ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ if (msblk->swap) ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ bh->b_data); ++ else ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ bh->b_data); ++ *c_byte = temp; ++ *offset = 1; ++ } else { ++ if (msblk->swap) { ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } else { ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } ++ *c_byte = temp; ++ *offset += 2; ++ } ++ ++ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { ++ if (*offset == msblk->devblksize) { ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ *offset = 0; ++ } ++ if (*((unsigned char *) (bh->b_data + *offset)) != ++ SQUASHFS_MARKER_BYTE) { ++ ERROR("Metadata block marker corrupt @ %x\n", ++ *cur_index); ++ brelse(bh); ++ goto out; ++ } ++ (*offset)++; ++ } ++ return bh; ++ ++out: ++ return NULL; ++} ++ ++ ++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> ++ msblk->devblksize_log2) + 2]; ++ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); ++ unsigned int cur_index = index >> msblk->devblksize_log2; ++ int bytes, avail_bytes, b = 0, k; ++ char *c_buffer; ++ unsigned int compressed; ++ unsigned int c_byte = length; ++ ++ if (c_byte) { ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); ++ c_buffer = compressed ? msblk->read_data : buffer; ++ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++ ? "" : "un", (unsigned int) c_byte); ++ ++ if (!(bh[0] = sb_getblk(s, cur_index))) ++ goto block_release; ++ ++ for (b = 1; bytes < c_byte; b++) { ++ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b, bh); ++ } else { ++ if (!(bh[0] = get_block_length(s, &cur_index, &offset, ++ &c_byte))) ++ goto read_failure; ++ ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED(c_byte); ++ c_buffer = compressed ? msblk->read_data : buffer; ++ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++ ? "" : "un", (unsigned int) c_byte); ++ ++ for (b = 1; bytes < c_byte; b++) { ++ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b - 1, bh + 1); ++ } ++ ++ if (compressed) ++ down(&msblk->read_data_mutex); ++ ++ for (bytes = 0, k = 0; k < b; k++) { ++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? ++ msblk->devblksize - offset : ++ c_byte - bytes; ++ wait_on_buffer(bh[k]); ++ if (!buffer_uptodate(bh[k])) ++ goto block_release; ++ memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); ++ bytes += avail_bytes; ++ offset = 0; ++ brelse(bh[k]); ++ } ++ ++ /* ++ * uncompress block ++ */ ++ if (compressed) { ++ int zlib_err; ++ ++ stream.next_in = c_buffer; ++ stream.avail_in = c_byte; ++ stream.next_out = buffer; ++ stream.avail_out = msblk->read_size; ++ ++ if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || ++ ((zlib_err = zlib_inflate(&stream, Z_FINISH)) ++ != Z_STREAM_END) || ((zlib_err = ++ zlib_inflateEnd(&stream)) != Z_OK)) { ++ ERROR("zlib_fs returned unexpected result 0x%x\n", ++ zlib_err); ++ bytes = 0; ++ } else ++ bytes = stream.total_out; ++ ++ up(&msblk->read_data_mutex); ++ } ++ ++ if (next_index) ++ *next_index = index + c_byte + (length ? 0 : ++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) ++ ? 3 : 2)); ++ return bytes; ++ ++block_release: ++ while (--b >= 0) ++ brelse(bh[b]); ++ ++read_failure: ++ ERROR("sb_bread failed reading block 0x%x\n", cur_index); ++ return 0; ++} ++ ++ ++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ int n, i, bytes, return_length = length; ++ long long next_index; ++ ++ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); ++ ++ while ( 1 ) { ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ if (msblk->block_cache[i].block == block) ++ break; ++ ++ down(&msblk->block_cache_mutex); ++ ++ if (i == SQUASHFS_CACHED_BLKS) { ++ /* read inode header block */ ++ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; ++ n ; n --, i = (i + 1) % ++ SQUASHFS_CACHED_BLKS) ++ if (msblk->block_cache[i].block != ++ SQUASHFS_USED_BLK) ++ break; ++ ++ if (n == 0) { ++ wait_queue_t wait; ++ ++ init_waitqueue_entry(&wait, current); ++ add_wait_queue(&msblk->waitq, &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ up(&msblk->block_cache_mutex); ++ schedule(); ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&msblk->waitq, &wait); ++ continue; ++ } ++ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; ++ ++ if (msblk->block_cache[i].block == ++ SQUASHFS_INVALID_BLK) { ++ if (!(msblk->block_cache[i].data = ++ kmalloc(SQUASHFS_METADATA_SIZE, ++ GFP_KERNEL))) { ++ ERROR("Failed to allocate cache" ++ "block\n"); ++ up(&msblk->block_cache_mutex); ++ goto out; ++ } ++ } ++ ++ msblk->block_cache[i].block = SQUASHFS_USED_BLK; ++ up(&msblk->block_cache_mutex); ++ ++ if (!(msblk->block_cache[i].length = ++ squashfs_read_data(s, ++ msblk->block_cache[i].data, ++ block, 0, &next_index))) { ++ ERROR("Unable to read cache block [%llx:%x]\n", ++ block, offset); ++ goto out; ++ } ++ ++ down(&msblk->block_cache_mutex); ++ wake_up(&msblk->waitq); ++ msblk->block_cache[i].block = block; ++ msblk->block_cache[i].next_index = next_index; ++ TRACE("Read cache block [%llx:%x]\n", block, offset); ++ } ++ ++ if (msblk->block_cache[i].block != block) { ++ up(&msblk->block_cache_mutex); ++ continue; ++ } ++ ++ if ((bytes = msblk->block_cache[i].length - offset) >= length) { ++ if (buffer) ++ memcpy(buffer, msblk->block_cache[i].data + ++ offset, length); ++ if (msblk->block_cache[i].length - offset == length) { ++ *next_block = msblk->block_cache[i].next_index; ++ *next_offset = 0; ++ } else { ++ *next_block = block; ++ *next_offset = offset + length; ++ } ++ up(&msblk->block_cache_mutex); ++ goto finish; ++ } else { ++ if (buffer) { ++ memcpy(buffer, msblk->block_cache[i].data + ++ offset, bytes); ++ buffer += bytes; ++ } ++ block = msblk->block_cache[i].next_index; ++ up(&msblk->block_cache_mutex); ++ length -= bytes; ++ offset = 0; ++ } ++ } ++ ++finish: ++ return return_length; ++out: ++ return 0; ++} ++ ++ ++static int get_fragment_location(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); ++ struct squashfs_fragment_entry fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++ start_block, offset, ++ sizeof(sfragment_entry), &start_block, ++ &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++ start_block, offset, ++ sizeof(fragment_entry), &start_block, ++ &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++ squashfs_fragment_cache *fragment) ++{ ++ down(&msblk->fragment_mutex); ++ fragment->locked --; ++ wake_up(&msblk->fragment_wait_queue); ++ up(&msblk->fragment_mutex); ++} ++ ++ ++SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++ *s, long long start_block, ++ int length) ++{ ++ int i, n; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ ++ while ( 1 ) { ++ down(&msblk->fragment_mutex); ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && ++ msblk->fragment[i].block != start_block; i++); ++ ++ if (i == SQUASHFS_CACHED_FRAGMENTS) { ++ for (i = msblk->next_fragment, n = ++ SQUASHFS_CACHED_FRAGMENTS; n && ++ msblk->fragment[i].locked; n--, i = (i + 1) % ++ SQUASHFS_CACHED_FRAGMENTS); ++ ++ if (n == 0) { ++ wait_queue_t wait; ++ ++ init_waitqueue_entry(&wait, current); ++ add_wait_queue(&msblk->fragment_wait_queue, ++ &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ up(&msblk->fragment_mutex); ++ schedule(); ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&msblk->fragment_wait_queue, ++ &wait); ++ continue; ++ } ++ msblk->next_fragment = (msblk->next_fragment + 1) % ++ SQUASHFS_CACHED_FRAGMENTS; ++ ++ if (msblk->fragment[i].data == NULL) ++ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC ++ (SQUASHFS_FILE_MAX_SIZE))) { ++ ERROR("Failed to allocate fragment " ++ "cache block\n"); ++ up(&msblk->fragment_mutex); ++ goto out; ++ } ++ ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ msblk->fragment[i].locked = 1; ++ up(&msblk->fragment_mutex); ++ ++ if (!(msblk->fragment[i].length = squashfs_read_data(s, ++ msblk->fragment[i].data, ++ start_block, length, NULL))) { ++ ERROR("Unable to read fragment cache block " ++ "[%llx]\n", start_block); ++ msblk->fragment[i].locked = 0; ++ goto out; ++ } ++ ++ msblk->fragment[i].block = start_block; ++ TRACE("New fragment %d, start block %lld, locked %d\n", ++ i, msblk->fragment[i].block, ++ msblk->fragment[i].locked); ++ break; ++ } ++ ++ msblk->fragment[i].locked++; ++ up(&msblk->fragment_mutex); ++ TRACE("Got fragment %d, start block %lld, locked %d\n", i, ++ msblk->fragment[i].block, ++ msblk->fragment[i].locked); ++ break; ++ } ++ ++ return &msblk->fragment[i]; ++ ++out: ++ return NULL; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++ struct squashfs_base_inode_header *inodeb) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct inode *i = new_inode(s); ++ ++ if (i) { ++ i->i_ino = inodeb->inode_number; ++ i->i_mtime.tv_sec = inodeb->mtime; ++ i->i_atime.tv_sec = inodeb->mtime; ++ i->i_ctime.tv_sec = inodeb->mtime; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_size = 0; ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++ } ++ ++ return i; ++} ++ ++ ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode) ++{ ++ struct inode *i; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long block = SQUASHFS_INODE_BLK(inode) + ++ sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header id, sid; ++ struct squashfs_base_inode_header *inodeb = &id.base, ++ *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_iget\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++ offset, sizeof(*sinodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, ++ sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++ offset, sizeof(*inodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_reg_inode_header *inodep = &id.reg; ++ struct squashfs_reg_inode_header *sinodep = &sid.reg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = 1; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_LREG_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_lreg_inode_header *inodep = &id.lreg; ++ struct squashfs_lreg_inode_header *sinodep = &sid.lreg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header *inodep = &id.dir; ++ struct squashfs_dir_inode_header *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = ++ next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = ++ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header *inodep = ++ &id.symlink; ++ struct squashfs_symlink_inode_header *sinodep = ++ &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header *inodep = &id.dev; ++ struct squashfs_dev_inode_header *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == ++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : ++ S_IFBLK; ++ init_special_inode(i, i->i_mode, ++ old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ struct squashfs_ipc_inode_header *inodep = &id.ipc; ++ struct squashfs_ipc_inode_header *sinodep = &sid.ipc; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ insert_inode_hash(i); ++ return i; ++ ++failed_read: ++ ERROR("Unable to read inode [%llx:%x]\n", block, offset); ++ ++failed_read1: ++ return NULL; ++} ++ ++ ++static int read_fragment_index_table(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ /* Allocate fragment index table */ ++ if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES ++ (sblk->fragments), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ return 0; ++ } ++ ++ if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && ++ !squashfs_read_data(s, (char *) ++ msblk->fragment_index, ++ sblk->fragment_table_start, ++ SQUASHFS_FRAGMENT_INDEX_BYTES ++ (sblk->fragments) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ long long fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); ++ i++) { ++ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), ++ &msblk->fragment_index[i], 1); ++ msblk->fragment_index[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->iget = squashfs_iget; ++ msblk->read_blocklist = read_blocklist; ++ msblk->read_fragment_index_table = read_fragment_index_table; ++ ++ if (sblk->s_major == 1) { ++ if (!squashfs_1_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with " ++ "Squashfs 1.0 support enabled\n"); ++ return 0; ++ } ++ } else if (sblk->s_major == 2) { ++ if (!squashfs_2_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with " ++ "Squashfs 2.0 support enabled\n"); ++ return 0; ++ } ++ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > ++ SQUASHFS_MINOR) { ++ SERROR("Major/Minor mismatch, trying to mount newer %d.%d " ++ "filesystem\n", sblk->s_major, sblk->s_minor); ++ SERROR("Please update your kernel\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static int squashfs_fill_super(struct super_block *s, void *data, int silent) ++{ ++ struct squashfs_sb_info *msblk; ++ struct squashfs_super_block *sblk; ++ int i; ++ char b[BDEVNAME_SIZE]; ++ struct inode *root; ++ ++ TRACE("Entered squashfs_read_superblock\n"); ++ ++ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), ++ GFP_KERNEL))) { ++ ERROR("Failed to allocate superblock\n"); ++ goto failure; ++ } ++ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); ++ msblk = s->s_fs_info; ++ sblk = &msblk->sblk; ++ ++ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); ++ msblk->devblksize_log2 = ffz(~msblk->devblksize); ++ ++ init_MUTEX(&msblk->read_data_mutex); ++ init_MUTEX(&msblk->read_page_mutex); ++ init_MUTEX(&msblk->block_cache_mutex); ++ init_MUTEX(&msblk->fragment_mutex); ++ init_MUTEX(&msblk->meta_index_mutex); ++ ++ init_waitqueue_head(&msblk->waitq); ++ init_waitqueue_head(&msblk->fragment_wait_queue); ++ ++ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, ++ sizeof(struct squashfs_super_block) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ SERROR("unable to read superblock\n"); ++ goto failed_mount; ++ } ++ ++ /* Check it is a SQUASHFS superblock */ ++ msblk->swap = 0; ++ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { ++ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { ++ struct squashfs_super_block ssblk; ++ ++ WARNING("Mounting a different endian SQUASHFS " ++ "filesystem on %s\n", bdevname(s->s_bdev, b)); ++ ++ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); ++ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); ++ msblk->swap = 1; ++ } else { ++ SERROR("Can't find a SQUASHFS superblock on %s\n", ++ bdevname(s->s_bdev, b)); ++ goto failed_mount; ++ } ++ } ++ ++ /* Check the MAJOR & MINOR versions */ ++ if(!supported_squashfs_filesystem(msblk, silent)) ++ goto failed_mount; ++ ++ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); ++ TRACE("Inodes are %scompressed\n", ++ SQUASHFS_UNCOMPRESSED_INODES ++ (sblk->flags) ? "un" : ""); ++ TRACE("Data is %scompressed\n", ++ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) ++ ? "un" : ""); ++ TRACE("Check data is %s present in the filesystem\n", ++ SQUASHFS_CHECK_DATA(sblk->flags) ? ++ "" : "not"); ++ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); ++ TRACE("Block size %d\n", sblk->block_size); ++ TRACE("Number of inodes %d\n", sblk->inodes); ++ if (sblk->s_major > 1) ++ TRACE("Number of fragments %d\n", sblk->fragments); ++ TRACE("Number of uids %d\n", sblk->no_uids); ++ TRACE("Number of gids %d\n", sblk->no_guids); ++ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); ++ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); ++ if (sblk->s_major > 1) ++ TRACE("sblk->fragment_table_start %llx\n", ++ sblk->fragment_table_start); ++ TRACE("sblk->uid_start %llx\n", sblk->uid_start); ++ ++ s->s_flags |= MS_RDONLY; ++ s->s_op = &squashfs_ops; ++ ++ /* Init inode_table block pointer array */ ++ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * ++ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { ++ ERROR("Failed to allocate block cache\n"); ++ goto failed_mount; ++ } ++ ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; ++ ++ msblk->next_cache = 0; ++ ++ /* Allocate read_data block */ ++ msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ? ++ SQUASHFS_METADATA_SIZE : ++ sblk->block_size; ++ ++ if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) { ++ ERROR("Failed to allocate read_data block\n"); ++ goto failed_mount; ++ } ++ ++ /* Allocate read_page block */ ++ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { ++ ERROR("Failed to allocate read_page block\n"); ++ goto failed_mount; ++ } ++ ++ /* Allocate uid and gid tables */ ++ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ goto failed_mount; ++ } ++ msblk->guid = msblk->uid + sblk->no_uids; ++ ++ if (msblk->swap) { ++ unsigned int suid[sblk->no_uids + sblk->no_guids]; ++ ++ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + ++ sblk->no_guids), (sizeof(unsigned int) * 8)); ++ } else ++ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ ++ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) ++ goto allocate_root; ++ ++ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * ++ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { ++ ERROR("Failed to allocate fragment block cache\n"); ++ goto failed_mount; ++ } ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { ++ msblk->fragment[i].locked = 0; ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ msblk->fragment[i].data = NULL; ++ } ++ ++ msblk->next_fragment = 0; ++ ++ /* Allocate fragment index table */ ++ if (msblk->read_fragment_index_table(s) == 0) ++ goto failed_mount; ++ ++allocate_root: ++ if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL) ++ goto failed_mount; ++ ++ if ((s->s_root = d_alloc_root(root)) == NULL) { ++ ERROR("Root inode create failed\n"); ++ iput(root); ++ goto failed_mount; ++ } ++ ++ TRACE("Leaving squashfs_read_super\n"); ++ return 0; ++ ++failed_mount: ++ kfree(msblk->fragment_index); ++ kfree(msblk->fragment); ++ kfree(msblk->uid); ++ kfree(msblk->read_page); ++ kfree(msblk->read_data); ++ kfree(msblk->block_cache); ++ kfree(msblk->fragment_index_2); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ return -EINVAL; ++ ++failure: ++ return -ENOMEM; ++} ++ ++ ++static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct squashfs_sb_info *msblk = dentry->d_inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ TRACE("Entered squashfs_statfs\n"); ++ ++ buf->f_type = SQUASHFS_MAGIC; ++ buf->f_bsize = sblk->block_size; ++ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; ++ buf->f_bfree = buf->f_bavail = 0; ++ buf->f_files = sblk->inodes; ++ buf->f_ffree = 0; ++ buf->f_namelen = SQUASHFS_NAME_LEN; ++ ++ return 0; ++} ++ ++ ++static int squashfs_symlink_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ int index = page->index << PAGE_CACHE_SHIFT, length, bytes; ++ long long block = SQUASHFS_I(inode)->start_block; ++ int offset = SQUASHFS_I(inode)->offset; ++ void *pageaddr = kmap(page); ++ ++ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " ++ "%llx, offset %x\n", page->index, ++ SQUASHFS_I(inode)->start_block, ++ SQUASHFS_I(inode)->offset); ++ ++ for (length = 0; length < index; length += bytes) { ++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, ++ block, offset, PAGE_CACHE_SIZE, &block, ++ &offset))) { ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, ++ offset); ++ goto skip_read; ++ } ++ } ++ ++ if (length != index) { ++ ERROR("(squashfs_symlink_readpage) length != index\n"); ++ bytes = 0; ++ goto skip_read; ++ } ++ ++ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : ++ i_size_read(inode) - length; ++ ++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, ++ offset, bytes, &block, &offset))) ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); ++ ++skip_read: ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) ++{ ++ struct meta_index *meta = NULL; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ int i; ++ ++ down(&msblk->meta_index_mutex); ++ ++ TRACE("locate_meta_index: index %d, offset %d\n", index, offset); ++ ++ if(msblk->meta_index == NULL) ++ goto not_allocated; ++ ++ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) ++ if (msblk->meta_index[i].inode_number == inode->i_ino && ++ msblk->meta_index[i].offset >= offset && ++ msblk->meta_index[i].offset <= index && ++ msblk->meta_index[i].locked == 0) { ++ TRACE("locate_meta_index: entry %d, offset %d\n", i, ++ msblk->meta_index[i].offset); ++ meta = &msblk->meta_index[i]; ++ offset = meta->offset; ++ } ++ ++ if (meta) ++ meta->locked = 1; ++ ++not_allocated: ++ up(&msblk->meta_index_mutex); ++ ++ return meta; ++} ++ ++ ++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct meta_index *meta = NULL; ++ int i; ++ ++ down(&msblk->meta_index_mutex); ++ ++ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); ++ ++ if(msblk->meta_index == NULL) { ++ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * ++ SQUASHFS_META_NUMBER, GFP_KERNEL))) { ++ ERROR("Failed to allocate meta_index\n"); ++ goto failed; ++ } ++ for(i = 0; i < SQUASHFS_META_NUMBER; i++) { ++ msblk->meta_index[i].inode_number = 0; ++ msblk->meta_index[i].locked = 0; ++ } ++ msblk->next_meta_index = 0; ++ } ++ ++ for(i = SQUASHFS_META_NUMBER; i && ++ msblk->meta_index[msblk->next_meta_index].locked; i --) ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ if(i == 0) { ++ TRACE("empty_meta_index: failed!\n"); ++ goto failed; ++ } ++ ++ TRACE("empty_meta_index: returned meta entry %d, %p\n", ++ msblk->next_meta_index, ++ &msblk->meta_index[msblk->next_meta_index]); ++ ++ meta = &msblk->meta_index[msblk->next_meta_index]; ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ meta->inode_number = inode->i_ino; ++ meta->offset = offset; ++ meta->skip = skip; ++ meta->entries = 0; ++ meta->locked = 1; ++ ++failed: ++ up(&msblk->meta_index_mutex); ++ return meta; ++} ++ ++ ++void release_meta_index(struct inode *inode, struct meta_index *meta) ++{ ++ meta->locked = 0; ++} ++ ++ ++static int read_block_index(struct super_block *s, int blocks, char *block_list, ++ long long *start_block, int *offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned int *block_listp; ++ int block = 0; ++ ++ if (msblk->swap) { ++ char sblock_list[blocks << 2]; ++ ++ if (!squashfs_get_cached_block(s, sblock_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Unable to read block list [%llx:%x]\n", ++ *start_block, *offset); ++ goto failure; ++ } ++ SQUASHFS_SWAP_INTS(((unsigned int *)block_list), ++ ((unsigned int *)sblock_list), blocks); ++ } else ++ if (!squashfs_get_cached_block(s, block_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Unable to read block list [%llx:%x]\n", ++ *start_block, *offset); ++ goto failure; ++ } ++ ++ for (block_listp = (unsigned int *) block_list; blocks; ++ block_listp++, blocks --) ++ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); ++ ++ return block; ++ ++failure: ++ return -1; ++} ++ ++ ++#define SIZE 256 ++ ++static inline int calculate_skip(int blocks) { ++ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); ++ return skip >= 7 ? 7 : skip + 1; ++} ++ ++ ++static int get_meta_index(struct inode *inode, int index, ++ long long *index_block, int *index_offset, ++ long long *data_block, char *block_list) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); ++ int offset = 0; ++ struct meta_index *meta; ++ struct meta_entry *meta_entry; ++ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; ++ int cur_offset = SQUASHFS_I(inode)->offset; ++ long long cur_data_block = SQUASHFS_I(inode)->start_block; ++ int i; ++ ++ index /= SQUASHFS_META_INDEXES * skip; ++ ++ while ( offset < index ) { ++ meta = locate_meta_index(inode, index, offset + 1); ++ ++ if (meta == NULL) { ++ if ((meta = empty_meta_index(inode, offset + 1, ++ skip)) == NULL) ++ goto all_done; ++ } else { ++ offset = index < meta->offset + meta->entries ? index : ++ meta->offset + meta->entries - 1; ++ meta_entry = &meta->meta_entry[offset - meta->offset]; ++ cur_index_block = meta_entry->index_block + sblk->inode_table_start; ++ cur_offset = meta_entry->offset; ++ cur_data_block = meta_entry->data_block; ++ TRACE("get_meta_index: offset %d, meta->offset %d, " ++ "meta->entries %d\n", offset, meta->offset, ++ meta->entries); ++ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" ++ " data_block 0x%llx\n", cur_index_block, ++ cur_offset, cur_data_block); ++ } ++ ++ for (i = meta->offset + meta->entries; i <= index && ++ i < meta->offset + SQUASHFS_META_ENTRIES; i++) { ++ int blocks = skip * SQUASHFS_META_INDEXES; ++ ++ while (blocks) { ++ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : ++ blocks; ++ int res = read_block_index(inode->i_sb, block, ++ block_list, &cur_index_block, ++ &cur_offset); ++ ++ if (res == -1) ++ goto failed; ++ ++ cur_data_block += res; ++ blocks -= block; ++ } ++ ++ meta_entry = &meta->meta_entry[i - meta->offset]; ++ meta_entry->index_block = cur_index_block - sblk->inode_table_start; ++ meta_entry->offset = cur_offset; ++ meta_entry->data_block = cur_data_block; ++ meta->entries ++; ++ offset ++; ++ } ++ ++ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", ++ meta->offset, meta->entries); ++ ++ release_meta_index(inode, meta); ++ } ++ ++all_done: ++ *index_block = cur_index_block; ++ *index_offset = cur_offset; ++ *data_block = cur_data_block; ++ ++ return offset * SQUASHFS_META_INDEXES * skip; ++ ++failed: ++ release_meta_index(inode, meta); ++ return -1; ++} ++ ++ ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize) ++{ ++ long long block_ptr; ++ int offset; ++ long long block; ++ int res = get_meta_index(inode, index, &block_ptr, &offset, &block, ++ block_list); ++ ++ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" ++ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, ++ block); ++ ++ if(res == -1) ++ goto failure; ++ ++ index -= res; ++ ++ while ( index ) { ++ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; ++ int res = read_block_index(inode->i_sb, blocks, block_list, ++ &block_ptr, &offset); ++ if (res == -1) ++ goto failure; ++ block += res; ++ index -= blocks; ++ } ++ ++ if (read_block_index(inode->i_sb, 1, block_list, ++ &block_ptr, &offset) == -1) ++ goto failure; ++ *bsize = *((unsigned int *) block_list); ++ ++ return block; ++ ++failure: ++ return 0; ++} ++ ++ ++static int squashfs_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned char block_list[SIZE]; ++ long long block; ++ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; ++ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); ++ void *pageaddr; ++ struct squashfs_fragment_cache *fragment = NULL; ++ char *data_ptr = msblk->read_page; ++ ++ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; ++ int start_index = page->index & ~mask; ++ int end_index = start_index | mask; ++ ++ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", ++ page->index, ++ SQUASHFS_I(inode)->start_block); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) ++ goto skip_read; ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < (i_size_read(inode) >> ++ sblk->block_log)) { ++ if ((block = (msblk->read_blocklist)(inode, index, 1, ++ block_list, NULL, &bsize)) == 0) ++ goto skip_read; ++ ++ down(&msblk->read_page_mutex); ++ ++ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, ++ block, bsize, NULL))) { ++ ERROR("Unable to read page, block %llx, size %x\n", block, ++ bsize); ++ up(&msblk->read_page_mutex); ++ goto skip_read; ++ } ++ } else { ++ if ((fragment = get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ SQUASHFS_I(inode)->u.s1.fragment_size)) ++ == NULL) { ++ ERROR("Unable to read page, block %llx, size %x\n", ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ (int) SQUASHFS_I(inode)-> ++ u.s1.fragment_size); ++ goto skip_read; ++ } ++ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + ++ (i_size_read(inode) & (sblk->block_size ++ - 1)); ++ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; ++ data_ptr = fragment->data; ++ } ++ ++ for (i = start_index; i <= end_index && byte_offset < bytes; ++ i++, byte_offset += PAGE_CACHE_SIZE) { ++ struct page *push_page; ++ int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? ++ PAGE_CACHE_SIZE : bytes - byte_offset; ++ ++ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", ++ bytes, i, byte_offset, available_bytes); ++ ++ if (i == page->index) { ++ pageaddr = kmap_atomic(page, KM_USER0); ++ memcpy(pageaddr, data_ptr + byte_offset, ++ available_bytes); ++ memset(pageaddr + available_bytes, 0, ++ PAGE_CACHE_SIZE - available_bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ } else if ((push_page = ++ grab_cache_page_nowait(page->mapping, i))) { ++ pageaddr = kmap_atomic(push_page, KM_USER0); ++ ++ memcpy(pageaddr, data_ptr + byte_offset, ++ available_bytes); ++ memset(pageaddr + available_bytes, 0, ++ PAGE_CACHE_SIZE - available_bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(push_page); ++ SetPageUptodate(push_page); ++ unlock_page(push_page); ++ page_cache_release(push_page); ++ } ++ } ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < (i_size_read(inode) >> ++ sblk->block_log)) ++ up(&msblk->read_page_mutex); ++ else ++ release_cached_fragment(msblk, fragment); ++ ++ return 0; ++ ++skip_read: ++ pageaddr = kmap_atomic(page, KM_USER0); ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++static int squashfs_readpage4K(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned char block_list[SIZE]; ++ long long block; ++ unsigned int bsize, bytes = 0; ++ void *pageaddr; ++ ++ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", ++ page->index, ++ SQUASHFS_I(inode)->start_block); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) { ++ pageaddr = kmap_atomic(page, KM_USER0); ++ goto skip_read; ++ } ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || page->index < (i_size_read(inode) >> ++ sblk->block_log)) { ++ block = (msblk->read_blocklist)(inode, page->index, 1, ++ block_list, NULL, &bsize); ++ ++ down(&msblk->read_page_mutex); ++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, ++ bsize, NULL); ++ pageaddr = kmap_atomic(page, KM_USER0); ++ if (bytes) ++ memcpy(pageaddr, msblk->read_page, bytes); ++ else ++ ERROR("Unable to read page, block %llx, size %x\n", ++ block, bsize); ++ up(&msblk->read_page_mutex); ++ } else { ++ struct squashfs_fragment_cache *fragment = ++ get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ SQUASHFS_I(inode)-> u.s1.fragment_size); ++ pageaddr = kmap_atomic(page, KM_USER0); ++ if (fragment) { ++ bytes = i_size_read(inode) & (sblk->block_size - 1); ++ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> ++ u.s1.fragment_offset, bytes); ++ release_cached_fragment(msblk, fragment); ++ } else ++ ERROR("Unable to read page, block %llx, size %x\n", ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, (int) ++ SQUASHFS_I(inode)-> u.s1.fragment_size); ++ } ++ ++skip_read: ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ f_pos =- 3; ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) &index, ++ index_start, index_offset, ++ sizeof(index), &index_start, ++ &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, ++ &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length + 3; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer; ++ char str[SQUASHFS_NAME_LEN + 1]; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) index, ++ index_start, index_offset, ++ sizeof(struct squashfs_dir_index), ++ &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, ++ index_offset, index->size + 1, ++ &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ return length + 3; ++} ++ ++ ++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++ dir_count; ++ struct squashfs_dir_header dirh; ++ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); ++ ++ while(file->f_pos < 3) { ++ char *name; ++ int size, i_ino; ++ ++ if(file->f_pos == 0) { ++ name = "."; ++ size = 1; ++ i_ino = i->i_ino; ++ } else { ++ name = ".."; ++ size = 2; ++ i_ino = SQUASHFS_I(i)->u.s2.parent_inode; ++ } ++ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", ++ (unsigned int) dirent, name, size, (int) ++ file->f_pos, i_ino, ++ squashfs_filetype_table[1]); ++ ++ if (filldir(dirent, name, size, ++ file->f_pos, i_ino, ++ squashfs_filetype_table[1]) < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos += size; ++ dirs_read++; ++ } ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, ++ file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block, next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block, next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, ++ dire->size + 1, &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", ++ (unsigned int) dirent, dire->name, ++ dire->size + 1, (int) file->f_pos, ++ dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, ++ file->f_pos, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]) ++ < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ dirs_read++; ++ } ++ } ++ ++finish: ++ return dirs_read; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header dirh; ++ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN]; ++ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_loop; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, ++ len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block,next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block,next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, dire->size + 1, ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (name[0] < dire->name[0]) ++ goto exit_loop; ++ ++ if ((len == dire->size + 1) && !strncmp(name, ++ dire->name, len)) { ++ squashfs_inode_t ino = ++ SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory " ++ "entry %s, inode %x:%x, %d\n", name, ++ dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number); ++ ++ inode = (msblk->iget)(i->i_sb, ino); ++ ++ goto exit_loop; ++ } ++ } ++ } ++ ++exit_loop: ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_loop; ++} ++ ++ ++static void squashfs_put_super(struct super_block *s) ++{ ++ int i; ++ ++ if (s->s_fs_info) { ++ struct squashfs_sb_info *sbi = s->s_fs_info; ++ if (sbi->block_cache) ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ if (sbi->block_cache[i].block != ++ SQUASHFS_INVALID_BLK) ++ kfree(sbi->block_cache[i].data); ++ if (sbi->fragment) ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) ++ SQUASHFS_FREE(sbi->fragment[i].data); ++ kfree(sbi->fragment); ++ kfree(sbi->block_cache); ++ kfree(sbi->read_data); ++ kfree(sbi->read_page); ++ kfree(sbi->uid); ++ kfree(sbi->fragment_index); ++ kfree(sbi->fragment_index_2); ++ kfree(sbi->meta_index); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ } ++} ++ ++ ++static int squashfs_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, mnt); ++} ++ ++ ++static int __init init_squashfs_fs(void) ++{ ++ int err = init_inodecache(); ++ if (err) ++ goto out; ++ ++ printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " ++ "Phillip Lougher\n"); ++ ++ if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { ++ ERROR("Failed to allocate zlib workspace\n"); ++ destroy_inodecache(); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if ((err = register_filesystem(&squashfs_fs_type))) { ++ vfree(stream.workspace); ++ destroy_inodecache(); ++ } ++ ++out: ++ return err; ++} ++ ++ ++static void __exit exit_squashfs_fs(void) ++{ ++ vfree(stream.workspace); ++ unregister_filesystem(&squashfs_fs_type); ++ destroy_inodecache(); ++} ++ ++ ++static struct kmem_cache * squashfs_inode_cachep; ++ ++ ++static struct inode *squashfs_alloc_inode(struct super_block *sb) ++{ ++ struct squashfs_inode_info *ei; ++ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); ++ if (!ei) ++ return NULL; ++ return &ei->vfs_inode; ++} ++ ++ ++static void squashfs_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode)); ++} ++ ++ ++static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) ++{ ++ struct squashfs_inode_info *ei = foo; ++ ++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == ++ SLAB_CTOR_CONSTRUCTOR) ++ inode_init_once(&ei->vfs_inode); ++} ++ ++ ++static int __init init_inodecache(void) ++{ ++ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", ++ sizeof(struct squashfs_inode_info), ++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, ++ init_once, NULL); ++ if (squashfs_inode_cachep == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++ ++static void destroy_inodecache(void) ++{ ++ kmem_cache_destroy(squashfs_inode_cachep); ++} ++ ++ ++module_init(init_squashfs_fs); ++module_exit(exit_squashfs_fs); ++MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); ++MODULE_AUTHOR("Phillip Lougher "); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.21.7/fs/squashfs/Makefile +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/squashfs/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the linux squashfs routines. ++# ++ ++obj-$(CONFIG_SQUASHFS) += squashfs.o ++squashfs-y += inode.o ++squashfs-y += squashfs2_0.o +Index: linux-2.6.21.7/fs/squashfs/squashfs2_0.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/squashfs/squashfs2_0.c +@@ -0,0 +1,758 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs2_0.c ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs.h" ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir); ++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *, ++ struct nameidata *); ++ ++static struct file_operations squashfs_dir_ops_2 = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir_2 ++}; ++ ++static struct inode_operations squashfs_dir_inode_ops_2 = { ++ .lookup = squashfs_lookup_2 ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static int read_fragment_index_table_2(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ return 0; ++ } ++ ++ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && ++ !squashfs_read_data(s, (char *) ++ msblk->fragment_index_2, ++ sblk->fragment_table_start, ++ SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ unsigned int fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments); ++ i++) { ++ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment), ++ &msblk->fragment_index_2[i], 1); ++ msblk->fragment_index_2[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int get_fragment_location_2(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment); ++ struct squashfs_fragment_entry_2 fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry_2 sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++ start_block, offset, ++ sizeof(sfragment_entry), &start_block, ++ &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++ start_block, offset, ++ sizeof(fragment_entry), &start_block, ++ &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ struct inode *i = new_inode(s); ++ ++ if (i) { ++ i->i_ino = ino; ++ i->i_mtime.tv_sec = sblk->mkfs_time; ++ i->i_atime.tv_sec = sblk->mkfs_time; ++ i->i_ctime.tv_sec = sblk->mkfs_time; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_nlink = 1; ++ i->i_size = 0; ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++ } ++ ++ return i; ++} ++ ++ ++static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode) ++{ ++ struct inode *i; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned int block = SQUASHFS_INODE_BLK(inode) + ++ sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ unsigned int ino = SQUASHFS_MK_VFS_INODE(block ++ - sblk->inode_table_start, offset); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header_2 id, sid; ++ struct squashfs_base_inode_header_2 *inodeb = &id.base, ++ *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_iget\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++ offset, sizeof(*sinodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb, ++ sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++ offset, sizeof(*inodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ struct squashfs_reg_inode_header_2 *inodep = &id.reg; ++ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg; ++ long long frag_blk; ++ unsigned int frag_size; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location_2(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ i->i_blksize = PAGE_CACHE_SIZE; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %x, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header_2 *inodep = &id.dir; ++ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = ++ next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = ++ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header_2 *inodep = ++ &id.symlink; ++ struct squashfs_symlink_inode_header_2 *sinodep = ++ &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header_2 *inodep = &id.dev; ++ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_mode |= (inodeb->inode_type == ++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : ++ S_IFBLK; ++ init_special_inode(i, i->i_mode, ++ old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ insert_inode_hash(i); ++ return i; ++ ++failed_read: ++ ERROR("Unable to read inode [%x:%x]\n", block, offset); ++ ++failed_read1: ++ return NULL; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index_2 index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) &index, ++ index_start, index_offset, ++ sizeof(index), &index_start, ++ &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, ++ &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer; ++ char str[SQUASHFS_NAME_LEN + 1]; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) index, ++ index_start, index_offset, ++ sizeof(struct squashfs_dir_index_2), ++ &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, ++ index_offset, index->size + 1, ++ &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ return length; ++} ++ ++ ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++ ++ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, ++ file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block, next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block, next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, ++ dire->size + 1, &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", ++ (unsigned int) dirent, dire->name, ++ dire->size + 1, (int) file->f_pos, ++ dirh.start_block, dire->offset, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, ++ file->f_pos, SQUASHFS_MK_VFS_INODE( ++ dirh.start_block, dire->offset), ++ squashfs_filetype_table[dire->type]) ++ < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ dirs_read++; ++ } ++ } ++ ++finish: ++ return dirs_read; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN]; ++ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; ++ ++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_loop; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, ++ len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block,next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block,next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, dire->size + 1, ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (sorted && name[0] < dire->name[0]) ++ goto exit_loop; ++ ++ if ((len == dire->size + 1) && !strncmp(name, ++ dire->name, len)) { ++ squashfs_inode_t ino = ++ SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory " ++ "entry %s, inode %x:%x, %lld\n", name, ++ dirh.start_block, dire->offset, ino); ++ ++ inode = (msblk->iget)(i->i_sb, ino); ++ ++ goto exit_loop; ++ } ++ } ++ } ++ ++exit_loop: ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_loop; ++} ++ ++ ++int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->iget = squashfs_iget_2; ++ msblk->read_fragment_index_table = read_fragment_index_table_2; ++ ++ sblk->bytes_used = sblk->bytes_used_2; ++ sblk->uid_start = sblk->uid_start_2; ++ sblk->guid_start = sblk->guid_start_2; ++ sblk->inode_table_start = sblk->inode_table_start_2; ++ sblk->directory_table_start = sblk->directory_table_start_2; ++ sblk->fragment_table_start = sblk->fragment_table_start_2; ++ ++ return 1; ++} +Index: linux-2.6.21.7/fs/squashfs/squashfs.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/squashfs/squashfs.h +@@ -0,0 +1,86 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs.h ++ */ ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#endif ++ ++#ifdef SQUASHFS_TRACE ++#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) ++#else ++#define TRACE(s, args...) {} ++#endif ++ ++#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) ++ ++#define SERROR(s, args...) do { \ ++ if (!silent) \ ++ printk(KERN_ERR "SQUASHFS error: "s, ## args);\ ++ } while(0) ++ ++#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) ++ ++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) ++{ ++ return list_entry(inode, struct squashfs_inode_info, vfs_inode); ++} ++ ++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY) ++#define SQSH_EXTERN ++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index); ++extern int squashfs_get_cached_block(struct super_block *s, char *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset); ++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++ squashfs_fragment_cache *fragment); ++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++ *s, long long start_block, ++ int length); ++extern struct address_space_operations squashfs_symlink_aops; ++extern struct address_space_operations squashfs_aops; ++extern struct address_space_operations squashfs_aops_4K; ++extern struct inode_operations squashfs_dir_inode_ops; ++#else ++#define SQSH_EXTERN static ++#endif ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif +Index: linux-2.6.21.7/include/linux/squashfs_fs.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/squashfs_fs.h +@@ -0,0 +1,911 @@ ++#ifndef SQUASHFS_FS ++#define SQUASHFS_FS ++ ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs.h ++ */ ++ ++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif ++ ++#ifdef CONFIG_SQUASHFS_VMALLOC ++#define SQUASHFS_ALLOC(a) vmalloc(a) ++#define SQUASHFS_FREE(a) vfree(a) ++#else ++#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL) ++#define SQUASHFS_FREE(a) kfree(a) ++#endif ++#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE ++#define SQUASHFS_MAJOR 3 ++#define SQUASHFS_MINOR 0 ++#define SQUASHFS_MAGIC 0x73717368 ++#define SQUASHFS_MAGIC_SWAP 0x68737173 ++#define SQUASHFS_START 0 ++ ++/* size of metadata (inode and directory) blocks */ ++#define SQUASHFS_METADATA_SIZE 8192 ++#define SQUASHFS_METADATA_LOG 13 ++ ++/* default size of data blocks */ ++#define SQUASHFS_FILE_SIZE 65536 ++#define SQUASHFS_FILE_LOG 16 ++ ++#define SQUASHFS_FILE_MAX_SIZE 65536 ++ ++/* Max number of uids and gids */ ++#define SQUASHFS_UIDS 256 ++#define SQUASHFS_GUIDS 255 ++ ++/* Max length of filename (not 255) */ ++#define SQUASHFS_NAME_LEN 256 ++ ++#define SQUASHFS_INVALID ((long long) 0xffffffffffff) ++#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) ++#define SQUASHFS_INVALID_BLK ((long long) -1) ++#define SQUASHFS_USED_BLK ((long long) -2) ++ ++/* Filesystem flags */ ++#define SQUASHFS_NOI 0 ++#define SQUASHFS_NOD 1 ++#define SQUASHFS_CHECK 2 ++#define SQUASHFS_NOF 3 ++#define SQUASHFS_NO_FRAG 4 ++#define SQUASHFS_ALWAYS_FRAG 5 ++#define SQUASHFS_DUPLICATE 6 ++ ++#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) ++ ++#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOI) ++ ++#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOD) ++ ++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOF) ++ ++#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NO_FRAG) ++ ++#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_ALWAYS_FRAG) ++ ++#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_DUPLICATE) ++ ++#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_CHECK) ++ ++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ ++ duplicate_checking) (noi | (nod << 1) | (check_data << 2) \ ++ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ ++ (duplicate_checking << 6)) ++ ++/* Max number of types and file types */ ++#define SQUASHFS_DIR_TYPE 1 ++#define SQUASHFS_FILE_TYPE 2 ++#define SQUASHFS_SYMLINK_TYPE 3 ++#define SQUASHFS_BLKDEV_TYPE 4 ++#define SQUASHFS_CHRDEV_TYPE 5 ++#define SQUASHFS_FIFO_TYPE 6 ++#define SQUASHFS_SOCKET_TYPE 7 ++#define SQUASHFS_LDIR_TYPE 8 ++#define SQUASHFS_LREG_TYPE 9 ++ ++/* 1.0 filesystem type definitions */ ++#define SQUASHFS_TYPES 5 ++#define SQUASHFS_IPC_TYPE 0 ++ ++/* Flag whether block is compressed or uncompressed, bit is set if block is ++ * uncompressed */ ++#define SQUASHFS_COMPRESSED_BIT (1 << 15) ++ ++#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ ++ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) ++ ++#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) ++ ++#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) ++ ++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) ++ ++#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) ++ ++/* ++ * Inode number ops. Inodes consist of a compressed block number, and an ++ * uncompressed offset within that block ++ */ ++#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) ++ ++#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) ++ ++#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\ ++ << 16) + (B))) ++ ++/* Compute 32 bit VFS inode number from squashfs inode number */ ++#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ ++ ((b) >> 2) + 1)) ++/* XXX */ ++ ++/* Translate between VFS mode and squashfs mode */ ++#define SQUASHFS_MODE(a) ((a) & 0xfff) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry)) ++ ++#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ ++ sizeof(long long)) ++ ++/* cached data constants for filesystem */ ++#define SQUASHFS_CACHED_BLKS 8 ++ ++#define SQUASHFS_MAX_FILE_SIZE_LOG 64 ++ ++#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ ++ (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) ++ ++#define SQUASHFS_MARKER_BYTE 0xff ++ ++/* meta index cache */ ++#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) ++#define SQUASHFS_META_ENTRIES 31 ++#define SQUASHFS_META_NUMBER 8 ++#define SQUASHFS_SLOTS 4 ++ ++struct meta_entry { ++ long long data_block; ++ unsigned int index_block; ++ unsigned short offset; ++ unsigned short pad; ++}; ++ ++struct meta_index { ++ unsigned int inode_number; ++ unsigned int offset; ++ unsigned short entries; ++ unsigned short skip; ++ unsigned short locked; ++ unsigned short pad; ++ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; ++}; ++ ++ ++/* ++ * definitions for structures on disk ++ */ ++ ++typedef long long squashfs_block_t; ++typedef long long squashfs_inode_t; ++ ++struct squashfs_super_block { ++ unsigned int s_magic; ++ unsigned int inodes; ++ unsigned int bytes_used_2; ++ unsigned int uid_start_2; ++ unsigned int guid_start_2; ++ unsigned int inode_table_start_2; ++ unsigned int directory_table_start_2; ++ unsigned int s_major:16; ++ unsigned int s_minor:16; ++ unsigned int block_size_1:16; ++ unsigned int block_log:16; ++ unsigned int flags:8; ++ unsigned int no_uids:8; ++ unsigned int no_guids:8; ++ unsigned int mkfs_time /* time of filesystem creation */; ++ squashfs_inode_t root_inode; ++ unsigned int block_size; ++ unsigned int fragments; ++ unsigned int fragment_table_start_2; ++ long long bytes_used; ++ long long uid_start; ++ long long guid_start; ++ long long inode_table_start; ++ long long directory_table_start; ++ long long fragment_table_start; ++ long long unused; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_index { ++ unsigned int index; ++ unsigned int start_block; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_BASE_INODE_HEADER \ ++ unsigned int inode_type:4; \ ++ unsigned int mode:12; \ ++ unsigned int uid:8; \ ++ unsigned int guid:8; \ ++ unsigned int mtime; \ ++ unsigned int inode_number; ++ ++struct squashfs_base_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_lreg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ long long file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int parent_inode; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int i_count:16; ++ unsigned int parent_inode; ++ struct squashfs_dir_index index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header { ++ struct squashfs_base_inode_header base; ++ struct squashfs_dev_inode_header dev; ++ struct squashfs_symlink_inode_header symlink; ++ struct squashfs_reg_inode_header reg; ++ struct squashfs_lreg_inode_header lreg; ++ struct squashfs_dir_inode_header dir; ++ struct squashfs_ldir_inode_header ldir; ++ struct squashfs_ipc_inode_header ipc; ++}; ++ ++struct squashfs_dir_entry { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ int inode_number:16; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_header { ++ unsigned int count:8; ++ unsigned int start_block; ++ unsigned int inode_number; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry { ++ long long start_block; ++ unsigned int size; ++ unsigned int unused; ++} __attribute__ ((packed)); ++ ++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); ++extern int squashfs_uncompress_init(void); ++extern int squashfs_uncompress_exit(void); ++ ++/* ++ * macros to convert each packed bitfield structure from little endian to big ++ * endian and vice versa. These are needed when creating or using a filesystem ++ * on a machine with different byte ordering to the target architecture. ++ * ++ */ ++ ++#define SQUASHFS_SWAP_START \ ++ int bits;\ ++ int b_pos;\ ++ unsigned long long val;\ ++ unsigned char *s;\ ++ unsigned char *d; ++ ++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ ++ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ ++ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ ++ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ ++ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ ++ SQUASHFS_SWAP((s)->flags, d, 288, 8);\ ++ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ ++ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ ++ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ ++ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ ++ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ ++ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ ++ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ ++ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ ++ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ ++ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ ++ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ ++ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ ++ SQUASHFS_SWAP((s)->unused, d, 888, 64);\ ++} ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 64, 32); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header))\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dev_inode_header)); \ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_reg_inode_header));\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_lreg_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 224, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 147, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 155, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ ++ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 8);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 32);\ ++} ++ ++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 2);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 16)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ ++} ++ ++#define SQUASHFS_SWAP_INTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 4);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 32)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 64)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * bits / 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ bits)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++ ++struct squashfs_base_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int type:4; ++ unsigned int offset:4; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 4);\ ++ SQUASHFS_SWAP((s)->guid, d, 20, 4); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header_1));\ ++ SQUASHFS_SWAP((s)->type, d, 24, 4);\ ++ SQUASHFS_SWAP((s)->offset, d, 28, 4);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_1));\ ++ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_1));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_1));\ ++ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_1));\ ++ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 43, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ ++} ++ ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++ ++struct squashfs_dir_index_2 { ++ unsigned int index:27; ++ unsigned int start_block:29; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_base_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++ unsigned int i_count:16; ++ struct squashfs_dir_index_2 index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header_2 { ++ struct squashfs_base_inode_header_2 base; ++ struct squashfs_dev_inode_header_2 dev; ++ struct squashfs_symlink_inode_header_2 symlink; ++ struct squashfs_reg_inode_header_2 reg; ++ struct squashfs_dir_inode_header_2 dir; ++ struct squashfs_ldir_inode_header_2 ldir; ++ struct squashfs_ipc_inode_header_2 ipc; ++}; ++ ++struct squashfs_dir_header_2 { ++ unsigned int count:8; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_entry_2 { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry_2 { ++ unsigned int start_block; ++ unsigned int size; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_2)); \ ++ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_2));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_2));\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 51, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 59, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ ++ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 27);\ ++ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ ++ SQUASHFS_SWAP((s)->size, d, 56, 8);\ ++} ++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 32, 32);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2)) ++ ++#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ ++ sizeof(int)) ++ ++#endif ++ ++#ifdef __KERNEL__ ++ ++/* ++ * macros used to swap each structure entry, taking into account ++ * bitfields and different bitfield placing conventions on differing ++ * architectures ++ */ ++ ++#include ++ ++#ifdef __BIG_ENDIAN ++ /* convert from little endian to big endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, b_pos) ++#else ++ /* convert from big endian to little endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, 64 - tbits - b_pos) ++#endif ++ ++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ ++ b_pos = pos % 8;\ ++ val = 0;\ ++ s = (unsigned char *)p + (pos / 8);\ ++ d = ((unsigned char *) &val) + 7;\ ++ for(bits = 0; bits < (tbits + b_pos); bits += 8) \ ++ *d-- = *s++;\ ++ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ ++} ++ ++#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n); ++ ++#endif ++#endif +Index: linux-2.6.21.7/include/linux/squashfs_fs_i.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/squashfs_fs_i.h +@@ -0,0 +1,45 @@ ++#ifndef SQUASHFS_FS_I ++#define SQUASHFS_FS_I ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_i.h ++ */ ++ ++struct squashfs_inode_info { ++ long long start_block; ++ unsigned int offset; ++ union { ++ struct { ++ long long fragment_start_block; ++ unsigned int fragment_size; ++ unsigned int fragment_offset; ++ long long block_list_start; ++ } s1; ++ struct { ++ long long directory_index_start; ++ unsigned int directory_index_offset; ++ unsigned int directory_index_count; ++ unsigned int parent_inode; ++ } s2; ++ } u; ++ struct inode vfs_inode; ++}; ++#endif +Index: linux-2.6.21.7/include/linux/squashfs_fs_sb.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/squashfs_fs_sb.h +@@ -0,0 +1,74 @@ ++#ifndef SQUASHFS_FS_SB ++#define SQUASHFS_FS_SB ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_sb.h ++ */ ++ ++#include ++ ++struct squashfs_cache { ++ long long block; ++ int length; ++ long long next_index; ++ char *data; ++}; ++ ++struct squashfs_fragment_cache { ++ long long block; ++ int length; ++ unsigned int locked; ++ char *data; ++}; ++ ++struct squashfs_sb_info { ++ struct squashfs_super_block sblk; ++ int devblksize; ++ int devblksize_log2; ++ int swap; ++ struct squashfs_cache *block_cache; ++ struct squashfs_fragment_cache *fragment; ++ int next_cache; ++ int next_fragment; ++ int next_meta_index; ++ unsigned int *uid; ++ unsigned int *guid; ++ long long *fragment_index; ++ unsigned int *fragment_index_2; ++ unsigned int read_size; ++ char *read_data; ++ char *read_page; ++ struct semaphore read_data_mutex; ++ struct semaphore read_page_mutex; ++ struct semaphore block_cache_mutex; ++ struct semaphore fragment_mutex; ++ struct semaphore meta_index_mutex; ++ wait_queue_head_t waitq; ++ wait_queue_head_t fragment_wait_queue; ++ struct meta_index *meta_index; ++ struct inode *(*iget)(struct super_block *s, squashfs_inode_t \ ++ inode); ++ long long (*read_blocklist)(struct inode *inode, int \ ++ index, int readahead_blks, char *block_list, \ ++ unsigned short **block_p, unsigned int *bsize); ++ int (*read_fragment_index_table)(struct super_block *s); ++}; ++#endif +Index: linux-2.6.21.7/init/do_mounts_rd.c +=================================================================== +--- linux-2.6.21.7.orig/init/do_mounts_rd.c ++++ linux-2.6.21.7/init/do_mounts_rd.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in + * numbers could not be found. + * + * We currently check for the following magic numbers: ++ * squashfs + * minix + * ext2 + * romfs +@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start + struct ext2_super_block *ext2sb; + struct romfs_super_block *romfsb; + struct cramfs_super *cramfsb; ++ struct squashfs_super_block *squashfsb; + int nblocks = -1; + unsigned char *buf; + +@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start + ext2sb = (struct ext2_super_block *) buf; + romfsb = (struct romfs_super_block *) buf; + cramfsb = (struct cramfs_super *) buf; ++ squashfsb = (struct squashfs_super_block *) buf; + memset(buf, 0xe5, size); + + /* +@@ -101,6 +105,15 @@ identify_ramdisk_image(int fd, int start + goto done; + } + ++ /* squashfs is at block zero too */ ++ if (squashfsb->s_magic == SQUASHFS_MAGIC) { ++ printk(KERN_NOTICE ++ "RAMDISK: squashfs filesystem found at block %d\n", ++ start_block); ++ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; ++ goto done; ++ } ++ + /* + * Read block 1 to test for minix and ext2 superblock + */ diff --git a/target/linux/generic-2.6/patches-2.6.21/002-lzma_decompress.patch b/target/linux/generic-2.6/patches-2.6.21/002-lzma_decompress.patch new file mode 100644 index 0000000000..e4a00692f2 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/002-lzma_decompress.patch @@ -0,0 +1,794 @@ +Index: linux-2.6.21.7/include/linux/LzmaDecode.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/LzmaDecode.h +@@ -0,0 +1,100 @@ ++/* ++ LzmaDecode.h ++ LZMA Decoder interface ++ ++ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) ++ http://www.7-zip.org/ ++ ++ LZMA SDK is licensed under two licenses: ++ 1) GNU Lesser General Public License (GNU LGPL) ++ 2) Common Public License (CPL) ++ It means that you can select one of these two licenses and ++ follow rules of that license. ++ ++ SPECIAL EXCEPTION: ++ Igor Pavlov, as the author of this code, expressly permits you to ++ statically or dynamically link your code (or bind by name) to the ++ interfaces of this file without subjecting your linked code to the ++ terms of the CPL or GNU LGPL. Any modifications or additions ++ to this file, however, are subject to the LGPL or CPL terms. ++*/ ++ ++#ifndef __LZMADECODE_H ++#define __LZMADECODE_H ++ ++/* #define _LZMA_IN_CB */ ++/* Use callback for input data */ ++ ++/* #define _LZMA_OUT_READ */ ++/* Use read function for output data */ ++ ++/* #define _LZMA_PROB32 */ ++/* It can increase speed on some 32-bit CPUs, ++ but memory usage will be doubled in that case */ ++ ++/* #define _LZMA_LOC_OPT */ ++/* Enable local speed optimizations inside code */ ++ ++#ifndef UInt32 ++#ifdef _LZMA_UINT32_IS_ULONG ++#define UInt32 unsigned long ++#else ++#define UInt32 unsigned int ++#endif ++#endif ++ ++#ifdef _LZMA_PROB32 ++#define CProb UInt32 ++#else ++#define CProb unsigned short ++#endif ++ ++#define LZMA_RESULT_OK 0 ++#define LZMA_RESULT_DATA_ERROR 1 ++#define LZMA_RESULT_NOT_ENOUGH_MEM 2 ++ ++#ifdef _LZMA_IN_CB ++typedef struct _ILzmaInCallback ++{ ++ int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize); ++} ILzmaInCallback; ++#endif ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++/* ++bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb) ++bufferSize += 100 in case of _LZMA_OUT_READ ++by default CProb is unsigned short, ++but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int) ++*/ ++ ++#ifdef _LZMA_OUT_READ ++int LzmaDecoderInit( ++ unsigned char *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ unsigned char *dictionary, UInt32 dictionarySize, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ unsigned char *inStream, UInt32 inSize ++ #endif ++); ++#endif ++ ++int LzmaDecode( ++ unsigned char *buffer, ++ #ifndef _LZMA_OUT_READ ++ UInt32 bufferSize, ++ int lc, int lp, int pb, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback, ++ #else ++ unsigned char *inStream, UInt32 inSize, ++ #endif ++ #endif ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed); ++ ++#endif +Index: linux-2.6.21.7/lib/LzmaDecode.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/lib/LzmaDecode.c +@@ -0,0 +1,663 @@ ++/* ++ LzmaDecode.c ++ LZMA Decoder ++ ++ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) ++ http://www.7-zip.org/ ++ ++ LZMA SDK is licensed under two licenses: ++ 1) GNU Lesser General Public License (GNU LGPL) ++ 2) Common Public License (CPL) ++ It means that you can select one of these two licenses and ++ follow rules of that license. ++ ++ SPECIAL EXCEPTION: ++ Igor Pavlov, as the author of this code, expressly permits you to ++ statically or dynamically link your code (or bind by name) to the ++ interfaces of this file without subjecting your linked code to the ++ terms of the CPL or GNU LGPL. Any modifications or additions ++ to this file, however, are subject to the LGPL or CPL terms. ++*/ ++ ++#include ++ ++#ifndef Byte ++#define Byte unsigned char ++#endif ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++typedef struct _CRangeDecoder ++{ ++ Byte *Buffer; ++ Byte *BufferLim; ++ UInt32 Range; ++ UInt32 Code; ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *InCallback; ++ int Result; ++ #endif ++ int ExtraBytes; ++} CRangeDecoder; ++ ++Byte RangeDecoderReadByte(CRangeDecoder *rd) ++{ ++ if (rd->Buffer == rd->BufferLim) ++ { ++ #ifdef _LZMA_IN_CB ++ UInt32 size; ++ rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size); ++ rd->BufferLim = rd->Buffer + size; ++ if (size == 0) ++ #endif ++ { ++ rd->ExtraBytes = 1; ++ return 0xFF; ++ } ++ } ++ return (*rd->Buffer++); ++} ++ ++/* #define ReadByte (*rd->Buffer++) */ ++#define ReadByte (RangeDecoderReadByte(rd)) ++ ++void RangeDecoderInit(CRangeDecoder *rd, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ Byte *stream, UInt32 bufferSize ++ #endif ++ ) ++{ ++ int i; ++ #ifdef _LZMA_IN_CB ++ rd->InCallback = inCallback; ++ rd->Buffer = rd->BufferLim = 0; ++ #else ++ rd->Buffer = stream; ++ rd->BufferLim = stream + bufferSize; ++ #endif ++ rd->ExtraBytes = 0; ++ rd->Code = 0; ++ rd->Range = (0xFFFFFFFF); ++ for(i = 0; i < 5; i++) ++ rd->Code = (rd->Code << 8) | ReadByte; ++} ++ ++#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code; ++#define RC_FLUSH_VAR rd->Range = range; rd->Code = code; ++#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; } ++ ++UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits) ++{ ++ RC_INIT_VAR ++ UInt32 result = 0; ++ int i; ++ for (i = numTotalBits; i > 0; i--) ++ { ++ /* UInt32 t; */ ++ range >>= 1; ++ ++ result <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ result |= 1; ++ } ++ /* ++ t = (code - range) >> 31; ++ t &= 1; ++ code -= range & (t - 1); ++ result = (result + result) | (1 - t); ++ */ ++ RC_NORMALIZE ++ } ++ RC_FLUSH_VAR ++ return result; ++} ++ ++int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd) ++{ ++ UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob; ++ if (rd->Code < bound) ++ { ++ rd->Range = bound; ++ *prob += (kBitModelTotal - *prob) >> kNumMoveBits; ++ if (rd->Range < kTopValue) ++ { ++ rd->Code = (rd->Code << 8) | ReadByte; ++ rd->Range <<= 8; ++ } ++ return 0; ++ } ++ else ++ { ++ rd->Range -= bound; ++ rd->Code -= bound; ++ *prob -= (*prob) >> kNumMoveBits; ++ if (rd->Range < kTopValue) ++ { ++ rd->Code = (rd->Code << 8) | ReadByte; ++ rd->Range <<= 8; ++ } ++ return 1; ++ } ++} ++ ++#define RC_GET_BIT2(prob, mi, A0, A1) \ ++ UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \ ++ if (code < bound) \ ++ { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \ ++ else \ ++ { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \ ++ RC_NORMALIZE ++ ++#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;) ++ ++int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) ++{ ++ int mi = 1; ++ int i; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ for(i = numLevels; i > 0; i--) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + mi; ++ RC_GET_BIT(prob, mi) ++ #else ++ mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd); ++ #endif ++ } ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return mi - (1 << numLevels); ++} ++ ++int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) ++{ ++ int mi = 1; ++ int i; ++ int symbol = 0; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ for(i = 0; i < numLevels; i++) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + mi; ++ RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i)) ++ #else ++ int bit = RangeDecoderBitDecode(probs + mi, rd); ++ mi = mi + mi + bit; ++ symbol |= (bit << i); ++ #endif ++ } ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd) ++{ ++ int symbol = 1; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ do ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + symbol; ++ RC_GET_BIT(prob, symbol) ++ #else ++ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); ++ #endif ++ } ++ while (symbol < 0x100); ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte) ++{ ++ int symbol = 1; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ do ++ { ++ int bit; ++ int matchBit = (matchByte >> 7) & 1; ++ matchByte <<= 1; ++ #ifdef _LZMA_LOC_OPT ++ { ++ CProb *prob = probs + ((1 + matchBit) << 8) + symbol; ++ RC_GET_BIT2(prob, symbol, bit = 0, bit = 1) ++ } ++ #else ++ bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd); ++ symbol = (symbol << 1) | bit; ++ #endif ++ if (matchBit != bit) ++ { ++ while (symbol < 0x100) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + symbol; ++ RC_GET_BIT(prob, symbol) ++ #else ++ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); ++ #endif ++ } ++ break; ++ } ++ } ++ while (symbol < 0x100); ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState) ++{ ++ if(RangeDecoderBitDecode(p + LenChoice, rd) == 0) ++ return RangeDecoderBitTreeDecode(p + LenLow + ++ (posState << kLenNumLowBits), kLenNumLowBits, rd); ++ if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0) ++ return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid + ++ (posState << kLenNumMidBits), kLenNumMidBits, rd); ++ return kLenNumLowSymbols + kLenNumMidSymbols + ++ RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd); ++} ++ ++#define kNumStates 12 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#ifdef _LZMA_OUT_READ ++ ++typedef struct _LzmaVarState ++{ ++ CRangeDecoder RangeDecoder; ++ Byte *Dictionary; ++ UInt32 DictionarySize; ++ UInt32 DictionaryPos; ++ UInt32 GlobalPos; ++ UInt32 Reps[4]; ++ int lc; ++ int lp; ++ int pb; ++ int State; ++ int PreviousIsMatch; ++ int RemainLen; ++} LzmaVarState; ++ ++int LzmaDecoderInit( ++ unsigned char *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ unsigned char *dictionary, UInt32 dictionarySize, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ unsigned char *inStream, UInt32 inSize ++ #endif ++ ) ++{ ++ LzmaVarState *vs = (LzmaVarState *)buffer; ++ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); ++ UInt32 i; ++ if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState)) ++ return LZMA_RESULT_NOT_ENOUGH_MEM; ++ vs->Dictionary = dictionary; ++ vs->DictionarySize = dictionarySize; ++ vs->DictionaryPos = 0; ++ vs->GlobalPos = 0; ++ vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1; ++ vs->lc = lc; ++ vs->lp = lp; ++ vs->pb = pb; ++ vs->State = 0; ++ vs->PreviousIsMatch = 0; ++ vs->RemainLen = 0; ++ dictionary[dictionarySize - 1] = 0; ++ for (i = 0; i < numProbs; i++) ++ p[i] = kBitModelTotal >> 1; ++ RangeDecoderInit(&vs->RangeDecoder, ++ #ifdef _LZMA_IN_CB ++ inCallback ++ #else ++ inStream, inSize ++ #endif ++ ); ++ return LZMA_RESULT_OK; ++} ++ ++int LzmaDecode(unsigned char *buffer, ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed) ++{ ++ LzmaVarState *vs = (LzmaVarState *)buffer; ++ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); ++ CRangeDecoder rd = vs->RangeDecoder; ++ int state = vs->State; ++ int previousIsMatch = vs->PreviousIsMatch; ++ Byte previousByte; ++ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; ++ UInt32 nowPos = 0; ++ UInt32 posStateMask = (1 << (vs->pb)) - 1; ++ UInt32 literalPosMask = (1 << (vs->lp)) - 1; ++ int lc = vs->lc; ++ int len = vs->RemainLen; ++ UInt32 globalPos = vs->GlobalPos; ++ ++ Byte *dictionary = vs->Dictionary; ++ UInt32 dictionarySize = vs->DictionarySize; ++ UInt32 dictionaryPos = vs->DictionaryPos; ++ ++ if (len == -1) ++ { ++ *outSizeProcessed = 0; ++ return LZMA_RESULT_OK; ++ } ++ ++ while(len > 0 && nowPos < outSize) ++ { ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ len--; ++ } ++ if (dictionaryPos == 0) ++ previousByte = dictionary[dictionarySize - 1]; ++ else ++ previousByte = dictionary[dictionaryPos - 1]; ++#else ++ ++int LzmaDecode( ++ Byte *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback, ++ #else ++ unsigned char *inStream, UInt32 inSize, ++ #endif ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); ++ CProb *p = (CProb *)buffer; ++ CRangeDecoder rd; ++ UInt32 i; ++ int state = 0; ++ int previousIsMatch = 0; ++ Byte previousByte = 0; ++ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; ++ UInt32 nowPos = 0; ++ UInt32 posStateMask = (1 << pb) - 1; ++ UInt32 literalPosMask = (1 << lp) - 1; ++ int len = 0; ++ if (bufferSize < numProbs * sizeof(CProb)) ++ return LZMA_RESULT_NOT_ENOUGH_MEM; ++ for (i = 0; i < numProbs; i++) ++ p[i] = kBitModelTotal >> 1; ++ RangeDecoderInit(&rd, ++ #ifdef _LZMA_IN_CB ++ inCallback ++ #else ++ inStream, inSize ++ #endif ++ ); ++#endif ++ ++ *outSizeProcessed = 0; ++ while(nowPos < outSize) ++ { ++ int posState = (int)( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ & posStateMask); ++ #ifdef _LZMA_IN_CB ++ if (rd.Result != LZMA_RESULT_OK) ++ return rd.Result; ++ #endif ++ if (rd.ExtraBytes != 0) ++ return LZMA_RESULT_DATA_ERROR; ++ if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0) ++ { ++ CProb *probs = p + Literal + (LZMA_LIT_SIZE * ++ ((( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ & literalPosMask) << lc) + (previousByte >> (8 - lc)))); ++ ++ if (state < 4) state = 0; ++ else if (state < 10) state -= 3; ++ else state -= 6; ++ if (previousIsMatch) ++ { ++ Byte matchByte; ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ matchByte = dictionary[pos]; ++ #else ++ matchByte = outStream[nowPos - rep0]; ++ #endif ++ previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte); ++ previousIsMatch = 0; ++ } ++ else ++ previousByte = LzmaLiteralDecode(probs, &rd); ++ outStream[nowPos++] = previousByte; ++ #ifdef _LZMA_OUT_READ ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #endif ++ } ++ else ++ { ++ previousIsMatch = 1; ++ if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1) ++ { ++ if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0) ++ { ++ if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0) ++ { ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos; ++ #endif ++ if ( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ == 0) ++ return LZMA_RESULT_DATA_ERROR; ++ state = state < 7 ? 9 : 11; ++ #ifdef _LZMA_OUT_READ ++ pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ previousByte = dictionary[pos]; ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #else ++ previousByte = outStream[nowPos - rep0]; ++ #endif ++ outStream[nowPos++] = previousByte; ++ continue; ++ } ++ } ++ else ++ { ++ UInt32 distance; ++ if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0) ++ distance = rep1; ++ else ++ { ++ if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0) ++ distance = rep2; ++ else ++ { ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ len = LzmaLenDecode(p + RepLenCoder, &rd, posState); ++ state = state < 7 ? 8 : 11; ++ } ++ else ++ { ++ int posSlot; ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ state = state < 7 ? 7 : 10; ++ len = LzmaLenDecode(p + LenCoder, &rd, posState); ++ posSlot = RangeDecoderBitTreeDecode(p + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits), kNumPosSlotBits, &rd); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits); ++ if (posSlot < kEndPosModelIndex) ++ { ++ rep0 += RangeDecoderReverseBitTreeDecode( ++ p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd); ++ } ++ else ++ { ++ rep0 += RangeDecoderDecodeDirectBits(&rd, ++ numDirectBits - kNumAlignBits) << kNumAlignBits; ++ rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd); ++ } ++ } ++ else ++ rep0 = posSlot; ++ rep0++; ++ } ++ if (rep0 == (UInt32)(0)) ++ { ++ /* it's for stream version */ ++ len = -1; ++ break; ++ } ++ if (rep0 > nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ { ++ return LZMA_RESULT_DATA_ERROR; ++ } ++ len += kMatchMinLen; ++ do ++ { ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ previousByte = dictionary[pos]; ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #else ++ previousByte = outStream[nowPos - rep0]; ++ #endif ++ outStream[nowPos++] = previousByte; ++ len--; ++ } ++ while(len > 0 && nowPos < outSize); ++ } ++ } ++ ++ #ifdef _LZMA_OUT_READ ++ vs->RangeDecoder = rd; ++ vs->DictionaryPos = dictionaryPos; ++ vs->GlobalPos = globalPos + nowPos; ++ vs->Reps[0] = rep0; ++ vs->Reps[1] = rep1; ++ vs->Reps[2] = rep2; ++ vs->Reps[3] = rep3; ++ vs->State = state; ++ vs->PreviousIsMatch = previousIsMatch; ++ vs->RemainLen = len; ++ #endif ++ ++ *outSizeProcessed = nowPos; ++ return LZMA_RESULT_OK; ++} +Index: linux-2.6.21.7/lib/Makefile +=================================================================== +--- linux-2.6.21.7.orig/lib/Makefile ++++ linux-2.6.21.7/lib/Makefile +@@ -12,7 +12,7 @@ lib-$(CONFIG_SMP) += cpumask.o + + lib-y += kobject.o kref.o kobject_uevent.o klist.o + +-obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o ++obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o LzmaDecode.o + + ifeq ($(CONFIG_DEBUG_KOBJECT),y) + CFLAGS_kobject.o += -DDEBUG +@@ -56,6 +56,7 @@ obj-$(CONFIG_SMP) += percpu_counter.o + obj-$(CONFIG_AUDIT_GENERIC) += audit.o + + obj-$(CONFIG_SWIOTLB) += swiotlb.o ++ + obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o + + lib-$(CONFIG_GENERIC_BUG) += bug.o diff --git a/target/linux/generic-2.6/patches-2.6.21/003-squashfs_lzma.patch b/target/linux/generic-2.6/patches-2.6.21/003-squashfs_lzma.patch new file mode 100644 index 0000000000..e476c76162 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/003-squashfs_lzma.patch @@ -0,0 +1,109 @@ +Index: linux-2.6.21.7/fs/squashfs/inode.c +=================================================================== +--- linux-2.6.21.7.orig/fs/squashfs/inode.c ++++ linux-2.6.21.7/fs/squashfs/inode.c +@@ -4,6 +4,9 @@ + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher + * ++ * LZMA decompressor support added by Oleg I. Vdovikin ++ * Copyright (c) 2005 Oleg I.Vdovikin ++ * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, +@@ -21,6 +24,7 @@ + * inode.c + */ + ++#define SQUASHFS_LZMA + #include + #include + #include +@@ -44,6 +48,19 @@ + + #include "squashfs.h" + ++#ifdef SQUASHFS_LZMA ++#include ++ ++/* default LZMA settings, should be in sync with mksquashfs */ ++#define LZMA_LC 3 ++#define LZMA_LP 0 ++#define LZMA_PB 2 ++ ++#define LZMA_WORKSPACE_SIZE ((LZMA_BASE_SIZE + \ ++ (LZMA_LIT_SIZE << (LZMA_LC + LZMA_LP))) * sizeof(CProb)) ++ ++#endif ++ + static void squashfs_put_super(struct super_block *); + static int squashfs_statfs(struct dentry *, struct kstatfs *); + static int squashfs_symlink_readpage(struct file *file, struct page *page); +@@ -64,7 +81,11 @@ static int squashfs_get_sb(struct file_s + const char *, void *, struct vfsmount *); + + ++#ifdef SQUASHFS_LZMA ++static unsigned char lzma_workspace[LZMA_WORKSPACE_SIZE]; ++#else + static z_stream stream; ++#endif + + static struct file_system_type squashfs_fs_type = { + .owner = THIS_MODULE, +@@ -249,6 +270,15 @@ SQSH_EXTERN unsigned int squashfs_read_d + if (compressed) { + int zlib_err; + ++#ifdef SQUASHFS_LZMA ++ if ((zlib_err = LzmaDecode(lzma_workspace, ++ LZMA_WORKSPACE_SIZE, LZMA_LC, LZMA_LP, LZMA_PB, ++ c_buffer, c_byte, buffer, msblk->read_size, &bytes)) != LZMA_RESULT_OK) ++ { ++ ERROR("lzma returned unexpected result 0x%x\n", zlib_err); ++ bytes = 0; ++ } ++#else + stream.next_in = c_buffer; + stream.avail_in = c_byte; + stream.next_out = buffer; +@@ -263,7 +293,7 @@ SQSH_EXTERN unsigned int squashfs_read_d + bytes = 0; + } else + bytes = stream.total_out; +- ++#endif + up(&msblk->read_data_mutex); + } + +@@ -2045,15 +2075,19 @@ static int __init init_squashfs_fs(void) + printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " + "Phillip Lougher\n"); + ++#ifndef SQUASHFS_LZMA + if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { + ERROR("Failed to allocate zlib workspace\n"); + destroy_inodecache(); + err = -ENOMEM; + goto out; + } ++#endif + + if ((err = register_filesystem(&squashfs_fs_type))) { ++#ifndef SQUASHFS_LZMA + vfree(stream.workspace); ++#endif + destroy_inodecache(); + } + +@@ -2064,7 +2098,9 @@ out: + + static void __exit exit_squashfs_fs(void) + { ++#ifndef SQUASHFS_LZMA + vfree(stream.workspace); ++#endif + unregister_filesystem(&squashfs_fs_type); + destroy_inodecache(); + } diff --git a/target/linux/generic-2.6/patches-2.6.21/004-extra_optimization.patch b/target/linux/generic-2.6/patches-2.6.21/004-extra_optimization.patch new file mode 100644 index 0000000000..3a08e13431 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/004-extra_optimization.patch @@ -0,0 +1,14 @@ +Index: linux-2.6.21.7/Makefile +=================================================================== +--- linux-2.6.21.7.orig/Makefile ++++ linux-2.6.21.7/Makefile +@@ -507,6 +507,9 @@ CFLAGS += $(call cc-option, -fn + NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) + CHECKFLAGS += $(NOSTDINC_FLAGS) + ++# improve gcc optimization ++CFLAGS += $(call cc-option,-funit-at-a-time,) ++ + # warn about C99 declaration after statement + CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) + diff --git a/target/linux/generic-2.6/patches-2.6.21/006-gcc4_inline_fix.patch b/target/linux/generic-2.6/patches-2.6.21/006-gcc4_inline_fix.patch new file mode 100644 index 0000000000..2c147b5e45 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/006-gcc4_inline_fix.patch @@ -0,0 +1,13 @@ +Index: linux-2.6.21.7/include/asm-mips/system.h +=================================================================== +--- linux-2.6.21.7.orig/include/asm-mips/system.h ++++ linux-2.6.21.7/include/asm-mips/system.h +@@ -188,7 +188,7 @@ extern __u64 __xchg_u64_unsupported_on_3 + if something tries to do an invalid xchg(). */ + extern void __xchg_called_with_bad_pointer(void); + +-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) ++static __always_inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) + { + switch (size) { + case 4: diff --git a/target/linux/generic-2.6/patches-2.6.21/007-samsung_flash.patch b/target/linux/generic-2.6/patches-2.6.21/007-samsung_flash.patch new file mode 100644 index 0000000000..6878633913 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/007-samsung_flash.patch @@ -0,0 +1,38 @@ +Index: linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0002.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/mtd/chips/cfi_cmdset_0002.c ++++ linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -51,6 +51,7 @@ + #define SST49LF040B 0x0050 + #define SST49LF008A 0x005a + #define AT49BV6416 0x00d6 ++#define MANUFACTURER_SAMSUNG 0x00ec + + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +@@ -294,12 +295,19 @@ struct mtd_info *cfi_cmdset_0002(struct + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { +- printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); +- kfree(mtd); +- return NULL; ++ if (cfi->mfr == MANUFACTURER_SAMSUNG && ++ (extp->MajorVersion == '3' && extp->MinorVersion == '3')) { ++ printk(KERN_NOTICE " Newer Samsung flash detected, " ++ "should be compatibile with Amd/Fujitsu.\n"); ++ } ++ else { ++ printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " ++ "version %c.%c.\n", extp->MajorVersion, ++ extp->MinorVersion); ++ kfree(extp); ++ kfree(mtd); ++ return NULL; ++ } + } + + /* Install our own private info structure */ diff --git a/target/linux/generic-2.6/patches-2.6.21/009-revert_intel_flash_breakage.patch b/target/linux/generic-2.6/patches-2.6.21/009-revert_intel_flash_breakage.patch new file mode 100644 index 0000000000..8c7684a5fb --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/009-revert_intel_flash_breakage.patch @@ -0,0 +1,171 @@ +Index: linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0001.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/mtd/chips/cfi_cmdset_0001.c ++++ linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0001.c +@@ -919,7 +919,7 @@ static void __xipram xip_enable(struct m + + static int __xipram xip_wait_for_operation( + struct map_info *map, struct flchip *chip, +- unsigned long adr, unsigned int chip_op_time ) ++ unsigned long adr, int *chip_op_time ) + { + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; +@@ -928,7 +928,7 @@ static int __xipram xip_wait_for_operati + flstate_t oldstate, newstate; + + start = xip_currtime(); +- usec = chip_op_time * 8; ++ usec = *chip_op_time * 8; + if (usec == 0) + usec = 500000; + done = 0; +@@ -1038,8 +1038,8 @@ static int __xipram xip_wait_for_operati + #define XIP_INVAL_CACHED_RANGE(map, from, size) \ + INVALIDATE_CACHED_RANGE(map, from, size) + +-#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, usec) \ +- xip_wait_for_operation(map, chip, cmd_adr, usec) ++#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, p_usec) \ ++ xip_wait_for_operation(map, chip, cmd_adr, p_usec) + + #else + +@@ -1051,65 +1051,65 @@ static int __xipram xip_wait_for_operati + static int inval_cache_and_wait_for_operation( + struct map_info *map, struct flchip *chip, + unsigned long cmd_adr, unsigned long inval_adr, int inval_len, +- unsigned int chip_op_time) ++ int *chip_op_time ) + { + struct cfi_private *cfi = map->fldrv_priv; + map_word status, status_OK = CMD(0x80); +- int chip_state = chip->state; +- unsigned int timeo, sleep_time; ++ int z, chip_state = chip->state; ++ unsigned long timeo; + + spin_unlock(chip->mutex); + if (inval_len) + INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); ++ if (*chip_op_time) ++ cfi_udelay(*chip_op_time); + spin_lock(chip->mutex); + +- /* set our timeout to 8 times the expected delay */ +- timeo = chip_op_time * 8; +- if (!timeo) +- timeo = 500000; +- sleep_time = chip_op_time / 2; ++ timeo = *chip_op_time * 8 * HZ / 1000000; ++ if (timeo < HZ/2) ++ timeo = HZ/2; ++ timeo += jiffies; + ++ z = 0; + for (;;) { ++ if (chip->state != chip_state) { ++ /* Someone's suspended the operation: sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ spin_unlock(chip->mutex); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ timeo = jiffies + (HZ / 2); /* FIXME */ ++ spin_lock(chip->mutex); ++ continue; ++ } ++ + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) + break; + +- if (!timeo) { ++ /* OK Still waiting */ ++ if (time_after(jiffies, timeo)) { + map_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; + return -ETIME; + } + +- /* OK Still waiting. Drop the lock, wait a while and retry. */ ++ /* Latency issues. Drop the lock, wait a while and retry */ ++ z++; + spin_unlock(chip->mutex); +- if (sleep_time >= 1000000/HZ) { +- /* +- * Half of the normal delay still remaining +- * can be performed with a sleeping delay instead +- * of busy waiting. +- */ +- msleep(sleep_time/1000); +- timeo -= sleep_time; +- sleep_time = 1000000/HZ; +- } else { +- udelay(1); +- cond_resched(); +- timeo--; +- } ++ cfi_udelay(1); + spin_lock(chip->mutex); +- +- while (chip->state != chip_state) { +- /* Someone's suspended the operation: sleep */ +- DECLARE_WAITQUEUE(wait, current); +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- spin_lock(chip->mutex); +- } + } + ++ if (!z) { ++ if (!--(*chip_op_time)) ++ *chip_op_time = 1; ++ } else if (z > 1) ++ ++(*chip_op_time); ++ + /* Done and happy. */ + chip->state = FL_STATUS; + return 0; +@@ -1118,7 +1118,8 @@ static int inval_cache_and_wait_for_oper + #endif + + #define WAIT_TIMEOUT(map, chip, adr, udelay) \ +- INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay); ++ ({ int __udelay = (udelay); \ ++ INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, &__udelay); }) + + + static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) +@@ -1342,7 +1343,7 @@ static int __xipram do_write_oneword(str + + ret = INVAL_CACHE_AND_WAIT(map, chip, adr, + adr, map_bankwidth(map), +- chip->word_write_time); ++ &chip->word_write_time); + if (ret) { + xip_enable(map, chip, adr); + printk(KERN_ERR "%s: word write error (status timeout)\n", map->name); +@@ -1579,7 +1580,7 @@ static int __xipram do_write_buffer(stru + + ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, + adr, len, +- chip->buffer_write_time); ++ &chip->buffer_write_time); + if (ret) { + map_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; +@@ -1714,7 +1715,7 @@ static int __xipram do_erase_oneblock(st + + ret = INVAL_CACHE_AND_WAIT(map, chip, adr, + adr, len, +- chip->erase_time); ++ &chip->erase_time); + if (ret) { + map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; diff --git a/target/linux/generic-2.6/patches-2.6.21/010-disable_old_squashfs_compatibility.patch b/target/linux/generic-2.6/patches-2.6.21/010-disable_old_squashfs_compatibility.patch new file mode 100644 index 0000000000..55498617af --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/010-disable_old_squashfs_compatibility.patch @@ -0,0 +1,23 @@ +Index: linux-2.6.21.7/fs/squashfs/Makefile +=================================================================== +--- linux-2.6.21.7.orig/fs/squashfs/Makefile ++++ linux-2.6.21.7/fs/squashfs/Makefile +@@ -4,4 +4,3 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += inode.o +-squashfs-y += squashfs2_0.o +Index: linux-2.6.21.7/fs/squashfs/squashfs.h +=================================================================== +--- linux-2.6.21.7.orig/fs/squashfs/squashfs.h ++++ linux-2.6.21.7/fs/squashfs/squashfs.h +@@ -24,6 +24,9 @@ + #ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY + #undef CONFIG_SQUASHFS_1_0_COMPATIBILITY + #endif ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif + + #ifdef SQUASHFS_TRACE + #define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) diff --git a/target/linux/generic-2.6/patches-2.6.21/011-mips_boot.patch b/target/linux/generic-2.6/patches-2.6.21/011-mips_boot.patch new file mode 100644 index 0000000000..866b3f01ef --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/011-mips_boot.patch @@ -0,0 +1,21 @@ +Index: linux-2.6.21.7/arch/mips/kernel/head.S +=================================================================== +--- linux-2.6.21.7.orig/arch/mips/kernel/head.S ++++ linux-2.6.21.7/arch/mips/kernel/head.S +@@ -129,11 +129,15 @@ + #endif + .endm + ++ ++ j kernel_entry ++ nop ++ + /* + * Reserved space for exception handlers. + * Necessary for machines which link their kernels at KSEG0. + */ +- .fill 0x400 ++ .align 10 + + EXPORT(stext) # used for profiling + EXPORT(_stext) diff --git a/target/linux/generic-2.6/patches-2.6.21/012-mips_cpu_tlb.patch b/target/linux/generic-2.6/patches-2.6.21/012-mips_cpu_tlb.patch new file mode 100644 index 0000000000..305cef0e3f --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/012-mips_cpu_tlb.patch @@ -0,0 +1,20 @@ +Index: linux-2.6.21.7/arch/mips/mm/tlbex.c +=================================================================== +--- linux-2.6.21.7.orig/arch/mips/mm/tlbex.c ++++ linux-2.6.21.7/arch/mips/mm/tlbex.c +@@ -887,7 +887,6 @@ static __init void build_tlb_write_entry + case CPU_R10000: + case CPU_R12000: + case CPU_R14000: +- case CPU_4KC: + case CPU_SB1: + case CPU_SB1A: + case CPU_4KSC: +@@ -915,6 +914,7 @@ static __init void build_tlb_write_entry + tlbw(p); + break; + ++ case CPU_4KC: + case CPU_4KEC: + case CPU_24K: + case CPU_34K: diff --git a/target/linux/generic-2.6/patches-2.6.21/013-mips_generic_gpio_support.patch b/target/linux/generic-2.6/patches-2.6.21/013-mips_generic_gpio_support.patch new file mode 100644 index 0000000000..c23519e378 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/013-mips_generic_gpio_support.patch @@ -0,0 +1,38 @@ +Index: linux-2.6.21.7/arch/mips/defconfig +=================================================================== +--- linux-2.6.21.7.orig/arch/mips/defconfig ++++ linux-2.6.21.7/arch/mips/defconfig +@@ -69,6 +69,7 @@ CONFIG_GENERIC_FIND_NEXT_BIT=y + CONFIG_GENERIC_HWEIGHT=y + CONFIG_GENERIC_CALIBRATE_DELAY=y + CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_GPIO=n + CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y + # CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set + CONFIG_ARC=y +Index: linux-2.6.21.7/arch/mips/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/arch/mips/Kconfig ++++ linux-2.6.21.7/arch/mips/Kconfig +@@ -869,6 +869,10 @@ config GENERIC_TIME + bool + default y + ++config GENERIC_GPIO ++ bool ++ default n ++ + config SCHED_NO_NO_OMIT_FRAME_POINTER + bool + default y +Index: linux-2.6.21.7/include/asm-mips/gpio.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/asm-mips/gpio.h +@@ -0,0 +1,6 @@ ++#ifndef _ASM_MIPS_GPIO_H ++#define _ASM_MIPS_GPIO_H ++ ++#include ++ ++#endif /* _ASM_MIPS_GPIO_H */ diff --git a/target/linux/generic-2.6/patches-2.6.21/060-rootfs_split.patch b/target/linux/generic-2.6/patches-2.6.21/060-rootfs_split.patch new file mode 100644 index 0000000000..210360600e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/060-rootfs_split.patch @@ -0,0 +1,492 @@ +Index: linux-2.6.21.7/drivers/mtd/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/drivers/mtd/Kconfig ++++ linux-2.6.21.7/drivers/mtd/Kconfig +@@ -49,6 +49,16 @@ config MTD_PARTITIONS + devices. Partitioning on NFTL 'devices' is a different - that's the + 'normal' form of partitioning used on a block device. + ++config MTD_ROOTFS_ROOT_DEV ++ bool "Automatically set 'rootfs' partition to be root filesystem" ++ depends on MTD_PARTITIONS ++ default y ++ ++config MTD_ROOTFS_SPLIT ++ bool "Automatically split 'rootfs' partition for squashfs" ++ depends on MTD_PARTITIONS ++ default y ++ + config MTD_REDBOOT_PARTS + tristate "RedBoot partition table parsing" + depends on MTD_PARTITIONS +Index: linux-2.6.21.7/drivers/mtd/mtdpart.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/mtd/mtdpart.c ++++ linux-2.6.21.7/drivers/mtd/mtdpart.c +@@ -20,6 +20,8 @@ + #include + #include + #include ++#include ++#include + + /* Our partition linked list */ + static LIST_HEAD(mtd_partitions); +@@ -308,6 +310,266 @@ int del_mtd_partitions(struct mtd_info * + return 0; + } + ++static u_int32_t cur_offset = 0; ++static int add_one_partition(struct mtd_info *master, const struct mtd_partition *part, ++ int i, struct mtd_part **slp) ++{ ++ struct mtd_part *slave; ++ ++ /* allocate the partition structure */ ++ slave = kzalloc (sizeof(*slave), GFP_KERNEL); ++ if (!slave) { ++ printk ("memory allocation error while creating partitions for \"%s\"\n", ++ master->name); ++ del_mtd_partitions(master); ++ return -ENOMEM; ++ } ++ list_add(&slave->list, &mtd_partitions); ++ ++ /* set up the MTD object for this partition */ ++ slave->mtd.type = master->type; ++ slave->mtd.flags = master->flags & ~part->mask_flags; ++ slave->mtd.size = part->size; ++ slave->mtd.writesize = master->writesize; ++ slave->mtd.oobsize = master->oobsize; ++ slave->mtd.oobavail = master->oobavail; ++ slave->mtd.subpage_sft = master->subpage_sft; ++ ++ slave->mtd.name = part->name; ++ slave->mtd.bank_size = master->bank_size; ++ slave->mtd.owner = master->owner; ++ ++ slave->mtd.read = part_read; ++ slave->mtd.write = part_write; ++ ++ if(master->point && master->unpoint){ ++ slave->mtd.point = part_point; ++ slave->mtd.unpoint = part_unpoint; ++ } ++ ++ if (master->read_oob) ++ slave->mtd.read_oob = part_read_oob; ++ if (master->write_oob) ++ slave->mtd.write_oob = part_write_oob; ++ if(master->read_user_prot_reg) ++ slave->mtd.read_user_prot_reg = part_read_user_prot_reg; ++ if(master->read_fact_prot_reg) ++ slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; ++ if(master->write_user_prot_reg) ++ slave->mtd.write_user_prot_reg = part_write_user_prot_reg; ++ if(master->lock_user_prot_reg) ++ slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; ++ if(master->get_user_prot_info) ++ slave->mtd.get_user_prot_info = part_get_user_prot_info; ++ if(master->get_fact_prot_info) ++ slave->mtd.get_fact_prot_info = part_get_fact_prot_info; ++ if (master->sync) ++ slave->mtd.sync = part_sync; ++ if (!i && master->suspend && master->resume) { ++ slave->mtd.suspend = part_suspend; ++ slave->mtd.resume = part_resume; ++ } ++ if (master->writev) ++ slave->mtd.writev = part_writev; ++ if (master->lock) ++ slave->mtd.lock = part_lock; ++ if (master->unlock) ++ slave->mtd.unlock = part_unlock; ++ if (master->block_isbad) ++ slave->mtd.block_isbad = part_block_isbad; ++ if (master->block_markbad) ++ slave->mtd.block_markbad = part_block_markbad; ++ slave->mtd.erase = part_erase; ++ slave->master = master; ++ slave->offset = part->offset; ++ slave->index = i; ++ ++ if (slave->offset == MTDPART_OFS_APPEND) ++ slave->offset = cur_offset; ++ if (slave->offset == MTDPART_OFS_NXTBLK) { ++ slave->offset = cur_offset; ++ if ((cur_offset % master->erasesize) != 0) { ++ /* Round up to next erasesize */ ++ slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; ++ printk(KERN_NOTICE "Moving partition %d: " ++ "0x%08x -> 0x%08x\n", i, ++ cur_offset, slave->offset); ++ } ++ } ++ if (slave->mtd.size == MTDPART_SIZ_FULL) ++ slave->mtd.size = master->size - slave->offset; ++ cur_offset = slave->offset + slave->mtd.size; ++ ++ printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, ++ slave->offset + slave->mtd.size, slave->mtd.name); ++ ++ /* let's do some sanity checks */ ++ if (slave->offset >= master->size) { ++ /* let's register it anyway to preserve ordering */ ++ slave->offset = 0; ++ slave->mtd.size = 0; ++ printk ("mtd: partition \"%s\" is out of reach -- disabled\n", ++ part->name); ++ } ++ if (slave->offset + slave->mtd.size > master->size) { ++ slave->mtd.size = master->size - slave->offset; ++ printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", ++ part->name, master->name, slave->mtd.size); ++ } ++ if (master->numeraseregions>1) { ++ /* Deal with variable erase size stuff */ ++ int i; ++ struct mtd_erase_region_info *regions = master->eraseregions; ++ ++ /* Find the first erase regions which is part of this partition. */ ++ for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) ++ ; ++ ++ for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { ++ if (slave->mtd.erasesize < regions[i].erasesize) { ++ slave->mtd.erasesize = regions[i].erasesize; ++ } ++ } ++ } else { ++ /* Single erase size */ ++ slave->mtd.erasesize = master->erasesize; ++ } ++ ++ if ((slave->mtd.flags & MTD_WRITEABLE) && ++ (slave->offset % slave->mtd.erasesize)) { ++ /* Doesn't start on a boundary of major erase size */ ++ /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ ++ slave->mtd.flags &= ~MTD_WRITEABLE; ++ printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", ++ part->name); ++ } ++ if ((slave->mtd.flags & MTD_WRITEABLE) && ++ (slave->mtd.size % slave->mtd.erasesize)) { ++ slave->mtd.flags &= ~MTD_WRITEABLE; ++ printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", ++ part->name); ++ } ++ ++ slave->mtd.ecclayout = master->ecclayout; ++ if (master->block_isbad) { ++ uint32_t offs = 0; ++ ++ while(offs < slave->mtd.size) { ++ if (master->block_isbad(master, ++ offs + slave->offset)) ++ slave->mtd.ecc_stats.badblocks++; ++ offs += slave->mtd.erasesize; ++ } ++ } ++ ++ if(part->mtdp) ++ { /* store the object pointer (caller may or may not register it */ ++ *part->mtdp = &slave->mtd; ++ slave->registered = 0; ++ } ++ else ++ { ++ /* register our partition */ ++ add_mtd_device(&slave->mtd); ++ slave->registered = 1; ++ } ++ ++ if (slp) ++ *slp = slave; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++#define ROOTFS_SPLIT_NAME "rootfs_data" ++static int split_squashfs(struct mtd_info *master, struct mtd_partition *old, ++ struct mtd_partition **new) ++{ ++ struct mtd_partition *part = NULL; ++ int len; ++ char buf[512]; ++ struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; ++ int ret; ++ ++ ret = master->read(master, old->offset, sizeof(*sb), &len, buf); ++ if (ret) { ++ printk(KERN_ALERT "split_squashfs: error occured while reading " ++ "from \"%s\"\n", master->name); ++ goto out; ++ } ++ ++ if (len != sizeof(*sb)) { ++ printk(KERN_ALERT "split_squashfs: unable to read superblock " ++ "from \"%s\"\n", master->name); ++ ret=-1; ++ goto out; ++ } ++ ++ if (*((u32 *) buf) != SQUASHFS_MAGIC) { ++ printk(KERN_ALERT "split_squasfs: no squashfs found in \"%s\"\n", ++ master->name); ++ ret=0; ++ goto out; ++ } ++ ++ if (sb->bytes_used <= 0) { ++ printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n", ++ master->name); ++ ret=0; ++ goto out; ++ } ++ ++ part = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL); ++ if (part == NULL) { ++ printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n", ++ ROOTFS_SPLIT_NAME); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ memcpy(part, old, sizeof(*part)); ++ part->name = (unsigned char *)&part[1]; ++ strcpy(part->name, ROOTFS_SPLIT_NAME); ++ ++ len = (u32) sb->bytes_used; ++ len += (part->offset & 0x000fffff); ++ len += (master->erasesize - 1); ++ len &= ~(master->erasesize - 1); ++ len -= (part->offset & 0x000fffff); ++ part->offset += len; ++ part->size -= len; ++ ++ ret = 0; ++ ++out: ++ *new = part; ++ return ret; ++} ++ ++static int split_rootfs_data(struct mtd_info *master, struct mtd_partition *part, ++ int index) ++{ ++ struct mtd_partition *dpart; ++ int ret; ++ ++ ret = split_squashfs(master, part, &dpart); ++ if (ret) ++ return ret; ++ ++ if (dpart == NULL) ++ return 1; ++ ++ printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%X, len=%X \n", ++ ROOTFS_SPLIT_NAME, dpart->offset, dpart->size); ++ ++ ret = add_one_partition(master, dpart, index, NULL); ++ if (ret) ++ kfree(dpart); ++ ++ return ret; ++} ++#endif /* CONFIG_MTD_ROOTFS_SPLIT */ ++ + /* + * This function, given a master MTD object and a partition table, creates + * and registers slave MTD objects which are bound to the master according to +@@ -320,169 +582,31 @@ int add_mtd_partitions(struct mtd_info * + int nbparts) + { + struct mtd_part *slave; +- u_int32_t cur_offset = 0; +- int i; ++ struct mtd_partition *part; ++ int i, j, ret = 0; + + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + +- for (i = 0; i < nbparts; i++) { +- +- /* allocate the partition structure */ +- slave = kzalloc (sizeof(*slave), GFP_KERNEL); +- if (!slave) { +- printk ("memory allocation error while creating partitions for \"%s\"\n", +- master->name); +- del_mtd_partitions(master); +- return -ENOMEM; +- } +- list_add(&slave->list, &mtd_partitions); +- +- /* set up the MTD object for this partition */ +- slave->mtd.type = master->type; +- slave->mtd.flags = master->flags & ~parts[i].mask_flags; +- slave->mtd.size = parts[i].size; +- slave->mtd.writesize = master->writesize; +- slave->mtd.oobsize = master->oobsize; +- slave->mtd.oobavail = master->oobavail; +- slave->mtd.subpage_sft = master->subpage_sft; +- +- slave->mtd.name = parts[i].name; +- slave->mtd.bank_size = master->bank_size; +- slave->mtd.owner = master->owner; +- +- slave->mtd.read = part_read; +- slave->mtd.write = part_write; +- +- if(master->point && master->unpoint){ +- slave->mtd.point = part_point; +- slave->mtd.unpoint = part_unpoint; +- } +- +- if (master->read_oob) +- slave->mtd.read_oob = part_read_oob; +- if (master->write_oob) +- slave->mtd.write_oob = part_write_oob; +- if(master->read_user_prot_reg) +- slave->mtd.read_user_prot_reg = part_read_user_prot_reg; +- if(master->read_fact_prot_reg) +- slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; +- if(master->write_user_prot_reg) +- slave->mtd.write_user_prot_reg = part_write_user_prot_reg; +- if(master->lock_user_prot_reg) +- slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; +- if(master->get_user_prot_info) +- slave->mtd.get_user_prot_info = part_get_user_prot_info; +- if(master->get_fact_prot_info) +- slave->mtd.get_fact_prot_info = part_get_fact_prot_info; +- if (master->sync) +- slave->mtd.sync = part_sync; +- if (!i && master->suspend && master->resume) { +- slave->mtd.suspend = part_suspend; +- slave->mtd.resume = part_resume; +- } +- if (master->writev) +- slave->mtd.writev = part_writev; +- if (master->lock) +- slave->mtd.lock = part_lock; +- if (master->unlock) +- slave->mtd.unlock = part_unlock; +- if (master->block_isbad) +- slave->mtd.block_isbad = part_block_isbad; +- if (master->block_markbad) +- slave->mtd.block_markbad = part_block_markbad; +- slave->mtd.erase = part_erase; +- slave->master = master; +- slave->offset = parts[i].offset; +- slave->index = i; +- +- if (slave->offset == MTDPART_OFS_APPEND) +- slave->offset = cur_offset; +- if (slave->offset == MTDPART_OFS_NXTBLK) { +- slave->offset = cur_offset; +- if ((cur_offset % master->erasesize) != 0) { +- /* Round up to next erasesize */ +- slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; +- printk(KERN_NOTICE "Moving partition %d: " +- "0x%08x -> 0x%08x\n", i, +- cur_offset, slave->offset); +- } +- } +- if (slave->mtd.size == MTDPART_SIZ_FULL) +- slave->mtd.size = master->size - slave->offset; +- cur_offset = slave->offset + slave->mtd.size; +- +- printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, +- slave->offset + slave->mtd.size, slave->mtd.name); +- +- /* let's do some sanity checks */ +- if (slave->offset >= master->size) { +- /* let's register it anyway to preserve ordering */ +- slave->offset = 0; +- slave->mtd.size = 0; +- printk ("mtd: partition \"%s\" is out of reach -- disabled\n", +- parts[i].name); +- } +- if (slave->offset + slave->mtd.size > master->size) { +- slave->mtd.size = master->size - slave->offset; +- printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", +- parts[i].name, master->name, slave->mtd.size); +- } +- if (master->numeraseregions>1) { +- /* Deal with variable erase size stuff */ +- int i; +- struct mtd_erase_region_info *regions = master->eraseregions; +- +- /* Find the first erase regions which is part of this partition. */ +- for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) +- ; +- +- for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { +- if (slave->mtd.erasesize < regions[i].erasesize) { +- slave->mtd.erasesize = regions[i].erasesize; +- } +- } +- } else { +- /* Single erase size */ +- slave->mtd.erasesize = master->erasesize; +- } +- +- if ((slave->mtd.flags & MTD_WRITEABLE) && +- (slave->offset % slave->mtd.erasesize)) { +- /* Doesn't start on a boundary of major erase size */ +- /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ +- slave->mtd.flags &= ~MTD_WRITEABLE; +- printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", +- parts[i].name); +- } +- if ((slave->mtd.flags & MTD_WRITEABLE) && +- (slave->mtd.size % slave->mtd.erasesize)) { +- slave->mtd.flags &= ~MTD_WRITEABLE; +- printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", +- parts[i].name); +- } +- +- slave->mtd.ecclayout = master->ecclayout; +- if (master->block_isbad) { +- uint32_t offs = 0; +- +- while(offs < slave->mtd.size) { +- if (master->block_isbad(master, +- offs + slave->offset)) +- slave->mtd.ecc_stats.badblocks++; +- offs += slave->mtd.erasesize; ++ for (i = 0, j = 0; i < nbparts; i++) { ++ part = (struct mtd_partition *) &parts[i]; ++ ret = add_one_partition(master, part, j, &slave); ++ if (ret) ++ return ret; ++ j++; ++ ++ if (strcmp(part->name, "rootfs") == 0 && slave->registered) { ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++ if (ROOT_DEV == 0) { ++ printk(KERN_NOTICE "mtd: partition \"rootfs\" " ++ "set to be root filesystem\n"); ++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index); + } +- } +- +- if(parts[i].mtdp) +- { /* store the object pointer (caller may or may not register it */ +- *parts[i].mtdp = &slave->mtd; +- slave->registered = 0; +- } +- else +- { +- /* register our partition */ +- add_mtd_device(&slave->mtd); +- slave->registered = 1; ++#endif ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++ ret = split_rootfs_data(master, part, j); ++ if (ret == 0) ++ j++; ++#endif + } + } + diff --git a/target/linux/generic-2.6/patches-2.6.21/065-block2mtd_init.patch b/target/linux/generic-2.6/patches-2.6.21/065-block2mtd_init.patch new file mode 100644 index 0000000000..0e36f9ad06 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/065-block2mtd_init.patch @@ -0,0 +1,114 @@ +Index: linux-2.6.21.7/drivers/mtd/devices/block2mtd.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/mtd/devices/block2mtd.c ++++ linux-2.6.21.7/drivers/mtd/devices/block2mtd.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -288,10 +289,11 @@ static void block2mtd_free_device(struct + + + /* FIXME: ensure that mtd->size % erase_size == 0 */ +-static struct block2mtd_dev *add_device(char *devname, int erase_size) ++static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) + { + struct block_device *bdev; + struct block2mtd_dev *dev; ++ struct mtd_partition *part; + + if (!devname) + return NULL; +@@ -330,14 +332,18 @@ static struct block2mtd_dev *add_device( + + /* Setup the MTD structure */ + /* make the name contain the block device in */ +- dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), +- GFP_KERNEL); ++ ++ if (!mtdname) ++ mtdname = devname; ++ ++ dev->mtd.name = kmalloc(strlen(mtdname) + 1, GFP_KERNEL); ++ + if (!dev->mtd.name) + goto devinit_err; ++ ++ strcpy(dev->mtd.name, mtdname); + +- sprintf(dev->mtd.name, "block2mtd: %s", devname); +- +- dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; ++ dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK & ~(erase_size - 1); + dev->mtd.erasesize = erase_size; + dev->mtd.writesize = 1; + dev->mtd.type = MTD_RAM; +@@ -349,15 +355,18 @@ static struct block2mtd_dev *add_device( + dev->mtd.read = block2mtd_read; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; +- +- if (add_mtd_device(&dev->mtd)) { ++ ++ part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL); ++ part->name = dev->mtd.name; ++ part->offset = 0; ++ part->size = dev->mtd.size; ++ if (add_mtd_partitions(&dev->mtd, part, 1)) { + /* Device didnt get added, so free the entry */ + goto devinit_err; + } + list_add(&dev->list, &blkmtd_device_list); + INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, +- dev->mtd.name + strlen("blkmtd: "), +- dev->mtd.erasesize >> 10, dev->mtd.erasesize); ++ mtdname, dev->mtd.erasesize >> 10, dev->mtd.erasesize); + return dev; + + devinit_err: +@@ -430,9 +439,9 @@ static __initdata char block2mtd_paramli + + static int block2mtd_setup2(const char *val) + { +- char buf[80 + 12]; /* 80 for device, 12 for erase size */ ++ char buf[80 + 12 + 80]; /* 80 for device, 12 for erase size, 80 for name */ + char *str = buf; +- char *token[2]; ++ char *token[3]; + char *name; + size_t erase_size = PAGE_SIZE; + int i, ret; +@@ -443,7 +452,7 @@ static int block2mtd_setup2(const char * + strcpy(str, val); + kill_final_newline(str); + +- for (i = 0; i < 2; i++) ++ for (i = 0; i < 3; i++) + token[i] = strsep(&str, ","); + + if (str) +@@ -463,8 +472,10 @@ static int block2mtd_setup2(const char * + parse_err("illegal erase size"); + } + } ++ if (token[2] && (strlen(token[2]) + 1 > 80)) ++ parse_err("mtd device name too long"); + +- add_device(name, erase_size); ++ add_device(name, erase_size, token[2]); + + return 0; + } +@@ -498,7 +509,7 @@ static int block2mtd_setup(const char *v + + + module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); +-MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); ++MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,[,]]\""); + + static int __init block2mtd_init(void) + { diff --git a/target/linux/generic-2.6/patches-2.6.21/100-netfilter_layer7_2.17.patch b/target/linux/generic-2.6/patches-2.6.21/100-netfilter_layer7_2.17.patch new file mode 100644 index 0000000000..52908b3076 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/100-netfilter_layer7_2.17.patch @@ -0,0 +1,2123 @@ +Index: linux-2.6.21.7/net/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/Kconfig ++++ linux-2.6.21.7/net/netfilter/Kconfig +@@ -640,6 +640,27 @@ config NETFILTER_XT_MATCH_STATE + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_MATCH_LAYER7 ++ tristate '"layer7" match support' ++ depends on NETFILTER_XTABLES ++ depends on EXPERIMENTAL && (IP_NF_CONNTRACK || NF_CONNTRACK) ++ depends on NF_CT_ACCT ++ help ++ Say Y if you want to be able to classify connections (and their ++ packets) based on regular expression matching of their application ++ layer data. This is one way to classify applications such as ++ peer-to-peer filesharing systems that do not always use the same ++ port. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config NETFILTER_XT_MATCH_LAYER7_DEBUG ++ bool 'Layer 7 debugging output' ++ depends on NETFILTER_XT_MATCH_LAYER7 ++ help ++ Say Y to get lots of debugging output. ++ ++ + config NETFILTER_XT_MATCH_STATISTIC + tristate '"statistic" match support' + depends on NETFILTER_XTABLES +Index: linux-2.6.21.7/net/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/Makefile ++++ linux-2.6.21.7/net/netfilter/Makefile +@@ -68,6 +68,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) + + obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o + obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_LAYER7) += xt_layer7.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o + obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o +Index: linux-2.6.21.7/net/netfilter/xt_layer7.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/xt_layer7.c +@@ -0,0 +1,634 @@ ++/* ++ Kernel module to match application layer (OSI layer 7) data in connections. ++ ++ http://l7-filter.sf.net ++ ++ (C) 2003, 2004, 2005, 2006, 2007 Matthew Strait and Ethan Sommer. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version ++ 2 of the License, or (at your option) any later version. ++ http://www.gnu.org/licenses/gpl.txt ++ ++ Based on ipt_string.c (C) 2000 Emmanuel Roger , ++ xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait, ++ Ethan Sommer, Justin Levandoski. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "regexp/regexp.c" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matthew Strait , Ethan Sommer "); ++MODULE_DESCRIPTION("iptables application layer match module"); ++MODULE_ALIAS("ipt_layer7"); ++MODULE_VERSION("2.17"); ++ ++static int maxdatalen = 2048; // this is the default ++module_param(maxdatalen, int, 0444); ++MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter"); ++#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG ++ #define DPRINTK(format,args...) printk(format,##args) ++#else ++ #define DPRINTK(format,args...) ++#endif ++ ++#define TOTAL_PACKETS master_conntrack->counters[IP_CT_DIR_ORIGINAL].packets + \ ++ master_conntrack->counters[IP_CT_DIR_REPLY].packets ++ ++/* Number of packets whose data we look at. ++This can be modified through /proc/net/layer7_numpackets */ ++static int num_packets = 10; ++ ++static struct pattern_cache { ++ char * regex_string; ++ regexp * pattern; ++ struct pattern_cache * next; ++} * first_pattern_cache = NULL; ++ ++DEFINE_SPINLOCK(l7_lock); ++ ++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++/* Converts an unfriendly string into a friendly one by ++replacing unprintables with periods and all whitespace with " ". */ ++static char * friendly_print(unsigned char * s) ++{ ++ char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!f) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "friendly_print, bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++){ ++ if(isprint(s[i]) && s[i] < 128) f[i] = s[i]; ++ else if(isspace(s[i])) f[i] = ' '; ++ else f[i] = '.'; ++ } ++ f[i] = '\0'; ++ return f; ++} ++ ++static char dec2hex(int i) ++{ ++ switch (i) { ++ case 0 ... 9: ++ return (i + '0'); ++ break; ++ case 10 ... 15: ++ return (i - 10 + 'a'); ++ break; ++ default: ++ if (net_ratelimit()) ++ printk("layer7: Problem in dec2hex\n"); ++ return '\0'; ++ } ++} ++ ++static char * hex_print(unsigned char * s) ++{ ++ char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!g) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in hex_print, " ++ "bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++) { ++ g[i*3 ] = dec2hex(s[i]/16); ++ g[i*3 + 1] = dec2hex(s[i]%16); ++ g[i*3 + 2] = ' '; ++ } ++ g[i*3] = '\0'; ++ ++ return g; ++} ++#endif // DEBUG ++ ++/* Use instead of regcomp. As we expect to be seeing the same regexps over and ++over again, it make sense to cache the results. */ ++static regexp * compile_and_cache(const char * regex_string, ++ const char * protocol) ++{ ++ struct pattern_cache * node = first_pattern_cache; ++ struct pattern_cache * last_pattern_cache = first_pattern_cache; ++ struct pattern_cache * tmp; ++ unsigned int len; ++ ++ while (node != NULL) { ++ if (!strcmp(node->regex_string, regex_string)) ++ return node->pattern; ++ ++ last_pattern_cache = node;/* points at the last non-NULL node */ ++ node = node->next; ++ } ++ ++ /* If we reach the end of the list, then we have not yet cached ++ the pattern for this regex. Let's do that now. ++ Be paranoid about running out of memory to avoid list corruption. */ ++ tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); ++ ++ if(!tmp) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "compile_and_cache, bailing.\n"); ++ return NULL; ++ } ++ ++ tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); ++ tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC); ++ tmp->next = NULL; ++ ++ if(!tmp->regex_string || !tmp->pattern) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "compile_and_cache, bailing.\n"); ++ kfree(tmp->regex_string); ++ kfree(tmp->pattern); ++ kfree(tmp); ++ return NULL; ++ } ++ ++ /* Ok. The new node is all ready now. */ ++ node = tmp; ++ ++ if(first_pattern_cache == NULL) /* list is empty */ ++ first_pattern_cache = node; /* make node the beginning */ ++ else ++ last_pattern_cache->next = node; /* attach node to the end */ ++ ++ /* copy the string and compile the regex */ ++ len = strlen(regex_string); ++ DPRINTK("About to compile this: \"%s\"\n", regex_string); ++ node->pattern = regcomp((char *)regex_string, &len); ++ if ( !node->pattern ) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: Error compiling regexp " ++ "\"%s\" (%s)\n", ++ regex_string, protocol); ++ /* pattern is now cached as NULL, so we won't try again. */ ++ } ++ ++ strcpy(node->regex_string, regex_string); ++ return node->pattern; ++} ++ ++static int can_handle(const struct sk_buff *skb) ++{ ++ if(!ip_hdr(skb)) /* not IP */ ++ return 0; ++ if(ip_hdr(skb)->protocol != IPPROTO_TCP && ++ ip_hdr(skb)->protocol != IPPROTO_UDP && ++ ip_hdr(skb)->protocol != IPPROTO_ICMP) ++ return 0; ++ return 1; ++} ++ ++/* Returns offset the into the skb->data that the application data starts */ ++static int app_data_offset(const struct sk_buff *skb) ++{ ++ /* In case we are ported somewhere (ebtables?) where ip_hdr(skb) ++ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ ++ int ip_hl = 4*ip_hdr(skb)->ihl; ++ ++ if( ip_hdr(skb)->protocol == IPPROTO_TCP ) { ++ /* 12 == offset into TCP header for the header length field. ++ Can't get this with skb->h.th->doff because the tcphdr ++ struct doesn't get set when routing (this is confirmed to be ++ true in Netfilter as well as QoS.) */ ++ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); ++ ++ return ip_hl + tcp_hl; ++ } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) { ++ return ip_hl + 8; /* UDP header is always 8 bytes */ ++ } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) { ++ return ip_hl + 8; /* ICMP header is 8 bytes */ ++ } else { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: tried to handle unknown " ++ "protocol!\n"); ++ return ip_hl + 8; /* something reasonable */ ++ } ++} ++ ++/* handles whether there's a match when we aren't appending data anymore */ ++static int match_no_append(struct nf_conn * conntrack, ++ struct nf_conn * master_conntrack, ++ enum ip_conntrack_info ctinfo, ++ enum ip_conntrack_info master_ctinfo, ++ const struct xt_layer7_info * info) ++{ ++ /* If we're in here, throw the app data away */ ++ if(master_conntrack->layer7.app_data != NULL) { ++ ++ #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++ if(!master_conntrack->layer7.app_proto) { ++ char * f = ++ friendly_print(master_conntrack->layer7.app_data); ++ char * g = ++ hex_print(master_conntrack->layer7.app_data); ++ DPRINTK("\nl7-filter gave up after %d bytes " ++ "(%d packets):\n%s\n", ++ strlen(f), TOTAL_PACKETS, f); ++ kfree(f); ++ DPRINTK("In hex: %s\n", g); ++ kfree(g); ++ } ++ #endif ++ ++ kfree(master_conntrack->layer7.app_data); ++ master_conntrack->layer7.app_data = NULL; /* don't free again */ ++ } ++ ++ if(master_conntrack->layer7.app_proto){ ++ /* Here child connections set their .app_proto (for /proc) */ ++ if(!conntrack->layer7.app_proto) { ++ conntrack->layer7.app_proto = ++ kmalloc(strlen(master_conntrack->layer7.app_proto)+1, ++ GFP_ATOMIC); ++ if(!conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory " ++ "in match_no_append, " ++ "bailing.\n"); ++ return 1; ++ } ++ strcpy(conntrack->layer7.app_proto, ++ master_conntrack->layer7.app_proto); ++ } ++ ++ return (!strcmp(master_conntrack->layer7.app_proto, ++ info->protocol)); ++ } ++ else { ++ /* If not classified, set to "unknown" to distinguish from ++ connections that are still being tested. */ ++ master_conntrack->layer7.app_proto = ++ kmalloc(strlen("unknown")+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "match_no_append, bailing.\n"); ++ return 1; ++ } ++ strcpy(master_conntrack->layer7.app_proto, "unknown"); ++ return 0; ++ } ++} ++ ++/* add the new app data to the conntrack. Return number of bytes added. */ ++static int add_data(struct nf_conn * master_conntrack, ++ char * app_data, int appdatalen) ++{ ++ int length = 0, i; ++ int oldlength = master_conntrack->layer7.app_data_len; ++ ++ /* This is a fix for a race condition by Deti Fliegl. However, I'm not ++ clear on whether the race condition exists or whether this really ++ fixes it. I might just be being dense... Anyway, if it's not really ++ a fix, all it does is waste a very small amount of time. */ ++ if(!master_conntrack->layer7.app_data) return 0; ++ ++ /* Strip nulls. Make everything lower case (our regex lib doesn't ++ do case insensitivity). Add it to the end of the current data. */ ++ for(i = 0; i < maxdatalen-oldlength-1 && ++ i < appdatalen; i++) { ++ if(app_data[i] != '\0') { ++ /* the kernel version of tolower mungs 'upper ascii' */ ++ master_conntrack->layer7.app_data[length+oldlength] = ++ isascii(app_data[i])? ++ tolower(app_data[i]) : app_data[i]; ++ length++; ++ } ++ } ++ ++ master_conntrack->layer7.app_data[length+oldlength] = '\0'; ++ master_conntrack->layer7.app_data_len = length + oldlength; ++ ++ return length; ++} ++ ++/* taken from drivers/video/modedb.c */ ++static int my_atoi(const char *s) ++{ ++ int val = 0; ++ ++ for (;; s++) { ++ switch (*s) { ++ case '0'...'9': ++ val = 10*val+(*s-'0'); ++ break; ++ default: ++ return val; ++ } ++ } ++} ++ ++/* write out num_packets to userland. */ ++static int layer7_read_proc(char* page, char ** start, off_t off, int count, ++ int* eof, void * data) ++{ ++ if(num_packets > 99 && net_ratelimit()) ++ printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n"); ++ ++ page[0] = num_packets/10 + '0'; ++ page[1] = num_packets%10 + '0'; ++ page[2] = '\n'; ++ page[3] = '\0'; ++ ++ *eof=1; ++ ++ return 3; ++} ++ ++/* Read in num_packets from userland */ ++static int layer7_write_proc(struct file* file, const char* buffer, ++ unsigned long count, void *data) ++{ ++ char * foo = kmalloc(count, GFP_ATOMIC); ++ ++ if(!foo){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory, bailing. " ++ "num_packets unchanged.\n"); ++ return count; ++ } ++ ++ if(copy_from_user(foo, buffer, count)) { ++ return -EFAULT; ++ } ++ ++ ++ num_packets = my_atoi(foo); ++ kfree (foo); ++ ++ /* This has an arbitrary limit to make the math easier. I'm lazy. ++ But anyway, 99 is a LOT! If you want more, you're doing it wrong! */ ++ if(num_packets > 99) { ++ printk(KERN_WARNING "layer7: num_packets can't be > 99.\n"); ++ num_packets = 99; ++ } else if(num_packets < 1) { ++ printk(KERN_WARNING "layer7: num_packets can't be < 1.\n"); ++ num_packets = 1; ++ } ++ ++ return count; ++} ++ ++static int ++match(const struct sk_buff *skbin, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct xt_match *match, ++ const void *matchinfo, ++ int offset, ++ unsigned int protoff, ++ int *hotdrop) ++{ ++ /* sidestep const without getting a compiler warning... */ ++ struct sk_buff * skb = (struct sk_buff *)skbin; ++ ++ const struct xt_layer7_info * info = matchinfo; ++ enum ip_conntrack_info master_ctinfo, ctinfo; ++ struct nf_conn *master_conntrack, *conntrack; ++ unsigned char * app_data; ++ unsigned int pattern_result, appdatalen; ++ regexp * comppattern; ++ ++ /* Be paranoid/incompetent - lock the entire match function. */ ++ spin_lock_bh(&l7_lock); ++ ++ if(!can_handle(skb)){ ++ DPRINTK("layer7: This is some protocol I can't handle.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ ++ /* Treat parent & all its children together as one connection, except ++ for the purpose of setting conntrack->layer7.app_proto in the actual ++ connection. This makes /proc/net/ip_conntrack more satisfying. */ ++ if(!(conntrack = nf_ct_get(skb, &ctinfo)) || ++ !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){ ++ DPRINTK("layer7: couldn't get conntrack.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ ++ /* Try to get a master conntrack (and its master etc) for FTP, etc. */ ++ while (master_ct(master_conntrack) != NULL) ++ master_conntrack = master_ct(master_conntrack); ++ ++ /* if we've classified it or seen too many packets */ ++ if(TOTAL_PACKETS > num_packets || ++ master_conntrack->layer7.app_proto) { ++ ++ pattern_result = match_no_append(conntrack, master_conntrack, ++ ctinfo, master_ctinfo, info); ++ ++ /* skb->cb[0] == seen. Don't do things twice if there are ++ multiple l7 rules. I'm not sure that using cb for this purpose ++ is correct, even though it says "put your private variables ++ there". But it doesn't look like it is being used for anything ++ else in the skbs that make it here. */ ++ skb->cb[0] = 1; /* marking it seen here's probably irrelevant */ ++ ++ spin_unlock_bh(&l7_lock); ++ return (pattern_result ^ info->invert); ++ } ++ ++ if(skb_is_nonlinear(skb)){ ++ if(skb_linearize(skb) != 0){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: failed to linearize " ++ "packet, bailing.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ } ++ ++ /* now that the skb is linearized, it's safe to set these. */ ++ app_data = skb->data + app_data_offset(skb); ++ appdatalen = skb_tail_pointer(skb) - app_data; ++ ++ /* the return value gets checked later, when we're ready to use it */ ++ comppattern = compile_and_cache(info->pattern, info->protocol); ++ ++ /* On the first packet of a connection, allocate space for app data */ ++ if(TOTAL_PACKETS == 1 && !skb->cb[0] && ++ !master_conntrack->layer7.app_data){ ++ master_conntrack->layer7.app_data = ++ kmalloc(maxdatalen, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_data){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "match, bailing.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ ++ master_conntrack->layer7.app_data[0] = '\0'; ++ } ++ ++ /* Can be here, but unallocated, if numpackets is increased near ++ the beginning of a connection */ ++ if(master_conntrack->layer7.app_data == NULL){ ++ spin_unlock_bh(&l7_lock); ++ return (info->invert); /* unmatched */ ++ } ++ ++ if(!skb->cb[0]){ ++ int newbytes; ++ newbytes = add_data(master_conntrack, app_data, appdatalen); ++ ++ if(newbytes == 0) { /* didn't add any data */ ++ skb->cb[0] = 1; ++ /* Didn't match before, not going to match now */ ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ } ++ ++ /* If looking for "unknown", then never match. "Unknown" means that ++ we've given up; we're still trying with these packets. */ ++ if(!strcmp(info->protocol, "unknown")) { ++ pattern_result = 0; ++ /* If looking for "unset", then always match. "Unset" means that we ++ haven't yet classified the connection. */ ++ } else if(!strcmp(info->protocol, "unset")) { ++ pattern_result = 2; ++ DPRINTK("layer7: matched unset: not yet classified " ++ "(%d/%d packets)\n", TOTAL_PACKETS, num_packets); ++ /* If the regexp failed to compile, don't bother running it */ ++ } else if(comppattern && ++ regexec(comppattern, master_conntrack->layer7.app_data)){ ++ DPRINTK("layer7: matched %s\n", info->protocol); ++ pattern_result = 1; ++ } else pattern_result = 0; ++ ++ if(pattern_result == 1) { ++ master_conntrack->layer7.app_proto = ++ kmalloc(strlen(info->protocol)+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "match, bailing.\n"); ++ spin_unlock_bh(&l7_lock); ++ return (pattern_result ^ info->invert); ++ } ++ strcpy(master_conntrack->layer7.app_proto, info->protocol); ++ } else if(pattern_result > 1) { /* cleanup from "unset" */ ++ pattern_result = 1; ++ } ++ ++ /* mark the packet seen */ ++ skb->cb[0] = 1; ++ ++ spin_unlock_bh(&l7_lock); ++ return (pattern_result ^ info->invert); ++} ++ ++static int check(const char *tablename, ++ const void *inf, ++ const struct xt_match *match, ++ void *matchinfo, ++ unsigned int hook_mask) ++ ++{ ++ // load nf_conntrack_ipv4 ++ if (nf_ct_l3proto_try_module_get(match->family) < 0) { ++ printk(KERN_WARNING "can't load conntrack support for " ++ "proto=%d\n", match->family); ++ return false; ++ } ++ return true; ++} ++ ++static void ++destroy(const struct xt_match *match, void *matchinfo) ++{ ++ nf_ct_l3proto_module_put(match->family); ++} ++ ++static struct xt_match xt_layer7_match[] = { ++{ ++ .name = "layer7", ++ .family = AF_INET, ++ .checkentry = check, ++ .match = match, ++ .destroy = destroy, ++ .matchsize = sizeof(struct xt_layer7_info), ++ .me = THIS_MODULE ++} ++}; ++ ++static void layer7_cleanup_proc(void) ++{ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) ++ remove_proc_entry("layer7_numpackets", proc_net); ++#else ++ remove_proc_entry("layer7_numpackets", init_net.proc_net); ++#endif ++} ++ ++/* register the proc file */ ++static void layer7_init_proc(void) ++{ ++ struct proc_dir_entry* entry; ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) ++ entry = create_proc_entry("layer7_numpackets", 0644, proc_net); ++#else ++ entry = create_proc_entry("layer7_numpackets", 0644, init_net.proc_net); ++#endif ++ entry->read_proc = layer7_read_proc; ++ entry->write_proc = layer7_write_proc; ++} ++ ++static int __init xt_layer7_init(void) ++{ ++ need_conntrack(); ++ ++ layer7_init_proc(); ++ if(maxdatalen < 1) { ++ printk(KERN_WARNING "layer7: maxdatalen can't be < 1, " ++ "using 1\n"); ++ maxdatalen = 1; ++ } ++ /* This is not a hard limit. It's just here to prevent people from ++ bringing their slow machines to a grinding halt. */ ++ else if(maxdatalen > 65536) { ++ printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, " ++ "using 65536\n"); ++ maxdatalen = 65536; ++ } ++ return xt_register_matches(xt_layer7_match, ++ ARRAY_SIZE(xt_layer7_match)); ++} ++ ++static void __exit xt_layer7_fini(void) ++{ ++ layer7_cleanup_proc(); ++ xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match)); ++} ++ ++module_init(xt_layer7_init); ++module_exit(xt_layer7_fini); +Index: linux-2.6.21.7/net/netfilter/regexp/regexp.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/regexp/regexp.c +@@ -0,0 +1,1197 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++struct match_globals { ++char *reginput; /* String-input pointer. */ ++char *regbol; /* Beginning of input, for ^ check. */ ++char **regstartp; /* Pointer to startp array. */ ++char **regendp; /* Ditto for endp. */ ++char *regparse; /* Input-scan pointer. */ ++int regnpar; /* () count. */ ++char regdummy; ++char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++long regsize; /* Code size. */ ++}; ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(struct match_globals *g, int paren,int *flagp); ++STATIC char *regbranch(struct match_globals *g, int *flagp); ++STATIC char *regpiece(struct match_globals *g, int *flagp); ++STATIC char *regatom(struct match_globals *g, int *flagp); ++STATIC char *regnode(struct match_globals *g, char op); ++STATIC char *regnext(struct match_globals *g, char *p); ++STATIC void regc(struct match_globals *g, char b); ++STATIC void reginsert(struct match_globals *g, char op, char *opnd); ++STATIC void regtail(struct match_globals *g, char *p, char *val); ++STATIC void regoptail(struct match_globals *g, char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ struct match_globals g; ++ ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regsize = 0L; ++ g.regcode = &g.regdummy; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (g.regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)g.regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regcode = r->program; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(&g, scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (g->regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = g->regnpar; ++ g->regnpar++; ++ ret = regnode(g, OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(g, ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*g->regparse == '|') { ++ g->regparse++; ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(g, ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode(g, (paren) ? CLOSE+parno : END); ++ regtail(g, ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(g, br)) ++ regoptail(g, br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *g->regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *g->regparse != '\0') { ++ if (*g->regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(g, BRANCH); ++ chain = NULL; ++ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { ++ latest = regpiece(g, &flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(g, chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(g, NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(g, &flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *g->regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(g, STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regoptail(g, ret, regnode(g, BACK)); /* and loop */ ++ regoptail(g, ret, ret); /* back */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(g, PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(g, BRANCH); /* Either */ ++ regtail(g, ret, next); ++ regtail(g, regnode(g, BACK), ret); /* loop back */ ++ regtail(g, next, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ next = regnode(g, NOTHING); /* null. */ ++ regtail(g, ret, next); ++ regoptail(g, ret, next); ++ } ++ g->regparse++; ++ if (ISMULT(*g->regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*g->regparse++) { ++ case '^': ++ ret = regnode(g, BOL); ++ break; ++ case '$': ++ ret = regnode(g, EOL); ++ break; ++ case '.': ++ ret = regnode(g, ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*g->regparse == '^') { /* Complement of range. */ ++ ret = regnode(g, ANYBUT); ++ g->regparse++; ++ } else ++ ret = regnode(g, ANYOF); ++ if (*g->regparse == ']' || *g->regparse == '-') ++ regc(g, *g->regparse++); ++ while (*g->regparse != '\0' && *g->regparse != ']') { ++ if (*g->regparse == '-') { ++ g->regparse++; ++ if (*g->regparse == ']' || *g->regparse == '\0') ++ regc(g, '-'); ++ else { ++ class = UCHARAT(g->regparse-2)+1; ++ classend = UCHARAT(g->regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(g, class); ++ g->regparse++; ++ } ++ } else ++ regc(g, *g->regparse++); ++ } ++ regc(g, '\0'); ++ if (*g->regparse != ']') ++ FAIL("unmatched []"); ++ g->regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(g, 1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*g->regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(g, EXACTLY); ++ regc(g, *g->regparse++); ++ regc(g, '\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ g->regparse--; ++ len = my_strcspn((const char *)g->regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(g->regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(g, EXACTLY); ++ while (len > 0) { ++ regc(g, *g->regparse++); ++ len--; ++ } ++ regc(g, '\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(struct match_globals *g, char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = g->regcode; ++ if (ret == &g->regdummy) { ++ g->regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ g->regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(struct match_globals *g, char b) ++{ ++ if (g->regcode != &g->regdummy) ++ *g->regcode++ = b; ++ else ++ g->regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(struct match_globals *g, char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (g->regcode == &g->regdummy) { ++ g->regsize += 3; ++ return; ++ } ++ ++ src = g->regcode; ++ g->regcode += 3; ++ dst = g->regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(struct match_globals *g, char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(g, scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(struct match_globals *g, char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) ++ return; ++ regtail(g, OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(struct match_globals *g, regexp *prog, char *string); ++STATIC int regmatch(struct match_globals *g, char *prog); ++STATIC int regrepeat(struct match_globals *g, char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ struct match_globals g; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ g.regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(&g, prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(&g, prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(&g, prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(struct match_globals *g, regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ g->reginput = string; ++ g->regstartp = prog->startp; ++ g->regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(g, prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = g->reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(struct match_globals *g, char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(g, scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (g->reginput != g->regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*g->reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*g->reginput == '\0') ++ return(0); ++ g->reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *g->reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) ++ return(0); ++ g->reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case ANYBUT: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regstartp[no] == NULL) ++ g->regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regendp[no] == NULL) ++ g->regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = g->reginput; ++ if (regmatch(g, OPERAND(scan))) ++ return(1); ++ g->reginput = save; ++ scan = regnext(g, scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = g->reginput; ++ no = regrepeat(g, OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *g->reginput == nextch) ++ if (regmatch(g, next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ g->reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(struct match_globals *g, char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = g->reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ g->reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(struct match_globals *g, char *p) ++{ ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +Index: linux-2.6.21.7/net/netfilter/regexp/regexp.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/regexp/regexp.h +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +Index: linux-2.6.21.7/net/netfilter/regexp/regmagic.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/regexp/regmagic.h +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +Index: linux-2.6.21.7/net/netfilter/regexp/regsub.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/regexp/regsub.c +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} +Index: linux-2.6.21.7/net/netfilter/nf_conntrack_core.c +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/nf_conntrack_core.c ++++ linux-2.6.21.7/net/netfilter/nf_conntrack_core.c +@@ -352,6 +352,14 @@ destroy_conntrack(struct nf_conntrack *n + * too. */ + nf_ct_remove_expectations(ct); + ++ #if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++ if(ct->layer7.app_proto) ++ kfree(ct->layer7.app_proto); ++ if(ct->layer7.app_data) ++ kfree(ct->layer7.app_data); ++ #endif ++ ++ + /* We overload first tuple to link into unconfirmed list. */ + if (!nf_ct_is_confirmed(ct)) { + BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list)); +Index: linux-2.6.21.7/net/netfilter/nf_conntrack_standalone.c +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/nf_conntrack_standalone.c ++++ linux-2.6.21.7/net/netfilter/nf_conntrack_standalone.c +@@ -195,7 +195,12 @@ static int ct_seq_show(struct seq_file * + return -ENOSPC; + #endif + +- if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) ++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++ if(conntrack->layer7.app_proto) ++ if(seq_printf(s, "l7proto=%s ", conntrack->layer7.app_proto)) ++ return -ENOSPC; ++#endif ++ if (seq_printf(s, "asdfuse=%u\n", atomic_read(&conntrack->ct_general.use))) + return -ENOSPC; + + return 0; +Index: linux-2.6.21.7/include/net/netfilter/nf_conntrack.h +=================================================================== +--- linux-2.6.21.7.orig/include/net/netfilter/nf_conntrack.h ++++ linux-2.6.21.7/include/net/netfilter/nf_conntrack.h +@@ -128,6 +128,22 @@ struct nf_conn + u_int32_t secmark; + #endif + ++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || \ ++ defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++ struct { ++ /* ++ * e.g. "http". NULL before decision. "unknown" after decision ++ * if no match. ++ */ ++ char *app_proto; ++ /* ++ * application layer data so far. NULL after match decision. ++ */ ++ char *app_data; ++ unsigned int app_data_len; ++ } layer7; ++#endif ++ + /* Storage reserved for other modules: */ + union nf_conntrack_proto proto; + +Index: linux-2.6.21.7/include/linux/netfilter/xt_layer7.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter/xt_layer7.h +@@ -0,0 +1,13 @@ ++#ifndef _XT_LAYER7_H ++#define _XT_LAYER7_H ++ ++#define MAX_PATTERN_LEN 8192 ++#define MAX_PROTOCOL_LEN 256 ++ ++struct xt_layer7_info { ++ char protocol[MAX_PROTOCOL_LEN]; ++ char pattern[MAX_PATTERN_LEN]; ++ u_int8_t invert; ++}; ++ ++#endif /* _XT_LAYER7_H */ diff --git a/target/linux/generic-2.6/patches-2.6.21/101-netfilter_layer7_pktmatch.patch b/target/linux/generic-2.6/patches-2.6.21/101-netfilter_layer7_pktmatch.patch new file mode 100644 index 0000000000..cabffacc14 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/101-netfilter_layer7_pktmatch.patch @@ -0,0 +1,113 @@ +Index: linux-2.6.21.7/include/linux/netfilter/xt_layer7.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/netfilter/xt_layer7.h ++++ linux-2.6.21.7/include/linux/netfilter/xt_layer7.h +@@ -8,6 +8,7 @@ struct xt_layer7_info { + char protocol[MAX_PROTOCOL_LEN]; + char pattern[MAX_PATTERN_LEN]; + u_int8_t invert; ++ u_int8_t pkt; + }; + + #endif /* _XT_LAYER7_H */ +Index: linux-2.6.21.7/net/netfilter/xt_layer7.c +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/xt_layer7.c ++++ linux-2.6.21.7/net/netfilter/xt_layer7.c +@@ -297,34 +297,36 @@ static int match_no_append(struct nf_con + } + + /* add the new app data to the conntrack. Return number of bytes added. */ +-static int add_data(struct nf_conn * master_conntrack, +- char * app_data, int appdatalen) ++static int add_datastr(char *target, int offset, char *app_data, int len) + { + int length = 0, i; +- int oldlength = master_conntrack->layer7.app_data_len; +- +- /* This is a fix for a race condition by Deti Fliegl. However, I'm not +- clear on whether the race condition exists or whether this really +- fixes it. I might just be being dense... Anyway, if it's not really +- a fix, all it does is waste a very small amount of time. */ +- if(!master_conntrack->layer7.app_data) return 0; ++ ++ if (!target) return 0; + + /* Strip nulls. Make everything lower case (our regex lib doesn't + do case insensitivity). Add it to the end of the current data. */ +- for(i = 0; i < maxdatalen-oldlength-1 && +- i < appdatalen; i++) { ++ for(i = 0; i < maxdatalen-offset-1 && i < len; i++) { + if(app_data[i] != '\0') { + /* the kernel version of tolower mungs 'upper ascii' */ +- master_conntrack->layer7.app_data[length+oldlength] = ++ target[length+offset] = + isascii(app_data[i])? + tolower(app_data[i]) : app_data[i]; + length++; + } + } ++ target[length+offset] = '\0'; ++ ++ return length; ++} + +- master_conntrack->layer7.app_data[length+oldlength] = '\0'; +- master_conntrack->layer7.app_data_len = length + oldlength; ++/* add the new app data to the conntrack. Return number of bytes added. */ ++static int add_data(struct nf_conn * master_conntrack, ++ char * app_data, int appdatalen) ++{ ++ int length; + ++ length = add_datastr(master_conntrack->layer7.app_data, master_conntrack->layer7.app_data_len, app_data, appdatalen); ++ master_conntrack->layer7.app_data_len += length; + return length; + } + +@@ -411,7 +413,7 @@ match(const struct sk_buff *skbin, + const struct xt_layer7_info * info = matchinfo; + enum ip_conntrack_info master_ctinfo, ctinfo; + struct nf_conn *master_conntrack, *conntrack; +- unsigned char * app_data; ++ unsigned char *app_data, *tmp_data; + unsigned int pattern_result, appdatalen; + regexp * comppattern; + +@@ -439,8 +441,8 @@ match(const struct sk_buff *skbin, + master_conntrack = master_ct(master_conntrack); + + /* if we've classified it or seen too many packets */ +- if(TOTAL_PACKETS > num_packets || +- master_conntrack->layer7.app_proto) { ++ if(!info->pkt && (TOTAL_PACKETS > num_packets || ++ master_conntrack->layer7.app_proto)) { + + pattern_result = match_no_append(conntrack, master_conntrack, + ctinfo, master_ctinfo, info); +@@ -473,6 +475,25 @@ match(const struct sk_buff *skbin, + /* the return value gets checked later, when we're ready to use it */ + comppattern = compile_and_cache(info->pattern, info->protocol); + ++ if (info->pkt) { ++ tmp_data = kmalloc(maxdatalen, GFP_ATOMIC); ++ if(!tmp_data){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++ return info->invert; ++ } ++ ++ tmp_data[0] = '\0'; ++ add_datastr(tmp_data, 0, app_data, appdatalen); ++ pattern_result = ((comppattern && regexec(comppattern, tmp_data)) ? 1 : 0); ++ ++ kfree(tmp_data); ++ tmp_data = NULL; ++ spin_unlock_bh(&l7_lock); ++ ++ return (pattern_result ^ info->invert); ++ } ++ + /* On the first packet of a connection, allocate space for app data */ + if(TOTAL_PACKETS == 1 && !skb->cb[0] && + !master_conntrack->layer7.app_data){ diff --git a/target/linux/generic-2.6/patches-2.6.21/110-ipp2p_0.8.1rc1.patch b/target/linux/generic-2.6/patches-2.6.21/110-ipp2p_0.8.1rc1.patch new file mode 100644 index 0000000000..5f202ca99e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/110-ipp2p_0.8.1rc1.patch @@ -0,0 +1,952 @@ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ipp2p.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ipp2p.h +@@ -0,0 +1,31 @@ ++#ifndef __IPT_IPP2P_H ++#define __IPT_IPP2P_H ++#define IPP2P_VERSION "0.8.1_rc1" ++ ++struct ipt_p2p_info { ++ int cmd; ++ int debug; ++}; ++ ++#endif //__IPT_IPP2P_H ++ ++#define SHORT_HAND_IPP2P 1 /* --ipp2p switch*/ ++//#define SHORT_HAND_DATA 4 /* --ipp2p-data switch*/ ++#define SHORT_HAND_NONE 5 /* no short hand*/ ++ ++#define IPP2P_EDK (1 << 1) ++#define IPP2P_DATA_KAZAA (1 << 2) ++#define IPP2P_DATA_EDK (1 << 3) ++#define IPP2P_DATA_DC (1 << 4) ++#define IPP2P_DC (1 << 5) ++#define IPP2P_DATA_GNU (1 << 6) ++#define IPP2P_GNU (1 << 7) ++#define IPP2P_KAZAA (1 << 8) ++#define IPP2P_BIT (1 << 9) ++#define IPP2P_APPLE (1 << 10) ++#define IPP2P_SOUL (1 << 11) ++#define IPP2P_WINMX (1 << 12) ++#define IPP2P_ARES (1 << 13) ++#define IPP2P_MUTE (1 << 14) ++#define IPP2P_WASTE (1 << 15) ++#define IPP2P_XDCC (1 << 16) +Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_ipp2p.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ipt_ipp2p.c +@@ -0,0 +1,882 @@ ++#if defined(MODVERSIONS) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define get_u8(X,O) (*(__u8 *)(X + O)) ++#define get_u16(X,O) (*(__u16 *)(X + O)) ++#define get_u32(X,O) (*(__u32 *)(X + O)) ++ ++MODULE_AUTHOR("Eicke Friedrich/Klaus Degner "); ++MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic."); ++MODULE_LICENSE("GPL"); ++ ++ ++/*Search for UDP eDonkey/eMule/Kad commands*/ ++int ++udp_search_edk (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ switch (t[0]) { ++ case 0xe3: ++ { /*edonkey*/ ++ switch (t[1]) ++ { ++ /* client -> server status request */ ++ case 0x96: ++ if (packet_len == 14) return ((IPP2P_EDK * 100) + 50); ++ break; ++ /* server -> client status request */ ++ case 0x97: if (packet_len == 42) return ((IPP2P_EDK * 100) + 51); ++ break; ++ /* server description request */ ++ /* e3 2a ff f0 .. | size == 6 */ ++ case 0xa2: if ( (packet_len == 14) && ( get_u16(t,2) == __constant_htons(0xfff0) ) ) return ((IPP2P_EDK * 100) + 52); ++ break; ++ /* server description response */ ++ /* e3 a3 ff f0 .. | size > 40 && size < 200 */ ++ //case 0xa3: return ((IPP2P_EDK * 100) + 53); ++ // break; ++ case 0x9a: if (packet_len==26) return ((IPP2P_EDK * 100) + 54); ++ break; ++ ++ case 0x92: if (packet_len==18) return ((IPP2P_EDK * 100) + 55); ++ break; ++ } ++ break; ++ } ++ case 0xe4: ++ { ++ switch (t[1]) ++ { ++ /* e4 20 .. | size == 43 */ ++ case 0x20: if ((packet_len == 43) && (t[2] != 0x00) && (t[34] != 0x00)) return ((IPP2P_EDK * 100) + 60); ++ break; ++ /* e4 00 .. 00 | size == 35 ? */ ++ case 0x00: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 61); ++ break; ++ /* e4 10 .. 00 | size == 35 ? */ ++ case 0x10: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 62); ++ break; ++ /* e4 18 .. 00 | size == 35 ? */ ++ case 0x18: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 63); ++ break; ++ /* e4 52 .. | size = 44 */ ++ case 0x52: if (packet_len == 44 ) return ((IPP2P_EDK * 100) + 64); ++ break; ++ /* e4 58 .. | size == 6 */ ++ case 0x58: if (packet_len == 14 ) return ((IPP2P_EDK * 100) + 65); ++ break; ++ /* e4 59 .. | size == 2 */ ++ case 0x59: if (packet_len == 10 )return ((IPP2P_EDK * 100) + 66); ++ break; ++ /* e4 28 .. | packet_len == 52,77,102,127... */ ++ case 0x28: if (((packet_len-52) % 25) == 0) return ((IPP2P_EDK * 100) + 67); ++ break; ++ /* e4 50 xx xx | size == 4 */ ++ case 0x50: if (packet_len == 12) return ((IPP2P_EDK * 100) + 68); ++ break; ++ /* e4 40 xx xx | size == 48 */ ++ case 0x40: if (packet_len == 56) return ((IPP2P_EDK * 100) + 69); ++ break; ++ } ++ break; ++ } ++ } /* end of switch (t[0]) */ ++ return 0; ++}/*udp_search_edk*/ ++ ++ ++/*Search for UDP Gnutella commands*/ ++int ++udp_search_gnu (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ if (memcmp(t, "GND", 3) == 0) return ((IPP2P_GNU * 100) + 51); ++ if (memcmp(t, "GNUTELLA ", 9) == 0) return ((IPP2P_GNU * 100) + 52); ++ return 0; ++}/*udp_search_gnu*/ ++ ++ ++/*Search for UDP KaZaA commands*/ ++int ++udp_search_kazaa (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ ++ if (t[packet_len-1] == 0x00){ ++ t += (packet_len - 6); ++ if (memcmp(t, "KaZaA", 5) == 0) return (IPP2P_KAZAA * 100 +50); ++ } ++ ++ return 0; ++}/*udp_search_kazaa*/ ++ ++/*Search for UDP DirectConnect commands*/ ++int ++udp_search_directconnect (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ if ((*(t + 8) == 0x24) && (*(t + packet_len - 1) == 0x7c)) { ++ t+=8; ++ if (memcmp(t, "SR ", 3) == 0) return ((IPP2P_DC * 100) + 60); ++ if (memcmp(t, "Ping ", 5) == 0) return ((IPP2P_DC * 100) + 61); ++ } ++ return 0; ++}/*udp_search_directconnect*/ ++ ++ ++ ++/*Search for UDP BitTorrent commands*/ ++int ++udp_search_bit (unsigned char *haystack, int packet_len) ++{ ++ switch(packet_len) ++ { ++ case 24: ++ /* ^ 00 00 04 17 27 10 19 80 */ ++ if ((ntohl(get_u32(haystack, 8)) == 0x00000417) && (ntohl(get_u32(haystack, 12)) == 0x27101980)) ++ return (IPP2P_BIT * 100 + 50); ++ break; ++ case 44: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000400) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 51); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000400)) ++ return (IPP2P_BIT * 100 + 61); ++ break; ++ case 65: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000404) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 52); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000404)) ++ return (IPP2P_BIT * 100 + 62); ++ break; ++ case 67: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000406) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 53); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000406)) ++ return (IPP2P_BIT * 100 + 63); ++ break; ++ case 211: ++ if (get_u32(haystack, 8) == __constant_htonl(0x00000405)) ++ return (IPP2P_BIT * 100 + 54); ++ break; ++ case 29: ++ if ((get_u32(haystack, 8) == __constant_htonl(0x00000401))) ++ return (IPP2P_BIT * 100 + 55); ++ break; ++ case 52: ++ if (get_u32(haystack,8) == __constant_htonl(0x00000827) && ++ get_u32(haystack,12) == __constant_htonl(0x37502950)) ++ return (IPP2P_BIT * 100 + 80); ++ break; ++ default: ++ /* this packet does not have a constant size */ ++ if (packet_len >= 40 && get_u32(haystack, 16) == __constant_htonl(0x00000402) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 56); ++ break; ++ } ++ ++ /* some extra-bitcomet rules: ++ * "d1:" [a|r] "d2:id20:" ++ */ ++ if (packet_len > 30 && get_u8(haystack, 8) == 'd' && get_u8(haystack, 9) == '1' && get_u8(haystack, 10) == ':' ) ++ { ++ if (get_u8(haystack, 11) == 'a' || get_u8(haystack, 11) == 'r') ++ { ++ if (memcmp(haystack+12,"d2:id20:",8)==0) ++ return (IPP2P_BIT * 100 + 57); ++ } ++ } ++ ++#if 0 ++ /* bitlord rules */ ++ /* packetlen must be bigger than 40 */ ++ /* first 4 bytes are zero */ ++ if (packet_len > 40 && get_u32(haystack, 8) == 0x00000000) ++ { ++ /* first rule: 00 00 00 00 01 00 00 xx xx xx xx 00 00 00 00*/ ++ if (get_u32(haystack, 12) == 0x00000000 && ++ get_u32(haystack, 16) == 0x00010000 && ++ get_u32(haystack, 24) == 0x00000000 ) ++ return (IPP2P_BIT * 100 + 71); ++ ++ /* 00 01 00 00 0d 00 00 xx xx xx xx 00 00 00 00*/ ++ if (get_u32(haystack, 12) == 0x00000001 && ++ get_u32(haystack, 16) == 0x000d0000 && ++ get_u32(haystack, 24) == 0x00000000 ) ++ return (IPP2P_BIT * 100 + 71); ++ ++ ++ } ++#endif ++ ++ return 0; ++}/*udp_search_bit*/ ++ ++ ++ ++/*Search for Ares commands*/ ++//#define IPP2P_DEBUG_ARES ++int ++search_ares (const unsigned char *payload, const u16 plen) ++//int search_ares (unsigned char *haystack, int packet_len, int head_len) ++{ ++// const unsigned char *t = haystack + head_len; ++ ++ /* all ares packets start with */ ++ if (payload[1] == 0 && (plen - payload[0]) == 3) ++ { ++ switch (payload[2]) ++ { ++ case 0x5a: ++ /* ares connect */ ++ if ( plen == 6 && payload[5] == 0x05 ) return ((IPP2P_ARES * 100) + 1); ++ break; ++ case 0x09: ++ /* ares search, min 3 chars --> 14 bytes ++ * lets define a search can be up to 30 chars --> max 34 bytes ++ */ ++ if ( plen >= 14 && plen <= 34 ) return ((IPP2P_ARES * 100) + 1); ++ break; ++#ifdef IPP2P_DEBUG_ARES ++ default: ++ printk(KERN_DEBUG "Unknown Ares command %x recognized, len: %u \n", (unsigned int) payload[2],plen); ++#endif /* IPP2P_DEBUG_ARES */ ++ } ++ } ++ ++#if 0 ++ /* found connect packet: 03 00 5a 04 03 05 */ ++ /* new version ares 1.8: 03 00 5a xx xx 05 */ ++ if ((plen) == 6){ /* possible connect command*/ ++ if ((payload[0] == 0x03) && (payload[1] == 0x00) && (payload[2] == 0x5a) && (payload[5] == 0x05)) ++ return ((IPP2P_ARES * 100) + 1); ++ } ++ if ((plen) == 60){ /* possible download command*/ ++ if ((payload[59] == 0x0a) && (payload[58] == 0x0a)){ ++ if (memcmp(t, "PUSH SHA1:", 10) == 0) /* found download command */ ++ return ((IPP2P_ARES * 100) + 2); ++ } ++ } ++#endif ++ ++ return 0; ++} /*search_ares*/ ++ ++/*Search for SoulSeek commands*/ ++int ++search_soul (const unsigned char *payload, const u16 plen) ++{ ++//#define IPP2P_DEBUG_SOUL ++ /* match: xx xx xx xx | xx = sizeof(payload) - 4 */ ++ if (get_u32(payload, 0) == (plen - 4)){ ++ const __u32 m=get_u32(payload, 4); ++ /* match 00 yy yy 00, yy can be everything */ ++ if ( get_u8(payload, 4) == 0x00 && get_u8(payload, 7) == 0x00 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "0: Soulseek command 0x%x recognized\n",get_u32(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 1); ++ } ++ ++ /* next match: 01 yy 00 00 | yy can be everything */ ++ if ( get_u8(payload, 4) == 0x01 && get_u16(payload, 6) == 0x0000 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "1: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 2); ++ } ++ ++ /* other soulseek commandos are: 1-5,7,9,13-18,22,23,26,28,35-37,40-46,50,51,60,62-69,91,92,1001 */ ++ /* try to do this in an intelligent way */ ++ /* get all small commandos */ ++ switch(m) ++ { ++ case 7: ++ case 9: ++ case 22: ++ case 23: ++ case 26: ++ case 28: ++ case 50: ++ case 51: ++ case 60: ++ case 91: ++ case 92: ++ case 1001: ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "2: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 3); ++ } ++ ++ if (m > 0 && m < 6 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "3: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 4); ++ } ++ if (m > 12 && m < 19 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "4: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 5); ++ } ++ ++ if (m > 34 && m < 38 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "5: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 6); ++ } ++ ++ if (m > 39 && m < 47 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "6: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 7); ++ } ++ ++ if (m > 61 && m < 70 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "7: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 8); ++ } ++ ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "unknown SOULSEEK command: 0x%x, first 16 bit: 0x%x, first 8 bit: 0x%x ,soulseek ???\n",get_u32(payload, 4),get_u16(payload, 4) >> 16,get_u8(payload, 4) >> 24); ++#endif /* IPP2P_DEBUG_SOUL */ ++ } ++ ++ /* match 14 00 00 00 01 yy 00 00 00 STRING(YY) 01 00 00 00 00 46|50 00 00 00 00 */ ++ /* without size at the beginning !!! */ ++ if ( get_u32(payload, 0) == 0x14 && get_u8(payload, 4) == 0x01 ) ++ { ++ __u32 y=get_u32(payload, 5); ++ /* we need 19 chars + string */ ++ if ( (y + 19) <= (plen) ) ++ { ++ const unsigned char *w=payload+9+y; ++ if (get_u32(w, 0) == 0x01 && ( get_u16(w, 4) == 0x4600 || get_u16(w, 4) == 0x5000) && get_u32(w, 6) == 0x00); ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "Soulssek special client command recognized\n"); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 9); ++ } ++ } ++ return 0; ++} ++ ++ ++/*Search for WinMX commands*/ ++int ++search_winmx (const unsigned char *payload, const u16 plen) ++{ ++//#define IPP2P_DEBUG_WINMX ++ if (((plen) == 4) && (memcmp(payload, "SEND", 4) == 0)) return ((IPP2P_WINMX * 100) + 1); ++ if (((plen) == 3) && (memcmp(payload, "GET", 3) == 0)) return ((IPP2P_WINMX * 100) + 2); ++ //if (packet_len < (head_len + 10)) return 0; ++ if (plen < 10) return 0; ++ ++ if ((memcmp(payload, "SEND", 4) == 0) || (memcmp(payload, "GET", 3) == 0)){ ++ u16 c=4; ++ const u16 end=plen-2; ++ u8 count=0; ++ while (c < end) ++ { ++ if (payload[c]== 0x20 && payload[c+1] == 0x22) ++ { ++ c++; ++ count++; ++ if (count>=2) return ((IPP2P_WINMX * 100) + 3); ++ } ++ c++; ++ } ++ } ++ ++ if ( plen == 149 && payload[0] == '8' ) ++ { ++#ifdef IPP2P_DEBUG_WINMX ++ printk(KERN_INFO "maybe WinMX\n"); ++#endif ++ if (get_u32(payload,17) == 0 && get_u32(payload,21) == 0 && get_u32(payload,25) == 0 && ++// get_u32(payload,33) == __constant_htonl(0x71182b1a) && get_u32(payload,37) == __constant_htonl(0x05050000) && ++// get_u32(payload,133) == __constant_htonl(0x31097edf) && get_u32(payload,145) == __constant_htonl(0xdcb8f792)) ++ get_u16(payload,39) == 0 && get_u16(payload,135) == __constant_htons(0x7edf) && get_u16(payload,147) == __constant_htons(0xf792)) ++ ++ { ++#ifdef IPP2P_DEBUG_WINMX ++ printk(KERN_INFO "got WinMX\n"); ++#endif ++ return ((IPP2P_WINMX * 100) + 4); ++ } ++ } ++ return 0; ++} /*search_winmx*/ ++ ++ ++/*Search for appleJuice commands*/ ++int ++search_apple (const unsigned char *payload, const u16 plen) ++{ ++ if ( (plen > 7) && (payload[6] == 0x0d) && (payload[7] == 0x0a) && (memcmp(payload, "ajprot", 6) == 0)) return (IPP2P_APPLE * 100); ++ ++ return 0; ++} ++ ++ ++/*Search for BitTorrent commands*/ ++int ++search_bittorrent (const unsigned char *payload, const u16 plen) ++{ ++ if (plen > 20) ++ { ++ /* test for match 0x13+"BitTorrent protocol" */ ++ if (payload[0] == 0x13) ++ { ++ if (memcmp(payload+1, "BitTorrent protocol", 19) == 0) return (IPP2P_BIT * 100); ++ } ++ ++ /* get tracker commandos, all starts with GET / ++ * then it can follow: scrape| announce ++ * and then ?hash_info= ++ */ ++ if (memcmp(payload,"GET /",5) == 0) ++ { ++ /* message scrape */ ++ if ( memcmp(payload+5,"scrape?info_hash=",17)==0 ) return (IPP2P_BIT * 100 + 1); ++ /* message announce */ ++ if ( memcmp(payload+5,"announce?info_hash=",19)==0 ) return (IPP2P_BIT * 100 + 2); ++ } ++ } ++ else ++ { ++ /* bitcomet encryptes the first packet, so we have to detect another ++ * one later in the flow */ ++ /* first try failed, too many missdetections */ ++ //if ( size == 5 && get_u32(t,0) == __constant_htonl(1) && t[4] < 3) return (IPP2P_BIT * 100 + 3); ++ ++ /* second try: block request packets */ ++ if ( plen == 17 && get_u32(payload,0) == __constant_htonl(0x0d) && payload[4] == 0x06 && get_u32(payload,13) == __constant_htonl(0x4000) ) return (IPP2P_BIT * 100 + 3); ++ } ++ ++ return 0; ++} ++ ++ ++ ++/*check for Kazaa get command*/ ++int ++search_kazaa (const unsigned char *payload, const u16 plen) ++ ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a) && memcmp(payload, "GET /.hash=", 11) == 0) ++ return (IPP2P_DATA_KAZAA * 100); ++ ++ return 0; ++} ++ ++ ++/*check for gnutella get command*/ ++int ++search_gnu (const unsigned char *payload, const u16 plen) ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ if (memcmp(payload, "GET /get/", 9) == 0) return ((IPP2P_DATA_GNU * 100) + 1); ++ if (memcmp(payload, "GET /uri-res/", 13) == 0) return ((IPP2P_DATA_GNU * 100) + 2); ++ } ++ return 0; ++} ++ ++ ++/*check for gnutella get commands and other typical data*/ ++int ++search_all_gnu (const unsigned char *payload, const u16 plen) ++{ ++ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ ++ if (memcmp(payload, "GNUTELLA CONNECT/", 17) == 0) return ((IPP2P_GNU * 100) + 1); ++ if (memcmp(payload, "GNUTELLA/", 9) == 0) return ((IPP2P_GNU * 100) + 2); ++ ++ ++ if ((memcmp(payload, "GET /get/", 9) == 0) || (memcmp(payload, "GET /uri-res/", 13) == 0)) ++ { ++ u16 c=8; ++ const u16 end=plen-22; ++ while (c < end) { ++ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Gnutella-", 11) == 0) || (memcmp(&payload[c+2], "X-Queue:", 8) == 0))) ++ return ((IPP2P_GNU * 100) + 3); ++ c++; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++/*check for KaZaA download commands and other typical data*/ ++int ++search_all_kazaa (const unsigned char *payload, const u16 plen) ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ ++ if (memcmp(payload, "GIVE ", 5) == 0) return ((IPP2P_KAZAA * 100) + 1); ++ ++ if (memcmp(payload, "GET /", 5) == 0) { ++ u16 c = 8; ++ const u16 end=plen-22; ++ while (c < end) { ++ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Kazaa-Username: ", 18) == 0) || (memcmp(&payload[c+2], "User-Agent: PeerEnabler/", 24) == 0))) ++ return ((IPP2P_KAZAA * 100) + 2); ++ c++; ++ } ++ } ++ } ++ return 0; ++} ++ ++/*fast check for edonkey file segment transfer command*/ ++int ++search_edk (const unsigned char *payload, const u16 plen) ++{ ++ if (payload[0] != 0xe3) ++ return 0; ++ else { ++ if (payload[5] == 0x47) ++ return (IPP2P_DATA_EDK * 100); ++ else ++ return 0; ++ } ++} ++ ++ ++ ++/*intensive but slower search for some edonkey packets including size-check*/ ++int ++search_all_edk (const unsigned char *payload, const u16 plen) ++{ ++ if (payload[0] != 0xe3) ++ return 0; ++ else { ++ //t += head_len; ++ const u16 cmd = get_u16(payload, 1); ++ if (cmd == (plen - 5)) { ++ switch (payload[5]) { ++ case 0x01: return ((IPP2P_EDK * 100) + 1); /*Client: hello or Server:hello*/ ++ case 0x4c: return ((IPP2P_EDK * 100) + 9); /*Client: Hello-Answer*/ ++ } ++ } ++ return 0; ++ } ++} ++ ++ ++/*fast check for Direct Connect send command*/ ++int ++search_dc (const unsigned char *payload, const u16 plen) ++{ ++ ++ if (payload[0] != 0x24 ) ++ return 0; ++ else { ++ if (memcmp(&payload[1], "Send|", 5) == 0) ++ return (IPP2P_DATA_DC * 100); ++ else ++ return 0; ++ } ++ ++} ++ ++ ++/*intensive but slower check for all direct connect packets*/ ++int ++search_all_dc (const unsigned char *payload, const u16 plen) ++{ ++// unsigned char *t = haystack; ++ ++ if (payload[0] == 0x24 && payload[plen-1] == 0x7c) ++ { ++ const unsigned char *t=&payload[1]; ++ /* Client-Hub-Protocol */ ++ if (memcmp(t, "Lock ", 5) == 0) return ((IPP2P_DC * 100) + 1); ++ /* Client-Client-Protocol, some are already recognized by client-hub (like lock) */ ++ if (memcmp(t, "MyNick ", 7) == 0) return ((IPP2P_DC * 100) + 38); ++ } ++ return 0; ++} ++ ++/*check for mute*/ ++int ++search_mute (const unsigned char *payload, const u16 plen) ++{ ++ if ( plen == 209 || plen == 345 || plen == 473 || plen == 609 || plen == 1121 ) ++ { ++ //printk(KERN_DEBUG "size hit: %u",size); ++ if (memcmp(payload,"PublicKey: ",11) == 0 ) ++ { ++ return ((IPP2P_MUTE * 100) + 0); ++ ++/* if (memcmp(t+size-14,"\x0aEndPublicKey\x0a",14) == 0) ++ { ++ printk(KERN_DEBUG "end pubic key hit: %u",size); ++ ++ }*/ ++ } ++ } ++ return 0; ++} ++ ++ ++/* check for xdcc */ ++int ++search_xdcc (const unsigned char *payload, const u16 plen) ++{ ++ /* search in small packets only */ ++ if (plen > 20 && plen < 200 && payload[plen-1] == 0x0a && payload[plen-2] == 0x0d && memcmp(payload,"PRIVMSG ",8) == 0) ++ { ++ ++ u16 x=10; ++ const u16 end=plen - 13; ++ ++ /* is seems to be a irc private massage, chedck for xdcc command */ ++ while (x < end) ++ { ++ if (payload[x] == ':') ++ { ++ if ( memcmp(&payload[x+1],"xdcc send #",11) == 0 ) ++ return ((IPP2P_XDCC * 100) + 0); ++ } ++ x++; ++ } ++ } ++ return 0; ++} ++ ++/* search for waste */ ++int search_waste(const unsigned char *payload, const u16 plen) ++{ ++ if ( plen >= 8 && memcmp(payload,"GET.sha1:",9) == 0) ++ return ((IPP2P_WASTE * 100) + 0); ++ ++ return 0; ++} ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (const unsigned char *, const u16); ++} matchlist[] = { ++ {IPP2P_EDK,SHORT_HAND_IPP2P,20, &search_all_edk}, ++// {IPP2P_DATA_KAZAA,SHORT_HAND_DATA,200, &search_kazaa}, ++// {IPP2P_DATA_EDK,SHORT_HAND_DATA,60, &search_edk}, ++// {IPP2P_DATA_DC,SHORT_HAND_DATA,26, &search_dc}, ++ {IPP2P_DC,SHORT_HAND_IPP2P,5, search_all_dc}, ++// {IPP2P_DATA_GNU,SHORT_HAND_DATA,40, &search_gnu}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,5, &search_all_gnu}, ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,5, &search_all_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_IPP2P,20, &search_bittorrent}, ++ {IPP2P_APPLE,SHORT_HAND_IPP2P,5, &search_apple}, ++ {IPP2P_SOUL,SHORT_HAND_IPP2P,5, &search_soul}, ++ {IPP2P_WINMX,SHORT_HAND_IPP2P,2, &search_winmx}, ++ {IPP2P_ARES,SHORT_HAND_IPP2P,5, &search_ares}, ++ {IPP2P_MUTE,SHORT_HAND_NONE,200, &search_mute}, ++ {IPP2P_WASTE,SHORT_HAND_NONE,5, &search_waste}, ++ {IPP2P_XDCC,SHORT_HAND_NONE,5, &search_xdcc}, ++ {0,0,0,NULL} ++}; ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (unsigned char *, int); ++} udp_list[] = { ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,14, &udp_search_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_IPP2P,23, &udp_search_bit}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,11, &udp_search_gnu}, ++ {IPP2P_EDK,SHORT_HAND_IPP2P,9, &udp_search_edk}, ++ {IPP2P_DC,SHORT_HAND_IPP2P,12, &udp_search_directconnect}, ++ {0,0,0,NULL} ++}; ++ ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ const struct xt_match *match, ++#endif ++ const void *matchinfo, ++ int offset, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ unsigned int protoff, ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ const void *hdr, ++ u_int16_t datalen, ++#endif ++ int *hotdrop) ++{ ++ const struct ipt_p2p_info *info = matchinfo; ++ unsigned char *haystack; ++ struct iphdr *ip = ip_hdr(skb); ++ int p2p_result = 0, i = 0; ++// int head_len; ++ int hlen = ntohs(ip->tot_len)-(ip->ihl*4); /*hlen = packet-data length*/ ++ ++ /*must not be a fragment*/ ++ if (offset) { ++ if (info->debug) printk("IPP2P.match: offset found %i \n",offset); ++ return 0; ++ } ++ ++ /*make sure that skb is linear*/ ++ if(skb_is_nonlinear(skb)){ ++ if (info->debug) printk("IPP2P.match: nonlinear skb found\n"); ++ return 0; ++ } ++ ++ ++ haystack=(char *)ip+(ip->ihl*4); /*haystack = packet data*/ ++ ++ switch (ip->protocol){ ++ case IPPROTO_TCP: /*what to do with a TCP packet*/ ++ { ++ struct tcphdr *tcph = (void *) ip + ip->ihl * 4; ++ ++ if (tcph->fin) return 0; /*if FIN bit is set bail out*/ ++ if (tcph->syn) return 0; /*if SYN bit is set bail out*/ ++ if (tcph->rst) return 0; /*if RST bit is set bail out*/ ++ ++ haystack += tcph->doff * 4; /*get TCP-Header-Size*/ ++ hlen -= tcph->doff * 4; ++ while (matchlist[i].command) { ++ if ((((info->cmd & matchlist[i].command) == matchlist[i].command) || ++ ((info->cmd & matchlist[i].short_hand) == matchlist[i].short_hand)) && ++ (hlen > matchlist[i].packet_len)) { ++ p2p_result = matchlist[i].function_name(haystack, hlen); ++ if (p2p_result) ++ { ++ if (info->debug) printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ case IPPROTO_UDP: /*what to do with an UDP packet*/ ++ { ++ struct udphdr *udph = (void *) ip + ip->ihl * 4; ++ ++ while (udp_list[i].command){ ++ if ((((info->cmd & udp_list[i].command) == udp_list[i].command) || ++ ((info->cmd & udp_list[i].short_hand) == udp_list[i].short_hand)) && ++ (hlen > udp_list[i].packet_len)) { ++ p2p_result = udp_list[i].function_name(haystack, hlen); ++ if (p2p_result){ ++ if (info->debug) printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(udph->source), NIPQUAD(ip->daddr),ntohs(udph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ default: return 0; ++ } ++} ++ ++ ++ ++static int ++checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ const void *ip, ++ const struct xt_match *match, ++#else ++ const struct ipt_ip *ip, ++#endif ++ void *matchinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int matchsize, ++#endif ++ unsigned int hook_mask) ++{ ++ /* Must specify -p tcp */ ++/* if (ip->proto != IPPROTO_TCP || (ip->invflags & IPT_INV_PROTO)) { ++ * printk("ipp2p: Only works on TCP packets, use -p tcp\n"); ++ * return 0; ++ * }*/ ++ return 1; ++} ++ ++ ++ ++ ++static struct ipt_match ipp2p_match = { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ { NULL, NULL }, ++ "ipp2p", ++ &match, ++ &checkentry, ++ NULL, ++ THIS_MODULE ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ .name = "ipp2p", ++ .match = &match, ++ .family = AF_INET, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ .matchsize = sizeof(struct ipt_p2p_info), ++#endif ++ .checkentry = &checkentry, ++ .me = THIS_MODULE, ++#endif ++}; ++ ++ ++static int __init init(void) ++{ ++ printk(KERN_INFO "IPP2P v%s loading\n", IPP2P_VERSION); ++ return xt_register_match(&ipp2p_match); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&ipp2p_match); ++ printk(KERN_INFO "IPP2P v%s unloaded\n", IPP2P_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++ +Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig ++++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig +@@ -245,6 +245,12 @@ config IP_NF_MATCH_IPRANGE + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_MATCH_IPP2P ++ tristate "IPP2P" ++ depends on IP_NF_IPTABLES ++ help ++ Module for matching traffic of various Peer-to-Peer applications ++ + config IP_NF_MATCH_TOS + tristate "TOS match support" + depends on IP_NF_IPTABLES +Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile ++++ linux-2.6.21.7/net/ipv4/netfilter/Makefile +@@ -91,6 +91,7 @@ obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o + obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o ++obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o + + # targets + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o diff --git a/target/linux/generic-2.6/patches-2.6.21/120-openswan-2.4.0.kernel-2.6-natt.patch b/target/linux/generic-2.6/patches-2.6.21/120-openswan-2.4.0.kernel-2.6-natt.patch new file mode 100644 index 0000000000..6df04a5a41 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/120-openswan-2.4.0.kernel-2.6-natt.patch @@ -0,0 +1,172 @@ +Index: linux-2.6.21.7/include/net/xfrmudp.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/net/xfrmudp.h +@@ -0,0 +1,10 @@ ++/* ++ * pointer to function for type that xfrm4_input wants, to permit ++ * decoupling of XFRM from udp.c ++ */ ++#define HAVE_XFRM4_UDP_REGISTER ++ ++typedef int (*xfrm4_rcv_encap_t)(struct sk_buff *skb, __u16 encap_type); ++extern int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func ++ , xfrm4_rcv_encap_t *oldfunc); ++extern int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func); +Index: linux-2.6.21.7/net/ipv4/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/Kconfig ++++ linux-2.6.21.7/net/ipv4/Kconfig +@@ -266,6 +266,12 @@ config NET_IPGRE_BROADCAST + Network), but can be distributed all over the Internet. If you want + to do that, say Y here and to "IP multicast routing" below. + ++config IPSEC_NAT_TRAVERSAL ++ bool "IPSEC NAT-Traversal (KLIPS compatible)" ++ depends on INET ++ ---help--- ++ Includes support for RFC3947/RFC3948 NAT-Traversal of ESP over UDP. ++ + config IP_MROUTE + bool "IP: multicast routing" + depends on IP_MULTICAST +Index: linux-2.6.21.7/net/ipv4/udp.c +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/udp.c ++++ linux-2.6.21.7/net/ipv4/udp.c +@@ -101,12 +101,15 @@ + #include + #include + #include ++#include + #include "udp_impl.h" + + /* + * Snmp MIB for the UDP layer + */ + ++static xfrm4_rcv_encap_t xfrm4_rcv_encap_func; ++ + DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly; + + struct hlist_head udp_hash[UDP_HTABLE_SIZE]; +@@ -915,6 +918,42 @@ int udp_disconnect(struct sock *sk, int + return 0; + } + ++#if defined(CONFIG_XFRM) || defined(CONFIG_IPSEC_NAT_TRAVERSAL) ++ ++/* if XFRM isn't a module, then register it directly. */ ++#if 0 && !defined(CONFIG_XFRM_MODULE) && !defined(CONFIG_IPSEC_NAT_TRAVERSAL) ++static xfrm4_rcv_encap_t xfrm4_rcv_encap_func = xfrm4_rcv_encap; ++#else ++static xfrm4_rcv_encap_t xfrm4_rcv_encap_func = NULL; ++#endif ++ ++int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func ++ , xfrm4_rcv_encap_t *oldfunc) ++{ ++ if(oldfunc != NULL) { ++ *oldfunc = xfrm4_rcv_encap_func; ++ } ++ ++#if 0 ++ if(xfrm4_rcv_encap_func != NULL) ++ return -1; ++#endif ++ ++ xfrm4_rcv_encap_func = func; ++ return 0; ++} ++ ++int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func) ++{ ++ if(xfrm4_rcv_encap_func != func) ++ return -1; ++ ++ xfrm4_rcv_encap_func = NULL; ++ return 0; ++} ++#endif /* CONFIG_XFRM_MODULE || CONFIG_IPSEC_NAT_TRAVERSAL */ ++ ++ + /* return: + * 1 if the the UDP system should process it + * 0 if we should drop this packet +@@ -922,7 +961,7 @@ int udp_disconnect(struct sock *sk, int + */ + static int udp_encap_rcv(struct sock * sk, struct sk_buff *skb) + { +-#ifndef CONFIG_XFRM ++#if !defined(CONFIG_XFRM) && !defined(CONFIG_IPSEC_NAT_TRAVERSAL) + return 1; + #else + struct udp_sock *up = udp_sk(sk); +@@ -937,11 +976,11 @@ static int udp_encap_rcv(struct sock * s + /* if we're overly short, let UDP handle it */ + len = skb->len - sizeof(struct udphdr); + if (len <= 0) +- return 1; ++ return 2; + + /* if this is not encapsulated socket, then just return now */ + if (!encap_type) +- return 1; ++ return 3; + + /* If this is a paged skb, make sure we pull up + * whatever data we need to look at. */ +@@ -964,7 +1003,7 @@ static int udp_encap_rcv(struct sock * s + len = sizeof(struct udphdr); + } else + /* Must be an IKE packet.. pass it through */ +- return 1; ++ return 4; + break; + case UDP_ENCAP_ESPINUDP_NON_IKE: + /* Check if this is a keepalive packet. If so, eat it. */ +@@ -977,7 +1016,7 @@ static int udp_encap_rcv(struct sock * s + len = sizeof(struct udphdr) + 2 * sizeof(u32); + } else + /* Must be an IKE packet.. pass it through */ +- return 1; ++ return 5; + break; + } + +@@ -988,6 +1027,8 @@ static int udp_encap_rcv(struct sock * s + */ + if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + return 0; ++ if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) ++ return 0; + + /* Now we can update and verify the packet length... */ + iph = skb->nh.iph; +@@ -1051,9 +1092,13 @@ int udp_queue_rcv_skb(struct sock * sk, + return 0; + } + if (ret < 0) { +- /* process the ESP packet */ +- ret = xfrm4_rcv_encap(skb, up->encap_type); +- UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag); ++ if(xfrm4_rcv_encap_func != NULL) { ++ ret = (*xfrm4_rcv_encap_func)(skb, up->encap_type); ++ UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag); ++ } else { ++ UDP_INC_STATS_BH(UDP_MIB_INERRORS, up->pcflag); ++ ret = 1; ++ } + return -ret; + } + /* FALLTHROUGH -- it's a UDP Packet */ +@@ -1733,3 +1778,9 @@ EXPORT_SYMBOL(udp_poll); + EXPORT_SYMBOL(udp_proc_register); + EXPORT_SYMBOL(udp_proc_unregister); + #endif ++ ++#if defined(CONFIG_IPSEC_NAT_TRAVERSAL) ++EXPORT_SYMBOL(udp4_register_esp_rcvencap); ++EXPORT_SYMBOL(udp4_unregister_esp_rcvencap); ++#endif ++ diff --git a/target/linux/generic-2.6/patches-2.6.21/130-netfilter_ipset.patch b/target/linux/generic-2.6/patches-2.6.21/130-netfilter_ipset.patch new file mode 100644 index 0000000000..b59c158652 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/130-netfilter_ipset.patch @@ -0,0 +1,7714 @@ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set.h +@@ -0,0 +1,498 @@ ++#ifndef _IP_SET_H ++#define _IP_SET_H ++ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#if 0 ++#define IP_SET_DEBUG ++#endif ++ ++/* ++ * A sockopt of such quality has hardly ever been seen before on the open ++ * market! This little beauty, hardly ever used: above 64, so it's ++ * traditionally used for firewalling, not touched (even once!) by the ++ * 2.0, 2.2 and 2.4 kernels! ++ * ++ * Comes with its own certificate of authenticity, valid anywhere in the ++ * Free world! ++ * ++ * Rusty, 19.4.2000 ++ */ ++#define SO_IP_SET 83 ++ ++/* ++ * Heavily modify by Joakim Axelsson 08.03.2002 ++ * - Made it more modulebased ++ * ++ * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 ++ * - bindings added ++ * - in order to "deal with" backward compatibility, renamed to ipset ++ */ ++ ++/* ++ * Used so that the kernel module and ipset-binary can match their versions ++ */ ++#define IP_SET_PROTOCOL_VERSION 2 ++ ++#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ ++ ++/* Lets work with our own typedef for representing an IP address. ++ * We hope to make the code more portable, possibly to IPv6... ++ * ++ * The representation works in HOST byte order, because most set types ++ * will perform arithmetic operations and compare operations. ++ * ++ * For now the type is an uint32_t. ++ * ++ * Make sure to ONLY use the functions when translating and parsing ++ * in order to keep the host byte order and make it more portable: ++ * parse_ip() ++ * parse_mask() ++ * parse_ipandmask() ++ * ip_tostring() ++ * (Joakim: where are they???) ++ */ ++ ++typedef uint32_t ip_set_ip_t; ++ ++/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t ++ * and IP_SET_INVALID_ID if you want to increase the max number of sets. ++ */ ++typedef uint16_t ip_set_id_t; ++ ++#define IP_SET_INVALID_ID 65535 ++ ++/* How deep we follow bindings */ ++#define IP_SET_MAX_BINDINGS 6 ++ ++/* ++ * Option flags for kernel operations (ipt_set_info) ++ */ ++#define IPSET_SRC 0x01 /* Source match/add */ ++#define IPSET_DST 0x02 /* Destination match/add */ ++#define IPSET_MATCH_INV 0x04 /* Inverse matching */ ++ ++/* ++ * Set features ++ */ ++#define IPSET_TYPE_IP 0x01 /* IP address type of set */ ++#define IPSET_TYPE_PORT 0x02 /* Port type of set */ ++#define IPSET_DATA_SINGLE 0x04 /* Single data storage */ ++#define IPSET_DATA_DOUBLE 0x08 /* Double data storage */ ++ ++/* Reserved keywords */ ++#define IPSET_TOKEN_DEFAULT ":default:" ++#define IPSET_TOKEN_ALL ":all:" ++ ++/* SO_IP_SET operation constants, and their request struct types. ++ * ++ * Operation ids: ++ * 0-99: commands with version checking ++ * 100-199: add/del/test/bind/unbind ++ * 200-299: list, save, restore ++ */ ++ ++/* Single shot operations: ++ * version, create, destroy, flush, rename and swap ++ * ++ * Sets are identified by name. ++ */ ++ ++#define IP_SET_REQ_STD \ ++ unsigned op; \ ++ unsigned version; \ ++ char name[IP_SET_MAXNAMELEN] ++ ++#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ ++struct ip_set_req_create { ++ IP_SET_REQ_STD; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ ++struct ip_set_req_std { ++ IP_SET_REQ_STD; ++}; ++ ++#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */ ++/* Uses ip_set_req_std */ ++ ++#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */ ++/* Uses ip_set_req_create */ ++ ++#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */ ++/* Uses ip_set_req_create */ ++ ++union ip_set_name_index { ++ char name[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++}; ++ ++#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ ++struct ip_set_req_get_set { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++}; ++ ++#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ ++/* Uses ip_set_req_get_set */ ++ ++#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ ++struct ip_set_req_version { ++ unsigned op; ++ unsigned version; ++}; ++ ++/* Double shots operations: ++ * add, del, test, bind and unbind. ++ * ++ * First we query the kernel to get the index and type of the target set, ++ * then issue the command. Validity of IP is checked in kernel in order ++ * to minimalize sockopt operations. ++ */ ++ ++/* Get minimal set data for add/del/test/bind/unbind IP */ ++#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */ ++struct ip_set_req_adt_get { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_REQ_BYINDEX \ ++ unsigned op; \ ++ ip_set_id_t index; ++ ++struct ip_set_req_adt { ++ IP_SET_REQ_BYINDEX; ++}; ++ ++#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++struct ip_set_req_bind { ++ IP_SET_REQ_BYINDEX; ++ char binding[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */ ++/* Uses ip_set_req_bind, with type speficic addage ++ * index = 0 means unbinding for all sets */ ++ ++#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++ ++/* Multiple shots operations: list, save, restore. ++ * ++ * - check kernel version and query the max number of sets ++ * - get the basic information on all sets ++ * and size required for the next step ++ * - get actual set data: header, data, bindings ++ */ ++ ++/* Get max_sets and the index of a queried set ++ */ ++#define IP_SET_OP_MAX_SETS 0x00000020 ++struct ip_set_req_max_sets { ++ unsigned op; ++ unsigned version; ++ ip_set_id_t max_sets; /* max_sets */ ++ ip_set_id_t sets; /* real number of sets */ ++ union ip_set_name_index set; /* index of set if name used */ ++}; ++ ++/* Get the id and name of the sets plus size for next step */ ++#define IP_SET_OP_LIST_SIZE 0x00000201 ++#define IP_SET_OP_SAVE_SIZE 0x00000202 ++struct ip_set_req_setnames { ++ unsigned op; ++ ip_set_id_t index; /* set to list/save */ ++ size_t size; /* size to get setdata/bindings */ ++ /* followed by sets number of struct ip_set_name_list */ ++}; ++ ++struct ip_set_name_list { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ ip_set_id_t id; ++}; ++ ++/* The actual list operation */ ++#define IP_SET_OP_LIST 0x00000203 ++struct ip_set_req_list { ++ IP_SET_REQ_BYINDEX; ++ /* sets number of struct ip_set_list in reply */ ++}; ++ ++struct ip_set_list { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ u_int32_t ref; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++ size_t bindings_size; /* Set bindings data of bindings_size */ ++}; ++ ++struct ip_set_hash_list { ++ ip_set_ip_t ip; ++ ip_set_id_t binding; ++}; ++ ++/* The save operation */ ++#define IP_SET_OP_SAVE 0x00000204 ++/* Uses ip_set_req_list, in the reply replaced by ++ * sets number of struct ip_set_save plus a marker ++ * ip_set_save followed by ip_set_hash_save structures. ++ */ ++struct ip_set_save { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++/* At restoring, ip == 0 means default binding for the given set: */ ++struct ip_set_hash_save { ++ ip_set_ip_t ip; ++ ip_set_id_t id; ++ ip_set_id_t binding; ++}; ++ ++/* The restore operation */ ++#define IP_SET_OP_RESTORE 0x00000205 ++/* Uses ip_set_req_setnames followed by ip_set_restore structures ++ * plus a marker ip_set_restore, followed by ip_set_hash_save ++ * structures. ++ */ ++struct ip_set_restore { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ size_t header_size; /* Create data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) ++{ ++ return 4 * ((((b - a + 8) / 8) + 3) / 4); ++} ++ ++#ifdef __KERNEL__ ++ ++#define ip_set_printk(format, args...) \ ++ do { \ ++ printk("%s: %s: ", __FILE__, __FUNCTION__); \ ++ printk(format "\n" , ## args); \ ++ } while (0) ++ ++#if defined(IP_SET_DEBUG) ++#define DP(format, args...) \ ++ do { \ ++ printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ ++ printk(format "\n" , ## args); \ ++ } while (0) ++#define IP_SET_ASSERT(x) \ ++ do { \ ++ if (!(x)) \ ++ printk("IP_SET_ASSERT: %s:%i(%s)\n", \ ++ __FILE__, __LINE__, __FUNCTION__); \ ++ } while (0) ++#else ++#define DP(format, args...) ++#define IP_SET_ASSERT(x) ++#endif ++ ++struct ip_set; ++ ++/* ++ * The ip_set_type definition - one per set type, e.g. "ipmap". ++ * ++ * Each individual set has a pointer, set->type, going to one ++ * of these structures. Function pointers inside the structure implement ++ * the real behaviour of the sets. ++ * ++ * If not mentioned differently, the implementation behind the function ++ * pointers of a set_type, is expected to return 0 if ok, and a negative ++ * errno (e.g. -EINVAL) on error. ++ */ ++struct ip_set_type { ++ struct list_head list; /* next in list of set types */ ++ ++ /* test for IP in set (kernel: iptables -m set src|dst) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ ip_set_ip_t *ip, ++ const u_int32_t *flags, ++ unsigned char index); ++ ++ /* test for IP in set (userspace: ipset -T set IP) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* ++ * Size of the data structure passed by when ++ * adding/deletin/testing an entry. ++ */ ++ size_t reqsize; ++ ++ /* Add IP into set (userspace: ipset -A set IP) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* Add IP into set (kernel: iptables ... -j SET set src|dst) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ ip_set_ip_t *ip, ++ const u_int32_t *flags, ++ unsigned char index); ++ ++ /* remove IP from set (userspace: ipset -D set --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* remove IP from set (kernel: iptables ... -j SET --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ ip_set_ip_t *ip, ++ const u_int32_t *flags, ++ unsigned char index); ++ ++ /* new set creation - allocated type specific items ++ */ ++ int (*create) (struct ip_set *set, ++ const void *data, size_t size); ++ ++ /* retry the operation after successfully tweaking the set ++ */ ++ int (*retry) (struct ip_set *set); ++ ++ /* set destruction - free type specific items ++ * There is no return value. ++ * Can be called only when child sets are destroyed. ++ */ ++ void (*destroy) (struct ip_set *set); ++ ++ /* set flushing - reset all bits in the set, or something similar. ++ * There is no return value. ++ */ ++ void (*flush) (struct ip_set *set); ++ ++ /* Listing: size needed for header ++ */ ++ size_t header_size; ++ ++ /* Listing: Get the header ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_header_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_header) (const struct ip_set *set, ++ void *data); ++ ++ /* Listing: Get the size for the set members ++ */ ++ int (*list_members_size) (const struct ip_set *set); ++ ++ /* Listing: Get the set members ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_member_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_members) (const struct ip_set *set, ++ void *data); ++ ++ char typename[IP_SET_MAXNAMELEN]; ++ unsigned char features; ++ int protocol_version; ++ ++ /* Set this to THIS_MODULE if you are a module, otherwise NULL */ ++ struct module *me; ++}; ++ ++extern int ip_set_register_set_type(struct ip_set_type *set_type); ++extern void ip_set_unregister_set_type(struct ip_set_type *set_type); ++ ++/* A generic ipset */ ++struct ip_set { ++ char name[IP_SET_MAXNAMELEN]; /* the name of the set */ ++ rwlock_t lock; /* lock for concurrency control */ ++ ip_set_id_t id; /* set id for swapping */ ++ ip_set_id_t binding; /* default binding for the set */ ++ atomic_t ref; /* in kernel and in hash references */ ++ struct ip_set_type *type; /* the set types */ ++ void *data; /* pooltype specific data */ ++}; ++ ++/* Structure to bind set elements to sets */ ++struct ip_set_hash { ++ struct list_head list; /* list of clashing entries in hash */ ++ ip_set_ip_t ip; /* ip from set */ ++ ip_set_id_t id; /* set id */ ++ ip_set_id_t binding; /* set we bind the element to */ ++}; ++ ++/* register and unregister set references */ ++extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]); ++extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id); ++extern void ip_set_put(ip_set_id_t id); ++ ++/* API for iptables set match, and SET target */ ++extern void ip_set_addip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern void ip_set_delip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern int ip_set_testip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_H*/ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iphash.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iphash.h +@@ -0,0 +1,30 @@ ++#ifndef __IP_SET_IPHASH_H ++#define __IP_SET_IPHASH_H ++ ++#include ++ ++#define SETTYPE_NAME "iphash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iphash { ++ ip_set_ip_t *members; /* the iphash proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t netmask; /* netmask */ ++ void *initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_iphash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_iphash { ++ ip_set_ip_t ip; ++}; ++ ++#endif /* __IP_SET_IPHASH_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipmap.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipmap.h +@@ -0,0 +1,56 @@ ++#ifndef __IP_SET_IPMAP_H ++#define __IP_SET_IPMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "ipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_ipmap { ++ void *members; /* the ipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ ip_set_ip_t netmask; /* subnet netmask */ ++ ip_set_ip_t sizeid; /* size of set in IPs */ ++ ip_set_ip_t hosts; /* number of hosts in a subnet */ ++}; ++ ++struct ip_set_req_ipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_ipmap { ++ ip_set_ip_t ip; ++}; ++ ++unsigned int ++mask_to_bits(ip_set_ip_t mask) ++{ ++ unsigned int bits = 32; ++ ip_set_ip_t maskaddr; ++ ++ if (mask == 0xFFFFFFFF) ++ return bits; ++ ++ maskaddr = 0xFFFFFFFE; ++ while (--bits >= 0 && maskaddr != mask) ++ maskaddr <<= 1; ++ ++ return bits; ++} ++ ++ip_set_ip_t ++range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) ++{ ++ ip_set_ip_t mask = 0xFFFFFFFE; ++ ++ *bits = 32; ++ while (--(*bits) >= 0 && mask && (to & mask) != from) ++ mask <<= 1; ++ ++ return mask; ++} ++ ++#endif /* __IP_SET_IPMAP_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipporthash.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipporthash.h +@@ -0,0 +1,34 @@ ++#ifndef __IP_SET_IPPORTHASH_H ++#define __IP_SET_IPPORTHASH_H ++ ++#include ++ ++#define SETTYPE_NAME "ipporthash" ++#define MAX_RANGE 0x0000FFFF ++#define INVALID_PORT (MAX_RANGE + 1) ++ ++struct ip_set_ipporthash { ++ ip_set_ip_t *members; /* the ipporthash proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ void *initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_ipporthash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_ipporthash { ++ ip_set_ip_t ip; ++ ip_set_ip_t port; ++}; ++ ++#endif /* __IP_SET_IPPORTHASH_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptree.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptree.h +@@ -0,0 +1,40 @@ ++#ifndef __IP_SET_IPTREE_H ++#define __IP_SET_IPTREE_H ++ ++#include ++ ++#define SETTYPE_NAME "iptree" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iptreed { ++ unsigned long expires[256]; /* x.x.x.ADDR */ ++}; ++ ++struct ip_set_iptreec { ++ struct ip_set_iptreed *tree[256]; /* x.x.ADDR.* */ ++}; ++ ++struct ip_set_iptreeb { ++ struct ip_set_iptreec *tree[256]; /* x.ADDR.*.* */ ++}; ++ ++struct ip_set_iptree { ++ unsigned int timeout; ++ unsigned int gc_interval; ++#ifdef __KERNEL__ ++ uint32_t elements; /* number of elements */ ++ struct timer_list gc; ++ struct ip_set_iptreeb *tree[256]; /* ADDR.*.*.* */ ++#endif ++}; ++ ++struct ip_set_req_iptree_create { ++ unsigned int timeout; ++}; ++ ++struct ip_set_req_iptree { ++ ip_set_ip_t ip; ++ unsigned int timeout; ++}; ++ ++#endif /* __IP_SET_IPTREE_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptreemap.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptreemap.h +@@ -0,0 +1,40 @@ ++#ifndef __IP_SET_IPTREEMAP_H ++#define __IP_SET_IPTREEMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "iptreemap" ++ ++#ifdef __KERNEL__ ++struct ip_set_iptreemap_d { ++ unsigned char bitmap[32]; /* x.x.x.y */ ++}; ++ ++struct ip_set_iptreemap_c { ++ struct ip_set_iptreemap_d *tree[256]; /* x.x.y.x */ ++}; ++ ++struct ip_set_iptreemap_b { ++ struct ip_set_iptreemap_c *tree[256]; /* x.y.x.x */ ++ unsigned char dirty[32]; ++}; ++#endif ++ ++struct ip_set_iptreemap { ++ unsigned int gc_interval; ++#ifdef __KERNEL__ ++ struct timer_list gc; ++ struct ip_set_iptreemap_b *tree[256]; /* y.x.x.x */ ++#endif ++}; ++ ++struct ip_set_req_iptreemap_create { ++ unsigned int gc_interval; ++}; ++ ++struct ip_set_req_iptreemap { ++ ip_set_ip_t start; ++ ip_set_ip_t end; ++}; ++ ++#endif /* __IP_SET_IPTREEMAP_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_jhash.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_jhash.h +@@ -0,0 +1,148 @@ ++#ifndef _LINUX_IPSET_JHASH_H ++#define _LINUX_IPSET_JHASH_H ++ ++/* This is a copy of linux/jhash.h but the types u32/u8 are changed ++ * to __u32/__u8 so that the header file can be included into ++ * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) ++ */ ++ ++/* jhash.h: Jenkins hash support. ++ * ++ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) ++ * ++ * http://burtleburtle.net/bob/hash/ ++ * ++ * These are the credits from Bob's sources: ++ * ++ * lookup2.c, by Bob Jenkins, December 1996, Public Domain. ++ * hash(), hash2(), hash3, and mix() are externally useful functions. ++ * Routines to test the hash are included if SELF_TEST is defined. ++ * You can use this free for any purpose. It has no warranty. ++ * ++ * Copyright (C) 2003 David S. Miller (davem@redhat.com) ++ * ++ * I've modified Bob's hash to be useful in the Linux kernel, and ++ * any bugs present are surely my fault. -DaveM ++ */ ++ ++/* NOTE: Arguments are modified. */ ++#define __jhash_mix(a, b, c) \ ++{ \ ++ a -= b; a -= c; a ^= (c>>13); \ ++ b -= c; b -= a; b ^= (a<<8); \ ++ c -= a; c -= b; c ^= (b>>13); \ ++ a -= b; a -= c; a ^= (c>>12); \ ++ b -= c; b -= a; b ^= (a<<16); \ ++ c -= a; c -= b; c ^= (b>>5); \ ++ a -= b; a -= c; a ^= (c>>3); \ ++ b -= c; b -= a; b ^= (a<<10); \ ++ c -= a; c -= b; c ^= (b>>15); \ ++} ++ ++/* The golden ration: an arbitrary value */ ++#define JHASH_GOLDEN_RATIO 0x9e3779b9 ++ ++/* The most generic version, hashes an arbitrary sequence ++ * of bytes. No alignment or length assumptions are made about ++ * the input key. ++ */ ++static inline __u32 jhash(void *key, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ __u8 *k = key; ++ ++ len = length; ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ ++ while (len >= 12) { ++ a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); ++ b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); ++ c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); ++ ++ __jhash_mix(a,b,c); ++ ++ k += 12; ++ len -= 12; ++ } ++ ++ c += length; ++ switch (len) { ++ case 11: c += ((__u32)k[10]<<24); ++ case 10: c += ((__u32)k[9]<<16); ++ case 9 : c += ((__u32)k[8]<<8); ++ case 8 : b += ((__u32)k[7]<<24); ++ case 7 : b += ((__u32)k[6]<<16); ++ case 6 : b += ((__u32)k[5]<<8); ++ case 5 : b += k[4]; ++ case 4 : a += ((__u32)k[3]<<24); ++ case 3 : a += ((__u32)k[2]<<16); ++ case 2 : a += ((__u32)k[1]<<8); ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++/* A special optimized version that handles 1 or more of __u32s. ++ * The length parameter here is the number of __u32s in the key. ++ */ ++static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ len = length; ++ ++ while (len >= 3) { ++ a += k[0]; ++ b += k[1]; ++ c += k[2]; ++ __jhash_mix(a, b, c); ++ k += 3; len -= 3; ++ } ++ ++ c += length * 4; ++ ++ switch (len) { ++ case 2 : b += k[1]; ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++ ++/* A special ultra-optimized versions that knows they are hashing exactly ++ * 3, 2 or 1 word(s). ++ * ++ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally ++ * done at the end is not done here. ++ */ ++static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) ++{ ++ a += JHASH_GOLDEN_RATIO; ++ b += JHASH_GOLDEN_RATIO; ++ c += initval; ++ ++ __jhash_mix(a, b, c); ++ ++ return c; ++} ++ ++static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) ++{ ++ return jhash_3words(a, b, 0, initval); ++} ++ ++static inline __u32 jhash_1word(__u32 a, __u32 initval) ++{ ++ return jhash_3words(a, 0, 0, initval); ++} ++ ++#endif /* _LINUX_IPSET_JHASH_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_macipmap.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_macipmap.h +@@ -0,0 +1,38 @@ ++#ifndef __IP_SET_MACIPMAP_H ++#define __IP_SET_MACIPMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "macipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++/* general flags */ ++#define IPSET_MACIP_MATCHUNSET 1 ++ ++/* per ip flags */ ++#define IPSET_MACIP_ISSET 1 ++ ++struct ip_set_macipmap { ++ void *members; /* the macipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap { ++ ip_set_ip_t ip; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++struct ip_set_macip { ++ unsigned short flags; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++#endif /* __IP_SET_MACIPMAP_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_malloc.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_malloc.h +@@ -0,0 +1,116 @@ ++#ifndef _IP_SET_MALLOC_H ++#define _IP_SET_MALLOC_H ++ ++#ifdef __KERNEL__ ++ ++/* Memory allocation and deallocation */ ++static size_t max_malloc_size = 0; ++ ++static inline void init_max_malloc_size(void) ++{ ++#define CACHE(x) max_malloc_size = x; ++#include ++#undef CACHE ++} ++ ++static inline void * ip_set_malloc(size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ return vmalloc(bytes); ++ else ++ return kmalloc(bytes, GFP_KERNEL); ++} ++ ++static inline void ip_set_free(void * data, size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ vfree(data); ++ else ++ kfree(data); ++} ++ ++struct harray { ++ size_t max_elements; ++ void *arrays[0]; ++}; ++ ++static inline void * ++harray_malloc(size_t hashsize, size_t typesize, int flags) ++{ ++ struct harray *harray; ++ size_t max_elements, size, i, j; ++ ++ if (!max_malloc_size) ++ init_max_malloc_size(); ++ ++ if (typesize > max_malloc_size) ++ return NULL; ++ ++ max_elements = max_malloc_size/typesize; ++ size = hashsize/max_elements; ++ if (hashsize % max_elements) ++ size++; ++ ++ /* Last pointer signals end of arrays */ ++ harray = kmalloc(sizeof(struct harray) + (size + 1) * sizeof(void *), ++ flags); ++ ++ if (!harray) ++ return NULL; ++ ++ for (i = 0; i < size - 1; i++) { ++ harray->arrays[i] = kmalloc(max_elements * typesize, flags); ++ if (!harray->arrays[i]) ++ goto undo; ++ memset(harray->arrays[i], 0, max_elements * typesize); ++ } ++ harray->arrays[i] = kmalloc((hashsize - i * max_elements) * typesize, ++ flags); ++ if (!harray->arrays[i]) ++ goto undo; ++ memset(harray->arrays[i], 0, (hashsize - i * max_elements) * typesize); ++ ++ harray->max_elements = max_elements; ++ harray->arrays[size] = NULL; ++ ++ return (void *)harray; ++ ++ undo: ++ for (j = 0; j < i; j++) { ++ kfree(harray->arrays[j]); ++ } ++ kfree(harray); ++ return NULL; ++} ++ ++static inline void harray_free(void *h) ++{ ++ struct harray *harray = (struct harray *) h; ++ size_t i; ++ ++ for (i = 0; harray->arrays[i] != NULL; i++) ++ kfree(harray->arrays[i]); ++ kfree(harray); ++} ++ ++static inline void harray_flush(void *h, size_t hashsize, size_t typesize) ++{ ++ struct harray *harray = (struct harray *) h; ++ size_t i; ++ ++ for (i = 0; harray->arrays[i+1] != NULL; i++) ++ memset(harray->arrays[i], 0, harray->max_elements * typesize); ++ memset(harray->arrays[i], 0, ++ (hashsize - i * harray->max_elements) * typesize); ++} ++ ++#define HARRAY_ELEM(h, type, which) \ ++({ \ ++ struct harray *__h = (struct harray *)(h); \ ++ ((type)((__h)->arrays[(which)/(__h)->max_elements]) \ ++ + (which)%(__h)->max_elements); \ ++}) ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_MALLOC_H*/ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_nethash.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_nethash.h +@@ -0,0 +1,55 @@ ++#ifndef __IP_SET_NETHASH_H ++#define __IP_SET_NETHASH_H ++ ++#include ++ ++#define SETTYPE_NAME "nethash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_nethash { ++ ip_set_ip_t *members; /* the nethash proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ unsigned char cidr[30]; /* CIDR sizes */ ++ void *initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_nethash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++}; ++ ++struct ip_set_req_nethash { ++ ip_set_ip_t ip; ++ unsigned char cidr; ++}; ++ ++static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; ++ ++static inline ip_set_ip_t ++pack(ip_set_ip_t ip, unsigned char cidr) ++{ ++ ip_set_ip_t addr, *paddr = &addr; ++ unsigned char n, t, *a; ++ ++ addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); ++#ifdef __KERNEL__ ++ DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); ++#endif ++ n = cidr / 8; ++ t = cidr % 8; ++ a = &((unsigned char *)paddr)[n]; ++ *a = *a /(1 << (8 - t)) + shifts[t]; ++#ifdef __KERNEL__ ++ DP("n: %u, t: %u, a: %u", n, t, *a); ++ DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", ++ HIPQUAD(ip), cidr, NIPQUAD(addr)); ++#endif ++ ++ return ntohl(addr); ++} ++ ++#endif /* __IP_SET_NETHASH_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_portmap.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_portmap.h +@@ -0,0 +1,25 @@ ++#ifndef __IP_SET_PORTMAP_H ++#define __IP_SET_PORTMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "portmap" ++#define MAX_RANGE 0x0000FFFF ++#define INVALID_PORT (MAX_RANGE + 1) ++ ++struct ip_set_portmap { ++ void *members; /* the portmap proper */ ++ ip_set_ip_t first_port; /* host byte order, included in range */ ++ ip_set_ip_t last_port; /* host byte order, included in range */ ++}; ++ ++struct ip_set_req_portmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_portmap { ++ ip_set_ip_t port; ++}; ++ ++#endif /* __IP_SET_PORTMAP_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_set.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_set.h +@@ -0,0 +1,21 @@ ++#ifndef _IPT_SET_H ++#define _IPT_SET_H ++ ++#include ++ ++struct ipt_set_info { ++ ip_set_id_t index; ++ u_int32_t flags[IP_SET_MAX_BINDINGS + 1]; ++}; ++ ++/* match info */ ++struct ipt_set_info_match { ++ struct ipt_set_info match_set; ++}; ++ ++struct ipt_set_info_target { ++ struct ipt_set_info add_set; ++ struct ipt_set_info del_set; ++}; ++ ++#endif /*_IPT_SET_H*/ +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set.c +@@ -0,0 +1,2003 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module for IP set management */ ++ ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ASSERT_READ_LOCK(x) ++#define ASSERT_WRITE_LOCK(x) ++#include ++ ++static struct list_head set_type_list; /* all registered sets */ ++static struct ip_set **ip_set_list; /* all individual sets */ ++static DEFINE_RWLOCK(ip_set_lock); /* protects the lists and the hash */ ++static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */ ++static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX; ++static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE; ++static struct list_head *ip_set_hash; /* hash of bindings */ ++static unsigned int ip_set_hash_random; /* random seed */ ++ ++/* ++ * Sets are identified either by the index in ip_set_list or by id. ++ * The id never changes and is used to find a key in the hash. ++ * The index may change by swapping and used at all other places ++ * (set/SET netfilter modules, binding value, etc.) ++ * ++ * Userspace requests are serialized by ip_set_mutex and sets can ++ * be deleted only from userspace. Therefore ip_set_list locking ++ * must obey the following rules: ++ * ++ * - kernel requests: read and write locking mandatory ++ * - user requests: read locking optional, write locking mandatory ++ */ ++ ++static inline void ++__ip_set_get(ip_set_id_t index) ++{ ++ atomic_inc(&ip_set_list[index]->ref); ++} ++ ++static inline void ++__ip_set_put(ip_set_id_t index) ++{ ++ atomic_dec(&ip_set_list[index]->ref); ++} ++ ++/* ++ * Binding routines ++ */ ++ ++static inline struct ip_set_hash * ++__ip_set_find(u_int32_t key, ip_set_id_t id, ip_set_ip_t ip) ++{ ++ struct ip_set_hash *set_hash; ++ ++ list_for_each_entry(set_hash, &ip_set_hash[key], list) ++ if (set_hash->id == id && set_hash->ip == ip) ++ return set_hash; ++ ++ return NULL; ++} ++ ++static ip_set_id_t ++ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ ASSERT_READ_LOCK(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ ++ set_hash = __ip_set_find(key, id, ip); ++ ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID); ++} ++ ++static inline void ++__set_hash_del(struct ip_set_hash *set_hash) ++{ ++ ASSERT_WRITE_LOCK(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ ++ __ip_set_put(set_hash->binding); ++ list_del(&set_hash->list); ++ kfree(set_hash); ++} ++ ++static int ++ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ write_lock_bh(&ip_set_lock); ++ set_hash = __ip_set_find(key, id, ip); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ if (set_hash != NULL) ++ __set_hash_del(set_hash); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++} ++ ++static int ++ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ int ret = 0; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ IP_SET_ASSERT(ip_set_list[binding]); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ip_set_list[binding]->name); ++ write_lock_bh(&ip_set_lock); ++ set_hash = __ip_set_find(key, id, ip); ++ if (!set_hash) { ++ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_ATOMIC); ++ if (!set_hash) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ INIT_LIST_HEAD(&set_hash->list); ++ set_hash->id = id; ++ set_hash->ip = ip; ++ list_add(&set_hash->list, &ip_set_hash[key]); ++ } else { ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ DP("overwrite binding: %s", ++ ip_set_list[set_hash->binding]->name); ++ __ip_set_put(set_hash->binding); ++ } ++ set_hash->binding = binding; ++ __ip_set_get(set_hash->binding); ++ DP("stored: key %u, id %u (%s), ip %u.%u.%u.%u, binding %u (%s)", ++ key, id, ip_set_list[id]->name, ++ HIPQUAD(ip), binding, ip_set_list[binding]->name); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return ret; ++} ++ ++#define FOREACH_HASH_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash; \ ++ \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++#define FOREACH_HASH_RW_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash, *__n; \ ++ \ ++ ASSERT_WRITE_LOCK(&ip_set_lock); \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++/* Add, del and test set entries from kernel */ ++ ++#define follow_bindings(index, set, ip) \ ++((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \ ++ || (index = (set)->binding) != IP_SET_INVALID_ID) ++ ++int ++ip_set_testip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res; ++ unsigned char i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ read_lock_bh(&set->lock); ++ res = set->type->testip_kernel(set, skb, &ip, flags, i++); ++ read_unlock_bh(&set->lock); ++ i += !!(set->type->features & IPSET_DATA_DOUBLE); ++ } while (res > 0 ++ && flags[i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++ ++ return res; ++} ++ ++void ++ip_set_addip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res; ++ unsigned char i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ retry: ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->addip_kernel(set, skb, &ip, flags, i++); ++ write_unlock_bh(&set->lock); ++ i += !!(set->type->features & IPSET_DATA_DOUBLE); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++ ++ if (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0) ++ goto retry; ++} ++ ++void ++ip_set_delip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res; ++ unsigned char i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->delip_kernel(set, skb, &ip, flags, i++); ++ write_unlock_bh(&set->lock); ++ i += !!(set->type->features & IPSET_DATA_DOUBLE); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++} ++ ++/* Register and deregister settype */ ++ ++static inline struct ip_set_type * ++find_set_type(const char *name) ++{ ++ struct ip_set_type *set_type; ++ ++ list_for_each_entry(set_type, &set_type_list, list) ++ if (!strncmp(set_type->typename, name, IP_SET_MAXNAMELEN - 1)) ++ return set_type; ++ return NULL; ++} ++ ++int ++ip_set_register_set_type(struct ip_set_type *set_type) ++{ ++ int ret = 0; ++ ++ if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { ++ ip_set_printk("'%s' uses wrong protocol version %u (want %u)", ++ set_type->typename, ++ set_type->protocol_version, ++ IP_SET_PROTOCOL_VERSION); ++ return -EINVAL; ++ } ++ ++ write_lock_bh(&ip_set_lock); ++ if (find_set_type(set_type->typename)) { ++ /* Duplicate! */ ++ ip_set_printk("'%s' already registered!", ++ set_type->typename); ++ ret = -EINVAL; ++ goto unlock; ++ } ++ if (!try_module_get(THIS_MODULE)) { ++ ret = -EFAULT; ++ goto unlock; ++ } ++ list_add(&set_type->list, &set_type_list); ++ DP("'%s' registered.", set_type->typename); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return ret; ++} ++ ++void ++ip_set_unregister_set_type(struct ip_set_type *set_type) ++{ ++ write_lock_bh(&ip_set_lock); ++ if (!find_set_type(set_type->typename)) { ++ ip_set_printk("'%s' not registered?", ++ set_type->typename); ++ goto unlock; ++ } ++ list_del(&set_type->list); ++ module_put(THIS_MODULE); ++ DP("'%s' unregistered.", set_type->typename); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ ++} ++ ++/* ++ * Userspace routines ++ */ ++ ++/* ++ * Find set by name, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ down(&ip_set_app_mutex); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ __ip_set_get(i); ++ index = i; ++ break; ++ } ++ } ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * Find set by index, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byindex(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ ++ if (index >= ip_set_max) ++ return IP_SET_INVALID_ID; ++ ++ if (ip_set_list[index]) ++ __ip_set_get(index); ++ else ++ index = IP_SET_INVALID_ID; ++ ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * If the given set pointer points to a valid set, decrement ++ * reference count by 1. The caller shall not assume the index ++ * to be valid, after calling this function. ++ */ ++void ip_set_put(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ if (ip_set_list[index]) ++ __ip_set_put(index); ++ up(&ip_set_app_mutex); ++} ++ ++/* Find a set by name or index */ ++static ip_set_id_t ++ip_set_find_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ index = i; ++ break; ++ } ++ } ++ return index; ++} ++ ++static ip_set_id_t ++ip_set_find_byindex(ip_set_id_t index) ++{ ++ if (index >= ip_set_max || ip_set_list[index] == NULL) ++ index = IP_SET_INVALID_ID; ++ ++ return index; ++} ++ ++/* ++ * Add, del, test, bind and unbind ++ */ ++ ++static inline int ++__ip_set_testip(struct ip_set *set, ++ const void *data, ++ size_t size, ++ ip_set_ip_t *ip) ++{ ++ int res; ++ ++ read_lock_bh(&set->lock); ++ res = set->type->testip(set, data, size, ip); ++ read_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++__ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ do { ++ write_lock_bh(&set->lock); ++ res = set->type->addip(set, data, size, &ip); ++ write_unlock_bh(&set->lock); ++ } while (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0); ++ ++ return res; ++} ++ ++static int ++ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ ++ return __ip_set_addip(index, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt)); ++} ++ ++static int ++ip_set_delip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ write_lock_bh(&set->lock); ++ res = set->type->delip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ write_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++ip_set_testip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ ++ return (res > 0 ? -EEXIST : res); ++} ++ ++static int ++ip_set_bindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of a set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ write_lock_bh(&ip_set_lock); ++ /* Sets as binding values are referenced */ ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ set->binding = binding; ++ __ip_set_get(set->binding); ++ write_unlock_bh(&ip_set_lock); ++ ++ return 0; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = ip_set_hash_add(set->id, ip, binding); ++ ++ return res; ++} ++ ++#define FOREACH_SET_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __i; \ ++ struct ip_set *__set; \ ++ \ ++ for (__i = 0; __i < ip_set_max; __i++) { \ ++ __set = ip_set_list[__i]; \ ++ if (__set != NULL) \ ++ fn(__set , ##args); \ ++ } \ ++}) ++ ++static inline void ++__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id) ++{ ++ if (set_hash->id == id) ++ __set_hash_del(set_hash); ++} ++ ++static inline void ++__unbind_default(struct ip_set *set) ++{ ++ if (set->binding != IP_SET_INVALID_ID) { ++ /* Sets as binding values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ } ++} ++ ++static int ++ip_set_unbindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ struct ip_set_req_bind *req_bind; ++ ip_set_ip_t ip; ++ int res; ++ ++ DP(""); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ DP("%u %s", index, req_bind->binding); ++ if (index == IP_SET_INVALID_ID) { ++ /* unbind :all: */ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of sets */ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_SET_DO(__unbind_default); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings of all sets*/ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } ++ DP("unreachable reached!"); ++ return -EINVAL; ++ } ++ ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ ip_set_id_t binding = ip_set_find_byindex(set->binding); ++ ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ write_lock_bh(&ip_set_lock); ++ /* Sets in hash values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ write_unlock_bh(&ip_set_lock); ++ ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings */ ++ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ ++ DP("set %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ if (res >= 0) ++ res = ip_set_hash_del(set->id, ip); ++ ++ return res; ++} ++ ++static int ++ip_set_testbind(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = (set->binding == binding) ? -EEXIST : 0; ++ ++ return res; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = (ip_set_find_in_hash(set->id, ip) == binding) ++ ? -EEXIST : 0; ++ ++ return res; ++} ++ ++static struct ip_set_type * ++find_set_type_rlock(const char *typename) ++{ ++ struct ip_set_type *type; ++ ++ read_lock_bh(&ip_set_lock); ++ type = find_set_type(typename); ++ if (type == NULL) ++ read_unlock_bh(&ip_set_lock); ++ ++ return type; ++} ++ ++static int ++find_free_id(const char *name, ++ ip_set_id_t *index, ++ ip_set_id_t *id) ++{ ++ ip_set_id_t i; ++ ++ *id = IP_SET_INVALID_ID; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) { ++ if (*id == IP_SET_INVALID_ID) ++ *id = *index = i; ++ } else if (strcmp(name, ip_set_list[i]->name) == 0) ++ /* Name clash */ ++ return -EEXIST; ++ } ++ if (*id == IP_SET_INVALID_ID) ++ /* No free slot remained */ ++ return -ERANGE; ++ /* Check that index is usable as id (swapping) */ ++ check: ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && ip_set_list[i]->id == *id) { ++ *id = i; ++ goto check; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * Create a set ++ */ ++static int ++ip_set_create(const char *name, ++ const char *typename, ++ ip_set_id_t restore, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ ip_set_id_t index = 0, id; ++ int res = 0; ++ ++ DP("setname: %s, typename: %s, id: %u", name, typename, restore); ++ /* ++ * First, and without any locks, allocate and initialize ++ * a normal base set structure. ++ */ ++ set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); ++ if (!set) ++ return -ENOMEM; ++ set->lock = RW_LOCK_UNLOCKED; ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ set->binding = IP_SET_INVALID_ID; ++ atomic_set(&set->ref, 0); ++ ++ /* ++ * Next, take the &ip_set_lock, check that we know the type, ++ * and take a reference on the type, to make sure it ++ * stays available while constructing our new set. ++ * ++ * After referencing the type, we drop the &ip_set_lock, ++ * and let the new set construction run without locks. ++ */ ++ set->type = find_set_type_rlock(typename); ++ if (set->type == NULL) { ++ /* Try loading the module */ ++ char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1]; ++ strcpy(modulename, "ip_set_"); ++ strcat(modulename, typename); ++ DP("try to load %s", modulename); ++ request_module(modulename); ++ set->type = find_set_type_rlock(typename); ++ } ++ if (set->type == NULL) { ++ ip_set_printk("no set type '%s', set '%s' not created", ++ typename, name); ++ res = -ENOENT; ++ goto out; ++ } ++ if (!try_module_get(set->type->me)) { ++ read_unlock_bh(&ip_set_lock); ++ res = -EFAULT; ++ goto out; ++ } ++ read_unlock_bh(&ip_set_lock); ++ ++ /* ++ * Without holding any locks, create private part. ++ */ ++ res = set->type->create(set, data, size); ++ if (res != 0) ++ goto put_out; ++ ++ /* BTW, res==0 here. */ ++ ++ /* ++ * Here, we have a valid, constructed set. &ip_set_lock again, ++ * find free id/index and check that it is not already in ++ * ip_set_list. ++ */ ++ write_lock_bh(&ip_set_lock); ++ if ((res = find_free_id(set->name, &index, &id)) != 0) { ++ DP("no free id!"); ++ goto cleanup; ++ } ++ ++ /* Make sure restore gets the same index */ ++ if (restore != IP_SET_INVALID_ID && index != restore) { ++ DP("Can't restore, sets are screwed up"); ++ res = -ERANGE; ++ goto cleanup; ++ } ++ ++ /* ++ * Finally! Add our shiny new set to the list, and be done. ++ */ ++ DP("create: '%s' created with index %u, id %u!", set->name, index, id); ++ set->id = id; ++ ip_set_list[index] = set; ++ write_unlock_bh(&ip_set_lock); ++ return res; ++ ++ cleanup: ++ write_unlock_bh(&ip_set_lock); ++ set->type->destroy(set); ++ put_out: ++ module_put(set->type->me); ++ out: ++ kfree(set); ++ return res; ++} ++ ++/* ++ * Destroy a given existing set ++ */ ++static void ++ip_set_destroy_set(ip_set_id_t index) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ++ IP_SET_ASSERT(set); ++ DP("set: %s", set->name); ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ ip_set_list[index] = NULL; ++ write_unlock_bh(&ip_set_lock); ++ ++ /* Must call it without holding any lock */ ++ set->type->destroy(set); ++ module_put(set->type->me); ++ kfree(set); ++} ++ ++/* ++ * Destroy a set - or all sets ++ * Sets must not be referenced/used. ++ */ ++static int ++ip_set_destroy(ip_set_id_t index) ++{ ++ ip_set_id_t i; ++ ++ /* ref modification always protected by the mutex */ ++ if (index != IP_SET_INVALID_ID) { ++ if (atomic_read(&ip_set_list[index]->ref)) ++ return -EBUSY; ++ ip_set_destroy_set(index); ++ } else { ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && (atomic_read(&ip_set_list[i]->ref))) ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ ip_set_destroy_set(i); ++ } ++ } ++ return 0; ++} ++ ++static void ++ip_set_flush_set(struct ip_set *set) ++{ ++ DP("set: %s %u", set->name, set->id); ++ ++ write_lock_bh(&set->lock); ++ set->type->flush(set); ++ write_unlock_bh(&set->lock); ++} ++ ++/* ++ * Flush data in a set - or in all sets ++ */ ++static int ++ip_set_flush(ip_set_id_t index) ++{ ++ if (index != IP_SET_INVALID_ID) { ++ IP_SET_ASSERT(ip_set_list[index]); ++ ip_set_flush_set(ip_set_list[index]); ++ } else ++ FOREACH_SET_DO(ip_set_flush_set); ++ ++ return 0; ++} ++ ++/* Rename a set */ ++static int ++ip_set_rename(ip_set_id_t index, const char *name) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_id_t i; ++ int res = 0; ++ ++ DP("set: %s to %s", set->name, name); ++ write_lock_bh(&ip_set_lock); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strncmp(ip_set_list[i]->name, ++ name, ++ IP_SET_MAXNAMELEN - 1) == 0) { ++ res = -EEXIST; ++ goto unlock; ++ } ++ } ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return res; ++} ++ ++/* ++ * Swap two sets so that name/index points to the other. ++ * References are also swapped. ++ */ ++static int ++ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index) ++{ ++ struct ip_set *from = ip_set_list[from_index]; ++ struct ip_set *to = ip_set_list[to_index]; ++ char from_name[IP_SET_MAXNAMELEN]; ++ u_int32_t from_ref; ++ ++ DP("set: %s to %s", from->name, to->name); ++ /* Features must not change. Artifical restriction. */ ++ if (from->type->features != to->type->features) ++ return -ENOEXEC; ++ ++ /* No magic here: ref munging protected by the mutex */ ++ write_lock_bh(&ip_set_lock); ++ strncpy(from_name, from->name, IP_SET_MAXNAMELEN); ++ from_ref = atomic_read(&from->ref); ++ ++ strncpy(from->name, to->name, IP_SET_MAXNAMELEN); ++ atomic_set(&from->ref, atomic_read(&to->ref)); ++ strncpy(to->name, from_name, IP_SET_MAXNAMELEN); ++ atomic_set(&to->ref, from_ref); ++ ++ ip_set_list[from_index] = to; ++ ip_set_list[to_index] = from; ++ ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++} ++ ++/* ++ * List set data ++ */ ++ ++static inline void ++__set_hash_bindings_size_list(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_list); ++} ++ ++static inline void ++__set_hash_bindings_size_save(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_save); ++} ++ ++static inline void ++__set_hash_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, void *data, int *used) ++{ ++ if (set_hash->id == id) { ++ struct ip_set_hash_list *hash_list = ++ (struct ip_set_hash_list *)(data + *used); ++ ++ hash_list->ip = set_hash->ip; ++ hash_list->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_list); ++ } ++} ++ ++static int ip_set_list_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_list *set_list; ++ ++ /* Pointer to our header */ ++ set_list = (struct ip_set_list *) (data + *used); ++ ++ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_list) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_list); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_list->header_size = set->type->header_size; ++ if (*used + set_list->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_list->index = index; ++ set_list->binding = set->binding; ++ set_list->ref = atomic_read(&set->ref); ++ ++ /* Fill in set spefific header data */ ++ set->type->list_header(set, data + *used); ++ *used += set_list->header_size; ++ ++ /* Get and ensure set specific members size */ ++ set_list->members_size = set->type->list_members_size(set); ++ if (*used + set_list->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ set->type->list_members(set, data + *used); ++ *used += set_list->members_size; ++ read_unlock_bh(&set->lock); ++ ++ /* Bindings */ ++ ++ /* Get and ensure set specific bindings size */ ++ set_list->bindings_size = 0; ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ set->id, &set_list->bindings_size); ++ if (*used + set_list->bindings_size > len) ++ goto not_enough_mem; ++ ++ /* Fill in set spefific bindings data */ ++ FOREACH_HASH_DO(__set_hash_bindings, set->id, data, used); ++ ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++/* ++ * Save sets ++ */ ++static int ip_set_save_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set; ++ struct ip_set_save *set_save; ++ ++ /* Pointer to our header */ ++ set_save = (struct ip_set_save *) (data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_save); ++ ++ set = ip_set_list[index]; ++ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len, ++ data, data + *used); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_save->header_size = set->type->header_size; ++ if (*used + set_save->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_save->index = index; ++ set_save->binding = set->binding; ++ ++ /* Fill in set spefific header data */ ++ set->type->list_header(set, data + *used); ++ *used += set_save->header_size; ++ ++ DP("set header filled: %s, used: %u(%u) %p %p", set->name, *used, ++ set_save->header_size, data, data + *used); ++ /* Get and ensure set specific members size */ ++ set_save->members_size = set->type->list_members_size(set); ++ if (*used + set_save->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ set->type->list_members(set, data + *used); ++ *used += set_save->members_size; ++ read_unlock_bh(&set->lock); ++ DP("set members filled: %s, used: %u(%u) %p %p", set->name, *used, ++ set_save->members_size, data, data + *used); ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++static inline void ++__set_hash_save_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, ++ void *data, ++ int *used, ++ int len, ++ int *res) ++{ ++ if (*res == 0 ++ && (id == IP_SET_INVALID_ID || set_hash->id == id)) { ++ struct ip_set_hash_save *hash_save = ++ (struct ip_set_hash_save *)(data + *used); ++ /* Ensure bindings size */ ++ if (*used + sizeof(struct ip_set_hash_save) > len) { ++ *res = -ENOMEM; ++ return; ++ } ++ hash_save->id = set_hash->id; ++ hash_save->ip = set_hash->ip; ++ hash_save->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_save); ++ } ++} ++ ++static int ip_set_save_bindings(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ int res = 0; ++ struct ip_set_save *set_save; ++ ++ DP("used %u, len %u", *used, len); ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ return -ENOMEM; ++ ++ /* Marker */ ++ set_save = (struct ip_set_save *) (data + *used); ++ set_save->index = IP_SET_INVALID_ID; ++ set_save->header_size = 0; ++ set_save->members_size = 0; ++ *used += sizeof(struct ip_set_save); ++ ++ DP("marker added used %u, len %u", *used, len); ++ /* Fill in bindings data */ ++ if (index != IP_SET_INVALID_ID) ++ /* Sets are identified by id in hash */ ++ index = ip_set_list[index]->id; ++ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res); ++ ++ return res; ++} ++ ++/* ++ * Restore sets ++ */ ++static int ip_set_restore(void *data, ++ int len) ++{ ++ int res = 0; ++ int line = 0, used = 0, members_size; ++ struct ip_set *set; ++ struct ip_set_hash_save *hash_save; ++ struct ip_set_restore *set_restore; ++ ip_set_id_t index; ++ ++ /* Loop to restore sets */ ++ while (1) { ++ line++; ++ ++ DP("%u %u %u", used, sizeof(struct ip_set_restore), len); ++ /* Get and ensure header size */ ++ if (used + sizeof(struct ip_set_restore) > len) ++ return line; ++ set_restore = (struct ip_set_restore *) (data + used); ++ used += sizeof(struct ip_set_restore); ++ ++ /* Ensure data size */ ++ if (used ++ + set_restore->header_size ++ + set_restore->members_size > len) ++ return line; ++ ++ /* Check marker */ ++ if (set_restore->index == IP_SET_INVALID_ID) { ++ line--; ++ goto bindings; ++ } ++ ++ /* Try to create the set */ ++ DP("restore %s %s", set_restore->name, set_restore->typename); ++ res = ip_set_create(set_restore->name, ++ set_restore->typename, ++ set_restore->index, ++ data + used, ++ set_restore->header_size); ++ ++ if (res != 0) ++ return line; ++ used += set_restore->header_size; ++ ++ index = ip_set_find_byindex(set_restore->index); ++ DP("index %u, restore_index %u", index, set_restore->index); ++ if (index != set_restore->index) ++ return line; ++ /* Try to restore members data */ ++ set = ip_set_list[index]; ++ members_size = 0; ++ DP("members_size %u reqsize %u", ++ set_restore->members_size, set->type->reqsize); ++ while (members_size + set->type->reqsize <= ++ set_restore->members_size) { ++ line++; ++ DP("members: %u, line %u", members_size, line); ++ res = __ip_set_addip(index, ++ data + used + members_size, ++ set->type->reqsize); ++ if (!(res == 0 || res == -EEXIST)) ++ return line; ++ members_size += set->type->reqsize; ++ } ++ ++ DP("members_size %u %u", ++ set_restore->members_size, members_size); ++ if (members_size != set_restore->members_size) ++ return line++; ++ used += set_restore->members_size; ++ } ++ ++ bindings: ++ /* Loop to restore bindings */ ++ while (used < len) { ++ line++; ++ ++ DP("restore binding, line %u", line); ++ /* Get and ensure size */ ++ if (used + sizeof(struct ip_set_hash_save) > len) ++ return line; ++ hash_save = (struct ip_set_hash_save *) (data + used); ++ used += sizeof(struct ip_set_hash_save); ++ ++ /* hash_save->id is used to store the index */ ++ index = ip_set_find_byindex(hash_save->id); ++ DP("restore binding index %u, id %u, %u -> %u", ++ index, hash_save->id, hash_save->ip, hash_save->binding); ++ if (index != hash_save->id) ++ return line; ++ if (ip_set_find_byindex(hash_save->binding) == IP_SET_INVALID_ID) { ++ DP("corrupt binding set index %u", hash_save->binding); ++ return line; ++ } ++ set = ip_set_list[hash_save->id]; ++ /* Null valued IP means default binding */ ++ if (hash_save->ip) ++ res = ip_set_hash_add(set->id, ++ hash_save->ip, ++ hash_save->binding); ++ else { ++ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID); ++ write_lock_bh(&ip_set_lock); ++ set->binding = hash_save->binding; ++ __ip_set_get(set->binding); ++ write_unlock_bh(&ip_set_lock); ++ DP("default binding: %u", set->binding); ++ } ++ if (res != 0) ++ return line; ++ } ++ if (used != len) ++ return line; ++ ++ return 0; ++} ++ ++static int ++ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) ++{ ++ void *data; ++ int res = 0; /* Assume OK */ ++ unsigned *op; ++ struct ip_set_req_adt *req_adt; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ int (*adtfn)(ip_set_id_t index, ++ const void *data, size_t size); ++ struct fn_table { ++ int (*fn)(ip_set_id_t index, ++ const void *data, size_t size); ++ } adtfn_table[] = ++ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, ++ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind }, ++ }; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (len <= sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >%zu, got %u)", ++ sizeof(unsigned), len); ++ return -EINVAL; ++ } ++ data = vmalloc(len); ++ if (!data) { ++ DP("out of mem for %u bytes", len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *)data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_CREATE:{ ++ struct ip_set_req_create *req_create ++ = (struct ip_set_req_create *) data; ++ ++ if (len < sizeof(struct ip_set_req_create)) { ++ ip_set_printk("short CREATE data (want >=%zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ res = ip_set_create(req_create->name, ++ req_create->typename, ++ IP_SET_INVALID_ID, ++ data + sizeof(struct ip_set_req_create), ++ len - sizeof(struct ip_set_req_create)); ++ goto done; ++ } ++ case IP_SET_OP_DESTROY:{ ++ struct ip_set_req_std *req_destroy ++ = (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid DESTROY data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) { ++ /* Destroy all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_destroy->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ ++ res = ip_set_destroy(index); ++ goto done; ++ } ++ case IP_SET_OP_FLUSH:{ ++ struct ip_set_req_std *req_flush = ++ (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid FLUSH data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_flush->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = ip_set_flush(index); ++ goto done; ++ } ++ case IP_SET_OP_RENAME:{ ++ struct ip_set_req_create *req_rename ++ = (struct ip_set_req_create *) data; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid RENAME data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_rename->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_rename(index, req_rename->typename); ++ goto done; ++ } ++ case IP_SET_OP_SWAP:{ ++ struct ip_set_req_create *req_swap ++ = (struct ip_set_req_create *) data; ++ ip_set_id_t to_index; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid SWAP data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_swap->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ to_index = ip_set_find_byname(req_swap->typename); ++ if (to_index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_swap(index, to_index); ++ goto done; ++ } ++ default: ++ break; /* Set identified by id */ ++ } ++ ++ /* There we may have add/del/test/bind/unbind/test_bind operations */ ++ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) { ++ res = -EBADMSG; ++ goto done; ++ } ++ adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn; ++ ++ if (len < sizeof(struct ip_set_req_adt)) { ++ ip_set_printk("short data in adt request (want >=%zu, got %u)", ++ sizeof(struct ip_set_req_adt), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_adt = (struct ip_set_req_adt *) data; ++ ++ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */ ++ if (!(*op == IP_SET_OP_UNBIND_SET ++ && req_adt->index == IP_SET_INVALID_ID)) { ++ index = ip_set_find_byindex(req_adt->index); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = adtfn(index, data, len); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static int ++ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) ++{ ++ int res = 0; ++ unsigned *op; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ void *data; ++ int copylen = *len; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, *len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (*len < sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >=%zu, got %d)", ++ sizeof(unsigned), *len); ++ return -EINVAL; ++ } ++ data = vmalloc(*len); ++ if (!data) { ++ DP("out of mem for %d bytes", *len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, *len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *) data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_VERSION: { ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ ++ if (*len != sizeof(struct ip_set_req_version)) { ++ ip_set_printk("invalid VERSION (want %zu, got %d)", ++ sizeof(struct ip_set_req_version), ++ *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_version->version = IP_SET_PROTOCOL_VERSION; ++ res = copy_to_user(user, req_version, ++ sizeof(struct ip_set_req_version)); ++ goto done; ++ } ++ case IP_SET_OP_GET_BYNAME: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYNAME (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ req_get->set.index = index; ++ goto copy; ++ } ++ case IP_SET_OP_GET_BYINDEX: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYINDEX (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byindex(req_get->set.index); ++ strncpy(req_get->set.name, ++ index == IP_SET_INVALID_ID ? "" ++ : ip_set_list[index]->name, IP_SET_MAXNAMELEN); ++ goto copy; ++ } ++ case IP_SET_OP_ADT_GET: { ++ struct ip_set_req_adt_get *req_get ++ = (struct ip_set_req_adt_get *) data; ++ ++ if (*len != sizeof(struct ip_set_req_adt_get)) { ++ ip_set_printk("invalid ADT_GET (want %zu, got %d)", ++ sizeof(struct ip_set_req_adt_get), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ if (index != IP_SET_INVALID_ID) { ++ req_get->set.index = index; ++ strncpy(req_get->typename, ++ ip_set_list[index]->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ } else { ++ res = -ENOENT; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_MAX_SETS: { ++ struct ip_set_req_max_sets *req_max_sets ++ = (struct ip_set_req_max_sets *) data; ++ ip_set_id_t i; ++ ++ if (*len != sizeof(struct ip_set_req_max_sets)) { ++ ip_set_printk("invalid MAX_SETS (want %zu, got %d)", ++ sizeof(struct ip_set_req_max_sets), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) { ++ req_max_sets->set.index = IP_SET_INVALID_ID; ++ } else { ++ req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_max_sets->set.index = ++ ip_set_find_byname(req_max_sets->set.name); ++ if (req_max_sets->set.index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ req_max_sets->max_sets = ip_set_max; ++ req_max_sets->sets = 0; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ req_max_sets->sets++; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST_SIZE: ++ case IP_SET_OP_SAVE_SIZE: { ++ struct ip_set_req_setnames *req_setnames ++ = (struct ip_set_req_setnames *) data; ++ struct ip_set_name_list *name_list; ++ struct ip_set *set; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_setnames)) { ++ ip_set_printk("short LIST_SIZE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_setnames), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_setnames->size = 0; ++ used = sizeof(struct ip_set_req_setnames); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) ++ continue; ++ name_list = (struct ip_set_name_list *) ++ (data + used); ++ used += sizeof(struct ip_set_name_list); ++ if (used > copylen) { ++ res = -EAGAIN; ++ goto done; ++ } ++ set = ip_set_list[i]; ++ /* Fill in index, name, etc. */ ++ name_list->index = i; ++ name_list->id = set->id; ++ strncpy(name_list->name, ++ set->name, ++ IP_SET_MAXNAMELEN - 1); ++ strncpy(name_list->typename, ++ set->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ DP("filled %s of type %s, index %u\n", ++ name_list->name, name_list->typename, ++ name_list->index); ++ if (!(req_setnames->index == IP_SET_INVALID_ID ++ || req_setnames->index == i)) ++ continue; ++ /* Update size */ ++ switch (*op) { ++ case IP_SET_OP_LIST_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_list) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ /* Sets are identified by id in the hash */ ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ set->id, &req_setnames->size); ++ break; ++ } ++ case IP_SET_OP_SAVE_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_save) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ FOREACH_HASH_DO(__set_hash_bindings_size_save, ++ set->id, &req_setnames->size); ++ break; ++ } ++ default: ++ break; ++ } ++ } ++ if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST: { ++ struct ip_set_req_list *req_list ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short LIST (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_list->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* List all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_list_set(i, data, &used, *len); ++ } ++ } else { ++ /* List an individual set */ ++ res = ip_set_list_set(index, data, &used, *len); ++ } ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_SAVE: { ++ struct ip_set_req_list *req_save ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short SAVE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_save->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* Save all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_save_set(i, data, &used, *len); ++ } ++ } else { ++ /* Save an individual set */ ++ res = ip_set_save_set(index, data, &used, *len); ++ } ++ if (res == 0) ++ res = ip_set_save_bindings(index, data, &used, *len); ++ ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_RESTORE: { ++ struct ip_set_req_setnames *req_restore ++ = (struct ip_set_req_setnames *) data; ++ int line; ++ ++ if (*len < sizeof(struct ip_set_req_setnames) ++ || *len != req_restore->size) { ++ ip_set_printk("invalid RESTORE (want =%zu, got %d)", ++ req_restore->size, *len); ++ res = -EINVAL; ++ goto done; ++ } ++ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames), ++ req_restore->size - sizeof(struct ip_set_req_setnames)); ++ DP("ip_set_restore: %u", line); ++ if (line != 0) { ++ res = -EAGAIN; ++ req_restore->size = line; ++ copylen = sizeof(struct ip_set_req_setnames); ++ goto copy; ++ } ++ goto done; ++ } ++ default: ++ res = -EBADMSG; ++ goto done; ++ } /* end of switch(op) */ ++ ++ copy: ++ DP("set %s, copylen %u", index != IP_SET_INVALID_ID ++ && ip_set_list[index] ++ ? ip_set_list[index]->name ++ : ":all:", copylen); ++ res = copy_to_user(user, data, copylen); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static struct nf_sockopt_ops so_set = { ++ .pf = PF_INET, ++ .set_optmin = SO_IP_SET, ++ .set_optmax = SO_IP_SET + 1, ++ .set = &ip_set_sockfn_set, ++ .get_optmin = SO_IP_SET, ++ .get_optmax = SO_IP_SET + 1, ++ .get = &ip_set_sockfn_get, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ .owner = THIS_MODULE, ++#endif ++}; ++ ++static int max_sets, hash_size; ++module_param(max_sets, int, 0600); ++MODULE_PARM_DESC(max_sets, "maximal number of sets"); ++module_param(hash_size, int, 0600); ++MODULE_PARM_DESC(hash_size, "hash size for bindings"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("module implementing core IP set support"); ++ ++static int __init ip_set_init(void) ++{ ++ int res; ++ ip_set_id_t i; ++ ++ get_random_bytes(&ip_set_hash_random, 4); ++ if (max_sets) ++ ip_set_max = max_sets; ++ ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max); ++ if (!ip_set_list) { ++ printk(KERN_ERR "Unable to create ip_set_list\n"); ++ return -ENOMEM; ++ } ++ memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max); ++ if (hash_size) ++ ip_set_bindings_hash_size = hash_size; ++ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size); ++ if (!ip_set_hash) { ++ printk(KERN_ERR "Unable to create ip_set_hash\n"); ++ vfree(ip_set_list); ++ return -ENOMEM; ++ } ++ for (i = 0; i < ip_set_bindings_hash_size; i++) ++ INIT_LIST_HEAD(&ip_set_hash[i]); ++ ++ INIT_LIST_HEAD(&set_type_list); ++ ++ res = nf_register_sockopt(&so_set); ++ if (res != 0) { ++ ip_set_printk("SO_SET registry failed: %d", res); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ return res; ++ } ++ return 0; ++} ++ ++static void __exit ip_set_fini(void) ++{ ++ /* There can't be any existing set or binding */ ++ nf_unregister_sockopt(&so_set); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ DP("these are the famous last words"); ++} ++ ++EXPORT_SYMBOL(ip_set_register_set_type); ++EXPORT_SYMBOL(ip_set_unregister_set_type); ++ ++EXPORT_SYMBOL(ip_set_get_byname); ++EXPORT_SYMBOL(ip_set_get_byindex); ++EXPORT_SYMBOL(ip_set_put); ++ ++EXPORT_SYMBOL(ip_set_addip_kernel); ++EXPORT_SYMBOL(ip_set_delip_kernel); ++EXPORT_SYMBOL(ip_set_testip_kernel); ++ ++module_init(ip_set_init); ++module_exit(ip_set_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_iphash.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_iphash.c +@@ -0,0 +1,429 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an ip hash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++static inline __u32 ++jhash_ip(const struct ip_set_iphash *map, uint16_t i, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ __u32 id; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ *hash_ip = ip & map->netmask; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip), HIPQUAD(map->netmask)); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ if (*elem == *hash_ip) ++ return id; ++ /* No shortcut at testing - there can be deleted ++ * entries. */ ++ } ++ return UINT_MAX; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ if (!ip || map->elements >= limit) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); ++ if (*elem == *hash_ip) ++ return -EEXIST; ++ if (!*elem) { ++ *elem = *hash_ip; ++ map->elements++; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __addip((struct ip_set_iphash *) set->data, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t hash_ip, *elem; ++ void *members; ++ u_int32_t i, hashsize = map->hashsize; ++ int res; ++ struct ip_set_iphash *tmp; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new hash size */ ++ hashsize += (hashsize * map->resize)/100; ++ if (hashsize == map->hashsize) ++ hashsize++; ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, map->hashsize, hashsize); ++ ++ tmp = kmalloc(sizeof(struct ip_set_iphash) ++ + map->probes * sizeof(uint32_t), GFP_ATOMIC); ++ if (!tmp) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iphash) ++ + map->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); ++ if (!tmp->members) { ++ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); ++ kfree(tmp); ++ return -ENOMEM; ++ } ++ tmp->hashsize = hashsize; ++ tmp->elements = 0; ++ tmp->probes = map->probes; ++ tmp->resize = map->resize; ++ tmp->netmask = map->netmask; ++ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_iphash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ if (*elem) ++ res = __addip(tmp, *elem, &hash_ip); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ harray_free(tmp->members); ++ kfree(tmp); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ ++ map->hashsize = tmp->hashsize; ++ map->members = tmp->members; ++ write_unlock_bh(&set->lock); ++ ++ harray_free(members); ++ kfree(tmp); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t id, *elem; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ id = hash_id(set, ip, hash_ip); ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ *elem = 0; ++ map->elements--; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iphash_create *req = ++ (struct ip_set_req_iphash_create *) data; ++ struct ip_set_iphash *map; ++ uint16_t i; ++ ++ if (size != sizeof(struct ip_set_req_iphash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ if (req->probes < 1) { ++ ip_set_printk("probes too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iphash) ++ + req->probes * sizeof(uint32_t), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iphash) ++ + req->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ for (i = 0; i < req->probes; i++) ++ get_random_bytes(((uint32_t *) map->initval)+i, 4); ++ map->elements = 0; ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ map->netmask = req->netmask; ++ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ return -ENOMEM; ++ } ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ harray_free(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); ++ map->elements = 0; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ struct ip_set_req_iphash_create *header = ++ (struct ip_set_req_iphash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t i, *elem; ++ ++ for (i = 0; i < map->hashsize; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ ((ip_set_ip_t *)data)[i] = *elem; ++ } ++} ++ ++static struct ip_set_type ip_set_iphash = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iphash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iphash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iphash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_iphash_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_iphash); ++} ++ ++static void __exit ip_set_iphash_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iphash); ++} ++ ++module_init(ip_set_iphash_init); ++module_exit(ip_set_iphash_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipmap.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipmap.c +@@ -0,0 +1,336 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the single bitmap type */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static inline ip_set_ip_t ++ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) ++{ ++ return (ip - map->first_ip)/map->hosts; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return !!test_bit(ip_to_id(map, *hash_ip), map->members); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ int res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u", HIPQUAD(req->ip)); ++ return __addip(set, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __addip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_ipmap_create *req = ++ (struct ip_set_req_ipmap_create *) data; ++ struct ip_set_ipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_ipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipmap)); ++ return -ENOMEM; ++ } ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ map->netmask = req->netmask; ++ ++ if (req->netmask == 0xFFFFFFFF) { ++ map->hosts = 1; ++ map->sizeid = map->last_ip - map->first_ip + 1; ++ } else { ++ unsigned int mask_bits, netmask_bits; ++ ip_set_ip_t mask; ++ ++ map->first_ip &= map->netmask; /* Should we better bark? */ ++ ++ mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); ++ netmask_bits = mask_to_bits(map->netmask); ++ ++ if ((!mask && (map->first_ip || map->last_ip != 0xFFFFFFFF)) ++ || netmask_bits <= mask_bits) ++ return -ENOEXEC; ++ ++ DP("mask_bits %u, netmask_bits %u", ++ mask_bits, netmask_bits); ++ map->hosts = 2 << (32 - netmask_bits - 1); ++ map->sizeid = 2 << (netmask_bits - mask_bits - 1); ++ } ++ if (map->sizeid > MAX_RANGE + 1) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE+1); ++ kfree(map); ++ return -ENOEXEC; ++ } ++ DP("hosts %u, sizeid %u", map->hosts, map->sizeid); ++ newbytes = bitmap_bytes(0, map->sizeid - 1); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ struct ip_set_req_ipmap_create *header = ++ (struct ip_set_req_ipmap_create *) data; ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ return bitmap_bytes(0, map->sizeid - 1); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ int bytes = bitmap_bytes(0, map->sizeid - 1); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_ipmap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_ipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_ipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("ipmap type of IP sets"); ++ ++static int __init ip_set_ipmap_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_ipmap); ++} ++ ++static void __exit ip_set_ipmap_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_ipmap); ++} ++ ++module_init(ip_set_ipmap_init); ++module_exit(ip_set_ipmap_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipporthash.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipporthash.c +@@ -0,0 +1,581 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an ip+port hash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++/* We must handle non-linear skbs */ ++static inline ip_set_ip_t ++get_port(const struct sk_buff *skb, u_int32_t flags) ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ struct iphdr *iph = ip_hdr(skb); ++#else ++ struct iphdr *iph = skb->nh.iph; ++#endif ++ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; ++ ++ switch (iph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr tcph; ++ ++ /* See comments at tcp_match in ip_tables.c */ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ tcph.source : tcph.dest); ++ } ++ case IPPROTO_UDP: { ++ struct udphdr udph; ++ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ udph.source : udph.dest); ++ } ++ default: ++ return INVALID_PORT; ++ } ++} ++ ++static inline __u32 ++jhash_ip(const struct ip_set_ipporthash *map, uint16_t i, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); ++} ++ ++#define HASH_IP(map, ip, port) (port + ((ip - ((map)->first_ip)) << 16)) ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipporthash *map = ++ (struct ip_set_ipporthash *) set->data; ++ __u32 id; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ *hash_ip = HASH_IP(map, ip, port); ++ DP("set: %s, ipport:%u.%u.%u.%u:%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), port, HIPQUAD(*hash_ip)); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ if (*elem == *hash_ip) ++ return id; ++ /* No shortcut at testing - there can be deleted ++ * entries. */ ++ } ++ return UINT_MAX; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ return (hash_id(set, ip, port, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipporthash *req = ++ (struct ip_set_req_ipporthash *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, req->port, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port; ++ int res; ++ ++ if (flags[index+1] == 0) ++ return 0; ++ ++ port = get_port(skb, flags[index+1]); ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ DP("flag %s port %u", ++ flags[index+1] & IPSET_SRC ? "SRC" : "DST", ++ port); ++ if (port == INVALID_PORT) ++ return 0; ++ ++ res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ port, ++ hash_ip); ++ return (res < 0 ? 0 : res); ++ ++} ++ ++static inline int ++__add_haship(struct ip_set_ipporthash *map, ip_set_ip_t hash_ip) ++{ ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip(map, i, hash_ip) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); ++ if (*elem == hash_ip) ++ return -EEXIST; ++ if (!*elem) { ++ *elem = hash_ip; ++ map->elements++; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static inline int ++__addip(struct ip_set_ipporthash *map, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ if (map->elements > limit) ++ return -ERANGE; ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = HASH_IP(map, ip, port); ++ ++ return __add_haship(map, *hash_ip); ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipporthash *req = ++ (struct ip_set_req_ipporthash *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash), ++ size); ++ return -EINVAL; ++ } ++ return __addip((struct ip_set_ipporthash *) set->data, ++ req->ip, req->port, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port; ++ ++ if (flags[index+1] == 0) ++ return -EINVAL; ++ ++ port = get_port(skb, flags[index+1]); ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ DP("flag %s port %u", ++ flags[index+1] & IPSET_SRC ? "SRC" : "DST", ++ port); ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __addip((struct ip_set_ipporthash *) set->data, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ port, ++ hash_ip); ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ip_set_ip_t *elem; ++ void *members; ++ u_int32_t i, hashsize = map->hashsize; ++ int res; ++ struct ip_set_ipporthash *tmp; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new hash size */ ++ hashsize += (hashsize * map->resize)/100; ++ if (hashsize == map->hashsize) ++ hashsize++; ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, map->hashsize, hashsize); ++ ++ tmp = kmalloc(sizeof(struct ip_set_ipporthash) ++ + map->probes * sizeof(uint32_t), GFP_ATOMIC); ++ if (!tmp) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipporthash) ++ + map->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); ++ if (!tmp->members) { ++ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); ++ kfree(tmp); ++ return -ENOMEM; ++ } ++ tmp->hashsize = hashsize; ++ tmp->elements = 0; ++ tmp->probes = map->probes; ++ tmp->resize = map->resize; ++ tmp->first_ip = map->first_ip; ++ tmp->last_ip = map->last_ip; ++ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_ipporthash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ if (*elem) ++ res = __add_haship(tmp, *elem); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ harray_free(tmp->members); ++ kfree(tmp); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ ++ map->hashsize = tmp->hashsize; ++ map->members = tmp->members; ++ write_unlock_bh(&set->lock); ++ ++ harray_free(members); ++ kfree(tmp); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ip_set_ip_t id; ++ ip_set_ip_t *elem; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ id = hash_id(set, ip, port, hash_ip); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ *elem = 0; ++ map->elements--; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipporthash *req = ++ (struct ip_set_req_ipporthash *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, req->port, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port; ++ ++ if (flags[index+1] == 0) ++ return -EINVAL; ++ ++ port = get_port(skb, flags[index+1]); ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ DP("flag %s port %u", ++ flags[index+1] & IPSET_SRC ? "SRC" : "DST", ++ port); ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ port, ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_ipporthash_create *req = ++ (struct ip_set_req_ipporthash_create *) data; ++ struct ip_set_ipporthash *map; ++ uint16_t i; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ if (req->probes < 1) { ++ ip_set_printk("probes too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_ipporthash) ++ + req->probes * sizeof(uint32_t), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipporthash) ++ + req->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ for (i = 0; i < req->probes; i++) ++ get_random_bytes(((uint32_t *) map->initval)+i, 4); ++ map->elements = 0; ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ return -ENOMEM; ++ } ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ++ harray_free(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); ++ map->elements = 0; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ struct ip_set_req_ipporthash_create *header = ++ (struct ip_set_req_ipporthash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ip_set_ip_t i, *elem; ++ ++ for (i = 0; i < map->hashsize; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ ((ip_set_ip_t *)data)[i] = *elem; ++ } ++} ++ ++static struct ip_set_type ip_set_ipporthash = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_ipporthash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_ipporthash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("ipporthash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_ipporthash_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_ipporthash); ++} ++ ++static void __exit ip_set_ipporthash_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_ipporthash); ++} ++ ++module_init(ip_set_ipporthash_init); ++module_exit(ip_set_ipporthash_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptree.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptree.c +@@ -0,0 +1,612 @@ ++/* Copyright (C) 2005 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the iptree type */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Backward compatibility */ ++#ifndef __nocast ++#define __nocast ++#endif ++ ++#include ++ ++static int limit = MAX_RANGE; ++ ++/* Garbage collection interval in seconds: */ ++#define IPTREE_GC_TIME 5*60 ++/* Sleep so many milliseconds before trying again ++ * to delete the gc timer at destroying/flushing a set */ ++#define IPTREE_DESTROY_SLEEP 100 ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++static struct kmem_cache *branch_cachep; ++static struct kmem_cache *leaf_cachep; ++#else ++static kmem_cache_t *branch_cachep; ++static kmem_cache_t *leaf_cachep; ++#endif ++ ++#if defined(__LITTLE_ENDIAN) ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[3]; \ ++ b = ((unsigned char *)addrp)[2]; \ ++ c = ((unsigned char *)addrp)[1]; \ ++ d = ((unsigned char *)addrp)[0]; \ ++} while (0) ++#elif defined(__BIG_ENDIAN) ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[0]; \ ++ b = ((unsigned char *)addrp)[1]; \ ++ c = ((unsigned char *)addrp)[2]; \ ++ d = ((unsigned char *)addrp)[3]; \ ++} while (0) ++#else ++#error "Please fix asm/byteorder.h" ++#endif /* __LITTLE_ENDIAN */ ++ ++#define TESTIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return 0; \ ++} while (0) ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); ++ TESTIP_WALK(map, a, btree); ++ TESTIP_WALK(btree, b, ctree); ++ TESTIP_WALK(ctree, c, dtree); ++ DP("%lu %lu", dtree->expires[d], jiffies); ++ return dtree->expires[d] ++ && (!map->timeout ++ || time_after(dtree->expires[d], jiffies)); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ int res; ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ ++ res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++#define ADDIP_WALK(map, elem, branch, type, cachep) do { \ ++ if ((map)->tree[elem]) { \ ++ DP("found %u", elem); \ ++ branch = (map)->tree[elem]; \ ++ } else { \ ++ branch = (type *) \ ++ kmem_cache_alloc(cachep, GFP_ATOMIC); \ ++ if (branch == NULL) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[elem] = branch; \ ++ DP("alloc %u", elem); \ ++ } \ ++} while (0) ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ int ret = 0; ++ ++ if (!ip || map->elements >= limit) ++ /* We could call the garbage collector ++ * but it's probably overkill */ ++ return -ERANGE; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, timeout); ++ ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep); ++ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep); ++ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep); ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ ret = -EEXIST; ++ dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; ++ /* Lottery: I won! */ ++ if (dtree->expires[d] == 0) ++ dtree->expires[d] = 1; ++ DP("%u %lu", d, dtree->expires[d]); ++ if (ret == 0) ++ map->elements++; ++ return ret; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u %u", HIPQUAD(req->ip), req->timeout); ++ return __addip(set, req->ip, ++ req->timeout ? req->timeout : map->timeout, ++ hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ return __addip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ map->timeout, ++ hash_ip); ++} ++ ++#define DELIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return -EEXIST; \ ++} while (0) ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DELIP_WALK(map, a, btree); ++ DELIP_WALK(btree, b, ctree); ++ DELIP_WALK(ctree, c, dtree); ++ ++ if (dtree->expires[d]) { ++ dtree->expires[d] = 0; ++ map->elements--; ++ return 0; ++ } ++ return -EEXIST; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++#define LOOP_WALK_BEGIN(map, i, branch) \ ++ for (i = 0; i < 256; i++) { \ ++ if (!(map)->tree[i]) \ ++ continue; \ ++ branch = (map)->tree[i] ++ ++#define LOOP_WALK_END } ++ ++static void ip_tree_gc(unsigned long ul_set) ++{ ++ struct ip_set *set = (void *) ul_set; ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c,d; ++ unsigned char i,j,k; ++ ++ i = j = k = 0; ++ DP("gc: %s", set->name); ++ write_lock_bh(&set->lock); ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 256; d++) { ++ if (dtree->expires[d]) { ++ DP("gc: %u %u %u %u: expires %lu jiffies %lu", ++ a, b, c, d, ++ dtree->expires[d], jiffies); ++ if (map->timeout ++ && time_before(dtree->expires[d], jiffies)) { ++ dtree->expires[d] = 0; ++ map->elements--; ++ } else ++ k = 1; ++ } ++ } ++ if (k == 0) { ++ DP("gc: %s: leaf %u %u %u empty", ++ set->name, a, b, c); ++ kmem_cache_free(leaf_cachep, dtree); ++ ctree->tree[c] = NULL; ++ } else { ++ DP("gc: %s: leaf %u %u %u not empty", ++ set->name, a, b, c); ++ j = 1; ++ k = 0; ++ } ++ LOOP_WALK_END; ++ if (j == 0) { ++ DP("gc: %s: branch %u %u empty", ++ set->name, a, b); ++ kmem_cache_free(branch_cachep, ctree); ++ btree->tree[b] = NULL; ++ } else { ++ DP("gc: %s: branch %u %u not empty", ++ set->name, a, b); ++ i = 1; ++ j = k = 0; ++ } ++ LOOP_WALK_END; ++ if (i == 0) { ++ DP("gc: %s: branch %u empty", ++ set->name, a); ++ kmem_cache_free(branch_cachep, btree); ++ map->tree[a] = NULL; ++ } else { ++ DP("gc: %s: branch %u not empty", ++ set->name, a); ++ i = j = k = 0; ++ } ++ LOOP_WALK_END; ++ write_unlock_bh(&set->lock); ++ ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static inline void init_gc_timer(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ /* Even if there is no timeout for the entries, ++ * we still have to call gc because delete ++ * do not clean up empty branches */ ++ map->gc_interval = IPTREE_GC_TIME; ++ init_timer(&map->gc); ++ map->gc.data = (unsigned long) set; ++ map->gc.function = ip_tree_gc; ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iptree_create *req = ++ (struct ip_set_req_iptree_create *) data; ++ struct ip_set_iptree *map; ++ ++ if (size != sizeof(struct ip_set_req_iptree_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree_create), ++ size); ++ return -EINVAL; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iptree)); ++ return -ENOMEM; ++ } ++ memset(map, 0, sizeof(*map)); ++ map->timeout = req->timeout; ++ map->elements = 0; ++ set->data = map; ++ ++ init_gc_timer(set); ++ ++ return 0; ++} ++ ++static void __flush(struct ip_set_iptree *map) ++{ ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ kmem_cache_free(leaf_cachep, dtree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, ctree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, btree); ++ LOOP_WALK_END; ++ map->elements = 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ /* gc might be running */ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREE_DESTROY_SLEEP); ++ __flush(map); ++ kfree(map); ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ unsigned int timeout = map->timeout; ++ ++ /* gc might be running */ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREE_DESTROY_SLEEP); ++ __flush(map); ++ memset(map, 0, sizeof(*map)); ++ map->timeout = timeout; ++ ++ init_gc_timer(set); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree_create *header = ++ (struct ip_set_req_iptree_create *) data; ++ ++ header->timeout = map->timeout; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c,d; ++ unsigned int count = 0; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 256; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ count++; ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ ++ DP("members %u", count); ++ return (count * sizeof(struct ip_set_req_iptree)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c,d; ++ size_t offset = 0; ++ struct ip_set_req_iptree *entry; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 256; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) { ++ entry = (struct ip_set_req_iptree *)(data + offset); ++ entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); ++ entry->timeout = !map->timeout ? 0 ++ : (dtree->expires[d] - jiffies)/HZ; ++ offset += sizeof(struct ip_set_req_iptree); ++ } ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++} ++ ++static struct ip_set_type ip_set_iptree = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iptree), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iptree_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iptree type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_iptree_init(void) ++{ ++ int ret; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ branch_cachep = kmem_cache_create("ip_set_iptreeb", ++ sizeof(struct ip_set_iptreeb), ++ 0, 0, NULL); ++#else ++ branch_cachep = kmem_cache_create("ip_set_iptreeb", ++ sizeof(struct ip_set_iptreeb), ++ 0, 0, NULL, NULL); ++#endif ++ if (!branch_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ leaf_cachep = kmem_cache_create("ip_set_iptreed", ++ sizeof(struct ip_set_iptreed), ++ 0, 0, NULL); ++#else ++ leaf_cachep = kmem_cache_create("ip_set_iptreed", ++ sizeof(struct ip_set_iptreed), ++ 0, 0, NULL, NULL); ++#endif ++ if (!leaf_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); ++ ret = -ENOMEM; ++ goto free_branch; ++ } ++ ret = ip_set_register_set_type(&ip_set_iptree); ++ if (ret == 0) ++ goto out; ++ ++ kmem_cache_destroy(leaf_cachep); ++ free_branch: ++ kmem_cache_destroy(branch_cachep); ++ out: ++ return ret; ++} ++ ++static void __exit ip_set_iptree_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iptree); ++ kmem_cache_destroy(leaf_cachep); ++ kmem_cache_destroy(branch_cachep); ++} ++ ++module_init(ip_set_iptree_init); ++module_exit(ip_set_iptree_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptreemap.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptreemap.c +@@ -0,0 +1,829 @@ ++/* Copyright (C) 2007 Sven Wegener ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++/* This modules implements the iptreemap ipset type. It uses bitmaps to ++ * represent every single IPv4 address as a single bit. The bitmaps are managed ++ * in a tree structure, where the first three octets of an addresses are used ++ * as an index to find the bitmap and the last octet is used as the bit number. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define IPTREEMAP_DEFAULT_GC_TIME (5 * 60) ++#define IPTREEMAP_DESTROY_SLEEP (100) ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++static struct kmem_cache *cachep_b; ++static struct kmem_cache *cachep_c; ++static struct kmem_cache *cachep_d; ++#else ++static kmem_cache_t *cachep_b; ++static kmem_cache_t *cachep_c; ++static kmem_cache_t *cachep_d; ++#endif ++ ++static struct ip_set_iptreemap_d *fullbitmap_d; ++static struct ip_set_iptreemap_c *fullbitmap_c; ++static struct ip_set_iptreemap_b *fullbitmap_b; ++ ++#if defined(__LITTLE_ENDIAN) ++#define ABCD(a, b, c, d, addr) \ ++ do { \ ++ a = ((unsigned char *)addr)[3]; \ ++ b = ((unsigned char *)addr)[2]; \ ++ c = ((unsigned char *)addr)[1]; \ ++ d = ((unsigned char *)addr)[0]; \ ++ } while (0) ++#elif defined(__BIG_ENDIAN) ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[0]; \ ++ b = ((unsigned char *)addrp)[1]; \ ++ c = ((unsigned char *)addrp)[2]; \ ++ d = ((unsigned char *)addrp)[3]; \ ++} while (0) ++#else ++#error "Please fix asm/byteorder.h" ++#endif /* __LITTLE_ENDIAN */ ++ ++#define TESTIP_WALK(map, elem, branch, full) \ ++ do { \ ++ branch = (map)->tree[elem]; \ ++ if (!branch) \ ++ return 0; \ ++ else if (branch == full) \ ++ return 1; \ ++ } while (0) ++ ++#define ADDIP_WALK(map, elem, branch, type, cachep, full) \ ++ do { \ ++ branch = (map)->tree[elem]; \ ++ if (!branch) { \ ++ branch = (type *) kmem_cache_alloc(cachep, GFP_ATOMIC); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[elem] = branch; \ ++ } else if (branch == full) { \ ++ return -EEXIST; \ ++ } \ ++ } while (0) ++ ++#define ADDIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free) \ ++ for (a = a1; a <= a2; a++) { \ ++ branch = (map)->tree[a]; \ ++ if (branch != full) { \ ++ if ((a > a1 && a < a2) || (hint)) { \ ++ if (branch) \ ++ free(branch); \ ++ (map)->tree[a] = full; \ ++ continue; \ ++ } else if (!branch) { \ ++ branch = kmem_cache_alloc(cachep, GFP_ATOMIC); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[a] = branch; \ ++ } ++ ++#define ADDIP_RANGE_LOOP_END() \ ++ } \ ++ } ++ ++#define DELIP_WALK(map, elem, branch, cachep, full, flags) \ ++ do { \ ++ branch = (map)->tree[elem]; \ ++ if (!branch) { \ ++ return -EEXIST; \ ++ } else if (branch == full) { \ ++ branch = kmem_cache_alloc(cachep, flags); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memcpy(branch, full, sizeof(*full)); \ ++ (map)->tree[elem] = branch; \ ++ } \ ++ } while (0) ++ ++#define DELIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free, flags) \ ++ for (a = a1; a <= a2; a++) { \ ++ branch = (map)->tree[a]; \ ++ if (branch) { \ ++ if ((a > a1 && a < a2) || (hint)) { \ ++ if (branch != full) \ ++ free(branch); \ ++ (map)->tree[a] = NULL; \ ++ continue; \ ++ } else if (branch == full) { \ ++ branch = kmem_cache_alloc(cachep, flags); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memcpy(branch, full, sizeof(*branch)); \ ++ (map)->tree[a] = branch; \ ++ } ++ ++#define DELIP_RANGE_LOOP_END() \ ++ } \ ++ } ++ ++#define LOOP_WALK_BEGIN(map, i, branch) \ ++ for (i = 0; i < 256; i++) { \ ++ branch = (map)->tree[i]; \ ++ if (likely(!branch)) \ ++ continue; ++ ++#define LOOP_WALK_END() \ ++ } ++ ++#define LOOP_WALK_BEGIN_GC(map, i, branch, full, cachep, count) \ ++ count = -256; \ ++ for (i = 0; i < 256; i++) { \ ++ branch = (map)->tree[i]; \ ++ if (likely(!branch)) \ ++ continue; \ ++ count++; \ ++ if (branch == full) { \ ++ count++; \ ++ continue; \ ++ } ++ ++#define LOOP_WALK_END_GC(map, i, branch, full, cachep, count) \ ++ if (-256 == count) { \ ++ kmem_cache_free(cachep, branch); \ ++ (map)->tree[i] = NULL; \ ++ } else if (256 == count) { \ ++ kmem_cache_free(cachep, branch); \ ++ (map)->tree[i] = full; \ ++ } \ ++ } ++ ++#define LOOP_WALK_BEGIN_COUNT(map, i, branch, inrange, count) \ ++ for (i = 0; i < 256; i++) { \ ++ if (!(map)->tree[i]) { \ ++ if (inrange) { \ ++ count++; \ ++ inrange = 0; \ ++ } \ ++ continue; \ ++ } \ ++ branch = (map)->tree[i]; ++ ++#define LOOP_WALK_END_COUNT() \ ++ } ++ ++#define MIN(a, b) (a < b ? a : b) ++#define MAX(a, b) (a > b ? a : b) ++ ++#define GETVALUE1(a, a1, b1, r) \ ++ (a == a1 ? b1 : r) ++ ++#define GETVALUE2(a, b, a1, b1, c1, r) \ ++ (a == a1 && b == b1 ? c1 : r) ++ ++#define GETVALUE3(a, b, c, a1, b1, c1, d1, r) \ ++ (a == a1 && b == b1 && c == c1 ? d1 : r) ++ ++#define CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2) \ ++ ( \ ++ GETVALUE1(a, a1, b1, 0) == 0 \ ++ && GETVALUE1(a, a2, b2, 255) == 255 \ ++ && c1 == 0 \ ++ && c2 == 255 \ ++ && d1 == 0 \ ++ && d2 == 255 \ ++ ) ++ ++#define CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2) \ ++ ( \ ++ GETVALUE2(a, b, a1, b1, c1, 0) == 0 \ ++ && GETVALUE2(a, b, a2, b2, c2, 255) == 255 \ ++ && d1 == 0 \ ++ && d2 == 255 \ ++ ) ++ ++#define CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2) \ ++ ( \ ++ GETVALUE3(a, b, c, a1, b1, c1, d1, 0) == 0 \ ++ && GETVALUE3(a, b, c, a2, b2, c2, d2, 255) == 255 \ ++ ) ++ ++ ++static inline void ++free_d(struct ip_set_iptreemap_d *map) ++{ ++ kmem_cache_free(cachep_d, map); ++} ++ ++static inline void ++free_c(struct ip_set_iptreemap_c *map) ++{ ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int i; ++ ++ LOOP_WALK_BEGIN(map, i, dtree) { ++ if (dtree != fullbitmap_d) ++ free_d(dtree); ++ } LOOP_WALK_END(); ++ ++ kmem_cache_free(cachep_c, map); ++} ++ ++static inline void ++free_b(struct ip_set_iptreemap_b *map) ++{ ++ struct ip_set_iptreemap_c *ctree; ++ unsigned int i; ++ ++ LOOP_WALK_BEGIN(map, i, ctree) { ++ if (ctree != fullbitmap_c) ++ free_c(ctree); ++ } LOOP_WALK_END(); ++ ++ kmem_cache_free(cachep_b, map); ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned char a, b, c, d; ++ ++ *hash_ip = ip; ++ ++ ABCD(a, b, c, d, hash_ip); ++ ++ TESTIP_WALK(map, a, btree, fullbitmap_b); ++ TESTIP_WALK(btree, b, ctree, fullbitmap_c); ++ TESTIP_WALK(ctree, c, dtree, fullbitmap_d); ++ ++ return !!test_bit(d, (void *) dtree->bitmap); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); ++ return -EINVAL; ++ } ++ ++ return __testip(set, req->start, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) ++{ ++ int res; ++ ++ res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++ ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned char a, b, c, d; ++ ++ *hash_ip = ip; ++ ++ ABCD(a, b, c, d, hash_ip); ++ ++ ADDIP_WALK(map, a, btree, struct ip_set_iptreemap_b, cachep_b, fullbitmap_b); ++ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreemap_c, cachep_c, fullbitmap_c); ++ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreemap_d, cachep_d, fullbitmap_d); ++ ++ if (test_and_set_bit(d, (void *) dtree->bitmap)) ++ return -EEXIST; ++ ++ set_bit(b, (void *) btree->dirty); ++ ++ return 0; ++} ++ ++static inline int ++__addip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d; ++ unsigned char a1, b1, c1, d1; ++ unsigned char a2, b2, c2, d2; ++ ++ if (start == end) ++ return __addip_single(set, start, hash_ip); ++ ++ *hash_ip = start; ++ ++ ABCD(a1, b1, c1, d1, &start); ++ ABCD(a2, b2, c2, d2, &end); ++ ++ /* This is sooo ugly... */ ++ ADDIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b) { ++ ADDIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c) { ++ ADDIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d) { ++ for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) ++ set_bit(d, (void *) dtree->bitmap); ++ set_bit(b, (void *) btree->dirty); ++ } ADDIP_RANGE_LOOP_END(); ++ } ADDIP_RANGE_LOOP_END(); ++ } ADDIP_RANGE_LOOP_END(); ++ ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); ++ return -EINVAL; ++ } ++ ++ return __addip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) ++{ ++ ++ return __addip_single(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__delip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip, unsigned int __nocast flags) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned char a,b,c,d; ++ ++ *hash_ip = ip; ++ ++ ABCD(a, b, c, d, hash_ip); ++ ++ DELIP_WALK(map, a, btree, cachep_b, fullbitmap_b, flags); ++ DELIP_WALK(btree, b, ctree, cachep_c, fullbitmap_c, flags); ++ DELIP_WALK(ctree, c, dtree, cachep_d, fullbitmap_d, flags); ++ ++ if (!test_and_clear_bit(d, (void *) dtree->bitmap)) ++ return -EEXIST; ++ ++ set_bit(b, (void *) btree->dirty); ++ ++ return 0; ++} ++ ++static inline int ++__delip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip, unsigned int __nocast flags) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d; ++ unsigned char a1, b1, c1, d1; ++ unsigned char a2, b2, c2, d2; ++ ++ if (start == end) ++ return __delip_single(set, start, hash_ip, flags); ++ ++ *hash_ip = start; ++ ++ ABCD(a1, b1, c1, d1, &start); ++ ABCD(a2, b2, c2, d2, &end); ++ ++ /* This is sooo ugly... */ ++ DELIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b, flags) { ++ DELIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c, flags) { ++ DELIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d, flags) { ++ for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) ++ clear_bit(d, (void *) dtree->bitmap); ++ set_bit(b, (void *) btree->dirty); ++ } DELIP_RANGE_LOOP_END(); ++ } DELIP_RANGE_LOOP_END(); ++ } DELIP_RANGE_LOOP_END(); ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); ++ return -EINVAL; ++ } ++ ++ return __delip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip, GFP_KERNEL); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) ++{ ++ return __delip_single(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip, ++ GFP_ATOMIC); ++} ++ ++/* Check the status of the bitmap ++ * -1 == all bits cleared ++ * 1 == all bits set ++ * 0 == anything else ++ */ ++static inline int ++bitmap_status(struct ip_set_iptreemap_d *dtree) ++{ ++ unsigned char first = dtree->bitmap[0]; ++ int a; ++ ++ for (a = 1; a < 32; a++) ++ if (dtree->bitmap[a] != first) ++ return 0; ++ ++ return (first == 0 ? -1 : (first == 255 ? 1 : 0)); ++} ++ ++static void ++gc(unsigned long addr) ++{ ++ struct ip_set *set = (struct ip_set *) addr; ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c; ++ int i, j, k; ++ ++ write_lock_bh(&set->lock); ++ ++ LOOP_WALK_BEGIN_GC(map, a, btree, fullbitmap_b, cachep_b, i) { ++ LOOP_WALK_BEGIN_GC(btree, b, ctree, fullbitmap_c, cachep_c, j) { ++ if (!test_and_clear_bit(b, (void *) btree->dirty)) ++ continue; ++ LOOP_WALK_BEGIN_GC(ctree, c, dtree, fullbitmap_d, cachep_d, k) { ++ switch (bitmap_status(dtree)) { ++ case -1: ++ kmem_cache_free(cachep_d, dtree); ++ ctree->tree[c] = NULL; ++ k--; ++ break; ++ case 1: ++ kmem_cache_free(cachep_d, dtree); ++ ctree->tree[c] = fullbitmap_d; ++ k++; ++ break; ++ } ++ } LOOP_WALK_END(); ++ } LOOP_WALK_END_GC(btree, b, ctree, fullbitmap_c, cachep_c, k); ++ } LOOP_WALK_END_GC(map, a, btree, fullbitmap_b, cachep_b, j); ++ ++ write_unlock_bh(&set->lock); ++ ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static inline void ++init_gc_timer(struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ ++ init_timer(&map->gc); ++ map->gc.data = (unsigned long) set; ++ map->gc.function = gc; ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iptreemap_create *req = (struct ip_set_req_iptreemap_create *) data; ++ struct ip_set_iptreemap *map; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap_create), size); ++ return -EINVAL; ++ } ++ ++ map = kzalloc(sizeof(*map), GFP_KERNEL); ++ if (!map) ++ return -ENOMEM; ++ ++ map->gc_interval = req->gc_interval ? req->gc_interval : IPTREEMAP_DEFAULT_GC_TIME; ++ set->data = map; ++ ++ init_gc_timer(set); ++ ++ return 0; ++} ++ ++static inline void __flush(struct ip_set_iptreemap *map) ++{ ++ struct ip_set_iptreemap_b *btree; ++ unsigned int a; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ if (btree != fullbitmap_b) ++ free_b(btree); ++ LOOP_WALK_END(); ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREEMAP_DESTROY_SLEEP); ++ ++ __flush(map); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREEMAP_DESTROY_SLEEP); ++ ++ __flush(map); ++ ++ memset(map, 0, sizeof(*map)); ++ ++ init_gc_timer(set); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_req_iptreemap_create *header = (struct ip_set_req_iptreemap_create *) data; ++ ++ header->gc_interval = map->gc_interval; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d, inrange = 0, count = 0; ++ ++ LOOP_WALK_BEGIN_COUNT(map, a, btree, inrange, count) { ++ LOOP_WALK_BEGIN_COUNT(btree, b, ctree, inrange, count) { ++ LOOP_WALK_BEGIN_COUNT(ctree, c, dtree, inrange, count) { ++ for (d = 0; d < 256; d++) { ++ if (test_bit(d, (void *) dtree->bitmap)) { ++ inrange = 1; ++ } else if (inrange) { ++ count++; ++ inrange = 0; ++ } ++ } ++ } LOOP_WALK_END_COUNT(); ++ } LOOP_WALK_END_COUNT(); ++ } LOOP_WALK_END_COUNT(); ++ ++ if (inrange) ++ count++; ++ ++ return (count * sizeof(struct ip_set_req_iptreemap)); ++} ++ ++static inline size_t add_member(void *data, size_t offset, ip_set_ip_t start, ip_set_ip_t end) ++{ ++ struct ip_set_req_iptreemap *entry = (struct ip_set_req_iptreemap *) (data + offset); ++ ++ entry->start = start; ++ entry->end = end; ++ ++ return sizeof(*entry); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d, inrange = 0; ++ size_t offset = 0; ++ ip_set_ip_t start = 0, end = 0, ip; ++ ++ LOOP_WALK_BEGIN(map, a, btree) { ++ LOOP_WALK_BEGIN(btree, b, ctree) { ++ LOOP_WALK_BEGIN(ctree, c, dtree) { ++ for (d = 0; d < 256; d++) { ++ if (test_bit(d, (void *) dtree->bitmap)) { ++ ip = ((a << 24) | (b << 16) | (c << 8) | d); ++ if (!inrange) { ++ inrange = 1; ++ start = ip; ++ } else if (end < ip - 1) { ++ offset += add_member(data, offset, start, end); ++ start = ip; ++ } ++ end = ip; ++ } else if (inrange) { ++ offset += add_member(data, offset, start, end); ++ inrange = 0; ++ } ++ } ++ } LOOP_WALK_END(); ++ } LOOP_WALK_END(); ++ } LOOP_WALK_END(); ++ ++ if (inrange) ++ add_member(data, offset, start, end); ++} ++ ++static struct ip_set_type ip_set_iptreemap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = create, ++ .destroy = destroy, ++ .flush = flush, ++ .reqsize = sizeof(struct ip_set_req_iptreemap), ++ .addip = addip, ++ .addip_kernel = addip_kernel, ++ .delip = delip, ++ .delip_kernel = delip_kernel, ++ .testip = testip, ++ .testip_kernel = testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iptreemap_create), ++ .list_header = list_header, ++ .list_members_size = list_members_size, ++ .list_members = list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Sven Wegener "); ++MODULE_DESCRIPTION("iptreemap type of IP sets"); ++ ++static int __init ip_set_iptreemap_init(void) ++{ ++ int ret = -ENOMEM; ++ int a; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ cachep_b = kmem_cache_create("ip_set_iptreemap_b", ++ sizeof(struct ip_set_iptreemap_b), ++ 0, 0, NULL); ++#else ++ cachep_b = kmem_cache_create("ip_set_iptreemap_b", ++ sizeof(struct ip_set_iptreemap_b), ++ 0, 0, NULL, NULL); ++#endif ++ if (!cachep_b) { ++ ip_set_printk("Unable to create ip_set_iptreemap_b slab cache"); ++ goto out; ++ } ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ cachep_c = kmem_cache_create("ip_set_iptreemap_c", ++ sizeof(struct ip_set_iptreemap_c), ++ 0, 0, NULL); ++#else ++ cachep_c = kmem_cache_create("ip_set_iptreemap_c", ++ sizeof(struct ip_set_iptreemap_c), ++ 0, 0, NULL, NULL); ++#endif ++ if (!cachep_c) { ++ ip_set_printk("Unable to create ip_set_iptreemap_c slab cache"); ++ goto outb; ++ } ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ cachep_d = kmem_cache_create("ip_set_iptreemap_d", ++ sizeof(struct ip_set_iptreemap_d), ++ 0, 0, NULL); ++#else ++ cachep_d = kmem_cache_create("ip_set_iptreemap_d", ++ sizeof(struct ip_set_iptreemap_d), ++ 0, 0, NULL, NULL); ++#endif ++ if (!cachep_d) { ++ ip_set_printk("Unable to create ip_set_iptreemap_d slab cache"); ++ goto outc; ++ } ++ ++ fullbitmap_d = kmem_cache_alloc(cachep_d, GFP_KERNEL); ++ if (!fullbitmap_d) ++ goto outd; ++ ++ fullbitmap_c = kmem_cache_alloc(cachep_c, GFP_KERNEL); ++ if (!fullbitmap_c) ++ goto outbitmapd; ++ ++ fullbitmap_b = kmem_cache_alloc(cachep_b, GFP_KERNEL); ++ if (!fullbitmap_b) ++ goto outbitmapc; ++ ++ ret = ip_set_register_set_type(&ip_set_iptreemap); ++ if (0 > ret) ++ goto outbitmapb; ++ ++ /* Now init our global bitmaps */ ++ memset(fullbitmap_d->bitmap, 0xff, sizeof(fullbitmap_d->bitmap)); ++ ++ for (a = 0; a < 256; a++) ++ fullbitmap_c->tree[a] = fullbitmap_d; ++ ++ for (a = 0; a < 256; a++) ++ fullbitmap_b->tree[a] = fullbitmap_c; ++ memset(fullbitmap_b->dirty, 0, sizeof(fullbitmap_b->dirty)); ++ ++ return 0; ++ ++outbitmapb: ++ kmem_cache_free(cachep_b, fullbitmap_b); ++outbitmapc: ++ kmem_cache_free(cachep_c, fullbitmap_c); ++outbitmapd: ++ kmem_cache_free(cachep_d, fullbitmap_d); ++outd: ++ kmem_cache_destroy(cachep_d); ++outc: ++ kmem_cache_destroy(cachep_c); ++outb: ++ kmem_cache_destroy(cachep_b); ++out: ++ ++ return ret; ++} ++ ++static void __exit ip_set_iptreemap_fini(void) ++{ ++ ip_set_unregister_set_type(&ip_set_iptreemap); ++ kmem_cache_free(cachep_d, fullbitmap_d); ++ kmem_cache_free(cachep_c, fullbitmap_c); ++ kmem_cache_free(cachep_b, fullbitmap_b); ++ kmem_cache_destroy(cachep_d); ++ kmem_cache_destroy(cachep_c); ++ kmem_cache_destroy(cachep_b); ++} ++ ++module_init(ip_set_iptreemap_init); ++module_exit(ip_set_iptreemap_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_macipmap.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_macipmap.c +@@ -0,0 +1,375 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the macipmap type */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = (struct ip_set_macip *) map->members; ++ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->ip < map->first_ip || req->ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = req->ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, ++ (void *) &table[req->ip - map->first_ip].flags)) { ++ return (memcmp(req->ethernet, ++ &table[req->ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return 0; ++ ++ *hash_ip = ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, ++ (void *) &table[ip - map->first_ip].flags)) { ++ /* Is mac pointer valid? ++ * If so, compare... */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ return (skb_mac_header(skb) >= skb->head ++ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data ++#else ++ return (skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data ++#endif ++ && (memcmp(eth_hdr(skb)->h_source, ++ &table[ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0)); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++/* returns 0 on success */ ++static inline int ++__addip(struct ip_set *set, ++ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (test_and_set_bit(IPSET_MACIP_ISSET, ++ (void *) &table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __addip(set, req->ip, req->ethernet, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (!(skb_mac_header(skb) >= skb->head ++ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data)) ++#else ++ if (!(skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data)) ++#endif ++ return -EINVAL; ++ ++ return __addip(set, ip, eth_hdr(skb)->h_source, hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (!test_and_clear_bit(IPSET_MACIP_ISSET, ++ (void *)&table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) ++{ ++ return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_macipmap_create *req = ++ (struct ip_set_req_macipmap_create *) data; ++ struct ip_set_macipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_macipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE+1); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_macipmap)); ++ return -ENOMEM; ++ } ++ map->flags = req->flags; ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ newbytes = members_size(map->first_ip, map->last_ip); ++ map->members = ip_set_malloc(newbytes); ++ DP("members: %u %p", newbytes, map->members); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ memset(map->members, 0, members_size(map->first_ip, map->last_ip)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_req_macipmap_create *header = ++ (struct ip_set_req_macipmap_create *) data; ++ ++ DP("list_header %x %x %u", map->first_ip, map->last_ip, ++ map->flags); ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->flags = map->flags; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ DP("%u", members_size(map->first_ip, map->last_ip)); ++ return members_size(map->first_ip, map->last_ip); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ int bytes = members_size(map->first_ip, map->last_ip); ++ ++ DP("members: %u %p", bytes, map->members); ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_macipmap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_macipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_macipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("macipmap type of IP sets"); ++ ++static int __init ip_set_macipmap_init(void) ++{ ++ init_max_malloc_size(); ++ return ip_set_register_set_type(&ip_set_macipmap); ++} ++ ++static void __exit ip_set_macipmap_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_macipmap); ++} ++ ++module_init(ip_set_macipmap_init); ++module_exit(ip_set_macipmap_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_nethash.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_nethash.c +@@ -0,0 +1,497 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing a cidr nethash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++static inline __u32 ++jhash_ip(const struct ip_set_nethash *map, uint16_t i, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); ++} ++ ++static inline __u32 ++hash_id_cidr(struct ip_set_nethash *map, ++ ip_set_ip_t ip, ++ unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ __u32 id; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ *hash_ip = pack(ip, cidr); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ if (*elem == *hash_ip) ++ return id; ++ } ++ return UINT_MAX; ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ __u32 id = UINT_MAX; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ id = hash_id_cidr(map, ip, map->cidr[i], hash_ip); ++ if (id != UINT_MAX) ++ break; ++ } ++ return id; ++} ++ ++static inline int ++__testip_cidr(struct ip_set *set, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (ip && hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX); ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ return (req->cidr == 32 ? __testip(set, req->ip, hash_ip) ++ : __testip_cidr(set, req->ip, req->cidr, hash_ip)); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip(map, i, ip) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); ++ if (*elem == ip) ++ return -EEXIST; ++ if (!*elem) { ++ *elem = ip; ++ map->elements++; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static inline int ++__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ if (!ip || map->elements >= limit) ++ return -ERANGE; ++ ++ *hash_ip = pack(ip, cidr); ++ DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip)); ++ ++ return __addip_base(map, *hash_ip); ++} ++ ++static void ++update_cidr_sizes(struct ip_set_nethash *map, unsigned char cidr) ++{ ++ unsigned char next; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ if (map->cidr[i] == cidr) { ++ return; ++ } else if (map->cidr[i] < cidr) { ++ next = map->cidr[i]; ++ map->cidr[i] = cidr; ++ cidr = next; ++ } ++ } ++ if (i < 30) ++ map->cidr[i] = cidr; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ int ret; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ ret = __addip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++ ++ if (ret == 0) ++ update_cidr_sizes((struct ip_set_nethash *) set->data, ++ req->cidr); ++ ++ return ret; ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++ if (map->cidr[0]) ++ ret = __addip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ip_set_ip_t *elem; ++ void *members; ++ u_int32_t i, hashsize = map->hashsize; ++ int res; ++ struct ip_set_nethash *tmp; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new parameters */ ++ hashsize += (hashsize * map->resize)/100; ++ if (hashsize == map->hashsize) ++ hashsize++; ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, map->hashsize, hashsize); ++ ++ tmp = kmalloc(sizeof(struct ip_set_nethash) ++ + map->probes * sizeof(uint32_t), GFP_ATOMIC); ++ if (!tmp) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_nethash) ++ + map->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); ++ if (!tmp->members) { ++ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); ++ kfree(tmp); ++ return -ENOMEM; ++ } ++ tmp->hashsize = hashsize; ++ tmp->elements = 0; ++ tmp->probes = map->probes; ++ tmp->resize = map->resize; ++ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); ++ memcpy(tmp->cidr, map->cidr, 30 * sizeof(unsigned char)); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_nethash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ if (*elem) ++ res = __addip_base(tmp, *elem); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ harray_free(tmp->members); ++ kfree(tmp); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ ++ map->hashsize = tmp->hashsize; ++ map->members = tmp->members; ++ write_unlock_bh(&set->lock); ++ ++ harray_free(members); ++ kfree(tmp); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ ip_set_ip_t id, *elem; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ id = hash_id_cidr(map, ip, cidr, hash_ip); ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ *elem = 0; ++ map->elements--; ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ /* TODO: no garbage collection in map->cidr */ ++ return __delip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++ if (map->cidr[0]) ++ ret = __delip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_nethash_create *req = ++ (struct ip_set_req_nethash_create *) data; ++ struct ip_set_nethash *map; ++ uint16_t i; ++ ++ if (size != sizeof(struct ip_set_req_nethash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ if (req->probes < 1) { ++ ip_set_printk("probes too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_nethash) ++ + req->probes * sizeof(uint32_t), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_nethash) ++ + req->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ for (i = 0; i < req->probes; i++) ++ get_random_bytes(((uint32_t *) map->initval)+i, 4); ++ map->elements = 0; ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ return -ENOMEM; ++ } ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ harray_free(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++ map->elements = 0; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ struct ip_set_req_nethash_create *header = ++ (struct ip_set_req_nethash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ip_set_ip_t i, *elem; ++ ++ for (i = 0; i < map->hashsize; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ ((ip_set_ip_t *)data)[i] = *elem; ++ } ++} ++ ++static struct ip_set_type ip_set_nethash = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_nethash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_nethash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("nethash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_nethash_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_nethash); ++} ++ ++static void __exit ip_set_nethash_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_nethash); ++} ++ ++module_init(ip_set_nethash_init); ++module_exit(ip_set_nethash_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_portmap.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_portmap.c +@@ -0,0 +1,346 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing a port set type as a bitmap */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++/* We must handle non-linear skbs */ ++static inline ip_set_ip_t ++get_port(const struct sk_buff *skb, u_int32_t flags) ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ struct iphdr *iph = ip_hdr(skb); ++#else ++ struct iphdr *iph = skb->nh.iph; ++#endif ++ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; ++ switch (iph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr tcph; ++ ++ /* See comments at tcp_match in ip_tables.c */ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ tcph.source : tcph.dest); ++ } ++ case IPPROTO_UDP: { ++ struct udphdr udph; ++ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ udph.source : udph.dest); ++ } ++ default: ++ return INVALID_PORT; ++ } ++} ++ ++static inline int ++__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ ++ *hash_port = port; ++ DP("set: %s, port:%u, %u", set->name, port, *hash_port); ++ return !!test_bit(port - map->first_port, map->members); ++} ++ ++static int ++testport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __testport(set, req->port, hash_port); ++} ++ ++static int ++testport_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_port, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ int res; ++ ip_set_ip_t port = get_port(skb, flags[index]); ++ ++ DP("flag %s port %u", flags[index] & IPSET_SRC ? "SRC" : "DST", port); ++ if (port == INVALID_PORT) ++ return 0; ++ ++ res = __testport(set, port, hash_port); ++ ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (test_and_set_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++addport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __addport(set, req->port, hash_port); ++} ++ ++static int ++addport_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_port, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port = get_port(skb, flags[index]); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __addport(set, port, hash_port); ++} ++ ++static inline int ++__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (!test_and_clear_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++delport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __delport(set, req->port, hash_port); ++} ++ ++static int ++delport_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_port, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port = get_port(skb, flags[index]); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __delport(set, port, hash_port); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_portmap_create *req = ++ (struct ip_set_req_portmap_create *) data; ++ struct ip_set_portmap *map; ++ ++ if (size != sizeof(struct ip_set_req_portmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u to %u", req->from, req->to); ++ ++ if (req->from > req->to) { ++ DP("bad port range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d ports)", ++ MAX_RANGE+1); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_portmap)); ++ return -ENOMEM; ++ } ++ map->first_port = req->from; ++ map->last_port = req->to; ++ newbytes = bitmap_bytes(req->from, req->to); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ struct ip_set_req_portmap_create *header = ++ (struct ip_set_req_portmap_create *) data; ++ ++ DP("list_header %u %u", map->first_port, map->last_port); ++ ++ header->from = map->first_port; ++ header->to = map->last_port; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ return bitmap_bytes(map->first_port, map->last_port); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ int bytes = bitmap_bytes(map->first_port, map->last_port); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_portmap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_PORT | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_portmap), ++ .addip = &addport, ++ .addip_kernel = &addport_kernel, ++ .delip = &delport, ++ .delip_kernel = &delport_kernel, ++ .testip = &testport, ++ .testip_kernel = &testport_kernel, ++ .header_size = sizeof(struct ip_set_req_portmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("portmap type of IP sets"); ++ ++static int __init ip_set_portmap_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_portmap); ++} ++ ++static void __exit ip_set_portmap_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_portmap); ++} ++ ++module_init(ip_set_portmap_init); ++module_exit(ip_set_portmap_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_set.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ipt_set.c +@@ -0,0 +1,160 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module to match an IP set. */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static inline int ++match_set(const struct ipt_set_info *info, ++ const struct sk_buff *skb, ++ int inv) ++{ ++ if (ip_set_testip_kernel(info->index, skb, info->flags)) ++ inv = !inv; ++ return inv; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++static bool ++#else ++static int ++#endif ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_match *match, ++#endif ++ const void *matchinfo, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ int offset, unsigned int protoff, bool *hotdrop) ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ int offset, unsigned int protoff, int *hotdrop) ++#else ++ int offset, int *hotdrop) ++#endif ++{ ++ const struct ipt_set_info_match *info = matchinfo; ++ ++ return match_set(&info->match_set, ++ skb, ++ info->match_set.flags[0] & IPSET_MATCH_INV); ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++bool ++#else ++static int ++#endif ++checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ const void *inf, ++#else ++ const struct ipt_ip *ip, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_match *match, ++#endif ++ void *matchinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int matchsize, ++#endif ++ unsigned int hook_mask) ++{ ++ struct ipt_set_info_match *info = ++ (struct ipt_set_info_match *) matchinfo; ++ ip_set_id_t index; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return 0; ++ } ++#endif ++ ++ index = ip_set_get_byindex(info->match_set.index); ++ ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("Cannot find set indentified by id %u to match", ++ info->match_set.index); ++ return 0; /* error */ ++ } ++ if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ ++ return 1; ++} ++ ++static void destroy( ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_match *match, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ void *matchinfo, unsigned int matchsize) ++#else ++ void *matchinfo) ++#endif ++{ ++ struct ipt_set_info_match *info = matchinfo; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return; ++ } ++#endif ++ ip_set_put(info->match_set.index); ++} ++ ++static struct ipt_match set_match = { ++ .name = "set", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++ .family = AF_INET, ++#endif ++ .match = &match, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ .matchsize = sizeof(struct ipt_set_info_match), ++#endif ++ .checkentry = &checkentry, ++ .destroy = &destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iptables IP set match module"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++#define ipt_register_match xt_register_match ++#define ipt_unregister_match xt_unregister_match ++#endif ++ ++static int __init ipt_ipset_init(void) ++{ ++ return ipt_register_match(&set_match); ++} ++ ++static void __exit ipt_ipset_fini(void) ++{ ++ ipt_unregister_match(&set_match); ++} ++ ++module_init(ipt_ipset_init); ++module_exit(ipt_ipset_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_SET.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ipt_SET.c +@@ -0,0 +1,172 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ipt_SET.c - netfilter target to manipulate IP sets */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned int ++target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ const void *targinfo, ++ void *userinfo) ++#else ++ const void *targinfo) ++#endif ++{ ++ const struct ipt_set_info_target *info = targinfo; ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_addip_kernel(info->add_set.index, ++ *pskb, ++ info->add_set.flags); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_delip_kernel(info->del_set.index, ++ *pskb, ++ info->del_set.flags); ++ ++ return IPT_CONTINUE; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++static bool ++#else ++static int ++#endif ++checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ const void *e, ++#else ++ const struct ipt_entry *e, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++ void *targinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int targinfosize, ++#endif ++ unsigned int hook_mask) ++{ ++ struct ipt_set_info_target *info = ++ (struct ipt_set_info_target *) targinfo; ++ ip_set_id_t index; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (targinfosize != IPT_ALIGN(sizeof(*info))) { ++ DP("bad target info size %u", targinfosize); ++ return 0; ++ } ++#endif ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->add_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find add_set index %u as target", ++ info->add_set.index); ++ return 0; /* error */ ++ } ++ } ++ ++ if (info->del_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->del_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find del_set index %u as target", ++ info->del_set.index); ++ return 0; /* error */ ++ } ++ } ++ if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 ++ || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ ++ return 1; ++} ++ ++static void destroy( ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ void *targetinfo, unsigned int targetsize) ++#else ++ void *targetinfo) ++#endif ++{ ++ struct ipt_set_info_target *info = targetinfo; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { ++ ip_set_printk("invalid targetsize %d", targetsize); ++ return; ++ } ++#endif ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->add_set.index); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->del_set.index); ++} ++ ++static struct ipt_target SET_target = { ++ .name = "SET", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++ .family = AF_INET, ++#endif ++ .target = target, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ .targetsize = sizeof(struct ipt_set_info_target), ++#endif ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iptables IP set target module"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++#define ipt_register_target xt_register_target ++#define ipt_unregister_target xt_unregister_target ++#endif ++ ++static int __init ipt_SET_init(void) ++{ ++ return ipt_register_target(&SET_target); ++} ++ ++static void __exit ipt_SET_fini(void) ++{ ++ ipt_unregister_target(&SET_target); ++} ++ ++module_init(ipt_SET_init); ++module_exit(ipt_SET_fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig ++++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig +@@ -663,5 +663,122 @@ config IP_NF_ARP_MANGLE + Allows altering the ARP packet payload: source and destination + hardware and network addresses. + ++config IP_NF_SET ++ tristate "IP set support" ++ depends on INET && NETFILTER ++ help ++ This option adds IP set support to the kernel. ++ In order to define and use sets, you need the userspace utility ++ ipset(8). ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_MAX ++ int "Maximum number of IP sets" ++ default 256 ++ range 2 65534 ++ depends on IP_NF_SET ++ help ++ You can define here default value of the maximum number ++ of IP sets for the kernel. ++ ++ The value can be overriden by the 'max_sets' module ++ parameter of the 'ip_set' module. ++ ++config IP_NF_SET_HASHSIZE ++ int "Hash size for bindings of IP sets" ++ default 1024 ++ depends on IP_NF_SET ++ help ++ You can define here default value of the hash size for ++ bindings of IP sets. ++ ++ The value can be overriden by the 'hash_size' module ++ parameter of the 'ip_set' module. ++ ++config IP_NF_SET_IPMAP ++ tristate "ipmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the ipmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_MACIPMAP ++ tristate "macipmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the macipmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_PORTMAP ++ tristate "portmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the portmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPHASH ++ tristate "iphash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iphash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_NETHASH ++ tristate "nethash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the nethash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPPORTHASH ++ tristate "ipporthash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the ipporthash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPTREE ++ tristate "iptree set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iptree set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPTREEMAP ++ tristate "iptreemap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iptreemap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_MATCH_SET ++ tristate "set match support" ++ depends on IP_NF_SET ++ help ++ Set matching matches against given IP sets. ++ You need the ipset utility to create and set up the sets. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_TARGET_SET ++ tristate "SET target support" ++ depends on IP_NF_SET ++ help ++ The SET target makes possible to add/delete entries ++ in IP sets. ++ You need the ipset utility to create and set up the sets. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++ + endmenu + +Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile ++++ linux-2.6.21.7/net/ipv4/netfilter/Makefile +@@ -90,6 +90,7 @@ obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_ + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o ++obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o + obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o + obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o + +@@ -106,6 +107,18 @@ obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LO + obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o + obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o + obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o ++obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o ++ ++# sets ++obj-$(CONFIG_IP_NF_SET) += ip_set.o ++obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o ++obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o ++obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o ++obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o ++obj-$(CONFIG_IP_NF_SET_NETHASH) += ip_set_nethash.o ++obj-$(CONFIG_IP_NF_SET_IPPORTHASH) += ip_set_ipporthash.o ++obj-$(CONFIG_IP_NF_SET_IPTREE) += ip_set_iptree.o ++obj-$(CONFIG_IP_NF_SET_IPTREEMAP) += ip_set_iptreemap.o + + # generic ARP tables + obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o diff --git a/target/linux/generic-2.6/patches-2.6.21/140-netfilter_time.patch b/target/linux/generic-2.6/patches-2.6.21/140-netfilter_time.patch new file mode 100644 index 0000000000..f1feaf6c0d --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/140-netfilter_time.patch @@ -0,0 +1,245 @@ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_time.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_time.h +@@ -0,0 +1,18 @@ ++#ifndef __ipt_time_h_included__ ++#define __ipt_time_h_included__ ++ ++ ++struct ipt_time_info { ++ u_int8_t days_match; /* 1 bit per day. -SMTWTFS */ ++ u_int16_t time_start; /* 0 < time_start < 23*60+59 = 1439 */ ++ u_int16_t time_stop; /* 0:0 < time_stat < 23:59 */ ++ ++ /* FIXME: Keep this one for userspace iptables binary compability: */ ++ u_int8_t kerneltime; /* ignore skb time (and use kerneltime) or not. */ ++ ++ time_t date_start; ++ time_t date_stop; ++}; ++ ++ ++#endif /* __ipt_time_h_included__ */ +Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_time.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ipt_time.c +@@ -0,0 +1,178 @@ ++/* ++ This is a module which is used for time matching ++ It is using some modified code from dietlibc (localtime() function) ++ that you can find at http://www.fefe.de/dietlibc/ ++ This file is distributed under the terms of the GNU General Public ++ License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL ++ 2001-05-04 Fabrice MARIE : initial development. ++ 2001-21-05 Fabrice MARIE : bug fix in the match code, ++ thanks to "Zeng Yu" for bug report. ++ 2001-26-09 Fabrice MARIE : force the match to be in LOCAL_IN or PRE_ROUTING only. ++ 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack, ++ added Nguyen Dang Phuoc Dong patch to support timezones. ++ 2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Fabrice MARIE "); ++MODULE_DESCRIPTION("Match arrival timestamp/date"); ++MODULE_LICENSE("GPL"); ++ ++struct tm ++{ ++ int tm_sec; /* Seconds. [0-60] (1 leap second) */ ++ int tm_min; /* Minutes. [0-59] */ ++ int tm_hour; /* Hours. [0-23] */ ++ int tm_mday; /* Day. [1-31] */ ++ int tm_mon; /* Month. [0-11] */ ++ int tm_year; /* Year - 1900. */ ++ int tm_wday; /* Day of week. [0-6] */ ++ int tm_yday; /* Days in year.[0-365] */ ++ int tm_isdst; /* DST. [-1/0/1]*/ ++ ++ long int tm_gmtoff; /* we don't care, we count from GMT */ ++ const char *tm_zone; /* we don't care, we count from GMT */ ++}; ++ ++void ++localtime(const u32 time, struct tm *r); ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct xt_match *match, ++ const void *matchinfo, ++ int offset, ++ unsigned int protoff, ++ int *hotdrop) ++{ ++ const struct ipt_time_info *info = matchinfo; /* match info for rule */ ++ struct tm currenttime; /* time human readable */ ++ u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; ++ u_int16_t packet_time; ++ ++ /* We might not have a timestamp, get one */ ++ if (skb->tstamp.off_sec == 0) ++ __net_timestamp((struct sk_buff *)skb); ++ ++ /* First we make sure we are in the date start-stop boundaries */ ++ if ((skb->tstamp.off_sec < info->date_start) || (skb->tstamp.off_sec > info->date_stop)) ++ return 0; /* We are outside the date boundaries */ ++ ++ /* Transform the timestamp of the packet, in a human readable form */ ++ localtime(skb->tstamp.off_sec, ¤ttime); ++ ++ /* check if we match this timestamp, we start by the days... */ ++ if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday]) ++ return 0; /* the day doesn't match */ ++ ++ /* ... check the time now */ ++ packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min; ++ if ((packet_time < info->time_start) || (packet_time > info->time_stop)) ++ return 0; ++ ++ /* here we match ! */ ++ return 1; ++} ++ ++static int ++checkentry(const char *tablename, ++ const void *ip, ++ const struct xt_match *match, ++ void *matchinfo, ++ unsigned int hook_mask) ++{ ++ struct ipt_time_info *info = matchinfo; /* match info for rule */ ++ ++ /* First, check that we are in the correct hooks */ ++ if (hook_mask ++ & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) ++ { ++ printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n"); ++ return 0; ++ } ++ ++ /* Now check the coherence of the data ... */ ++ if ((info->time_start > 1439) || /* 23*60+59 = 1439*/ ++ (info->time_stop > 1439)) ++ { ++ printk(KERN_WARNING "ipt_time: invalid argument\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ipt_match time_match = { ++ .name = "time", ++ .match = &match, ++ .matchsize = sizeof(struct ipt_time_info), ++ .checkentry = &checkentry, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ printk("ipt_time loading\n"); ++ return xt_register_match(&time_match); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&time_match); ++ printk("ipt_time unloaded\n"); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++ ++/* The part below is borowed and modified from dietlibc */ ++ ++/* seconds per day */ ++#define SPD 24*60*60 ++ ++void ++localtime(const u32 time, struct tm *r) { ++ u32 i, timep; ++ extern struct timezone sys_tz; ++ const unsigned int __spm[12] = ++ { 0, ++ (31), ++ (31+28), ++ (31+28+31), ++ (31+28+31+30), ++ (31+28+31+30+31), ++ (31+28+31+30+31+30), ++ (31+28+31+30+31+30+31), ++ (31+28+31+30+31+30+31+31), ++ (31+28+31+30+31+30+31+31+30), ++ (31+28+31+30+31+30+31+31+30+31), ++ (31+28+31+30+31+30+31+31+30+31+30), ++ }; ++ register u32 work; ++ ++ timep = time - (sys_tz.tz_minuteswest * 60); ++ work=timep%(SPD); ++ r->tm_sec=work%60; work/=60; ++ r->tm_min=work%60; r->tm_hour=work/60; ++ work=timep/(SPD); ++ r->tm_wday=(4+work)%7; ++ for (i=1970; ; ++i) { ++ register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365; ++ if (work>k) ++ work-=k; ++ else ++ break; ++ } ++ r->tm_year=i-1900; ++ for (i=11; i && __spm[i]>work; --i) ; ++ r->tm_mon=i; ++ r->tm_mday=work-__spm[i]+1; ++} +Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig ++++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig +@@ -260,6 +260,22 @@ config IP_NF_MATCH_TOS + + To compile it as a module, choose M here. If unsure, say N. + ++ ++config IP_NF_MATCH_TIME ++ tristate 'TIME match support' ++ depends on IP_NF_IPTABLES ++ help ++ This option adds a `time' match, which allows you ++ to match based on the packet arrival time/date ++ (arrival time/date at the machine which netfilter is running on) or ++ departure time/date (for locally generated packets). ++ ++ If you say Y here, try iptables -m time --help for more information. ++ If you want to compile it as a module, say M here and read ++ ++ Documentation/modules.txt. If unsure, say `N'. ++ ++ + config IP_NF_MATCH_RECENT + tristate "recent match support" + depends on IP_NF_IPTABLES +Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile ++++ linux-2.6.21.7/net/ipv4/netfilter/Makefile +@@ -86,6 +86,7 @@ obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o + obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o + obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o + obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o ++obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o + obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o diff --git a/target/linux/generic-2.6/patches-2.6.21/150-netfilter_imq.patch b/target/linux/generic-2.6/patches-2.6.21/150-netfilter_imq.patch new file mode 100644 index 0000000000..66fb99bfb1 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/150-netfilter_imq.patch @@ -0,0 +1,904 @@ +Index: linux-2.6.21.7/drivers/net/imq.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/drivers/net/imq.c +@@ -0,0 +1,402 @@ ++/* ++ * Pseudo-driver for the intermediate queue device. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Authors: Patrick McHardy, ++ * ++ * The first version was written by Martin Devera, ++ * ++ * Credits: Jan Rafaj ++ * - Update patch to 2.4.21 ++ * Sebastian Strollo ++ * - Fix "Dead-loop on netdevice imq"-issue ++ * Marcel Sebek ++ * - Update to 2.6.2-rc1 ++ * ++ * After some time of inactivity there is a group taking care ++ * of IMQ again: http://www.linuximq.net ++ * ++ * ++ * 2004/06/30 - New version of IMQ patch to kernels <=2.6.7 including ++ * the following changes: ++ * ++ * - Correction of ipv6 support "+"s issue (Hasso Tepper) ++ * - Correction of imq_init_devs() issue that resulted in ++ * kernel OOPS unloading IMQ as module (Norbert Buchmuller) ++ * - Addition of functionality to choose number of IMQ devices ++ * during kernel config (Andre Correa) ++ * - Addition of functionality to choose how IMQ hooks on ++ * PRE and POSTROUTING (after or before NAT) (Andre Correa) ++ * - Cosmetic corrections (Norbert Buchmuller) (Andre Correa) ++ * ++ * ++ * 2005/12/16 - IMQ versions between 2.6.7 and 2.6.13 were ++ * released with almost no problems. 2.6.14-x was released ++ * with some important changes: nfcache was removed; After ++ * some weeks of trouble we figured out that some IMQ fields ++ * in skb were missing in skbuff.c - skb_clone and copy_skb_header. ++ * These functions are correctly patched by this new patch version. ++ * ++ * Thanks for all who helped to figure out all the problems with ++ * 2.6.14.x: Patrick McHardy, Rune Kock, VeNoMouS, Max CtRiX, ++ * Kevin Shanahan, Richard Lucassen, Valery Dachev (hopefully ++ * I didn't forget anybody). I apologize again for my lack of time. ++ * ++ * More info at: http://www.linuximq.net/ (Andre Correa) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ #include ++#endif ++#include ++#include ++ ++extern int qdisc_restart1(struct net_device *dev); ++ ++static nf_hookfn imq_nf_hook; ++ ++static struct nf_hook_ops imq_ingress_ipv4 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET, ++ .hooknum = NF_IP_PRE_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ .priority = NF_IP_PRI_MANGLE + 1 ++#else ++ .priority = NF_IP_PRI_NAT_DST + 1 ++#endif ++}; ++ ++static struct nf_hook_ops imq_egress_ipv4 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET, ++ .hooknum = NF_IP_POST_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) ++ .priority = NF_IP_PRI_LAST ++#else ++ .priority = NF_IP_PRI_NAT_SRC - 1 ++#endif ++}; ++ ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++static struct nf_hook_ops imq_ingress_ipv6 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET6, ++ .hooknum = NF_IP6_PRE_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ .priority = NF_IP6_PRI_MANGLE + 1 ++#else ++ .priority = NF_IP6_PRI_NAT_DST + 1 ++#endif ++}; ++ ++static struct nf_hook_ops imq_egress_ipv6 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET6, ++ .hooknum = NF_IP6_POST_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) ++ .priority = NF_IP6_PRI_LAST ++#else ++ .priority = NF_IP6_PRI_NAT_SRC - 1 ++#endif ++}; ++#endif ++ ++#if defined(CONFIG_IMQ_NUM_DEVS) ++static unsigned int numdevs = CONFIG_IMQ_NUM_DEVS; ++#else ++static unsigned int numdevs = 2; ++#endif ++ ++static struct net_device *imq_devs; ++ ++static struct net_device_stats *imq_get_stats(struct net_device *dev) ++{ ++ return (struct net_device_stats *)dev->priv; ++} ++ ++/* called for packets kfree'd in qdiscs at places other than enqueue */ ++static void imq_skb_destructor(struct sk_buff *skb) ++{ ++ struct nf_info *info = skb->nf_info; ++ ++ if (info) { ++ if (info->indev) ++ dev_put(info->indev); ++ if (info->outdev) ++ dev_put(info->outdev); ++ kfree(info); ++ } ++} ++ ++static int imq_dev_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct net_device_stats *stats = (struct net_device_stats*) dev->priv; ++ ++ stats->tx_bytes += skb->len; ++ stats->tx_packets++; ++ ++ skb->imq_flags = 0; ++ skb->destructor = NULL; ++ ++ dev->trans_start = jiffies; ++ nf_reinject(skb, skb->nf_info, NF_ACCEPT); ++ return 0; ++} ++ ++static int imq_nf_queue(struct sk_buff *skb, struct nf_info *info, unsigned queue_num, void *data) ++{ ++ struct net_device *dev; ++ struct net_device_stats *stats; ++ struct sk_buff *skb2 = NULL; ++ struct Qdisc *q; ++ unsigned int index = skb->imq_flags&IMQ_F_IFMASK; ++ int ret = -1; ++ ++ if (index > numdevs) ++ return -1; ++ ++ dev = imq_devs + index; ++ if (!(dev->flags & IFF_UP)) { ++ skb->imq_flags = 0; ++ nf_reinject(skb, info, NF_ACCEPT); ++ return 0; ++ } ++ dev->last_rx = jiffies; ++ ++ if (skb->destructor) { ++ skb2 = skb; ++ skb = skb_clone(skb, GFP_ATOMIC); ++ if (!skb) ++ return -1; ++ } ++ skb->nf_info = info; ++ ++ stats = (struct net_device_stats *)dev->priv; ++ stats->rx_bytes+= skb->len; ++ stats->rx_packets++; ++ ++ spin_lock_bh(&dev->queue_lock); ++ q = dev->qdisc; ++ if (q->enqueue) { ++ q->enqueue(skb_get(skb), q); ++ if (skb_shared(skb)) { ++ skb->destructor = imq_skb_destructor; ++ kfree_skb(skb); ++ ret = 0; ++ } ++ } ++ if (spin_is_locked(&dev->_xmit_lock)) ++ netif_schedule(dev); ++ else ++ while (!netif_queue_stopped(dev) && qdisc_restart1(dev) < 0) ++ /* NOTHING */; ++ ++ spin_unlock_bh(&dev->queue_lock); ++ ++ if (skb2) ++ kfree_skb(ret ? skb : skb2); ++ ++ return ret; ++} ++ ++static struct nf_queue_handler nfqh = { ++ .name = "imq", ++ .outfn = imq_nf_queue, ++}; ++ ++static unsigned int imq_nf_hook(unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *indev, ++ const struct net_device *outdev, ++ int (*okfn)(struct sk_buff *)) ++{ ++ if ((*pskb)->imq_flags & IMQ_F_ENQUEUE) ++ return NF_QUEUE; ++ ++ return NF_ACCEPT; ++} ++ ++ ++static int __init imq_init_hooks(void) ++{ ++ int err; ++ ++ err = nf_register_queue_handler(PF_INET, &nfqh); ++ if (err > 0) ++ goto err1; ++ if ((err = nf_register_hook(&imq_ingress_ipv4))) ++ goto err2; ++ if ((err = nf_register_hook(&imq_egress_ipv4))) ++ goto err3; ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ if ((err = nf_register_queue_handler(PF_INET6, &nfqh))) ++ goto err4; ++ if ((err = nf_register_hook(&imq_ingress_ipv6))) ++ goto err5; ++ if ((err = nf_register_hook(&imq_egress_ipv6))) ++ goto err6; ++#endif ++ ++ return 0; ++ ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++err6: ++ nf_unregister_hook(&imq_ingress_ipv6); ++err5: ++ nf_unregister_queue_handler(PF_INET6); ++err4: ++ nf_unregister_hook(&imq_egress_ipv4); ++#endif ++err3: ++ nf_unregister_hook(&imq_ingress_ipv4); ++err2: ++ nf_unregister_queue_handler(PF_INET); ++err1: ++ return err; ++} ++ ++static void __exit imq_unhook(void) ++{ ++ nf_unregister_hook(&imq_ingress_ipv4); ++ nf_unregister_hook(&imq_egress_ipv4); ++ nf_unregister_queue_handler(PF_INET); ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ nf_unregister_hook(&imq_ingress_ipv6); ++ nf_unregister_hook(&imq_egress_ipv6); ++ nf_unregister_queue_handler(PF_INET6); ++#endif ++} ++ ++static int __init imq_dev_init(struct net_device *dev) ++{ ++ dev->hard_start_xmit = imq_dev_xmit; ++ dev->type = ARPHRD_VOID; ++ dev->mtu = 1500; ++ dev->tx_queue_len = 30; ++ dev->flags = IFF_NOARP; ++ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); ++ if (dev->priv == NULL) ++ return -ENOMEM; ++ memset(dev->priv, 0, sizeof(struct net_device_stats)); ++ dev->get_stats = imq_get_stats; ++ ++ return 0; ++} ++ ++static void imq_dev_uninit(struct net_device *dev) ++{ ++ kfree(dev->priv); ++} ++ ++static int __init imq_init_devs(void) ++{ ++ struct net_device *dev; ++ int i,j; ++ j = numdevs; ++ ++ if (!numdevs || numdevs > IMQ_MAX_DEVS) { ++ printk(KERN_ERR "IMQ: numdevs has to be betweed 1 and %u\n", ++ IMQ_MAX_DEVS); ++ return -EINVAL; ++ } ++ ++ imq_devs = kmalloc(sizeof(struct net_device) * numdevs, GFP_KERNEL); ++ if (!imq_devs) ++ return -ENOMEM; ++ memset(imq_devs, 0, sizeof(struct net_device) * numdevs); ++ ++ /* we start counting at zero */ ++ numdevs--; ++ ++ for (i = 0, dev = imq_devs; i <= numdevs; i++, dev++) { ++ SET_MODULE_OWNER(dev); ++ strcpy(dev->name, "imq%d"); ++ dev->init = imq_dev_init; ++ dev->uninit = imq_dev_uninit; ++ ++ if (register_netdev(dev) < 0) ++ goto err_register; ++ } ++ printk(KERN_INFO "IMQ starting with %u devices...\n", j); ++ return 0; ++ ++err_register: ++ for (; i; i--) ++ unregister_netdev(--dev); ++ kfree(imq_devs); ++ return -EIO; ++} ++ ++static void imq_cleanup_devs(void) ++{ ++ int i; ++ struct net_device *dev = imq_devs; ++ ++ for (i = 0; i <= numdevs; i++) ++ unregister_netdev(dev++); ++ ++ kfree(imq_devs); ++} ++ ++static int __init imq_init_module(void) ++{ ++ int err; ++ ++ if ((err = imq_init_devs())) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_devs()\n"); ++ return err; ++ } ++ if ((err = imq_init_hooks())) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_hooks()\n"); ++ imq_cleanup_devs(); ++ return err; ++ } ++ ++ printk(KERN_INFO "IMQ driver loaded successfully.\n"); ++ ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ printk(KERN_INFO "\tHooking IMQ before NAT on PREROUTING.\n"); ++#else ++ printk(KERN_INFO "\tHooking IMQ after NAT on PREROUTING.\n"); ++#endif ++#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ printk(KERN_INFO "\tHooking IMQ before NAT on POSTROUTING.\n"); ++#else ++ printk(KERN_INFO "\tHooking IMQ after NAT on POSTROUTING.\n"); ++#endif ++ ++ return 0; ++} ++ ++static void __exit imq_cleanup_module(void) ++{ ++ imq_unhook(); ++ imq_cleanup_devs(); ++ printk(KERN_INFO "IMQ driver unloaded successfully.\n"); ++} ++ ++ ++module_init(imq_init_module); ++module_exit(imq_cleanup_module); ++ ++module_param(numdevs, int, 0); ++MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)"); ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.21.7/drivers/net/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/Kconfig ++++ linux-2.6.21.7/drivers/net/Kconfig +@@ -96,6 +96,129 @@ config EQUALIZER + To compile this driver as a module, choose M here: the module + will be called eql. If unsure, say N. + ++config IMQ ++ tristate "IMQ (intermediate queueing device) support" ++ depends on NETDEVICES && NETFILTER ++ ---help--- ++ The IMQ device(s) is used as placeholder for QoS queueing ++ disciplines. Every packet entering/leaving the IP stack can be ++ directed through the IMQ device where it's enqueued/dequeued to the ++ attached qdisc. This allows you to treat network devices as classes ++ and distribute bandwidth among them. Iptables is used to specify ++ through which IMQ device, if any, packets travel. ++ ++ More information at: http://www.linuximq.net/ ++ ++ To compile this driver as a module, choose M here: the module ++ will be called imq. If unsure, say N. ++ ++choice ++ prompt "IMQ behavior (PRE/POSTROUTING)" ++ depends on IMQ ++ default IMQ_BEHAVIOR_BA ++ help ++ ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ IMQ can work in any of the following ways: ++ ++ PREROUTING | POSTROUTING ++ -----------------|------------------- ++ #1 After NAT | After NAT ++ #2 After NAT | Before NAT ++ #3 Before NAT | After NAT ++ #4 Before NAT | Before NAT ++ ++ The default behavior is to hook before NAT on PREROUTING ++ and after NAT on POSTROUTING (#3). ++ ++ This settings are specially usefull when trying to use IMQ ++ to shape NATed clients. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AA ++ bool "IMQ AA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AB ++ bool "IMQ AB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BA ++ bool "IMQ BA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BB ++ bool "IMQ BB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++endchoice ++ ++config IMQ_NUM_DEVS ++ ++ int "Number of IMQ devices" ++ range 2 8 ++ depends on IMQ ++ default "2" ++ help ++ ++ This settings defines how many IMQ devices will be ++ created. ++ ++ The default value is 2. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ + config TUN + tristate "Universal TUN/TAP device driver support" + select CRC32 +Index: linux-2.6.21.7/drivers/net/Makefile +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/Makefile ++++ linux-2.6.21.7/drivers/net/Makefile +@@ -124,6 +124,7 @@ obj-$(CONFIG_SLIP) += slip.o + obj-$(CONFIG_SLHC) += slhc.o + + obj-$(CONFIG_DUMMY) += dummy.o ++obj-$(CONFIG_IMQ) += imq.o + obj-$(CONFIG_IFB) += ifb.o + obj-$(CONFIG_DE600) += de600.o + obj-$(CONFIG_DE620) += de620.o +Index: linux-2.6.21.7/include/linux/imq.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/imq.h +@@ -0,0 +1,9 @@ ++#ifndef _IMQ_H ++#define _IMQ_H ++ ++#define IMQ_MAX_DEVS 16 ++ ++#define IMQ_F_IFMASK 0x7f ++#define IMQ_F_ENQUEUE 0x80 ++ ++#endif /* _IMQ_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_IMQ.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_IMQ.h +@@ -0,0 +1,8 @@ ++#ifndef _IPT_IMQ_H ++#define _IPT_IMQ_H ++ ++struct ipt_imq_info { ++ unsigned int todev; /* target imq device */ ++}; ++ ++#endif /* _IPT_IMQ_H */ +Index: linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_IMQ.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_IMQ.h +@@ -0,0 +1,8 @@ ++#ifndef _IP6T_IMQ_H ++#define _IP6T_IMQ_H ++ ++struct ip6t_imq_info { ++ unsigned int todev; /* target imq device */ ++}; ++ ++#endif /* _IP6T_IMQ_H */ +Index: linux-2.6.21.7/include/linux/skbuff.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/skbuff.h ++++ linux-2.6.21.7/include/linux/skbuff.h +@@ -294,6 +294,10 @@ struct sk_buff { + #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) + struct sk_buff *nfct_reasm; + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ unsigned char imq_flags; ++ struct nf_info *nf_info; ++#endif + #ifdef CONFIG_BRIDGE_NETFILTER + struct nf_bridge_info *nf_bridge; + #endif +Index: linux-2.6.21.7/net/core/dev.c +=================================================================== +--- linux-2.6.21.7.orig/net/core/dev.c ++++ linux-2.6.21.7/net/core/dev.c +@@ -94,6 +94,9 @@ + #include + #include + #include ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++#include ++#endif + #include + #include + #include +@@ -1340,7 +1343,11 @@ static int dev_gso_segment(struct sk_buf + int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + if (likely(!skb->next)) { +- if (netdev_nit) ++ if (netdev_nit ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ && !(skb->imq_flags & IMQ_F_ENQUEUE) ++#endif ++ ) + dev_queue_xmit_nit(skb, dev); + + if (netif_needs_gso(dev, skb)) { +Index: linux-2.6.21.7/net/core/skbuff.c +=================================================================== +--- linux-2.6.21.7.orig/net/core/skbuff.c ++++ linux-2.6.21.7/net/core/skbuff.c +@@ -430,6 +430,10 @@ struct sk_buff *skb_clone(struct sk_buff + C(nfct_reasm); + nf_conntrack_get_reasm(skb->nfct_reasm); + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ C(imq_flags); ++ C(nf_info); ++#endif /*CONFIG_IMQ*/ + #ifdef CONFIG_BRIDGE_NETFILTER + C(nf_bridge); + nf_bridge_get(skb->nf_bridge); +@@ -494,6 +498,10 @@ static void copy_skb_header(struct sk_bu + #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE) + new->ipvs_property = old->ipvs_property; + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ new->imq_flags = old->imq_flags; ++ new->nf_info = old->nf_info; ++#endif /*CONFIG_IMQ*/ + #ifdef CONFIG_BRIDGE_NETFILTER + new->nf_bridge = old->nf_bridge; + nf_bridge_get(old->nf_bridge); +Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_IMQ.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ipt_IMQ.c +@@ -0,0 +1,69 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned int imq_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++{ ++ struct ipt_imq_info *mr = (struct ipt_imq_info*)targinfo; ++ ++ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; ++ ++ return XT_CONTINUE; ++} ++ ++static int imq_checkentry(const char *tablename, ++ const void *e, ++ const struct xt_target *target, ++ void *targinfo, ++ unsigned int hook_mask) ++{ ++ struct ipt_imq_info *mr; ++ ++ mr = (struct ipt_imq_info*)targinfo; ++ ++ if (mr->todev > IMQ_MAX_DEVS) { ++ printk(KERN_WARNING ++ "IMQ: invalid device specified, highest is %u\n", ++ IMQ_MAX_DEVS); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct xt_target ipt_imq_reg = { ++ .name = "IMQ", ++ .family = AF_INET, ++ .target = imq_target, ++ .targetsize = sizeof(struct ipt_imq_info), ++ .checkentry = imq_checkentry, ++ .me = THIS_MODULE, ++ .table = "mangle" ++}; ++ ++static int __init init(void) ++{ ++ return xt_register_target(&ipt_imq_reg); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ipt_imq_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig ++++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig +@@ -587,6 +587,17 @@ config IP_NF_MANGLE + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_TARGET_IMQ ++ tristate "IMQ target support" ++ depends on IP_NF_MANGLE ++ help ++ This option adds a `IMQ' target which is used to specify if and ++ to which IMQ device packets should get enqueued/dequeued. ++ ++ For more information visit: http://www.linuximq.net/ ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP_NF_TARGET_TOS + tristate "TOS target support" + depends on IP_NF_MANGLE +Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile ++++ linux-2.6.21.7/net/ipv4/netfilter/Makefile +@@ -99,6 +99,7 @@ obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_i + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o + obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o + obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o ++obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o +Index: linux-2.6.21.7/net/ipv6/netfilter/ip6t_IMQ.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv6/netfilter/ip6t_IMQ.c +@@ -0,0 +1,69 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned int imq_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++{ ++ struct ip6t_imq_info *mr = (struct ip6t_imq_info*)targinfo; ++ ++ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; ++ ++ return XT_CONTINUE; ++} ++ ++static int imq_checkentry(const char *tablename, ++ const void *entry, ++ const struct xt_target *target, ++ void *targinfo, ++ unsigned int hook_mask) ++{ ++ struct ip6t_imq_info *mr; ++ ++ mr = (struct ip6t_imq_info*)targinfo; ++ ++ if (mr->todev > IMQ_MAX_DEVS) { ++ printk(KERN_WARNING ++ "IMQ: invalid device specified, highest is %u\n", ++ IMQ_MAX_DEVS); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct xt_target ip6t_imq_reg = { ++ .name = "IMQ", ++ .family = AF_INET6, ++ .target = imq_target, ++ .targetsize = sizeof(struct ip6t_imq_info), ++ .table = "mangle", ++ .checkentry = imq_checkentry, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ return xt_register_target(&ip6t_imq_reg); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ip6t_imq_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.21.7/net/ipv6/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv6/netfilter/Kconfig ++++ linux-2.6.21.7/net/ipv6/netfilter/Kconfig +@@ -173,6 +173,15 @@ config IP6_NF_MANGLE + + To compile it as a module, choose M here. If unsure, say N. + ++config IP6_NF_TARGET_IMQ ++ tristate "IMQ target support" ++ depends on IP6_NF_MANGLE ++ help ++ This option adds a `IMQ' target which is used to specify if and ++ to which imq device packets should get enqueued/dequeued. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP6_NF_TARGET_HL + tristate 'HL (hoplimit) target support' + depends on IP6_NF_MANGLE +Index: linux-2.6.21.7/net/ipv6/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/ipv6/netfilter/Makefile ++++ linux-2.6.21.7/net/ipv6/netfilter/Makefile +@@ -13,6 +13,7 @@ obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t + obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o + obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o + obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o ++obj-$(CONFIG_IP6_NF_TARGET_IMQ) += ip6t_IMQ.o + obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o + obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o + obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o +Index: linux-2.6.21.7/net/sched/sch_generic.c +=================================================================== +--- linux-2.6.21.7.orig/net/sched/sch_generic.c ++++ linux-2.6.21.7/net/sched/sch_generic.c +@@ -87,7 +87,6 @@ void qdisc_unlock_tree(struct net_device + + NOTE: Called under dev->queue_lock with locally disabled BH. + */ +- + static inline int qdisc_restart(struct net_device *dev) + { + struct Qdisc *q = dev->qdisc; +@@ -181,6 +180,11 @@ requeue: + return q->q.qlen; + } + ++int qdisc_restart1(struct net_device *dev) ++{ ++ return qdisc_restart(dev); ++} ++ + void __qdisc_run(struct net_device *dev) + { + if (unlikely(dev->qdisc == &noop_qdisc)) +@@ -617,3 +621,4 @@ EXPORT_SYMBOL(qdisc_destroy); + EXPORT_SYMBOL(qdisc_reset); + EXPORT_SYMBOL(qdisc_lock_tree); + EXPORT_SYMBOL(qdisc_unlock_tree); ++EXPORT_SYMBOL(qdisc_restart1); diff --git a/target/linux/generic-2.6/patches-2.6.21/160-netfilter_route.patch b/target/linux/generic-2.6/patches-2.6.21/160-netfilter_route.patch new file mode 100644 index 0000000000..9bd7d2a416 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/160-netfilter_route.patch @@ -0,0 +1,965 @@ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ROUTE.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ROUTE.h +@@ -0,0 +1,23 @@ ++/* Header file for iptables ipt_ROUTE target ++ * ++ * (C) 2002 by Cédric de Launois ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++#ifndef _IPT_ROUTE_H_target ++#define _IPT_ROUTE_H_target ++ ++#define IPT_ROUTE_IFNAMSIZ 16 ++ ++struct ipt_route_target_info { ++ char oif[IPT_ROUTE_IFNAMSIZ]; /* Output Interface Name */ ++ char iif[IPT_ROUTE_IFNAMSIZ]; /* Input Interface Name */ ++ u_int32_t gw; /* IP address of gateway */ ++ u_int8_t flags; ++}; ++ ++/* Values for "flags" field */ ++#define IPT_ROUTE_CONTINUE 0x01 ++#define IPT_ROUTE_TEE 0x02 ++ ++#endif /*_IPT_ROUTE_H_target*/ +Index: linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_ROUTE.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_ROUTE.h +@@ -0,0 +1,23 @@ ++/* Header file for iptables ip6t_ROUTE target ++ * ++ * (C) 2003 by Cédric de Launois ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++#ifndef _IPT_ROUTE_H_target ++#define _IPT_ROUTE_H_target ++ ++#define IP6T_ROUTE_IFNAMSIZ 16 ++ ++struct ip6t_route_target_info { ++ char oif[IP6T_ROUTE_IFNAMSIZ]; /* Output Interface Name */ ++ char iif[IP6T_ROUTE_IFNAMSIZ]; /* Input Interface Name */ ++ u_int32_t gw[4]; /* IPv6 address of gateway */ ++ u_int8_t flags; ++}; ++ ++/* Values for "flags" field */ ++#define IP6T_ROUTE_CONTINUE 0x01 ++#define IP6T_ROUTE_TEE 0x02 ++ ++#endif /*_IP6T_ROUTE_H_target*/ +Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_ROUTE.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv4/netfilter/ipt_ROUTE.c +@@ -0,0 +1,483 @@ ++/* ++ * This implements the ROUTE target, which enables you to setup unusual ++ * routes not supported by the standard kernel routing table. ++ * ++ * Copyright (C) 2002 Cedric de Launois ++ * ++ * v 1.11 2004/11/23 ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Cedric de Launois "); ++MODULE_DESCRIPTION("iptables ROUTE target module"); ++ ++/* Try to route the packet according to the routing keys specified in ++ * route_info. Keys are : ++ * - ifindex : ++ * 0 if no oif preferred, ++ * otherwise set to the index of the desired oif ++ * - route_info->gw : ++ * 0 if no gateway specified, ++ * otherwise set to the next host to which the pkt must be routed ++ * If success, skb->dev is the output device to which the packet must ++ * be sent and skb->dst is not NULL ++ * ++ * RETURN: -1 if an error occured ++ * 1 if the packet was succesfully routed to the ++ * destination desired ++ * 0 if the kernel routing table could not route the packet ++ * according to the keys specified ++ */ ++static int route(struct sk_buff *skb, ++ unsigned int ifindex, ++ const struct ipt_route_target_info *route_info) ++{ ++ int err; ++ struct rtable *rt; ++ struct iphdr *iph = skb->nh.iph; ++ struct flowi fl = { ++ .oif = ifindex, ++ .nl_u = { ++ .ip4_u = { ++ .daddr = iph->daddr, ++ .saddr = 0, ++ .tos = RT_TOS(iph->tos), ++ .scope = RT_SCOPE_UNIVERSE, ++ } ++ } ++ }; ++ ++ /* The destination address may be overloaded by the target */ ++ if (route_info->gw) ++ fl.fl4_dst = route_info->gw; ++ ++ /* Trying to route the packet using the standard routing table. */ ++ if ((err = ip_route_output_key(&rt, &fl))) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err); ++ return -1; ++ } ++ ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ ++ /* Success if no oif specified or if the oif correspond to the ++ * one desired */ ++ if (!ifindex || rt->u.dst.dev->ifindex == ifindex) { ++ skb->dst = &rt->u.dst; ++ skb->dev = skb->dst->dev; ++ skb->protocol = htons(ETH_P_IP); ++ return 1; ++ } ++ ++ /* The interface selected by the routing table is not the one ++ * specified by the user. This may happen because the dst address ++ * is one of our own addresses. ++ */ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n", ++ NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex); ++ ++ return 0; ++} ++ ++ ++/* Stolen from ip_finish_output2 ++ * PRE : skb->dev is set to the device we are leaving by ++ * skb->dst is not NULL ++ * POST: the packet is sent with the link layer header pushed ++ * the packet is destroyed ++ */ ++static void ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ struct hh_cache *hh = dst->hh; ++ struct net_device *dev = dst->dev; ++ int hh_len = LL_RESERVED_SPACE(dev); ++ ++ /* Be paranoid, rather than too clever. */ ++ if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) { ++ struct sk_buff *skb2; ++ ++ skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev)); ++ if (skb2 == NULL) { ++ kfree_skb(skb); ++ return; ++ } ++ if (skb->sk) ++ skb_set_owner_w(skb2, skb->sk); ++ kfree_skb(skb); ++ skb = skb2; ++ } ++ ++ if (hh) { ++ int hh_alen; ++ ++ read_lock_bh(&hh->hh_lock); ++ hh_alen = HH_DATA_ALIGN(hh->hh_len); ++ memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); ++ read_unlock_bh(&hh->hh_lock); ++ skb_push(skb, hh->hh_len); ++ hh->hh_output(skb); ++ } else if (dst->neighbour) ++ dst->neighbour->output(skb); ++ else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n"); ++ kfree_skb(skb); ++ } ++} ++ ++ ++/* PRE : skb->dev is set to the device we are leaving by ++ * POST: - the packet is directly sent to the skb->dev device, without ++ * pushing the link layer header. ++ * - the packet is destroyed ++ */ ++static inline int dev_direct_send(struct sk_buff *skb) ++{ ++ return dev_queue_xmit(skb); ++} ++ ++ ++static unsigned int route_oif(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ unsigned int ifindex = 0; ++ struct net_device *dev_out = NULL; ++ ++ /* The user set the interface name to use. ++ * Getting the current interface index. ++ */ ++ if ((dev_out = dev_get_by_name(route_info->oif))) { ++ ifindex = dev_out->ifindex; ++ } else { ++ /* Unknown interface name : packet dropped */ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif); ++ return NF_DROP; ++ } ++ ++ /* Trying the standard way of routing packets */ ++ switch (route(skb, ifindex, route_info)) { ++ case 1: ++ dev_put(dev_out); ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return IPT_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ ++ case 0: ++ /* Failed to send to oif. Trying the hard way */ ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return NF_DROP; ++ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: forcing the use of %i\n", ++ ifindex); ++ ++ /* We have to force the use of an interface. ++ * This interface must be a tunnel interface since ++ * otherwise we can't guess the hw address for ++ * the packet. For a tunnel interface, no hw address ++ * is needed. ++ */ ++ if ((dev_out->type != ARPHRD_TUNNEL) ++ && (dev_out->type != ARPHRD_IPGRE)) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: can't guess the hw addr !\n"); ++ dev_put(dev_out); ++ return NF_DROP; ++ } ++ ++ /* Send the packet. This will also free skb ++ * Do not go through the POST_ROUTING hook because ++ * skb->dst is not set and because it will probably ++ * get confused by the destination IP address. ++ */ ++ skb->dev = dev_out; ++ dev_direct_send(skb); ++ dev_put(dev_out); ++ return NF_STOLEN; ++ ++ default: ++ /* Unexpected error */ ++ dev_put(dev_out); ++ return NF_DROP; ++ } ++} ++ ++ ++static unsigned int route_iif(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ struct net_device *dev_in = NULL; ++ ++ /* Getting the current interface index. */ ++ if (!(dev_in = dev_get_by_name(route_info->iif))) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->iif); ++ return NF_DROP; ++ } ++ ++ skb->dev = dev_in; ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ ++ netif_rx(skb); ++ dev_put(dev_in); ++ return NF_STOLEN; ++} ++ ++ ++static unsigned int route_gw(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ if (route(skb, 0, route_info)!=1) ++ return NF_DROP; ++ ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return IPT_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++} ++ ++ ++/* To detect and deter routed packet loopback when using the --tee option, ++ * we take a page out of the raw.patch book: on the copied skb, we set up ++ * a fake ->nfct entry, pointing to the local &route_tee_track. We skip ++ * routing packets when we see they already have that ->nfct. ++ */ ++ ++static struct ip_conntrack route_tee_track; ++ ++static unsigned int ipt_route_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ const void *targinfo, ++ void *userinfo) ++#else ++ const void *targinfo) ++#endif ++{ ++ const struct ipt_route_target_info *route_info = targinfo; ++ struct sk_buff *skb = *pskb; ++ unsigned int res; ++ ++ if (skb->nfct == &route_tee_track.ct_general) { ++ /* Loopback - a packet we already routed, is to be ++ * routed another time. Avoid that, now. ++ */ ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: loopback - DROP!\n"); ++ return NF_DROP; ++ } ++ ++ /* If we are at PREROUTING or INPUT hook ++ * the TTL isn't decreased by the IP stack ++ */ ++ if (hooknum == NF_IP_PRE_ROUTING || ++ hooknum == NF_IP_LOCAL_IN) { ++ ++ struct iphdr *iph = skb->nh.iph; ++ ++ if (iph->ttl <= 1) { ++ struct rtable *rt; ++ struct flowi fl = { ++ .oif = 0, ++ .nl_u = { ++ .ip4_u = { ++ .daddr = iph->daddr, ++ .saddr = iph->saddr, ++ .tos = RT_TOS(iph->tos), ++ .scope = ((iph->tos & RTO_ONLINK) ? ++ RT_SCOPE_LINK : ++ RT_SCOPE_UNIVERSE) ++ } ++ } ++ }; ++ ++ if (ip_route_output_key(&rt, &fl)) { ++ return NF_DROP; ++ } ++ ++ if (skb->dev == rt->u.dst.dev) { ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = &rt->u.dst; ++ ++ /* this will traverse normal stack, and ++ * thus call conntrack on the icmp packet */ ++ icmp_send(skb, ICMP_TIME_EXCEEDED, ++ ICMP_EXC_TTL, 0); ++ } ++ ++ return NF_DROP; ++ } ++ ++ /* ++ * If we are at INPUT the checksum must be recalculated since ++ * the length could change as the result of a defragmentation. ++ */ ++ if(hooknum == NF_IP_LOCAL_IN) { ++ iph->ttl = iph->ttl - 1; ++ iph->check = 0; ++ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); ++ } else { ++ ip_decrease_ttl(iph); ++ } ++ } ++ ++ if ((route_info->flags & IPT_ROUTE_TEE)) { ++ /* ++ * Copy the *pskb, and route the copy. Will later return ++ * IPT_CONTINUE for the original skb, which should continue ++ * on its way as if nothing happened. The copy should be ++ * independantly delivered to the ROUTE --gw. ++ */ ++ skb = skb_copy(*pskb, GFP_ATOMIC); ++ if (!skb) { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: copy failed!\n"); ++ return IPT_CONTINUE; ++ } ++ } ++ ++ /* Tell conntrack to forget this packet since it may get confused ++ * when a packet is leaving with dst address == our address. ++ * Good idea ? Dunno. Need advice. ++ * ++ * NEW: mark the skb with our &route_tee_track, so we avoid looping ++ * on any already routed packet. ++ */ ++ if (!(route_info->flags & IPT_ROUTE_CONTINUE)) { ++ nf_conntrack_put(skb->nfct); ++ skb->nfct = &route_tee_track.ct_general; ++ skb->nfctinfo = IP_CT_NEW; ++ nf_conntrack_get(skb->nfct); ++ } ++ ++ if (route_info->oif[0] != '\0') { ++ res = route_oif(route_info, skb); ++ } else if (route_info->iif[0] != '\0') { ++ res = route_iif(route_info, skb); ++ } else if (route_info->gw) { ++ res = route_gw(route_info, skb); ++ } else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n"); ++ res = IPT_CONTINUE; ++ } ++ ++ if ((route_info->flags & IPT_ROUTE_TEE)) ++ res = IPT_CONTINUE; ++ ++ return res; ++} ++ ++ ++static int ipt_route_checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ const void *e, ++#else ++ const struct ipt_ip *ip, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++ void *targinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int targinfosize, ++#endif ++ unsigned int hook_mask) ++{ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n", ++ tablename); ++ return 0; ++ } ++ ++ if (hook_mask & ~( (1 << NF_IP_PRE_ROUTING) ++ | (1 << NF_IP_LOCAL_IN) ++ | (1 << NF_IP_FORWARD) ++ | (1 << NF_IP_LOCAL_OUT) ++ | (1 << NF_IP_POST_ROUTING))) { ++ printk("ipt_ROUTE: bad hook\n"); ++ return 0; ++ } ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_route_target_info))) { ++ printk(KERN_WARNING "ipt_ROUTE: targinfosize %u != %Zu\n", ++ targinfosize, ++ IPT_ALIGN(sizeof(struct ipt_route_target_info))); ++ return 0; ++ } ++#endif ++ ++ return 1; ++} ++ ++ ++static struct ipt_target ipt_route_reg = { ++ .name = "ROUTE", ++ .target = ipt_route_target, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ .targetsize = sizeof(struct ipt_route_target_info), ++#endif ++ .checkentry = ipt_route_checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ /* Set up fake conntrack (stolen from raw.patch): ++ - to never be deleted, not in any hashes */ ++ atomic_set(&route_tee_track.ct_general.use, 1); ++ /* - and look it like as a confirmed connection */ ++ set_bit(IPS_CONFIRMED_BIT, &route_tee_track.status); ++ /* Initialize fake conntrack so that NAT will skip it */ ++ route_tee_track.status |= IPS_NAT_DONE_MASK; ++ ++ return xt_register_target(&ipt_route_reg); ++} ++ ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ipt_route_reg); ++} ++ ++module_init(init); ++module_exit(fini); +Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig ++++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig +@@ -807,5 +807,22 @@ config IP_NF_TARGET_SET + To compile it as a module, choose M here. If unsure, say N. + + ++config IP_NF_TARGET_ROUTE ++ tristate 'ROUTE target support' ++ depends on IP_NF_MANGLE ++ help ++ This option adds a `ROUTE' target, which enables you to setup unusual ++ routes. For example, the ROUTE lets you route a received packet through ++ an interface or towards a host, even if the regular destination of the ++ packet is the router itself. The ROUTE target is also able to change the ++ incoming interface of a packet. ++ ++ The target can be or not a final target. It has to be used inside the ++ mangle table. ++ ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. The module will be called ipt_ROUTE.o. ++ If unsure, say `N'. ++ + endmenu + +Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile ++++ linux-2.6.21.7/net/ipv4/netfilter/Makefile +@@ -102,6 +102,7 @@ obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_EC + obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o ++obj-$(CONFIG_IP_NF_TARGET_ROUTE) += ipt_ROUTE.o + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o + obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o + obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o +Index: linux-2.6.21.7/net/ipv6/ipv6_syms.c +=================================================================== +--- linux-2.6.21.7.orig/net/ipv6/ipv6_syms.c ++++ linux-2.6.21.7/net/ipv6/ipv6_syms.c +@@ -10,6 +10,7 @@ EXPORT_SYMBOL(icmpv6_send); + EXPORT_SYMBOL(icmpv6_statistics); + EXPORT_SYMBOL(icmpv6_err_convert); + EXPORT_SYMBOL(ndisc_mc_map); ++EXPORT_SYMBOL(nd_tbl); + EXPORT_SYMBOL(register_inet6addr_notifier); + EXPORT_SYMBOL(unregister_inet6addr_notifier); + EXPORT_SYMBOL(ip6_route_output); +Index: linux-2.6.21.7/net/ipv6/netfilter/ip6t_ROUTE.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/ipv6/netfilter/ip6t_ROUTE.c +@@ -0,0 +1,330 @@ ++/* ++ * This implements the ROUTE v6 target, which enables you to setup unusual ++ * routes not supported by the standard kernel routing table. ++ * ++ * Copyright (C) 2003 Cedric de Launois ++ * ++ * v 1.1 2004/11/23 ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if 1 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++#define NIP6(addr) \ ++ ntohs((addr).s6_addr16[0]), \ ++ ntohs((addr).s6_addr16[1]), \ ++ ntohs((addr).s6_addr16[2]), \ ++ ntohs((addr).s6_addr16[3]), \ ++ ntohs((addr).s6_addr16[4]), \ ++ ntohs((addr).s6_addr16[5]), \ ++ ntohs((addr).s6_addr16[6]), \ ++ ntohs((addr).s6_addr16[7]) ++ ++/* Route the packet according to the routing keys specified in ++ * route_info. Keys are : ++ * - ifindex : ++ * 0 if no oif preferred, ++ * otherwise set to the index of the desired oif ++ * - route_info->gw : ++ * 0 if no gateway specified, ++ * otherwise set to the next host to which the pkt must be routed ++ * If success, skb->dev is the output device to which the packet must ++ * be sent and skb->dst is not NULL ++ * ++ * RETURN: 1 if the packet was succesfully routed to the ++ * destination desired ++ * 0 if the kernel routing table could not route the packet ++ * according to the keys specified ++ */ ++static int ++route6(struct sk_buff *skb, ++ unsigned int ifindex, ++ const struct ip6t_route_target_info *route_info) ++{ ++ struct rt6_info *rt = NULL; ++ struct ipv6hdr *ipv6h = skb->nh.ipv6h; ++ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; ++ ++ DEBUGP("ip6t_ROUTE: called with: "); ++ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); ++ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw)); ++ DEBUGP("OUT=%s\n", route_info->oif); ++ ++ if (ipv6_addr_any(gw)) ++ rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1); ++ else ++ rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1); ++ ++ if (!rt) ++ goto no_route; ++ ++ DEBUGP("ip6t_ROUTE: routing gives: "); ++ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr)); ++ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway)); ++ DEBUGP("OUT=%s\n", rt->rt6i_dev->name); ++ ++ if (ifindex && rt->rt6i_dev->ifindex!=ifindex) ++ goto wrong_route; ++ ++ if (!rt->rt6i_nexthop) { ++ DEBUGP("ip6t_ROUTE: discovering neighbour\n"); ++ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr); ++ } ++ ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = &rt->u.dst; ++ skb->dev = rt->rt6i_dev; ++ return 1; ++ ++ wrong_route: ++ dst_release(&rt->u.dst); ++ no_route: ++ if (!net_ratelimit()) ++ return 0; ++ ++ printk("ip6t_ROUTE: no explicit route found "); ++ if (ifindex) ++ printk("via interface %s ", route_info->oif); ++ if (!ipv6_addr_any(gw)) ++ printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw)); ++ printk("\n"); ++ return 0; ++} ++ ++ ++/* Stolen from ip6_output_finish ++ * PRE : skb->dev is set to the device we are leaving by ++ * skb->dst is not NULL ++ * POST: the packet is sent with the link layer header pushed ++ * the packet is destroyed ++ */ ++static void ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ struct hh_cache *hh = dst->hh; ++ ++ if (hh) { ++ read_lock_bh(&hh->hh_lock); ++ memcpy(skb->data - 16, hh->hh_data, 16); ++ read_unlock_bh(&hh->hh_lock); ++ skb_push(skb, hh->hh_len); ++ hh->hh_output(skb); ++ } else if (dst->neighbour) ++ dst->neighbour->output(skb); ++ else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n"); ++ kfree_skb(skb); ++ } ++} ++ ++ ++static unsigned int ++route6_oif(const struct ip6t_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ unsigned int ifindex = 0; ++ struct net_device *dev_out = NULL; ++ ++ /* The user set the interface name to use. ++ * Getting the current interface index. ++ */ ++ if ((dev_out = dev_get_by_name(route_info->oif))) { ++ ifindex = dev_out->ifindex; ++ } else { ++ /* Unknown interface name : packet dropped */ ++ if (net_ratelimit()) ++ DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); ++ ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ else ++ return NF_DROP; ++ } ++ ++ /* Trying the standard way of routing packets */ ++ if (route6(skb, ifindex, route_info)) { ++ dev_put(dev_out); ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ } else ++ return NF_DROP; ++} ++ ++ ++static unsigned int ++route6_gw(const struct ip6t_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ if (route6(skb, 0, route_info)) { ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ } else ++ return NF_DROP; ++} ++ ++ ++static unsigned int ++ip6t_route_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ const void *targinfo, ++ void *userinfo) ++#else ++ const void *targinfo) ++#endif ++{ ++ const struct ip6t_route_target_info *route_info = targinfo; ++ struct sk_buff *skb = *pskb; ++ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; ++ unsigned int res; ++ ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ goto do_it; ++ ++ /* If we are at PREROUTING or INPUT hook ++ * the TTL isn't decreased by the IP stack ++ */ ++ if (hooknum == NF_IP6_PRE_ROUTING || ++ hooknum == NF_IP6_LOCAL_IN) { ++ ++ struct ipv6hdr *ipv6h = skb->nh.ipv6h; ++ ++ if (ipv6h->hop_limit <= 1) { ++ /* Force OUTPUT device used as source address */ ++ skb->dev = skb->dst->dev; ++ ++ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ++ ICMPV6_EXC_HOPLIMIT, 0, skb->dev); ++ ++ return NF_DROP; ++ } ++ ++ ipv6h->hop_limit--; ++ } ++ ++ if ((route_info->flags & IP6T_ROUTE_TEE)) { ++ /* ++ * Copy the *pskb, and route the copy. Will later return ++ * IP6T_CONTINUE for the original skb, which should continue ++ * on its way as if nothing happened. The copy should be ++ * independantly delivered to the ROUTE --gw. ++ */ ++ skb = skb_copy(*pskb, GFP_ATOMIC); ++ if (!skb) { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n"); ++ return IP6T_CONTINUE; ++ } ++ } ++ ++do_it: ++ if (route_info->oif[0]) { ++ res = route6_oif(route_info, skb); ++ } else if (!ipv6_addr_any(gw)) { ++ res = route6_gw(route_info, skb); ++ } else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); ++ res = IP6T_CONTINUE; ++ } ++ ++ if ((route_info->flags & IP6T_ROUTE_TEE)) ++ res = IP6T_CONTINUE; ++ ++ return res; ++} ++ ++ ++static int ++ip6t_route_checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ const void *entry, ++#else ++ const struct ip6t_entry *entry ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++ void *targinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int targinfosize, ++#endif ++ unsigned int hook_mask) ++{ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); ++ return 0; ++ } ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { ++ printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", ++ targinfosize, ++ IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); ++ return 0; ++ } ++#endif ++ ++ return 1; ++} ++ ++ ++static struct ip6t_target ip6t_route_reg = { ++ .name = "ROUTE", ++ .target = ip6t_route_target, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ .targetsize = sizeof(struct ip6t_route_target_info), ++#endif ++ .checkentry = ip6t_route_checkentry, ++ .me = THIS_MODULE ++}; ++ ++ ++static int __init init(void) ++{ ++ printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); ++ if (xt_register_target(&ip6t_route_reg)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ip6t_route_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.21.7/net/ipv6/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/ipv6/netfilter/Kconfig ++++ linux-2.6.21.7/net/ipv6/netfilter/Kconfig +@@ -209,5 +209,18 @@ config IP6_NF_RAW + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + ++config IP6_NF_TARGET_ROUTE ++ tristate 'ROUTE target support' ++ depends on IP6_NF_MANGLE ++ help ++ This option adds a `ROUTE' target, which enables you to setup unusual ++ routes. The ROUTE target is also able to change the incoming interface ++ of a packet. ++ ++ The target can be or not a final target. It has to be used inside the ++ mangle table. ++ ++ Not working as a module. ++ + endmenu + +Index: linux-2.6.21.7/net/ipv6/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/ipv6/netfilter/Makefile ++++ linux-2.6.21.7/net/ipv6/netfilter/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_ + obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o + obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o + obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o ++obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o + obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o + + # objects for l3 independent conntrack diff --git a/target/linux/generic-2.6/patches-2.6.21/170-netfilter_chaostables_0.8.patch b/target/linux/generic-2.6/patches-2.6.21/170-netfilter_chaostables_0.8.patch new file mode 100644 index 0000000000..063bcf1e64 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/170-netfilter_chaostables_0.8.patch @@ -0,0 +1,853 @@ +Index: linux-2.6.21.7/include/linux/netfilter/oot_conntrack.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter/oot_conntrack.h +@@ -0,0 +1,5 @@ ++#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) ++# include ++#else /* linux-2.6.20+ */ ++# include ++#endif +Index: linux-2.6.21.7/include/linux/netfilter/oot_trans.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter/oot_trans.h +@@ -0,0 +1,14 @@ ++/* Out of tree workarounds */ ++#include ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++# define HAVE_MATCHINFOSIZE 1 ++# define HAVE_TARGUSERINFO 1 ++# define HAVE_TARGINFOSIZE 1 ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) ++# define nfmark mark ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) ++# define tcp_v4_check(tcph, tcph_sz, s, d, csp) \ ++ tcp_v4_check((tcph_sz), (s), (d), (csp)) ++#endif +Index: linux-2.6.21.7/include/linux/netfilter/xt_CHAOS.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter/xt_CHAOS.h +@@ -0,0 +1,14 @@ ++#ifndef _LINUX_NETFILTER_XT_CHAOS_H ++#define _LINUX_NETFILTER_XT_CHAOS_H 1 ++ ++enum xt_chaos_target_variant { ++ XTCHAOS_NORMAL, ++ XTCHAOS_TARPIT, ++ XTCHAOS_DELUDE, ++}; ++ ++struct xt_chaos_target_info { ++ uint8_t variant; ++}; ++ ++#endif /* _LINUX_NETFILTER_XT_CHAOS_H */ +Index: linux-2.6.21.7/include/linux/netfilter/xt_portscan.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/linux/netfilter/xt_portscan.h +@@ -0,0 +1,8 @@ ++#ifndef _LINUX_NETFILTER_XT_PORTSCAN_H ++#define _LINUX_NETFILTER_XT_PORTSCAN_H 1 ++ ++struct xt_portscan_match_info { ++ uint8_t match_stealth, match_syn, match_cn, match_gr; ++}; ++ ++#endif /* _LINUX_NETFILTER_XT_PORTSCAN_H */ +Index: linux-2.6.21.7/net/netfilter/find_match.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/find_match.c +@@ -0,0 +1,39 @@ ++/* ++ xt_request_find_match ++ by Jan Engelhardt , 2006 - 2007 ++ ++ Based upon linux-2.6.18.5/net/netfilter/x_tables.c: ++ Copyright (C) 2006-2006 Harald Welte ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation. ++*/ ++#include ++#include ++#include ++#include ++ ++/* ++ * Yeah this code is sub-optimal, but the function is missing in ++ * mainline so far. -jengelh ++ */ ++static struct xt_match *xt_request_find_match_lo(int af, const char *name, ++ u8 revision) ++{ ++ static const char *const xt_prefix[] = { ++ [AF_INET] = "ip", ++ [AF_INET6] = "ip6", ++ [NF_ARP] = "arp", ++ }; ++ struct xt_match *match; ++ ++ match = try_then_request_module(xt_find_match(af, name, revision), ++ "%st_%s", xt_prefix[af], name); ++ if (IS_ERR(match) || match == NULL) ++ return NULL; ++ ++ return match; ++} ++ ++/* In case it goes into mainline, let this out-of-tree package compile */ ++#define xt_request_find_match xt_request_find_match_lo +Index: linux-2.6.21.7/net/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/Kconfig ++++ linux-2.6.21.7/net/netfilter/Kconfig +@@ -287,6 +287,14 @@ config NETFILTER_XTABLES + + # alphabetically ordered list of targets + ++config NETFILTER_XT_TARGET_CHAOS ++ tristate '"CHAOS" target support' ++ depends on NETFILTER_XTABLES ++ help ++ This option adds a `CHAOS' target. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config NETFILTER_XT_TARGET_CLASSIFY + tristate '"CLASSIFY" target support' + depends on NETFILTER_XTABLES +@@ -315,6 +323,14 @@ config NETFILTER_XT_TARGET_CONNMARK + . The module will be called + ipt_CONNMARK.o. If unsure, say `N'. + ++config NETFILTER_XT_TARGET_DELUDE ++ tristate '"DELUDE" target support' ++ depends on NETFILTER_XTABLES ++ help ++ This option adds a `DELUDE' target. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config NETFILTER_XT_TARGET_DSCP + tristate '"DSCP" target support' + depends on NETFILTER_XTABLES +@@ -563,6 +579,14 @@ config NETFILTER_XT_MATCH_POLICY + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_MATCH_PORTSCAN ++ tristate '"portscan" match support' ++ depends on NETFILTER_XTABLES ++ help ++ This option adds a 'portscan' match support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config NETFILTER_XT_MATCH_MULTIPORT + tristate "Multiple port match support" + depends on NETFILTER_XTABLES +Index: linux-2.6.21.7/net/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/Makefile ++++ linux-2.6.21.7/net/netfilter/Makefile +@@ -47,6 +47,8 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK + obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_DELUDE) += xt_DELUDE.o + + # matches + obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o +@@ -74,3 +76,4 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) + obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o + obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o + obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) += xt_portscan.o +Index: linux-2.6.21.7/net/netfilter/xt_CHAOS.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/xt_CHAOS.c +@@ -0,0 +1,200 @@ ++/* ++ * CHAOS target for netfilter ++ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 ++ * Contact: Jan Engelhardt ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License; either version ++ * 2 or 3 as published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(_LOCAL) ++# include "xt_CHAOS.h" ++# include "find_match.c" ++#elif defined(CONFIG_NETFILTER_XT_TARGET_CHAOS) || \ ++ defined(CONFIG_NETFILTER_XT_TARGET_CHAOS_MODULE) ++# include ++# include "find_match.c" ++#else ++# include "xt_CHAOS.h" ++# include "find_match.c" ++#endif ++#define PFX KBUILD_MODNAME ": " ++ ++/* Module parameters */ ++static unsigned int reject_percentage = ~0U * .01; ++static unsigned int delude_percentage = ~0U * .0101; ++module_param(reject_percentage, uint, S_IRUGO | S_IWUSR); ++module_param(delude_percentage, uint, S_IRUGO | S_IWUSR); ++ ++/* References to other matches/targets */ ++static struct xt_match *xm_tcp; ++static struct xt_target *xt_delude, *xt_reject, *xt_tarpit; ++ ++static int have_delude, have_tarpit; ++ ++/* Static data for other matches/targets */ ++static const struct ipt_reject_info reject_params = { ++ .with = ICMP_HOST_UNREACH, ++}; ++ ++static const struct xt_tcp tcp_params = { ++ .spts = {0, ~0}, ++ .dpts = {0, ~0}, ++}; ++ ++/* CHAOS functions */ ++static void xt_chaos_total(const struct xt_chaos_target_info *info, ++ struct sk_buff **pskb, const struct net_device *in, ++ const struct net_device *out, unsigned int hooknum) ++{ ++ const struct iphdr *iph = ip_hdr(*pskb); ++ const int protoff = 4 * iph->ihl; ++ const int offset = ntohs(iph->frag_off) & IP_OFFSET; ++ const struct xt_target *destiny; ++ int hotdrop = 0, ret; ++ ++ ret = xm_tcp->match(*pskb, in, out, xm_tcp, &tcp_params, ++ offset, protoff, &hotdrop); ++ if (!ret || hotdrop || (unsigned int)net_random() > delude_percentage) ++ return; ++ ++ destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude; ++ destiny->target(pskb, in, out, hooknum, destiny, NULL); ++ return; ++} ++ ++static unsigned int chaos_tg(struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ unsigned int hooknum, const struct xt_target *target, const void *targinfo) ++{ ++ /* ++ * Equivalent to: ++ * -A chaos -m statistic --mode random --probability \ ++ * $reject_percentage -j REJECT --reject-with host-unreach; ++ * -A chaos -p tcp -m statistic --mode random --probability \ ++ * $delude_percentage -j DELUDE; ++ * -A chaos -j DROP; ++ */ ++ const struct xt_chaos_target_info *info = targinfo; ++ const struct iphdr *iph = ip_hdr(*pskb); ++ ++ if ((unsigned int)net_random() <= reject_percentage) ++ return xt_reject->target(pskb, in, out, hooknum, target, ++ &reject_params); ++ ++ /* TARPIT/DELUDE may not be called from the OUTPUT chain */ ++ if (iph->protocol == IPPROTO_TCP && ++ info->variant != XTCHAOS_NORMAL && hooknum != NF_IP_LOCAL_OUT) ++ xt_chaos_total(info, pskb, in, out, hooknum); ++ ++ return NF_DROP; ++} ++ ++static int chaos_tg_check(const char *tablename, const void *entry, ++ const struct xt_target *target, void *targinfo, unsigned int hook_mask) ++{ ++ const struct xt_chaos_target_info *info = targinfo; ++ ++ if (info->variant == XTCHAOS_DELUDE && !have_delude) { ++ printk(KERN_WARNING PFX "Error: Cannot use --delude when " ++ "DELUDE module not available\n"); ++ return false; ++ } ++ if (info->variant == XTCHAOS_TARPIT && !have_tarpit) { ++ printk(KERN_WARNING PFX "Error: Cannot use --tarpit when " ++ "TARPIT module not available\n"); ++ return false; ++ } ++ ++ return true; ++} ++ ++static struct xt_target chaos_tg_reg = { ++ .name = "CHAOS", ++ .family = AF_INET, ++ .table = "filter", ++ .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | ++ (1 << NF_IP_LOCAL_OUT), ++ .checkentry = chaos_tg_check, ++ .target = chaos_tg, ++ .targetsize = sizeof(struct xt_chaos_target_info), ++ .me = THIS_MODULE, ++}; ++ ++static int __init chaos_tg_init(void) ++{ ++ int ret = -EINVAL; ++ ++ xm_tcp = xt_request_find_match(AF_INET, "tcp", 0); ++ if (xm_tcp == NULL) { ++ printk(KERN_WARNING PFX "Error: Could not find or load " ++ "\"tcp\" match\n"); ++ return -EINVAL; ++ } ++ ++ xt_reject = xt_request_find_target(AF_INET, "REJECT", 0); ++ if (xt_reject == NULL) { ++ printk(KERN_WARNING PFX "Error: Could not find or load " ++ "\"REJECT\" target\n"); ++ goto out2; ++ } ++ ++ xt_tarpit = xt_request_find_target(AF_INET, "TARPIT", 0); ++ have_tarpit = xt_tarpit != NULL; ++ if (!have_tarpit) ++ printk(KERN_WARNING PFX "Warning: Could not find or load " ++ "\"TARPIT\" target\n"); ++ ++ xt_delude = xt_request_find_target(AF_INET, "DELUDE", 0); ++ have_delude = xt_delude != NULL; ++ if (!have_delude) ++ printk(KERN_WARNING PFX "Warning: Could not find or load " ++ "\"DELUDE\" target\n"); ++ ++ if ((ret = xt_register_target(&chaos_tg_reg)) != 0) { ++ printk(KERN_WARNING PFX "xt_register_target returned " ++ "error %d\n", ret); ++ goto out3; ++ } ++ ++ return 0; ++ ++ out3: ++ if (have_delude) ++ module_put(xt_delude->me); ++ if (have_tarpit) ++ module_put(xt_tarpit->me); ++ module_put(xt_reject->me); ++ out2: ++ module_put(xm_tcp->me); ++ return ret; ++} ++ ++static void __exit chaos_tg_exit(void) ++{ ++ xt_unregister_target(&chaos_tg_reg); ++ module_put(xm_tcp->me); ++ module_put(xt_reject->me); ++ if (have_delude) ++ module_put(xt_delude->me); ++ if (have_tarpit) ++ module_put(xt_tarpit->me); ++ return; ++} ++ ++module_init(chaos_tg_init); ++module_exit(chaos_tg_exit); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_DESCRIPTION("netfilter \"CHAOS\" target"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_CHAOS"); +Index: linux-2.6.21.7/net/netfilter/xt_DELUDE.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/xt_DELUDE.c +@@ -0,0 +1,197 @@ ++/* ++ * DELUDE target ++ * Copyright © CC Computer Consultants GmbH, 2007 ++ * Contact: Jan Engelhardt ++ * ++ * Based upon linux-2.6.18.5/net/ipv4/netfilter/ipt_REJECT.c: ++ * (C) 1999-2001 Paul `Rusty' Russell ++ * (C) 2002-2004 Netfilter Core Team ++ * ++ * xt_DELUDE acts like REJECT, but does reply with SYN-ACK on SYN. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_BRIDGE_NETFILTER ++# include ++#endif ++#include ++#define PFX KBUILD_MODNAME ": " ++ ++static void delude_send_reset(struct sk_buff *oldskb, unsigned int hook) ++{ ++ struct tcphdr _otcph, *oth, *tcph; ++ unsigned int addr_type; ++ struct sk_buff *nskb; ++ u_int16_t tmp_port; ++ u_int32_t tmp_addr; ++ struct iphdr *niph; ++ bool needs_ack; ++ ++ /* IP header checks: fragment. */ ++ if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) ++ return; ++ ++ oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), ++ sizeof(_otcph), &_otcph); ++ if (oth == NULL) ++ return; ++ ++ /* No RST for RST. */ ++ if (oth->rst) ++ return; ++ ++ /* Check checksum */ ++ if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) ++ return; ++ ++ /* We need a linear, writeable skb. We also need to expand ++ headroom in case hh_len of incoming interface < hh_len of ++ outgoing interface */ ++ nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), ++ GFP_ATOMIC); ++ if (!nskb) ++ return; ++ ++ /* This packet will not be the same as the other: clear nf fields */ ++ nf_reset(nskb); ++ nskb->mark = 0; ++ skb_init_secmark(nskb); ++ ++ skb_shinfo(nskb)->gso_size = 0; ++ skb_shinfo(nskb)->gso_segs = 0; ++ skb_shinfo(nskb)->gso_type = 0; ++ ++ tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); ++ ++ /* Swap source and dest */ ++ niph = ip_hdr(nskb); ++ tmp_addr = niph->saddr; ++ niph->saddr = niph->daddr; ++ niph->daddr = tmp_addr; ++ tmp_port = tcph->source; ++ tcph->source = tcph->dest; ++ tcph->dest = tmp_port; ++ ++ /* Truncate to length (no data) */ ++ tcph->doff = sizeof(struct tcphdr) / 4; ++ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); ++ niph->tot_len = htons(nskb->len); ++ ++ if (oth->syn && !oth->ack && !oth->rst && !oth->fin) { ++ /* DELUDE essential part */ ++ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + ++ oldskb->len - ip_hdrlen(oldskb) - ++ (oth->doff << 2)); ++ tcph->seq = false; ++ tcph->ack = true; ++ } else { ++ if (!tcph->ack) { ++ needs_ack = true; ++ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + ++ oth->fin + oldskb->len - ++ ip_hdrlen(oldskb) - (oth->doff<<2)); ++ tcph->seq = false; ++ } else { ++ needs_ack = false; ++ tcph->seq = oth->ack_seq; ++ tcph->ack_seq = false; ++ } ++ ++ /* Reset flags */ ++ ((u_int8_t *)tcph)[13] = 0; ++ tcph->rst = true; ++ tcph->ack = needs_ack; ++ } ++ ++ tcph->window = 0; ++ tcph->urg_ptr = 0; ++ ++ /* Adjust TCP checksum */ ++ tcph->check = 0; ++ tcph->check = tcp_v4_check(sizeof(struct tcphdr), niph->saddr, ++ niph->daddr, csum_partial((char *)tcph, ++ sizeof(struct tcphdr), 0)); ++ ++ /* Set DF, id = 0 */ ++ niph->frag_off = htons(IP_DF); ++ niph->id = 0; ++ ++ addr_type = RTN_UNSPEC; ++#ifdef CONFIG_BRIDGE_NETFILTER ++ if (hook != NF_IP_FORWARD || (nskb->nf_bridge != NULL && ++ nskb->nf_bridge->mask & BRNF_BRIDGED)) ++#else ++ if (hook != NF_IP_FORWARD) ++#endif ++ addr_type = RTN_LOCAL; ++ ++ if (ip_route_me_harder(&nskb, addr_type)) ++ goto free_nskb; ++ ++ nskb->ip_summed = CHECKSUM_NONE; ++ ++ /* Adjust IP TTL */ ++ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); ++ ++ /* Adjust IP checksum */ ++ niph->check = 0; ++ niph->check = ip_fast_csum(skb_network_header(nskb), niph->ihl); ++ ++ /* "Never happens" */ ++ if (nskb->len > dst_mtu(nskb->dst)) ++ goto free_nskb; ++ ++ nf_ct_attach(nskb, oldskb); ++ ++ NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev, ++ dst_output); ++ return; ++ ++ free_nskb: ++ kfree_skb(nskb); ++} ++ ++static unsigned int delude_tg(struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ unsigned int hooknum, const struct xt_target *target, const void *targinfo) ++{ ++ /* WARNING: This code causes reentry within iptables. ++ This means that the iptables jump stack is now crap. We ++ must return an absolute verdict. --RR */ ++ delude_send_reset(*pskb, hooknum); ++ return NF_DROP; ++} ++ ++static struct xt_target delude_tg_reg = { ++ .name = "DELUDE", ++ .family = AF_INET, ++ .table = "filter", ++ .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD), ++ .target = delude_tg, ++ .proto = IPPROTO_TCP, ++ .me = THIS_MODULE, ++}; ++ ++static int __init delude_tg_init(void) ++{ ++ return xt_register_target(&delude_tg_reg); ++} ++ ++static void __exit delude_tg_exit(void) ++{ ++ xt_unregister_target(&delude_tg_reg); ++} ++ ++module_init(delude_tg_init); ++module_exit(delude_tg_exit); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_DESCRIPTION("netfilter \"DELUDE\" target"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_DELUDE"); +Index: linux-2.6.21.7/net/netfilter/xt_portscan.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/xt_portscan.c +@@ -0,0 +1,269 @@ ++/* ++ * portscan match for netfilter ++ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 ++ * Contact: Jan Engelhardt ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License; either version ++ * 2 or 3 as published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(_LOCAL) ++# include "xt_portscan.h" ++#elif defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) || \ ++ defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN_MODULE) ++# include ++#else ++# include "xt_portscan.h" ++#endif ++#define PFX KBUILD_MODNAME ": " ++ ++enum { ++ TCP_FLAGS_ALL3 = TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_SYN, ++ TCP_FLAGS_ALL4 = TCP_FLAGS_ALL3 | TCP_FLAG_ACK, ++ TCP_FLAGS_ALL6 = TCP_FLAGS_ALL4 | TCP_FLAG_PSH | TCP_FLAG_URG, ++}; ++ ++/* Module parameters */ ++static unsigned int ++ connmark_mask = ~0, ++ packet_mask = ~0, ++ mark_seen = 0x9, ++ mark_synrcv = 0x1, ++ mark_closed = 0x2, ++ mark_synscan = 0x3, ++ mark_estab1 = 0x4, ++ mark_estab2 = 0x5, ++ mark_cnscan = 0x6, ++ mark_grscan = 0x7, ++ mark_valid = 0x8; ++ ++module_param(connmark_mask, uint, S_IRUGO | S_IWUSR); ++module_param(packet_mask, uint, S_IRUGO | S_IWUSR); ++module_param(mark_seen, uint, S_IRUGO | S_IWUSR); ++module_param(mark_synrcv, uint, S_IRUGO | S_IWUSR); ++module_param(mark_closed, uint, S_IRUGO | S_IWUSR); ++module_param(mark_synscan, uint, S_IRUGO | S_IWUSR); ++module_param(mark_estab1, uint, S_IRUGO | S_IWUSR); ++module_param(mark_estab2, uint, S_IRUGO | S_IWUSR); ++module_param(mark_cnscan, uint, S_IRUGO | S_IWUSR); ++module_param(mark_grscan, uint, S_IRUGO | S_IWUSR); ++module_param(mark_valid, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(connmark_mask, "only set specified bits in connection mark"); ++MODULE_PARM_DESC(packet_mask, "only set specified bits in packet mark"); ++MODULE_PARM_DESC(mark_seen, "nfmark value for packet-seen state"); ++MODULE_PARM_DESC(mark_synrcv, "connmark value for SYN Received state"); ++MODULE_PARM_DESC(mark_closed, "connmark value for closed state"); ++MODULE_PARM_DESC(mark_synscan, "connmark value for SYN Scan state"); ++MODULE_PARM_DESC(mark_estab1, "connmark value for Established-1 state"); ++MODULE_PARM_DESC(mark_estab2, "connmark value for Established-2 state"); ++MODULE_PARM_DESC(mark_cnscan, "connmark value for Connect Scan state"); ++MODULE_PARM_DESC(mark_grscan, "connmark value for Grab Scan state"); ++MODULE_PARM_DESC(mark_valid, "connmark value for Valid state"); ++ ++/* TCP flag functions */ ++static inline bool tflg_ack4(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_ACK; ++} ++ ++static inline bool tflg_ack6(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL6) == TCP_FLAG_ACK; ++} ++ ++static inline bool tflg_fin(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_FIN; ++} ++ ++static inline bool tflg_rst(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_RST; ++} ++ ++static inline bool tflg_rstack(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == ++ (TCP_FLAG_ACK | TCP_FLAG_RST); ++} ++ ++static inline bool tflg_syn(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_SYN; ++} ++ ++static inline bool tflg_synack(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == ++ (TCP_FLAG_SYN | TCP_FLAG_ACK); ++} ++ ++/* portscan functions */ ++static inline bool portscan_mt_stealth(const struct tcphdr *th) ++{ ++ /* ++ * "Connection refused" replies to our own probes must not be matched. ++ */ ++ if (tflg_rstack(th)) ++ return false; ++ ++ if (tflg_rst(th) && printk_ratelimit()) { ++ printk(KERN_WARNING PFX "Warning: Pure RST received\n"); ++ return false; ++ } ++ ++ /* ++ * -p tcp ! --syn -m conntrack --ctstate INVALID: Looking for non-start ++ * packets that are not associated with any connection -- this will ++ * match most scan types (NULL, XMAS, FIN) and ridiculous flag ++ * combinations (SYN-RST, SYN-FIN, SYN-FIN-RST, FIN-RST, etc.). ++ */ ++ return !tflg_syn(th); ++} ++ ++static inline unsigned int portscan_mt_full(int mark, ++ enum ip_conntrack_info ctstate, bool loopback, const struct tcphdr *tcph, ++ unsigned int payload_len) ++{ ++ if (mark == mark_estab2) { ++ /* ++ * -m connmark --mark $ESTAB2 ++ */ ++ if (tflg_ack4(tcph) && payload_len == 0) ++ return mark; /* keep mark */ ++ else if (tflg_rst(tcph) || tflg_fin(tcph)) ++ return mark_grscan; ++ else ++ return mark_valid; ++ } else if (mark == mark_estab1) { ++ /* ++ * -m connmark --mark $ESTAB1 ++ */ ++ if (tflg_rst(tcph) || tflg_fin(tcph)) ++ return mark_cnscan; ++ else if (!loopback && tflg_ack4(tcph) && payload_len == 0) ++ return mark_estab2; ++ else ++ return mark_valid; ++ } else if (mark == mark_synrcv) { ++ /* ++ * -m connmark --mark $SYN ++ */ ++ if (loopback && tflg_synack(tcph)) ++ return mark; /* keep mark */ ++ else if (loopback && tflg_rstack(tcph)) ++ return mark_closed; ++ else if (tflg_ack6(tcph)) ++ return mark_estab1; ++ else ++ return mark_synscan; ++ } else if (ctstate == IP_CT_NEW && tflg_syn(tcph)) { ++ /* ++ * -p tcp --syn --ctstate NEW ++ */ ++ return mark_synrcv; ++ } ++ return mark; ++} ++ ++static int portscan_mt(const struct sk_buff *skb, ++ const struct net_device *in, const struct net_device *out, ++ const struct xt_match *match, const void *matchinfo, int offset, ++ unsigned int protoff, int *hotdrop) ++{ ++ const struct xt_portscan_match_info *info = matchinfo; ++ enum ip_conntrack_info ctstate; ++ const struct tcphdr *tcph; ++ struct nf_conn *ctdata; ++ struct tcphdr tcph_buf; ++ ++ tcph = skb_header_pointer(skb, protoff, sizeof(tcph_buf), &tcph_buf); ++ if (tcph == NULL) ++ return false; ++ ++ /* Check for invalid packets: -m conntrack --ctstate INVALID */ ++ if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) { ++ if (info->match_stealth) ++ return portscan_mt_stealth(tcph); ++ /* ++ * If @ctdata is NULL, we cannot match the other scan ++ * types, return. ++ */ ++ return false; ++ } ++ ++ /* ++ * If -m portscan was previously applied to this packet, the rules we ++ * simulate must not be run through again. And for speedup, do not call ++ * it either when the connection is already VALID. ++ */ ++ if ((ctdata->mark & connmark_mask) == mark_valid || ++ (skb->mark & packet_mask) != mark_seen) { ++ unsigned int n; ++ ++ n = portscan_mt_full(ctdata->mark & connmark_mask, ctstate, ++ in == &loopback_dev, tcph, ++ skb->len - protoff - 4 * tcph->doff); ++ ++ ctdata->mark = (ctdata->mark & ~connmark_mask) | n; ++ ((struct sk_buff *)skb)->mark = ++ (skb->mark & ~packet_mask) ^ mark_seen; ++ } ++ ++ return (info->match_syn && ctdata->mark == mark_synscan) || ++ (info->match_cn && ctdata->mark == mark_cnscan) || ++ (info->match_gr && ctdata->mark == mark_grscan); ++} ++ ++static int portscan_mt_check(const char *tablename, const void *entry, ++ const struct xt_match *match, void *matchinfo, unsigned int hook_mask) ++{ ++ const struct xt_portscan_match_info *info = matchinfo; ++ ++ if ((info->match_stealth & ~1) || (info->match_syn & ~1) || ++ (info->match_cn & ~1) || (info->match_gr & ~1)) { ++ printk(KERN_WARNING PFX "Invalid flags\n"); ++ return false; ++ } ++ return true; ++} ++ ++static struct xt_match portscan_mt_reg __read_mostly = { ++ .name = "portscan", ++ .family = AF_INET, ++ .match = portscan_mt, ++ .checkentry = portscan_mt_check, ++ .matchsize = sizeof(struct xt_portscan_match_info), ++ .proto = IPPROTO_TCP, ++ .me = THIS_MODULE, ++}; ++ ++static int __init portscan_mt_init(void) ++{ ++ return xt_register_match(&portscan_mt_reg); ++} ++ ++static void __exit portscan_mt_exit(void) ++{ ++ xt_unregister_match(&portscan_mt_reg); ++ return; ++} ++ ++module_init(portscan_mt_init); ++module_exit(portscan_mt_exit); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_DESCRIPTION("netfilter \"portscan\" match"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_portscan"); diff --git a/target/linux/generic-2.6/patches-2.6.21/171-netfilter_tarpit.patch b/target/linux/generic-2.6/patches-2.6.21/171-netfilter_tarpit.patch new file mode 100644 index 0000000000..a686bd3d6f --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/171-netfilter_tarpit.patch @@ -0,0 +1,325 @@ +Index: linux-2.6.21.7/net/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/Kconfig ++++ linux-2.6.21.7/net/netfilter/Kconfig +@@ -414,6 +414,23 @@ config NETFILTER_XT_TARGET_CONNSECMARK + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_TARGET_TARPIT ++ tristate '"TARPIT" target support' ++ depends on NETFILTER_XTABLES ++ ---help--- ++ Adds a TARPIT target to iptables, which captures and holds ++ incoming TCP connections using no local per-connection resources. ++ Connections are accepted, but immediately switched to the persist ++ state (0 byte window), in which the remote side stops sending data ++ and asks to continue every 60-240 seconds. Attempts to close the ++ connection are ignored, forcing the remote side to time out the ++ connection in 12-24 minutes. ++ ++ This offers similar functionality to LaBrea ++ , but does not require dedicated ++ hardware or IPs. Any TCP port that you would normally DROP or REJECT ++ can instead become a tarpit. ++ + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' + depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) +Index: linux-2.6.21.7/net/netfilter/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/Makefile ++++ linux-2.6.21.7/net/netfilter/Makefile +@@ -45,6 +45,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE + obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o + obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_TARPIT) += xt_TARPIT.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o +Index: linux-2.6.21.7/net/netfilter/xt_TARPIT.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/netfilter/xt_TARPIT.c +@@ -0,0 +1,280 @@ ++/* ++ * Kernel module to capture and hold incoming TCP connections using ++ * no local per-connection resources. ++ * ++ * Based on ipt_REJECT.c and offering functionality similar to ++ * LaBrea . ++ * ++ * Copyright (c) 2002 Aaron Hopkins ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Goal: ++ * - Allow incoming TCP connections to be established. ++ * - Passing data should result in the connection being switched to the ++ * persist state (0 byte window), in which the remote side stops sending ++ * data and asks to continue every 60 seconds. ++ * - Attempts to shut down the connection should be ignored completely, so ++ * the remote side ends up having to time it out. ++ * ++ * This means: ++ * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes ++ * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing ++ * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++struct in_device; ++#include ++#include ++#include ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++/* Stolen from ip_finish_output2 */ ++static int ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ ++ if (dst->hh != NULL) ++ return neigh_hh_output(dst->hh, skb); ++ else if (dst->neighbour != NULL) ++ return dst->neighbour->output(skb); ++ ++ if (net_ratelimit()) ++ printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n"); ++ ++ kfree_skb(skb); ++ return -EINVAL; ++} ++ ++ ++/* Send reply */ ++static void tarpit_tcp(const struct sk_buff *oskb, struct rtable *ort, ++ unsigned int local) ++{ ++ struct sk_buff *nskb; ++ struct rtable *nrt; ++ struct tcphdr *otcph, *ntcph; ++ struct flowi fl = {}; ++ unsigned int otcplen; ++ u_int16_t tmp; ++ ++ const struct iphdr *oiph = ip_hdr(oskb); ++ struct iphdr *niph; ++ ++ /* A truncated TCP header is not going to be useful */ ++ if (oskb->len < ip_hdrlen(oskb) + sizeof(struct tcphdr)) ++ return; ++ ++ otcph = (void *)oiph + ip_hdrlen(oskb); ++ otcplen = oskb->len - ip_hdrlen(oskb); ++ ++ /* No replies for RST or FIN */ ++ if (otcph->rst || otcph->fin) ++ return; ++ ++ /* No reply to !SYN,!ACK. Rate-limit replies to !SYN,ACKs */ ++ if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ))) ++ return; ++ ++ /* Check checksum. */ ++ if (tcp_v4_check(otcplen, oiph->saddr, oiph->daddr, ++ csum_partial((char *)otcph, otcplen, 0)) != 0) ++ return; ++ ++ /* ++ * Copy skb (even if skb is about to be dropped, we cannot just ++ * clone it because there may be other things, such as tcpdump, ++ * interested in it) ++ */ ++ nskb = skb_copy(oskb, GFP_ATOMIC); ++ if (nskb == NULL) ++ return; ++ ++ niph = ip_hdr(nskb); ++ ++ /* This packet will not be the same as the other: clear nf fields */ ++ nf_conntrack_put(nskb->nfct); ++ nskb->nfct = NULL; ++#ifdef CONFIG_NETFILTER_DEBUG ++ nskb->nf_debug = 0; ++#endif ++ ++ ntcph = (void *)niph + ip_hdrlen(nskb); ++ ++ /* Truncate to length (no data) */ ++ ntcph->doff = sizeof(struct tcphdr)/4; ++ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); ++ niph->tot_len = htons(nskb->len); ++ ++ /* Swap source and dest */ ++ niph->daddr = xchg(&niph->saddr, niph->daddr); ++ tmp = ntcph->source; ++ ntcph->source = ntcph->dest; ++ ntcph->dest = tmp; ++ ++ /* Use supplied sequence number or make a new one */ ++ ntcph->seq = otcph->ack ? otcph->ack_seq ++ : htonl(secure_tcp_sequence_number(niph->saddr, ++ niph->daddr, ++ ntcph->source, ++ ntcph->dest)); ++ ++ /* Our SYN-ACKs must have a >0 window */ ++ ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0; ++ ++ ntcph->urg_ptr = 0; ++ ++ /* Reset flags */ ++ ((u_int8_t *)ntcph)[13] = 0; ++ ++ if (otcph->syn && otcph->ack) { ++ ntcph->rst = 1; ++ ntcph->ack_seq = 0; ++ } else { ++ ntcph->syn = otcph->syn; ++ ntcph->ack = 1; ++ ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn); ++ } ++ ++ /* Adjust TCP checksum */ ++ ntcph->check = 0; ++ ntcph->check = tcp_v4_check(sizeof(struct tcphdr), ++ niph->saddr, ++ niph->daddr, ++ csum_partial((char *)ntcph, ++ sizeof(struct tcphdr), 0)); ++ ++ fl.nl_u.ip4_u.daddr = niph->daddr; ++ fl.nl_u.ip4_u.saddr = local ? niph->saddr : 0; ++ fl.nl_u.ip4_u.tos = RT_TOS(niph->tos) | RTO_CONN; ++ fl.oif = 0; ++ ++ if (ip_route_output_key(&nrt, &fl)) ++ goto free_nskb; ++ ++ dst_release(nskb->dst); ++ nskb->dst = &nrt->u.dst; ++ ++ /* Adjust IP TTL */ ++ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); ++ ++ /* Set DF, id = 0 */ ++ niph->frag_off = htons(IP_DF); ++ niph->id = 0; ++ ++ /* Adjust IP checksum */ ++ niph->check = 0; ++ niph->check = ip_fast_csum((unsigned char *)niph, niph->ihl); ++ ++ /* "Never happens" */ ++ if (nskb->len > dst_mtu(nskb->dst)) ++ goto free_nskb; ++ ++ ip_direct_send(nskb); ++ return; ++ ++ free_nskb: ++ kfree_skb(nskb); ++} ++ ++static unsigned int xt_tarpit_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++{ ++ const struct sk_buff *skb = *pskb; ++ const struct iphdr *iph = ip_hdr(skb); ++ struct rtable *rt = (void *)skb->dst; ++ ++ /* Do we have an input route cache entry? */ ++ if (rt == NULL) ++ return NF_DROP; ++ ++ /* No replies to physical multicast/broadcast */ ++ if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST) ++ return NF_DROP; ++ ++ /* Now check at the protocol level */ ++ if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) ++ return NF_DROP; ++ ++ /* ++ * Our naive response construction does not deal with IP ++ * options, and probably should not try. ++ */ ++ if (iph->ihl * 4 != sizeof(struct iphdr)) ++ return NF_DROP; ++ ++ /* We are not interested in fragments */ ++ if (iph->frag_off & htons(IP_OFFSET)) ++ return NF_DROP; ++ ++ tarpit_tcp(skb, rt, hooknum == NF_IP_LOCAL_IN); ++ return NF_DROP; ++} ++ ++static int xt_tarpit_check(const char *tablename, const void *entry, ++ const struct xt_target *target, void *targinfo, ++ unsigned int hook_mask) ++{ ++ bool invalid; ++ ++ if (strcmp(tablename, "raw") == 0 && hook_mask == NF_IP_PRE_ROUTING) ++ return true; ++ if (strcmp(tablename, "filter") != 0) ++ return false; ++ invalid = hook_mask & ~((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD)); ++ return !invalid; ++} ++ ++static struct xt_target xt_tarpit_reg = { ++ .name = "TARPIT", ++ .family = AF_INET, ++ .proto = IPPROTO_TCP, ++ .target = xt_tarpit_target, ++ .checkentry = xt_tarpit_check, ++ .me = THIS_MODULE, ++}; ++ ++static int __init xt_tarpit_init(void) ++{ ++ return xt_register_target(&xt_tarpit_reg); ++} ++ ++static void __exit xt_tarpit_exit(void) ++{ ++ xt_unregister_target(&xt_tarpit_reg); ++} ++ ++module_init(xt_tarpit_init); ++module_exit(xt_tarpit_exit); ++MODULE_DESCRIPTION("netfilter xt_TARPIT target module"); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_TARPIT"); diff --git a/target/linux/generic-2.6/patches-2.6.21/180-netfilter_depends.patch b/target/linux/generic-2.6/patches-2.6.21/180-netfilter_depends.patch new file mode 100644 index 0000000000..e155ecc3c4 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/180-netfilter_depends.patch @@ -0,0 +1,22 @@ +Index: linux-2.6.21.7/net/netfilter/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/netfilter/Kconfig ++++ linux-2.6.21.7/net/netfilter/Kconfig +@@ -165,7 +165,7 @@ config NF_CONNTRACK_FTP + + config NF_CONNTRACK_H323 + tristate "H.323 protocol support (EXPERIMENTAL)" +- depends on EXPERIMENTAL && NF_CONNTRACK && (IPV6 || IPV6=n) ++ depends on EXPERIMENTAL && NF_CONNTRACK + help + H.323 is a VoIP signalling protocol from ITU-T. As one of the most + important VoIP protocols, it is widely used by voice hardware and +@@ -433,7 +433,7 @@ config NETFILTER_XT_TARGET_TARPIT + + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' +- depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) ++ depends on NETFILTER_XTABLES + ---help--- + This option adds a `TCPMSS' target, which allows you to alter the + MSS value of TCP SYN packets, to control the maximum size for that diff --git a/target/linux/generic-2.6/patches-2.6.21/200-sched_esfq.patch b/target/linux/generic-2.6/patches-2.6.21/200-sched_esfq.patch new file mode 100644 index 0000000000..38efcea6d5 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/200-sched_esfq.patch @@ -0,0 +1,797 @@ +Index: linux-2.6.21.7/include/linux/pkt_sched.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/pkt_sched.h ++++ linux-2.6.21.7/include/linux/pkt_sched.h +@@ -146,8 +146,40 @@ struct tc_sfq_qopt + * + * The only reason for this is efficiency, it is possible + * to change these parameters in compile time. ++ * ++ * If you need to play with these values use esfq instead. + */ + ++/* ESFQ section */ ++ ++enum ++{ ++ /* traditional */ ++ TCA_SFQ_HASH_CLASSIC, ++ TCA_SFQ_HASH_DST, ++ TCA_SFQ_HASH_SRC, ++ TCA_SFQ_HASH_FWMARK, ++ /* direct */ ++ TCA_SFQ_HASH_DSTDIR, ++ TCA_SFQ_HASH_SRCDIR, ++ TCA_SFQ_HASH_FWMARKDIR, ++ /* conntrack */ ++ TCA_SFQ_HASH_CTORIGDST, ++ TCA_SFQ_HASH_CTORIGSRC, ++ TCA_SFQ_HASH_CTREPLDST, ++ TCA_SFQ_HASH_CTREPLSRC, ++}; ++ ++struct tc_esfq_qopt ++{ ++ unsigned quantum; /* Bytes per round allocated to flow */ ++ int perturb_period; /* Period of hash perturbation */ ++ __u32 limit; /* Maximal packets in queue */ ++ unsigned divisor; /* Hash divisor */ ++ unsigned flows; /* Maximal number of flows */ ++ unsigned hash_kind; /* Hash function to use for flow identification */ ++}; ++ + /* RED section */ + + enum +Index: linux-2.6.21.7/net/sched/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/net/sched/Kconfig ++++ linux-2.6.21.7/net/sched/Kconfig +@@ -189,6 +189,26 @@ config NET_SCH_SFQ + To compile this code as a module, choose M here: the + module will be called sch_sfq. + ++config NET_SCH_ESFQ ++ tristate "Enhanced Stochastic Fairness Queueing (ESFQ)" ++ ---help--- ++ Say Y here if you want to use the Enhanced Stochastic Fairness ++ Queueing (ESFQ) packet scheduling algorithm for some of your network ++ devices or as a leaf discipline for a classful qdisc such as HTB or ++ CBQ (see the top of for details and ++ references to the SFQ algorithm). ++ ++ This is an enchanced SFQ version which allows you to control some ++ hardcoded values in the SFQ scheduler. ++ ++ ESFQ also adds control of the hash function used to identify packet ++ flows. The original SFQ discipline hashes by connection; ESFQ add ++ several other hashing methods, such as by src IP or by dst IP, which ++ can be more fair to users in some networking situations. ++ ++ To compile this code as a module, choose M here: the ++ module will be called sch_esfq. ++ + config NET_SCH_TEQL + tristate "True Link Equalizer (TEQL)" + ---help--- +Index: linux-2.6.21.7/net/sched/Makefile +=================================================================== +--- linux-2.6.21.7.orig/net/sched/Makefile ++++ linux-2.6.21.7/net/sched/Makefile +@@ -23,6 +23,7 @@ obj-$(CONFIG_NET_SCH_GRED) += sch_gred.o + obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o + obj-$(CONFIG_NET_SCH_DSMARK) += sch_dsmark.o + obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o ++obj-$(CONFIG_NET_SCH_ESFQ) += sch_esfq.o + obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o + obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o + obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o +Index: linux-2.6.21.7/net/sched/sch_esfq.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/net/sched/sch_esfq.c +@@ -0,0 +1,704 @@ ++/* ++ * net/sched/sch_esfq.c Extended Stochastic Fairness Queueing discipline. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Authors: Alexey Kuznetsov, ++ * ++ * Changes: Alexander Atanasov, ++ * Added dynamic depth,limit,divisor,hash_kind options. ++ * Added dst and src hashes. ++ * ++ * Alexander Clouter, ++ * Ported ESFQ to Linux 2.6. ++ * ++ * Corey Hickey, ++ * Maintenance of the Linux 2.6 port. ++ * Added fwmark hash (thanks to Robert Kurjata). ++ * Added direct hashing for src, dst, and fwmark. ++ * Added usage of jhash. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_NF_CONNTRACK_ENABLED ++#include ++#endif ++ ++/* Stochastic Fairness Queuing algorithm. ++ For more comments look at sch_sfq.c. ++ The difference is that you can change limit, depth, ++ hash table size and choose alternate hash types. ++ ++ classic: same as in sch_sfq.c ++ dst: destination IP address ++ src: source IP address ++ fwmark: netfilter mark value ++ dst_direct: ++ src_direct: ++ fwmark_direct: direct hashing of the above sources ++ ctorigdst: original destination IP address ++ ctorigsrc: original source IP address ++ ctrepldst: reply destination IP address ++ ctreplsrc: reply source IP ++ ++*/ ++ ++ ++/* This type should contain at least SFQ_DEPTH*2 values */ ++typedef unsigned int esfq_index; ++ ++struct esfq_head ++{ ++ esfq_index next; ++ esfq_index prev; ++}; ++ ++struct esfq_sched_data ++{ ++/* Parameters */ ++ int perturb_period; ++ unsigned quantum; /* Allotment per round: MUST BE >= MTU */ ++ int limit; ++ unsigned depth; ++ unsigned hash_divisor; ++ unsigned hash_kind; ++/* Variables */ ++ struct timer_list perturb_timer; ++ int perturbation; ++ esfq_index tail; /* Index of current slot in round */ ++ esfq_index max_depth; /* Maximal depth */ ++ ++ esfq_index *ht; /* Hash table */ ++ esfq_index *next; /* Active slots link */ ++ short *allot; /* Current allotment per slot */ ++ unsigned short *hash; /* Hash value indexed by slots */ ++ struct sk_buff_head *qs; /* Slot queue */ ++ struct esfq_head *dep; /* Linked list of slots, indexed by depth */ ++ unsigned dyn_min; /* For dynamic divisor adjustment; minimum value seen */ ++ unsigned dyn_max; /* maximum value seen */ ++ unsigned dyn_range; /* saved range */ ++}; ++ ++/* This contains the info we will hash. */ ++struct esfq_packet_info ++{ ++ u32 proto; /* protocol or port */ ++ u32 src; /* source from packet header */ ++ u32 dst; /* destination from packet header */ ++ u32 ctorigsrc; /* original source from conntrack */ ++ u32 ctorigdst; /* original destination from conntrack */ ++ u32 ctreplsrc; /* reply source from conntrack */ ++ u32 ctrepldst; /* reply destination from conntrack */ ++ u32 mark; /* netfilter mark (fwmark) */ ++}; ++ ++/* Hash input values directly into the "nearest" slot, taking into account the ++ * range of input values seen. This is most useful when the hash table is at ++ * least as large as the range of possible values. ++ * Note: this functionality was added before the change to using jhash, and may ++ * no longer be useful. */ ++static __inline__ unsigned esfq_hash_direct(struct esfq_sched_data *q, u32 h) ++{ ++ /* adjust minimum and maximum */ ++ if (h < q->dyn_min || h > q->dyn_max) { ++ q->dyn_min = h < q->dyn_min ? h : q->dyn_min; ++ q->dyn_max = h > q->dyn_max ? h : q->dyn_max; ++ ++ /* find new range */ ++ if ((q->dyn_range = q->dyn_max - q->dyn_min) >= q->hash_divisor) ++ printk(KERN_WARNING "ESFQ: (direct hash) Input range %u is larger than hash " ++ "table. See ESFQ README for details.\n", q->dyn_range); ++ } ++ ++ /* hash input values into slot numbers */ ++ if (q->dyn_min == q->dyn_max) ++ return 0; /* only one value seen; avoid division by 0 */ ++ else ++ return (h - q->dyn_min) * (q->hash_divisor - 1) / q->dyn_range; ++} ++ ++static __inline__ unsigned esfq_jhash_1word(struct esfq_sched_data *q,u32 a) ++{ ++ return jhash_1word(a, q->perturbation) & (q->hash_divisor-1); ++} ++ ++static __inline__ unsigned esfq_jhash_2words(struct esfq_sched_data *q, u32 a, u32 b) ++{ ++ return jhash_2words(a, b, q->perturbation) & (q->hash_divisor-1); ++} ++ ++static __inline__ unsigned esfq_jhash_3words(struct esfq_sched_data *q, u32 a, u32 b, u32 c) ++{ ++ return jhash_3words(a, b, c, q->perturbation) & (q->hash_divisor-1); ++} ++ ++ ++static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb) ++{ ++ struct esfq_packet_info info; ++#ifdef CONFIG_NF_CONNTRACK_ENABLED ++ enum ip_conntrack_info ctinfo; ++ struct nf_conn *ct = nf_ct_get(skb, &ctinfo); ++#endif ++ ++ switch (skb->protocol) { ++ case __constant_htons(ETH_P_IP): ++ { ++ struct iphdr *iph = skb->nh.iph; ++ info.dst = iph->daddr; ++ info.src = iph->saddr; ++ if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && ++ (iph->protocol == IPPROTO_TCP || ++ iph->protocol == IPPROTO_UDP || ++ iph->protocol == IPPROTO_SCTP || ++ iph->protocol == IPPROTO_DCCP || ++ iph->protocol == IPPROTO_ESP)) ++ info.proto = *(((u32*)iph) + iph->ihl); ++ else ++ info.proto = iph->protocol; ++ break; ++ } ++ case __constant_htons(ETH_P_IPV6): ++ { ++ struct ipv6hdr *iph = skb->nh.ipv6h; ++ /* Hash ipv6 addresses into a u32. This isn't ideal, ++ * but the code is simple. */ ++ info.dst = jhash2(iph->daddr.s6_addr32, 4, q->perturbation); ++ info.src = jhash2(iph->saddr.s6_addr32, 4, q->perturbation); ++ if (iph->nexthdr == IPPROTO_TCP || ++ iph->nexthdr == IPPROTO_UDP || ++ iph->nexthdr == IPPROTO_SCTP || ++ iph->nexthdr == IPPROTO_DCCP || ++ iph->nexthdr == IPPROTO_ESP) ++ info.proto = *(u32*)&iph[1]; ++ else ++ info.proto = iph->nexthdr; ++ break; ++ } ++ default: ++ info.dst = (u32)(unsigned long)skb->dst; ++ info.src = (u32)(unsigned long)skb->sk; ++ info.proto = skb->protocol; ++ } ++ ++ info.mark = skb->mark; ++ ++#ifdef CONFIG_NF_CONNTRACK_ENABLED ++ /* defaults if there is no conntrack info */ ++ info.ctorigsrc = info.src; ++ info.ctorigdst = info.dst; ++ info.ctreplsrc = info.dst; ++ info.ctrepldst = info.src; ++ /* collect conntrack info */ ++ if (ct && ct != &nf_conntrack_untracked) { ++ if (skb->protocol == __constant_htons(ETH_P_IP)) { ++ info.ctorigsrc = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; ++ info.ctorigdst = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip; ++ info.ctreplsrc = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip; ++ info.ctrepldst = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; ++ } ++ else if (skb->protocol == __constant_htons(ETH_P_IPV6)) { ++ /* Again, hash ipv6 addresses into a single u32. */ ++ info.ctorigsrc = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6, 4, q->perturbation); ++ info.ctorigdst = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip6, 4, q->perturbation); ++ info.ctreplsrc = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6, 4, q->perturbation); ++ info.ctrepldst = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6, 4, q->perturbation); ++ } ++ ++ } ++#endif ++ ++ switch(q->hash_kind) ++ { ++ case TCA_SFQ_HASH_CLASSIC: ++ return esfq_jhash_3words(q, info.dst, info.src, info.proto); ++ case TCA_SFQ_HASH_DST: ++ return esfq_jhash_1word(q, info.dst); ++ case TCA_SFQ_HASH_DSTDIR: ++ return esfq_hash_direct(q, ntohl(info.dst)); ++ case TCA_SFQ_HASH_SRC: ++ return esfq_jhash_1word(q, info.src); ++ case TCA_SFQ_HASH_SRCDIR: ++ return esfq_hash_direct(q, ntohl(info.src)); ++ case TCA_SFQ_HASH_FWMARK: ++ return esfq_jhash_1word(q, info.mark); ++ case TCA_SFQ_HASH_FWMARKDIR: ++ return esfq_hash_direct(q, info.mark); ++#ifdef CONFIG_NF_CONNTRACK_ENABLED ++ case TCA_SFQ_HASH_CTORIGDST: ++ return esfq_jhash_1word(q, info.ctorigdst); ++ case TCA_SFQ_HASH_CTORIGSRC: ++ return esfq_jhash_1word(q, info.ctorigsrc); ++ case TCA_SFQ_HASH_CTREPLDST: ++ return esfq_jhash_1word(q, info.ctrepldst); ++ case TCA_SFQ_HASH_CTREPLSRC: ++ return esfq_jhash_1word(q, info.ctreplsrc); ++#endif ++ default: ++ if (net_ratelimit()) ++ printk(KERN_WARNING "ESFQ: Unknown hash method. Falling back to classic.\n"); ++ } ++ return esfq_jhash_3words(q, info.dst, info.src, info.proto); ++} ++ ++static inline void esfq_link(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ int d = q->qs[x].qlen + q->depth; ++ ++ p = d; ++ n = q->dep[d].next; ++ q->dep[x].next = n; ++ q->dep[x].prev = p; ++ q->dep[p].next = q->dep[n].prev = x; ++} ++ ++static inline void esfq_dec(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ ++ n = q->dep[x].next; ++ p = q->dep[x].prev; ++ q->dep[p].next = n; ++ q->dep[n].prev = p; ++ ++ if (n == p && q->max_depth == q->qs[x].qlen + 1) ++ q->max_depth--; ++ ++ esfq_link(q, x); ++} ++ ++static inline void esfq_inc(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ int d; ++ ++ n = q->dep[x].next; ++ p = q->dep[x].prev; ++ q->dep[p].next = n; ++ q->dep[n].prev = p; ++ d = q->qs[x].qlen; ++ if (q->max_depth < d) ++ q->max_depth = d; ++ ++ esfq_link(q, x); ++} ++ ++static unsigned int esfq_drop(struct Qdisc *sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_index d = q->max_depth; ++ struct sk_buff *skb; ++ unsigned int len; ++ ++ /* Queue is full! Find the longest slot and ++ drop a packet from it */ ++ ++ if (d > 1) { ++ esfq_index x = q->dep[d+q->depth].next; ++ skb = q->qs[x].prev; ++ len = skb->len; ++ __skb_unlink(skb, &q->qs[x]); ++ kfree_skb(skb); ++ esfq_dec(q, x); ++ sch->q.qlen--; ++ sch->qstats.drops++; ++ sch->qstats.backlog -= len; ++ return len; ++ } ++ ++ if (d == 1) { ++ /* It is difficult to believe, but ALL THE SLOTS HAVE LENGTH 1. */ ++ d = q->next[q->tail]; ++ q->next[q->tail] = q->next[d]; ++ q->allot[q->next[d]] += q->quantum; ++ skb = q->qs[d].prev; ++ len = skb->len; ++ __skb_unlink(skb, &q->qs[d]); ++ kfree_skb(skb); ++ esfq_dec(q, d); ++ sch->q.qlen--; ++ q->ht[q->hash[d]] = q->depth; ++ sch->qstats.drops++; ++ sch->qstats.backlog -= len; ++ return len; ++ } ++ ++ return 0; ++} ++ ++static int ++esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ unsigned hash = esfq_hash(q, skb); ++ unsigned depth = q->depth; ++ esfq_index x; ++ ++ x = q->ht[hash]; ++ if (x == depth) { ++ q->ht[hash] = x = q->dep[depth].next; ++ q->hash[x] = hash; ++ } ++ sch->qstats.backlog += skb->len; ++ __skb_queue_tail(&q->qs[x], skb); ++ esfq_inc(q, x); ++ if (q->qs[x].qlen == 1) { /* The flow is new */ ++ if (q->tail == depth) { /* It is the first flow */ ++ q->tail = x; ++ q->next[x] = x; ++ q->allot[x] = q->quantum; ++ } else { ++ q->next[x] = q->next[q->tail]; ++ q->next[q->tail] = x; ++ q->tail = x; ++ } ++ } ++ if (++sch->q.qlen < q->limit-1) { ++ sch->bstats.bytes += skb->len; ++ sch->bstats.packets++; ++ return 0; ++ } ++ ++ esfq_drop(sch); ++ return NET_XMIT_CN; ++} ++ ++static int ++esfq_requeue(struct sk_buff *skb, struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ unsigned hash = esfq_hash(q, skb); ++ unsigned depth = q->depth; ++ esfq_index x; ++ ++ x = q->ht[hash]; ++ if (x == depth) { ++ q->ht[hash] = x = q->dep[depth].next; ++ q->hash[x] = hash; ++ } ++ sch->qstats.backlog += skb->len; ++ __skb_queue_head(&q->qs[x], skb); ++ esfq_inc(q, x); ++ if (q->qs[x].qlen == 1) { /* The flow is new */ ++ if (q->tail == depth) { /* It is the first flow */ ++ q->tail = x; ++ q->next[x] = x; ++ q->allot[x] = q->quantum; ++ } else { ++ q->next[x] = q->next[q->tail]; ++ q->next[q->tail] = x; ++ q->tail = x; ++ } ++ } ++ if (++sch->q.qlen < q->limit - 1) { ++ sch->qstats.requeues++; ++ return 0; ++ } ++ ++ sch->qstats.drops++; ++ esfq_drop(sch); ++ return NET_XMIT_CN; ++} ++ ++ ++ ++ ++static struct sk_buff * ++esfq_dequeue(struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct sk_buff *skb; ++ unsigned depth = q->depth; ++ esfq_index a, old_a; ++ ++ /* No active slots */ ++ if (q->tail == depth) ++ return NULL; ++ ++ a = old_a = q->next[q->tail]; ++ ++ /* Grab packet */ ++ skb = __skb_dequeue(&q->qs[a]); ++ esfq_dec(q, a); ++ sch->q.qlen--; ++ sch->qstats.backlog -= skb->len; ++ ++ /* Is the slot empty? */ ++ if (q->qs[a].qlen == 0) { ++ q->ht[q->hash[a]] = depth; ++ a = q->next[a]; ++ if (a == old_a) { ++ q->tail = depth; ++ return skb; ++ } ++ q->next[q->tail] = a; ++ q->allot[a] += q->quantum; ++ } else if ((q->allot[a] -= skb->len) <= 0) { ++ q->tail = a; ++ a = q->next[a]; ++ q->allot[a] += q->quantum; ++ } ++ ++ return skb; ++} ++ ++static void ++esfq_reset(struct Qdisc* sch) ++{ ++ struct sk_buff *skb; ++ ++ while ((skb = esfq_dequeue(sch)) != NULL) ++ kfree_skb(skb); ++} ++ ++static void esfq_perturbation(unsigned long arg) ++{ ++ struct Qdisc *sch = (struct Qdisc*)arg; ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ ++ q->perturbation = net_random()&0x1F; ++ ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } ++} ++ ++static int esfq_change(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct tc_esfq_qopt *ctl = RTA_DATA(opt); ++ int old_perturb = q->perturb_period; ++ ++ if (opt->rta_len < RTA_LENGTH(sizeof(*ctl))) ++ return -EINVAL; ++ ++ sch_tree_lock(sch); ++ q->quantum = ctl->quantum ? : psched_mtu(sch->dev); ++ q->perturb_period = ctl->perturb_period*HZ; ++// q->hash_divisor = ctl->divisor; ++// q->tail = q->limit = q->depth = ctl->flows; ++ ++ if (ctl->limit) ++ q->limit = min_t(u32, ctl->limit, q->depth); ++ ++ if (ctl->hash_kind) { ++ q->hash_kind = ctl->hash_kind; ++ if (q->hash_kind != TCA_SFQ_HASH_CLASSIC) ++ q->perturb_period = 0; ++ } ++ ++ // is sch_tree_lock enough to do this ? ++ while (sch->q.qlen >= q->limit-1) ++ esfq_drop(sch); ++ ++ if (old_perturb) ++ del_timer(&q->perturb_timer); ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } else { ++ q->perturbation = 0; ++ } ++ sch_tree_unlock(sch); ++ return 0; ++} ++ ++static int esfq_init(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct tc_esfq_qopt *ctl; ++ esfq_index p = ~0U/2; ++ int i; ++ ++ if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl))) ++ return -EINVAL; ++ ++ init_timer(&q->perturb_timer); ++ q->perturb_timer.data = (unsigned long)sch; ++ q->perturb_timer.function = esfq_perturbation; ++ q->perturbation = 0; ++ q->hash_kind = TCA_SFQ_HASH_CLASSIC; ++ q->max_depth = 0; ++ q->dyn_min = ~0U; /* maximum value for this type */ ++ q->dyn_max = 0; /* dyn_min/dyn_max will be set properly upon first packet */ ++ if (opt == NULL) { ++ q->quantum = psched_mtu(sch->dev); ++ q->perturb_period = 0; ++ q->hash_divisor = 1024; ++ q->tail = q->limit = q->depth = 128; ++ ++ } else { ++ ctl = RTA_DATA(opt); ++ q->quantum = ctl->quantum ? : psched_mtu(sch->dev); ++ q->perturb_period = ctl->perturb_period*HZ; ++ q->hash_divisor = ctl->divisor ? : 1024; ++ q->tail = q->limit = q->depth = ctl->flows ? : 128; ++ ++ if ( q->depth > p - 1 ) ++ return -EINVAL; ++ ++ if (ctl->limit) ++ q->limit = min_t(u32, ctl->limit, q->depth); ++ ++ if (ctl->hash_kind) { ++ q->hash_kind = ctl->hash_kind; ++ } ++ ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } ++ } ++ ++ q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL); ++ if (!q->ht) ++ goto err_case; ++ ++ q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL); ++ if (!q->dep) ++ goto err_case; ++ q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL); ++ if (!q->next) ++ goto err_case; ++ ++ q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL); ++ if (!q->allot) ++ goto err_case; ++ q->hash = kmalloc(q->depth*sizeof(unsigned short), GFP_KERNEL); ++ if (!q->hash) ++ goto err_case; ++ q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL); ++ if (!q->qs) ++ goto err_case; ++ ++ for (i=0; i< q->hash_divisor; i++) ++ q->ht[i] = q->depth; ++ for (i=0; idepth; i++) { ++ skb_queue_head_init(&q->qs[i]); ++ q->dep[i+q->depth].next = i+q->depth; ++ q->dep[i+q->depth].prev = i+q->depth; ++ } ++ ++ for (i=0; idepth; i++) ++ esfq_link(q, i); ++ return 0; ++err_case: ++ del_timer(&q->perturb_timer); ++ if (q->ht) ++ kfree(q->ht); ++ if (q->dep) ++ kfree(q->dep); ++ if (q->next) ++ kfree(q->next); ++ if (q->allot) ++ kfree(q->allot); ++ if (q->hash) ++ kfree(q->hash); ++ if (q->qs) ++ kfree(q->qs); ++ return -ENOBUFS; ++} ++ ++static void esfq_destroy(struct Qdisc *sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ del_timer(&q->perturb_timer); ++ if(q->ht) ++ kfree(q->ht); ++ if(q->dep) ++ kfree(q->dep); ++ if(q->next) ++ kfree(q->next); ++ if(q->allot) ++ kfree(q->allot); ++ if(q->hash) ++ kfree(q->hash); ++ if(q->qs) ++ kfree(q->qs); ++} ++ ++static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ unsigned char *b = skb->tail; ++ struct tc_esfq_qopt opt; ++ ++ opt.quantum = q->quantum; ++ opt.perturb_period = q->perturb_period/HZ; ++ ++ opt.limit = q->limit; ++ opt.divisor = q->hash_divisor; ++ opt.flows = q->depth; ++ opt.hash_kind = q->hash_kind; ++ ++ RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); ++ ++ return skb->len; ++ ++rtattr_failure: ++ skb_trim(skb, b - skb->data); ++ return -1; ++} ++ ++static struct Qdisc_ops esfq_qdisc_ops = ++{ ++ .next = NULL, ++ .cl_ops = NULL, ++ .id = "esfq", ++ .priv_size = sizeof(struct esfq_sched_data), ++ .enqueue = esfq_enqueue, ++ .dequeue = esfq_dequeue, ++ .requeue = esfq_requeue, ++ .drop = esfq_drop, ++ .init = esfq_init, ++ .reset = esfq_reset, ++ .destroy = esfq_destroy, ++ .change = NULL, /* esfq_change - needs more work */ ++ .dump = esfq_dump, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init esfq_module_init(void) ++{ ++ return register_qdisc(&esfq_qdisc_ops); ++} ++static void __exit esfq_module_exit(void) ++{ ++ unregister_qdisc(&esfq_qdisc_ops); ++} ++module_init(esfq_module_init) ++module_exit(esfq_module_exit) ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/patches-2.6.21/201-multiple_default_gateways.patch b/target/linux/generic-2.6/patches-2.6.21/201-multiple_default_gateways.patch new file mode 100644 index 0000000000..b0acd9a6fa --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/201-multiple_default_gateways.patch @@ -0,0 +1,360 @@ +Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_nat.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/netfilter_ipv4/ip_nat.h ++++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_nat.h +@@ -64,6 +64,13 @@ struct ip_nat_info + + struct ip_conntrack; + ++/* Call input routing for SNAT-ed traffic */ ++extern unsigned int ip_nat_route_input(unsigned int hooknum, ++ struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++ + /* Set up the info structure to map into this range. */ + extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack, + const struct ip_nat_range *range, +Index: linux-2.6.21.7/include/linux/rtnetlink.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/rtnetlink.h ++++ linux-2.6.21.7/include/linux/rtnetlink.h +@@ -293,6 +293,8 @@ struct rtnexthop + #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ + #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ + #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ ++#define RTNH_F_SUSPECT 8 /* We don't know the real state */ ++#define RTNH_F_BADSTATE (RTNH_F_DEAD | RTNH_F_SUSPECT) + + /* Macros to handle hexthops */ + +Index: linux-2.6.21.7/include/net/flow.h +=================================================================== +--- linux-2.6.21.7.orig/include/net/flow.h ++++ linux-2.6.21.7/include/net/flow.h +@@ -19,6 +19,8 @@ struct flowi { + struct { + __be32 daddr; + __be32 saddr; ++ __u32 lsrc; ++ __u32 gw; + __u8 tos; + __u8 scope; + } ip4_u; +@@ -43,6 +45,8 @@ struct flowi { + #define fl6_flowlabel nl_u.ip6_u.flowlabel + #define fl4_dst nl_u.ip4_u.daddr + #define fl4_src nl_u.ip4_u.saddr ++#define fl4_lsrc nl_u.ip4_u.lsrc ++#define fl4_gw nl_u.ip4_u.gw + #define fl4_tos nl_u.ip4_u.tos + #define fl4_scope nl_u.ip4_u.scope + +Index: linux-2.6.21.7/net/ipv4/route.c +=================================================================== +--- linux-2.6.21.7.orig/net/ipv4/route.c ++++ linux-2.6.21.7/net/ipv4/route.c +@@ -1208,6 +1208,7 @@ void ip_rt_redirect(__be32 old_gw, __be3 + + /* Gateway is different ... */ + rt->rt_gateway = new_gw; ++ if (rt->fl.fl4_gw) rt->fl.fl4_gw = new_gw; + + /* Redirect received -> path was valid */ + dst_confirm(&rth->u.dst); +@@ -1643,6 +1644,7 @@ static int ip_route_input_mc(struct sk_b + rth->fl.fl4_tos = tos; + rth->fl.mark = skb->mark; + rth->fl.fl4_src = saddr; ++ rth->fl.fl4_lsrc = 0; + rth->rt_src = saddr; + #ifdef CONFIG_NET_CLS_ROUTE + rth->u.dst.tclassid = itag; +@@ -1653,6 +1655,7 @@ static int ip_route_input_mc(struct sk_b + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); + rth->fl.oif = 0; ++ rth->fl.fl4_gw = 0; + rth->rt_gateway = daddr; + rth->rt_spec_dst= spec_dst; + rth->rt_type = RTN_MULTICAST; +@@ -1716,7 +1719,7 @@ static void ip_handle_martian_source(str + static inline int __mkroute_input(struct sk_buff *skb, + struct fib_result* res, + struct in_device *in_dev, +- __be32 daddr, __be32 saddr, u32 tos, ++ __be32 daddr, __be32 saddr, u32 tos, u32 lsrc, + struct rtable **result) + { + +@@ -1751,6 +1754,7 @@ static inline int __mkroute_input(struct + flags |= RTCF_DIRECTSRC; + + if (out_dev == in_dev && err && !(flags & (RTCF_NAT | RTCF_MASQ)) && ++ !lsrc && + (IN_DEV_SHARED_MEDIA(out_dev) || + inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res)))) + flags |= RTCF_DOREDIRECT; +@@ -1788,6 +1792,7 @@ static inline int __mkroute_input(struct + rth->fl.mark = skb->mark; + rth->fl.fl4_src = saddr; + rth->rt_src = saddr; ++ rth->fl.fl4_lsrc = lsrc; + rth->rt_gateway = daddr; + rth->rt_iif = + rth->fl.iif = in_dev->dev->ifindex; +@@ -1795,6 +1800,7 @@ static inline int __mkroute_input(struct + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); + rth->fl.oif = 0; ++ rth->fl.fl4_gw = 0; + rth->rt_spec_dst= spec_dst; + + rth->u.dst.input = ip_forward; +@@ -1816,19 +1822,21 @@ static inline int ip_mkroute_input_def(s + struct fib_result* res, + const struct flowi *fl, + struct in_device *in_dev, +- __be32 daddr, __be32 saddr, u32 tos) ++ __be32 daddr, __be32 saddr, u32 tos, ++ u32 lsrc) + { + struct rtable* rth = NULL; + int err; + unsigned hash; + ++ fib_select_default(fl, res); + #ifdef CONFIG_IP_ROUTE_MULTIPATH +- if (res->fi && res->fi->fib_nhs > 1 && fl->oif == 0) ++ if (res->fi && res->fi->fib_nhs > 1) + fib_select_multipath(fl, res); + #endif + + /* create a routing cache entry */ +- err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth); ++ err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, lsrc, &rth); + if (err) + return err; + +@@ -1841,7 +1849,8 @@ static inline int ip_mkroute_input(struc + struct fib_result* res, + const struct flowi *fl, + struct in_device *in_dev, +- __be32 daddr, __be32 saddr, u32 tos) ++ __be32 daddr, __be32 saddr, u32 tos, ++ u32 lsrc) + { + #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED + struct rtable* rth = NULL, *rtres; +@@ -1857,7 +1866,7 @@ static inline int ip_mkroute_input(struc + /* distinguish between multipath and singlepath */ + if (hopcount < 2) + return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, +- saddr, tos); ++ saddr, tos, 0); + + /* add all alternatives to the routing cache */ + for (hop = 0; hop < hopcount; hop++) { +@@ -1869,7 +1878,7 @@ static inline int ip_mkroute_input(struc + + /* create a routing cache entry */ + err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, +- &rth); ++ 0, &rth); + if (err) + return err; + +@@ -1889,7 +1898,7 @@ static inline int ip_mkroute_input(struc + skb->dst = &rtres->u.dst; + return err; + #else /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ +- return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos); ++ return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos, lsrc); + #endif /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ + } + +@@ -1905,18 +1914,18 @@ static inline int ip_mkroute_input(struc + */ + + static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, +- u8 tos, struct net_device *dev) ++ u8 tos, struct net_device *dev, u32 lsrc) + { + struct fib_result res; + struct in_device *in_dev = in_dev_get(dev); + struct flowi fl = { .nl_u = { .ip4_u = + { .daddr = daddr, +- .saddr = saddr, ++ .saddr = lsrc ? : saddr, + .tos = tos, + .scope = RT_SCOPE_UNIVERSE, + } }, + .mark = skb->mark, +- .iif = dev->ifindex }; ++ .iif = lsrc? loopback_dev.ifindex : dev->ifindex }; + unsigned flags = 0; + u32 itag = 0; + struct rtable * rth; +@@ -1949,6 +1958,12 @@ static int ip_route_input_slow(struct sk + if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr)) + goto martian_destination; + ++ if (lsrc) { ++ if (MULTICAST(lsrc) || BADCLASS(lsrc) || ++ ZERONET(lsrc) || LOOPBACK(lsrc)) ++ goto e_inval; ++ } ++ + /* + * Now we are ready to route packet. + */ +@@ -1958,6 +1973,10 @@ static int ip_route_input_slow(struct sk + goto no_route; + } + free_res = 1; ++ if (lsrc && res.type != RTN_UNICAST && res.type != RTN_NAT) ++ goto e_inval; ++ fl.iif = dev->ifindex; ++ fl.fl4_src = saddr; + + RT_CACHE_STAT_INC(in_slow_tot); + +@@ -1982,7 +2001,7 @@ static int ip_route_input_slow(struct sk + if (res.type != RTN_UNICAST) + goto martian_destination; + +- err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); ++ err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos, lsrc); + if (err == -ENOBUFS) + goto e_nobufs; + if (err == -EINVAL) +@@ -1997,6 +2016,8 @@ out: return err; + brd_input: + if (skb->protocol != htons(ETH_P_IP)) + goto e_inval; ++ if (lsrc) ++ goto e_inval; + + if (ZERONET(saddr)) + spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); +@@ -2037,6 +2058,7 @@ local_input: + rth->u.dst.dev = &loopback_dev; + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); ++ rth->fl.fl4_gw = 0; + rth->rt_gateway = daddr; + rth->rt_spec_dst= spec_dst; + rth->u.dst.input= ip_local_deliver; +@@ -2086,8 +2108,9 @@ martian_source: + goto e_inval; + } + +-int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr, +- u8 tos, struct net_device *dev) ++static inline int ++ip_route_input_cached(struct sk_buff *skb, __be32 daddr, __be32 saddr, ++ u8 tos, struct net_device *dev, u32 lsrc) + { + struct rtable * rth; + unsigned hash; +@@ -2102,6 +2125,7 @@ int ip_route_input(struct sk_buff *skb, + if (rth->fl.fl4_dst == daddr && + rth->fl.fl4_src == saddr && + rth->fl.iif == iif && ++ rth->fl.fl4_lsrc == lsrc && + rth->fl.oif == 0 && + rth->fl.mark == skb->mark && + rth->fl.fl4_tos == tos) { +@@ -2148,7 +2172,19 @@ int ip_route_input(struct sk_buff *skb, + rcu_read_unlock(); + return -EINVAL; + } +- return ip_route_input_slow(skb, daddr, saddr, tos, dev); ++ return ip_route_input_slow(skb, daddr, saddr, tos, dev, lsrc); ++} ++ ++int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, ++ u8 tos, struct net_device *dev) ++{ ++ return ip_route_input_cached(skb, daddr, saddr, tos, dev, 0); ++} ++ ++int ip_route_input_lookup(struct sk_buff *skb, u32 daddr, u32 saddr, ++ u8 tos, struct net_device *dev, u32 lsrc) ++{ ++ return ip_route_input_cached(skb, daddr, saddr, tos, dev, lsrc); + } + + static inline int __mkroute_output(struct rtable **result, +@@ -2227,6 +2263,7 @@ static inline int __mkroute_output(struc + rth->fl.fl4_tos = tos; + rth->fl.fl4_src = oldflp->fl4_src; + rth->fl.oif = oldflp->oif; ++ rth->fl.fl4_gw = oldflp->fl4_gw; + rth->fl.mark = oldflp->mark; + rth->rt_dst = fl->fl4_dst; + rth->rt_src = fl->fl4_src; +@@ -2367,6 +2404,7 @@ static int ip_route_output_slow(struct r + struct flowi fl = { .nl_u = { .ip4_u = + { .daddr = oldflp->fl4_dst, + .saddr = oldflp->fl4_src, ++ .gw = oldflp->fl4_gw, + .tos = tos & IPTOS_RT_MASK, + .scope = ((tos & RTO_ONLINK) ? + RT_SCOPE_LINK : +@@ -2470,6 +2508,7 @@ static int ip_route_output_slow(struct r + dev_out = &loopback_dev; + dev_hold(dev_out); + fl.oif = loopback_dev.ifindex; ++ fl.fl4_gw = 0; + res.type = RTN_LOCAL; + flags |= RTCF_LOCAL; + goto make_route; +@@ -2477,7 +2516,7 @@ static int ip_route_output_slow(struct r + + if (fib_lookup(&fl, &res)) { + res.fi = NULL; +- if (oldflp->oif) { ++ if (oldflp->oif && dev_out->flags & IFF_UP) { + /* Apparently, routing tables are wrong. Assume, + that the destination is on link. + +@@ -2517,6 +2556,7 @@ static int ip_route_output_slow(struct r + dev_out = &loopback_dev; + dev_hold(dev_out); + fl.oif = dev_out->ifindex; ++ fl.fl4_gw = 0; + if (res.fi) + fib_info_put(res.fi); + res.fi = NULL; +@@ -2524,13 +2564,12 @@ static int ip_route_output_slow(struct r + goto make_route; + } + ++ if (res.type == RTN_UNICAST) ++ fib_select_default(&fl, &res); + #ifdef CONFIG_IP_ROUTE_MULTIPATH +- if (res.fi->fib_nhs > 1 && fl.oif == 0) ++ if (res.fi->fib_nhs > 1) + fib_select_multipath(&fl, &res); +- else + #endif +- if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif) +- fib_select_default(&fl, &res); + + if (!fl.fl4_src) + fl.fl4_src = FIB_RES_PREFSRC(res); +@@ -2567,6 +2606,7 @@ int __ip_route_output_key(struct rtable + rth->fl.fl4_src == flp->fl4_src && + rth->fl.iif == 0 && + rth->fl.oif == flp->oif && ++ rth->fl.fl4_gw == flp->fl4_gw && + rth->fl.mark == flp->mark && + !((rth->fl.fl4_tos ^ flp->fl4_tos) & + (IPTOS_RT_MASK | RTO_ONLINK))) { +@@ -3199,3 +3239,4 @@ int __init ip_rt_init(void) + EXPORT_SYMBOL(__ip_select_ident); + EXPORT_SYMBOL(ip_route_input); + EXPORT_SYMBOL(ip_route_output_key); ++EXPORT_SYMBOL(ip_route_input_lookup); diff --git a/target/linux/generic-2.6/patches-2.6.21/202-mips-freestanding.patch b/target/linux/generic-2.6/patches-2.6.21/202-mips-freestanding.patch new file mode 100644 index 0000000000..51a96a40ad --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/202-mips-freestanding.patch @@ -0,0 +1,14 @@ +Index: linux-2.6.21.7/arch/mips/Makefile +=================================================================== +--- linux-2.6.21.7.orig/arch/mips/Makefile ++++ linux-2.6.21.7/arch/mips/Makefile +@@ -605,6 +605,9 @@ core-$(CONFIG_TOSHIBA_RBTX4938) += arch/ + core-$(CONFIG_TOSHIBA_RBTX4938) += arch/mips/tx4938/common/ + load-$(CONFIG_TOSHIBA_RBTX4938) += 0xffffffff80100000 + ++# temporary until string.h is fixed ++cflags-y += -ffreestanding ++ + cflags-y += -Iinclude/asm-mips/mach-generic + drivers-$(CONFIG_PCI) += arch/mips/pci/ + diff --git a/target/linux/generic-2.6/patches-2.6.21/204-jffs2_eofdetect.patch b/target/linux/generic-2.6/patches-2.6.21/204-jffs2_eofdetect.patch new file mode 100644 index 0000000000..5c301b47d2 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/204-jffs2_eofdetect.patch @@ -0,0 +1,60 @@ +Index: linux-2.6.21.7/fs/jffs2/build.c +=================================================================== +--- linux-2.6.21.7.orig/fs/jffs2/build.c ++++ linux-2.6.21.7/fs/jffs2/build.c +@@ -107,6 +107,17 @@ static int jffs2_build_filesystem(struct + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + ++ if (c->flags & (1 << 7)) { ++ printk("%s(): unlocking the mtd device... ", __func__); ++ if (c->mtd->unlock) ++ c->mtd->unlock(c->mtd, 0, c->mtd->size); ++ printk("done.\n"); ++ ++ printk("%s(): erasing all blocks after the end marker... ", __func__); ++ jffs2_erase_pending_blocks(c, -1); ++ printk("done.\n"); ++ } ++ + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* Now scan the directory tree, increasing nlink according to every dirent found. */ +Index: linux-2.6.21.7/fs/jffs2/scan.c +=================================================================== +--- linux-2.6.21.7.orig/fs/jffs2/scan.c ++++ linux-2.6.21.7/fs/jffs2/scan.c +@@ -143,9 +143,12 @@ int jffs2_scan_medium(struct jffs2_sb_in + + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); +- +- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), +- buf_size, s); ++ ++ if (c->flags & (1 << 7)) ++ ret = BLK_STATE_ALLFF; ++ else ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), ++ buf_size, s); + + if (ret < 0) + goto out; +@@ -546,6 +549,17 @@ static int jffs2_scan_eraseblock (struct + return err; + } + ++ if ((buf[0] == 0xde) && ++ (buf[1] == 0xad) && ++ (buf[2] == 0xc0) && ++ (buf[3] == 0xde)) { ++ /* end of filesystem. erase everything after this point */ ++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); ++ c->flags |= (1 << 7); ++ ++ return BLK_STATE_ALLFF; ++ } ++ + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + diff --git a/target/linux/generic-2.6/patches-2.6.21/207-powerpc_asm_segment_h.patch b/target/linux/generic-2.6/patches-2.6.21/207-powerpc_asm_segment_h.patch new file mode 100644 index 0000000000..963ecd182b --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/207-powerpc_asm_segment_h.patch @@ -0,0 +1,11 @@ +Index: linux-2.6.21.7/include/asm-powerpc/segment.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/include/asm-powerpc/segment.h +@@ -0,0 +1,6 @@ ++#ifndef _ASM_SEGMENT_H ++#define _ASM_SEGMENT_H ++ ++/* Only here because we have some old header files that expect it.. */ ++ ++#endif /* _ASM_SEGMENT_H */ diff --git a/target/linux/generic-2.6/patches-2.6.21/208-rtl8110sb_fix.patch b/target/linux/generic-2.6/patches-2.6.21/208-rtl8110sb_fix.patch new file mode 100644 index 0000000000..2b8cebe9df --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/208-rtl8110sb_fix.patch @@ -0,0 +1,26 @@ +Index: linux-2.6.21.7/drivers/net/r8169.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/r8169.c ++++ linux-2.6.21.7/drivers/net/r8169.c +@@ -494,7 +494,7 @@ static int rtl8169_poll(struct net_devic + #endif + + static const u16 rtl8169_intr_mask = +- SYSErr | LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; ++ LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; + static const u16 rtl8169_napi_event = + RxOK | RxOverflow | RxFIFOOver | TxOK | TxErr; + static const unsigned int rtl8169_rx_config = +@@ -2652,10 +2652,12 @@ rtl8169_interrupt(int irq, void *dev_ins + if (!(status & rtl8169_intr_mask)) + break; + ++#if 0 + if (unlikely(status & SYSErr)) { + rtl8169_pcierr_interrupt(dev); + break; + } ++#endif + + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); diff --git a/target/linux/generic-2.6/patches-2.6.21/209-mini_fo.patch b/target/linux/generic-2.6/patches-2.6.21/209-mini_fo.patch new file mode 100644 index 0000000000..10cabec40c --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/209-mini_fo.patch @@ -0,0 +1,7817 @@ +Index: linux-2.6.21.7/fs/Kconfig +=================================================================== +--- linux-2.6.21.7.orig/fs/Kconfig ++++ linux-2.6.21.7/fs/Kconfig +@@ -461,6 +461,9 @@ config OCFS2_DEBUG_MASKLOG + This option will enlarge your kernel, but it allows debugging of + ocfs2 filesystem issues. + ++config MINI_FO ++ tristate "Mini fanout overlay filesystem" ++ + config MINIX_FS + tristate "Minix fs support" + help +Index: linux-2.6.21.7/fs/Makefile +=================================================================== +--- linux-2.6.21.7.orig/fs/Makefile ++++ linux-2.6.21.7/fs/Makefile +@@ -72,6 +72,7 @@ obj-$(CONFIG_SQUASHFS) += squashfs/ + obj-$(CONFIG_RAMFS) += ramfs/ + obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ + obj-$(CONFIG_CODA_FS) += coda/ ++obj-$(CONFIG_MINI_FO) += mini_fo/ + obj-$(CONFIG_MINIX_FS) += minix/ + obj-$(CONFIG_FAT_FS) += fat/ + obj-$(CONFIG_MSDOS_FS) += msdos/ +Index: linux-2.6.21.7/fs/mini_fo/aux.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/aux.c +@@ -0,0 +1,580 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++/* check if file exists in storage */ ++int exists_in_storage(dentry_t *dentry) ++{ ++ check_mini_fo_dentry(dentry); ++ if(dtost(dentry) == MODIFIED || dtost(dentry) == CREATED || dtost(dentry) == DEL_REWRITTEN) ++ return 1; ++ return 0; ++} ++ ++/* check if dentry is in an existing state */ ++int is_mini_fo_existant(dentry_t *dentry) ++{ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) == DELETED || dtost(dentry) == NON_EXISTANT) ++ return 0; ++ else ++ return 1; ++} ++ ++/* ++ * This function will create a negative storage dentry for ++ * dentry, what is required for many create like options. ++ * It will create the storage structure if necessary. ++ */ ++int get_neg_sto_dentry(dentry_t *dentry) ++{ ++ int err = 0; ++ unsigned int len; ++ const unsigned char *name; ++ ++ if(!dentry || ++ !dtopd(dentry) || ++ !(dtost(dentry) == UNMODIFIED || ++ dtost(dentry) == NON_EXISTANT || ++ dtost(dentry) == DELETED)) { ++ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: invalid dentry passed.\n"); ++ err = -1; ++ goto out; ++ } ++ /* Have we got a neg. dentry already? */ ++ if(dtohd2(dentry)) { ++ err = 0; ++ goto out; ++ } ++ if(dtost(dentry->d_parent) == UNMODIFIED) { ++ /* build sto struct */ ++ err = build_sto_structure(dentry->d_parent->d_parent, dentry->d_parent); ++ if(err || ++ dtost(dentry->d_parent) != MODIFIED) { ++ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: ERROR building sto structure.\n"); ++ err = -1; ++ goto out; ++ } ++ } ++ ++ len = dentry->d_name.len; ++ name = dentry->d_name.name; ++ ++ dtohd2(dentry) = ++ lookup_one_len(name, dtohd2(dentry->d_parent), len); ++ ++ out: ++ return err; ++} ++ ++int check_mini_fo_dentry(dentry_t *dentry) ++{ ++ ASSERT(dentry != NULL); ++ ASSERT(dtopd(dentry) != NULL); ++ ASSERT((dtohd(dentry) != NULL) || (dtohd2(dentry) != NULL)); ++ ++/* if(dtost(dentry) == MODIFIED) { */ ++/* ASSERT(dentry->d_inode != NULL); */ ++/* ASSERT(dtohd(dentry) != NULL); */ ++/* ASSERT(dtohd(dentry)->d_inode != NULL); */ ++/* ASSERT(dtohd2(dentry) != NULL); */ ++/* ASSERT(dtohd2(dentry)->d_inode != NULL); */ ++/* } */ ++/* else if(dtost(dentry) == UNMODIFIED) { */ ++/* ASSERT(dentry->d_inode != NULL); */ ++/* ASSERT( */ ++/* } */ ++ return 0; ++} ++ ++int check_mini_fo_file(file_t *file) ++{ ++ ASSERT(file != NULL); ++ ASSERT(ftopd(file) != NULL); ++ ASSERT(file->f_dentry != NULL); ++ ++ /* violent checking, check depending of state and type ++ * if(S_ISDIR(file->f_dentry->d_inode->i_mode)) {} ++ */ ++ ASSERT((ftohf(file) != NULL) || (ftohf2(file) != NULL)); ++ return 0; ++} ++ ++int check_mini_fo_inode(inode_t *inode) ++{ ++ ASSERT(inode != NULL); ++ ASSERT(itopd(inode) != NULL); ++ ASSERT((itohi(inode) != NULL) || (itohi2(inode) != NULL)); ++ return 0; ++} ++ ++/* ++ * will walk a base path as provided by get_mini_fo_bpath and return ++ * the (hopefully ;-) ) positive dentry of the renamed base dir. ++ * ++ * This does some work of path_init. ++ */ ++dentry_t *bpath_walk(super_block_t *sb, char *bpath) ++{ ++ int err; ++ struct nameidata nd; ++ ++ /* be paranoid */ ++ if(!bpath || bpath[0] != '/') { ++ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid string.\n"); ++ return NULL; ++ } ++ if(!sb || !stopd(sb)) { ++ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid sb.\n"); ++ return NULL; ++ } ++ ++ /* setup nd as path_init does */ ++ nd.last_type = LAST_ROOT; /* if there are only slashes... */ ++ nd.flags = LOOKUP_FOLLOW; ++ /* fix this: how do I reach this lock? ++ * read_lock(¤t->fs->lock); */ ++ nd.mnt = mntget(stopd(sb)->hidden_mnt); ++ nd.dentry = dget(stopd(sb)->base_dir_dentry); ++ /* read_unlock(¤t->fs->lock); */ ++ ++ err = path_walk(bpath+1, &nd); ++ ++ /* validate */ ++ if (err || !nd.dentry || !nd.dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: bpath_walk: path_walk failed.\n"); ++ return NULL; ++ } ++ return nd.dentry; ++} ++ ++ ++/* returns the full path of the basefile incl. its name */ ++int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len) ++{ ++ char *buf_walker; ++ int len = 0; ++ dentry_t *sky_walker; ++ ++ if(!dentry || !dtohd(dentry)) { ++ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: invalid dentry passed.\n"); ++ return -1; ++ } ++ sky_walker = dtohd(dentry); ++ ++ do { ++ len += sky_walker->d_name.len + 1 ; /* 1 for '/' */ ++ sky_walker = sky_walker->d_parent; ++ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); ++ ++ /* 1 to oil the loop */ ++ *bpath = (char*) kmalloc(len + 1, GFP_KERNEL); ++ if(!*bpath) { ++ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: out of mem.\n"); ++ return -1; ++ } ++ buf_walker = *bpath+len; /* put it on last char */ ++ *buf_walker = '\n'; ++ sky_walker = dtohd(dentry); ++ ++ do { ++ buf_walker -= sky_walker->d_name.len; ++ strncpy(buf_walker, ++ sky_walker->d_name.name, ++ sky_walker->d_name.len); ++ *(--buf_walker) = '/'; ++ sky_walker = sky_walker->d_parent; ++ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); ++ ++ /* bpath_len doesn't count newline! */ ++ *bpath_len = len; ++ return 0; ++} ++ ++int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, ++ dentry_t *src_dentry, struct vfsmount *src_mnt) ++{ ++ void *buf; ++ mm_segment_t old_fs; ++ file_t *tgt_file; ++ file_t *src_file; ++ int bytes, len, tmp, err; ++ err = 0; ++ ++ if(!(tgt_dentry->d_inode && src_dentry->d_inode)) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, neg. dentry passed.\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ dget(tgt_dentry); ++ dget(src_dentry); ++ mntget(tgt_mnt); ++ mntget(src_mnt); ++ ++ /* open file write only */ ++ tgt_file = dentry_open(tgt_dentry, tgt_mnt, 0x1); ++ if(!tgt_file || IS_ERR(tgt_file)) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening target file.\n"); ++ err = PTR_ERR(tgt_file); ++ goto out_err; ++ } ++ ++ /* open file read only */ ++ src_file = dentry_open(src_dentry, src_mnt, 0x0); ++ if(!src_file || IS_ERR(src_file)) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening source file.\n"); ++ err = PTR_ERR(src_file); ++ ++ /* close target file */ ++ fput(tgt_file); ++ goto out_err; ++ } ++ ++ /* check if the filesystem(s) support read respective write */ ++ if(!src_file->f_op->read || !tgt_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, no fs read or write support.\n"); ++ err = -EPERM; ++ goto out_close; ++ } ++ ++ /* allocate a page for transfering the data */ ++ buf = (void *) __get_free_page(GFP_KERNEL); ++ if(!buf) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, out of kernel mem.\n"); ++ goto out_err; ++ } ++ ++ tgt_file->f_pos = 0; ++ src_file->f_pos = 0; ++ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* Doing this I assume that a read operation will return a full ++ * buffer while there is still data to read, and a less than ++ * full buffer when all data has been read. ++ */ ++ bytes = len = PAGE_SIZE; ++ while(bytes == len) { ++ bytes = src_file->f_op->read(src_file, buf, len, ++ &src_file->f_pos); ++ tmp = tgt_file->f_op->write(tgt_file, buf, bytes, ++ &tgt_file->f_pos); ++ if(tmp != bytes) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR writing.\n"); ++ goto out_close_unset; ++ } ++ } ++ ++ free_page((unsigned long) buf); ++ set_fs(old_fs); ++ fput(tgt_file); ++ fput(src_file); ++ goto out; ++ ++ out_close_unset: ++ free_page((unsigned long) buf); ++ set_fs(old_fs); ++ ++ out_close: ++ fput(tgt_file); ++ fput(src_file); ++ ++ out_err: ++ dput(tgt_dentry); ++ dput(src_dentry); ++ ++ /* mk: not sure if this need to be done */ ++ mntput(tgt_mnt); ++ mntput(src_mnt); ++ ++ out: ++ return err; ++} ++ ++/* mk: ++ * ndl (no-duplicate list) stuff ++ * This is used in mini_fo_readdir, to save the storage directory contents ++ * and later when reading base, match them against the list in order ++ * to avoid duplicates. ++ */ ++ ++/* add a file specified by name and len to the ndl ++ * Return values: 0 on success, <0 on failure. ++ */ ++int ndl_add_entry(struct readdir_data *rd, const char *name, int len) ++{ ++ struct ndl_entry *tmp_entry; ++ ++ tmp_entry = (struct ndl_entry *) ++ kmalloc(sizeof(struct ndl_entry), GFP_KERNEL); ++ if(!tmp_entry) { ++ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); ++ return -ENOMEM; ++ } ++ tmp_entry->name = (char*) kmalloc(len, GFP_KERNEL); ++ if(!tmp_entry->name) { ++ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); ++ return -ENOMEM; ++ } ++ strncpy(tmp_entry->name, name, len); ++ tmp_entry->len = len; ++ ++ list_add(&tmp_entry->list, &rd->ndl_list); ++ rd->ndl_size++; ++ return 0; ++} ++ ++/* delete all list entries and free memory */ ++void ndl_put_list(struct readdir_data *rd) ++{ ++ struct list_head *tmp; ++ struct ndl_entry *tmp_entry; ++ ++ if(rd->ndl_size <= 0) ++ return; ++ while(!list_empty(&rd->ndl_list)) { ++ tmp = rd->ndl_list.next; ++ list_del(tmp); ++ tmp_entry = list_entry(tmp, struct ndl_entry, list); ++ kfree(tmp_entry->name); ++ kfree(tmp_entry); ++ } ++ rd->ndl_size = 0; ++} ++ ++/* Check if a file specified by name and len is in the ndl ++ * Return value: 0 if not in list, 1 if file is found in ndl. ++ */ ++int ndl_check_entry(struct readdir_data *rd, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct ndl_entry *tmp_entry; ++ ++ if(rd->ndl_size <= 0) ++ return 0; ++ ++ list_for_each(tmp, &rd->ndl_list) { ++ tmp_entry = list_entry(tmp, struct ndl_entry, list); ++ if(tmp_entry->len != len) ++ continue; ++ if(!strncmp(tmp_entry->name, name, len)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* mk: ++ * Recursive function to create corresponding directorys in the storage fs. ++ * The function will build the storage directorys up to dentry. ++ */ ++int build_sto_structure(dentry_t *dir, dentry_t *dentry) ++{ ++ int err; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ if(dentry->d_parent != dir) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [1].\n"); ++ return 1; ++ } ++ ++ if(dtost(dir) != MODIFIED) { ++ err = build_sto_structure(dir->d_parent, dentry->d_parent); ++ if(err) ++ return err; ++ } ++ ++ /* ok, coming back again. */ ++ check_mini_fo_dentry(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(!hidden_sto_dentry) { ++ /* ++ * This is the case after creating the first ++ * hidden_sto_dentry. ++ * After one negative storage_dentry, all pointers to ++ * hidden_storage dentries are set to NULL. We need to ++ * create the negative dentry before we create the storage ++ * file. ++ */ ++ unsigned int len; ++ const unsigned char *name; ++ len = dtohd(dentry)->d_name.len; ++ name = dtohd(dentry)->d_name.name; ++ hidden_sto_dentry = lookup_one_len(name, dtohd2(dir), len); ++ dtohd2(dentry) = hidden_sto_dentry; ++ } ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ /* lets be safe */ ++ if(dtohd2(dir) != hidden_sto_dir_dentry) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [2].\n"); ++ return 1; ++ } ++ ++ /* check for errors in lock_parent */ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if(IS_ERR(hidden_sto_dir_dentry)) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: lock_parent failed.\n"); ++ return err; ++ } ++ ++ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dir->d_inode->i_mode); ++ ++ if(err) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [1].\n"); ++ /* was: unlock_dir(dir); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&dir->d_inode->i_mutex); ++#else ++ up(&dir->d_inode->i_sem); ++#endif ++ dput(dir); ++ return err; ++ } ++ ++ /* everything ok! */ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [2].\n"); ++ /* was: unlock_dir(dir); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&dir->d_inode->i_mutex); ++#else ++ up(&dir->d_inode->i_sem); ++#endif ++ dput(dir); ++ return 1; ++ } ++ ++ /* interpose the new inode and set new state */ ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ dtopd(dentry)->state = MODIFIED; ++ ++ /* initalize the wol list */ ++ itopd(dentry->d_inode)->deleted_list_size = -1; ++ itopd(dentry->d_inode)->renamed_list_size = -1; ++ meta_build_lists(dentry); ++ ++ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); ++ fist_copy_attr_timesizes(dir->d_inode, ++ hidden_sto_dir_dentry->d_inode); ++ dir->d_inode->i_nlink++; ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ return 0; ++} ++ ++ ++#if 0 /* unused */ ++ ++/* ++ * Read "len" bytes from "filename" into "buf". ++ * "buf" is in kernel space. ++ */ ++int ++mini_fo_read_file(const char *filename, void *buf, int len) ++{ ++ file_t *filp; ++ mm_segment_t oldfs; ++ int bytes; ++ /* Chroot? Maybe NULL isn't right here */ ++ filp = filp_open(filename, O_RDONLY, 0); ++ if (!filp || IS_ERR(filp)) { ++ printk("mini_fo_read_file err %d\n", (int) PTR_ERR(filp)); ++ return -1; /* or do something else */ ++ } ++ ++ if (!filp->f_op->read) ++ return -2; /* file(system) doesn't allow reads */ ++ ++ /* now read len bytes from offset 0 */ ++ filp->f_pos = 0; /* start offset */ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ bytes = filp->f_op->read(filp, buf, len, &filp->f_pos); ++ set_fs(oldfs); ++ ++ /* close the file */ ++ fput(filp); ++ ++ return bytes; ++} ++ ++ ++ ++/* ++ * Write "len" bytes from "buf" to "filename" ++ * "buf" is in kernel space. ++ */ ++int ++mini_fo_write_file(const char *filename, void *buf, int len) ++{ ++ file_t *filp; ++ mm_segment_t oldfs; ++ int bytes; ++ /* Chroot? Maybe NULL isn't right here */ ++ filp = filp_open(filename, O_RDWR|O_CREAT, 0640); ++ if (!filp || IS_ERR(filp)) { ++ printk("mini_fo_write_file err %d\n", (int) PTR_ERR(filp)); ++ return -1; /* or do something else */ ++ } ++ ++ if (!filp->f_op->write) ++ return -2; /* file(system) doesn't allow writes */ ++ ++ /* now write len bytes from offset 0 */ ++ filp->f_pos = 0; /* start offset */ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ bytes = filp->f_op->write(filp, buf, len, &filp->f_pos); ++ set_fs(oldfs); ++ ++ /* close the file */ ++ fput(filp); ++ ++ return bytes; ++} ++ ++#endif /* unused */ ++ +Index: linux-2.6.21.7/fs/mini_fo/ChangeLog +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/ChangeLog +@@ -0,0 +1,281 @@ ++2006-01-24 Markus Klotzbuecher ++ ++ * Add tons of ugly ifdefs to Ed L. Cashin's mutex patch to ++ retain backwards compatibility. ++ ++2006-01-24 Ed L. Cashin ++ ++ * Support for the new mutex infrastructure ++ (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) ++ ++2005-10-15 Markus Klotzbuecher ++ ++ * Bugfix for a serious memory leak in mini_fo_follow_link. ++ ++2005-09-21 Markus Klotzbuecher ++ ++ * new release 0.6.1 ++ ++ * fix of a compiler warning due to changes in 2.6.13 ++ ++2005-09-21 Klaus Wenninger ++ ++ * file.c: readdir: fix for a bug that caused directory entries ++ to show up twice when using storage filesystems such as ++ minixfs or pramfs. ++ ++2005-06-30 Eric Lammerts ++ ++ * fix for an oops when overwriting a binary thats beeing ++ executed. ++ ++2005-06-09 ++ ++ * Renamed overlay to mini_fo-overlay. ++ ++ * Added mini_fo-merge script to allow merging of storage and base ++ after making modifications. ++ ++2005-05-22 root ++ ++ * Added overlay script that allows to easily mount mini_fo ontop ++ of a given base directory ++ ++2005-05-10 ++ ++ * inode.c: xattr functions return -EOPNOSUPP instead of ++ -ENOSUPP, what confuses "ls -l" ++ ++ * Changed license from LGPL to GPL. ++ ++2005-05-08 root ++ ++ * Makefile: clean it up and added make install and make ++ uninstall. ++ ++2005-05-06 ++ ++ * merged devel branch back to main. [v0-6-0-pre3] ++ ++ * removed unused files print.c and fist_ioctl. [devel-0-0-18] ++ ++ * ioctl: removed fist_ioctl stuff, that is not needed for ++ now. ++ ++2005-05-03 ++ ++ * file.c: simplified mini_fo_open and mini_fo_setattr using ++ new state changing functions. [devel-0-0-17] ++ ++ * inode.c: Fixed getattr state bug (see below) in 2.4 function ++ mini_fo_inode revalidate. ++ ++ * inode.c: found an other bug in mini_fo_getattr. States are not ++ reliable in this function, as a file can be opened, unlinked and ++ the getattr function called. This results in a deleted dentry ++ with an inode. Fix is to ignore states and simply use the inode ++ available. ++ ++2005-04-29 ++ ++ * file.c: Bugfix and cleanup in fasync and fsync. [devel-0-0-16] ++ ++ * file.c: do not use mini_fo_lock so the generic version is ++ used (I guess). ++ ++ * inode.c: getattr, never call getattr on lower files, as this ++ will cause the inum to change. ++ ++ * inode.c: rename_reg_file renamed to rename_nondir, as it ++ doesn't matter as long it't not a dir. Removed all ++ rename_dev_file etc. ++ ++ * tagged as devel-0-0-15 ++ ++ * inode.c: added support for chosing support for extended ++ attrs at compile time by XATTR define in mini_fo.h . ++ ++ * inode.c: fixed mini_fo_getattr to use mini_fo inode and not ++ lower again, what avoids inode number changes that confused ++ rm again. This is the proper solution. ++ ++2005-04-24 ++ ++ * all files: updated Copyright notive to 2005. [devel-0-0-14] ++ ++ * inode.c: fixed mini_fo_getattr to not change the inode ++ number, even if lower files change. ++ ++ * super.c: fixed a bug that caused deleted base file to show ++ up suddenly after some time, or after creating a special ++ file. The problem was that after some time or after special ++ file creating sync_sb_inodes is called by the vfs, that ++ called our mini_fo_put_inode. There was (wrongly) called ++ __meta_put_lists, that nuked the lists, although the inode ++ was going to continue its life. Moving __meta_put_lists to ++ mini_fo_clear_inode, where an inode is really destroyed, ++ solved the problem. ++ ++ ++2005-04-23 ++ ++ * state.c, aux.c: more cleaning up and ++ simplifications. [devel-0-0-13] ++ ++ * inode.c: implemented mini_fo_getattr, that was required for ++ 2.6 because inode_revalidate has been remove there, and the ++ old "du" bug returned. ++ ++ ++2005-04-20 ++ ++ * aux.c: get_neg_sto_dentry(): allow to be called for dentries ++ in state UNMODIFIED, NON_EXISTANT _and_ DELETED. ++ ++2005-04-19 ++ ++ * Fixed a bug under 2.6 that caused files deleted via mini_fo ++ not to be deleted properly and therefore the fs filled up ++ untill no memory was left. [devel-0-0-12] ++ ++ * Added basic hard link support. This means that creating ++ hardlinks will work, but existing ones will be treated as ++ individual files. [devel-0-0-11] ++ ++2005-04-17 ++ ++ * Bugfixes ++ ++2005-04-13 root ++ ++ * Added file state.c for the state transition ++ functions. Doesn't work very well yet, though... ++ ++2005-04-12 ++ ++ * Porting to 2.6 started, which is easier than expected, also ++ due to Olivier previous work. ++ ++2005-04-08 ++ ++ * Fixed the bug that caused du to return invalid sizes of ++ directory trees. The problem was that ++ mini_fo_inode_revalidate didn't always copy the attributes ++ from the base inode properly. ++ ++2005-04-01 Markus Klotzbuecher ++ ++ * Merged devel branch back to main trunk and updated the ++ RELEASE notes. This will be 0-6-0-pre1. ++ ++2005-03-31 Markus Klotzbuecher ++ ++ * Fixed some bugs in rename_reg_file, that only showed up in ++ the kernel compile test. Kernel compiles cleanly ontop of ++ mini_fo, now also make mrproper etc. work. Seems pretty stable. ++ ++2005-03-28 Markus Klotzbuecher ++ ++ * Many, many directory renaming bugfixes and a lot of other ++ cleanup. Dir renaming seems to work relatively stable. ++ ++2005-03-22 Markus Klotzbuecher ++ ++ * Finished implementing lightweight directory renaming. Some ++ basic testing indicates it works fine. ++ Next is to implement testcases for the testsuite and confirm ++ everything is really working ok. ++ ++2005-03-18 Markus Klotzbuecher ++ ++ * Finished implementing meta.c stuff required for directory ++ renaming. ++ ++2005-03-17 Markus Klotzbuecher ++ ++ * Fixed all compile warnings + an extremly old bug that ++ somehow crept in while reworking the wol stuff to the META ++ system. Turning on -Werror again... :-) ++ ++ * Fixed some bugs in the new rename_reg_file function. ++ ++ * Rewrote mini_fo rename and split it into several ++ subfunctions, that handle the different types ++ seperately. Rewrote the regular file function aswell, as it ++ was implemented somewhat inefficient. ++ ++2005-03-16 Markus Klotzbuecher ++ ++ * Implemented new META subsystem, removed old WOL stuff in favor ++ if it. ++ ++ * After some basic testing everything seems ok... ++ ++2005-03-11 Markus Klotzbuecher ++ ++ * Renaming a non regular file caused trouble because I always ++ tried to copy the contents. Now I only do this for regular ++ files. mini_fo_rename still isn't implemented properly, renaming ++ of device files, symlinks etc. results in a empty regular file ++ instead of the proper type. ++ ++ * Directory renaming suddenly works! What a surprise! I guess ++ this is because renaming is implemented as making a copy and ++ removing the original. Still this might not work ++ everywhere... ++ ++2005-03-09 Markus Klotzbuecher ++ ++ * Bugfix, when a mini_fo directory that exists in storage ++ (state: MODIFIED, CREATED and DEL_REWRITTEN) is deleted, a ++ possibly existing WOL file contained in it needs to be ++ deleted too. ++ ++ * Starting cleanup: defined state names in order to get rid of ++ the state numbers. ++ ++2005-03-08 Markus Klotzbuecher ++ ++ * Makefile fix, fist_ioctl was built against wrong sources if ARCH=um ++ ++ * Fixed a bug in dentry.c, mini_fo_d_hash. In state 4 = ++ DEL_REWRITTEN the hash was calculated from the base dentry, ++ which was wrong and and caused assertions in ++ __mini_fo_hidden_dentry to fail. ++ ++2005-02-21 ++ ++ * Implemented directory deleting (inode.c) ++ ++ * main.c: made mini_fo_parse_options a little more robust. ++ ++2004-12-22 ++ ++ * Makefile cleanup and uml stuff, removed unneccessary files ++ ++ * Created a new and hopefully more informative README ++ ++ * CHANGELOG: created a new CHANGELOG and added old entries reversely ++ ++ ++2004-10-24 Gleb Natapov ++ ++ * Fix: owner and group where not correctly copied from base to ++ storage. ++ ++ ++2004-10-05 Gleb Natapov ++ ++ * Implementation of fsync, fasync and lock mini_fo functions. ++ ++ ++2004-09-29 Bob Lee ++ ++ * Fix of a serious pointer bug ++ ++ ++2004-09-28 Gleb Natapov ++ ++ * Implementation of mini_fo_mknod and mini_fo_rename, support ++ for device files. ++ +Index: linux-2.6.21.7/fs/mini_fo/dentry.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/dentry.c +@@ -0,0 +1,244 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++/* ++ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. ++ */ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_d_revalidate(dentry_t *dentry, struct nameidata *nd) ++#else ++mini_fo_d_revalidate(dentry_t *dentry, int flags) ++#endif ++{ ++ int err1 = 1; /* valid = 1, invalid = 0 */ ++ int err2 = 1; ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ ++ check_mini_fo_dentry(dentry); ++ ++ hidden_dentry = dtohd(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(hidden_dentry && ++ hidden_dentry->d_op && ++ hidden_dentry->d_op->d_revalidate) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, nd); ++#else ++ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags); ++#endif ++ } ++ if(hidden_sto_dentry && ++ hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_revalidate) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, ++ nd); ++#else ++ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, ++ flags); ++#endif ++ } ++ ++ /* mk: if one of the lower level dentries are valid, ++ * the mini_fo dentry is too. ++ */ ++ return (err1 || err2); ++} ++ ++ ++STATIC int ++mini_fo_d_hash(dentry_t *dentry, qstr_t *name) ++{ ++ int err = 0; ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ /* hidden_dentry = mini_fo_hidden_dentry(dentry); ++ * hidden_sto_dentry = mini_fo_hidden_sto_dentry(dentry); */ ++ ++ /* state 1, 3, 4, 5: build the hash for the storage dentry */ ++ if((dtopd(dentry)->state == MODIFIED) || ++ (dtopd(dentry)->state == CREATED) || ++ (dtopd(dentry)->state == DEL_REWRITTEN) || ++ (dtopd(dentry)->state == DELETED)) { ++ hidden_sto_dentry = dtohd2(dentry); ++ if(hidden_sto_dentry && ++ hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_hash) { ++ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); ++ } ++ goto out; ++ } ++ /* state 2: build the hash for the base dentry */ ++ if(dtopd(dentry)->state == UNMODIFIED) { ++ hidden_dentry = dtohd(dentry); ++ if(hidden_dentry && ++ hidden_dentry->d_op && ++ hidden_dentry->d_op->d_hash) { ++ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); ++ } ++ goto out; ++ } ++ /* state 6: build hash for the dentry that exists */ ++ if(dtopd(dentry)->state == NON_EXISTANT) { ++ hidden_sto_dentry = dtohd2(dentry); ++ if(hidden_sto_dentry && ++ hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_hash) { ++ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); ++ goto out; ++ } ++ hidden_dentry = dtohd(dentry); ++ if(hidden_dentry && ++ hidden_dentry->d_op && ++ hidden_dentry->d_op->d_hash) { ++ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); ++ goto out; ++ } ++ } ++ ++ printk(KERN_CRIT "mini_fo: d_hash: invalid state detected.\n"); ++ ++ out: ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_d_compare(dentry_t *dentry, qstr_t *a, qstr_t *b) ++{ ++ int err; ++ dentry_t *hidden_dentry=NULL; ++ ++ /* hidden_dentry = mini_fo_hidden_dentry(dentry); */ ++ if(dtohd2(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else if(dtohd(dentry)) ++ hidden_dentry = dtohd(dentry); ++ ++ if (hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_compare) { ++ err = hidden_dentry->d_op->d_compare(hidden_dentry, a, b); ++ } else { ++ err = ((a->len != b->len) || memcmp(a->name, b->name, b->len)); ++ } ++ ++ return err; ++} ++ ++ ++int ++mini_fo_d_delete(dentry_t *dentry) ++{ ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ int err = 0; ++ ++ /* this could be a negative dentry, so check first */ ++ if (!dtopd(dentry)) { ++ printk(KERN_CRIT "mini_fo_d_delete: negative dentry passed.\n"); ++ goto out; ++ } ++ hidden_dentry = dtohd(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(hidden_dentry) { ++ if(hidden_dentry->d_op && ++ hidden_dentry->d_op->d_delete) { ++ err = hidden_dentry->d_op->d_delete(hidden_dentry); ++ } ++ } ++ if(hidden_sto_dentry) { ++ if(hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_delete) { ++ err = hidden_sto_dentry->d_op->d_delete(hidden_sto_dentry); ++ } ++ } ++ ++ out: ++ return err; ++} ++ ++ ++void ++mini_fo_d_release(dentry_t *dentry) ++{ ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ ++ /* this could be a negative dentry, so check first */ ++ if (!dtopd(dentry)) { ++ printk(KERN_CRIT "mini_fo_d_release: no private data.\n"); ++ goto out; ++ } ++ hidden_dentry = dtohd(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(hidden_dentry) { ++ /* decrement hidden dentry's counter and free its inode */ ++ dput(hidden_dentry); ++ } ++ if(hidden_sto_dentry) { ++ /* decrement hidden dentry's counter and free its inode */ ++ dput(hidden_sto_dentry); ++ } ++ ++ /* free private data (mini_fo_dentry_info) here */ ++ kfree(dtopd(dentry)); ++ __dtopd(dentry) = NULL; /* just to be safe */ ++ out: ++ return; ++} ++ ++ ++/* ++ * we don't really need mini_fo_d_iput, because dentry_iput will call iput() if ++ * mini_fo_d_iput is not defined. We left this implemented for ease of ++ * tracing/debugging. ++ */ ++void ++mini_fo_d_iput(dentry_t *dentry, inode_t *inode) ++{ ++ iput(inode); ++} ++ ++ ++struct dentry_operations mini_fo_dops = { ++ d_revalidate: mini_fo_d_revalidate, ++ d_hash: mini_fo_d_hash, ++ d_compare: mini_fo_d_compare, ++ d_release: mini_fo_d_release, ++ d_delete: mini_fo_d_delete, ++ d_iput: mini_fo_d_iput, ++}; +Index: linux-2.6.21.7/fs/mini_fo/file.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/file.c +@@ -0,0 +1,713 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) ++ ++/******************* ++ * File Operations * ++ *******************/ ++ ++STATIC loff_t ++mini_fo_llseek(file_t *file, loff_t offset, int origin) ++{ ++ loff_t err; ++ file_t *hidden_file = NULL; ++ ++ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { ++ /* Check if trying to llseek from a directory */ ++ err = -EISDIR; ++ goto out; ++ } ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ /* always set hidden position to this one */ ++ hidden_file->f_pos = file->f_pos; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ memcpy(&(hidden_file->f_ra), ++ &(file->f_ra), ++ sizeof(struct file_ra_state)); ++#else ++ if (file->f_reada) { /* update readahead information if needed */ ++ hidden_file->f_reada = file->f_reada; ++ hidden_file->f_ramax = file->f_ramax; ++ hidden_file->f_raend = file->f_raend; ++ hidden_file->f_ralen = file->f_ralen; ++ hidden_file->f_rawin = file->f_rawin; ++ } ++#endif ++ if (hidden_file->f_op && hidden_file->f_op->llseek) ++ err = hidden_file->f_op->llseek(hidden_file, offset, origin); ++ else ++ err = generic_file_llseek(hidden_file, offset, origin); ++ ++ if (err < 0) ++ goto out; ++ ++ if (err != file->f_pos) { ++ file->f_pos = err; ++ // ION maybe this? ++ // file->f_pos = hidden_file->f_pos; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ file->f_reada = 0; ++#endif ++ file->f_version++; ++ } ++ ++ out: ++ return err; ++} ++ ++ ++/* mk: fanout capable */ ++STATIC ssize_t ++mini_fo_read(file_t *file, char *buf, size_t count, loff_t *ppos) ++{ ++ int err = -EINVAL; ++ file_t *hidden_file = NULL; ++ loff_t pos = *ppos; ++ ++ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { ++ /* Check if trying to read from a directory */ ++ /* printk(KERN_CRIT "mini_fo_read: ERROR: trying to read data from a directory.\n"); */ ++ err = -EISDIR; ++ goto out; ++ } ++ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ if (!hidden_file->f_op || !hidden_file->f_op->read) ++ goto out; ++ ++ err = hidden_file->f_op->read(hidden_file, buf, count, &pos); ++ *ppos = pos; ++ ++ if (err >= 0) { ++ /* atime should also be updated for reads of size zero or more */ ++ fist_copy_attr_atime(file->f_dentry->d_inode, ++ hidden_file->f_dentry->d_inode); ++ } ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ /* ++ * MAJOR HACK ++ * because pread() does not have any way to tell us that it is ++ * our caller, then we don't know for sure if we have to update ++ * the file positions. This hack relies on read() having passed us ++ * the "real" pointer of its struct file's f_pos field. ++ */ ++ if (ppos == &file->f_pos) ++ hidden_file->f_pos = *ppos = pos; ++ if (hidden_file->f_reada) { /* update readahead information if needed */ ++ file->f_reada = hidden_file->f_reada; ++ file->f_ramax = hidden_file->f_ramax; ++ file->f_raend = hidden_file->f_raend; ++ file->f_ralen = hidden_file->f_ralen; ++ file->f_rawin = hidden_file->f_rawin; ++ } ++#else ++ memcpy(&(file->f_ra),&(hidden_file->f_ra),sizeof(struct file_ra_state)); ++#endif ++ ++ out: ++ return err; ++} ++ ++ ++/* this mini_fo_write() does not modify data pages! */ ++STATIC ssize_t ++mini_fo_write(file_t *file, const char *buf, size_t count, loff_t *ppos) ++{ ++ int err = -EINVAL; ++ file_t *hidden_file = NULL; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ loff_t pos = *ppos; ++ ++ /* mk: fan out: */ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ /* This is bad! We have no storage file to write to. This ++ * should never happen because if a file is opened for ++ * writing, a copy should have been made earlier. ++ */ ++ printk(KERN_CRIT "mini_fo: write : ERROR, no storage file to write.\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ inode = file->f_dentry->d_inode; ++ hidden_inode = itohi2(inode); ++ if(!hidden_inode) { ++ printk(KERN_CRIT "mini_fo: write: no sto inode found, not good.\n"); ++ goto out; ++ } ++ ++ if (!hidden_file->f_op || !hidden_file->f_op->write) ++ goto out; ++ ++ /* adjust for append -- seek to the end of the file */ ++ if (file->f_flags & O_APPEND) ++ pos = inode->i_size; ++ ++ err = hidden_file->f_op->write(hidden_file, buf, count, &pos); ++ ++ /* ++ * copy ctime and mtime from lower layer attributes ++ * atime is unchanged for both layers ++ */ ++ if (err >= 0) ++ fist_copy_attr_times(inode, hidden_inode); ++ ++ *ppos = pos; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ /* ++ * XXX: MAJOR HACK ++ * ++ * because pwrite() does not have any way to tell us that it is ++ * our caller, then we don't know for sure if we have to update ++ * the file positions. This hack relies on write() having passed us ++ * the "real" pointer of its struct file's f_pos field. ++ */ ++ if (ppos == &file->f_pos) ++ hidden_file->f_pos = *ppos = pos; ++#endif ++ /* update this inode's size */ ++ if (pos > inode->i_size) ++ inode->i_size = pos; ++ ++ out: ++ return err; ++} ++ ++/* Global variable to hold a file_t pointer. ++ * This serves to allow mini_fo_filldir function to know which file is ++ * beeing read, which is required for two reasons: ++ * ++ * - be able to call wol functions in order to avoid listing deleted ++ * base files. ++ * - if we're reading a directory which is in state 1, we need to ++ * maintain a list (in mini_fo_filldir) of which files allready ++ * have been copied to userspace,to detect files existing in base ++ * and storage and not list them twice. ++ */ ++filldir_t mini_fo_filldir_orig; ++file_t *mini_fo_filldir_file; ++ ++/* mainly copied from fs/readdir.c */ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, ++ u64 ino, unsigned int d_type) ++#else ++mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, ++ ino_t ino, unsigned int d_type) ++#endif ++{ ++ struct getdents_callback * buf = (struct getdents_callback *) __buf; ++ file_t* file = mini_fo_filldir_file; ++ ++ /* In theses states we filter meta files in storage (WOL) */ ++ if(file && (dtopd(file->f_dentry)->state == MODIFIED || ++ dtopd(file->f_dentry)->state == CREATED || ++ dtopd(file->f_dentry)->state == DEL_REWRITTEN)) { ++ ++ int tmp = strlen(META_FILENAME); ++ if(tmp == namlen) { ++ if(!strncmp(name, META_FILENAME, namlen)) ++ return 0; ++ } ++ } ++ ++ /* check if we are merging the contents of storage and base */ ++ if(file && dtopd(file->f_dentry)->state == MODIFIED) { ++ /* check if we are still reading storage contents, if ++ * yes, we just save the name of the file for duplicate ++ * checking later. */ ++ ++ if(!ftopd(file)->rd.sto_done) { ++ /* put file into ndl list */ ++ if(ndl_add_entry(&ftopd(file)->rd, name, namlen)) ++ printk(KERN_CRIT "mini_fo_filldir: Error adding to ndl.\n"); ++ } else { ++ /* check if file has been deleted */ ++ if(meta_check_d_entry(file->f_dentry, name, namlen)) ++ return 0; ++ ++ /* do duplicate checking */ ++ if(ndl_check_entry(&ftopd(file)->rd, name, namlen)) ++ return 0; ++ } ++ } ++ ++ return mini_fo_filldir_orig(buf, name, namlen, offset, ino, d_type); ++} ++ ++ ++STATIC int ++mini_fo_readdir(file_t *file, void *dirent, filldir_t filldir) ++{ ++ int err = 0;/* mk: ??? -ENOTDIR; */ ++ file_t *hidden_file = NULL; ++ file_t *hidden_sto_file = NULL; ++ inode_t *inode; ++ struct getdents_callback *buf; ++ int oldcount; ++ ++#if defined(FIST_FILTER_NAME) || defined(FIST_FILTER_SCA) ++ struct mini_fo_getdents_callback buf; ++#endif /* FIST_FILTER_NAME || FIST_FILTER_SCA */ ++ ++ buf = (struct getdents_callback *) dirent; ++ oldcount = buf->count; ++ inode = file->f_dentry->d_inode; ++ mini_fo_filldir_file = file; ++ mini_fo_filldir_orig = filldir; ++ ++ ftopd(file)->rd.sto_done = 0; ++ do { ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_sto_file = ftohf2(file); ++ err = vfs_readdir(hidden_sto_file, mini_fo_filldir, dirent); ++ file->f_pos = hidden_sto_file->f_pos; ++ if (err > 0) ++ fist_copy_attr_atime(inode, hidden_sto_file->f_dentry->d_inode); ++ /* not finshed yet, we'll be called again */ ++ if (buf->count != oldcount) ++ break; ++ } ++ ++ ftopd(file)->rd.sto_done = 1; ++ ++ if(ftohf(file)) { ++ hidden_file = ftohf(file); ++ err = vfs_readdir(hidden_file, mini_fo_filldir, dirent); ++ file->f_pos = hidden_file->f_pos; ++ if (err > 0) ++ fist_copy_attr_atime(inode, hidden_file->f_dentry->d_inode); ++ } ++ ++ } ++ } while (0); ++ ++ /* mk: ++ * we need to check if all the directory data has been copied to userspace, ++ * or if we will be called again by userspace to complete the operation. ++ */ ++ if(buf->count == oldcount) { ++ ndl_put_list(&ftopd(file)->rd); ++ } ++ ++ /* unset this, safe */ ++ mini_fo_filldir_file = NULL; ++ return err; ++} ++ ++ ++STATIC unsigned int ++mini_fo_poll(file_t *file, poll_table *wait) ++{ ++ unsigned int mask = DEFAULT_POLLMASK; ++ file_t *hidden_file = NULL; ++ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ if (!hidden_file->f_op || !hidden_file->f_op->poll) ++ goto out; ++ ++ mask = hidden_file->f_op->poll(hidden_file, wait); ++ ++ out: ++ return mask; ++} ++ ++/* FIST-LITE special version of mmap */ ++STATIC int ++mini_fo_mmap(file_t *file, vm_area_t *vma) ++{ ++ int err = 0; ++ file_t *hidden_file = NULL; ++ ++ /* fanout capability */ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ ASSERT(hidden_file != NULL); ++ ASSERT(hidden_file->f_op != NULL); ++ ASSERT(hidden_file->f_op->mmap != NULL); ++ ++ vma->vm_file = hidden_file; ++ err = hidden_file->f_op->mmap(hidden_file, vma); ++ get_file(hidden_file); /* make sure it doesn't get freed on us */ ++ fput(file); /* no need to keep extra ref on ours */ ++ ++ return err; ++} ++ ++ ++ ++STATIC int ++mini_fo_open(inode_t *inode, file_t *file) ++{ ++ int err = 0; ++ int hidden_flags; ++ file_t *hidden_file = NULL; ++ dentry_t *hidden_dentry = NULL; ++ ++ /* fanout stuff */ ++ file_t *hidden_sto_file = NULL; ++ dentry_t *hidden_sto_dentry = NULL; ++ ++ __ftopd(file) = ++ kmalloc(sizeof(struct mini_fo_file_info), GFP_KERNEL); ++ if (!ftopd(file)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* init the readdir_helper structure */ ++ INIT_LIST_HEAD(&ftopd(file)->rd.ndl_list); ++ ftopd(file)->rd.ndl_size = 0; ++ ++ /* In certain paths this could stay uninitalized and cause trouble */ ++ ftohf(file) = NULL; ++ ftohf2(file) = NULL; ++ hidden_flags = file->f_flags; ++ ++ /* create storage files? */ ++ if(dtost(file->f_dentry) == UNMODIFIED) { ++ if(!IS_WRITE_FLAG(file->f_flags)) { ++ hidden_dentry = dtohd(file->f_dentry); ++ dget(hidden_dentry); ++ /* dentry_open will decrement mnt refcnt if err. ++ * otherwise fput() will do an mntput() for us upon file close. */ ++ mntget(stopd(inode->i_sb)->hidden_mnt); ++ hidden_file = dentry_open(hidden_dentry, ++ stopd(inode->i_sb)->hidden_mnt, ++ hidden_flags); ++ if (IS_ERR(hidden_file)) { ++ err = PTR_ERR(hidden_file); ++ dput(hidden_dentry); ++ goto out; ++ } ++ ftohf(file) = hidden_file; /* link two files */ ++ goto out; ++ } ++ else { ++ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { ++ err = dir_unmod_to_mod(file->f_dentry); ++ } else ++ err = nondir_unmod_to_mod(file->f_dentry, 1); ++ ++ if (err) { ++ printk("mini_fo_open: ERROR creating storage file.\n"); ++ goto out; ++ } ++ } ++ } ++ hidden_sto_dentry = dtohd2(file->f_dentry); ++ dget(hidden_sto_dentry); ++ ++ if(dtopd(file->f_dentry)->state == MODIFIED) { ++ /* Directorys are special, interpose on both lower level files */ ++ if(S_ISDIR(itohi(inode)->i_mode)) { ++ /* check for invalid file types of lower level files */ ++ if(!(S_ISDIR(itohi(inode)->i_mode) && S_ISDIR(itohi2(inode)->i_mode))) { ++ printk(KERN_CRIT "mini_fo_open: meta data corruption detected.\n"); ++ dput(hidden_sto_dentry); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* lower level directorys are ok, open the base file */ ++ hidden_dentry = dtohd(file->f_dentry); ++ dget(hidden_dentry); ++ ++ mntget(stopd(inode->i_sb)->hidden_mnt); ++ hidden_file = dentry_open(hidden_dentry, ++ stopd(inode->i_sb)->hidden_mnt, ++ hidden_flags); ++ if (IS_ERR(hidden_file)) { ++ err = PTR_ERR(hidden_file); ++ dput(hidden_dentry); ++ dput(hidden_sto_dentry); ++ goto out; ++ } ++ ftohf(file) = hidden_file; /* link the two files */ ++ } ++ } ++ ++ if(!exists_in_storage(file->f_dentry)) { ++ printk(KERN_CRIT "mini_fo_open: invalid file state detected.\n"); ++ err = -EINVAL; ++ dput(hidden_sto_dentry); ++ ++ /* If the base file has been opened, we need to close it here */ ++ if(ftohf(file)) { ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ hidden_file->f_op->flush(hidden_file, NULL); ++#else ++ hidden_file->f_op->flush(hidden_file); ++#endif ++ dput(hidden_dentry); ++ } ++ goto out; ++ } ++ ++ /* ok, now we can safely open the storage file */ ++ mntget(stopd(inode->i_sb)->hidden_mnt2); ++ hidden_sto_file = dentry_open(hidden_sto_dentry, ++ stopd(inode->i_sb)->hidden_mnt2, ++ hidden_flags); ++ ++ /* dentry_open dputs the dentry if it fails */ ++ if (IS_ERR(hidden_sto_file)) { ++ err = PTR_ERR(hidden_sto_file); ++ /* close base file if open */ ++ if(ftohf(file)) { ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ hidden_file->f_op->flush(hidden_file, NULL); ++#else ++ hidden_file->f_op->flush(hidden_file); ++#endif ++ dput(hidden_dentry); ++ } ++ goto out; ++ } ++ ftohf2(file) = hidden_sto_file; /* link storage file */ ++ ++ out: ++ if (err < 0 && ftopd(file)) { ++ kfree(ftopd(file)); ++ } ++ return err; ++} ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_flush(file_t *file, fl_owner_t id) ++#else ++mini_fo_flush(file_t *file) ++#endif ++{ ++ int err1 = 0; /* assume ok (see open.c:close_fp) */ ++ int err2 = 0; ++ file_t *hidden_file = NULL; ++ ++ check_mini_fo_file(file); ++ ++ /* mk: we don't do any state checking here, as its not worth the time. ++ * Just flush the lower level files if they exist. ++ */ ++ if(ftopd(file) != NULL) { ++ if(ftohf(file) != NULL) { ++ hidden_file = ftohf(file); ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ err1 = hidden_file->f_op->flush(hidden_file, id); ++#else ++ err1 = hidden_file->f_op->flush(hidden_file); ++#endif ++ } ++ if(ftohf2(file) != NULL) { ++ hidden_file = ftohf2(file); ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ err2 = hidden_file->f_op->flush(hidden_file, id); ++#else ++ err2 = hidden_file->f_op->flush(hidden_file); ++#endif ++ } ++ } ++ return (err1 | err2); ++} ++ ++ ++STATIC int ++mini_fo_release(inode_t *inode, file_t *file) ++{ ++ int err = 0; ++ file_t *hidden_file = NULL; ++ ++ if (ftopd(file) != NULL) { ++ if(ftohf(file)) { ++ hidden_file = ftohf(file); ++ fput(hidden_file); ++ } ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ fput(hidden_file); ++ } ++ kfree(ftopd(file)); ++ } ++ return err; ++} ++ ++STATIC int ++mini_fo_fsync(file_t *file, dentry_t *dentry, int datasync) ++{ ++ int err1 = 0; ++ int err2 = 0; ++ file_t *hidden_file = NULL; ++ dentry_t *hidden_dentry; ++ ++ check_mini_fo_file(file); ++ ++ if ((hidden_file = ftohf(file)) != NULL) { ++ hidden_dentry = dtohd(dentry); ++ if (hidden_file->f_op && hidden_file->f_op->fsync) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ err1 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ } ++ ++ if ((hidden_file = ftohf2(file)) != NULL) { ++ hidden_dentry = dtohd2(dentry); ++ if (hidden_file->f_op && hidden_file->f_op->fsync) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ err2 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ } ++ else ++ goto err; ++ ++err: ++ return (err1 || err2); ++} ++ ++ ++STATIC int ++mini_fo_fasync(int fd, file_t *file, int flag) ++{ ++ int err1 = 0; ++ int err2 = 0; ++ ++ file_t *hidden_file = NULL; ++ ++ check_mini_fo_file(file); ++ ++ if((hidden_file = ftohf(file)) != NULL) { ++ err1 = hidden_file->f_op->fasync(fd, hidden_file, flag); ++ } ++ if((hidden_file = ftohf2(file)) != NULL) { ++ err2 = hidden_file->f_op->fasync(fd, hidden_file, flag); ++ } ++ ++ return (err1 || err2); ++} ++ ++ ++ ++struct file_operations mini_fo_dir_fops = ++ { ++ read: generic_read_dir, ++ write: mini_fo_write, ++ readdir: mini_fo_readdir, ++ poll: mini_fo_poll, ++ /* ioctl: mini_fo_ioctl, */ ++ mmap: mini_fo_mmap, ++ open: mini_fo_open, ++ flush: mini_fo_flush, ++ release: mini_fo_release, ++ fsync: mini_fo_fsync, ++ fasync: mini_fo_fasync, ++ /* not needed lock: mini_fo_lock, */ ++ /* not needed: readv */ ++ /* not needed: writev */ ++ /* not implemented: sendpage */ ++ /* not implemented: get_unmapped_area */ ++ }; ++ ++struct file_operations mini_fo_main_fops = ++ { ++ llseek: mini_fo_llseek, ++ read: mini_fo_read, ++ write: mini_fo_write, ++ readdir: mini_fo_readdir, ++ poll: mini_fo_poll, ++ /* ioctl: mini_fo_ioctl, */ ++ mmap: mini_fo_mmap, ++ open: mini_fo_open, ++ flush: mini_fo_flush, ++ release: mini_fo_release, ++ fsync: mini_fo_fsync, ++ fasync: mini_fo_fasync, ++ /* not needed: lock: mini_fo_lock, */ ++ /* not needed: readv */ ++ /* not needed: writev */ ++ /* not implemented: sendpage */ ++ /* not implemented: get_unmapped_area */ ++ }; +Index: linux-2.6.21.7/fs/mini_fo/fist.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/fist.h +@@ -0,0 +1,252 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifndef __FIST_H_ ++#define __FIST_H_ ++ ++/* ++ * KERNEL ONLY CODE: ++ */ ++#ifdef __KERNEL__ ++#include ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) ++#include ++#else ++#include ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#ifdef CONFIG_MODVERSIONS ++# define MODVERSIONS ++# include ++#endif /* CONFIG_MODVERSIONS */ ++#endif /* KERNEL_VERSION < 2.6.0 */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#include ++#else ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) ++#include ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#include ++#endif ++ ++#include ++ ++#include ++/* #include */ ++#include ++#include ++ ++/* ++ * MACROS: ++ */ ++ ++/* those mapped to ATTR_* were copied from linux/fs.h */ ++#define FA_MODE ATTR_MODE ++#define FA_UID ATTR_UID ++#define FA_GID ATTR_GID ++#define FA_SIZE ATTR_SIZE ++#define FA_ATIME ATTR_ATIME ++#define FA_MTIME ATTR_MTIME ++#define FA_CTIME ATTR_CTIME ++#define FA_ATIME_SET ATTR_ATIME_SET ++#define FA_MTIME_SET ATTR_MTIME_SET ++#define FA_FORCE ATTR_FORCE ++#define FA_ATTR_FLAGS ATTR_ATTR_FLAG ++ ++/* must be greater than all other ATTR_* flags! */ ++#define FA_NLINK 2048 ++#define FA_BLKSIZE 4096 ++#define FA_BLOCKS 8192 ++#define FA_TIMES (FA_ATIME|FA_MTIME|FA_CTIME) ++#define FA_ALL 0 ++ ++/* macros to manage changes between kernels */ ++#define INODE_DATA(i) (&(i)->i_data) ++ ++#define MIN(x,y) ((x < y) ? (x) : (y)) ++#define MAX(x,y) ((x > y) ? (x) : (y)) ++#define MAXPATHLEN PATH_MAX ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) ++# define lookup_one_len(a,b,c) lookup_one(a,b) ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) */ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) ++# define generic_file_llseek default_llseek ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) */ ++ ++#ifndef SEEK_SET ++# define SEEK_SET 0 ++#endif /* not SEEK_SET */ ++ ++#ifndef SEEK_CUR ++# define SEEK_CUR 1 ++#endif /* not SEEK_CUR */ ++ ++#ifndef SEEK_END ++# define SEEK_END 2 ++#endif /* not SEEK_END */ ++ ++#ifndef DEFAULT_POLLMASK ++# define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) ++#endif /* not DEFAULT_POLLMASK */ ++ ++/* XXX: fix this so fistgen generates kfree() code directly */ ++#define kfree_s(a,b) kfree(a) ++ ++/* ++ * TYPEDEFS: ++ */ ++typedef struct dentry dentry_t; ++typedef struct file file_t; ++typedef struct inode inode_t; ++typedef inode_t vnode_t; ++typedef struct page page_t; ++typedef struct qstr qstr_t; ++typedef struct super_block super_block_t; ++typedef super_block_t vfs_t; ++typedef struct vm_area_struct vm_area_t; ++ ++ ++/* ++ * EXTERNALS: ++ */ ++ ++#define FPPF(str,page) printk("PPF %s 0x%x/%d: Lck:%d Err:%d Ref:%d Upd:%d Other::%d:%d:%d:%d:\n", \ ++ str, \ ++ (int) page, \ ++ (int) page->index, \ ++ (PageLocked(page) ? 1 : 0), \ ++ (PageError(page) ? 1 : 0), \ ++ (PageReferenced(page) ? 1 : 0), \ ++ (Page_Uptodate(page) ? 1 : 0), \ ++ (PageDecrAfter(page) ? 1 : 0), \ ++ (PageSlab(page) ? 1 : 0), \ ++ (PageSwapCache(page) ? 1 : 0), \ ++ (PageReserved(page) ? 1 : 0) \ ++ ) ++#define EZKDBG printk("EZK %s:%d:%s\n",__FILE__,__LINE__,__FUNCTION__) ++#if 0 ++# define EZKDBG1 printk("EZK %s:%d\n",__FILE__,__LINE__) ++#else ++# define EZKDBG1 ++#endif ++ ++extern int fist_get_debug_value(void); ++extern int fist_set_debug_value(int val); ++#if 0 /* mini_fo doesn't need these */ ++extern void fist_dprint_internal(int level, char *str,...); ++extern void fist_print_dentry(char *str, const dentry_t *dentry); ++extern void fist_print_inode(char *str, const inode_t *inode); ++extern void fist_print_file(char *str, const file_t *file); ++extern void fist_print_buffer_flags(char *str, struct buffer_head *buffer); ++extern void fist_print_page_flags(char *str, page_t *page); ++extern void fist_print_page_bytes(char *str, page_t *page); ++extern void fist_print_pte_flags(char *str, const page_t *page); ++extern void fist_checkinode(inode_t *inode, char *msg); ++extern void fist_print_sb(char *str, const super_block_t *sb); ++ ++/* §$% by mk: special debug functions */ ++extern void fist_mk_print_dentry(char *str, const dentry_t *dentry); ++extern void fist_mk_print_inode(char *str, const inode_t *inode); ++ ++extern char *add_indent(void); ++extern char *del_indent(void); ++#endif/* mini_fo doesn't need these */ ++ ++ ++#define STATIC ++#define ASSERT(EX) \ ++do { \ ++ if (!(EX)) { \ ++ printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \ ++ __FILE__, __LINE__, __FUNCTION__); \ ++ (*((char *)0))=0; \ ++ } \ ++} while (0) ++/* same ASSERT, but tell me who was the caller of the function */ ++#define ASSERT2(EX) \ ++do { \ ++ if (!(EX)) { \ ++ printk(KERN_CRIT "ASSERTION FAILED (caller): %s at %s:%d (%s)\n", #EX, \ ++ file, line, func); \ ++ (*((char *)0))=0; \ ++ } \ ++} while (0) ++ ++#if 0 /* mini_fo doesn't need these */ ++#define dprintk(format, args...) printk(KERN_DEBUG format, ##args) ++#define fist_dprint(level, str, args...) fist_dprint_internal(level, KERN_DEBUG str, ## args) ++#define print_entry_location() fist_dprint(4, "%sIN: %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__) ++#define print_exit_location() fist_dprint(4, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__) ++#define print_exit_status(status) fist_dprint(4, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status) ++#define print_exit_pointer(status) \ ++do { \ ++ if (IS_ERR(status)) \ ++ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ ++ else \ ++ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: 0x%x\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ ++} while (0) ++#endif/* mini_fo doesn't need these */ ++ ++#endif /* __KERNEL__ */ ++ ++ ++/* ++ * DEFINITIONS FOR USER AND KERNEL CODE: ++ * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest ++ * are auto-generated automatically based on the user's .fist file.) ++ */ ++# define FIST_IOCTL_GET_DEBUG_VALUE _IOR(0x15, 1, int) ++# define FIST_IOCTL_SET_DEBUG_VALUE _IOW(0x15, 2, int) ++ ++#endif /* not __FIST_H_ */ +Index: linux-2.6.21.7/fs/mini_fo/inode.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/inode.c +@@ -0,0 +1,1564 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd) ++#else ++mini_fo_create(inode_t *dir, dentry_t *dentry, int mode) ++#endif ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = create_sto_reg_file(dentry, mode, nd); ++#else ++ err = create_sto_reg_file(dentry, mode); ++#endif ++ check_mini_fo_dentry(dentry); ++ return err; ++} ++ ++ ++STATIC dentry_t * ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_lookup(inode_t *dir, dentry_t *dentry, struct nameidata* nd) ++#else ++mini_fo_lookup(inode_t *dir, dentry_t *dentry) ++#endif ++{ ++ int err = 0; ++ dentry_t *hidden_dir_dentry; ++ dentry_t *hidden_dentry = NULL; ++ ++ dentry_t *hidden_sto_dir_dentry; ++ dentry_t *hidden_sto_dentry = NULL; ++ ++ /* whiteout flag */ ++ int del_flag = 0; ++ char *bpath = NULL; ++ ++ const char *name; ++ unsigned int namelen; ++ ++ /* Don't allow lookups of META-files */ ++ namelen = strlen(META_FILENAME); ++ if(namelen == dentry->d_name.len) { ++ if(!strncmp(dentry->d_name.name, META_FILENAME, namelen)) { ++ err = -ENOENT; ++ goto out; ++ } ++ } ++ ++ hidden_dir_dentry = dtohd(dentry->d_parent); ++ hidden_sto_dir_dentry = dtohd2(dentry->d_parent); ++ ++ name = dentry->d_name.name; ++ namelen = dentry->d_name.len; ++ ++ /* must initialize dentry operations */ ++ dentry->d_op = &mini_fo_dops; ++ ++ /* setup the del_flag */ ++ del_flag = __meta_check_d_entry(dir, name, namelen); ++ bpath = __meta_check_r_entry(dir, name, namelen); ++ ++ /* perform the lookups of base and storage files: ++ * ++ * This caused some serious trouble, as a lookup_one_len passing ++ * a negative dentry oopses. Solution is to only do the lookup ++ * if the dentry is positive, else we set it to NULL ++ * More trouble, who said a *_dir_dentry can't be NULL? ++ */ ++ if(bpath) { ++ /* Cross-Interposing (C), yeah! */ ++ hidden_dentry = bpath_walk(dir->i_sb, bpath); ++ if(!hidden_dentry || !hidden_dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo_lookup: bpath_walk failed.\n"); ++ err= -EINVAL; ++ goto out; ++ } ++ ++ /* this can be set up safely without fear of spaghetti ++ * interposing as it is only used for copying times */ ++ hidden_dir_dentry = hidden_dentry->d_parent; ++ kfree(bpath); ++ } ++ else if(hidden_dir_dentry && hidden_dir_dentry->d_inode) ++ hidden_dentry = ++ lookup_one_len(name, hidden_dir_dentry, namelen); ++ else ++ hidden_dentry = NULL; ++ ++ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) ++ hidden_sto_dentry = ++ lookup_one_len(name, hidden_sto_dir_dentry, namelen); ++ else ++ hidden_sto_dentry = NULL; ++ ++ /* catch error in lookup */ ++ if (IS_ERR(hidden_dentry) || IS_ERR(hidden_sto_dentry)) { ++ /* mk: we need to call dput on the dentry, whose ++ * lookup_one_len operation failed, in order to avoid ++ * unmount trouble. ++ */ ++ if(IS_ERR(hidden_dentry)) { ++ printk(KERN_CRIT "mini_fo_lookup: ERR from base dentry, lookup failed.\n"); ++ err = PTR_ERR(hidden_dentry); ++ } else { ++ dput(hidden_dentry); ++ } ++ if(IS_ERR(hidden_sto_dentry)) { ++ printk(KERN_CRIT "mini_fo_lookup: ERR from storage dentry, lookup failed.\n"); ++ err = PTR_ERR(hidden_sto_dentry); ++ } else { ++ dput(hidden_sto_dentry); ++ } ++ goto out; ++ } ++ ++ /* allocate dentry private data */ ++ __dtopd(dentry) = (struct mini_fo_dentry_info *) ++ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); ++ ++ if (!dtopd(dentry)) { ++ err = -ENOMEM; ++ goto out_dput; ++ } ++ ++ /* check for different states of the mini_fo file to be looked up. */ ++ ++ /* state 1, file has been modified */ ++ if(hidden_dentry && hidden_sto_dentry && ++ hidden_dentry->d_inode && hidden_sto_dentry->d_inode && !del_flag) { ++ ++ /* update parent directory's atime */ ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = MODIFIED; ++ dtohd(dentry) = hidden_dentry; ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ err = mini_fo_tri_interpose(hidden_dentry, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state1).\n"); ++ goto out_free; ++ } ++ goto out; ++ } ++ /* state 2, file is unmodified */ ++ if(hidden_dentry && hidden_dentry->d_inode && !del_flag) { ++ ++ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = UNMODIFIED; ++ dtohd(dentry) = hidden_dentry; ++ dtohd2(dentry) = hidden_sto_dentry; /* could be negative */ ++ ++ err = mini_fo_tri_interpose(hidden_dentry, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state2).\n"); ++ goto out_free; ++ } ++ goto out; ++ } ++ /* state 3, file has been newly created */ ++ if(hidden_sto_dentry && hidden_sto_dentry->d_inode && !del_flag) { ++ ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ dtopd(dentry)->state = CREATED; ++ dtohd(dentry) = hidden_dentry; /* could be negative */ ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ err = mini_fo_tri_interpose(hidden_dentry, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state3).\n"); ++ goto out_free; ++ } ++ goto out; ++ } ++ ++ /* state 4, file has deleted and created again. */ ++ if(hidden_dentry && hidden_sto_dentry && ++ hidden_dentry->d_inode && ++ hidden_sto_dentry->d_inode && del_flag) { ++ ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ dtopd(dentry)->state = DEL_REWRITTEN; ++ dtohd(dentry) = NULL; ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ err = mini_fo_tri_interpose(NULL, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state4).\n"); ++ goto out_free; ++ } ++ /* We will never need this dentry again, as the file has been ++ * deleted from base */ ++ dput(hidden_dentry); ++ goto out; ++ } ++ /* state 5, file has been deleted in base */ ++ if(hidden_dentry && hidden_sto_dentry && ++ hidden_dentry->d_inode && ++ !hidden_sto_dentry->d_inode && del_flag) { ++ ++ /* check which parents atime we need for updating */ ++ if(hidden_sto_dir_dentry->d_inode) ++ fist_copy_attr_atime(dir, ++ hidden_sto_dir_dentry->d_inode); ++ else ++ fist_copy_attr_atime(dir, ++ hidden_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = DELETED; ++ dtohd(dentry) = NULL; ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ /* add negative dentry to dcache to speed up lookups */ ++ d_add(dentry, NULL); ++ dput(hidden_dentry); ++ goto out; ++ } ++ /* state 6, file does not exist */ ++ if(((hidden_dentry && !hidden_dentry->d_inode) || ++ (hidden_sto_dentry && !hidden_sto_dentry->d_inode)) && !del_flag) ++ { ++ /* check which parents atime we need for updating */ ++ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ else ++ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = NON_EXISTANT; ++ dtohd(dentry) = hidden_dentry; ++ dtohd2(dentry) = hidden_sto_dentry; ++ d_add(dentry, NULL); ++ goto out; ++ } ++ ++ /* if we get to here, were in an invalid state. bad. */ ++ printk(KERN_CRIT "mini_fo_lookup: ERROR, meta data corruption detected.\n"); ++ ++ /* end state checking */ ++ out_free: ++ d_drop(dentry); /* so that our bad dentry will get destroyed */ ++ kfree(dtopd(dentry)); ++ __dtopd(dentry) = NULL; /* be safe */ ++ ++ out_dput: ++ if(hidden_dentry) ++ dput(hidden_dentry); ++ if(hidden_sto_dentry) ++ dput(hidden_sto_dentry); /* drops usage count and marks for release */ ++ ++ out: ++ /* initalize wol if file exists and is directory */ ++ if(dentry->d_inode) { ++ if(S_ISDIR(dentry->d_inode->i_mode)) { ++ itopd(dentry->d_inode)->deleted_list_size = -1; ++ itopd(dentry->d_inode)->renamed_list_size = -1; ++ meta_build_lists(dentry); ++ } ++ } ++ return ERR_PTR(err); ++} ++ ++ ++STATIC int ++mini_fo_link(dentry_t *old_dentry, inode_t *dir, dentry_t *new_dentry) ++{ ++ int err; ++ dentry_t *hidden_old_dentry; ++ dentry_t *hidden_new_dentry; ++ dentry_t *hidden_dir_dentry; ++ ++ ++ check_mini_fo_dentry(old_dentry); ++ check_mini_fo_dentry(new_dentry); ++ check_mini_fo_inode(dir); ++ ++ /* no links to directorys and existing targets target allowed */ ++ if(S_ISDIR(old_dentry->d_inode->i_mode) || ++ is_mini_fo_existant(new_dentry)) { ++ err = -EPERM; ++ goto out; ++ } ++ ++ /* bring it directly from unmod to del_rew */ ++ if(dtost(old_dentry) == UNMODIFIED) { ++ err = nondir_unmod_to_mod(old_dentry, 1); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ err = meta_add_d_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ dput(dtohd(old_dentry)); ++ dtohd(old_dentry) = NULL; ++ dtost(old_dentry) = DEL_REWRITTEN; ++ } ++ ++ err = get_neg_sto_dentry(new_dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ hidden_old_dentry = dtohd2(old_dentry); ++ hidden_new_dentry = dtohd2(new_dentry); ++ ++ dget(hidden_old_dentry); ++ dget(hidden_new_dentry); ++ ++ /* was: hidden_dir_dentry = lock_parent(hidden_new_dentry); */ ++ hidden_dir_dentry = dget(hidden_new_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = vfs_link(hidden_old_dentry, ++ hidden_dir_dentry->d_inode, ++ hidden_new_dentry); ++ if (err || !hidden_new_dentry->d_inode) ++ goto out_lock; ++ ++ dtost(new_dentry) = CREATED; ++ err = mini_fo_tri_interpose(NULL, hidden_new_dentry, new_dentry, dir->i_sb, 0); ++ if (err) ++ goto out_lock; ++ ++ fist_copy_attr_timesizes(dir, hidden_new_dentry->d_inode); ++ /* propagate number of hard-links */ ++ old_dentry->d_inode->i_nlink = itohi2(old_dentry->d_inode)->i_nlink; ++ ++ out_lock: ++ /* was: unlock_dir(hidden_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_dir_dentry); ++ ++ dput(hidden_new_dentry); ++ dput(hidden_old_dentry); ++ if (!new_dentry->d_inode) ++ d_drop(new_dentry); ++ ++ out: ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_unlink(inode_t *dir, dentry_t *dentry) ++{ ++ int err = 0; ++ ++ dget(dentry); ++ if(dtopd(dentry)->state == MODIFIED) { ++ err = nondir_mod_to_del(dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == UNMODIFIED) { ++ err = nondir_unmod_to_del(dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == CREATED) { ++ err = nondir_creat_to_del(dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == DEL_REWRITTEN) { ++ err = nondir_del_rew_to_del(dentry); ++ goto out; ++ } ++ ++ printk(KERN_CRIT "mini_fo_unlink: ERROR, invalid state detected.\n"); ++ ++ out: ++ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); ++ ++ if(!err) { ++ /* is this causing my pain? d_delete(dentry); */ ++ d_drop(dentry); ++ } ++ ++ dput(dentry); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_symlink(inode_t *dir, dentry_t *dentry, const char *symname) ++{ ++ int err=0; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ umode_t mode; ++#endif ++ ++ /* Fail if the symlink file exists */ ++ if(!(dtost(dentry) == DELETED || ++ dtost(dentry) == NON_EXISTANT)) { ++ err = -EEXIST; ++ goto out; ++ } ++ ++ err = get_neg_sto_dentry(dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ dget(hidden_sto_dentry); ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ mode = S_IALLUGO; ++ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, symname, mode); ++#else ++ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ symname); ++#endif ++ if (err || !hidden_sto_dentry->d_inode) ++ goto out_lock; ++ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } else if(dtost(dentry) == NON_EXISTANT) { ++ dtost(dentry) = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); ++ ++ out_lock: ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ dput(hidden_sto_dentry); ++ if (!dentry->d_inode) ++ d_drop(dentry); ++ out: ++ return err; ++} ++ ++STATIC int ++mini_fo_mkdir(inode_t *dir, dentry_t *dentry, int mode) ++{ ++ int err; ++ ++ err = create_sto_dir(dentry, mode); ++ ++ check_mini_fo_dentry(dentry); ++ ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_rmdir(inode_t *dir, dentry_t *dentry) ++{ ++ int err = 0; ++ ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ dentry_t *meta_dentry; ++ inode_t *hidden_sto_dir = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ check_mini_fo_inode(dir); ++ ++ dget(dentry); ++ if(dtopd(dentry)->state == MODIFIED) { ++ /* XXX: disabled, because it does not bother to check files on ++ * the original filesystem - just a hack, but better than simply ++ * removing it without testing */ ++ err = -EINVAL; ++ goto out; ++ ++ hidden_sto_dir = itohi2(dir); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was:hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* Delete an old WOL file contained in the storage dir */ ++ meta_dentry = lookup_one_len(META_FILENAME, ++ hidden_sto_dentry, ++ strlen(META_FILENAME)); ++ if(meta_dentry->d_inode) { ++ err = vfs_unlink(hidden_sto_dentry->d_inode, meta_dentry); ++ dput(meta_dentry); ++ if(!err) ++ d_delete(meta_dentry); ++ } ++ ++ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ ++ dput(dtohd(dentry)); ++ ++ dtohd(dentry) = NULL; ++ dtopd(dentry)->state = DELETED; ++ ++ /* carefull with R files */ ++ if( __meta_is_r_entry(dir, ++ dentry->d_name.name, ++ dentry->d_name.len) == 1) { ++ err = meta_remove_r_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: rmdir: meta_remove_r_entry failed.\n"); ++ goto out; ++ } ++ } ++ else { ++ /* ok, add deleted file to META */ ++ meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ } ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == UNMODIFIED) { ++ /* XXX: simply adding it to the delete list here is fscking dangerous! ++ * as a temporary hack, i will disable rmdir on unmodified directories ++ * for now. ++ */ ++ err = -EINVAL; ++ goto out; ++ ++ err = get_neg_sto_dentry(dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* dput base dentry, this will relase the inode and free the ++ * dentry, as we will never need it again. */ ++ dput(dtohd(dentry)); ++ dtohd(dentry) = NULL; ++ dtopd(dentry)->state = DELETED; ++ ++ /* add deleted file to META-file */ ++ meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == CREATED) { ++ hidden_sto_dir = itohi2(dir); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* Delete an old WOL file contained in the storage dir */ ++ meta_dentry = lookup_one_len(META_FILENAME, ++ hidden_sto_dentry, ++ strlen(META_FILENAME)); ++ if(meta_dentry->d_inode) { ++ /* is this necessary? dget(meta_dentry); */ ++ err = vfs_unlink(hidden_sto_dentry->d_inode, ++ meta_dentry); ++ dput(meta_dentry); ++ if(!err) ++ d_delete(meta_dentry); ++ } ++ ++ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ dtopd(dentry)->state = NON_EXISTANT; ++ ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ goto out; ++ } ++ else if(dtopd(dentry)->state == DEL_REWRITTEN) { ++ hidden_sto_dir = itohi2(dir); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* Delete an old WOL file contained in the storage dir */ ++ meta_dentry = lookup_one_len(META_FILENAME, ++ hidden_sto_dentry, ++ strlen(META_FILENAME)); ++ if(meta_dentry->d_inode) { ++ /* is this necessary? dget(meta_dentry); */ ++ err = vfs_unlink(hidden_sto_dentry->d_inode, ++ meta_dentry); ++ dput(meta_dentry); ++ if(!err) ++ d_delete(meta_dentry); ++ } ++ ++ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ dtopd(dentry)->state = DELETED; ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ goto out; ++ } ++ ++ printk(KERN_CRIT "mini_fo_rmdir: ERROR, invalid state detected.\n"); ++ ++ out: ++ if(!err) { ++ d_drop(dentry); ++ } ++ ++ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); ++ dput(dentry); ++ ++ return err; ++} ++ ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, dev_t dev) ++#else ++mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, int dev) ++#endif ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++ err = create_sto_nod(dentry, mode, dev); ++ if(err) { ++ printk(KERN_CRIT "mini_fo_mknod: creating sto nod failed.\n"); ++ err = -EINVAL; ++ } ++ ++ check_mini_fo_dentry(dentry); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_rename(inode_t *old_dir, dentry_t *old_dentry, ++ inode_t *new_dir, dentry_t *new_dentry) ++{ ++ /* dispatch */ ++ if(S_ISDIR(old_dentry->d_inode->i_mode)) ++ return rename_directory(old_dir, old_dentry, new_dir, new_dentry); ++ return rename_nondir(old_dir, old_dentry, new_dir, new_dentry); ++ ++} ++ ++int rename_directory(inode_t *old_dir, dentry_t *old_dentry, ++ inode_t *new_dir, dentry_t *new_dentry) ++{ ++ int err, bpath_len; ++ char *bpath; ++ ++ dentry_t *hidden_old_dentry; ++ dentry_t *hidden_new_dentry; ++ dentry_t *hidden_old_dir_dentry; ++ dentry_t *hidden_new_dir_dentry; ++ ++ err = 0; ++ bpath = NULL; ++ bpath_len = 0; ++ ++ /* this is a test, chuck out if it works */ ++ if(!(dtopd(new_dentry)->state == DELETED || ++ dtopd(new_dentry)->state == NON_EXISTANT)) { ++ printk(KERN_CRIT "mini_fo: rename_directory: \ ++ uh, ah, new_dentry not negative.\n"); ++ /* return -1; */ ++ } ++ ++ /* state = UNMODIFIED */ ++ if(dtopd(old_dentry)->state == UNMODIFIED) { ++ err = dir_unmod_to_mod(old_dentry); ++ if (err) ++ goto out; ++ } ++ ++ /* state = MODIFIED */ ++ if(dtopd(old_dentry)->state == MODIFIED) { ++ bpath = meta_check_r_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if(bpath) { ++ err = meta_remove_r_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: rename_directory:\ ++ meta_remove_r_entry \ ++ failed.\n"); ++ goto out; ++ } ++ err = meta_add_r_entry(new_dentry->d_parent, ++ bpath, ++ strlen(bpath), ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ kfree(bpath); ++ } ++ else {/* wol it */ ++ err = meta_add_d_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if (err) ++ goto out; ++ /* put it on rename list */ ++ err = get_mini_fo_bpath(old_dentry, ++ &bpath, ++ &bpath_len); ++ if (err) ++ goto out; ++ err = meta_add_r_entry(new_dentry->d_parent, ++ bpath, bpath_len, ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ if (err) ++ goto out; ++ } ++ /* no state change, MODIFIED stays MODIFIED */ ++ } ++ /* state = CREATED */ ++ if(dtopd(old_dentry)->state == CREATED || ++ dtopd(old_dentry)->state == DEL_REWRITTEN) { ++ if(dtohd(old_dentry)) ++ dput(dtohd(old_dentry)); ++ ++ if(dtopd(new_dentry)->state == DELETED) { ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ dtohd(old_dentry) = NULL; ++ } ++ else if(dtopd(new_dentry)->state == NON_EXISTANT) { ++ dtopd(old_dentry)->state = CREATED; ++ /* steal new dentry's neg. base dentry */ ++ dtohd(old_dentry) = dtohd(new_dentry); ++ dtohd(new_dentry) = NULL; ++ } ++ } ++ if(dtopd(new_dentry)->state == UNMODIFIED || ++ dtopd(new_dentry)->state == NON_EXISTANT) { ++ err = get_neg_sto_dentry(new_dentry); ++ if(err) ++ goto out; ++ } ++ ++ /* now move sto file */ ++ hidden_old_dentry = dtohd2(old_dentry); ++ hidden_new_dentry = dtohd2(new_dentry); ++ ++ dget(hidden_old_dentry); ++ dget(hidden_new_dentry); ++ ++ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); ++ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); ++ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ ++ err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, ++ hidden_new_dir_dentry->d_inode, hidden_new_dentry); ++ if(err) ++ goto out_lock; ++ ++ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); ++ if (new_dir != old_dir) ++ fist_copy_attr_all(old_dir, ++ hidden_old_dir_dentry->d_inode); ++ ++ out_lock: ++ /* double_unlock will dput the new/old parent dentries ++ * whose refcnts were incremented via get_parent above. */ ++ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ dput(hidden_new_dentry); ++ dput(hidden_old_dentry); ++ ++ out: ++ return err; ++} ++ ++int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, ++ inode_t *new_dir, dentry_t *new_dentry) ++{ ++ int err=0; ++ ++ check_mini_fo_dentry(old_dentry); ++ check_mini_fo_dentry(new_dentry); ++ check_mini_fo_inode(old_dir); ++ check_mini_fo_inode(new_dir); ++ ++ /* state: UNMODIFIED */ ++ if(dtost(old_dentry) == UNMODIFIED) { ++ err = nondir_unmod_to_mod(old_dentry, 1); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ /* the easy states */ ++ if(exists_in_storage(old_dentry)) { ++ ++ dentry_t *hidden_old_dentry; ++ dentry_t *hidden_new_dentry; ++ dentry_t *hidden_old_dir_dentry; ++ dentry_t *hidden_new_dir_dentry; ++ ++ /* if old file is MODIFIED, add it to the deleted_list */ ++ if(dtopd(old_dentry)->state == MODIFIED) { ++ meta_add_d_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ ++ dput(dtohd(old_dentry)); ++ } ++ /* if old file is CREATED, we only release the base dentry */ ++ if(dtopd(old_dentry)->state == CREATED) { ++ if(dtohd(old_dentry)) ++ dput(dtohd(old_dentry)); ++ } ++ ++ /* now setup the new states (depends on new_dentry state) */ ++ /* new dentry state = MODIFIED */ ++ if(dtopd(new_dentry)->state == MODIFIED) { ++ meta_add_d_entry(new_dentry->d_parent, ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ ++ /* new dentry will be d_put'ed later by the vfs ++ * so don't do it here ++ * dput(dtohd(new_dentry)); ++ */ ++ dtohd(old_dentry) = NULL; ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ } ++ /* new dentry state = UNMODIFIED */ ++ else if(dtopd(new_dentry)->state == UNMODIFIED) { ++ if(get_neg_sto_dentry(new_dentry)) ++ return -EINVAL; ++ ++ meta_add_d_entry(new_dentry->d_parent, ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ ++ /* is this right??? */ ++ /*dput(dtohd(new_dentry));*/ ++ dtohd(old_dentry) = NULL; ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ } ++ /* new dentry state = CREATED */ ++ else if(dtopd(new_dentry)->state == CREATED) { ++ /* we keep the neg. base dentry (if exists) */ ++ dtohd(old_dentry) = dtohd(new_dentry); ++ /* ...and set it to Null, or we'll get ++ * dcache.c:345 if it gets dput twice... */ ++ dtohd(new_dentry) = NULL; ++ dtopd(old_dentry)->state = CREATED; ++ } ++ /* new dentry state = NON_EXISTANT */ ++ else if(dtopd(new_dentry)->state == NON_EXISTANT) { ++ if(get_neg_sto_dentry(new_dentry)) ++ return -EINVAL; ++ ++ /* we keep the neg. base dentry (if exists) */ ++ dtohd(old_dentry) = dtohd(new_dentry); ++ /* ...and set it to Null, or we'll get ++ * Dr. dcache.c:345 if it gets dput twice... */ ++ dtohd(new_dentry) = NULL; ++ dtopd(old_dentry)->state = CREATED; ++ } ++ /* new dentry state = DEL_REWRITTEN or DELETED */ ++ else if(dtopd(new_dentry)->state == DEL_REWRITTEN || ++ dtopd(new_dentry)->state == DELETED) { ++ dtohd(old_dentry) = NULL; ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ } ++ else { /* not possible, uhh, ahh */ ++ printk(KERN_CRIT ++ "mini_fo: rename_reg_file: invalid state detected [1].\n"); ++ return -1; ++ } ++ ++ /* now we definitely have a sto file */ ++ hidden_old_dentry = dtohd2(old_dentry); ++ hidden_new_dentry = dtohd2(new_dentry); ++ ++ dget(hidden_old_dentry); ++ dget(hidden_new_dentry); ++ ++ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); ++ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); ++ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ ++ err = vfs_rename(hidden_old_dir_dentry->d_inode, ++ hidden_old_dentry, ++ hidden_new_dir_dentry->d_inode, ++ hidden_new_dentry); ++ if(err) ++ goto out_lock; ++ ++ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); ++ if (new_dir != old_dir) ++ fist_copy_attr_all(old_dir, hidden_old_dir_dentry->d_inode); ++ ++ out_lock: ++ /* double_unlock will dput the new/old parent dentries ++ * whose refcnts were incremented via get_parent above. ++ */ ++ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ dput(hidden_new_dentry); ++ dput(hidden_old_dentry); ++ out: ++ return err; ++ } ++ else { /* invalid state */ ++ printk(KERN_CRIT "mini_fo: rename_reg_file: ERROR: invalid state detected [2].\n"); ++ return -1; ++ } ++} ++ ++ ++STATIC int ++mini_fo_readlink(dentry_t *dentry, char *buf, int bufsiz) ++{ ++ int err=0; ++ dentry_t *hidden_dentry = NULL; ++ ++ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { ++ hidden_dentry = dtohd2(dentry); ++ } else if(dtohd(dentry) && dtohd(dentry)->d_inode) { ++ hidden_dentry = dtohd(dentry); ++ } else { ++ goto out; ++ } ++ ++ if (!hidden_dentry->d_inode->i_op || ++ !hidden_dentry->d_inode->i_op->readlink) { ++ err = -EINVAL; goto out; ++ } ++ ++ err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, ++ buf, ++ bufsiz); ++ if (err > 0) ++ fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode); ++ ++ out: ++ return err; ++} ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ++static int mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) ++#else ++static void* mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) ++#endif ++{ ++ char *buf; ++ int len = PAGE_SIZE, err; ++ mm_segment_t old_fs; ++ ++ /* in 2.6 this is freed by mini_fo_put_link called by __do_follow_link */ ++ buf = kmalloc(len, GFP_KERNEL); ++ if (!buf) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* read the symlink, and then we will follow it */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = dentry->d_inode->i_op->readlink(dentry, buf, len); ++ set_fs(old_fs); ++ if (err < 0) { ++ kfree(buf); ++ buf = NULL; ++ goto out; ++ } ++ buf[err] = 0; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ nd_set_link(nd, buf); ++ err = 0; ++#else ++ err = vfs_follow_link(nd, buf); ++#endif ++ ++ out: ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ kfree(buf); ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ++ return err; ++#else ++ return ERR_PTR(err); ++#endif ++} ++ ++STATIC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ++void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd) ++#else ++void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) ++#endif ++{ ++ char *link; ++ link = nd_get_link(nd); ++ kfree(link); ++} ++#endif ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_permission(inode_t *inode, int mask, struct nameidata *nd) ++#else ++mini_fo_permission(inode_t *inode, int mask) ++#endif ++{ ++ inode_t *hidden_inode; ++ int mode; ++ int err; ++ ++ if(itohi2(inode)) { ++ hidden_inode = itohi2(inode); ++ } else { ++ hidden_inode = itohi(inode); ++ } ++ mode = inode->i_mode; ++ ++ /* not really needed, as permission handles everything: ++ * err = vfs_permission(inode, mask); ++ * if (err) ++ * goto out; ++ */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = permission(hidden_inode, mask, nd); ++#else ++ err = permission(hidden_inode, mask); ++#endif ++ ++ /* out: */ ++ return err; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++STATIC int ++mini_fo_inode_revalidate(dentry_t *dentry) ++{ ++ int err = 0; ++ dentry_t *hidden_dentry; ++ inode_t *hidden_inode; ++ ++ ASSERT(dentry->d_inode); ++ ASSERT(itopd(dentry->d_inode)); ++ ++ if(itohi2(dentry->d_inode)) { ++ hidden_dentry = dtohd2(dentry); ++ hidden_inode = hidden_dentry->d_inode; ++ } else if(itohi(dentry->d_inode)) { ++ hidden_dentry = dtohd(dentry); ++ hidden_inode = hidden_dentry->d_inode; ++ } else { ++ printk(KERN_CRIT "mini_fo_inode_revalidate: ERROR, invalid state detected.\n"); ++ err = -ENOENT; ++ goto out; ++ } ++ if (hidden_inode && hidden_inode->i_op && hidden_inode->i_op->revalidate){ ++ err = hidden_inode->i_op->revalidate(hidden_dentry); ++ if (err) ++ goto out; ++ } ++ fist_copy_attr_all(dentry->d_inode, hidden_inode); ++ out: ++ return err; ++} ++#endif ++ ++STATIC int ++mini_fo_setattr(dentry_t *dentry, struct iattr *ia) ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(!is_mini_fo_existant(dentry)) { ++ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [1].\n"); ++ goto out; ++ } ++ ++ if(dtost(dentry) == UNMODIFIED) { ++ if(!IS_COPY_FLAG(ia->ia_valid)) ++ goto out; /* we ignore these changes to base */ ++ ++ if(S_ISDIR(dentry->d_inode->i_mode)) { ++ err = dir_unmod_to_mod(dentry); ++ } else { ++ /* we copy contents if file is not beeing truncated */ ++ if(S_ISREG(dentry->d_inode->i_mode) && ++ !(ia->ia_size == 0 && (ia->ia_valid & ATTR_SIZE))) { ++ err = nondir_unmod_to_mod(dentry, 1); ++ } else ++ err = nondir_unmod_to_mod(dentry, 0); ++ } ++ if(err) { ++ err = -EINVAL; ++ printk(KERN_CRIT "mini_fo_setattr: ERROR changing states.\n"); ++ goto out; ++ } ++ } ++ if(!exists_in_storage(dentry)) { ++ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [2].\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ASSERT(dentry->d_inode); ++ ASSERT(dtohd2(dentry)); ++ ASSERT(itopd(dentry->d_inode)); ++ ASSERT(itohi2(dentry->d_inode)); ++ ++ err = notify_change(dtohd2(dentry), ia); ++ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); ++ out: ++ return err; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++STATIC int ++mini_fo_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) ++{ ++ int err = 0; ++ dentry_t *hidden_dentry; ++ ++ ASSERT(dentry->d_inode); ++ ASSERT(itopd(dentry->d_inode)); ++ ++ if(itohi2(dentry->d_inode)) { ++ hidden_dentry = dtohd2(dentry); ++ } else if(itohi(dentry->d_inode)) { ++ hidden_dentry = dtohd(dentry); ++ } else { ++ printk(KERN_CRIT "mini_fo_getattr: ERROR, invalid state detected.\n"); ++ err = -ENOENT; ++ goto out; ++ } ++ fist_copy_attr_all(dentry->d_inode, hidden_dentry->d_inode); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ generic_fillattr(dentry->d_inode, stat); ++ if (!stat->blksize) { ++ struct super_block *s = hidden_dentry->d_inode->i_sb; ++ unsigned blocks; ++ blocks = (stat->size+s->s_blocksize-1) >> s->s_blocksize_bits; ++ stat->blocks = (s->s_blocksize / 512) * blocks; ++ stat->blksize = s->s_blocksize; ++ } ++ out: ++ return err; ++} ++#endif ++ ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++#if 0 /* no xattr_alloc() and xattr_free() */ ++/* This is lifted from fs/xattr.c */ ++static void * ++xattr_alloc(size_t size, size_t limit) ++{ ++ void *ptr; ++ ++ if (size > limit) ++ return ERR_PTR(-E2BIG); ++ ++ if (!size) /* size request, no buffer is needed */ ++ return NULL; ++ else if (size <= PAGE_SIZE) ++ ptr = kmalloc((unsigned long) size, GFP_KERNEL); ++ else ++ ptr = vmalloc((unsigned long) size); ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ return ptr; ++} ++ ++static void ++xattr_free(void *ptr, size_t size) ++{ ++ if (!size) /* size request, no buffer was needed */ ++ return; ++ else if (size <= PAGE_SIZE) ++ kfree(ptr); ++ else ++ vfree(ptr); ++} ++#endif /* no xattr_alloc() and xattr_free() */ ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++mini_fo_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ /* Define these anyway so we don't need as much ifdef'ed code. */ ++ char *encoded_name = NULL; ++ char *encoded_value = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->getxattr) { ++ encoded_name = (char *)name; ++ encoded_value = (char *)value; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, encoded_name, encoded_value, size); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) \ ++ && LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,23)) \ ++ || LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) ++mini_fo_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++#else ++mini_fo_setxattr(struct dentry *dentry, const char *name, ++ void *value, size_t size, int flags) ++#endif ++ ++{ ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ ++ /* Define these anyway, so we don't have as much ifdef'ed code. */ ++ char *encoded_value = NULL; ++ char *encoded_name = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->setxattr) { ++ encoded_name = (char *)name; ++ encoded_value = (char *)value; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->setxattr(hidden_dentry, encoded_name, encoded_value, size, flags); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++mini_fo_removexattr(struct dentry *dentry, const char *name) { ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ char *encoded_name; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->removexattr) { ++ encoded_name = (char *)name; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, encoded_name); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++mini_fo_listxattr(struct dentry *dentry, char *list, size_t size) { ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ char *encoded_list = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->listxattr) { ++ encoded_list = list; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->listxattr(hidden_dentry, encoded_list, size); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ ++ ++struct inode_operations mini_fo_symlink_iops = ++ { ++ readlink: mini_fo_readlink, ++ follow_link: mini_fo_follow_link, ++ /* mk: permission: mini_fo_permission, */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ revalidate: mini_fo_inode_revalidate, ++#endif ++ setattr: mini_fo_setattr, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ getattr: mini_fo_getattr, ++ put_link: mini_fo_put_link, ++#endif ++ ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++ setxattr: mini_fo_setxattr, ++ getxattr: mini_fo_getxattr, ++ listxattr: mini_fo_listxattr, ++ removexattr: mini_fo_removexattr ++# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ ++ }; ++ ++struct inode_operations mini_fo_dir_iops = ++ { ++ create: mini_fo_create, ++ lookup: mini_fo_lookup, ++ link: mini_fo_link, ++ unlink: mini_fo_unlink, ++ symlink: mini_fo_symlink, ++ mkdir: mini_fo_mkdir, ++ rmdir: mini_fo_rmdir, ++ mknod: mini_fo_mknod, ++ rename: mini_fo_rename, ++ /* no readlink/follow_link for non-symlinks */ ++ // off because we have setattr ++ // truncate: mini_fo_truncate, ++ /* mk:permission: mini_fo_permission, */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ revalidate: mini_fo_inode_revalidate, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ getattr: mini_fo_getattr, ++#endif ++ setattr: mini_fo_setattr, ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++ setxattr: mini_fo_setxattr, ++ getxattr: mini_fo_getxattr, ++ listxattr: mini_fo_listxattr, ++ removexattr: mini_fo_removexattr ++# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ ++ }; ++ ++struct inode_operations mini_fo_main_iops = ++ { ++ /* permission: mini_fo_permission, */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ revalidate: mini_fo_inode_revalidate, ++#endif ++ setattr: mini_fo_setattr, ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ getattr: mini_fo_getattr, ++#endif ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++ setxattr: mini_fo_setxattr, ++ getxattr: mini_fo_getxattr, ++ listxattr: mini_fo_listxattr, ++ removexattr: mini_fo_removexattr ++# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ ++ }; +Index: linux-2.6.21.7/fs/mini_fo/main.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/main.c +@@ -0,0 +1,423 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++#include ++ ++/* This definition must only appear after we include */ ++#ifndef MODULE_LICENSE ++# define MODULE_LICENSE(bison) ++#endif /* not MODULE_LICENSE */ ++ ++/* ++ * This is the mini_fo tri interpose function, which extends the ++ * functionality of the regular interpose by interposing a higher ++ * level inode on top of two lower level ones: the base filesystem ++ * inode and the storage filesystem inode. ++ * ++ * sb we pass is mini_fo's super_block ++ */ ++int ++mini_fo_tri_interpose(dentry_t *hidden_dentry, ++ dentry_t *hidden_sto_dentry, ++ dentry_t *dentry, super_block_t *sb, int flag) ++{ ++ inode_t *hidden_inode = NULL; ++ inode_t *hidden_sto_inode = NULL; /* store corresponding storage inode */ ++ int err = 0; ++ inode_t *inode; ++ ++ /* Pointer to hidden_sto_inode if exists, else to hidden_inode. ++ * This is used to copy the attributes of the correct inode. */ ++ inode_t *master_inode; ++ ++ if(hidden_dentry) ++ hidden_inode = hidden_dentry->d_inode; ++ if(hidden_sto_dentry) ++ hidden_sto_inode = hidden_sto_dentry->d_inode; ++ ++ ASSERT(dentry->d_inode == NULL); ++ ++ /* mk: One of the inodes associated with the dentrys is likely to ++ * be NULL, so carefull: ++ */ ++ ASSERT((hidden_inode != NULL) || (hidden_sto_inode != NULL)); ++ ++ if(hidden_sto_inode) ++ master_inode = hidden_sto_inode; ++ else ++ master_inode = hidden_inode; ++ ++ /* ++ * We allocate our new inode below, by calling iget. ++ * iget will call our read_inode which will initialize some ++ * of the new inode's fields ++ */ ++ ++ /* ++ * original: inode = iget(sb, hidden_inode->i_ino); ++ */ ++ inode = iget(sb, iunique(sb, 25)); ++ if (!inode) { ++ err = -EACCES; /* should be impossible??? */ ++ goto out; ++ } ++ ++ /* ++ * interpose the inode if not already interposed ++ * this is possible if the inode is being reused ++ * XXX: what happens if we get_empty_inode() but there's another already? ++ * for now, ASSERT() that this can't happen; fix later. ++ */ ++ if (itohi(inode) != NULL) { ++ printk(KERN_CRIT "mini_fo_tri_interpose: itohi(inode) != NULL.\n"); ++ } ++ if (itohi2(inode) != NULL) { ++ printk(KERN_CRIT "mini_fo_tri_interpose: itohi2(inode) != NULL.\n"); ++ } ++ ++ /* mk: Carefull, igrab can't handle NULL inodes (ok, why should it?), so ++ * we need to check here: ++ */ ++ if(hidden_inode) ++ itohi(inode) = igrab(hidden_inode); ++ else ++ itohi(inode) = NULL; ++ ++ if(hidden_sto_inode) ++ itohi2(inode) = igrab(hidden_sto_inode); ++ else ++ itohi2(inode) = NULL; ++ ++ ++ /* Use different set of inode ops for symlinks & directories*/ ++ if (S_ISLNK(master_inode->i_mode)) ++ inode->i_op = &mini_fo_symlink_iops; ++ else if (S_ISDIR(master_inode->i_mode)) ++ inode->i_op = &mini_fo_dir_iops; ++ ++ /* Use different set of file ops for directories */ ++ if (S_ISDIR(master_inode->i_mode)) ++ inode->i_fop = &mini_fo_dir_fops; ++ ++ /* properly initialize special inodes */ ++ if (S_ISBLK(master_inode->i_mode) || S_ISCHR(master_inode->i_mode) || ++ S_ISFIFO(master_inode->i_mode) || S_ISSOCK(master_inode->i_mode)) { ++ init_special_inode(inode, master_inode->i_mode, master_inode->i_rdev); ++ } ++ ++ /* Fix our inode's address operations to that of the lower inode */ ++ if (inode->i_mapping->a_ops != master_inode->i_mapping->a_ops) { ++ inode->i_mapping->a_ops = master_inode->i_mapping->a_ops; ++ } ++ ++ /* only (our) lookup wants to do a d_add */ ++ if (flag) ++ d_add(dentry, inode); ++ else ++ d_instantiate(dentry, inode); ++ ++ ASSERT(dtopd(dentry) != NULL); ++ ++ /* all well, copy inode attributes */ ++ fist_copy_attr_all(inode, master_inode); ++ ++ out: ++ return err; ++} ++ ++/* parse mount options "base=" and "sto=" */ ++dentry_t * ++mini_fo_parse_options(super_block_t *sb, char *options) ++{ ++ dentry_t *hidden_root = ERR_PTR(-EINVAL); ++ dentry_t *hidden_root2 = ERR_PTR(-EINVAL); ++ struct nameidata nd, nd2; ++ char *name, *tmp, *end; ++ int err = 0; ++ ++ /* We don't want to go off the end of our arguments later on. */ ++ for (end = options; *end; end++); ++ ++ while (options < end) { ++ tmp = options; ++ while (*tmp && *tmp != ',') ++ tmp++; ++ *tmp = '\0'; ++ if (!strncmp("base=", options, 5)) { ++ name = options + 5; ++ printk(KERN_INFO "mini_fo: using base directory: %s\n", name); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ if (path_init(name, LOOKUP_FOLLOW, &nd)) ++ err = path_walk(name, &nd); ++#else ++ err = path_lookup(name, LOOKUP_FOLLOW, &nd); ++#endif ++ if (err) { ++ printk(KERN_CRIT "mini_fo: error accessing hidden directory '%s'\n", name); ++ hidden_root = ERR_PTR(err); ++ goto out; ++ } ++ hidden_root = nd.dentry; ++ stopd(sb)->base_dir_dentry = nd.dentry; ++ stopd(sb)->hidden_mnt = nd.mnt; ++ ++ } else if(!strncmp("sto=", options, 4)) { ++ /* parse the storage dir */ ++ name = options + 4; ++ printk(KERN_INFO "mini_fo: using storage directory: %s\n", name); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ if(path_init(name, LOOKUP_FOLLOW, &nd2)) ++ err = path_walk(name, &nd2); ++#else ++ err = path_lookup(name, LOOKUP_FOLLOW, &nd2); ++#endif ++ if(err) { ++ printk(KERN_CRIT "mini_fo: error accessing hidden storage directory '%s'\n", name); ++ ++ hidden_root2 = ERR_PTR(err); ++ goto out; ++ } ++ hidden_root2 = nd2.dentry; ++ stopd(sb)->storage_dir_dentry = nd2.dentry; ++ stopd(sb)->hidden_mnt2 = nd2.mnt; ++ stohs2(sb) = hidden_root2->d_sb; ++ ++ /* validate storage dir, this is done in ++ * mini_fo_read_super for the base directory. ++ */ ++ if (IS_ERR(hidden_root2)) { ++ printk(KERN_WARNING "mini_fo_parse_options: storage dentry lookup failed (err = %ld)\n", PTR_ERR(hidden_root2)); ++ goto out; ++ } ++ if (!hidden_root2->d_inode) { ++ printk(KERN_WARNING "mini_fo_parse_options: no storage dir to interpose on.\n"); ++ goto out; ++ } ++ stohs2(sb) = hidden_root2->d_sb; ++ } else { ++ printk(KERN_WARNING "mini_fo: unrecognized option '%s'\n", options); ++ hidden_root = ERR_PTR(-EINVAL); ++ goto out; ++ } ++ options = tmp + 1; ++ } ++ ++ out: ++ if(IS_ERR(hidden_root2)) ++ return hidden_root2; ++ return hidden_root; ++} ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++static int ++#else ++super_block_t * ++#endif ++mini_fo_read_super(super_block_t *sb, void *raw_data, int silent) ++{ ++ dentry_t *hidden_root; ++ int err = 0; ++ ++ if (!raw_data) { ++ printk(KERN_WARNING "mini_fo_read_super: missing argument\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ /* ++ * Allocate superblock private data ++ */ ++ __stopd(sb) = kmalloc(sizeof(struct mini_fo_sb_info), GFP_KERNEL); ++ if (!stopd(sb)) { ++ printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__); ++ err = -ENOMEM; ++ goto out; ++ } ++ stohs(sb) = NULL; ++ ++ hidden_root = mini_fo_parse_options(sb, raw_data); ++ if (IS_ERR(hidden_root)) { ++ printk(KERN_WARNING "mini_fo_read_super: lookup_dentry failed (err = %ld)\n", PTR_ERR(hidden_root)); ++ err = PTR_ERR(hidden_root); ++ goto out_free; ++ } ++ if (!hidden_root->d_inode) { ++ printk(KERN_WARNING "mini_fo_read_super: no directory to interpose on\n"); ++ goto out_free; ++ } ++ stohs(sb) = hidden_root->d_sb; ++ ++ /* ++ * Linux 2.4.2-ac3 and beyond has code in ++ * mm/filemap.c:generic_file_write() that requires sb->s_maxbytes ++ * to be populated. If not set, all write()s under that sb will ++ * return 0. ++ * ++ * Linux 2.4.4+ automatically sets s_maxbytes to MAX_NON_LFS; ++ * the filesystem should override it only if it supports LFS. ++ */ ++ /* non-SCA code is good to go with LFS */ ++ sb->s_maxbytes = hidden_root->d_sb->s_maxbytes; ++ ++ sb->s_op = &mini_fo_sops; ++ /* ++ * we can't use d_alloc_root if we want to use ++ * our own interpose function unchanged, ++ * so we simply replicate *most* of the code in d_alloc_root here ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); ++#else ++ sb->s_root = d_alloc(NULL, &(const struct qstr){hash: 0, name: "/", len : 1}); ++#endif ++ if (IS_ERR(sb->s_root)) { ++ printk(KERN_WARNING "mini_fo_read_super: d_alloc failed\n"); ++ err = -ENOMEM; ++ goto out_dput; ++ } ++ ++ sb->s_root->d_op = &mini_fo_dops; ++ sb->s_root->d_sb = sb; ++ sb->s_root->d_parent = sb->s_root; ++ ++ /* link the upper and lower dentries */ ++ __dtopd(sb->s_root) = (struct mini_fo_dentry_info *) ++ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); ++ if (!dtopd(sb->s_root)) { ++ err = -ENOMEM; ++ goto out_dput2; ++ } ++ dtopd(sb->s_root)->state = MODIFIED; ++ dtohd(sb->s_root) = hidden_root; ++ ++ /* fanout relevant, interpose on storage root dentry too */ ++ dtohd2(sb->s_root) = stopd(sb)->storage_dir_dentry; ++ ++ /* ...and call tri-interpose to interpose root dir inodes ++ * if (mini_fo_interpose(hidden_root, sb->s_root, sb, 0)) ++ */ ++ if(mini_fo_tri_interpose(hidden_root, dtohd2(sb->s_root), sb->s_root, sb, 0)) ++ goto out_dput2; ++ ++ /* initalize the wol list */ ++ itopd(sb->s_root->d_inode)->deleted_list_size = -1; ++ itopd(sb->s_root->d_inode)->renamed_list_size = -1; ++ meta_build_lists(sb->s_root); ++ ++ goto out; ++ ++ out_dput2: ++ dput(sb->s_root); ++ out_dput: ++ dput(hidden_root); ++ dput(dtohd2(sb->s_root)); /* release the hidden_sto_dentry too */ ++ out_free: ++ kfree(stopd(sb)); ++ __stopd(sb) = NULL; ++ out: ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ return err; ++#else ++ if (err) { ++ return ERR_PTR(err); ++ } else { ++ return sb; ++ } ++#endif ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++static int mini_fo_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *raw_data, struct vfsmount *mnt) ++{ ++ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super, mnt); ++} ++#else ++static struct super_block *mini_fo_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *raw_data) ++{ ++ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super); ++} ++#endif ++ ++void mini_fo_kill_block_super(struct super_block *sb) ++{ ++ generic_shutdown_super(sb); ++ /* ++ * XXX: BUG: Halcrow: Things get unstable sometime after this point: ++ * lib/rwsem-spinlock.c:127: spin_is_locked on uninitialized ++ * fs/fs-writeback.c:402: spin_lock(fs/super.c:a0381828) already ++ * locked by fs/fs-writeback.c/402 ++ * ++ * Apparently, someone's not releasing a lock on sb_lock... ++ */ ++} ++ ++static struct file_system_type mini_fo_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "mini_fo", ++ .get_sb = mini_fo_get_sb, ++ .kill_sb = mini_fo_kill_block_super, ++ .fs_flags = 0, ++}; ++ ++ ++#else ++static DECLARE_FSTYPE(mini_fo_fs_type, "mini_fo", mini_fo_read_super, 0); ++#endif ++ ++static int __init init_mini_fo_fs(void) ++{ ++ printk("Registering mini_fo version $Id$\n"); ++ return register_filesystem(&mini_fo_fs_type); ++} ++static void __exit exit_mini_fo_fs(void) ++{ ++ printk("Unregistering mini_fo version $Id$\n"); ++ unregister_filesystem(&mini_fo_fs_type); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++EXPORT_NO_SYMBOLS; ++#endif ++ ++MODULE_AUTHOR("Erez Zadok "); ++MODULE_DESCRIPTION("FiST-generated mini_fo filesystem"); ++MODULE_LICENSE("GPL"); ++ ++/* MODULE_PARM(fist_debug_var, "i"); */ ++/* MODULE_PARM_DESC(fist_debug_var, "Debug level"); */ ++ ++module_init(init_mini_fo_fs) ++module_exit(exit_mini_fo_fs) +Index: linux-2.6.21.7/fs/mini_fo/Makefile +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/Makefile +@@ -0,0 +1,17 @@ ++# ++# Makefile for mini_fo 2.4 and 2.6 Linux kernels ++# ++# Copyright (C) 2004, 2005 Markus Klotzbuecher ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version ++# 2 of the License, or (at your option) any later version. ++# ++ ++obj-$(CONFIG_MINI_FO) := mini_fo.o ++mini_fo-objs := meta.o dentry.o file.o inode.o main.o super.o state.o aux.o ++ ++# dependencies ++${mini_fo-objs}: mini_fo.h fist.h ++ +Index: linux-2.6.21.7/fs/mini_fo/meta.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/meta.c +@@ -0,0 +1,1000 @@ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++#include "fist.h" ++#include "mini_fo.h" ++ ++int meta_build_lists(dentry_t *dentry) ++{ ++ struct mini_fo_inode_info *inode_info; ++ ++ dentry_t *meta_dentry = 0; ++ file_t *meta_file = 0; ++ mm_segment_t old_fs; ++ void *buf; ++ ++ int bytes, len; ++ struct vfsmount *meta_mnt; ++ char *entry; ++ ++ inode_info = itopd(dentry->d_inode); ++ if(!(inode_info->deleted_list_size == -1 && ++ inode_info->renamed_list_size == -1)) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ Error, list(s) not virgin.\n"); ++ return -1; ++ } ++ ++ /* init our meta lists */ ++ INIT_LIST_HEAD(&inode_info->deleted_list); ++ inode_info->deleted_list_size = 0; ++ ++ INIT_LIST_HEAD(&inode_info->renamed_list); ++ inode_info->renamed_list_size = 0; ++ ++ /* might there be a META-file? */ ++ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen(META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ dput(meta_dentry); ++ goto out_ok; ++ } ++ /* $%& err, is this correct? */ ++ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ mntget(meta_mnt); ++ ++ ++ /* open META-file for reading */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x0); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ ERROR opening META file.\n"); ++ goto out_err; ++ } ++ ++ /* check if fs supports reading */ ++ if(!meta_file->f_op->read) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ ERROR, fs does not support reading.\n"); ++ goto out_err_close; ++ } ++ ++ /* allocate a page for transfering the data */ ++ buf = (void *) __get_free_page(GFP_KERNEL); ++ if(!buf) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ ERROR, out of mem.\n"); ++ goto out_err_close; ++ } ++ meta_file->f_pos = 0; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ char *c; ++ bytes = meta_file->f_op->read(meta_file, buf, PAGE_SIZE, &meta_file->f_pos); ++ if(bytes == PAGE_SIZE) { ++ /* trim a cut off filename and adjust f_pos to get it next time */ ++ for(c = (char*) buf+PAGE_SIZE; ++ *c != '\n'; ++ c--, bytes--, meta_file->f_pos--); ++ } ++ entry = (char *) buf; ++ while(entry < (char *) buf+bytes) { ++ ++ char *old_path; ++ char *dir_name; ++ int old_len, new_len; ++ ++ /* len without '\n'*/ ++ len = (int) (strchr(entry, '\n') - entry); ++ switch (*entry) { ++ case 'D': ++ /* format: "D filename" */ ++ meta_list_add_d_entry(dentry, ++ entry+2, ++ len-2); ++ break; ++ case 'R': ++ /* format: "R path/xy/dir newDir" */ ++ old_path = entry+2; ++ dir_name = strchr(old_path, ' ') + 1; ++ old_len = dir_name - old_path - 1; ++ new_len = ((int) entry) + len - ((int ) dir_name); ++ meta_list_add_r_entry(dentry, ++ old_path, ++ old_len, ++ dir_name, ++ new_len); ++ break; ++ default: ++ /* unknown entry type detected */ ++ break; ++ } ++ entry += len+1; ++ } ++ ++ } while(meta_file->f_pos < meta_dentry->d_inode->i_size); ++ ++ free_page((unsigned long) buf); ++ set_fs(old_fs); ++ fput(meta_file); ++ } ++ goto out_ok; ++ ++ out_err_close: ++ fput(meta_file); ++ out_err: ++ mntput(meta_mnt); ++ dput(meta_dentry); ++ return -1; ++ out_ok: ++ return 1; /* check this!!! inode_info->wol_size; */ ++} ++ ++/* cleanups up all lists and free's the mem by dentry */ ++int meta_put_lists(dentry_t *dentry) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk("mini_fo: meta_put_lists: invalid dentry passed.\n"); ++ return -1; ++ } ++ return __meta_put_lists(dentry->d_inode); ++} ++ ++/* cleanups up all lists and free's the mem by inode */ ++int __meta_put_lists(inode_t *inode) ++{ ++ int err = 0; ++ if(!inode || !itopd(inode)) { ++ printk("mini_fo: __meta_put_lists: invalid inode passed.\n"); ++ return -1; ++ } ++ err = __meta_put_d_list(inode); ++ err |= __meta_put_r_list(inode); ++ return err; ++} ++ ++int meta_sync_lists(dentry_t *dentry) ++{ ++ int err = 0; ++ if(!dentry || !dentry->d_inode) { ++ printk("mini_fo: meta_sync_lists: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ err = meta_sync_d_list(dentry, 0); ++ err |= meta_sync_r_list(dentry, 1); ++ return err; ++} ++ ++ ++/* remove all D entries from the renamed list and free the mem */ ++int __meta_put_d_list(inode_t *inode) ++{ ++ struct list_head *tmp; ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: __meta_put_d_list: \ ++ invalid inode passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(inode); ++ ++ /* nuke the DELETED-list */ ++ if(inode_info->deleted_list_size <= 0) ++ return 0; ++ ++ while(!list_empty(&inode_info->deleted_list)) { ++ tmp = inode_info->deleted_list.next; ++ list_del(tmp); ++ del_entry = list_entry(tmp, struct deleted_entry, list); ++ kfree(del_entry->name); ++ kfree(del_entry); ++ } ++ inode_info->deleted_list_size = 0; ++ ++ return 0; ++} ++ ++/* remove all R entries from the renamed list and free the mem */ ++int __meta_put_r_list(inode_t *inode) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: meta_put_r_list: invalid inode.\n"); ++ return -1; ++ } ++ inode_info = itopd(inode); ++ ++ /* nuke the RENAMED-list */ ++ if(inode_info->renamed_list_size <= 0) ++ return 0; ++ ++ while(!list_empty(&inode_info->renamed_list)) { ++ tmp = inode_info->renamed_list.next; ++ list_del(tmp); ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ kfree(ren_entry->new_name); ++ kfree(ren_entry->old_name); ++ kfree(ren_entry); ++ } ++ inode_info->renamed_list_size = 0; ++ ++ return 0; ++} ++ ++int meta_add_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ int err = 0; ++ err = meta_list_add_d_entry(dentry, name, len); ++ err |= meta_write_d_entry(dentry,name,len); ++ return err; ++} ++ ++/* add a D entry to the deleted list */ ++int meta_list_add_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->deleted_list_size < 0) ++ return -1; ++ ++ del_entry = (struct deleted_entry *) ++ kmalloc(sizeof(struct deleted_entry), GFP_KERNEL); ++ del_entry->name = (char*) kmalloc(len, GFP_KERNEL); ++ if(!del_entry || !del_entry->name) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ ++ out of mem.\n"); ++ kfree(del_entry->name); ++ kfree(del_entry); ++ return -ENOMEM; ++ } ++ ++ strncpy(del_entry->name, name, len); ++ del_entry->len = len; ++ ++ list_add(&del_entry->list, &inode_info->deleted_list); ++ inode_info->deleted_list_size++; ++ return 0; ++} ++ ++int meta_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len) ++{ ++ int err = 0; ++ err = meta_list_add_r_entry(dentry, ++ old_name, old_len, ++ new_name, new_len); ++ err |= meta_write_r_entry(dentry, ++ old_name, old_len, ++ new_name, new_len); ++ return err; ++} ++ ++/* add a R entry to the renamed list */ ++int meta_list_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len) ++{ ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->renamed_list_size < 0) ++ return -1; ++ ++ ren_entry = (struct renamed_entry *) ++ kmalloc(sizeof(struct renamed_entry), GFP_KERNEL); ++ ren_entry->old_name = (char*) kmalloc(old_len, GFP_KERNEL); ++ ren_entry->new_name = (char*) kmalloc(new_len, GFP_KERNEL); ++ ++ if(!ren_entry || !ren_entry->old_name || !ren_entry->new_name) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ ++ out of mem.\n"); ++ kfree(ren_entry->new_name); ++ kfree(ren_entry->old_name); ++ kfree(ren_entry); ++ return -ENOMEM; ++ } ++ ++ strncpy(ren_entry->old_name, old_name, old_len); ++ ren_entry->old_len = old_len; ++ strncpy(ren_entry->new_name, new_name, new_len); ++ ren_entry->new_len = new_len; ++ ++ list_add(&ren_entry->list, &inode_info->renamed_list); ++ inode_info->renamed_list_size++; ++ return 0; ++} ++ ++ ++int meta_remove_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ int err = 0; ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT ++ "mini_fo: meta_remove_r_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ ++ err = meta_list_remove_r_entry(dentry, name, len); ++ err |= meta_sync_lists(dentry); ++ return err; ++} ++ ++int meta_list_remove_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT ++ "mini_fo: meta_list_remove_r_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ return __meta_list_remove_r_entry(dentry->d_inode, name, len); ++} ++ ++int __meta_list_remove_r_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) ++ printk(KERN_CRIT ++ "mini_fo: __meta_list_remove_r_entry: \ ++ invalid inode passed.\n"); ++ inode_info = itopd(inode); ++ ++ if(inode_info->renamed_list_size < 0) ++ return -1; ++ if(inode_info->renamed_list_size == 0) ++ return 1; ++ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ if(ren_entry->new_len != len) ++ continue; ++ ++ if(!strncmp(ren_entry->new_name, name, len)) { ++ list_del(tmp); ++ kfree(ren_entry->new_name); ++ kfree(ren_entry->old_name); ++ kfree(ren_entry); ++ inode_info->renamed_list_size--; ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++ ++/* append a single D entry to the meta file */ ++int meta_write_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ dentry_t *meta_dentry = 0; ++ file_t *meta_file = 0; ++ mm_segment_t old_fs; ++ ++ int bytes, err; ++ struct vfsmount *meta_mnt = 0; ++ char *buf; ++ ++ err = 0; ++ ++ if(itopd(dentry->d_inode)->deleted_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ if(dtopd(dentry)->state == UNMODIFIED) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), strlen (META_FILENAME)); ++ ++ /* We need to create a META-file */ ++ if(!meta_dentry->d_inode) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, ++ S_IRUSR | S_IWUSR, ++ NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, ++ S_IRUSR | S_IWUSR); ++#endif ++ } ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ ERROR opening meta file.\n"); ++ mntput(meta_mnt); /* $%& is this necessary? */ ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* size: len for name, 1 for \n and 2 for "D " */ ++ buf = (char *) kmalloc(len+3, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ out of mem.\n"); ++ return -ENOMEM; ++ } ++ ++ buf[0] = 'D'; ++ buf[1] = ' '; ++ strncpy(buf+2, name, len); ++ buf[len+2] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, len+3, ++ &meta_file->f_pos); ++ if(bytes != len+3) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ ERROR writing.\n"); ++ err = -1; ++ } ++ kfree(buf); ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++} ++ ++/* append a single R entry to the meta file */ ++int meta_write_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len) ++{ ++ dentry_t *meta_dentry = 0; ++ file_t *meta_file = 0; ++ mm_segment_t old_fs; ++ ++ int bytes, err, buf_len; ++ struct vfsmount *meta_mnt = 0; ++ char *buf; ++ ++ ++ err = 0; ++ ++ if(itopd(dentry->d_inode)->renamed_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ /* build the storage structure? */ ++ if(dtopd(dentry)->state == UNMODIFIED) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen (META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ /* We need to create a META-file */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR, NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR); ++#endif ++ } ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ ++ ERROR opening meta file.\n"); ++ mntput(meta_mnt); ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* size: 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ ++ buf_len = old_len + new_len + 4; ++ buf = (char *) kmalloc(buf_len, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: out of mem.\n"); ++ return -ENOMEM; ++ } ++ ++ buf[0] = 'R'; ++ buf[1] = ' '; ++ strncpy(buf + 2, old_name, old_len); ++ buf[old_len + 2] = ' '; ++ strncpy(buf + old_len + 3, new_name, new_len); ++ buf[buf_len -1] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, buf_len, &meta_file->f_pos); ++ if(bytes != buf_len) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: ERROR writing.\n"); ++ err = -1; ++ } ++ ++ kfree(buf); ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++} ++ ++/* sync D list to disk, append data if app_flag is 1 */ ++/* check the meta_mnt, which seems not to be used (properly) */ ++ ++int meta_sync_d_list(dentry_t *dentry, int app_flag) ++{ ++ dentry_t *meta_dentry; ++ file_t *meta_file; ++ mm_segment_t old_fs; ++ ++ int bytes, err; ++ struct vfsmount *meta_mnt; ++ char *buf; ++ ++ struct list_head *tmp; ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ err = 0; ++ meta_file=0; ++ meta_mnt=0; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ invalid inode passed.\n"); ++ err = -1; ++ goto out; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->deleted_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ /* ok, there is something to sync */ ++ ++ /* build the storage structure? */ ++ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen(META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ /* We need to create a META-file */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR, NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR); ++#endif ++ app_flag = 0; ++ } ++ /* need we truncate the meta file? */ ++ if(!app_flag) { ++ struct iattr newattrs; ++ newattrs.ia_size = 0; ++ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&meta_dentry->d_inode->i_mutex); ++#else ++ down(&meta_dentry->d_inode->i_sem); ++#endif ++ err = notify_change(meta_dentry, &newattrs); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&meta_dentry->d_inode->i_mutex); ++#else ++ up(&meta_dentry->d_inode->i_sem); ++#endif ++ ++ if(err || meta_dentry->d_inode->i_size != 0) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR truncating meta file.\n"); ++ goto out_err_close; ++ } ++ } ++ ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR opening meta file.\n"); ++ /* we don't mntget so we dont't mntput (for now) ++ * mntput(meta_mnt); ++ */ ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* here we go... */ ++ list_for_each(tmp, &inode_info->deleted_list) { ++ del_entry = list_entry(tmp, struct deleted_entry, list); ++ ++ /* size: len for name, 1 for \n and 2 for "D " */ ++ buf = (char *) kmalloc(del_entry->len+3, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ out of mem.\n"); ++ return -ENOMEM; ++ } ++ ++ buf[0] = 'D'; ++ buf[1] = ' '; ++ strncpy(buf+2, del_entry->name, del_entry->len); ++ buf[del_entry->len+2] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, ++ del_entry->len+3, ++ &meta_file->f_pos); ++ if(bytes != del_entry->len+3) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR writing.\n"); ++ err |= -1; ++ } ++ kfree(buf); ++ } ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++ ++} ++ ++int meta_sync_r_list(dentry_t *dentry, int app_flag) ++{ ++ dentry_t *meta_dentry; ++ file_t *meta_file; ++ mm_segment_t old_fs; ++ ++ int bytes, err, buf_len; ++ struct vfsmount *meta_mnt; ++ char *buf; ++ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ err = 0; ++ meta_file=0; ++ meta_mnt=0; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ invalid dentry passed.\n"); ++ err = -1; ++ goto out; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->deleted_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ /* ok, there is something to sync */ ++ ++ /* build the storage structure? */ ++ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen(META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ /* We need to create a META-file */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR, NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR); ++#endif ++ app_flag = 0; ++ } ++ /* need we truncate the meta file? */ ++ if(!app_flag) { ++ struct iattr newattrs; ++ newattrs.ia_size = 0; ++ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&meta_dentry->d_inode->i_mutex); ++#else ++ down(&meta_dentry->d_inode->i_sem); ++#endif ++ err = notify_change(meta_dentry, &newattrs); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&meta_dentry->d_inode->i_mutex); ++#else ++ up(&meta_dentry->d_inode->i_sem); ++#endif ++ if(err || meta_dentry->d_inode->i_size != 0) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR truncating meta file.\n"); ++ goto out_err_close; ++ } ++ } ++ ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR opening meta file.\n"); ++ /* we don't mntget so we dont't mntput (for now) ++ * mntput(meta_mnt); ++ */ ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* here we go... */ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ /* size: ++ * 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ ++ buf_len = ren_entry->old_len + ren_entry->new_len + 4; ++ buf = (char *) kmalloc(buf_len, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ out of mem.\n"); ++ return -ENOMEM; ++ } ++ buf[0] = 'R'; ++ buf[1] = ' '; ++ strncpy(buf + 2, ren_entry->old_name, ren_entry->old_len); ++ buf[ren_entry->old_len + 2] = ' '; ++ strncpy(buf + ren_entry->old_len + 3, ++ ren_entry->new_name, ren_entry->new_len); ++ buf[buf_len - 1] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, ++ buf_len, &meta_file->f_pos); ++ if(bytes != buf_len) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR writing.\n"); ++ err |= -1; ++ } ++ kfree(buf); ++ } ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++} ++ ++int meta_check_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) ++ printk(KERN_CRIT "mini_fo: meta_check_d_dentry: \ ++ invalid dentry passed.\n"); ++ return __meta_check_d_entry(dentry->d_inode, name, len); ++} ++ ++int __meta_check_d_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) ++ printk(KERN_CRIT "mini_fo: __meta_check_d_dentry: \ ++ invalid inode passed.\n"); ++ ++ inode_info = itopd(inode); ++ ++ if(inode_info->deleted_list_size <= 0) ++ return 0; ++ ++ list_for_each(tmp, &inode_info->deleted_list) { ++ del_entry = list_entry(tmp, struct deleted_entry, list); ++ if(del_entry->len != len) ++ continue; ++ ++ if(!strncmp(del_entry->name, name, len)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * check if file has been renamed and return path to orig. base dir. ++ * Implements no error return values so far, what of course sucks. ++ * String is null terminated.' ++ */ ++char* meta_check_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ ++ invalid dentry passed.\n"); ++ return NULL; ++ } ++ return __meta_check_r_entry(dentry->d_inode, name, len); ++} ++ ++char* __meta_check_r_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ char *old_path; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ ++ invalid inode passed.\n"); ++ return NULL; ++ } ++ inode_info = itopd(inode); ++ ++ if(inode_info->renamed_list_size <= 0) ++ return NULL; ++ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ if(ren_entry->new_len != len) ++ continue; ++ ++ if(!strncmp(ren_entry->new_name, name, len)) { ++ old_path = (char *) ++ kmalloc(ren_entry->old_len+1, GFP_KERNEL); ++ strncpy(old_path, ++ ren_entry->old_name, ++ ren_entry->old_len); ++ old_path[ren_entry->old_len]='\0'; ++ return old_path; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * This version only checks if entry exists and return: ++ * 1 if exists, ++ * 0 if not, ++ * -1 if error. ++ */ ++int meta_is_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ return __meta_is_r_entry(dentry->d_inode, name, len); ++} ++ ++int __meta_is_r_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ ++ invalid inode passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(inode); ++ ++ if(inode_info->renamed_list_size <= 0) ++ return -1; ++ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ if(ren_entry->new_len != len) ++ continue; ++ ++ if(!strncmp(ren_entry->new_name, name, len)) ++ return 1; ++ } ++ return 0; ++} ++ +Index: linux-2.6.21.7/fs/mini_fo/mini_fo.h +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/mini_fo.h +@@ -0,0 +1,510 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifndef __MINI_FO_H_ ++#define __MINI_FO_H_ ++ ++#ifdef __KERNEL__ ++ ++/* META stuff */ ++#define META_FILENAME "META_dAfFgHE39ktF3HD2sr" ++ ++/* use xattrs? */ ++#define XATTR ++ ++/* File attributes that when changed, result in a file beeing copied to storage */ ++#define COPY_FLAGS ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_SIZE ++ ++/* ++ * mini_fo filestates ++ */ ++#define MODIFIED 1 ++#define UNMODIFIED 2 ++#define CREATED 3 ++#define DEL_REWRITTEN 4 ++#define DELETED 5 ++#define NON_EXISTANT 6 ++ ++/* fist file systems superblock magic */ ++# define MINI_FO_SUPER_MAGIC 0xf15f ++ ++/* ++ * STRUCTURES: ++ */ ++ ++/* mini_fo inode data in memory */ ++struct mini_fo_inode_info { ++ inode_t *wii_inode; ++ inode_t *wii_inode2; /* pointer to storage inode */ ++ ++ /* META-data lists */ ++ /* deleted list, ex wol */ ++ struct list_head deleted_list; ++ int deleted_list_size; ++ ++ /* renamed list */ ++ struct list_head renamed_list; ++ int renamed_list_size; ++ ++ /* add other lists here ... */ ++}; ++ ++/* mini_fo dentry data in memory */ ++struct mini_fo_dentry_info { ++ dentry_t *wdi_dentry; ++ dentry_t *wdi_dentry2; /* pointer to storage dentry */ ++ unsigned int state; /* state of the mini_fo dentry */ ++}; ++ ++ ++/* mini_fo super-block data in memory */ ++struct mini_fo_sb_info { ++ super_block_t *wsi_sb, *wsi_sb2; /* mk: might point to the same sb */ ++ struct vfsmount *hidden_mnt, *hidden_mnt2; ++ dentry_t *base_dir_dentry; ++ dentry_t *storage_dir_dentry; ++ ; ++}; ++ ++/* readdir_data, readdir helper struct */ ++struct readdir_data { ++ struct list_head ndl_list; /* linked list head ptr */ ++ int ndl_size; /* list size */ ++ int sto_done; /* flag to show that the storage dir entries have ++ * all been read an now follow base entries */ ++}; ++ ++/* file private data. */ ++struct mini_fo_file_info { ++ struct file *wfi_file; ++ struct file *wfi_file2; /* pointer to storage file */ ++ struct readdir_data rd; ++}; ++ ++/* struct ndl_entry */ ++struct ndl_entry { ++ struct list_head list; ++ char *name; ++ int len; ++}; ++ ++/******************************** ++ * META-data structures ++ ********************************/ ++ ++/* deleted entry */ ++struct deleted_entry { ++ struct list_head list; ++ char *name; ++ int len; ++}; ++ ++/* renamed entry */ ++struct renamed_entry { ++ struct list_head list; ++ char *old_name; /* old directory with full path */ ++ int old_len; /* length of above string */ ++ char *new_name; /* new directory name */ ++ int new_len; /* length of above string */ ++}; ++ ++/* attr_change entry */ ++struct attr_change_entry { ++ struct list_head list; ++ char *name; ++ int len; ++}; ++ ++/* link entry */ ++struct link_entry { ++ struct list_head list; ++ int links_moved; ++ int inum_base; ++ int inum_sto; ++ char *weird_name; ++ int weird_name_len; ++}; ++ ++ ++/* Some other stuff required for mini_fo_filldir64, copied from ++ * fs/readdir.c ++ */ ++ ++#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) ++#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) ++ ++ ++struct linux_dirent64 { ++ u64 d_ino; ++ s64 d_off; ++ unsigned short d_reclen; ++ unsigned char d_type; ++ char d_name[0]; ++}; ++ ++ ++struct getdents_callback64 { ++ struct linux_dirent64 * current_dir; ++ struct linux_dirent64 * previous; ++ int count; ++ int error; ++}; ++ ++struct linux_dirent { ++ unsigned long d_ino; ++ unsigned long d_off; ++ unsigned short d_reclen; ++ char d_name[1]; ++}; ++ ++struct getdents_callback { ++ struct linux_dirent * current_dir; ++ struct linux_dirent * previous; ++ int count; ++ int error; ++}; ++ ++ ++/* ++ * MACROS: ++ */ ++ ++/* file TO private_data */ ++# define ftopd(file) ((struct mini_fo_file_info *)((file)->private_data)) ++# define __ftopd(file) ((file)->private_data) ++/* file TO hidden_file */ ++# define ftohf(file) ((ftopd(file))->wfi_file) ++# define ftohf2(file) ((ftopd(file))->wfi_file2) ++ ++/* inode TO private_data */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->i_private) ++# define __itopd(ino) ((ino)->i_private) ++#else ++# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->u.generic_ip) ++# define __itopd(ino) ((ino)->u.generic_ip) ++#endif ++/* inode TO hidden_inode */ ++# define itohi(ino) (itopd(ino)->wii_inode) ++# define itohi2(ino) (itopd(ino)->wii_inode2) ++ ++/* superblock TO private_data */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++# define stopd(super) ((struct mini_fo_sb_info *)(super)->s_fs_info) ++# define __stopd(super) ((super)->s_fs_info) ++#else ++# define stopd(super) ((struct mini_fo_sb_info *)(super)->u.generic_sbp) ++# define __stopd(super) ((super)->u.generic_sbp) ++#endif ++ ++/* unused? # define vfs2priv stopd */ ++/* superblock TO hidden_superblock */ ++ ++# define stohs(super) (stopd(super)->wsi_sb) ++# define stohs2(super) (stopd(super)->wsi_sb2) ++ ++/* dentry TO private_data */ ++# define dtopd(dentry) ((struct mini_fo_dentry_info *)(dentry)->d_fsdata) ++# define __dtopd(dentry) ((dentry)->d_fsdata) ++/* dentry TO hidden_dentry */ ++# define dtohd(dent) (dtopd(dent)->wdi_dentry) ++# define dtohd2(dent) (dtopd(dent)->wdi_dentry2) ++ ++/* dentry to state */ ++# define dtost(dent) (dtopd(dent)->state) ++# define sbt(sb) ((sb)->s_type->name) ++ ++#define IS_WRITE_FLAG(flag) (flag & (O_RDWR | O_WRONLY | O_APPEND)) ++#define IS_COPY_FLAG(flag) (flag & (COPY_FLAGS)) ++ ++/* macros to simplify non-SCA code */ ++# define MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages) ++# define MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages) ++# define FREE_PAGE_POINTERS(hidden_pages, num) ++# define FREE_PAGEDATA_POINTERS(hidden_pages_data, num) ++# define FOR_EACH_PAGE ++# define CURRENT_HIDDEN_PAGE hidden_page ++# define CURRENT_HIDDEN_PAGEDATA hidden_page_data ++# define CURRENT_HIDDEN_PAGEINDEX page->index ++ ++/* ++ * EXTERNALS: ++ */ ++extern struct file_operations mini_fo_main_fops; ++extern struct file_operations mini_fo_dir_fops; ++extern struct inode_operations mini_fo_main_iops; ++extern struct inode_operations mini_fo_dir_iops; ++extern struct inode_operations mini_fo_symlink_iops; ++extern struct super_operations mini_fo_sops; ++extern struct dentry_operations mini_fo_dops; ++extern struct vm_operations_struct mini_fo_shared_vmops; ++extern struct vm_operations_struct mini_fo_private_vmops; ++extern struct address_space_operations mini_fo_aops; ++ ++#if 0 /* unused by mini_fo */ ++extern int mini_fo_interpose(dentry_t *hidden_dentry, dentry_t *this_dentry, super_block_t *sb, int flag); ++#if defined(FIST_FILTER_DATA) || defined(FIST_FILTER_SCA) ++extern page_t *mini_fo_get1page(file_t *file, int index); ++extern int mini_fo_fill_zeros(file_t *file, page_t *page, unsigned from); ++# endif /* FIST_FILTER_DATA || FIST_FILTER_SCA */ ++ ++ ++# define mini_fo_hidden_dentry(d) __mini_fo_hidden_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) ++# define mini_fo_hidden_sto_dentry(d) __mini_fo_hidden_sto_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) ++ ++extern dentry_t *__mini_fo_hidden_dentry(char *file, char *func, int line, dentry_t *this_dentry); ++extern dentry_t *__mini_fo_hidden_sto_dentry(char *file, char *func, int line, dentry_t *this_dentry); ++ ++extern int mini_fo_read_file(const char *filename, void *buf, int len); ++extern int mini_fo_write_file(const char *filename, void *buf, int len); ++extern dentry_t *fist_lookup(dentry_t *dir, const char *name, vnode_t **out, uid_t uid, gid_t gid); ++#endif /* unused by mini_fo */ ++ ++/* state transition functions */ ++extern int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag); ++extern int nondir_del_rew_to_del(dentry_t *dentry); ++extern int nondir_creat_to_del(dentry_t *dentry); ++extern int nondir_mod_to_del(dentry_t *dentry); ++extern int nondir_unmod_to_del(dentry_t *dentry); ++ ++extern int dir_unmod_to_mod(dentry_t *dentry); ++ ++/* rename specials */ ++extern int rename_directory(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); ++extern int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); ++ ++/* misc stuff */ ++extern int mini_fo_tri_interpose(dentry_t *hidden_dentry, ++ dentry_t *hidden_sto_dentry, ++ dentry_t *dentry, ++ super_block_t *sb, int flag); ++ ++extern int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, ++ dentry_t *src_dentry, struct vfsmount *src_mnt); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd); ++ ++extern int create_sto_nod(dentry_t *dentry, int mode, dev_t dev); ++extern int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd); ++#else ++extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode); ++ ++extern int create_sto_nod(dentry_t *dentry, int mode, int dev); ++extern int create_sto_reg_file(dentry_t *dentry, int mode); ++#endif ++ ++extern int create_sto_dir(dentry_t *dentry, int mode); ++ ++extern int exists_in_storage(dentry_t *dentry); ++extern int is_mini_fo_existant(dentry_t *dentry); ++extern int get_neg_sto_dentry(dentry_t *dentry); ++extern int build_sto_structure(dentry_t *dir, dentry_t *dentry); ++extern int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len); ++extern dentry_t *bpath_walk(super_block_t *sb, char *bpath); ++extern int bpath_put(dentry_t *dentry); ++ ++/* check_mini_fo types functions */ ++extern int check_mini_fo_dentry(dentry_t *dentry); ++extern int check_mini_fo_file(file_t *file); ++extern int check_mini_fo_inode(inode_t *inode); ++ ++/* General meta functions, can be called from outside of meta.c */ ++extern int meta_build_lists(dentry_t *dentry); ++extern int meta_put_lists(dentry_t *dentry); ++extern int __meta_put_lists(inode_t *inode); ++ ++extern int meta_add_d_entry(dentry_t *dentry, const char *name, int len); ++extern int meta_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len); ++ ++extern int meta_remove_r_entry(dentry_t *dentry, const char *name, int len); ++ ++extern int meta_check_d_entry(dentry_t *dentry, const char *name, int len); ++extern int __meta_check_d_entry(inode_t *inode, const char *name, int len); ++ ++extern char* meta_check_r_entry(dentry_t *dentry, const char *name, int len); ++extern char* __meta_check_r_entry(inode_t *inode, const char *name, int len); ++extern int meta_is_r_entry(dentry_t *dentry, const char *name, int len); ++extern int __meta_is_r_entry(inode_t *inode, const char *name, int len); ++ ++/* Specific meta functions, should be called only inside meta.c */ ++extern int __meta_put_d_list(inode_t *inode); ++extern int __meta_put_r_list(inode_t *inode); ++ ++extern int meta_list_add_d_entry(dentry_t *dentry, ++ const char *name, int len); ++extern int meta_list_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len); ++ ++extern int meta_list_remove_r_entry(dentry_t *dentry, ++ const char *name, int len); ++ ++extern int __meta_list_remove_r_entry(inode_t *inode, ++ const char *name, int len); ++ ++extern int meta_write_d_entry(dentry_t *dentry, const char *name, int len); ++extern int meta_write_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len); ++ ++extern int meta_sync_lists(dentry_t *dentry); ++extern int meta_sync_d_list(dentry_t *dentry, int app_flag); ++extern int meta_sync_r_list(dentry_t *dentry, int app_flag); ++ ++/* ndl stuff */ ++extern int ndl_add_entry(struct readdir_data *rd, const char *name, int len); ++extern void ndl_put_list(struct readdir_data *rd); ++extern int ndl_check_entry(struct readdir_data *rd, ++ const char *name, int len); ++ ++ ++# define copy_inode_size(dst, src) \ ++ dst->i_size = src->i_size; \ ++ dst->i_blocks = src->i_blocks; ++ ++static inline void ++fist_copy_attr_atime(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_atime = src->i_atime; ++} ++static inline void ++fist_copy_attr_times(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_atime = src->i_atime; ++ dest->i_mtime = src->i_mtime; ++ dest->i_ctime = src->i_ctime; ++} ++static inline void ++fist_copy_attr_timesizes(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_atime = src->i_atime; ++ dest->i_mtime = src->i_mtime; ++ dest->i_ctime = src->i_ctime; ++ copy_inode_size(dest, src); ++} ++static inline void ++fist_copy_attr_all(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_mode = src->i_mode; ++ dest->i_nlink = src->i_nlink; ++ dest->i_uid = src->i_uid; ++ dest->i_gid = src->i_gid; ++ dest->i_rdev = src->i_rdev; ++ dest->i_atime = src->i_atime; ++ dest->i_mtime = src->i_mtime; ++ dest->i_ctime = src->i_ctime; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ dest->i_blksize = src->i_blksize; ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12) ++ dest->i_blkbits = src->i_blkbits; ++# endif /* linux 2.4.12 and newer */ ++ copy_inode_size(dest, src); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ dest->i_attr_flags = src->i_attr_flags; ++#else ++ dest->i_flags = src->i_flags; ++#endif ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++/* copied from linux/fs.h */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++static inline void double_lock(struct dentry *d1, struct dentry *d2) ++{ ++ struct mutex *m1 = &d1->d_inode->i_mutex; ++ struct mutex *m2 = &d2->d_inode->i_mutex; ++ if (m1 != m2) { ++ if ((unsigned long) m1 < (unsigned long) m2) { ++ struct mutex *tmp = m2; ++ m2 = m1; m1 = tmp; ++ } ++ mutex_lock(m1); ++ } ++ mutex_lock(m2); ++} ++ ++static inline void double_unlock(struct dentry *d1, struct dentry *d2) ++{ ++ struct mutex *m1 = &d1->d_inode->i_mutex; ++ struct mutex *m2 = &d2->d_inode->i_mutex; ++ mutex_unlock(m1); ++ if (m1 != m2) ++ mutex_unlock(m2); ++ dput(d1); ++ dput(d2); ++} ++ ++#else ++static inline void double_down(struct semaphore *s1, struct semaphore *s2) ++{ ++ if (s1 != s2) { ++ if ((unsigned long) s1 < (unsigned long) s2) { ++ struct semaphore *tmp = s2; ++ s2 = s1; s1 = tmp; ++ } ++ down(s1); ++ } ++ down(s2); ++} ++ ++static inline void double_up(struct semaphore *s1, struct semaphore *s2) ++{ ++ up(s1); ++ if (s1 != s2) ++ up(s2); ++} ++ ++static inline void double_lock(struct dentry *d1, struct dentry *d2) ++{ ++ double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem); ++} ++ ++static inline void double_unlock(struct dentry *d1, struct dentry *d2) ++{ ++ double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem); ++ dput(d1); ++ dput(d2); ++} ++#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) */ ++#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ ++#endif /* __KERNEL__ */ ++ ++/* ++ * Definitions for user and kernel code ++ */ ++ ++/* ioctls */ ++ ++#endif /* not __MINI_FO_H_ */ +Index: linux-2.6.21.7/fs/mini_fo/mini_fo-merge +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/mini_fo-merge +@@ -0,0 +1,180 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version ++# 2 of the License, or (at your option) any later version. ++# ++ ++BASE= ++STO= ++HELP= ++DRYRUN= ++VERBOSE= ++TMP="/tmp/" ++META_NAME="META_dAfFgHE39ktF3HD2sr" ++SKIP_DEL_LIST="skip-delete-list.mini_fo-merge" ++ ++COMMAND= ++exec_command() ++{ ++ if [ x$DRYRUN == "xset" ]; then ++ echo " would run: $COMMAND" ++ elif ! [ x$DRYRUN == "xset" ]; then ++ if [ x$VERBOSE == "xset" ]; then ++ echo " running: $COMMAND" ++ fi ++ eval $COMMAND ++ fi ++} ++ ++usage() ++{ ++cat < -s ++Version 0.1 ++ ++This script merges the contents of a mini_fo storage file system back ++to the base file system. ++ ++!!! Warning: This will modify the base filesystem and can destroy data ++ if used wrongly. ++ ++Options: ++ -b ++ the directory of the base file system. ++ ++ -s ++ the directory of the storage file system. ++ ++ -d dry run, will not change anything and print the commands that ++ would be executed. ++ ++ -t tmp dir for storing temporary file. default: $TMP ++ ++ -v show what operations are performed. ++ ++ -h displays this message. ++ ++EOF ++} ++ ++# parse parameters ++while getopts hdvt:b:s: OPTS ++ do ++ case $OPTS in ++ h) HELP="set";; ++ d) DRYRUN="set";; ++ v) VERBOSE="set";; ++ b) BASE="$OPTARG";; ++ s) STO="$OPTARG";; ++ t) TMP="$OPTARG";; ++ ?) usage ++ exit 1;; ++ esac ++done ++ ++if [ "x$HELP" == "xset" ]; then ++ usage ++ exit -1 ++fi ++ ++if ! [ -d "$BASE" ] || ! [ -d "$STO" ]; then ++ echo -e "$0:\n Error, -s and/or -b argument missing. type $0 -h for help." ++ exit -1; ++fi ++ ++# get full paths ++pushd $STO; STO=`pwd`; popd ++pushd $BASE; BASE=`pwd`; popd ++TMP=${TMP%/} ++ ++ ++cat< /dev/null ++find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^R ' | tr -s ':R' ' ' | while read ENTRY; do ++ echo "entry: $ENTRY" ++ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` ++ OLD_B_DIR=`echo $ENTRY | cut -d ' ' -f 2 | sed -e 's/\///'` ++ NEW_NAME=`echo $ENTRY | cut -d ' ' -f 3` ++ NEW_B_DIR=`echo $META_FILE | sed -e "s/$META_NAME/$NEW_NAME/" | sed -e 's/^\.\///'` ++ echo "META_FILE: $META_FILE" ++ echo "OLD_B_DIR: $OLD_B_DIR" ++ echo "NEW_NAME: $NEW_NAME" ++ echo "NEW_B_DIR: $NEW_B_DIR" ++ ++ pushd $BASE &> /dev/null ++ # remove an existing dir in storage ++ COMMAND="rm -rf $NEW_B_DIR"; exec_command ++ COMMAND="cp -R $OLD_B_DIR $NEW_B_DIR"; exec_command ++ echo "" ++ popd &> /dev/null ++ ++ # remember this dir to exclude it from deleting later ++ echo $NEW_B_DIR >> $TMP/$SKIP_DEL_LIST ++done ++ ++# delete all whiteouted files from base ++echo -e "\nDeleting whiteout'ed files from base file system..." ++find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^D ' | sed -e 's/:D//' | while read ENTRY; do ++ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` ++ DEL_NAME=`echo $ENTRY | cut -d ' ' -f 2` ++ DEL_FILE=`echo $META_FILE | sed -e "s/$META_NAME/$DEL_NAME/" | sed -e 's/^\.\///'` ++ grep -x $DEL_FILE $TMP/$SKIP_DEL_LIST &> /dev/null ++ if [ $? -ne 0 ]; then ++ pushd $BASE &> /dev/null ++ COMMAND="rm -rf $DEL_FILE"; exec_command ++ popd &> /dev/null ++ else ++ echo " excluding: $DEL_FILE as in skip-del-list." ++ fi ++done ++ ++# create all dirs and update permissions ++echo -e "\nSetting up directory structures in base file system..." ++find . -type d | sed -e 's/^\.\///' | while read DIR; do ++ PERMS=`stat -c %a $DIR` ++ DIR_UID=`stat -c %u $DIR` ++ DIR_GID=`stat -c %g $DIR` ++ pushd $BASE &> /dev/null ++ if ! [ -d $DIR ]; then ++ COMMAND="mkdir -p $DIR"; exec_command ++ fi ++ COMMAND="chmod $PERMS $DIR"; exec_command ++ COMMAND="chown $DIR_UID:$DIR_GID $DIR"; exec_command ++ popd &> /dev/null ++done ++ ++# merge all non-directory files ++echo -e "\nMerging all non-directory files...." ++for i in b c p f l s; do ++ find . -type $i | sed -e 's/^\.\///' | grep -v "$META_NAME" | while read FILE; do ++ pushd $BASE #&> /dev/null ++ COMMAND="cp -df $STO/$FILE $BASE/$FILE"; exec_command ++ popd &> /dev/null ++ done ++done ++popd &> /dev/null ++ ++#rm $TMP/$SKIP_DEL_LIST ++ ++echo "Done!" +Index: linux-2.6.21.7/fs/mini_fo/mini_fo-overlay +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/mini_fo-overlay +@@ -0,0 +1,130 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version ++# 2 of the License, or (at your option) any later version. ++# ++ ++HELP= ++SUFF= ++MNTP= ++MNT_DIR="/mnt" ++STO= ++STO_DIR="/tmp" ++BASE= ++ ++usage() ++{ ++cat <" in $STO_DIR, ++and mount point "mini_fo-" in $MNT_DIR. ++ ++Options: ++ -s ++ add given suffix to storage directory and the mount ++ point. This is usefull for overlaying one base directory ++ several times and avoiding conflicts with storage directory ++ names and mount points. ++ ++ -d ++ change the directory in which the storage directory will be ++ created (default is currently "$STO_DIR". ++ ++ -m ++ use an alternative directory to create the mini_fo ++ mountpoint (default is currently "$MNT_DIR". ++ ++ -h displays this message. ++ ++EOF ++exit 1; ++} ++ ++while getopts hm:s:d: OPTS ++ do ++ case $OPTS in ++ s) SUFF="$OPTARG";; ++ d) STO_DIR="$OPTARG";; ++ m) MNT_DIR="$OPTARG";; ++ h) HELP="set";; ++ ?) usage ++ exit 1;; ++ esac ++done ++shift $(($OPTIND - 1)) ++ ++BASE="$1" ++ ++if [ "x$HELP" == "xset" ]; then ++ usage ++ exit -1 ++fi ++ ++# fix suffix ++if [ "x$SUFF" != "x" ]; then ++ SUFF="-$SUFF" ++fi ++ ++# kill trailing slashes ++MNT_DIR=${MNT_DIR%/} ++STO_DIR=${STO_DIR%/} ++BASE=${BASE%/} ++ ++ ++if ! [ -d "$BASE" ]; then ++ echo "invalid base dir $BASE, run $0 -h for help." ++ exit -1 ++fi ++ ++# check opts ++if ! [ -d "$MNT_DIR" ]; then ++ echo "invalid mount dir $MNT_DIR, run $0 -h for help." ++ exit -1 ++fi ++ ++if ! [ -d "$STO_DIR" ]; then ++ echo "invalid sto_dir_dir $STO_DIR, run $0 -h for help." ++ exit -1 ++fi ++ ++MNTP="$MNT_DIR/mini_fo-`basename $BASE`$SUFF" ++STO="$STO_DIR/sto-`basename $BASE`$SUFF" ++ ++# create the mount point if it doesn't exist ++mkdir -p $MNTP ++if [ $? -ne 0 ]; then ++ echo "Error, failed to create mount point $MNTP" ++fi ++ ++mkdir -p $STO ++if [ $? -ne 0 ]; then ++ echo "Error, failed to create storage dir $STO" ++fi ++ ++# check if fs is already mounted ++mount | grep mini_fo | grep $MNTP &> /dev/null ++if [ $? -eq 0 ]; then ++ echo "Error, existing mini_fo mount at $MNTP." ++ exit -1 ++fi ++ ++mount | grep mini_fo | grep $STO &> /dev/null ++if [ $? -eq 0 ]; then ++ echo "Error, $STO seems to be used already." ++ exit -1 ++fi ++ ++# mount ++mount -t mini_fo -o base=$BASE,sto=$STO $BASE $MNTP ++ ++if [ $? -ne 0 ]; then ++ echo "Error, mounting failed, maybe no permisson to mount?" ++fi +Index: linux-2.6.21.7/fs/mini_fo/mmap.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/mmap.c +@@ -0,0 +1,637 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++ ++#ifdef FIST_COUNT_WRITES ++/* for counting writes in the middle vs. regular writes */ ++unsigned long count_writes = 0, count_writes_middle = 0; ++#endif /* FIST_COUNT_WRITES */ ++ ++/* forward declaration of commit write and prepare write */ ++STATIC int mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to); ++STATIC int mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to); ++ ++ ++/* ++ * Function for handling creation of holes when lseek-ing past the ++ * end of the file and then writing some data. ++ */ ++int ++mini_fo_fill_zeros(file_t* file, page_t *page, unsigned from) ++{ ++ int err = 0; ++ dentry_t *dentry = file->f_dentry; ++ inode_t *inode = dentry->d_inode; ++ page_t *tmp_page; ++ int index; ++ ++ print_entry_location(); ++ ++ for (index = inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { ++ tmp_page = mini_fo_get1page(file, index); ++ if (IS_ERR(tmp_page)) { ++ err = PTR_ERR(tmp_page); ++ goto out; ++ } ++ ++ /* ++ * zero out rest of the contents of the page between the appropriate ++ * offsets. ++ */ ++ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); ++ ++ if (! (err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) ++ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); ++ ++ page_cache_release(tmp_page); ++ if (err < 0) ++ goto out; ++ if (current->need_resched) ++ schedule(); ++ } ++ ++ /* zero out appropriate parts of last page */ ++ ++ /* ++ * if the encoding type is block, then adjust the 'from' (where the ++ * zeroing will start) offset appropriately ++ */ ++ from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); ++ ++ if ((from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) { ++ ++ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, from - (inode->i_size & ~PAGE_CACHE_MASK)); ++ if (! (err = mini_fo_prepare_write(file, page, 0, PAGE_CACHE_SIZE))) ++ err = mini_fo_commit_write(file, page, 0, PAGE_CACHE_SIZE); ++ ++ if (err < 0) ++ goto out; ++ if (current->need_resched) ++ schedule(); ++ } ++ ++ out: ++ print_exit_status(err); ++ return err; ++} ++ ++ ++ ++STATIC int ++mini_fo_writepage(page_t *page) ++{ ++ int err = -EIO; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ page_t *hidden_page; ++ char *kaddr, *hidden_kaddr; ++ ++ print_entry_location(); ++ ++ inode = page->mapping->host; ++ hidden_inode = itohi(inode); ++ ++ /* ++ * writepage is called when shared mmap'ed files need to write ++ * their pages, while prepare/commit_write are called from the ++ * non-paged write() interface. (However, in 2.3 the two interfaces ++ * share the same cache, while in 2.2 they didn't.) ++ * ++ * So we pretty much have to duplicate much of what commit_write does. ++ */ ++ ++ /* find lower page (returns a locked page) */ ++ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); ++ if (!hidden_page) ++ goto out; ++ ++ /* get page address, and encode it */ ++ kaddr = (char *) kmap(page); ++ hidden_kaddr = (char*) kmap(hidden_page); ++ mini_fo_encode_block(kaddr, hidden_kaddr, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); ++ /* if encode_block could fail, then return error */ ++ kunmap(page); ++ kunmap(hidden_page); ++ ++ /* call lower writepage (expects locked page) */ ++ err = hidden_inode->i_mapping->a_ops->writepage(hidden_page); ++ ++ /* ++ * update mtime and ctime of lower level file system ++ * mini_fo' mtime and ctime are updated by generic_file_write ++ */ ++ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,1) ++ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ ++# endif /* kernel older than 2.4.1 */ ++ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ ++ ++ if (err) ++ ClearPageUptodate(page); ++ else ++ SetPageUptodate(page); ++ out: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1) ++ UnlockPage(page); ++# endif /* kernel 2.4.1 and newer */ ++ print_exit_status(err); ++ return err; ++} ++ ++ ++/* ++ * get one page from cache or lower f/s, return error otherwise. ++ * returns unlocked, up-to-date page (if ok), with increased refcnt. ++ */ ++page_t * ++mini_fo_get1page(file_t *file, int index) ++{ ++ page_t *page; ++ dentry_t *dentry; ++ inode_t *inode; ++ struct address_space *mapping; ++ int err; ++ ++ print_entry_location(); ++ ++ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ ++ inode = dentry->d_inode; ++ mapping = inode->i_mapping; ++ ++ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); ++ if (index < 0) { ++ printk("%s BUG: index=%d\n", __FUNCTION__, index); ++ page = ERR_PTR(-EIO); ++ goto out; ++ } ++ page = read_cache_page(mapping, ++ index, ++ (filler_t *) mapping->a_ops->readpage, ++ (void *) file); ++ if (IS_ERR(page)) ++ goto out; ++ wait_on_page(page); ++ if (!Page_Uptodate(page)) { ++ lock_page(page); ++ err = mapping->a_ops->readpage(file, page); ++ if (err) { ++ page = ERR_PTR(err); ++ goto out; ++ } ++ wait_on_page(page); ++ if (!Page_Uptodate(page)) { ++ page = ERR_PTR(-EIO); ++ goto out; ++ } ++ } ++ ++ out: ++ print_exit_pointer(page); ++ return page; ++} ++ ++ ++/* ++ * get one page from cache or lower f/s, return error otherwise. ++ * similar to get1page, but doesn't guarantee that it will return ++ * an unlocked page. ++ */ ++page_t * ++mini_fo_get1page_cached(file_t *file, int index) ++{ ++ page_t *page; ++ dentry_t *dentry; ++ inode_t *inode; ++ struct address_space *mapping; ++ int err; ++ ++ print_entry_location(); ++ ++ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ ++ inode = dentry->d_inode; ++ mapping = inode->i_mapping; ++ ++ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); ++ if (index < 0) { ++ printk("%s BUG: index=%d\n", __FUNCTION__, index); ++ page = ERR_PTR(-EIO); ++ goto out; ++ } ++ page = read_cache_page(mapping, ++ index, ++ (filler_t *) mapping->a_ops->readpage, ++ (void *) file); ++ if (IS_ERR(page)) ++ goto out; ++ ++ out: ++ print_exit_pointer(page); ++ return page; ++} ++ ++ ++/* ++ * readpage is called from generic_page_read and the fault handler. ++ * If your file system uses generic_page_read for the read op, it ++ * must implement readpage. ++ * ++ * Readpage expects a locked page, and must unlock it. ++ */ ++STATIC int ++mini_fo_do_readpage(file_t *file, page_t *page) ++{ ++ int err = -EIO; ++ dentry_t *dentry; ++ file_t *hidden_file = NULL; ++ dentry_t *hidden_dentry; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ char *page_data; ++ page_t *hidden_page; ++ char *hidden_page_data; ++ int real_size; ++ ++ print_entry_location(); ++ ++ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ ++ if (ftopd(file) != NULL) ++ hidden_file = ftohf(file); ++ hidden_dentry = dtohd(dentry); ++ inode = dentry->d_inode; ++ hidden_inode = itohi(inode); ++ ++ fist_dprint(7, "%s: requesting page %d from file %s\n", __FUNCTION__, page->index, dentry->d_name.name); ++ ++ MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages); ++ MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); ++ FOR_EACH_PAGE ++ CURRENT_HIDDEN_PAGE = NULL; ++ ++ /* find lower page (returns a locked page) */ ++ FOR_EACH_PAGE { ++ fist_dprint(8, "%s: Current page index = %d\n", __FUNCTION__, CURRENT_HIDDEN_PAGEINDEX); ++ CURRENT_HIDDEN_PAGE = read_cache_page(hidden_inode->i_mapping, ++ CURRENT_HIDDEN_PAGEINDEX, ++ (filler_t *) hidden_inode->i_mapping->a_ops->readpage, ++ (void *) hidden_file); ++ if (IS_ERR(CURRENT_HIDDEN_PAGE)) { ++ err = PTR_ERR(CURRENT_HIDDEN_PAGE); ++ CURRENT_HIDDEN_PAGE = NULL; ++ goto out_release; ++ } ++ } ++ ++ /* ++ * wait for the page data to show up ++ * (signaled by readpage as unlocking the page) ++ */ ++ FOR_EACH_PAGE { ++ wait_on_page(CURRENT_HIDDEN_PAGE); ++ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { ++ /* ++ * call readpage() again if we returned from wait_on_page with a ++ * page that's not up-to-date; that can happen when a partial ++ * page has a few buffers which are ok, but not the whole ++ * page. ++ */ ++ lock_page(CURRENT_HIDDEN_PAGE); ++ err = hidden_inode->i_mapping->a_ops->readpage(hidden_file, ++ CURRENT_HIDDEN_PAGE); ++ if (err) { ++ CURRENT_HIDDEN_PAGE = NULL; ++ goto out_release; ++ } ++ wait_on_page(CURRENT_HIDDEN_PAGE); ++ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { ++ err = -EIO; ++ goto out_release; ++ } ++ } ++ } ++ ++ /* map pages, get their addresses */ ++ page_data = (char *) kmap(page); ++ FOR_EACH_PAGE ++ CURRENT_HIDDEN_PAGEDATA = (char *) kmap(CURRENT_HIDDEN_PAGE); ++ ++ /* if decode_block could fail, then return error */ ++ err = 0; ++ real_size = hidden_inode->i_size - (page->index << PAGE_CACHE_SHIFT); ++ if (real_size <= 0) ++ memset(page_data, 0, PAGE_CACHE_SIZE); ++ else if (real_size < PAGE_CACHE_SIZE) { ++ mini_fo_decode_block(hidden_page_data, page_data, real_size, inode, inode->i_sb, page->index); ++ memset(page_data + real_size, 0, PAGE_CACHE_SIZE - real_size); ++ } else ++ mini_fo_decode_block(hidden_page_data, page_data, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); ++ ++ FOR_EACH_PAGE ++ kunmap(CURRENT_HIDDEN_PAGE); ++ kunmap(page); ++ ++ out_release: ++ FOR_EACH_PAGE ++ if (CURRENT_HIDDEN_PAGE) ++ page_cache_release(CURRENT_HIDDEN_PAGE); /* undo read_cache_page */ ++ ++ FREE_PAGE_POINTERS(hidden_pages, num_hidden_pages); ++ FREE_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); ++ ++ out: ++ if (err == 0) ++ SetPageUptodate(page); ++ else ++ ClearPageUptodate(page); ++ ++ print_exit_status(err); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_readpage(file_t *file, page_t *page) ++{ ++ int err; ++ print_entry_location(); ++ ++ err = mini_fo_do_readpage(file, page); ++ ++ /* ++ * we have to unlock our page, b/c we _might_ have gotten a locked page. ++ * but we no longer have to wakeup on our page here, b/c UnlockPage does ++ * it ++ */ ++ UnlockPage(page); ++ ++ print_exit_status(err); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to) ++{ ++ int err = 0; ++ ++ print_entry_location(); ++ ++ /* ++ * we call kmap(page) only here, and do the kunmap ++ * and the actual downcalls, including unlockpage and uncache ++ * in commit_write. ++ */ ++ kmap(page); ++ ++ /* fast path for whole page writes */ ++ if (from == 0 && to == PAGE_CACHE_SIZE) ++ goto out; ++ /* read the page to "revalidate" our data */ ++ /* call the helper function which doesn't unlock the page */ ++ if (!Page_Uptodate(page)) ++ err = mini_fo_do_readpage(file, page); ++ ++ out: ++ print_exit_status(err); ++ return err; ++} ++ ++ ++ ++STATIC int ++mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to) ++{ ++ int err = -ENOMEM; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ page_t *hidden_page; ++ file_t *hidden_file = NULL; ++ loff_t pos; ++ unsigned bytes = to - from; ++ unsigned hidden_from, hidden_to, hidden_bytes; ++ ++ print_entry_location(); ++ ++ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ ++ hidden_inode = itohi(inode); ++ ++ ASSERT(file != NULL); ++ /* ++ * here we have a kmapped page, with data from the user copied ++ * into it. we need to encode_block it, and then call the lower ++ * commit_write. We also need to simulate same behavior of ++ * generic_file_write, and call prepare_write on the lower f/s first. ++ */ ++#ifdef FIST_COUNT_WRITES ++ count_writes++; ++# endif /* FIST_COUNT_WRITES */ ++ ++ /* this is append and/or extend -- we can't have holes so fill them in */ ++ if (page->index > (hidden_inode->i_size >> PAGE_CACHE_SHIFT)) { ++ page_t *tmp_page; ++ int index; ++ for (index = hidden_inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { ++ tmp_page = mini_fo_get1page(file, index); ++ if (IS_ERR(tmp_page)) { ++ err = PTR_ERR(tmp_page); ++ goto out; ++ } ++ /* zero out the contents of the page at the appropriate offsets */ ++ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); ++ if (!(err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) ++ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); ++ page_cache_release(tmp_page); ++ if (err < 0) ++ goto out; ++ if (current->need_resched) ++ schedule(); ++ } ++ } ++ ++ if (ftopd(file) != NULL) ++ hidden_file = ftohf(file); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_inode->i_mutex); ++#else ++ down(&hidden_inode->i_sem); ++#endif ++ /* find lower page (returns a locked page) */ ++ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); ++ if (!hidden_page) ++ goto out; ++ ++#if FIST_ENCODING_BLOCKSIZE > 1 ++# error encoding_blocksize greater than 1 is not yet supported ++# endif /* FIST_ENCODING_BLOCKSIZE > 1 */ ++ ++ hidden_from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); ++ hidden_to = ((to + FIST_ENCODING_BLOCKSIZE - 1) & (~(FIST_ENCODING_BLOCKSIZE - 1))); ++ if ((page->index << PAGE_CACHE_SHIFT) + to > hidden_inode->i_size) { ++ ++ /* ++ * if this call to commit_write had introduced holes and the code ++ * for handling holes was invoked, then the beginning of this page ++ * must be zeroed out ++ * zero out bytes from 'size_of_file%pagesize' to 'from'. ++ */ ++ if ((hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) ++ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)); ++ ++ } ++ hidden_bytes = hidden_to - hidden_from; ++ ++ /* call lower prepare_write */ ++ err = -EINVAL; ++ if (hidden_inode->i_mapping && ++ hidden_inode->i_mapping->a_ops && ++ hidden_inode->i_mapping->a_ops->prepare_write) ++ err = hidden_inode->i_mapping->a_ops->prepare_write(hidden_file, ++ hidden_page, ++ hidden_from, ++ hidden_to); ++ if (err) ++ /* don't leave locked pages behind, esp. on an ENOSPC */ ++ goto out_unlock; ++ ++ fist_dprint(8, "%s: encoding %d bytes\n", __FUNCTION__, hidden_bytes); ++ mini_fo_encode_block((char *) page_address(page) + hidden_from, (char*) page_address(hidden_page) + hidden_from, hidden_bytes, inode, inode->i_sb, page->index); ++ /* if encode_block could fail, then goto unlock and return error */ ++ ++ /* call lower commit_write */ ++ err = hidden_inode->i_mapping->a_ops->commit_write(hidden_file, ++ hidden_page, ++ hidden_from, ++ hidden_to); ++ ++ if (err < 0) ++ goto out_unlock; ++ ++ err = bytes; /* convert error to no. of bytes */ ++ ++ inode->i_blocks = hidden_inode->i_blocks; ++ /* we may have to update i_size */ ++ pos = (page->index << PAGE_CACHE_SHIFT) + to; ++ if (pos > inode->i_size) ++ inode->i_size = pos; ++ ++ /* ++ * update mtime and ctime of lower level file system ++ * mini_fo' mtime and ctime are updated by generic_file_write ++ */ ++ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; ++ ++ mark_inode_dirty_sync(inode); ++ ++ out_unlock: ++ UnlockPage(hidden_page); ++ page_cache_release(hidden_page); ++ kunmap(page); /* kmap was done in prepare_write */ ++ out: ++ /* we must set our page as up-to-date */ ++ if (err < 0) ++ ClearPageUptodate(page); ++ else ++ SetPageUptodate(page); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_inode->i_mutex); ++#else ++ up(&hidden_inode->i_sem); ++#endif ++ print_exit_status(err); ++ return err; /* assume all is ok */ ++} ++ ++ ++STATIC int ++mini_fo_bmap(struct address_space *mapping, long block) ++{ ++ int err = 0; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ ++ print_entry_location(); ++ ++ inode = (inode_t *) mapping->host; ++ hidden_inode = itohi(inode); ++ ++ if (hidden_inode->i_mapping->a_ops->bmap) ++ err = hidden_inode->i_mapping->a_ops->bmap(hidden_inode->i_mapping, block); ++ print_exit_location(); ++ return err; ++} ++ ++ ++/* ++ * This function is copied verbatim from mm/filemap.c. ++ * XXX: It should be simply moved to some header file instead -- bug Al about it! ++ */ ++static inline int sync_page(struct page *page) ++{ ++ struct address_space *mapping = page->mapping; ++ ++ if (mapping && mapping->a_ops && mapping->a_ops->sync_page) ++ return mapping->a_ops->sync_page(page); ++ return 0; ++} ++ ++ ++/* ++ * XXX: we may not need this function if not FIST_FILTER_DATA. ++ * FIXME: for FIST_FILTER_SCA, get all lower pages and sync them each. ++ */ ++STATIC int ++mini_fo_sync_page(page_t *page) ++{ ++ int err = 0; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ page_t *hidden_page; ++ ++ print_entry_location(); ++ ++ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ ++ hidden_inode = itohi(inode); ++ ++ /* find lower page (returns a locked page) */ ++ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); ++ if (!hidden_page) ++ goto out; ++ ++ err = sync_page(hidden_page); ++ ++ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ ++ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ ++ ++ out: ++ print_exit_status(err); ++ return err; ++} +Index: linux-2.6.21.7/fs/mini_fo/README +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/README +@@ -0,0 +1,163 @@ ++README for the mini_fo overlay file system ++========================================= ++ ++ ++WHAT IS MINI_FO? ++---------------- ++ ++mini_fo is a virtual kernel file system that can make read-only ++file systems writable. This is done by redirecting modifying operations ++to a writeable location called "storage directory", and leaving the ++original data in the "base directory" untouched. When reading, the ++file system merges the modifed and original data so that only the ++newest versions will appear. This occurs transparently to the user, ++who can access the data like on any other read-write file system. ++ ++Base and storage directories may be located on the same or on ++different partitions and may be of different file system types. While ++the storage directory obviously needs to be writable, the base may or ++may not be writable, what doesn't matter as it will no be modified ++anyway. ++ ++ ++WHAT IS GOOD FOR? ++----------------- ++ ++The primary purpose of the mini_fo file system is to allow easy ++software updates to embedded systems, that often store their root ++file system in a read-only flash file system, but there are many ++more as for example sandboxing, or for allowing live-cds to ++permanently store information. ++ ++ ++BUILDING ++-------- ++This should be simple. Adjust the Makefile to point to the correct ++kernel headers you want to build the module for. Then: ++ ++ # make ++ ++should build "mini_fo.o" for a 2.4 kernel or "mini_fo.ko" for a 2.6 ++kernel. ++ ++If you are building the module for you current kernel, you can install ++the module (as root): ++ ++ # make install ++ ++or uninstall with ++ ++ # make uninstall ++ ++ ++USING THE FILE SYSTEM ++-------------------- ++ ++the general mount syntax is: ++ ++ mount -t mini_fo -o base=,sto=\ ++ ++ ++Example: ++ ++You have mounted a cdrom to /mnt/cdrom and want to modifiy some files ++on it: ++ ++load the module (as root) ++ ++ # insmod mini_fo.o for a 2.4 kernel or ++ ++ # insmod mini_fo.ko for a 2.6 kernel ++ ++ ++create a storage dir in tmp and a mountpoint for mini_fo: ++ ++ # mkdir /tmp/sto ++ # mkdir /mnt/mini_fo ++ ++and mount the mini_fo file system: ++ ++ # mount -t mini_fo -o base=/mnt/cdrom,sto=/tmp/sto /mnt/cdrom /mnt/mini_fo ++ ++ ++Now the data stored on the cd can be accessed via the mini_fo ++mountpoint just like any read-write file system, files can be modified ++and deleted, new ones can be created and so on. When done unmount the ++file system: ++ ++ # unmount /mnt/mini_fo ++ ++Note that if the file system is mounted again using the same storage ++file system, of course it will appear in the modified state again. If ++you remount it using an new empty storage directory, it will be ++unmodified. Therefore by executing: ++ ++ # cd /tmp/sto ++ # rm -rf * ++ ++you can nuke all the changes you made to the original file system. But ++ remember NEVER do this while the mini_fo file system is mounted! ++ ++ ++Alternatively you can use the mini_fo-overlay bash script, that ++simplifies managing mini_fo mounts. See TOOLS Section. ++ ++ ++TOOLS ++----- ++ ++mini_fo-merge (experimental): ++ ++This is a bash script that will merge changes contained in the storage ++directory back to the base directory. This allows mini_fo to function ++as a cache file system by overlaying a slow (network, ...) file system ++and using a fast (ramdisk, ...) as storage. When done, changes can be ++merged back to the (slow) base with mini_fo-merge. See "mini_fo-merge ++-h" for details. ++ ++It can be usefull for merging changes back after a successfull test ++(patches, software updates...) ++ ++ ++mini_fo-overlay: ++ ++This bash script simplifies managing one or more mini_fo mounts. For ++overlaying a directory called "basedir1", you can just call: ++ ++ # mini_fo-overlay basedir1 ++ ++This will mount mini_fo with "basedir1" as base, "/tmp/sto-basedir1/" ++as storage to "/mnt/mini_fo-basedir1/". It has more options though, ++type "mini_fo-overlay -h" for details. ++ ++ ++DOCUMENTATION, REPORTING BUGS, GETTING HELP ++------------------------------------------- ++ ++Please visit the mini_fo project page at: ++ ++http://www.denx.de/twiki/bin/view/Know/MiniFOHome ++ ++ ++WARNINGS ++-------- ++ ++Never modify the base or the storage directorys while the mini_fo ++file system is mounted, or you might crash you system. Simply accessing ++and reading should not cause any trouble. ++ ++Exporting a mini_fo mount point via NFS has not been tested, and may ++or may not work. ++ ++Check the RELEASE_NOTES for details on bugs and features. ++ ++ ++ ++Copyright (C) 2004, 2005 Markus Klotzbuecher ++ ++This program is free software; you can redistribute it and/or ++modify it under the terms of the GNU General Public License ++as published by the Free Software Foundation; either version ++2 of the License, or (at your option) any later version. ++ ++ +Index: linux-2.6.21.7/fs/mini_fo/RELEASE_NOTES +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/RELEASE_NOTES +@@ -0,0 +1,111 @@ ++Release: mini_fo-0.6.1 (v0-6-1) ++Date: 21.09.2005 ++ ++ ++Changes: ++-------- ++v0-6-1: ++ ++- bugfixes (see ChangeLog) ++ ++- two helper scripts "mini_fo_merge" and "mini_fo_overlay" (see ++ README for details). ++ ++v0-6-0: ++ ++- Support for 2.4 and 2.6 (see Makefile) ++ ++- Partial hard link support (creating works as expected, but already ++ existing links in the base file system will be treated as if they ++ were individual files). ++ ++- Various bugfixes and cleanups. ++ ++ ++v0-6-0-pre1: ++ ++- This is mini_fo-0-6-0-pre1! This release is a complete rewrite of ++ many vital mini_fo parts such as the old whiteout list code which ++ has been replaced by the new META subsystem. ++ ++- Light weight directory renaming implemented. This means if a ++ directory is renamed via the mini_fo filesystem this will no longer ++ result in a complete copy in storage, instead only one empty ++ directory will be created. All base filed contained in the original ++ directory stay there until modified. ++ ++- Special files (creating, renaming, deleting etc.) now working. ++ ++- Many bugfixes and cleanup, mini_fo is now a lot more stable. ++ ++ ++v0-5-10: ++ ++- Final release of the 0-5-* versions. Next will be a complete rewrite ++ of many features. This release contains several bugfixes related to ++ directory renaming. ++ ++ ++v0-5-10-pre6: ++ ++- Lots of cleanup and several bugfixes related to directory deleting ++ ++- Directory renaming suddenly works, what is most likely due to the ++ fact tha that "mv" is smart: if the classic rename doesn't work it ++ will assume that source and target file are on different fs and will ++ copy the directory and try to remove the source directory. Until ++ directory removing wasn't implemented, it would fail to do this and ++ rollback. ++ So, directory renaming works for now, but it doesn't yet do what you ++ would expect from a overlay fs, so use with care. ++ ++ ++v0-5-10-pre5: ++ ++- implemented directory deleting ++- made parsing of mount options more stable ++- New format of mount options! (See README) ++- I can't reproduce the unknown panic with 2.4.25 anymore, so I'll ++ happily assume it never existed! ++ ++ ++Implemented features: ++--------------------- ++ ++- creating hard links (see BUGS on already existing hard links) ++- lightweight directory renaming ++- renaming device files, pipes, sockets, etc. ++- creating, renaming, deleting of special files ++- deleting directorys ++- general directory reading (simple "ls" ) ++- creating files in existing directorys ++- creating directorys ++- renaming files. ++- reading and writing files (involves opening) ++- appending to files (creates copy in storage) ++- deleting files ++- llseek works too, what allows editors to work ++- persistency (a deleted file stay deleted over remounts) ++- use of symbolic links ++- creating of device files ++ ++ ++Not (yet) implemented features: ++------------------------------- ++ ++- full hard link support. ++ ++ ++ ++BUGS: ++----- ++ ++Hard links in the base file system will be treated as individual ++files, not as links to one inode. ++ ++The main problem with hard links isn't allowing to create them, but ++their pure existence. If you modify a base hard link, the changes made ++will only show up on this link, the other link will remain in the ++original state. I hope to fix this someday. Please note that this does ++not effect the special hard links '.' and '..', that are handled ++seperately by the lower fs. +Index: linux-2.6.21.7/fs/mini_fo/state.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/state.c +@@ -0,0 +1,620 @@ ++/* ++ * Copyright (C) 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++ ++/* create the storage file, setup new states */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd) ++#else ++int create_sto_reg_file(dentry_t *dentry, int mode) ++#endif ++{ ++ int err = 0; ++ inode_t *dir; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ if(exists_in_storage(dentry)) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: wrong type or state.\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ err = get_neg_sto_dentry(dentry); ++ ++ if (err) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR getting neg. sto dentry.\n"); ++ goto out; ++ } ++ ++ dir = dentry->d_parent->d_inode; ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* lock parent */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ mode, nd); ++#else ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ mode); ++#endif ++ if(err) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file.\n"); ++ goto out_lock; ++ } ++ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file [2].\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ ++ /* interpose the new inode */ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == NON_EXISTANT) { ++ dtost(dentry) = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == UNMODIFIED) { ++ dtost(dentry) = MODIFIED; ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ } ++ fist_copy_attr_timesizes(dentry->d_parent->d_inode, ++ hidden_sto_dir_dentry->d_inode); ++ ++ out_lock: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++/* create the sto dir, setup states */ ++int create_sto_dir(dentry_t *dentry, int mode) ++{ ++ int err = 0; ++ inode_t *dir; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ /* had to take the "!S_ISDIR(mode))" check out, because it failed */ ++ if(exists_in_storage(dentry)) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: wrong type or state.\\ ++n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = get_neg_sto_dentry(dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ dir = dentry->d_parent->d_inode; ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ mode); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir.\n"); ++ goto out_lock; ++ } ++ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir [2].\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ ++ /* interpose the new inode */ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtopd(dentry)->state == NON_EXISTANT) { ++ dtopd(dentry)->state = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtopd(dentry)->state == UNMODIFIED) { ++ dtopd(dentry)->state = MODIFIED; ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR, invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ } ++ ++ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); ++ ++ /* initalize the wol list */ ++ itopd(dentry->d_inode)->deleted_list_size = -1; ++ itopd(dentry->d_inode)->renamed_list_size = -1; ++ meta_build_lists(dentry); ++ ++ ++ out_lock: ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++int create_sto_nod(dentry_t *dentry, int mode, dev_t dev) ++#else ++int create_sto_nod(dentry_t *dentry, int mode, int dev) ++#endif ++{ ++ int err = 0; ++ inode_t *dir; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ if(exists_in_storage(dentry)) { ++ err = -EEXIST; ++ goto out; ++ } ++ err = get_neg_sto_dentry(dentry); ++ ++ if (err) { ++ printk(KERN_CRIT "mini_fo: create_sto_nod: ERROR getting neg. sto dentry.\n"); ++ goto out; ++ } ++ ++ dir = dentry->d_parent->d_inode; ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* lock parent */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode, dev); ++ if(err) ++ goto out_lock; ++ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: create_sto_nod: creating storage inode failed [1].\n"); ++ err = -EINVAL; /* return something indicating failure */ ++ goto out_lock; ++ } ++ ++ /* interpose the new inode */ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == NON_EXISTANT) { ++ dtost(dentry) = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == UNMODIFIED) { ++ dtost(dentry) = MODIFIED; ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: create_sto_nod: error, invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ } ++ ++ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); ++ ++ out_lock: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++ ++/* unimplemented (and possibly not usefull): ++ ++ nondir-del_to_del_rew ++ nondir-non_exist_to_creat ++ ++ dir-unmod_to_del ++ dir-mod_to_del ++ dir-creat_to_del ++ dir-del_rew_to_del ++ dir-del_to_del_rew ++ dir-non_exist_to_creat ++*/ ++ ++ ++/* bring a file of any type from state UNMODIFIED to MODIFIED */ ++int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag) ++{ ++ int err = 0; ++ struct vfsmount *tgt_mnt; ++ struct vfsmount *src_mnt; ++ dentry_t *tgt_dentry; ++ dentry_t *src_dentry; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if((dtost(dentry) != UNMODIFIED) || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ err = get_neg_sto_dentry(dentry); ++ ++ if (err) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR getting neg. sto dentry.\n"); ++ goto out; ++ } ++ ++ /* create sto file */ ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* lock parent */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++ /* handle different types of nondirs */ ++ if(S_ISCHR(dentry->d_inode->i_mode) || ++ S_ISBLK(dentry->d_inode->i_mode)) { ++ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dtohd(dentry)->d_inode->i_mode, ++ dtohd(dentry)->d_inode->i_rdev); ++ } ++ ++ else if(S_ISREG(dentry->d_inode->i_mode)) { ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dtohd(dentry)->d_inode->i_mode, NULL); ++#else ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dtohd(dentry)->d_inode->i_mode); ++#endif ++ } ++ if(err) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR creating sto file.\n"); ++ goto out_lock; ++ } ++ ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR, invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ ++ fist_copy_attr_timesizes(dentry->d_parent->d_inode, ++ hidden_sto_dir_dentry->d_inode); ++ dtost(dentry) = MODIFIED; ++ ++ /* copy contents if regular file and cp_flag = 1 */ ++ if((cp_flag == 1) && S_ISREG(dentry->d_inode->i_mode)) { ++ ++ /* unlock first */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ dput(hidden_sto_dir_dentry); ++ ++ tgt_dentry = dtohd2(dentry); ++ tgt_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ src_dentry = dtohd(dentry); ++ src_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt; ++ ++ err = mini_fo_cp_cont(tgt_dentry, tgt_mnt, ++ src_dentry, src_mnt); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR copying contents.\n"); ++ } ++ goto out; ++ } ++ ++ out_lock: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++/* this function is currently identical to nondir_creat_to_del */ ++int nondir_del_rew_to_del(dentry_t *dentry) ++{ ++ return nondir_creat_to_del(dentry); ++} ++ ++int nondir_creat_to_del(dentry_t *dentry) ++{ ++ int err = 0; ++ ++ inode_t *hidden_sto_dir_inode; ++ dentry_t *hidden_sto_dir_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ check_mini_fo_dentry(dentry); ++ ++ /* for now this function serves for both state DEL_REWRITTEN and ++ * CREATED */ ++ if(!(dtost(dentry) == CREATED || (dtost(dentry) == DEL_REWRITTEN)) || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_mod_to_del/del_rew_to_del: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* avoid destroying the hidden inode if the file is in use */ ++ dget(hidden_sto_dentry); ++ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ ++ dtost(dentry) = NON_EXISTANT; ++ ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ out: ++ return err; ++} ++ ++int nondir_mod_to_del(dentry_t *dentry) ++{ ++ int err; ++ dentry_t *hidden_sto_dentry; ++ inode_t *hidden_sto_dir_inode; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) != MODIFIED || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_mod_to_del: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* avoid destroying the hidden inode if the file is in use */ ++ dget(hidden_sto_dentry); ++ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ ++ /* dput base dentry, this will relase the inode and free the ++ * dentry, as we will never need it again. */ ++ dput(dtohd(dentry)); ++ dtohd(dentry) = NULL; ++ dtost(dentry) = DELETED; ++ ++ /* add deleted file to META-file */ ++ meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ out: ++ return err; ++} ++ ++int nondir_unmod_to_del(dentry_t *dentry) ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) != UNMODIFIED || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_del: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ /* next we have to get a negative dentry for the storage file */ ++ err = get_neg_sto_dentry(dentry); ++ ++ if(err) ++ goto out; ++ ++ /* add deleted file to META lists */ ++ err = meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ ++ if(err) ++ goto out; ++ ++ /* dput base dentry, this will relase the inode and free the ++ * dentry, as we will never need it again. */ ++ dput(dtohd(dentry)); ++ dtohd(dentry) = NULL; ++ dtost(dentry) = DELETED; ++ ++ out: ++ return err; ++} ++ ++/* bring a dir from state UNMODIFIED to MODIFIED */ ++int dir_unmod_to_mod(dentry_t *dentry) ++{ ++ int err; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) != UNMODIFIED || ++ !S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ /* this creates our dir incl. sto. structure */ ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ out: ++ return err; ++} ++ +Index: linux-2.6.21.7/fs/mini_fo/super.c +=================================================================== +--- /dev/null ++++ linux-2.6.21.7/fs/mini_fo/super.c +@@ -0,0 +1,281 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++ ++STATIC void ++mini_fo_read_inode(inode_t *inode) ++{ ++ static struct address_space_operations mini_fo_empty_aops; ++ ++ __itopd(inode) = kmalloc(sizeof(struct mini_fo_inode_info), GFP_KERNEL); ++ if (!itopd(inode)) { ++ printk("<0>%s:%s:%d: No kernel memory!\n", __FILE__, __FUNCTION__, __LINE__); ++ ASSERT(NULL); ++ } ++ itohi(inode) = NULL; ++ itohi2(inode) = NULL; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ inode->i_version++; ++#else ++ inode->i_version = ++event; /* increment inode version */ ++#endif ++ inode->i_op = &mini_fo_main_iops; ++ inode->i_fop = &mini_fo_main_fops; ++#if 0 ++ /* ++ * XXX: To export a file system via NFS, it has to have the ++ * FS_REQUIRES_DEV flag, so turn it on. But should we inherit it from ++ * the lower file system, or can we allow our file system to be exported ++ * even if the lower one cannot be natively exported. ++ */ ++ inode->i_sb->s_type->fs_flags |= FS_REQUIRES_DEV; ++ /* ++ * OK, the above was a hack, which is now turned off because it may ++ * cause a panic/oops on some systems. The correct way to export a ++ * "nodev" filesystem is via using nfs-utils > 1.0 and the "fsid=" export ++ * parameter, which requires 2.4.20 or later. ++ */ ++#endif ++ /* I don't think ->a_ops is ever allowed to be NULL */ ++ inode->i_mapping->a_ops = &mini_fo_empty_aops; ++} ++ ++ ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++/* ++ * No need to call write_inode() on the lower inode, as it ++ * will have been marked 'dirty' anyway. But we might need ++ * to write some of our own stuff to disk. ++ */ ++STATIC void ++mini_fo_write_inode(inode_t *inode, int sync) ++{ ++ print_entry_location(); ++ print_exit_location(); ++} ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ ++ ++STATIC void ++mini_fo_put_inode(inode_t *inode) ++{ ++ /* ++ * This is really funky stuff: ++ * Basically, if i_count == 1, iput will then decrement it and this inode will be destroyed. ++ * It is currently holding a reference to the hidden inode. ++ * Therefore, it needs to release that reference by calling iput on the hidden inode. ++ * iput() _will_ do it for us (by calling our clear_inode), but _only_ if i_nlink == 0. ++ * The problem is, NFS keeps i_nlink == 1 for silly_rename'd files. ++ * So we must for our i_nlink to 0 here to trick iput() into calling our clear_inode. ++ */ ++ if (atomic_read(&inode->i_count) == 1) ++ inode->i_nlink = 0; ++} ++ ++ ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++/* ++ * we now define delete_inode, because there are two VFS paths that may ++ * destroy an inode: one of them calls clear inode before doing everything ++ * else that's needed, and the other is fine. This way we truncate the inode ++ * size (and its pages) and then clear our own inode, which will do an iput ++ * on our and the lower inode. ++ */ ++STATIC void ++mini_fo_delete_inode(inode_t *inode) ++{ ++ print_entry_location(); ++ ++ fist_checkinode(inode, "mini_fo_delete_inode IN"); ++ inode->i_size = 0; /* every f/s seems to do that */ ++ clear_inode(inode); ++ ++ print_exit_location(); ++} ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ ++ ++/* final actions when unmounting a file system */ ++STATIC void ++mini_fo_put_super(super_block_t *sb) ++{ ++ if (stopd(sb)) { ++ mntput(stopd(sb)->hidden_mnt); ++ mntput(stopd(sb)->hidden_mnt2); ++ ++ /* mk: no! dput(stopd(sb)->base_dir_dentry); ++ dput(stopd(sb)->storage_dir_dentry); */ ++ ++ kfree(stopd(sb)); ++ __stopd(sb) = NULL; ++ } ++} ++ ++ ++#ifdef NOT_NEEDED ++/* ++ * This is called in do_umount before put_super. ++ * The superblock lock is not held yet. ++ * We probably do not need to define this or call write_super ++ * on the hidden_sb, because sync_supers() will get to hidden_sb ++ * sooner or later. But it is also called from file_fsync()... ++ */ ++STATIC void ++mini_fo_write_super(super_block_t *sb) ++{ ++ return; ++} ++#endif /* NOT_NEEDED */ ++ ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_statfs(struct dentry *d, struct kstatfs *buf) ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_statfs(super_block_t *sb, struct kstatfs *buf) ++#else ++mini_fo_statfs(super_block_t *sb, struct statfs *buf) ++#endif ++{ ++ int err = 0; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ struct dentry *hidden_d; ++ ++ hidden_d = dtohd(d); ++ err = vfs_statfs(hidden_d, buf); ++#else ++ super_block_t *hidden_sb; ++ ++ hidden_sb = stohs(sb); ++ err = vfs_statfs(hidden_sb, buf); ++#endif ++ ++ return err; ++} ++ ++ ++/* ++ * XXX: not implemented. This is not allowed yet. ++ * Should we call this on the hidden_sb? Probably not. ++ */ ++STATIC int ++mini_fo_remount_fs(super_block_t *sb, int *flags, char *data) ++{ ++ //printk(KERN_CRIT "mini_fo_remount_fs: WARNING, this function is umimplemented.\n"); ++ return -ENOSYS; ++} ++ ++ ++/* ++ * Called by iput() when the inode reference count reached zero ++ * and the inode is not hashed anywhere. Used to clear anything ++ * that needs to be, before the inode is completely destroyed and put ++ * on the inode free list. ++ */ ++STATIC void ++mini_fo_clear_inode(inode_t *inode) ++{ ++ /* ++ * Decrement a reference to a hidden_inode, which was incremented ++ * by our read_inode when it was created initially. ++ */ ++ ++ /* release the wol_list */ ++ if(S_ISDIR(inode->i_mode)) { ++ __meta_put_lists(inode); ++ } ++ ++ /* mk: fan out fun */ ++ if(itohi(inode)) ++ iput(itohi(inode)); ++ if(itohi2(inode)) ++ iput(itohi2(inode)); ++ ++ // XXX: why this assertion fails? ++ // because it doesn't like us ++ // ASSERT((inode->i_state & I_DIRTY) == 0); ++ kfree(itopd(inode)); ++ __itopd(inode) = NULL; ++} ++ ++ ++/* ++ * Called in do_umount() if the MNT_FORCE flag was used and this ++ * function is defined. See comment in linux/fs/super.c:do_umount(). ++ * Used only in nfs, to kill any pending RPC tasks, so that subsequent ++ * code can actually succeed and won't leave tasks that need handling. ++ * ++ * PS. I wonder if this is somehow useful to undo damage that was ++ * left in the kernel after a user level file server (such as amd) ++ * dies. ++ */ ++STATIC void ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_umount_begin(struct vfsmount *mnt, int flags) ++{ ++ struct vfsmount *hidden_mnt; ++ ++ hidden_mnt = stopd(mnt->mnt_sb)->hidden_mnt; ++ ++ if (hidden_mnt->mnt_sb->s_op->umount_begin) ++ hidden_mnt->mnt_sb->s_op->umount_begin(hidden_mnt, flags); ++ ++} ++#else ++mini_fo_umount_begin(super_block_t *sb) ++{ ++ super_block_t *hidden_sb; ++ ++ hidden_sb = stohs(sb); ++ ++ if (hidden_sb->s_op->umount_begin) ++ hidden_sb->s_op->umount_begin(hidden_sb); ++ ++} ++#endif ++ ++ ++struct super_operations mini_fo_sops = ++{ ++ read_inode: mini_fo_read_inode, ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++ write_inode: mini_fo_write_inode, ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ put_inode: mini_fo_put_inode, ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++ delete_inode: mini_fo_delete_inode, ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ put_super: mini_fo_put_super, ++ statfs: mini_fo_statfs, ++ remount_fs: mini_fo_remount_fs, ++ clear_inode: mini_fo_clear_inode, ++ umount_begin: mini_fo_umount_begin, ++}; diff --git a/target/linux/generic-2.6/patches-2.6.21/210-d80211_compat.patch b/target/linux/generic-2.6/patches-2.6.21/210-d80211_compat.patch new file mode 100644 index 0000000000..9d57f1fa7a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/210-d80211_compat.patch @@ -0,0 +1,13 @@ +Index: linux-2.6.21.7/include/linux/netdevice.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/netdevice.h ++++ linux-2.6.21.7/include/linux/netdevice.h +@@ -532,6 +532,8 @@ struct net_device + struct device dev; + /* space for optional statistics and wireless sysfs groups */ + struct attribute_group *sysfs_groups[3]; ++ ++ void *ieee80211_ptr; + }; + #define to_net_dev(d) container_of(d, struct net_device, dev) + diff --git a/target/linux/generic-2.6/patches-2.6.21/211-no_block2mtd_readahead.patch b/target/linux/generic-2.6/patches-2.6.21/211-no_block2mtd_readahead.patch new file mode 100644 index 0000000000..3cad44ed3a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/211-no_block2mtd_readahead.patch @@ -0,0 +1,13 @@ +Index: linux-2.6.21.7/drivers/mtd/devices/block2mtd.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/mtd/devices/block2mtd.c ++++ linux-2.6.21.7/drivers/mtd/devices/block2mtd.c +@@ -41,7 +41,7 @@ struct block2mtd_dev { + static LIST_HEAD(blkmtd_device_list); + + +-#define PAGE_READAHEAD 64 ++#define PAGE_READAHEAD 0 + static void cache_readahead(struct address_space *mapping, int index) + { + filler_t *filler = (filler_t*)mapping->a_ops->readpage; diff --git a/target/linux/generic-2.6/patches-2.6.21/212-block2mtd_erase_scan.patch b/target/linux/generic-2.6/patches-2.6.21/212-block2mtd_erase_scan.patch new file mode 100644 index 0000000000..6c68f99fe0 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/212-block2mtd_erase_scan.patch @@ -0,0 +1,13 @@ +Index: linux-2.6.21.7/drivers/mtd/devices/block2mtd.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/mtd/devices/block2mtd.c ++++ linux-2.6.21.7/drivers/mtd/devices/block2mtd.c +@@ -112,7 +112,7 @@ static int _block2mtd_erase(struct block + if (IS_ERR(page)) + return PTR_ERR(page); + +- max = (u_long*)page_address(page) + PAGE_SIZE; ++ max = (u_long*) ((u8 *) page_address(page) + PAGE_SIZE); + for (p=(u_long*)page_address(page); pf_version = inode->i_version; + } + +- list_for_each(i, &obj->variant.directoryVariant.children) { ++ list_for_each(i, (struct list_head *)&obj->variant.directoryVariant.children) { + curoffs++; + if (curoffs >= offset) { + l = list_entry(i, yaffs_Object, siblings); +@@ -1273,7 +1275,7 @@ + + if (target && + target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && +- !list_empty(&target->variant.directoryVariant.children)) { ++ !list_empty((struct list_head *)&target->variant.directoryVariant.children)) { + + T(YAFFS_TRACE_OS, (KERN_DEBUG "target is non-empty dir\n")); + +@@ -1529,7 +1531,7 @@ + yaffs_GrossUnlock(dev); + + /* we assume this is protected by lock_kernel() in mount/umount */ +- list_del(&dev->devList); ++ list_del((struct list_head *)&dev->devList); + + if(dev->spareBuffer){ + YFREE(dev->spareBuffer); +@@ -1864,7 +1866,7 @@ + dev->skipCheckpointWrite = options.skip_checkpoint_write; + + /* we assume this is protected by lock_kernel() in mount/umount */ +- list_add_tail(&dev->devList, &yaffs_dev_list); ++ list_add_tail((struct list_head *)&dev->devList, &yaffs_dev_list); + + init_MUTEX(&dev->grossLock); + +--- a/fs/yaffs2/yaffs_mtdif1.c ++++ b/fs/yaffs2/yaffs_mtdif1.c +@@ -323,7 +323,7 @@ + * Always returns YAFFS_OK. + */ + int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, +- yaffs_BlockState * pState, int *pSequenceNumber) ++ yaffs_BlockState * pState, __u32 *pSequenceNumber) + { + struct mtd_info * mtd = dev->genericDevice; + int chunkNo = blockNo * dev->nChunksPerBlock; +--- a/fs/yaffs2/yaffs_mtdif1.h ++++ b/fs/yaffs2/yaffs_mtdif1.h +@@ -23,6 +23,6 @@ + int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); + + int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, +- yaffs_BlockState * state, int *sequenceNumber); ++ yaffs_BlockState * state, __u32 *sequenceNumber); + + #endif +--- a/fs/yaffs2/yaffs_mtdif2.c ++++ b/fs/yaffs2/yaffs_mtdif2.c +@@ -204,7 +204,7 @@ + } + + int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, +- yaffs_BlockState * state, int *sequenceNumber) ++ yaffs_BlockState * state, __u32 *sequenceNumber) + { + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); + int retval; +--- a/fs/yaffs2/yaffs_mtdif2.h ++++ b/fs/yaffs2/yaffs_mtdif2.h +@@ -24,6 +24,6 @@ + __u8 * data, yaffs_ExtendedTags * tags); + int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); + int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, +- yaffs_BlockState * state, int *sequenceNumber); ++ yaffs_BlockState * state, __u32 *sequenceNumber); + + #endif diff --git a/target/linux/generic-2.6/patches-2.6.21/900-headers_type_and_time.patch b/target/linux/generic-2.6/patches-2.6.21/900-headers_type_and_time.patch new file mode 100644 index 0000000000..3029ce28ec --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/900-headers_type_and_time.patch @@ -0,0 +1,50 @@ +Index: linux-2.6.21.7/include/linux/time.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/time.h ++++ linux-2.6.21.7/include/linux/time.h +@@ -1,6 +1,10 @@ + #ifndef _LINUX_TIME_H + #define _LINUX_TIME_H + ++#ifndef __KERNEL__ ++#include ++#else ++ + #include + + #ifdef __KERNEL__ +@@ -224,4 +228,6 @@ struct itimerval { + */ + #define TIMER_ABSTIME 0x01 + ++#endif /* __KERNEL__ DEBIAN */ ++ + #endif +Index: linux-2.6.21.7/include/linux/types.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/types.h ++++ linux-2.6.21.7/include/linux/types.h +@@ -1,6 +1,14 @@ + #ifndef _LINUX_TYPES_H + #define _LINUX_TYPES_H + ++/* Debian: Use userland types instead. */ ++#ifndef __KERNEL__ ++# include ++/* For other kernel headers. */ ++# include ++# include ++#else ++ + #ifdef __KERNEL__ + + #define BITS_TO_LONGS(bits) \ +@@ -162,6 +170,8 @@ typedef unsigned long blkcnt_t; + + #endif /* __KERNEL_STRICT_NAMES */ + ++#endif /* __KERNEL__ DEBIAN */ ++ + /* + * Below are truly Linux-specific types that should never collide with + * any application/library that wants linux/types.h. diff --git a/target/linux/generic-2.6/patches-2.6.21/902-darwin_scripts_include.patch b/target/linux/generic-2.6/patches-2.6.21/902-darwin_scripts_include.patch new file mode 100644 index 0000000000..0e981def57 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/902-darwin_scripts_include.patch @@ -0,0 +1,154 @@ +Index: linux-2.6.21.7/scripts/genksyms/parse.c_shipped +=================================================================== +--- linux-2.6.21.7.orig/scripts/genksyms/parse.c_shipped ++++ linux-2.6.21.7/scripts/genksyms/parse.c_shipped +@@ -144,7 +144,9 @@ + + + #include ++#ifndef __APPLE__ + #include ++#endif + #include "genksyms.h" + + static int is_typedef; +Index: linux-2.6.21.7/scripts/genksyms/parse.y +=================================================================== +--- linux-2.6.21.7.orig/scripts/genksyms/parse.y ++++ linux-2.6.21.7/scripts/genksyms/parse.y +@@ -24,7 +24,9 @@ + %{ + + #include ++#ifndef __APPLE__ + #include ++#endif + #include "genksyms.h" + + static int is_typedef; +Index: linux-2.6.21.7/scripts/kallsyms.c +=================================================================== +--- linux-2.6.21.7.orig/scripts/kallsyms.c ++++ linux-2.6.21.7/scripts/kallsyms.c +@@ -30,6 +30,35 @@ + #include + #include + #include ++#ifdef __APPLE__ ++/* Darwin has no memmem implementation, this one is ripped of the uClibc-0.9.28 source */ ++void *memmem (const void *haystack, size_t haystack_len, ++ const void *needle, size_t needle_len) ++{ ++ const char *begin; ++ const char *const last_possible ++ = (const char *) haystack + haystack_len - needle_len; ++ ++ if (needle_len == 0) ++ /* The first occurrence of the empty string is deemed to occur at ++ the beginning of the string. */ ++ return (void *) haystack; ++ ++ /* Sanity check, otherwise the loop might search through the whole ++ memory. */ ++ if (__builtin_expect (haystack_len < needle_len, 0)) ++ return NULL; ++ ++ for (begin = (const char *) haystack; begin <= last_possible; ++begin) ++ if (begin[0] == ((const char *) needle)[0] && ++ !memcmp ((const void *) &begin[1], ++ (const void *) ((const char *) needle + 1), ++ needle_len - 1)) ++ return (void *) begin; ++ ++ return NULL; ++} ++#endif + + #define KSYM_NAME_LEN 127 + +Index: linux-2.6.21.7/scripts/kconfig/Makefile +=================================================================== +--- linux-2.6.21.7.orig/scripts/kconfig/Makefile ++++ linux-2.6.21.7/scripts/kconfig/Makefile +@@ -87,6 +87,9 @@ check-lxdialog := $(srctree)/$(src)/lxd + # we really need to do so. (Do not call gcc as part of make mrproper) + HOST_EXTRACFLAGS = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) + HOST_LOADLIBES = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC)) ++ifeq ($(shell uname -s),Darwin) ++HOST_LOADLIBES += -lncurses ++endif + + HOST_EXTRACFLAGS += -DLOCALE + +Index: linux-2.6.21.7/scripts/mod/file2alias.c +=================================================================== +--- linux-2.6.21.7.orig/scripts/mod/file2alias.c ++++ linux-2.6.21.7/scripts/mod/file2alias.c +@@ -37,7 +37,21 @@ typedef unsigned char __u8; + * even potentially has different endianness and word sizes, since + * we handle those differences explicitly below */ + #include "../../include/linux/mod_devicetable.h" ++#ifndef __APPLE__ + #include "../../include/linux/input.h" ++#else ++#define EV_MAX 0x1f ++#define KEY_MUTE 113 ++#define KEY_MIN_INTERESTING KEY_MUTE ++#define KEY_MAX 0x1ff ++#define REL_MAX 0x0f ++#define ABS_MAX 0x3f ++#define MSC_MAX 0x07 ++#define LED_MAX 0x0f ++#define SND_MAX 0x07 ++#define FF_MAX 0x7f ++#define SW_MAX 0x0f ++#endif + + #define ADD(str, sep, cond, field) \ + do { \ +Index: linux-2.6.21.7/scripts/mod/mk_elfconfig.c +=================================================================== +--- linux-2.6.21.7.orig/scripts/mod/mk_elfconfig.c ++++ linux-2.6.21.7/scripts/mod/mk_elfconfig.c +@@ -1,7 +1,11 @@ + #include + #include + #include ++#ifndef __APPLE__ + #include ++#else ++#include "../../../../../tools/sstrip/include/elf.h" ++#endif + + int + main(int argc, char **argv) +Index: linux-2.6.21.7/scripts/mod/modpost.h +=================================================================== +--- linux-2.6.21.7.orig/scripts/mod/modpost.h ++++ linux-2.6.21.7/scripts/mod/modpost.h +@@ -7,7 +7,11 @@ + #include + #include + #include ++#ifndef __APPLE__ + #include ++#else ++#include "../../../../../tools/sstrip/include/elf.h" ++#endif + + #include "elfconfig.h" + +Index: linux-2.6.21.7/scripts/mod/sumversion.c +=================================================================== +--- linux-2.6.21.7.orig/scripts/mod/sumversion.c ++++ linux-2.6.21.7/scripts/mod/sumversion.c +@@ -8,6 +8,9 @@ + #include + #include + #include "modpost.h" ++#ifdef __APPLE__ ++#include ++#endif + + /* + * Stolen form Cryptographic API. diff --git a/target/linux/generic-2.6/patches-2.6.21/903-hostap_txpower.patch b/target/linux/generic-2.6/patches-2.6.21/903-hostap_txpower.patch new file mode 100644 index 0000000000..474fbeea0b --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/903-hostap_txpower.patch @@ -0,0 +1,166 @@ +Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ap.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_ap.c ++++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ap.c +@@ -2346,13 +2346,13 @@ int prism2_ap_get_sta_qual(local_info_t + addr[count].sa_family = ARPHRD_ETHER; + memcpy(addr[count].sa_data, sta->addr, ETH_ALEN); + if (sta->last_rx_silence == 0) +- qual[count].qual = sta->last_rx_signal < 27 ? +- 0 : (sta->last_rx_signal - 27) * 92 / 127; ++ qual[count].qual = (sta->last_rx_signal - 156) == 0 ? ++ 0 : (sta->last_rx_signal - 156) * 92 / 64; + else +- qual[count].qual = sta->last_rx_signal - +- sta->last_rx_silence - 35; +- qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); +- qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); ++ qual[count].qual = (sta->last_rx_signal - ++ sta->last_rx_silence) * 92 / 64; ++ qual[count].level = sta->last_rx_signal; ++ qual[count].noise = sta->last_rx_silence; + qual[count].updated = sta->last_rx_updated; + + sta->last_rx_updated = IW_QUAL_DBM; +@@ -2417,13 +2417,13 @@ int prism2_ap_translate_scan(struct net_ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + if (sta->last_rx_silence == 0) +- iwe.u.qual.qual = sta->last_rx_signal < 27 ? +- 0 : (sta->last_rx_signal - 27) * 92 / 127; ++ iwe.u.qual.qual = (sta->last_rx_signal -156) == 0 ? ++ 0 : (sta->last_rx_signal - 156) * 92 / 64; + else +- iwe.u.qual.qual = sta->last_rx_signal - +- sta->last_rx_silence - 35; +- iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); +- iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); ++ iwe.u.qual.qual = (sta->last_rx_signal - ++ sta->last_rx_silence) * 92 / 64; ++ iwe.u.qual.level = sta->last_rx_signal; ++ iwe.u.qual.noise = sta->last_rx_silence; + iwe.u.qual.updated = sta->last_rx_updated; + iwe.len = IW_EV_QUAL_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, +Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_config.h +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_config.h ++++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_config.h +@@ -47,4 +47,9 @@ + */ + /* #define PRISM2_NO_STATION_MODES */ + ++/* Enable TX power Setting functions ++ * (min att = -128 , max att = 127) ++ */ ++#define RAW_TXPOWER_SETTING ++ + #endif /* HOSTAP_CONFIG_H */ +Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap.h +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap.h ++++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap.h +@@ -90,6 +90,7 @@ extern const struct iw_handler_def hosta + extern const struct ethtool_ops prism2_ethtool_ops; + + int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); ++int hostap_restore_power(struct net_device *dev); + + + #endif /* HOSTAP_H */ +Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_hw.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_hw.c ++++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_hw.c +@@ -933,6 +933,7 @@ static int hfa384x_set_rid(struct net_de + prism2_hw_reset(dev); + } + ++ hostap_restore_power(dev); + return res; + } + +Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_info.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_info.c ++++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_info.c +@@ -428,6 +428,11 @@ static void handle_info_queue_linkstatus + } + + /* Get BSSID if we have a valid AP address */ ++ ++ if ( val == HFA384X_LINKSTATUS_CONNECTED || ++ val == HFA384X_LINKSTATUS_DISCONNECTED ) ++ hostap_restore_power(local->dev); ++ + if (connected) { + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); +Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ioctl.c +=================================================================== +--- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_ioctl.c ++++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ioctl.c +@@ -1504,23 +1504,20 @@ static int prism2_txpower_hfa386x_to_dBm + val = 255; + + tmp = val; +- tmp >>= 2; + +- return -12 - tmp; ++ return tmp; + } + + static u16 prism2_txpower_dBm_to_hfa386x(int val) + { + signed char tmp; + +- if (val > 20) +- return 128; +- else if (val < -43) ++ if (val > 127) + return 127; ++ else if (val < -128) ++ return 128; + + tmp = val; +- tmp = -12 - tmp; +- tmp <<= 2; + + return (unsigned char) tmp; + } +@@ -4080,3 +4077,35 @@ int hostap_ioctl(struct net_device *dev, + + return ret; + } ++ ++/* BUG FIX: Restore power setting value when lost due to F/W bug */ ++ ++int hostap_restore_power(struct net_device *dev) ++{ ++ struct hostap_interface *iface = dev->priv; ++ local_info_t *local = iface->local; ++ ++ u16 val; ++ int ret = 0; ++ ++ if (local->txpower_type == PRISM2_TXPOWER_OFF) { ++ val = 0xff; /* use all standby and sleep modes */ ++ ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, ++ HFA386X_CR_A_D_TEST_MODES2, ++ &val, NULL); ++ } ++ ++#ifdef RAW_TXPOWER_SETTING ++ if (local->txpower_type == PRISM2_TXPOWER_FIXED) { ++ val = HFA384X_TEST_CFG_BIT_ALC; ++ local->func->cmd(dev, HFA384X_CMDCODE_TEST | ++ (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL); ++ val = prism2_txpower_dBm_to_hfa386x(local->txpower); ++ ret = (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, ++ HFA386X_CR_MANUAL_TX_POWER, &val, NULL)); ++ } ++#endif /* RAW_TXPOWER_SETTING */ ++ return (ret ? -EOPNOTSUPP : 0); ++} ++ ++EXPORT_SYMBOL(hostap_restore_power); diff --git a/target/linux/generic-2.6/patches-2.6.21/903-stddef_include.patch b/target/linux/generic-2.6/patches-2.6.21/903-stddef_include.patch new file mode 100644 index 0000000000..3b40ced971 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/903-stddef_include.patch @@ -0,0 +1,19 @@ +Index: linux-2.6.21.7/include/linux/stddef.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/stddef.h ++++ linux-2.6.21.7/include/linux/stddef.h +@@ -16,6 +16,7 @@ enum { + false = 0, + true = 1 + }; ++#endif /* __KERNEL__ */ + + #undef offsetof + #ifdef __compiler_offsetof +@@ -23,6 +24,5 @@ enum { + #else + #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + #endif +-#endif /* __KERNEL__ */ + + #endif diff --git a/target/linux/generic-2.6/patches-2.6.21/904-ls_time_locale.patch b/target/linux/generic-2.6/patches-2.6.21/904-ls_time_locale.patch new file mode 100644 index 0000000000..c34e180d13 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/904-ls_time_locale.patch @@ -0,0 +1,22 @@ +Index: linux-2.6.21.7/scripts/gen_initramfs_list.sh +=================================================================== +--- linux-2.6.21.7.orig/scripts/gen_initramfs_list.sh ++++ linux-2.6.21.7/scripts/gen_initramfs_list.sh +@@ -125,7 +125,7 @@ parse() { + str="${ftype} ${name} ${location} ${str}" + ;; + "nod") +- local dev=`LC_ALL=C ls -l "${location}"` ++ local dev=`LC_ALL=C ls -l --time-style=locale "${location}"` + local maj=`field 5 ${dev}` + local min=`field 6 ${dev}` + maj=${maj%,} +@@ -135,7 +135,7 @@ parse() { + str="${ftype} ${name} ${str} ${dev} ${maj} ${min}" + ;; + "slink") +- local target=`field 11 $(LC_ALL=C ls -l "${location}")` ++ local target=`field 11 $(LC_ALL=C ls -l --time-style=locale "${location}")` + str="${ftype} ${name} ${target} ${str}" + ;; + *) diff --git a/target/linux/generic-2.6/patches-2.6.21/999-backport_ip_hdr_and_friends.patch b/target/linux/generic-2.6/patches-2.6.21/999-backport_ip_hdr_and_friends.patch new file mode 100644 index 0000000000..bb8f4f3342 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.21/999-backport_ip_hdr_and_friends.patch @@ -0,0 +1,58 @@ +Index: linux-2.6.21.7/include/linux/ip.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/ip.h ++++ linux-2.6.21.7/include/linux/ip.h +@@ -104,6 +104,16 @@ struct iphdr { + /*The options start here. */ + }; + ++#ifdef __KERNEL__ ++#include ++ ++static inline struct iphdr *ip_hdr(const struct sk_buff *skb) ++{ ++ return (struct iphdr *)skb_network_header(skb); ++} ++ ++#endif ++ + struct ip_auth_hdr { + __u8 nexthdr; + __u8 hdrlen; /* This one is measured in 32 bit units! */ +Index: linux-2.6.21.7/include/linux/skbuff.h +=================================================================== +--- linux-2.6.21.7.orig/include/linux/skbuff.h ++++ linux-2.6.21.7/include/linux/skbuff.h +@@ -966,6 +966,16 @@ static inline void skb_reserve(struct sk + skb->tail += len; + } + ++static inline unsigned char *skb_network_header(const struct sk_buff *skb) ++{ ++ return skb->nh.raw; ++} ++ ++static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) ++{ ++ return skb->tail; ++} ++ + /* + * CPUs often take a performance hit when accessing unaligned memory + * locations. The actual performance hit varies, it can be small if the +Index: linux-2.6.21.7/include/net/ip.h +=================================================================== +--- linux-2.6.21.7.orig/include/net/ip.h ++++ linux-2.6.21.7/include/net/ip.h +@@ -43,6 +43,11 @@ struct inet_skb_parm + #define IPSKB_REROUTED 16 + }; + ++static inline unsigned int ip_hdrlen(const struct sk_buff *skb) ++{ ++ return ip_hdr(skb)->ihl * 4; ++} ++ + struct ipcm_cookie + { + __be32 addr; diff --git a/target/linux/generic-2.6/patches/001-squashfs.patch b/target/linux/generic-2.6/patches/001-squashfs.patch deleted file mode 100644 index e64db466cf..0000000000 --- a/target/linux/generic-2.6/patches/001-squashfs.patch +++ /dev/null @@ -1,4180 +0,0 @@ -Index: linux-2.6.21.7/fs/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/fs/Kconfig -+++ linux-2.6.21.7/fs/Kconfig -@@ -1371,6 +1371,71 @@ config CRAMFS - - If unsure, say N. - -+config SQUASHFS -+ tristate "SquashFS 3.0 - Squashed file system support" -+ select ZLIB_INFLATE -+ help -+ Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File -+ System). Squashfs is a highly compressed read-only filesystem for Linux. -+ It uses zlib compression to compress both files, inodes and directories. -+ Inodes in the system are very small and all blocks are packed to minimise -+ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. -+ SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full -+ uid/gid information, hard links and timestamps. -+ -+ Squashfs is intended for general read-only filesystem use, for archival -+ use (i.e. in cases where a .tar.gz file may be used), and in embedded -+ systems where low overhead is needed. Further information and filesystem tools -+ are available from http://squashfs.sourceforge.net. -+ -+ If you want to compile this as a module ( = code which can be -+ inserted in and removed from the running kernel whenever you want), -+ say M here and read . The module -+ will be called squashfs. Note that the root file system (the one -+ containing the directory /) cannot be compiled as a module. -+ -+ If unsure, say N. -+ -+config SQUASHFS_EMBEDDED -+ -+ bool "Additional options for memory-constrained systems" -+ depends on SQUASHFS -+ default n -+ help -+ Saying Y here allows you to specify cache sizes and how Squashfs -+ allocates memory. This is only intended for memory constrained -+ systems. -+ -+ If unsure, say N. -+ -+config SQUASHFS_FRAGMENT_CACHE_SIZE -+ int "Number of fragments cached" if SQUASHFS_EMBEDDED -+ depends on SQUASHFS -+ default "3" -+ help -+ By default SquashFS caches the last 3 fragments read from -+ the filesystem. Increasing this amount may mean SquashFS -+ has to re-read fragments less often from disk, at the expense -+ of extra system memory. Decreasing this amount will mean -+ SquashFS uses less memory at the expense of extra reads from disk. -+ -+ Note there must be at least one cached fragment. Anything -+ much more than three will probably not make much difference. -+ -+config SQUASHFS_VMALLOC -+ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED -+ depends on SQUASHFS -+ default n -+ help -+ By default SquashFS uses kmalloc to obtain fragment cache memory. -+ Kmalloc memory is the standard kernel allocator, but it can fail -+ on memory constrained systems. Because of the way Vmalloc works, -+ Vmalloc can succeed when kmalloc fails. Specifying this option -+ will make SquashFS always use Vmalloc to allocate the -+ fragment cache memory. -+ -+ If unsure, say N. -+ - config VXFS_FS - tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" - depends on BLOCK -Index: linux-2.6.21.7/fs/Makefile -=================================================================== ---- linux-2.6.21.7.orig/fs/Makefile -+++ linux-2.6.21.7/fs/Makefile -@@ -68,6 +68,7 @@ obj-$(CONFIG_JBD) += jbd/ - obj-$(CONFIG_JBD2) += jbd2/ - obj-$(CONFIG_EXT2_FS) += ext2/ - obj-$(CONFIG_CRAMFS) += cramfs/ -+obj-$(CONFIG_SQUASHFS) += squashfs/ - obj-$(CONFIG_RAMFS) += ramfs/ - obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ - obj-$(CONFIG_CODA_FS) += coda/ -Index: linux-2.6.21.7/fs/squashfs/inode.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/squashfs/inode.c -@@ -0,0 +1,2124 @@ -+/* -+ * Squashfs - a compressed read only filesystem for Linux -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * inode.c -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "squashfs.h" -+ -+static void squashfs_put_super(struct super_block *); -+static int squashfs_statfs(struct dentry *, struct kstatfs *); -+static int squashfs_symlink_readpage(struct file *file, struct page *page); -+static int squashfs_readpage(struct file *file, struct page *page); -+static int squashfs_readpage4K(struct file *file, struct page *page); -+static int squashfs_readdir(struct file *, void *, filldir_t); -+static struct inode *squashfs_alloc_inode(struct super_block *sb); -+static void squashfs_destroy_inode(struct inode *inode); -+static int init_inodecache(void); -+static void destroy_inodecache(void); -+static struct dentry *squashfs_lookup(struct inode *, struct dentry *, -+ struct nameidata *); -+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode); -+static long long read_blocklist(struct inode *inode, int index, -+ int readahead_blks, char *block_list, -+ unsigned short **block_p, unsigned int *bsize); -+static int squashfs_get_sb(struct file_system_type *, int, -+ const char *, void *, struct vfsmount *); -+ -+ -+static z_stream stream; -+ -+static struct file_system_type squashfs_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "squashfs", -+ .get_sb = squashfs_get_sb, -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV -+}; -+ -+static unsigned char squashfs_filetype_table[] = { -+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK -+}; -+ -+static struct super_operations squashfs_ops = { -+ .alloc_inode = squashfs_alloc_inode, -+ .destroy_inode = squashfs_destroy_inode, -+ .statfs = squashfs_statfs, -+ .put_super = squashfs_put_super, -+}; -+ -+SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { -+ .readpage = squashfs_symlink_readpage -+}; -+ -+SQSH_EXTERN struct address_space_operations squashfs_aops = { -+ .readpage = squashfs_readpage -+}; -+ -+SQSH_EXTERN struct address_space_operations squashfs_aops_4K = { -+ .readpage = squashfs_readpage4K -+}; -+ -+static struct file_operations squashfs_dir_ops = { -+ .read = generic_read_dir, -+ .readdir = squashfs_readdir -+}; -+ -+SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { -+ .lookup = squashfs_lookup -+}; -+ -+ -+static struct buffer_head *get_block_length(struct super_block *s, -+ int *cur_index, int *offset, int *c_byte) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ unsigned short temp; -+ struct buffer_head *bh; -+ -+ if (!(bh = sb_bread(s, *cur_index))) -+ goto out; -+ -+ if (msblk->devblksize - *offset == 1) { -+ if (msblk->swap) -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ else -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ brelse(bh); -+ if (!(bh = sb_bread(s, ++(*cur_index)))) -+ goto out; -+ if (msblk->swap) -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ bh->b_data); -+ else -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ bh->b_data); -+ *c_byte = temp; -+ *offset = 1; -+ } else { -+ if (msblk->swap) { -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ (bh->b_data + *offset + 1)); -+ } else { -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ (bh->b_data + *offset + 1)); -+ } -+ *c_byte = temp; -+ *offset += 2; -+ } -+ -+ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { -+ if (*offset == msblk->devblksize) { -+ brelse(bh); -+ if (!(bh = sb_bread(s, ++(*cur_index)))) -+ goto out; -+ *offset = 0; -+ } -+ if (*((unsigned char *) (bh->b_data + *offset)) != -+ SQUASHFS_MARKER_BYTE) { -+ ERROR("Metadata block marker corrupt @ %x\n", -+ *cur_index); -+ brelse(bh); -+ goto out; -+ } -+ (*offset)++; -+ } -+ return bh; -+ -+out: -+ return NULL; -+} -+ -+ -+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, -+ long long index, unsigned int length, -+ long long *next_index) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> -+ msblk->devblksize_log2) + 2]; -+ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); -+ unsigned int cur_index = index >> msblk->devblksize_log2; -+ int bytes, avail_bytes, b = 0, k; -+ char *c_buffer; -+ unsigned int compressed; -+ unsigned int c_byte = length; -+ -+ if (c_byte) { -+ bytes = msblk->devblksize - offset; -+ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); -+ c_buffer = compressed ? msblk->read_data : buffer; -+ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); -+ -+ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed -+ ? "" : "un", (unsigned int) c_byte); -+ -+ if (!(bh[0] = sb_getblk(s, cur_index))) -+ goto block_release; -+ -+ for (b = 1; bytes < c_byte; b++) { -+ if (!(bh[b] = sb_getblk(s, ++cur_index))) -+ goto block_release; -+ bytes += msblk->devblksize; -+ } -+ ll_rw_block(READ, b, bh); -+ } else { -+ if (!(bh[0] = get_block_length(s, &cur_index, &offset, -+ &c_byte))) -+ goto read_failure; -+ -+ bytes = msblk->devblksize - offset; -+ compressed = SQUASHFS_COMPRESSED(c_byte); -+ c_buffer = compressed ? msblk->read_data : buffer; -+ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); -+ -+ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed -+ ? "" : "un", (unsigned int) c_byte); -+ -+ for (b = 1; bytes < c_byte; b++) { -+ if (!(bh[b] = sb_getblk(s, ++cur_index))) -+ goto block_release; -+ bytes += msblk->devblksize; -+ } -+ ll_rw_block(READ, b - 1, bh + 1); -+ } -+ -+ if (compressed) -+ down(&msblk->read_data_mutex); -+ -+ for (bytes = 0, k = 0; k < b; k++) { -+ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? -+ msblk->devblksize - offset : -+ c_byte - bytes; -+ wait_on_buffer(bh[k]); -+ if (!buffer_uptodate(bh[k])) -+ goto block_release; -+ memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); -+ bytes += avail_bytes; -+ offset = 0; -+ brelse(bh[k]); -+ } -+ -+ /* -+ * uncompress block -+ */ -+ if (compressed) { -+ int zlib_err; -+ -+ stream.next_in = c_buffer; -+ stream.avail_in = c_byte; -+ stream.next_out = buffer; -+ stream.avail_out = msblk->read_size; -+ -+ if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || -+ ((zlib_err = zlib_inflate(&stream, Z_FINISH)) -+ != Z_STREAM_END) || ((zlib_err = -+ zlib_inflateEnd(&stream)) != Z_OK)) { -+ ERROR("zlib_fs returned unexpected result 0x%x\n", -+ zlib_err); -+ bytes = 0; -+ } else -+ bytes = stream.total_out; -+ -+ up(&msblk->read_data_mutex); -+ } -+ -+ if (next_index) -+ *next_index = index + c_byte + (length ? 0 : -+ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) -+ ? 3 : 2)); -+ return bytes; -+ -+block_release: -+ while (--b >= 0) -+ brelse(bh[b]); -+ -+read_failure: -+ ERROR("sb_bread failed reading block 0x%x\n", cur_index); -+ return 0; -+} -+ -+ -+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, -+ long long block, unsigned int offset, -+ int length, long long *next_block, -+ unsigned int *next_offset) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ int n, i, bytes, return_length = length; -+ long long next_index; -+ -+ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); -+ -+ while ( 1 ) { -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ if (msblk->block_cache[i].block == block) -+ break; -+ -+ down(&msblk->block_cache_mutex); -+ -+ if (i == SQUASHFS_CACHED_BLKS) { -+ /* read inode header block */ -+ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; -+ n ; n --, i = (i + 1) % -+ SQUASHFS_CACHED_BLKS) -+ if (msblk->block_cache[i].block != -+ SQUASHFS_USED_BLK) -+ break; -+ -+ if (n == 0) { -+ wait_queue_t wait; -+ -+ init_waitqueue_entry(&wait, current); -+ add_wait_queue(&msblk->waitq, &wait); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ up(&msblk->block_cache_mutex); -+ schedule(); -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&msblk->waitq, &wait); -+ continue; -+ } -+ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; -+ -+ if (msblk->block_cache[i].block == -+ SQUASHFS_INVALID_BLK) { -+ if (!(msblk->block_cache[i].data = -+ kmalloc(SQUASHFS_METADATA_SIZE, -+ GFP_KERNEL))) { -+ ERROR("Failed to allocate cache" -+ "block\n"); -+ up(&msblk->block_cache_mutex); -+ goto out; -+ } -+ } -+ -+ msblk->block_cache[i].block = SQUASHFS_USED_BLK; -+ up(&msblk->block_cache_mutex); -+ -+ if (!(msblk->block_cache[i].length = -+ squashfs_read_data(s, -+ msblk->block_cache[i].data, -+ block, 0, &next_index))) { -+ ERROR("Unable to read cache block [%llx:%x]\n", -+ block, offset); -+ goto out; -+ } -+ -+ down(&msblk->block_cache_mutex); -+ wake_up(&msblk->waitq); -+ msblk->block_cache[i].block = block; -+ msblk->block_cache[i].next_index = next_index; -+ TRACE("Read cache block [%llx:%x]\n", block, offset); -+ } -+ -+ if (msblk->block_cache[i].block != block) { -+ up(&msblk->block_cache_mutex); -+ continue; -+ } -+ -+ if ((bytes = msblk->block_cache[i].length - offset) >= length) { -+ if (buffer) -+ memcpy(buffer, msblk->block_cache[i].data + -+ offset, length); -+ if (msblk->block_cache[i].length - offset == length) { -+ *next_block = msblk->block_cache[i].next_index; -+ *next_offset = 0; -+ } else { -+ *next_block = block; -+ *next_offset = offset + length; -+ } -+ up(&msblk->block_cache_mutex); -+ goto finish; -+ } else { -+ if (buffer) { -+ memcpy(buffer, msblk->block_cache[i].data + -+ offset, bytes); -+ buffer += bytes; -+ } -+ block = msblk->block_cache[i].next_index; -+ up(&msblk->block_cache_mutex); -+ length -= bytes; -+ offset = 0; -+ } -+ } -+ -+finish: -+ return return_length; -+out: -+ return 0; -+} -+ -+ -+static int get_fragment_location(struct super_block *s, unsigned int fragment, -+ long long *fragment_start_block, -+ unsigned int *fragment_size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ long long start_block = -+ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; -+ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); -+ struct squashfs_fragment_entry fragment_entry; -+ -+ if (msblk->swap) { -+ struct squashfs_fragment_entry sfragment_entry; -+ -+ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, -+ start_block, offset, -+ sizeof(sfragment_entry), &start_block, -+ &offset)) -+ goto out; -+ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, -+ start_block, offset, -+ sizeof(fragment_entry), &start_block, -+ &offset)) -+ goto out; -+ -+ *fragment_start_block = fragment_entry.start_block; -+ *fragment_size = fragment_entry.size; -+ -+ return 1; -+ -+out: -+ return 0; -+} -+ -+ -+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct -+ squashfs_fragment_cache *fragment) -+{ -+ down(&msblk->fragment_mutex); -+ fragment->locked --; -+ wake_up(&msblk->fragment_wait_queue); -+ up(&msblk->fragment_mutex); -+} -+ -+ -+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block -+ *s, long long start_block, -+ int length) -+{ -+ int i, n; -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ -+ while ( 1 ) { -+ down(&msblk->fragment_mutex); -+ -+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && -+ msblk->fragment[i].block != start_block; i++); -+ -+ if (i == SQUASHFS_CACHED_FRAGMENTS) { -+ for (i = msblk->next_fragment, n = -+ SQUASHFS_CACHED_FRAGMENTS; n && -+ msblk->fragment[i].locked; n--, i = (i + 1) % -+ SQUASHFS_CACHED_FRAGMENTS); -+ -+ if (n == 0) { -+ wait_queue_t wait; -+ -+ init_waitqueue_entry(&wait, current); -+ add_wait_queue(&msblk->fragment_wait_queue, -+ &wait); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ up(&msblk->fragment_mutex); -+ schedule(); -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&msblk->fragment_wait_queue, -+ &wait); -+ continue; -+ } -+ msblk->next_fragment = (msblk->next_fragment + 1) % -+ SQUASHFS_CACHED_FRAGMENTS; -+ -+ if (msblk->fragment[i].data == NULL) -+ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC -+ (SQUASHFS_FILE_MAX_SIZE))) { -+ ERROR("Failed to allocate fragment " -+ "cache block\n"); -+ up(&msblk->fragment_mutex); -+ goto out; -+ } -+ -+ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; -+ msblk->fragment[i].locked = 1; -+ up(&msblk->fragment_mutex); -+ -+ if (!(msblk->fragment[i].length = squashfs_read_data(s, -+ msblk->fragment[i].data, -+ start_block, length, NULL))) { -+ ERROR("Unable to read fragment cache block " -+ "[%llx]\n", start_block); -+ msblk->fragment[i].locked = 0; -+ goto out; -+ } -+ -+ msblk->fragment[i].block = start_block; -+ TRACE("New fragment %d, start block %lld, locked %d\n", -+ i, msblk->fragment[i].block, -+ msblk->fragment[i].locked); -+ break; -+ } -+ -+ msblk->fragment[i].locked++; -+ up(&msblk->fragment_mutex); -+ TRACE("Got fragment %d, start block %lld, locked %d\n", i, -+ msblk->fragment[i].block, -+ msblk->fragment[i].locked); -+ break; -+ } -+ -+ return &msblk->fragment[i]; -+ -+out: -+ return NULL; -+} -+ -+ -+static struct inode *squashfs_new_inode(struct super_block *s, -+ struct squashfs_base_inode_header *inodeb) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct inode *i = new_inode(s); -+ -+ if (i) { -+ i->i_ino = inodeb->inode_number; -+ i->i_mtime.tv_sec = inodeb->mtime; -+ i->i_atime.tv_sec = inodeb->mtime; -+ i->i_ctime.tv_sec = inodeb->mtime; -+ i->i_uid = msblk->uid[inodeb->uid]; -+ i->i_mode = inodeb->mode; -+ i->i_size = 0; -+ if (inodeb->guid == SQUASHFS_GUIDS) -+ i->i_gid = i->i_uid; -+ else -+ i->i_gid = msblk->guid[inodeb->guid]; -+ } -+ -+ return i; -+} -+ -+ -+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode) -+{ -+ struct inode *i; -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long block = SQUASHFS_INODE_BLK(inode) + -+ sblk->inode_table_start; -+ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); -+ long long next_block; -+ unsigned int next_offset; -+ union squashfs_inode_header id, sid; -+ struct squashfs_base_inode_header *inodeb = &id.base, -+ *sinodeb = &sid.base; -+ -+ TRACE("Entered squashfs_iget\n"); -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, -+ offset, sizeof(*sinodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, -+ sizeof(*sinodeb)); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) inodeb, block, -+ offset, sizeof(*inodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ switch(inodeb->inode_type) { -+ case SQUASHFS_FILE_TYPE: { -+ unsigned int frag_size; -+ long long frag_blk; -+ struct squashfs_reg_inode_header *inodep = &id.reg; -+ struct squashfs_reg_inode_header *sinodep = &sid.reg; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = 1; -+ i->i_size = inodep->file_size; -+ i->i_fop = &generic_ro_fops; -+ i->i_mode |= S_IFREG; -+ i->i_blocks = ((i->i_size - 1) >> 9) + 1; -+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; -+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; -+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->u.s1.block_list_start = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; -+ -+ TRACE("File inode %x:%x, start_block %llx, " -+ "block_list_start %llx, offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, next_block, -+ next_offset); -+ break; -+ } -+ case SQUASHFS_LREG_TYPE: { -+ unsigned int frag_size; -+ long long frag_blk; -+ struct squashfs_lreg_inode_header *inodep = &id.lreg; -+ struct squashfs_lreg_inode_header *sinodep = &sid.lreg; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->file_size; -+ i->i_fop = &generic_ro_fops; -+ i->i_mode |= S_IFREG; -+ i->i_blocks = ((i->i_size - 1) >> 9) + 1; -+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; -+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; -+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->u.s1.block_list_start = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; -+ -+ TRACE("File inode %x:%x, start_block %llx, " -+ "block_list_start %llx, offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, next_block, -+ next_offset); -+ break; -+ } -+ case SQUASHFS_DIR_TYPE: { -+ struct squashfs_dir_inode_header *inodep = &id.dir; -+ struct squashfs_dir_inode_header *sinodep = &sid.dir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops; -+ i->i_fop = &squashfs_dir_ops; -+ i->i_mode |= S_IFDIR; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = 0; -+ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; -+ -+ TRACE("Directory inode %x:%x, start_block %x, offset " -+ "%x\n", SQUASHFS_INODE_BLK(inode), -+ offset, inodep->start_block, -+ inodep->offset); -+ break; -+ } -+ case SQUASHFS_LDIR_TYPE: { -+ struct squashfs_ldir_inode_header *inodep = &id.ldir; -+ struct squashfs_ldir_inode_header *sinodep = &sid.ldir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops; -+ i->i_fop = &squashfs_dir_ops; -+ i->i_mode |= S_IFDIR; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; -+ SQUASHFS_I(i)->u.s2.directory_index_offset = -+ next_offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = -+ inodep->i_count; -+ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; -+ -+ TRACE("Long directory inode %x:%x, start_block %x, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, inodep->offset); -+ break; -+ } -+ case SQUASHFS_SYMLINK_TYPE: { -+ struct squashfs_symlink_inode_header *inodep = -+ &id.symlink; -+ struct squashfs_symlink_inode_header *sinodep = -+ &sid.symlink; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->symlink_size; -+ i->i_op = &page_symlink_inode_operations; -+ i->i_data.a_ops = &squashfs_symlink_aops; -+ i->i_mode |= S_IFLNK; -+ SQUASHFS_I(i)->start_block = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ -+ TRACE("Symbolic link inode %x:%x, start_block %llx, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ next_block, next_offset); -+ break; -+ } -+ case SQUASHFS_BLKDEV_TYPE: -+ case SQUASHFS_CHRDEV_TYPE: { -+ struct squashfs_dev_inode_header *inodep = &id.dev; -+ struct squashfs_dev_inode_header *sinodep = &sid.dev; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if ((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_mode |= (inodeb->inode_type == -+ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : -+ S_IFBLK; -+ init_special_inode(i, i->i_mode, -+ old_decode_dev(inodep->rdev)); -+ -+ TRACE("Device inode %x:%x, rdev %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->rdev); -+ break; -+ } -+ case SQUASHFS_FIFO_TYPE: -+ case SQUASHFS_SOCKET_TYPE: { -+ struct squashfs_ipc_inode_header *inodep = &id.ipc; -+ struct squashfs_ipc_inode_header *sinodep = &sid.ipc; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if ((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) -+ ? S_IFIFO : S_IFSOCK; -+ init_special_inode(i, i->i_mode, 0); -+ break; -+ } -+ default: -+ ERROR("Unknown inode type %d in squashfs_iget!\n", -+ inodeb->inode_type); -+ goto failed_read1; -+ } -+ -+ insert_inode_hash(i); -+ return i; -+ -+failed_read: -+ ERROR("Unable to read inode [%llx:%x]\n", block, offset); -+ -+failed_read1: -+ return NULL; -+} -+ -+ -+static int read_fragment_index_table(struct super_block *s) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ /* Allocate fragment index table */ -+ if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES -+ (sblk->fragments), GFP_KERNEL))) { -+ ERROR("Failed to allocate uid/gid table\n"); -+ return 0; -+ } -+ -+ if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && -+ !squashfs_read_data(s, (char *) -+ msblk->fragment_index, -+ sblk->fragment_table_start, -+ SQUASHFS_FRAGMENT_INDEX_BYTES -+ (sblk->fragments) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read fragment index table\n"); -+ return 0; -+ } -+ -+ if (msblk->swap) { -+ int i; -+ long long fragment; -+ -+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); -+ i++) { -+ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), -+ &msblk->fragment_index[i], 1); -+ msblk->fragment_index[i] = fragment; -+ } -+ } -+ -+ return 1; -+} -+ -+ -+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) -+{ -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ msblk->iget = squashfs_iget; -+ msblk->read_blocklist = read_blocklist; -+ msblk->read_fragment_index_table = read_fragment_index_table; -+ -+ if (sblk->s_major == 1) { -+ if (!squashfs_1_0_supported(msblk)) { -+ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " -+ "are unsupported\n"); -+ SERROR("Please recompile with " -+ "Squashfs 1.0 support enabled\n"); -+ return 0; -+ } -+ } else if (sblk->s_major == 2) { -+ if (!squashfs_2_0_supported(msblk)) { -+ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " -+ "are unsupported\n"); -+ SERROR("Please recompile with " -+ "Squashfs 2.0 support enabled\n"); -+ return 0; -+ } -+ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > -+ SQUASHFS_MINOR) { -+ SERROR("Major/Minor mismatch, trying to mount newer %d.%d " -+ "filesystem\n", sblk->s_major, sblk->s_minor); -+ SERROR("Please update your kernel\n"); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+ -+static int squashfs_fill_super(struct super_block *s, void *data, int silent) -+{ -+ struct squashfs_sb_info *msblk; -+ struct squashfs_super_block *sblk; -+ int i; -+ char b[BDEVNAME_SIZE]; -+ struct inode *root; -+ -+ TRACE("Entered squashfs_read_superblock\n"); -+ -+ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), -+ GFP_KERNEL))) { -+ ERROR("Failed to allocate superblock\n"); -+ goto failure; -+ } -+ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); -+ msblk = s->s_fs_info; -+ sblk = &msblk->sblk; -+ -+ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); -+ msblk->devblksize_log2 = ffz(~msblk->devblksize); -+ -+ init_MUTEX(&msblk->read_data_mutex); -+ init_MUTEX(&msblk->read_page_mutex); -+ init_MUTEX(&msblk->block_cache_mutex); -+ init_MUTEX(&msblk->fragment_mutex); -+ init_MUTEX(&msblk->meta_index_mutex); -+ -+ init_waitqueue_head(&msblk->waitq); -+ init_waitqueue_head(&msblk->fragment_wait_queue); -+ -+ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, -+ sizeof(struct squashfs_super_block) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ SERROR("unable to read superblock\n"); -+ goto failed_mount; -+ } -+ -+ /* Check it is a SQUASHFS superblock */ -+ msblk->swap = 0; -+ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { -+ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { -+ struct squashfs_super_block ssblk; -+ -+ WARNING("Mounting a different endian SQUASHFS " -+ "filesystem on %s\n", bdevname(s->s_bdev, b)); -+ -+ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); -+ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); -+ msblk->swap = 1; -+ } else { -+ SERROR("Can't find a SQUASHFS superblock on %s\n", -+ bdevname(s->s_bdev, b)); -+ goto failed_mount; -+ } -+ } -+ -+ /* Check the MAJOR & MINOR versions */ -+ if(!supported_squashfs_filesystem(msblk, silent)) -+ goto failed_mount; -+ -+ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); -+ TRACE("Inodes are %scompressed\n", -+ SQUASHFS_UNCOMPRESSED_INODES -+ (sblk->flags) ? "un" : ""); -+ TRACE("Data is %scompressed\n", -+ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) -+ ? "un" : ""); -+ TRACE("Check data is %s present in the filesystem\n", -+ SQUASHFS_CHECK_DATA(sblk->flags) ? -+ "" : "not"); -+ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); -+ TRACE("Block size %d\n", sblk->block_size); -+ TRACE("Number of inodes %d\n", sblk->inodes); -+ if (sblk->s_major > 1) -+ TRACE("Number of fragments %d\n", sblk->fragments); -+ TRACE("Number of uids %d\n", sblk->no_uids); -+ TRACE("Number of gids %d\n", sblk->no_guids); -+ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); -+ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); -+ if (sblk->s_major > 1) -+ TRACE("sblk->fragment_table_start %llx\n", -+ sblk->fragment_table_start); -+ TRACE("sblk->uid_start %llx\n", sblk->uid_start); -+ -+ s->s_flags |= MS_RDONLY; -+ s->s_op = &squashfs_ops; -+ -+ /* Init inode_table block pointer array */ -+ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * -+ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { -+ ERROR("Failed to allocate block cache\n"); -+ goto failed_mount; -+ } -+ -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; -+ -+ msblk->next_cache = 0; -+ -+ /* Allocate read_data block */ -+ msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ? -+ SQUASHFS_METADATA_SIZE : -+ sblk->block_size; -+ -+ if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) { -+ ERROR("Failed to allocate read_data block\n"); -+ goto failed_mount; -+ } -+ -+ /* Allocate read_page block */ -+ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { -+ ERROR("Failed to allocate read_page block\n"); -+ goto failed_mount; -+ } -+ -+ /* Allocate uid and gid tables */ -+ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * -+ sizeof(unsigned int), GFP_KERNEL))) { -+ ERROR("Failed to allocate uid/gid table\n"); -+ goto failed_mount; -+ } -+ msblk->guid = msblk->uid + sblk->no_uids; -+ -+ if (msblk->swap) { -+ unsigned int suid[sblk->no_uids + sblk->no_guids]; -+ -+ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, -+ ((sblk->no_uids + sblk->no_guids) * -+ sizeof(unsigned int)) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read uid/gid table\n"); -+ goto failed_mount; -+ } -+ -+ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + -+ sblk->no_guids), (sizeof(unsigned int) * 8)); -+ } else -+ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, -+ ((sblk->no_uids + sblk->no_guids) * -+ sizeof(unsigned int)) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read uid/gid table\n"); -+ goto failed_mount; -+ } -+ -+ -+ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) -+ goto allocate_root; -+ -+ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * -+ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { -+ ERROR("Failed to allocate fragment block cache\n"); -+ goto failed_mount; -+ } -+ -+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { -+ msblk->fragment[i].locked = 0; -+ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; -+ msblk->fragment[i].data = NULL; -+ } -+ -+ msblk->next_fragment = 0; -+ -+ /* Allocate fragment index table */ -+ if (msblk->read_fragment_index_table(s) == 0) -+ goto failed_mount; -+ -+allocate_root: -+ if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL) -+ goto failed_mount; -+ -+ if ((s->s_root = d_alloc_root(root)) == NULL) { -+ ERROR("Root inode create failed\n"); -+ iput(root); -+ goto failed_mount; -+ } -+ -+ TRACE("Leaving squashfs_read_super\n"); -+ return 0; -+ -+failed_mount: -+ kfree(msblk->fragment_index); -+ kfree(msblk->fragment); -+ kfree(msblk->uid); -+ kfree(msblk->read_page); -+ kfree(msblk->read_data); -+ kfree(msblk->block_cache); -+ kfree(msblk->fragment_index_2); -+ kfree(s->s_fs_info); -+ s->s_fs_info = NULL; -+ return -EINVAL; -+ -+failure: -+ return -ENOMEM; -+} -+ -+ -+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ struct squashfs_sb_info *msblk = dentry->d_inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ TRACE("Entered squashfs_statfs\n"); -+ -+ buf->f_type = SQUASHFS_MAGIC; -+ buf->f_bsize = sblk->block_size; -+ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; -+ buf->f_bfree = buf->f_bavail = 0; -+ buf->f_files = sblk->inodes; -+ buf->f_ffree = 0; -+ buf->f_namelen = SQUASHFS_NAME_LEN; -+ -+ return 0; -+} -+ -+ -+static int squashfs_symlink_readpage(struct file *file, struct page *page) -+{ -+ struct inode *inode = page->mapping->host; -+ int index = page->index << PAGE_CACHE_SHIFT, length, bytes; -+ long long block = SQUASHFS_I(inode)->start_block; -+ int offset = SQUASHFS_I(inode)->offset; -+ void *pageaddr = kmap(page); -+ -+ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " -+ "%llx, offset %x\n", page->index, -+ SQUASHFS_I(inode)->start_block, -+ SQUASHFS_I(inode)->offset); -+ -+ for (length = 0; length < index; length += bytes) { -+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, -+ block, offset, PAGE_CACHE_SIZE, &block, -+ &offset))) { -+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, -+ offset); -+ goto skip_read; -+ } -+ } -+ -+ if (length != index) { -+ ERROR("(squashfs_symlink_readpage) length != index\n"); -+ bytes = 0; -+ goto skip_read; -+ } -+ -+ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : -+ i_size_read(inode) - length; -+ -+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, -+ offset, bytes, &block, &offset))) -+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); -+ -+skip_read: -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); -+ kunmap(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ -+ return 0; -+} -+ -+ -+struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) -+{ -+ struct meta_index *meta = NULL; -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ int i; -+ -+ down(&msblk->meta_index_mutex); -+ -+ TRACE("locate_meta_index: index %d, offset %d\n", index, offset); -+ -+ if(msblk->meta_index == NULL) -+ goto not_allocated; -+ -+ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) -+ if (msblk->meta_index[i].inode_number == inode->i_ino && -+ msblk->meta_index[i].offset >= offset && -+ msblk->meta_index[i].offset <= index && -+ msblk->meta_index[i].locked == 0) { -+ TRACE("locate_meta_index: entry %d, offset %d\n", i, -+ msblk->meta_index[i].offset); -+ meta = &msblk->meta_index[i]; -+ offset = meta->offset; -+ } -+ -+ if (meta) -+ meta->locked = 1; -+ -+not_allocated: -+ up(&msblk->meta_index_mutex); -+ -+ return meta; -+} -+ -+ -+struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) -+{ -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct meta_index *meta = NULL; -+ int i; -+ -+ down(&msblk->meta_index_mutex); -+ -+ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); -+ -+ if(msblk->meta_index == NULL) { -+ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * -+ SQUASHFS_META_NUMBER, GFP_KERNEL))) { -+ ERROR("Failed to allocate meta_index\n"); -+ goto failed; -+ } -+ for(i = 0; i < SQUASHFS_META_NUMBER; i++) { -+ msblk->meta_index[i].inode_number = 0; -+ msblk->meta_index[i].locked = 0; -+ } -+ msblk->next_meta_index = 0; -+ } -+ -+ for(i = SQUASHFS_META_NUMBER; i && -+ msblk->meta_index[msblk->next_meta_index].locked; i --) -+ msblk->next_meta_index = (msblk->next_meta_index + 1) % -+ SQUASHFS_META_NUMBER; -+ -+ if(i == 0) { -+ TRACE("empty_meta_index: failed!\n"); -+ goto failed; -+ } -+ -+ TRACE("empty_meta_index: returned meta entry %d, %p\n", -+ msblk->next_meta_index, -+ &msblk->meta_index[msblk->next_meta_index]); -+ -+ meta = &msblk->meta_index[msblk->next_meta_index]; -+ msblk->next_meta_index = (msblk->next_meta_index + 1) % -+ SQUASHFS_META_NUMBER; -+ -+ meta->inode_number = inode->i_ino; -+ meta->offset = offset; -+ meta->skip = skip; -+ meta->entries = 0; -+ meta->locked = 1; -+ -+failed: -+ up(&msblk->meta_index_mutex); -+ return meta; -+} -+ -+ -+void release_meta_index(struct inode *inode, struct meta_index *meta) -+{ -+ meta->locked = 0; -+} -+ -+ -+static int read_block_index(struct super_block *s, int blocks, char *block_list, -+ long long *start_block, int *offset) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ unsigned int *block_listp; -+ int block = 0; -+ -+ if (msblk->swap) { -+ char sblock_list[blocks << 2]; -+ -+ if (!squashfs_get_cached_block(s, sblock_list, *start_block, -+ *offset, blocks << 2, start_block, offset)) { -+ ERROR("Unable to read block list [%llx:%x]\n", -+ *start_block, *offset); -+ goto failure; -+ } -+ SQUASHFS_SWAP_INTS(((unsigned int *)block_list), -+ ((unsigned int *)sblock_list), blocks); -+ } else -+ if (!squashfs_get_cached_block(s, block_list, *start_block, -+ *offset, blocks << 2, start_block, offset)) { -+ ERROR("Unable to read block list [%llx:%x]\n", -+ *start_block, *offset); -+ goto failure; -+ } -+ -+ for (block_listp = (unsigned int *) block_list; blocks; -+ block_listp++, blocks --) -+ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); -+ -+ return block; -+ -+failure: -+ return -1; -+} -+ -+ -+#define SIZE 256 -+ -+static inline int calculate_skip(int blocks) { -+ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); -+ return skip >= 7 ? 7 : skip + 1; -+} -+ -+ -+static int get_meta_index(struct inode *inode, int index, -+ long long *index_block, int *index_offset, -+ long long *data_block, char *block_list) -+{ -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); -+ int offset = 0; -+ struct meta_index *meta; -+ struct meta_entry *meta_entry; -+ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; -+ int cur_offset = SQUASHFS_I(inode)->offset; -+ long long cur_data_block = SQUASHFS_I(inode)->start_block; -+ int i; -+ -+ index /= SQUASHFS_META_INDEXES * skip; -+ -+ while ( offset < index ) { -+ meta = locate_meta_index(inode, index, offset + 1); -+ -+ if (meta == NULL) { -+ if ((meta = empty_meta_index(inode, offset + 1, -+ skip)) == NULL) -+ goto all_done; -+ } else { -+ offset = index < meta->offset + meta->entries ? index : -+ meta->offset + meta->entries - 1; -+ meta_entry = &meta->meta_entry[offset - meta->offset]; -+ cur_index_block = meta_entry->index_block + sblk->inode_table_start; -+ cur_offset = meta_entry->offset; -+ cur_data_block = meta_entry->data_block; -+ TRACE("get_meta_index: offset %d, meta->offset %d, " -+ "meta->entries %d\n", offset, meta->offset, -+ meta->entries); -+ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" -+ " data_block 0x%llx\n", cur_index_block, -+ cur_offset, cur_data_block); -+ } -+ -+ for (i = meta->offset + meta->entries; i <= index && -+ i < meta->offset + SQUASHFS_META_ENTRIES; i++) { -+ int blocks = skip * SQUASHFS_META_INDEXES; -+ -+ while (blocks) { -+ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : -+ blocks; -+ int res = read_block_index(inode->i_sb, block, -+ block_list, &cur_index_block, -+ &cur_offset); -+ -+ if (res == -1) -+ goto failed; -+ -+ cur_data_block += res; -+ blocks -= block; -+ } -+ -+ meta_entry = &meta->meta_entry[i - meta->offset]; -+ meta_entry->index_block = cur_index_block - sblk->inode_table_start; -+ meta_entry->offset = cur_offset; -+ meta_entry->data_block = cur_data_block; -+ meta->entries ++; -+ offset ++; -+ } -+ -+ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", -+ meta->offset, meta->entries); -+ -+ release_meta_index(inode, meta); -+ } -+ -+all_done: -+ *index_block = cur_index_block; -+ *index_offset = cur_offset; -+ *data_block = cur_data_block; -+ -+ return offset * SQUASHFS_META_INDEXES * skip; -+ -+failed: -+ release_meta_index(inode, meta); -+ return -1; -+} -+ -+ -+static long long read_blocklist(struct inode *inode, int index, -+ int readahead_blks, char *block_list, -+ unsigned short **block_p, unsigned int *bsize) -+{ -+ long long block_ptr; -+ int offset; -+ long long block; -+ int res = get_meta_index(inode, index, &block_ptr, &offset, &block, -+ block_list); -+ -+ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" -+ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, -+ block); -+ -+ if(res == -1) -+ goto failure; -+ -+ index -= res; -+ -+ while ( index ) { -+ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; -+ int res = read_block_index(inode->i_sb, blocks, block_list, -+ &block_ptr, &offset); -+ if (res == -1) -+ goto failure; -+ block += res; -+ index -= blocks; -+ } -+ -+ if (read_block_index(inode->i_sb, 1, block_list, -+ &block_ptr, &offset) == -1) -+ goto failure; -+ *bsize = *((unsigned int *) block_list); -+ -+ return block; -+ -+failure: -+ return 0; -+} -+ -+ -+static int squashfs_readpage(struct file *file, struct page *page) -+{ -+ struct inode *inode = page->mapping->host; -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned char block_list[SIZE]; -+ long long block; -+ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; -+ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); -+ void *pageaddr; -+ struct squashfs_fragment_cache *fragment = NULL; -+ char *data_ptr = msblk->read_page; -+ -+ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; -+ int start_index = page->index & ~mask; -+ int end_index = start_index | mask; -+ -+ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", -+ page->index, -+ SQUASHFS_I(inode)->start_block); -+ -+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> -+ PAGE_CACHE_SHIFT)) -+ goto skip_read; -+ -+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || index < (i_size_read(inode) >> -+ sblk->block_log)) { -+ if ((block = (msblk->read_blocklist)(inode, index, 1, -+ block_list, NULL, &bsize)) == 0) -+ goto skip_read; -+ -+ down(&msblk->read_page_mutex); -+ -+ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, -+ block, bsize, NULL))) { -+ ERROR("Unable to read page, block %llx, size %x\n", block, -+ bsize); -+ up(&msblk->read_page_mutex); -+ goto skip_read; -+ } -+ } else { -+ if ((fragment = get_cached_fragment(inode->i_sb, -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ SQUASHFS_I(inode)->u.s1.fragment_size)) -+ == NULL) { -+ ERROR("Unable to read page, block %llx, size %x\n", -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ (int) SQUASHFS_I(inode)-> -+ u.s1.fragment_size); -+ goto skip_read; -+ } -+ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + -+ (i_size_read(inode) & (sblk->block_size -+ - 1)); -+ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; -+ data_ptr = fragment->data; -+ } -+ -+ for (i = start_index; i <= end_index && byte_offset < bytes; -+ i++, byte_offset += PAGE_CACHE_SIZE) { -+ struct page *push_page; -+ int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? -+ PAGE_CACHE_SIZE : bytes - byte_offset; -+ -+ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", -+ bytes, i, byte_offset, available_bytes); -+ -+ if (i == page->index) { -+ pageaddr = kmap_atomic(page, KM_USER0); -+ memcpy(pageaddr, data_ptr + byte_offset, -+ available_bytes); -+ memset(pageaddr + available_bytes, 0, -+ PAGE_CACHE_SIZE - available_bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ } else if ((push_page = -+ grab_cache_page_nowait(page->mapping, i))) { -+ pageaddr = kmap_atomic(push_page, KM_USER0); -+ -+ memcpy(pageaddr, data_ptr + byte_offset, -+ available_bytes); -+ memset(pageaddr + available_bytes, 0, -+ PAGE_CACHE_SIZE - available_bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(push_page); -+ SetPageUptodate(push_page); -+ unlock_page(push_page); -+ page_cache_release(push_page); -+ } -+ } -+ -+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || index < (i_size_read(inode) >> -+ sblk->block_log)) -+ up(&msblk->read_page_mutex); -+ else -+ release_cached_fragment(msblk, fragment); -+ -+ return 0; -+ -+skip_read: -+ pageaddr = kmap_atomic(page, KM_USER0); -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ -+ return 0; -+} -+ -+ -+static int squashfs_readpage4K(struct file *file, struct page *page) -+{ -+ struct inode *inode = page->mapping->host; -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned char block_list[SIZE]; -+ long long block; -+ unsigned int bsize, bytes = 0; -+ void *pageaddr; -+ -+ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", -+ page->index, -+ SQUASHFS_I(inode)->start_block); -+ -+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> -+ PAGE_CACHE_SHIFT)) { -+ pageaddr = kmap_atomic(page, KM_USER0); -+ goto skip_read; -+ } -+ -+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || page->index < (i_size_read(inode) >> -+ sblk->block_log)) { -+ block = (msblk->read_blocklist)(inode, page->index, 1, -+ block_list, NULL, &bsize); -+ -+ down(&msblk->read_page_mutex); -+ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, -+ bsize, NULL); -+ pageaddr = kmap_atomic(page, KM_USER0); -+ if (bytes) -+ memcpy(pageaddr, msblk->read_page, bytes); -+ else -+ ERROR("Unable to read page, block %llx, size %x\n", -+ block, bsize); -+ up(&msblk->read_page_mutex); -+ } else { -+ struct squashfs_fragment_cache *fragment = -+ get_cached_fragment(inode->i_sb, -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ SQUASHFS_I(inode)-> u.s1.fragment_size); -+ pageaddr = kmap_atomic(page, KM_USER0); -+ if (fragment) { -+ bytes = i_size_read(inode) & (sblk->block_size - 1); -+ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> -+ u.s1.fragment_offset, bytes); -+ release_cached_fragment(msblk, fragment); -+ } else -+ ERROR("Unable to read page, block %llx, size %x\n", -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, (int) -+ SQUASHFS_I(inode)-> u.s1.fragment_size); -+ } -+ -+skip_read: -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ -+ return 0; -+} -+ -+ -+static int get_dir_index_using_offset(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ long long f_pos) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ struct squashfs_dir_index index; -+ -+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", -+ i_count, (unsigned int) f_pos); -+ -+ f_pos =- 3; -+ if (f_pos == 0) -+ goto finish; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) &index, -+ index_start, index_offset, -+ sizeof(index), &index_start, -+ &index_offset); -+ -+ if (index.index > f_pos) -+ break; -+ -+ squashfs_get_cached_block(s, NULL, index_start, index_offset, -+ index.size + 1, &index_start, -+ &index_offset); -+ -+ length = index.index; -+ *next_block = index.start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ -+finish: -+ return length + 3; -+} -+ -+ -+static int get_dir_index_using_name(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ const char *name, int size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer; -+ char str[SQUASHFS_NAME_LEN + 1]; -+ -+ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); -+ -+ strncpy(str, name, size); -+ str[size] = '\0'; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX(index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) index, -+ index_start, index_offset, -+ sizeof(struct squashfs_dir_index), -+ &index_start, &index_offset); -+ -+ squashfs_get_cached_block(s, index->name, index_start, -+ index_offset, index->size + 1, -+ &index_start, &index_offset); -+ -+ index->name[index->size + 1] = '\0'; -+ -+ if (strcmp(index->name, str) > 0) -+ break; -+ -+ length = index->index; -+ *next_block = index->start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ return length + 3; -+} -+ -+ -+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) -+{ -+ struct inode *i = file->f_dentry->d_inode; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, -+ dir_count; -+ struct squashfs_dir_header dirh; -+ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; -+ -+ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); -+ -+ while(file->f_pos < 3) { -+ char *name; -+ int size, i_ino; -+ -+ if(file->f_pos == 0) { -+ name = "."; -+ size = 1; -+ i_ino = i->i_ino; -+ } else { -+ name = ".."; -+ size = 2; -+ i_ino = SQUASHFS_I(i)->u.s2.parent_inode; -+ } -+ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", -+ (unsigned int) dirent, name, size, (int) -+ file->f_pos, i_ino, -+ squashfs_filetype_table[1]); -+ -+ if (filldir(dirent, name, size, -+ file->f_pos, i_ino, -+ squashfs_filetype_table[1]) < 0) { -+ TRACE("Filldir returned less than 0\n"); -+ goto finish; -+ } -+ file->f_pos += size; -+ dirs_read++; -+ } -+ -+ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, -+ file->f_pos); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header sdirh; -+ -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block, next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block, next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, -+ dire->size + 1, &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (file->f_pos >= length) -+ continue; -+ -+ dire->name[dire->size + 1] = '\0'; -+ -+ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", -+ (unsigned int) dirent, dire->name, -+ dire->size + 1, (int) file->f_pos, -+ dirh.start_block, dire->offset, -+ dirh.inode_number + dire->inode_number, -+ squashfs_filetype_table[dire->type]); -+ -+ if (filldir(dirent, dire->name, dire->size + 1, -+ file->f_pos, -+ dirh.inode_number + dire->inode_number, -+ squashfs_filetype_table[dire->type]) -+ < 0) { -+ TRACE("Filldir returned less than 0\n"); -+ goto finish; -+ } -+ file->f_pos = length; -+ dirs_read++; -+ } -+ } -+ -+finish: -+ return dirs_read; -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ return 0; -+} -+ -+ -+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, -+ struct nameidata *nd) -+{ -+ const unsigned char *name = dentry->d_name.name; -+ int len = dentry->d_name.len; -+ struct inode *inode = NULL; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, -+ dir_count; -+ struct squashfs_dir_header dirh; -+ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN]; -+ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; -+ -+ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); -+ -+ if (len > SQUASHFS_NAME_LEN) -+ goto exit_loop; -+ -+ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, name, -+ len); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header sdirh; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block,next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block,next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, dire->size + 1, -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (name[0] < dire->name[0]) -+ goto exit_loop; -+ -+ if ((len == dire->size + 1) && !strncmp(name, -+ dire->name, len)) { -+ squashfs_inode_t ino = -+ SQUASHFS_MKINODE(dirh.start_block, -+ dire->offset); -+ -+ TRACE("calling squashfs_iget for directory " -+ "entry %s, inode %x:%x, %d\n", name, -+ dirh.start_block, dire->offset, -+ dirh.inode_number + dire->inode_number); -+ -+ inode = (msblk->iget)(i->i_sb, ino); -+ -+ goto exit_loop; -+ } -+ } -+ } -+ -+exit_loop: -+ d_add(dentry, inode); -+ return ERR_PTR(0); -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ goto exit_loop; -+} -+ -+ -+static void squashfs_put_super(struct super_block *s) -+{ -+ int i; -+ -+ if (s->s_fs_info) { -+ struct squashfs_sb_info *sbi = s->s_fs_info; -+ if (sbi->block_cache) -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ if (sbi->block_cache[i].block != -+ SQUASHFS_INVALID_BLK) -+ kfree(sbi->block_cache[i].data); -+ if (sbi->fragment) -+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) -+ SQUASHFS_FREE(sbi->fragment[i].data); -+ kfree(sbi->fragment); -+ kfree(sbi->block_cache); -+ kfree(sbi->read_data); -+ kfree(sbi->read_page); -+ kfree(sbi->uid); -+ kfree(sbi->fragment_index); -+ kfree(sbi->fragment_index_2); -+ kfree(sbi->meta_index); -+ kfree(s->s_fs_info); -+ s->s_fs_info = NULL; -+ } -+} -+ -+ -+static int squashfs_get_sb(struct file_system_type *fs_type, -+ int flags, const char *dev_name, void *data, -+ struct vfsmount *mnt) -+{ -+ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, mnt); -+} -+ -+ -+static int __init init_squashfs_fs(void) -+{ -+ int err = init_inodecache(); -+ if (err) -+ goto out; -+ -+ printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " -+ "Phillip Lougher\n"); -+ -+ if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { -+ ERROR("Failed to allocate zlib workspace\n"); -+ destroy_inodecache(); -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ if ((err = register_filesystem(&squashfs_fs_type))) { -+ vfree(stream.workspace); -+ destroy_inodecache(); -+ } -+ -+out: -+ return err; -+} -+ -+ -+static void __exit exit_squashfs_fs(void) -+{ -+ vfree(stream.workspace); -+ unregister_filesystem(&squashfs_fs_type); -+ destroy_inodecache(); -+} -+ -+ -+static struct kmem_cache * squashfs_inode_cachep; -+ -+ -+static struct inode *squashfs_alloc_inode(struct super_block *sb) -+{ -+ struct squashfs_inode_info *ei; -+ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); -+ if (!ei) -+ return NULL; -+ return &ei->vfs_inode; -+} -+ -+ -+static void squashfs_destroy_inode(struct inode *inode) -+{ -+ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode)); -+} -+ -+ -+static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) -+{ -+ struct squashfs_inode_info *ei = foo; -+ -+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == -+ SLAB_CTOR_CONSTRUCTOR) -+ inode_init_once(&ei->vfs_inode); -+} -+ -+ -+static int __init init_inodecache(void) -+{ -+ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", -+ sizeof(struct squashfs_inode_info), -+ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, -+ init_once, NULL); -+ if (squashfs_inode_cachep == NULL) -+ return -ENOMEM; -+ return 0; -+} -+ -+ -+static void destroy_inodecache(void) -+{ -+ kmem_cache_destroy(squashfs_inode_cachep); -+} -+ -+ -+module_init(init_squashfs_fs); -+module_exit(exit_squashfs_fs); -+MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); -+MODULE_AUTHOR("Phillip Lougher "); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.21.7/fs/squashfs/Makefile -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/squashfs/Makefile -@@ -0,0 +1,7 @@ -+# -+# Makefile for the linux squashfs routines. -+# -+ -+obj-$(CONFIG_SQUASHFS) += squashfs.o -+squashfs-y += inode.o -+squashfs-y += squashfs2_0.o -Index: linux-2.6.21.7/fs/squashfs/squashfs2_0.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/squashfs/squashfs2_0.c -@@ -0,0 +1,758 @@ -+/* -+ * Squashfs - a compressed read only filesystem for Linux -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs2_0.c -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "squashfs.h" -+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir); -+static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *, -+ struct nameidata *); -+ -+static struct file_operations squashfs_dir_ops_2 = { -+ .read = generic_read_dir, -+ .readdir = squashfs_readdir_2 -+}; -+ -+static struct inode_operations squashfs_dir_inode_ops_2 = { -+ .lookup = squashfs_lookup_2 -+}; -+ -+static unsigned char squashfs_filetype_table[] = { -+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK -+}; -+ -+static int read_fragment_index_table_2(struct super_block *s) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2 -+ (sblk->fragments), GFP_KERNEL))) { -+ ERROR("Failed to allocate uid/gid table\n"); -+ return 0; -+ } -+ -+ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && -+ !squashfs_read_data(s, (char *) -+ msblk->fragment_index_2, -+ sblk->fragment_table_start, -+ SQUASHFS_FRAGMENT_INDEX_BYTES_2 -+ (sblk->fragments) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read fragment index table\n"); -+ return 0; -+ } -+ -+ if (msblk->swap) { -+ int i; -+ unsigned int fragment; -+ -+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments); -+ i++) { -+ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment), -+ &msblk->fragment_index_2[i], 1); -+ msblk->fragment_index_2[i] = fragment; -+ } -+ } -+ -+ return 1; -+} -+ -+ -+static int get_fragment_location_2(struct super_block *s, unsigned int fragment, -+ long long *fragment_start_block, -+ unsigned int *fragment_size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ long long start_block = -+ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)]; -+ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment); -+ struct squashfs_fragment_entry_2 fragment_entry; -+ -+ if (msblk->swap) { -+ struct squashfs_fragment_entry_2 sfragment_entry; -+ -+ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, -+ start_block, offset, -+ sizeof(sfragment_entry), &start_block, -+ &offset)) -+ goto out; -+ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, -+ start_block, offset, -+ sizeof(fragment_entry), &start_block, -+ &offset)) -+ goto out; -+ -+ *fragment_start_block = fragment_entry.start_block; -+ *fragment_size = fragment_entry.size; -+ -+ return 1; -+ -+out: -+ return 0; -+} -+ -+ -+static struct inode *squashfs_new_inode(struct super_block *s, -+ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ struct inode *i = new_inode(s); -+ -+ if (i) { -+ i->i_ino = ino; -+ i->i_mtime.tv_sec = sblk->mkfs_time; -+ i->i_atime.tv_sec = sblk->mkfs_time; -+ i->i_ctime.tv_sec = sblk->mkfs_time; -+ i->i_uid = msblk->uid[inodeb->uid]; -+ i->i_mode = inodeb->mode; -+ i->i_nlink = 1; -+ i->i_size = 0; -+ if (inodeb->guid == SQUASHFS_GUIDS) -+ i->i_gid = i->i_uid; -+ else -+ i->i_gid = msblk->guid[inodeb->guid]; -+ } -+ -+ return i; -+} -+ -+ -+static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode) -+{ -+ struct inode *i; -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned int block = SQUASHFS_INODE_BLK(inode) + -+ sblk->inode_table_start; -+ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); -+ unsigned int ino = SQUASHFS_MK_VFS_INODE(block -+ - sblk->inode_table_start, offset); -+ long long next_block; -+ unsigned int next_offset; -+ union squashfs_inode_header_2 id, sid; -+ struct squashfs_base_inode_header_2 *inodeb = &id.base, -+ *sinodeb = &sid.base; -+ -+ TRACE("Entered squashfs_iget\n"); -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, -+ offset, sizeof(*sinodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb, -+ sizeof(*sinodeb)); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) inodeb, block, -+ offset, sizeof(*inodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ switch(inodeb->inode_type) { -+ case SQUASHFS_FILE_TYPE: { -+ struct squashfs_reg_inode_header_2 *inodep = &id.reg; -+ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg; -+ long long frag_blk; -+ unsigned int frag_size; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location_2(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->file_size; -+ i->i_fop = &generic_ro_fops; -+ i->i_mode |= S_IFREG; -+ i->i_mtime.tv_sec = inodep->mtime; -+ i->i_atime.tv_sec = inodep->mtime; -+ i->i_ctime.tv_sec = inodep->mtime; -+ i->i_blocks = ((i->i_size - 1) >> 9) + 1; -+ i->i_blksize = PAGE_CACHE_SIZE; -+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; -+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; -+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->u.s1.block_list_start = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; -+ -+ TRACE("File inode %x:%x, start_block %x, " -+ "block_list_start %llx, offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, next_block, -+ next_offset); -+ break; -+ } -+ case SQUASHFS_DIR_TYPE: { -+ struct squashfs_dir_inode_header_2 *inodep = &id.dir; -+ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops_2; -+ i->i_fop = &squashfs_dir_ops_2; -+ i->i_mode |= S_IFDIR; -+ i->i_mtime.tv_sec = inodep->mtime; -+ i->i_atime.tv_sec = inodep->mtime; -+ i->i_ctime.tv_sec = inodep->mtime; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = 0; -+ SQUASHFS_I(i)->u.s2.parent_inode = 0; -+ -+ TRACE("Directory inode %x:%x, start_block %x, offset " -+ "%x\n", SQUASHFS_INODE_BLK(inode), -+ offset, inodep->start_block, -+ inodep->offset); -+ break; -+ } -+ case SQUASHFS_LDIR_TYPE: { -+ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir; -+ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops_2; -+ i->i_fop = &squashfs_dir_ops_2; -+ i->i_mode |= S_IFDIR; -+ i->i_mtime.tv_sec = inodep->mtime; -+ i->i_atime.tv_sec = inodep->mtime; -+ i->i_ctime.tv_sec = inodep->mtime; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; -+ SQUASHFS_I(i)->u.s2.directory_index_offset = -+ next_offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = -+ inodep->i_count; -+ SQUASHFS_I(i)->u.s2.parent_inode = 0; -+ -+ TRACE("Long directory inode %x:%x, start_block %x, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, inodep->offset); -+ break; -+ } -+ case SQUASHFS_SYMLINK_TYPE: { -+ struct squashfs_symlink_inode_header_2 *inodep = -+ &id.symlink; -+ struct squashfs_symlink_inode_header_2 *sinodep = -+ &sid.symlink; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->symlink_size; -+ i->i_op = &page_symlink_inode_operations; -+ i->i_data.a_ops = &squashfs_symlink_aops; -+ i->i_mode |= S_IFLNK; -+ SQUASHFS_I(i)->start_block = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ -+ TRACE("Symbolic link inode %x:%x, start_block %llx, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ next_block, next_offset); -+ break; -+ } -+ case SQUASHFS_BLKDEV_TYPE: -+ case SQUASHFS_CHRDEV_TYPE: { -+ struct squashfs_dev_inode_header_2 *inodep = &id.dev; -+ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_mode |= (inodeb->inode_type == -+ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : -+ S_IFBLK; -+ init_special_inode(i, i->i_mode, -+ old_decode_dev(inodep->rdev)); -+ -+ TRACE("Device inode %x:%x, rdev %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->rdev); -+ break; -+ } -+ case SQUASHFS_FIFO_TYPE: -+ case SQUASHFS_SOCKET_TYPE: { -+ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) -+ ? S_IFIFO : S_IFSOCK; -+ init_special_inode(i, i->i_mode, 0); -+ break; -+ } -+ default: -+ ERROR("Unknown inode type %d in squashfs_iget!\n", -+ inodeb->inode_type); -+ goto failed_read1; -+ } -+ -+ insert_inode_hash(i); -+ return i; -+ -+failed_read: -+ ERROR("Unable to read inode [%x:%x]\n", block, offset); -+ -+failed_read1: -+ return NULL; -+} -+ -+ -+static int get_dir_index_using_offset(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ long long f_pos) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ struct squashfs_dir_index_2 index; -+ -+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", -+ i_count, (unsigned int) f_pos); -+ -+ if (f_pos == 0) -+ goto finish; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index_2 sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) &index, -+ index_start, index_offset, -+ sizeof(index), &index_start, -+ &index_offset); -+ -+ if (index.index > f_pos) -+ break; -+ -+ squashfs_get_cached_block(s, NULL, index_start, index_offset, -+ index.size + 1, &index_start, -+ &index_offset); -+ -+ length = index.index; -+ *next_block = index.start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ -+finish: -+ return length; -+} -+ -+ -+static int get_dir_index_using_name(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ const char *name, int size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer; -+ char str[SQUASHFS_NAME_LEN + 1]; -+ -+ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); -+ -+ strncpy(str, name, size); -+ str[size] = '\0'; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index_2 sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) index, -+ index_start, index_offset, -+ sizeof(struct squashfs_dir_index_2), -+ &index_start, &index_offset); -+ -+ squashfs_get_cached_block(s, index->name, index_start, -+ index_offset, index->size + 1, -+ &index_start, &index_offset); -+ -+ index->name[index->size + 1] = '\0'; -+ -+ if (strcmp(index->name, str) > 0) -+ break; -+ -+ length = index->index; -+ *next_block = index->start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ return length; -+} -+ -+ -+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) -+{ -+ struct inode *i = file->f_dentry->d_inode; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, -+ dir_count; -+ struct squashfs_dir_header_2 dirh; -+ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; -+ -+ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); -+ -+ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, -+ file->f_pos); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header_2 sdirh; -+ -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry_2 sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block, next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block, next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, -+ dire->size + 1, &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (file->f_pos >= length) -+ continue; -+ -+ dire->name[dire->size + 1] = '\0'; -+ -+ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", -+ (unsigned int) dirent, dire->name, -+ dire->size + 1, (int) file->f_pos, -+ dirh.start_block, dire->offset, -+ squashfs_filetype_table[dire->type]); -+ -+ if (filldir(dirent, dire->name, dire->size + 1, -+ file->f_pos, SQUASHFS_MK_VFS_INODE( -+ dirh.start_block, dire->offset), -+ squashfs_filetype_table[dire->type]) -+ < 0) { -+ TRACE("Filldir returned less than 0\n"); -+ goto finish; -+ } -+ file->f_pos = length; -+ dirs_read++; -+ } -+ } -+ -+finish: -+ return dirs_read; -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ return 0; -+} -+ -+ -+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, -+ struct nameidata *nd) -+{ -+ const unsigned char *name = dentry->d_name.name; -+ int len = dentry->d_name.len; -+ struct inode *inode = NULL; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, -+ dir_count; -+ struct squashfs_dir_header_2 dirh; -+ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN]; -+ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; -+ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; -+ -+ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); -+ -+ if (len > SQUASHFS_NAME_LEN) -+ goto exit_loop; -+ -+ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, name, -+ len); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header_2 sdirh; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry_2 sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block,next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block,next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, dire->size + 1, -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (sorted && name[0] < dire->name[0]) -+ goto exit_loop; -+ -+ if ((len == dire->size + 1) && !strncmp(name, -+ dire->name, len)) { -+ squashfs_inode_t ino = -+ SQUASHFS_MKINODE(dirh.start_block, -+ dire->offset); -+ -+ TRACE("calling squashfs_iget for directory " -+ "entry %s, inode %x:%x, %lld\n", name, -+ dirh.start_block, dire->offset, ino); -+ -+ inode = (msblk->iget)(i->i_sb, ino); -+ -+ goto exit_loop; -+ } -+ } -+ } -+ -+exit_loop: -+ d_add(dentry, inode); -+ return ERR_PTR(0); -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ goto exit_loop; -+} -+ -+ -+int squashfs_2_0_supported(struct squashfs_sb_info *msblk) -+{ -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ msblk->iget = squashfs_iget_2; -+ msblk->read_fragment_index_table = read_fragment_index_table_2; -+ -+ sblk->bytes_used = sblk->bytes_used_2; -+ sblk->uid_start = sblk->uid_start_2; -+ sblk->guid_start = sblk->guid_start_2; -+ sblk->inode_table_start = sblk->inode_table_start_2; -+ sblk->directory_table_start = sblk->directory_table_start_2; -+ sblk->fragment_table_start = sblk->fragment_table_start_2; -+ -+ return 1; -+} -Index: linux-2.6.21.7/fs/squashfs/squashfs.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/squashfs/squashfs.h -@@ -0,0 +1,86 @@ -+/* -+ * Squashfs - a compressed read only filesystem for Linux -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs.h -+ */ -+ -+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+#endif -+ -+#ifdef SQUASHFS_TRACE -+#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) -+#else -+#define TRACE(s, args...) {} -+#endif -+ -+#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) -+ -+#define SERROR(s, args...) do { \ -+ if (!silent) \ -+ printk(KERN_ERR "SQUASHFS error: "s, ## args);\ -+ } while(0) -+ -+#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) -+ -+static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) -+{ -+ return list_entry(inode, struct squashfs_inode_info, vfs_inode); -+} -+ -+#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY) -+#define SQSH_EXTERN -+extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, -+ long long index, unsigned int length, -+ long long *next_index); -+extern int squashfs_get_cached_block(struct super_block *s, char *buffer, -+ long long block, unsigned int offset, -+ int length, long long *next_block, -+ unsigned int *next_offset); -+extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct -+ squashfs_fragment_cache *fragment); -+extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block -+ *s, long long start_block, -+ int length); -+extern struct address_space_operations squashfs_symlink_aops; -+extern struct address_space_operations squashfs_aops; -+extern struct address_space_operations squashfs_aops_4K; -+extern struct inode_operations squashfs_dir_inode_ops; -+#else -+#define SQSH_EXTERN static -+#endif -+ -+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk); -+#else -+static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk) -+{ -+ return 0; -+} -+#endif -+ -+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk); -+#else -+static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk) -+{ -+ return 0; -+} -+#endif -Index: linux-2.6.21.7/include/linux/squashfs_fs.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/squashfs_fs.h -@@ -0,0 +1,911 @@ -+#ifndef SQUASHFS_FS -+#define SQUASHFS_FS -+ -+/* -+ * Squashfs -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs_fs.h -+ */ -+ -+#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+#define CONFIG_SQUASHFS_2_0_COMPATIBILITY -+#endif -+ -+#ifdef CONFIG_SQUASHFS_VMALLOC -+#define SQUASHFS_ALLOC(a) vmalloc(a) -+#define SQUASHFS_FREE(a) vfree(a) -+#else -+#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL) -+#define SQUASHFS_FREE(a) kfree(a) -+#endif -+#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE -+#define SQUASHFS_MAJOR 3 -+#define SQUASHFS_MINOR 0 -+#define SQUASHFS_MAGIC 0x73717368 -+#define SQUASHFS_MAGIC_SWAP 0x68737173 -+#define SQUASHFS_START 0 -+ -+/* size of metadata (inode and directory) blocks */ -+#define SQUASHFS_METADATA_SIZE 8192 -+#define SQUASHFS_METADATA_LOG 13 -+ -+/* default size of data blocks */ -+#define SQUASHFS_FILE_SIZE 65536 -+#define SQUASHFS_FILE_LOG 16 -+ -+#define SQUASHFS_FILE_MAX_SIZE 65536 -+ -+/* Max number of uids and gids */ -+#define SQUASHFS_UIDS 256 -+#define SQUASHFS_GUIDS 255 -+ -+/* Max length of filename (not 255) */ -+#define SQUASHFS_NAME_LEN 256 -+ -+#define SQUASHFS_INVALID ((long long) 0xffffffffffff) -+#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) -+#define SQUASHFS_INVALID_BLK ((long long) -1) -+#define SQUASHFS_USED_BLK ((long long) -2) -+ -+/* Filesystem flags */ -+#define SQUASHFS_NOI 0 -+#define SQUASHFS_NOD 1 -+#define SQUASHFS_CHECK 2 -+#define SQUASHFS_NOF 3 -+#define SQUASHFS_NO_FRAG 4 -+#define SQUASHFS_ALWAYS_FRAG 5 -+#define SQUASHFS_DUPLICATE 6 -+ -+#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) -+ -+#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NOI) -+ -+#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NOD) -+ -+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NOF) -+ -+#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NO_FRAG) -+ -+#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_ALWAYS_FRAG) -+ -+#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_DUPLICATE) -+ -+#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_CHECK) -+ -+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ -+ duplicate_checking) (noi | (nod << 1) | (check_data << 2) \ -+ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ -+ (duplicate_checking << 6)) -+ -+/* Max number of types and file types */ -+#define SQUASHFS_DIR_TYPE 1 -+#define SQUASHFS_FILE_TYPE 2 -+#define SQUASHFS_SYMLINK_TYPE 3 -+#define SQUASHFS_BLKDEV_TYPE 4 -+#define SQUASHFS_CHRDEV_TYPE 5 -+#define SQUASHFS_FIFO_TYPE 6 -+#define SQUASHFS_SOCKET_TYPE 7 -+#define SQUASHFS_LDIR_TYPE 8 -+#define SQUASHFS_LREG_TYPE 9 -+ -+/* 1.0 filesystem type definitions */ -+#define SQUASHFS_TYPES 5 -+#define SQUASHFS_IPC_TYPE 0 -+ -+/* Flag whether block is compressed or uncompressed, bit is set if block is -+ * uncompressed */ -+#define SQUASHFS_COMPRESSED_BIT (1 << 15) -+ -+#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ -+ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) -+ -+#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) -+ -+#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) -+ -+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \ -+ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ -+ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) -+ -+#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) -+ -+/* -+ * Inode number ops. Inodes consist of a compressed block number, and an -+ * uncompressed offset within that block -+ */ -+#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) -+ -+#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) -+ -+#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\ -+ << 16) + (B))) -+ -+/* Compute 32 bit VFS inode number from squashfs inode number */ -+#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ -+ ((b) >> 2) + 1)) -+/* XXX */ -+ -+/* Translate between VFS mode and squashfs mode */ -+#define SQUASHFS_MODE(a) ((a) & 0xfff) -+ -+/* fragment and fragment table defines */ -+#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry)) -+ -+#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ -+ SQUASHFS_METADATA_SIZE - 1) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ -+ sizeof(long long)) -+ -+/* cached data constants for filesystem */ -+#define SQUASHFS_CACHED_BLKS 8 -+ -+#define SQUASHFS_MAX_FILE_SIZE_LOG 64 -+ -+#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ -+ (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) -+ -+#define SQUASHFS_MARKER_BYTE 0xff -+ -+/* meta index cache */ -+#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) -+#define SQUASHFS_META_ENTRIES 31 -+#define SQUASHFS_META_NUMBER 8 -+#define SQUASHFS_SLOTS 4 -+ -+struct meta_entry { -+ long long data_block; -+ unsigned int index_block; -+ unsigned short offset; -+ unsigned short pad; -+}; -+ -+struct meta_index { -+ unsigned int inode_number; -+ unsigned int offset; -+ unsigned short entries; -+ unsigned short skip; -+ unsigned short locked; -+ unsigned short pad; -+ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; -+}; -+ -+ -+/* -+ * definitions for structures on disk -+ */ -+ -+typedef long long squashfs_block_t; -+typedef long long squashfs_inode_t; -+ -+struct squashfs_super_block { -+ unsigned int s_magic; -+ unsigned int inodes; -+ unsigned int bytes_used_2; -+ unsigned int uid_start_2; -+ unsigned int guid_start_2; -+ unsigned int inode_table_start_2; -+ unsigned int directory_table_start_2; -+ unsigned int s_major:16; -+ unsigned int s_minor:16; -+ unsigned int block_size_1:16; -+ unsigned int block_log:16; -+ unsigned int flags:8; -+ unsigned int no_uids:8; -+ unsigned int no_guids:8; -+ unsigned int mkfs_time /* time of filesystem creation */; -+ squashfs_inode_t root_inode; -+ unsigned int block_size; -+ unsigned int fragments; -+ unsigned int fragment_table_start_2; -+ long long bytes_used; -+ long long uid_start; -+ long long guid_start; -+ long long inode_table_start; -+ long long directory_table_start; -+ long long fragment_table_start; -+ long long unused; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_index { -+ unsigned int index; -+ unsigned int start_block; -+ unsigned char size; -+ unsigned char name[0]; -+} __attribute__ ((packed)); -+ -+#define SQUASHFS_BASE_INODE_HEADER \ -+ unsigned int inode_type:4; \ -+ unsigned int mode:12; \ -+ unsigned int uid:8; \ -+ unsigned int guid:8; \ -+ unsigned int mtime; \ -+ unsigned int inode_number; -+ -+struct squashfs_base_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+} __attribute__ ((packed)); -+ -+struct squashfs_ipc_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+} __attribute__ ((packed)); -+ -+struct squashfs_dev_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned short rdev; -+} __attribute__ ((packed)); -+ -+struct squashfs_symlink_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned short symlink_size; -+ char symlink[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_reg_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ squashfs_block_t start_block; -+ unsigned int fragment; -+ unsigned int offset; -+ unsigned int file_size; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_lreg_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ squashfs_block_t start_block; -+ unsigned int fragment; -+ unsigned int offset; -+ long long file_size; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned int file_size:19; -+ unsigned int offset:13; -+ unsigned int start_block; -+ unsigned int parent_inode; -+} __attribute__ ((packed)); -+ -+struct squashfs_ldir_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned int file_size:27; -+ unsigned int offset:13; -+ unsigned int start_block; -+ unsigned int i_count:16; -+ unsigned int parent_inode; -+ struct squashfs_dir_index index[0]; -+} __attribute__ ((packed)); -+ -+union squashfs_inode_header { -+ struct squashfs_base_inode_header base; -+ struct squashfs_dev_inode_header dev; -+ struct squashfs_symlink_inode_header symlink; -+ struct squashfs_reg_inode_header reg; -+ struct squashfs_lreg_inode_header lreg; -+ struct squashfs_dir_inode_header dir; -+ struct squashfs_ldir_inode_header ldir; -+ struct squashfs_ipc_inode_header ipc; -+}; -+ -+struct squashfs_dir_entry { -+ unsigned int offset:13; -+ unsigned int type:3; -+ unsigned int size:8; -+ int inode_number:16; -+ char name[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_header { -+ unsigned int count:8; -+ unsigned int start_block; -+ unsigned int inode_number; -+} __attribute__ ((packed)); -+ -+struct squashfs_fragment_entry { -+ long long start_block; -+ unsigned int size; -+ unsigned int unused; -+} __attribute__ ((packed)); -+ -+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); -+extern int squashfs_uncompress_init(void); -+extern int squashfs_uncompress_exit(void); -+ -+/* -+ * macros to convert each packed bitfield structure from little endian to big -+ * endian and vice versa. These are needed when creating or using a filesystem -+ * on a machine with different byte ordering to the target architecture. -+ * -+ */ -+ -+#define SQUASHFS_SWAP_START \ -+ int bits;\ -+ int b_pos;\ -+ unsigned long long val;\ -+ unsigned char *s;\ -+ unsigned char *d; -+ -+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ -+ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ -+ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ -+ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ -+ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ -+ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ -+ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ -+ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ -+ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ -+ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ -+ SQUASHFS_SWAP((s)->flags, d, 288, 8);\ -+ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ -+ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ -+ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ -+ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ -+ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ -+ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ -+ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ -+ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ -+ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ -+ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ -+ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ -+ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ -+ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ -+ SQUASHFS_SWAP((s)->unused, d, 888, 64);\ -+} -+ -+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ -+ SQUASHFS_MEMSET(s, d, n);\ -+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ -+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ -+ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ -+ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ -+ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->inode_number, d, 64, 32); -+ -+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ -+} -+ -+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_ipc_inode_header))\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_dev_inode_header)); \ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ -+} -+ -+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_symlink_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ -+} -+ -+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_reg_inode_header));\ -+ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ -+ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ -+ SQUASHFS_SWAP((s)->offset, d, 192, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ -+} -+ -+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_lreg_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ -+ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ -+ SQUASHFS_SWAP((s)->offset, d, 224, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_dir_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ -+ SQUASHFS_SWAP((s)->offset, d, 147, 13);\ -+ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ -+ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ -+} -+ -+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_ldir_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ -+ SQUASHFS_SWAP((s)->offset, d, 155, 13);\ -+ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ -+ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ -+ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ -+ SQUASHFS_SWAP((s)->index, d, 0, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->size, d, 64, 8);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ -+ SQUASHFS_SWAP((s)->count, d, 0, 8);\ -+ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ -+ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ -+ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ -+ SQUASHFS_SWAP((s)->type, d, 13, 3);\ -+ SQUASHFS_SWAP((s)->size, d, 16, 8);\ -+ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ -+ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ -+ SQUASHFS_SWAP((s)->size, d, 64, 32);\ -+} -+ -+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * 2);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ 16)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ -+} -+ -+#define SQUASHFS_SWAP_INTS(s, d, n) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * 4);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ 32)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ -+} -+ -+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * 8);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ 64)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ -+} -+ -+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * bits / 8);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ bits)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) -+ -+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+ -+struct squashfs_base_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+} __attribute__ ((packed)); -+ -+struct squashfs_ipc_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned int type:4; -+ unsigned int offset:4; -+} __attribute__ ((packed)); -+ -+struct squashfs_dev_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned short rdev; -+} __attribute__ ((packed)); -+ -+struct squashfs_symlink_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned short symlink_size; -+ char symlink[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_reg_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned int mtime; -+ unsigned int start_block; -+ unsigned int file_size:32; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned int file_size:19; -+ unsigned int offset:13; -+ unsigned int mtime; -+ unsigned int start_block:24; -+} __attribute__ ((packed)); -+ -+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ -+ SQUASHFS_MEMSET(s, d, n);\ -+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ -+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ -+ SQUASHFS_SWAP((s)->uid, d, 16, 4);\ -+ SQUASHFS_SWAP((s)->guid, d, 20, 4); -+ -+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ -+} -+ -+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_ipc_inode_header_1));\ -+ SQUASHFS_SWAP((s)->type, d, 24, 4);\ -+ SQUASHFS_SWAP((s)->offset, d, 28, 4);\ -+} -+ -+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_dev_inode_header_1));\ -+ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ -+} -+ -+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_symlink_inode_header_1));\ -+ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ -+} -+ -+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_reg_inode_header_1));\ -+ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_dir_inode_header_1));\ -+ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ -+ SQUASHFS_SWAP((s)->offset, d, 43, 13);\ -+ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ -+} -+ -+#endif -+ -+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+ -+struct squashfs_dir_index_2 { -+ unsigned int index:27; -+ unsigned int start_block:29; -+ unsigned char size; -+ unsigned char name[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_base_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+} __attribute__ ((packed)); -+ -+struct squashfs_ipc_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+} __attribute__ ((packed)); -+ -+struct squashfs_dev_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned short rdev; -+} __attribute__ ((packed)); -+ -+struct squashfs_symlink_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned short symlink_size; -+ char symlink[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_reg_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned int mtime; -+ unsigned int start_block; -+ unsigned int fragment; -+ unsigned int offset; -+ unsigned int file_size:32; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned int file_size:19; -+ unsigned int offset:13; -+ unsigned int mtime; -+ unsigned int start_block:24; -+} __attribute__ ((packed)); -+ -+struct squashfs_ldir_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned int file_size:27; -+ unsigned int offset:13; -+ unsigned int mtime; -+ unsigned int start_block:24; -+ unsigned int i_count:16; -+ struct squashfs_dir_index_2 index[0]; -+} __attribute__ ((packed)); -+ -+union squashfs_inode_header_2 { -+ struct squashfs_base_inode_header_2 base; -+ struct squashfs_dev_inode_header_2 dev; -+ struct squashfs_symlink_inode_header_2 symlink; -+ struct squashfs_reg_inode_header_2 reg; -+ struct squashfs_dir_inode_header_2 dir; -+ struct squashfs_ldir_inode_header_2 ldir; -+ struct squashfs_ipc_inode_header_2 ipc; -+}; -+ -+struct squashfs_dir_header_2 { -+ unsigned int count:8; -+ unsigned int start_block:24; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_entry_2 { -+ unsigned int offset:13; -+ unsigned int type:3; -+ unsigned int size:8; -+ char name[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_fragment_entry_2 { -+ unsigned int start_block; -+ unsigned int size; -+} __attribute__ ((packed)); -+ -+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ -+ SQUASHFS_MEMSET(s, d, n);\ -+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ -+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ -+ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ -+ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ -+ -+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ -+} -+ -+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ -+ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) -+ -+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_dev_inode_header_2)); \ -+ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ -+} -+ -+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_symlink_inode_header_2));\ -+ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ -+} -+ -+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_reg_inode_header_2));\ -+ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ -+ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->offset, d, 128, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_dir_inode_header_2));\ -+ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ -+ SQUASHFS_SWAP((s)->offset, d, 51, 13);\ -+ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ -+} -+ -+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_ldir_inode_header_2));\ -+ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ -+ SQUASHFS_SWAP((s)->offset, d, 59, 13);\ -+ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ -+ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ -+ SQUASHFS_SWAP((s)->index, d, 0, 27);\ -+ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ -+ SQUASHFS_SWAP((s)->size, d, 56, 8);\ -+} -+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ -+ SQUASHFS_SWAP((s)->count, d, 0, 8);\ -+ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ -+ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ -+ SQUASHFS_SWAP((s)->type, d, 13, 3);\ -+ SQUASHFS_SWAP((s)->size, d, 16, 8);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ -+ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ -+ SQUASHFS_SWAP((s)->size, d, 32, 32);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) -+ -+/* fragment and fragment table defines */ -+#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2)) -+ -+#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ -+ SQUASHFS_METADATA_SIZE - 1) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ -+ sizeof(int)) -+ -+#endif -+ -+#ifdef __KERNEL__ -+ -+/* -+ * macros used to swap each structure entry, taking into account -+ * bitfields and different bitfield placing conventions on differing -+ * architectures -+ */ -+ -+#include -+ -+#ifdef __BIG_ENDIAN -+ /* convert from little endian to big endian */ -+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ -+ tbits, b_pos) -+#else -+ /* convert from big endian to little endian */ -+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ -+ tbits, 64 - tbits - b_pos) -+#endif -+ -+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ -+ b_pos = pos % 8;\ -+ val = 0;\ -+ s = (unsigned char *)p + (pos / 8);\ -+ d = ((unsigned char *) &val) + 7;\ -+ for(bits = 0; bits < (tbits + b_pos); bits += 8) \ -+ *d-- = *s++;\ -+ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ -+} -+ -+#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n); -+ -+#endif -+#endif -Index: linux-2.6.21.7/include/linux/squashfs_fs_i.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/squashfs_fs_i.h -@@ -0,0 +1,45 @@ -+#ifndef SQUASHFS_FS_I -+#define SQUASHFS_FS_I -+/* -+ * Squashfs -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs_fs_i.h -+ */ -+ -+struct squashfs_inode_info { -+ long long start_block; -+ unsigned int offset; -+ union { -+ struct { -+ long long fragment_start_block; -+ unsigned int fragment_size; -+ unsigned int fragment_offset; -+ long long block_list_start; -+ } s1; -+ struct { -+ long long directory_index_start; -+ unsigned int directory_index_offset; -+ unsigned int directory_index_count; -+ unsigned int parent_inode; -+ } s2; -+ } u; -+ struct inode vfs_inode; -+}; -+#endif -Index: linux-2.6.21.7/include/linux/squashfs_fs_sb.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/squashfs_fs_sb.h -@@ -0,0 +1,74 @@ -+#ifndef SQUASHFS_FS_SB -+#define SQUASHFS_FS_SB -+/* -+ * Squashfs -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs_fs_sb.h -+ */ -+ -+#include -+ -+struct squashfs_cache { -+ long long block; -+ int length; -+ long long next_index; -+ char *data; -+}; -+ -+struct squashfs_fragment_cache { -+ long long block; -+ int length; -+ unsigned int locked; -+ char *data; -+}; -+ -+struct squashfs_sb_info { -+ struct squashfs_super_block sblk; -+ int devblksize; -+ int devblksize_log2; -+ int swap; -+ struct squashfs_cache *block_cache; -+ struct squashfs_fragment_cache *fragment; -+ int next_cache; -+ int next_fragment; -+ int next_meta_index; -+ unsigned int *uid; -+ unsigned int *guid; -+ long long *fragment_index; -+ unsigned int *fragment_index_2; -+ unsigned int read_size; -+ char *read_data; -+ char *read_page; -+ struct semaphore read_data_mutex; -+ struct semaphore read_page_mutex; -+ struct semaphore block_cache_mutex; -+ struct semaphore fragment_mutex; -+ struct semaphore meta_index_mutex; -+ wait_queue_head_t waitq; -+ wait_queue_head_t fragment_wait_queue; -+ struct meta_index *meta_index; -+ struct inode *(*iget)(struct super_block *s, squashfs_inode_t \ -+ inode); -+ long long (*read_blocklist)(struct inode *inode, int \ -+ index, int readahead_blks, char *block_list, \ -+ unsigned short **block_p, unsigned int *bsize); -+ int (*read_fragment_index_table)(struct super_block *s); -+}; -+#endif -Index: linux-2.6.21.7/init/do_mounts_rd.c -=================================================================== ---- linux-2.6.21.7.orig/init/do_mounts_rd.c -+++ linux-2.6.21.7/init/do_mounts_rd.c -@@ -5,6 +5,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in - * numbers could not be found. - * - * We currently check for the following magic numbers: -+ * squashfs - * minix - * ext2 - * romfs -@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start - struct ext2_super_block *ext2sb; - struct romfs_super_block *romfsb; - struct cramfs_super *cramfsb; -+ struct squashfs_super_block *squashfsb; - int nblocks = -1; - unsigned char *buf; - -@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start - ext2sb = (struct ext2_super_block *) buf; - romfsb = (struct romfs_super_block *) buf; - cramfsb = (struct cramfs_super *) buf; -+ squashfsb = (struct squashfs_super_block *) buf; - memset(buf, 0xe5, size); - - /* -@@ -101,6 +105,15 @@ identify_ramdisk_image(int fd, int start - goto done; - } - -+ /* squashfs is at block zero too */ -+ if (squashfsb->s_magic == SQUASHFS_MAGIC) { -+ printk(KERN_NOTICE -+ "RAMDISK: squashfs filesystem found at block %d\n", -+ start_block); -+ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; -+ goto done; -+ } -+ - /* - * Read block 1 to test for minix and ext2 superblock - */ diff --git a/target/linux/generic-2.6/patches/002-lzma_decompress.patch b/target/linux/generic-2.6/patches/002-lzma_decompress.patch deleted file mode 100644 index e4a00692f2..0000000000 --- a/target/linux/generic-2.6/patches/002-lzma_decompress.patch +++ /dev/null @@ -1,794 +0,0 @@ -Index: linux-2.6.21.7/include/linux/LzmaDecode.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/LzmaDecode.h -@@ -0,0 +1,100 @@ -+/* -+ LzmaDecode.h -+ LZMA Decoder interface -+ -+ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) -+ http://www.7-zip.org/ -+ -+ LZMA SDK is licensed under two licenses: -+ 1) GNU Lesser General Public License (GNU LGPL) -+ 2) Common Public License (CPL) -+ It means that you can select one of these two licenses and -+ follow rules of that license. -+ -+ SPECIAL EXCEPTION: -+ Igor Pavlov, as the author of this code, expressly permits you to -+ statically or dynamically link your code (or bind by name) to the -+ interfaces of this file without subjecting your linked code to the -+ terms of the CPL or GNU LGPL. Any modifications or additions -+ to this file, however, are subject to the LGPL or CPL terms. -+*/ -+ -+#ifndef __LZMADECODE_H -+#define __LZMADECODE_H -+ -+/* #define _LZMA_IN_CB */ -+/* Use callback for input data */ -+ -+/* #define _LZMA_OUT_READ */ -+/* Use read function for output data */ -+ -+/* #define _LZMA_PROB32 */ -+/* It can increase speed on some 32-bit CPUs, -+ but memory usage will be doubled in that case */ -+ -+/* #define _LZMA_LOC_OPT */ -+/* Enable local speed optimizations inside code */ -+ -+#ifndef UInt32 -+#ifdef _LZMA_UINT32_IS_ULONG -+#define UInt32 unsigned long -+#else -+#define UInt32 unsigned int -+#endif -+#endif -+ -+#ifdef _LZMA_PROB32 -+#define CProb UInt32 -+#else -+#define CProb unsigned short -+#endif -+ -+#define LZMA_RESULT_OK 0 -+#define LZMA_RESULT_DATA_ERROR 1 -+#define LZMA_RESULT_NOT_ENOUGH_MEM 2 -+ -+#ifdef _LZMA_IN_CB -+typedef struct _ILzmaInCallback -+{ -+ int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize); -+} ILzmaInCallback; -+#endif -+ -+#define LZMA_BASE_SIZE 1846 -+#define LZMA_LIT_SIZE 768 -+ -+/* -+bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb) -+bufferSize += 100 in case of _LZMA_OUT_READ -+by default CProb is unsigned short, -+but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int) -+*/ -+ -+#ifdef _LZMA_OUT_READ -+int LzmaDecoderInit( -+ unsigned char *buffer, UInt32 bufferSize, -+ int lc, int lp, int pb, -+ unsigned char *dictionary, UInt32 dictionarySize, -+ #ifdef _LZMA_IN_CB -+ ILzmaInCallback *inCallback -+ #else -+ unsigned char *inStream, UInt32 inSize -+ #endif -+); -+#endif -+ -+int LzmaDecode( -+ unsigned char *buffer, -+ #ifndef _LZMA_OUT_READ -+ UInt32 bufferSize, -+ int lc, int lp, int pb, -+ #ifdef _LZMA_IN_CB -+ ILzmaInCallback *inCallback, -+ #else -+ unsigned char *inStream, UInt32 inSize, -+ #endif -+ #endif -+ unsigned char *outStream, UInt32 outSize, -+ UInt32 *outSizeProcessed); -+ -+#endif -Index: linux-2.6.21.7/lib/LzmaDecode.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/lib/LzmaDecode.c -@@ -0,0 +1,663 @@ -+/* -+ LzmaDecode.c -+ LZMA Decoder -+ -+ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) -+ http://www.7-zip.org/ -+ -+ LZMA SDK is licensed under two licenses: -+ 1) GNU Lesser General Public License (GNU LGPL) -+ 2) Common Public License (CPL) -+ It means that you can select one of these two licenses and -+ follow rules of that license. -+ -+ SPECIAL EXCEPTION: -+ Igor Pavlov, as the author of this code, expressly permits you to -+ statically or dynamically link your code (or bind by name) to the -+ interfaces of this file without subjecting your linked code to the -+ terms of the CPL or GNU LGPL. Any modifications or additions -+ to this file, however, are subject to the LGPL or CPL terms. -+*/ -+ -+#include -+ -+#ifndef Byte -+#define Byte unsigned char -+#endif -+ -+#define kNumTopBits 24 -+#define kTopValue ((UInt32)1 << kNumTopBits) -+ -+#define kNumBitModelTotalBits 11 -+#define kBitModelTotal (1 << kNumBitModelTotalBits) -+#define kNumMoveBits 5 -+ -+typedef struct _CRangeDecoder -+{ -+ Byte *Buffer; -+ Byte *BufferLim; -+ UInt32 Range; -+ UInt32 Code; -+ #ifdef _LZMA_IN_CB -+ ILzmaInCallback *InCallback; -+ int Result; -+ #endif -+ int ExtraBytes; -+} CRangeDecoder; -+ -+Byte RangeDecoderReadByte(CRangeDecoder *rd) -+{ -+ if (rd->Buffer == rd->BufferLim) -+ { -+ #ifdef _LZMA_IN_CB -+ UInt32 size; -+ rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size); -+ rd->BufferLim = rd->Buffer + size; -+ if (size == 0) -+ #endif -+ { -+ rd->ExtraBytes = 1; -+ return 0xFF; -+ } -+ } -+ return (*rd->Buffer++); -+} -+ -+/* #define ReadByte (*rd->Buffer++) */ -+#define ReadByte (RangeDecoderReadByte(rd)) -+ -+void RangeDecoderInit(CRangeDecoder *rd, -+ #ifdef _LZMA_IN_CB -+ ILzmaInCallback *inCallback -+ #else -+ Byte *stream, UInt32 bufferSize -+ #endif -+ ) -+{ -+ int i; -+ #ifdef _LZMA_IN_CB -+ rd->InCallback = inCallback; -+ rd->Buffer = rd->BufferLim = 0; -+ #else -+ rd->Buffer = stream; -+ rd->BufferLim = stream + bufferSize; -+ #endif -+ rd->ExtraBytes = 0; -+ rd->Code = 0; -+ rd->Range = (0xFFFFFFFF); -+ for(i = 0; i < 5; i++) -+ rd->Code = (rd->Code << 8) | ReadByte; -+} -+ -+#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code; -+#define RC_FLUSH_VAR rd->Range = range; rd->Code = code; -+#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; } -+ -+UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits) -+{ -+ RC_INIT_VAR -+ UInt32 result = 0; -+ int i; -+ for (i = numTotalBits; i > 0; i--) -+ { -+ /* UInt32 t; */ -+ range >>= 1; -+ -+ result <<= 1; -+ if (code >= range) -+ { -+ code -= range; -+ result |= 1; -+ } -+ /* -+ t = (code - range) >> 31; -+ t &= 1; -+ code -= range & (t - 1); -+ result = (result + result) | (1 - t); -+ */ -+ RC_NORMALIZE -+ } -+ RC_FLUSH_VAR -+ return result; -+} -+ -+int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd) -+{ -+ UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob; -+ if (rd->Code < bound) -+ { -+ rd->Range = bound; -+ *prob += (kBitModelTotal - *prob) >> kNumMoveBits; -+ if (rd->Range < kTopValue) -+ { -+ rd->Code = (rd->Code << 8) | ReadByte; -+ rd->Range <<= 8; -+ } -+ return 0; -+ } -+ else -+ { -+ rd->Range -= bound; -+ rd->Code -= bound; -+ *prob -= (*prob) >> kNumMoveBits; -+ if (rd->Range < kTopValue) -+ { -+ rd->Code = (rd->Code << 8) | ReadByte; -+ rd->Range <<= 8; -+ } -+ return 1; -+ } -+} -+ -+#define RC_GET_BIT2(prob, mi, A0, A1) \ -+ UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \ -+ if (code < bound) \ -+ { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \ -+ else \ -+ { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \ -+ RC_NORMALIZE -+ -+#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;) -+ -+int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) -+{ -+ int mi = 1; -+ int i; -+ #ifdef _LZMA_LOC_OPT -+ RC_INIT_VAR -+ #endif -+ for(i = numLevels; i > 0; i--) -+ { -+ #ifdef _LZMA_LOC_OPT -+ CProb *prob = probs + mi; -+ RC_GET_BIT(prob, mi) -+ #else -+ mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd); -+ #endif -+ } -+ #ifdef _LZMA_LOC_OPT -+ RC_FLUSH_VAR -+ #endif -+ return mi - (1 << numLevels); -+} -+ -+int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) -+{ -+ int mi = 1; -+ int i; -+ int symbol = 0; -+ #ifdef _LZMA_LOC_OPT -+ RC_INIT_VAR -+ #endif -+ for(i = 0; i < numLevels; i++) -+ { -+ #ifdef _LZMA_LOC_OPT -+ CProb *prob = probs + mi; -+ RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i)) -+ #else -+ int bit = RangeDecoderBitDecode(probs + mi, rd); -+ mi = mi + mi + bit; -+ symbol |= (bit << i); -+ #endif -+ } -+ #ifdef _LZMA_LOC_OPT -+ RC_FLUSH_VAR -+ #endif -+ return symbol; -+} -+ -+Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd) -+{ -+ int symbol = 1; -+ #ifdef _LZMA_LOC_OPT -+ RC_INIT_VAR -+ #endif -+ do -+ { -+ #ifdef _LZMA_LOC_OPT -+ CProb *prob = probs + symbol; -+ RC_GET_BIT(prob, symbol) -+ #else -+ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); -+ #endif -+ } -+ while (symbol < 0x100); -+ #ifdef _LZMA_LOC_OPT -+ RC_FLUSH_VAR -+ #endif -+ return symbol; -+} -+ -+Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte) -+{ -+ int symbol = 1; -+ #ifdef _LZMA_LOC_OPT -+ RC_INIT_VAR -+ #endif -+ do -+ { -+ int bit; -+ int matchBit = (matchByte >> 7) & 1; -+ matchByte <<= 1; -+ #ifdef _LZMA_LOC_OPT -+ { -+ CProb *prob = probs + ((1 + matchBit) << 8) + symbol; -+ RC_GET_BIT2(prob, symbol, bit = 0, bit = 1) -+ } -+ #else -+ bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd); -+ symbol = (symbol << 1) | bit; -+ #endif -+ if (matchBit != bit) -+ { -+ while (symbol < 0x100) -+ { -+ #ifdef _LZMA_LOC_OPT -+ CProb *prob = probs + symbol; -+ RC_GET_BIT(prob, symbol) -+ #else -+ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); -+ #endif -+ } -+ break; -+ } -+ } -+ while (symbol < 0x100); -+ #ifdef _LZMA_LOC_OPT -+ RC_FLUSH_VAR -+ #endif -+ return symbol; -+} -+ -+#define kNumPosBitsMax 4 -+#define kNumPosStatesMax (1 << kNumPosBitsMax) -+ -+#define kLenNumLowBits 3 -+#define kLenNumLowSymbols (1 << kLenNumLowBits) -+#define kLenNumMidBits 3 -+#define kLenNumMidSymbols (1 << kLenNumMidBits) -+#define kLenNumHighBits 8 -+#define kLenNumHighSymbols (1 << kLenNumHighBits) -+ -+#define LenChoice 0 -+#define LenChoice2 (LenChoice + 1) -+#define LenLow (LenChoice2 + 1) -+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) -+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) -+#define kNumLenProbs (LenHigh + kLenNumHighSymbols) -+ -+int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState) -+{ -+ if(RangeDecoderBitDecode(p + LenChoice, rd) == 0) -+ return RangeDecoderBitTreeDecode(p + LenLow + -+ (posState << kLenNumLowBits), kLenNumLowBits, rd); -+ if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0) -+ return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid + -+ (posState << kLenNumMidBits), kLenNumMidBits, rd); -+ return kLenNumLowSymbols + kLenNumMidSymbols + -+ RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd); -+} -+ -+#define kNumStates 12 -+ -+#define kStartPosModelIndex 4 -+#define kEndPosModelIndex 14 -+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) -+ -+#define kNumPosSlotBits 6 -+#define kNumLenToPosStates 4 -+ -+#define kNumAlignBits 4 -+#define kAlignTableSize (1 << kNumAlignBits) -+ -+#define kMatchMinLen 2 -+ -+#define IsMatch 0 -+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) -+#define IsRepG0 (IsRep + kNumStates) -+#define IsRepG1 (IsRepG0 + kNumStates) -+#define IsRepG2 (IsRepG1 + kNumStates) -+#define IsRep0Long (IsRepG2 + kNumStates) -+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) -+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) -+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) -+#define LenCoder (Align + kAlignTableSize) -+#define RepLenCoder (LenCoder + kNumLenProbs) -+#define Literal (RepLenCoder + kNumLenProbs) -+ -+#if Literal != LZMA_BASE_SIZE -+StopCompilingDueBUG -+#endif -+ -+#ifdef _LZMA_OUT_READ -+ -+typedef struct _LzmaVarState -+{ -+ CRangeDecoder RangeDecoder; -+ Byte *Dictionary; -+ UInt32 DictionarySize; -+ UInt32 DictionaryPos; -+ UInt32 GlobalPos; -+ UInt32 Reps[4]; -+ int lc; -+ int lp; -+ int pb; -+ int State; -+ int PreviousIsMatch; -+ int RemainLen; -+} LzmaVarState; -+ -+int LzmaDecoderInit( -+ unsigned char *buffer, UInt32 bufferSize, -+ int lc, int lp, int pb, -+ unsigned char *dictionary, UInt32 dictionarySize, -+ #ifdef _LZMA_IN_CB -+ ILzmaInCallback *inCallback -+ #else -+ unsigned char *inStream, UInt32 inSize -+ #endif -+ ) -+{ -+ LzmaVarState *vs = (LzmaVarState *)buffer; -+ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); -+ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); -+ UInt32 i; -+ if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState)) -+ return LZMA_RESULT_NOT_ENOUGH_MEM; -+ vs->Dictionary = dictionary; -+ vs->DictionarySize = dictionarySize; -+ vs->DictionaryPos = 0; -+ vs->GlobalPos = 0; -+ vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1; -+ vs->lc = lc; -+ vs->lp = lp; -+ vs->pb = pb; -+ vs->State = 0; -+ vs->PreviousIsMatch = 0; -+ vs->RemainLen = 0; -+ dictionary[dictionarySize - 1] = 0; -+ for (i = 0; i < numProbs; i++) -+ p[i] = kBitModelTotal >> 1; -+ RangeDecoderInit(&vs->RangeDecoder, -+ #ifdef _LZMA_IN_CB -+ inCallback -+ #else -+ inStream, inSize -+ #endif -+ ); -+ return LZMA_RESULT_OK; -+} -+ -+int LzmaDecode(unsigned char *buffer, -+ unsigned char *outStream, UInt32 outSize, -+ UInt32 *outSizeProcessed) -+{ -+ LzmaVarState *vs = (LzmaVarState *)buffer; -+ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); -+ CRangeDecoder rd = vs->RangeDecoder; -+ int state = vs->State; -+ int previousIsMatch = vs->PreviousIsMatch; -+ Byte previousByte; -+ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; -+ UInt32 nowPos = 0; -+ UInt32 posStateMask = (1 << (vs->pb)) - 1; -+ UInt32 literalPosMask = (1 << (vs->lp)) - 1; -+ int lc = vs->lc; -+ int len = vs->RemainLen; -+ UInt32 globalPos = vs->GlobalPos; -+ -+ Byte *dictionary = vs->Dictionary; -+ UInt32 dictionarySize = vs->DictionarySize; -+ UInt32 dictionaryPos = vs->DictionaryPos; -+ -+ if (len == -1) -+ { -+ *outSizeProcessed = 0; -+ return LZMA_RESULT_OK; -+ } -+ -+ while(len > 0 && nowPos < outSize) -+ { -+ UInt32 pos = dictionaryPos - rep0; -+ if (pos >= dictionarySize) -+ pos += dictionarySize; -+ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; -+ if (++dictionaryPos == dictionarySize) -+ dictionaryPos = 0; -+ len--; -+ } -+ if (dictionaryPos == 0) -+ previousByte = dictionary[dictionarySize - 1]; -+ else -+ previousByte = dictionary[dictionaryPos - 1]; -+#else -+ -+int LzmaDecode( -+ Byte *buffer, UInt32 bufferSize, -+ int lc, int lp, int pb, -+ #ifdef _LZMA_IN_CB -+ ILzmaInCallback *inCallback, -+ #else -+ unsigned char *inStream, UInt32 inSize, -+ #endif -+ unsigned char *outStream, UInt32 outSize, -+ UInt32 *outSizeProcessed) -+{ -+ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); -+ CProb *p = (CProb *)buffer; -+ CRangeDecoder rd; -+ UInt32 i; -+ int state = 0; -+ int previousIsMatch = 0; -+ Byte previousByte = 0; -+ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; -+ UInt32 nowPos = 0; -+ UInt32 posStateMask = (1 << pb) - 1; -+ UInt32 literalPosMask = (1 << lp) - 1; -+ int len = 0; -+ if (bufferSize < numProbs * sizeof(CProb)) -+ return LZMA_RESULT_NOT_ENOUGH_MEM; -+ for (i = 0; i < numProbs; i++) -+ p[i] = kBitModelTotal >> 1; -+ RangeDecoderInit(&rd, -+ #ifdef _LZMA_IN_CB -+ inCallback -+ #else -+ inStream, inSize -+ #endif -+ ); -+#endif -+ -+ *outSizeProcessed = 0; -+ while(nowPos < outSize) -+ { -+ int posState = (int)( -+ (nowPos -+ #ifdef _LZMA_OUT_READ -+ + globalPos -+ #endif -+ ) -+ & posStateMask); -+ #ifdef _LZMA_IN_CB -+ if (rd.Result != LZMA_RESULT_OK) -+ return rd.Result; -+ #endif -+ if (rd.ExtraBytes != 0) -+ return LZMA_RESULT_DATA_ERROR; -+ if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0) -+ { -+ CProb *probs = p + Literal + (LZMA_LIT_SIZE * -+ ((( -+ (nowPos -+ #ifdef _LZMA_OUT_READ -+ + globalPos -+ #endif -+ ) -+ & literalPosMask) << lc) + (previousByte >> (8 - lc)))); -+ -+ if (state < 4) state = 0; -+ else if (state < 10) state -= 3; -+ else state -= 6; -+ if (previousIsMatch) -+ { -+ Byte matchByte; -+ #ifdef _LZMA_OUT_READ -+ UInt32 pos = dictionaryPos - rep0; -+ if (pos >= dictionarySize) -+ pos += dictionarySize; -+ matchByte = dictionary[pos]; -+ #else -+ matchByte = outStream[nowPos - rep0]; -+ #endif -+ previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte); -+ previousIsMatch = 0; -+ } -+ else -+ previousByte = LzmaLiteralDecode(probs, &rd); -+ outStream[nowPos++] = previousByte; -+ #ifdef _LZMA_OUT_READ -+ dictionary[dictionaryPos] = previousByte; -+ if (++dictionaryPos == dictionarySize) -+ dictionaryPos = 0; -+ #endif -+ } -+ else -+ { -+ previousIsMatch = 1; -+ if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1) -+ { -+ if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0) -+ { -+ if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0) -+ { -+ #ifdef _LZMA_OUT_READ -+ UInt32 pos; -+ #endif -+ if ( -+ (nowPos -+ #ifdef _LZMA_OUT_READ -+ + globalPos -+ #endif -+ ) -+ == 0) -+ return LZMA_RESULT_DATA_ERROR; -+ state = state < 7 ? 9 : 11; -+ #ifdef _LZMA_OUT_READ -+ pos = dictionaryPos - rep0; -+ if (pos >= dictionarySize) -+ pos += dictionarySize; -+ previousByte = dictionary[pos]; -+ dictionary[dictionaryPos] = previousByte; -+ if (++dictionaryPos == dictionarySize) -+ dictionaryPos = 0; -+ #else -+ previousByte = outStream[nowPos - rep0]; -+ #endif -+ outStream[nowPos++] = previousByte; -+ continue; -+ } -+ } -+ else -+ { -+ UInt32 distance; -+ if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0) -+ distance = rep1; -+ else -+ { -+ if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0) -+ distance = rep2; -+ else -+ { -+ distance = rep3; -+ rep3 = rep2; -+ } -+ rep2 = rep1; -+ } -+ rep1 = rep0; -+ rep0 = distance; -+ } -+ len = LzmaLenDecode(p + RepLenCoder, &rd, posState); -+ state = state < 7 ? 8 : 11; -+ } -+ else -+ { -+ int posSlot; -+ rep3 = rep2; -+ rep2 = rep1; -+ rep1 = rep0; -+ state = state < 7 ? 7 : 10; -+ len = LzmaLenDecode(p + LenCoder, &rd, posState); -+ posSlot = RangeDecoderBitTreeDecode(p + PosSlot + -+ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << -+ kNumPosSlotBits), kNumPosSlotBits, &rd); -+ if (posSlot >= kStartPosModelIndex) -+ { -+ int numDirectBits = ((posSlot >> 1) - 1); -+ rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits); -+ if (posSlot < kEndPosModelIndex) -+ { -+ rep0 += RangeDecoderReverseBitTreeDecode( -+ p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd); -+ } -+ else -+ { -+ rep0 += RangeDecoderDecodeDirectBits(&rd, -+ numDirectBits - kNumAlignBits) << kNumAlignBits; -+ rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd); -+ } -+ } -+ else -+ rep0 = posSlot; -+ rep0++; -+ } -+ if (rep0 == (UInt32)(0)) -+ { -+ /* it's for stream version */ -+ len = -1; -+ break; -+ } -+ if (rep0 > nowPos -+ #ifdef _LZMA_OUT_READ -+ + globalPos -+ #endif -+ ) -+ { -+ return LZMA_RESULT_DATA_ERROR; -+ } -+ len += kMatchMinLen; -+ do -+ { -+ #ifdef _LZMA_OUT_READ -+ UInt32 pos = dictionaryPos - rep0; -+ if (pos >= dictionarySize) -+ pos += dictionarySize; -+ previousByte = dictionary[pos]; -+ dictionary[dictionaryPos] = previousByte; -+ if (++dictionaryPos == dictionarySize) -+ dictionaryPos = 0; -+ #else -+ previousByte = outStream[nowPos - rep0]; -+ #endif -+ outStream[nowPos++] = previousByte; -+ len--; -+ } -+ while(len > 0 && nowPos < outSize); -+ } -+ } -+ -+ #ifdef _LZMA_OUT_READ -+ vs->RangeDecoder = rd; -+ vs->DictionaryPos = dictionaryPos; -+ vs->GlobalPos = globalPos + nowPos; -+ vs->Reps[0] = rep0; -+ vs->Reps[1] = rep1; -+ vs->Reps[2] = rep2; -+ vs->Reps[3] = rep3; -+ vs->State = state; -+ vs->PreviousIsMatch = previousIsMatch; -+ vs->RemainLen = len; -+ #endif -+ -+ *outSizeProcessed = nowPos; -+ return LZMA_RESULT_OK; -+} -Index: linux-2.6.21.7/lib/Makefile -=================================================================== ---- linux-2.6.21.7.orig/lib/Makefile -+++ linux-2.6.21.7/lib/Makefile -@@ -12,7 +12,7 @@ lib-$(CONFIG_SMP) += cpumask.o - - lib-y += kobject.o kref.o kobject_uevent.o klist.o - --obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o -+obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o LzmaDecode.o - - ifeq ($(CONFIG_DEBUG_KOBJECT),y) - CFLAGS_kobject.o += -DDEBUG -@@ -56,6 +56,7 @@ obj-$(CONFIG_SMP) += percpu_counter.o - obj-$(CONFIG_AUDIT_GENERIC) += audit.o - - obj-$(CONFIG_SWIOTLB) += swiotlb.o -+ - obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o - - lib-$(CONFIG_GENERIC_BUG) += bug.o diff --git a/target/linux/generic-2.6/patches/003-squashfs_lzma.patch b/target/linux/generic-2.6/patches/003-squashfs_lzma.patch deleted file mode 100644 index e476c76162..0000000000 --- a/target/linux/generic-2.6/patches/003-squashfs_lzma.patch +++ /dev/null @@ -1,109 +0,0 @@ -Index: linux-2.6.21.7/fs/squashfs/inode.c -=================================================================== ---- linux-2.6.21.7.orig/fs/squashfs/inode.c -+++ linux-2.6.21.7/fs/squashfs/inode.c -@@ -4,6 +4,9 @@ - * Copyright (c) 2002, 2003, 2004, 2005, 2006 - * Phillip Lougher - * -+ * LZMA decompressor support added by Oleg I. Vdovikin -+ * Copyright (c) 2005 Oleg I.Vdovikin -+ * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, -@@ -21,6 +24,7 @@ - * inode.c - */ - -+#define SQUASHFS_LZMA - #include - #include - #include -@@ -44,6 +48,19 @@ - - #include "squashfs.h" - -+#ifdef SQUASHFS_LZMA -+#include -+ -+/* default LZMA settings, should be in sync with mksquashfs */ -+#define LZMA_LC 3 -+#define LZMA_LP 0 -+#define LZMA_PB 2 -+ -+#define LZMA_WORKSPACE_SIZE ((LZMA_BASE_SIZE + \ -+ (LZMA_LIT_SIZE << (LZMA_LC + LZMA_LP))) * sizeof(CProb)) -+ -+#endif -+ - static void squashfs_put_super(struct super_block *); - static int squashfs_statfs(struct dentry *, struct kstatfs *); - static int squashfs_symlink_readpage(struct file *file, struct page *page); -@@ -64,7 +81,11 @@ static int squashfs_get_sb(struct file_s - const char *, void *, struct vfsmount *); - - -+#ifdef SQUASHFS_LZMA -+static unsigned char lzma_workspace[LZMA_WORKSPACE_SIZE]; -+#else - static z_stream stream; -+#endif - - static struct file_system_type squashfs_fs_type = { - .owner = THIS_MODULE, -@@ -249,6 +270,15 @@ SQSH_EXTERN unsigned int squashfs_read_d - if (compressed) { - int zlib_err; - -+#ifdef SQUASHFS_LZMA -+ if ((zlib_err = LzmaDecode(lzma_workspace, -+ LZMA_WORKSPACE_SIZE, LZMA_LC, LZMA_LP, LZMA_PB, -+ c_buffer, c_byte, buffer, msblk->read_size, &bytes)) != LZMA_RESULT_OK) -+ { -+ ERROR("lzma returned unexpected result 0x%x\n", zlib_err); -+ bytes = 0; -+ } -+#else - stream.next_in = c_buffer; - stream.avail_in = c_byte; - stream.next_out = buffer; -@@ -263,7 +293,7 @@ SQSH_EXTERN unsigned int squashfs_read_d - bytes = 0; - } else - bytes = stream.total_out; -- -+#endif - up(&msblk->read_data_mutex); - } - -@@ -2045,15 +2075,19 @@ static int __init init_squashfs_fs(void) - printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " - "Phillip Lougher\n"); - -+#ifndef SQUASHFS_LZMA - if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { - ERROR("Failed to allocate zlib workspace\n"); - destroy_inodecache(); - err = -ENOMEM; - goto out; - } -+#endif - - if ((err = register_filesystem(&squashfs_fs_type))) { -+#ifndef SQUASHFS_LZMA - vfree(stream.workspace); -+#endif - destroy_inodecache(); - } - -@@ -2064,7 +2098,9 @@ out: - - static void __exit exit_squashfs_fs(void) - { -+#ifndef SQUASHFS_LZMA - vfree(stream.workspace); -+#endif - unregister_filesystem(&squashfs_fs_type); - destroy_inodecache(); - } diff --git a/target/linux/generic-2.6/patches/004-extra_optimization.patch b/target/linux/generic-2.6/patches/004-extra_optimization.patch deleted file mode 100644 index 3a08e13431..0000000000 --- a/target/linux/generic-2.6/patches/004-extra_optimization.patch +++ /dev/null @@ -1,14 +0,0 @@ -Index: linux-2.6.21.7/Makefile -=================================================================== ---- linux-2.6.21.7.orig/Makefile -+++ linux-2.6.21.7/Makefile -@@ -507,6 +507,9 @@ CFLAGS += $(call cc-option, -fn - NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) - CHECKFLAGS += $(NOSTDINC_FLAGS) - -+# improve gcc optimization -+CFLAGS += $(call cc-option,-funit-at-a-time,) -+ - # warn about C99 declaration after statement - CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) - diff --git a/target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch b/target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch deleted file mode 100644 index 2c147b5e45..0000000000 --- a/target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: linux-2.6.21.7/include/asm-mips/system.h -=================================================================== ---- linux-2.6.21.7.orig/include/asm-mips/system.h -+++ linux-2.6.21.7/include/asm-mips/system.h -@@ -188,7 +188,7 @@ extern __u64 __xchg_u64_unsupported_on_3 - if something tries to do an invalid xchg(). */ - extern void __xchg_called_with_bad_pointer(void); - --static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) -+static __always_inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) - { - switch (size) { - case 4: diff --git a/target/linux/generic-2.6/patches/007-samsung_flash.patch b/target/linux/generic-2.6/patches/007-samsung_flash.patch deleted file mode 100644 index 6878633913..0000000000 --- a/target/linux/generic-2.6/patches/007-samsung_flash.patch +++ /dev/null @@ -1,38 +0,0 @@ -Index: linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0002.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/mtd/chips/cfi_cmdset_0002.c -+++ linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0002.c -@@ -51,6 +51,7 @@ - #define SST49LF040B 0x0050 - #define SST49LF008A 0x005a - #define AT49BV6416 0x00d6 -+#define MANUFACTURER_SAMSUNG 0x00ec - - static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); - static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); -@@ -294,12 +295,19 @@ struct mtd_info *cfi_cmdset_0002(struct - - if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { -- printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " -- "version %c.%c.\n", extp->MajorVersion, -- extp->MinorVersion); -- kfree(extp); -- kfree(mtd); -- return NULL; -+ if (cfi->mfr == MANUFACTURER_SAMSUNG && -+ (extp->MajorVersion == '3' && extp->MinorVersion == '3')) { -+ printk(KERN_NOTICE " Newer Samsung flash detected, " -+ "should be compatibile with Amd/Fujitsu.\n"); -+ } -+ else { -+ printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " -+ "version %c.%c.\n", extp->MajorVersion, -+ extp->MinorVersion); -+ kfree(extp); -+ kfree(mtd); -+ return NULL; -+ } - } - - /* Install our own private info structure */ diff --git a/target/linux/generic-2.6/patches/009-revert_intel_flash_breakage.patch b/target/linux/generic-2.6/patches/009-revert_intel_flash_breakage.patch deleted file mode 100644 index 8c7684a5fb..0000000000 --- a/target/linux/generic-2.6/patches/009-revert_intel_flash_breakage.patch +++ /dev/null @@ -1,171 +0,0 @@ -Index: linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0001.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/mtd/chips/cfi_cmdset_0001.c -+++ linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0001.c -@@ -919,7 +919,7 @@ static void __xipram xip_enable(struct m - - static int __xipram xip_wait_for_operation( - struct map_info *map, struct flchip *chip, -- unsigned long adr, unsigned int chip_op_time ) -+ unsigned long adr, int *chip_op_time ) - { - struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *cfip = cfi->cmdset_priv; -@@ -928,7 +928,7 @@ static int __xipram xip_wait_for_operati - flstate_t oldstate, newstate; - - start = xip_currtime(); -- usec = chip_op_time * 8; -+ usec = *chip_op_time * 8; - if (usec == 0) - usec = 500000; - done = 0; -@@ -1038,8 +1038,8 @@ static int __xipram xip_wait_for_operati - #define XIP_INVAL_CACHED_RANGE(map, from, size) \ - INVALIDATE_CACHED_RANGE(map, from, size) - --#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, usec) \ -- xip_wait_for_operation(map, chip, cmd_adr, usec) -+#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, p_usec) \ -+ xip_wait_for_operation(map, chip, cmd_adr, p_usec) - - #else - -@@ -1051,65 +1051,65 @@ static int __xipram xip_wait_for_operati - static int inval_cache_and_wait_for_operation( - struct map_info *map, struct flchip *chip, - unsigned long cmd_adr, unsigned long inval_adr, int inval_len, -- unsigned int chip_op_time) -+ int *chip_op_time ) - { - struct cfi_private *cfi = map->fldrv_priv; - map_word status, status_OK = CMD(0x80); -- int chip_state = chip->state; -- unsigned int timeo, sleep_time; -+ int z, chip_state = chip->state; -+ unsigned long timeo; - - spin_unlock(chip->mutex); - if (inval_len) - INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); -+ if (*chip_op_time) -+ cfi_udelay(*chip_op_time); - spin_lock(chip->mutex); - -- /* set our timeout to 8 times the expected delay */ -- timeo = chip_op_time * 8; -- if (!timeo) -- timeo = 500000; -- sleep_time = chip_op_time / 2; -+ timeo = *chip_op_time * 8 * HZ / 1000000; -+ if (timeo < HZ/2) -+ timeo = HZ/2; -+ timeo += jiffies; - -+ z = 0; - for (;;) { -+ if (chip->state != chip_state) { -+ /* Someone's suspended the operation: sleep */ -+ DECLARE_WAITQUEUE(wait, current); -+ -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ add_wait_queue(&chip->wq, &wait); -+ spin_unlock(chip->mutex); -+ schedule(); -+ remove_wait_queue(&chip->wq, &wait); -+ timeo = jiffies + (HZ / 2); /* FIXME */ -+ spin_lock(chip->mutex); -+ continue; -+ } -+ - status = map_read(map, cmd_adr); - if (map_word_andequal(map, status, status_OK, status_OK)) - break; - -- if (!timeo) { -+ /* OK Still waiting */ -+ if (time_after(jiffies, timeo)) { - map_write(map, CMD(0x70), cmd_adr); - chip->state = FL_STATUS; - return -ETIME; - } - -- /* OK Still waiting. Drop the lock, wait a while and retry. */ -+ /* Latency issues. Drop the lock, wait a while and retry */ -+ z++; - spin_unlock(chip->mutex); -- if (sleep_time >= 1000000/HZ) { -- /* -- * Half of the normal delay still remaining -- * can be performed with a sleeping delay instead -- * of busy waiting. -- */ -- msleep(sleep_time/1000); -- timeo -= sleep_time; -- sleep_time = 1000000/HZ; -- } else { -- udelay(1); -- cond_resched(); -- timeo--; -- } -+ cfi_udelay(1); - spin_lock(chip->mutex); -- -- while (chip->state != chip_state) { -- /* Someone's suspended the operation: sleep */ -- DECLARE_WAITQUEUE(wait, current); -- set_current_state(TASK_UNINTERRUPTIBLE); -- add_wait_queue(&chip->wq, &wait); -- spin_unlock(chip->mutex); -- schedule(); -- remove_wait_queue(&chip->wq, &wait); -- spin_lock(chip->mutex); -- } - } - -+ if (!z) { -+ if (!--(*chip_op_time)) -+ *chip_op_time = 1; -+ } else if (z > 1) -+ ++(*chip_op_time); -+ - /* Done and happy. */ - chip->state = FL_STATUS; - return 0; -@@ -1118,7 +1118,8 @@ static int inval_cache_and_wait_for_oper - #endif - - #define WAIT_TIMEOUT(map, chip, adr, udelay) \ -- INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay); -+ ({ int __udelay = (udelay); \ -+ INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, &__udelay); }) - - - static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) -@@ -1342,7 +1343,7 @@ static int __xipram do_write_oneword(str - - ret = INVAL_CACHE_AND_WAIT(map, chip, adr, - adr, map_bankwidth(map), -- chip->word_write_time); -+ &chip->word_write_time); - if (ret) { - xip_enable(map, chip, adr); - printk(KERN_ERR "%s: word write error (status timeout)\n", map->name); -@@ -1579,7 +1580,7 @@ static int __xipram do_write_buffer(stru - - ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, - adr, len, -- chip->buffer_write_time); -+ &chip->buffer_write_time); - if (ret) { - map_write(map, CMD(0x70), cmd_adr); - chip->state = FL_STATUS; -@@ -1714,7 +1715,7 @@ static int __xipram do_erase_oneblock(st - - ret = INVAL_CACHE_AND_WAIT(map, chip, adr, - adr, len, -- chip->erase_time); -+ &chip->erase_time); - if (ret) { - map_write(map, CMD(0x70), adr); - chip->state = FL_STATUS; diff --git a/target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch b/target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch deleted file mode 100644 index 55498617af..0000000000 --- a/target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch +++ /dev/null @@ -1,23 +0,0 @@ -Index: linux-2.6.21.7/fs/squashfs/Makefile -=================================================================== ---- linux-2.6.21.7.orig/fs/squashfs/Makefile -+++ linux-2.6.21.7/fs/squashfs/Makefile -@@ -4,4 +4,3 @@ - - obj-$(CONFIG_SQUASHFS) += squashfs.o - squashfs-y += inode.o --squashfs-y += squashfs2_0.o -Index: linux-2.6.21.7/fs/squashfs/squashfs.h -=================================================================== ---- linux-2.6.21.7.orig/fs/squashfs/squashfs.h -+++ linux-2.6.21.7/fs/squashfs/squashfs.h -@@ -24,6 +24,9 @@ - #ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY - #undef CONFIG_SQUASHFS_1_0_COMPATIBILITY - #endif -+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+#undef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+#endif - - #ifdef SQUASHFS_TRACE - #define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) diff --git a/target/linux/generic-2.6/patches/011-mips_boot.patch b/target/linux/generic-2.6/patches/011-mips_boot.patch deleted file mode 100644 index 866b3f01ef..0000000000 --- a/target/linux/generic-2.6/patches/011-mips_boot.patch +++ /dev/null @@ -1,21 +0,0 @@ -Index: linux-2.6.21.7/arch/mips/kernel/head.S -=================================================================== ---- linux-2.6.21.7.orig/arch/mips/kernel/head.S -+++ linux-2.6.21.7/arch/mips/kernel/head.S -@@ -129,11 +129,15 @@ - #endif - .endm - -+ -+ j kernel_entry -+ nop -+ - /* - * Reserved space for exception handlers. - * Necessary for machines which link their kernels at KSEG0. - */ -- .fill 0x400 -+ .align 10 - - EXPORT(stext) # used for profiling - EXPORT(_stext) diff --git a/target/linux/generic-2.6/patches/012-mips_cpu_tlb.patch b/target/linux/generic-2.6/patches/012-mips_cpu_tlb.patch deleted file mode 100644 index 305cef0e3f..0000000000 --- a/target/linux/generic-2.6/patches/012-mips_cpu_tlb.patch +++ /dev/null @@ -1,20 +0,0 @@ -Index: linux-2.6.21.7/arch/mips/mm/tlbex.c -=================================================================== ---- linux-2.6.21.7.orig/arch/mips/mm/tlbex.c -+++ linux-2.6.21.7/arch/mips/mm/tlbex.c -@@ -887,7 +887,6 @@ static __init void build_tlb_write_entry - case CPU_R10000: - case CPU_R12000: - case CPU_R14000: -- case CPU_4KC: - case CPU_SB1: - case CPU_SB1A: - case CPU_4KSC: -@@ -915,6 +914,7 @@ static __init void build_tlb_write_entry - tlbw(p); - break; - -+ case CPU_4KC: - case CPU_4KEC: - case CPU_24K: - case CPU_34K: diff --git a/target/linux/generic-2.6/patches/013-mips_generic_gpio_support.patch b/target/linux/generic-2.6/patches/013-mips_generic_gpio_support.patch deleted file mode 100644 index c23519e378..0000000000 --- a/target/linux/generic-2.6/patches/013-mips_generic_gpio_support.patch +++ /dev/null @@ -1,38 +0,0 @@ -Index: linux-2.6.21.7/arch/mips/defconfig -=================================================================== ---- linux-2.6.21.7.orig/arch/mips/defconfig -+++ linux-2.6.21.7/arch/mips/defconfig -@@ -69,6 +69,7 @@ CONFIG_GENERIC_FIND_NEXT_BIT=y - CONFIG_GENERIC_HWEIGHT=y - CONFIG_GENERIC_CALIBRATE_DELAY=y - CONFIG_GENERIC_TIME=y -+CONFIG_GENERIC_GPIO=n - CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y - # CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set - CONFIG_ARC=y -Index: linux-2.6.21.7/arch/mips/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/arch/mips/Kconfig -+++ linux-2.6.21.7/arch/mips/Kconfig -@@ -869,6 +869,10 @@ config GENERIC_TIME - bool - default y - -+config GENERIC_GPIO -+ bool -+ default n -+ - config SCHED_NO_NO_OMIT_FRAME_POINTER - bool - default y -Index: linux-2.6.21.7/include/asm-mips/gpio.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/asm-mips/gpio.h -@@ -0,0 +1,6 @@ -+#ifndef _ASM_MIPS_GPIO_H -+#define _ASM_MIPS_GPIO_H -+ -+#include -+ -+#endif /* _ASM_MIPS_GPIO_H */ diff --git a/target/linux/generic-2.6/patches/060-rootfs_split.patch b/target/linux/generic-2.6/patches/060-rootfs_split.patch deleted file mode 100644 index 210360600e..0000000000 --- a/target/linux/generic-2.6/patches/060-rootfs_split.patch +++ /dev/null @@ -1,492 +0,0 @@ -Index: linux-2.6.21.7/drivers/mtd/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/drivers/mtd/Kconfig -+++ linux-2.6.21.7/drivers/mtd/Kconfig -@@ -49,6 +49,16 @@ config MTD_PARTITIONS - devices. Partitioning on NFTL 'devices' is a different - that's the - 'normal' form of partitioning used on a block device. - -+config MTD_ROOTFS_ROOT_DEV -+ bool "Automatically set 'rootfs' partition to be root filesystem" -+ depends on MTD_PARTITIONS -+ default y -+ -+config MTD_ROOTFS_SPLIT -+ bool "Automatically split 'rootfs' partition for squashfs" -+ depends on MTD_PARTITIONS -+ default y -+ - config MTD_REDBOOT_PARTS - tristate "RedBoot partition table parsing" - depends on MTD_PARTITIONS -Index: linux-2.6.21.7/drivers/mtd/mtdpart.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/mtd/mtdpart.c -+++ linux-2.6.21.7/drivers/mtd/mtdpart.c -@@ -20,6 +20,8 @@ - #include - #include - #include -+#include -+#include - - /* Our partition linked list */ - static LIST_HEAD(mtd_partitions); -@@ -308,6 +310,266 @@ int del_mtd_partitions(struct mtd_info * - return 0; - } - -+static u_int32_t cur_offset = 0; -+static int add_one_partition(struct mtd_info *master, const struct mtd_partition *part, -+ int i, struct mtd_part **slp) -+{ -+ struct mtd_part *slave; -+ -+ /* allocate the partition structure */ -+ slave = kzalloc (sizeof(*slave), GFP_KERNEL); -+ if (!slave) { -+ printk ("memory allocation error while creating partitions for \"%s\"\n", -+ master->name); -+ del_mtd_partitions(master); -+ return -ENOMEM; -+ } -+ list_add(&slave->list, &mtd_partitions); -+ -+ /* set up the MTD object for this partition */ -+ slave->mtd.type = master->type; -+ slave->mtd.flags = master->flags & ~part->mask_flags; -+ slave->mtd.size = part->size; -+ slave->mtd.writesize = master->writesize; -+ slave->mtd.oobsize = master->oobsize; -+ slave->mtd.oobavail = master->oobavail; -+ slave->mtd.subpage_sft = master->subpage_sft; -+ -+ slave->mtd.name = part->name; -+ slave->mtd.bank_size = master->bank_size; -+ slave->mtd.owner = master->owner; -+ -+ slave->mtd.read = part_read; -+ slave->mtd.write = part_write; -+ -+ if(master->point && master->unpoint){ -+ slave->mtd.point = part_point; -+ slave->mtd.unpoint = part_unpoint; -+ } -+ -+ if (master->read_oob) -+ slave->mtd.read_oob = part_read_oob; -+ if (master->write_oob) -+ slave->mtd.write_oob = part_write_oob; -+ if(master->read_user_prot_reg) -+ slave->mtd.read_user_prot_reg = part_read_user_prot_reg; -+ if(master->read_fact_prot_reg) -+ slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; -+ if(master->write_user_prot_reg) -+ slave->mtd.write_user_prot_reg = part_write_user_prot_reg; -+ if(master->lock_user_prot_reg) -+ slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; -+ if(master->get_user_prot_info) -+ slave->mtd.get_user_prot_info = part_get_user_prot_info; -+ if(master->get_fact_prot_info) -+ slave->mtd.get_fact_prot_info = part_get_fact_prot_info; -+ if (master->sync) -+ slave->mtd.sync = part_sync; -+ if (!i && master->suspend && master->resume) { -+ slave->mtd.suspend = part_suspend; -+ slave->mtd.resume = part_resume; -+ } -+ if (master->writev) -+ slave->mtd.writev = part_writev; -+ if (master->lock) -+ slave->mtd.lock = part_lock; -+ if (master->unlock) -+ slave->mtd.unlock = part_unlock; -+ if (master->block_isbad) -+ slave->mtd.block_isbad = part_block_isbad; -+ if (master->block_markbad) -+ slave->mtd.block_markbad = part_block_markbad; -+ slave->mtd.erase = part_erase; -+ slave->master = master; -+ slave->offset = part->offset; -+ slave->index = i; -+ -+ if (slave->offset == MTDPART_OFS_APPEND) -+ slave->offset = cur_offset; -+ if (slave->offset == MTDPART_OFS_NXTBLK) { -+ slave->offset = cur_offset; -+ if ((cur_offset % master->erasesize) != 0) { -+ /* Round up to next erasesize */ -+ slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; -+ printk(KERN_NOTICE "Moving partition %d: " -+ "0x%08x -> 0x%08x\n", i, -+ cur_offset, slave->offset); -+ } -+ } -+ if (slave->mtd.size == MTDPART_SIZ_FULL) -+ slave->mtd.size = master->size - slave->offset; -+ cur_offset = slave->offset + slave->mtd.size; -+ -+ printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, -+ slave->offset + slave->mtd.size, slave->mtd.name); -+ -+ /* let's do some sanity checks */ -+ if (slave->offset >= master->size) { -+ /* let's register it anyway to preserve ordering */ -+ slave->offset = 0; -+ slave->mtd.size = 0; -+ printk ("mtd: partition \"%s\" is out of reach -- disabled\n", -+ part->name); -+ } -+ if (slave->offset + slave->mtd.size > master->size) { -+ slave->mtd.size = master->size - slave->offset; -+ printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", -+ part->name, master->name, slave->mtd.size); -+ } -+ if (master->numeraseregions>1) { -+ /* Deal with variable erase size stuff */ -+ int i; -+ struct mtd_erase_region_info *regions = master->eraseregions; -+ -+ /* Find the first erase regions which is part of this partition. */ -+ for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) -+ ; -+ -+ for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { -+ if (slave->mtd.erasesize < regions[i].erasesize) { -+ slave->mtd.erasesize = regions[i].erasesize; -+ } -+ } -+ } else { -+ /* Single erase size */ -+ slave->mtd.erasesize = master->erasesize; -+ } -+ -+ if ((slave->mtd.flags & MTD_WRITEABLE) && -+ (slave->offset % slave->mtd.erasesize)) { -+ /* Doesn't start on a boundary of major erase size */ -+ /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ -+ slave->mtd.flags &= ~MTD_WRITEABLE; -+ printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", -+ part->name); -+ } -+ if ((slave->mtd.flags & MTD_WRITEABLE) && -+ (slave->mtd.size % slave->mtd.erasesize)) { -+ slave->mtd.flags &= ~MTD_WRITEABLE; -+ printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", -+ part->name); -+ } -+ -+ slave->mtd.ecclayout = master->ecclayout; -+ if (master->block_isbad) { -+ uint32_t offs = 0; -+ -+ while(offs < slave->mtd.size) { -+ if (master->block_isbad(master, -+ offs + slave->offset)) -+ slave->mtd.ecc_stats.badblocks++; -+ offs += slave->mtd.erasesize; -+ } -+ } -+ -+ if(part->mtdp) -+ { /* store the object pointer (caller may or may not register it */ -+ *part->mtdp = &slave->mtd; -+ slave->registered = 0; -+ } -+ else -+ { -+ /* register our partition */ -+ add_mtd_device(&slave->mtd); -+ slave->registered = 1; -+ } -+ -+ if (slp) -+ *slp = slave; -+ -+ return 0; -+} -+ -+#ifdef CONFIG_MTD_ROOTFS_SPLIT -+#define ROOTFS_SPLIT_NAME "rootfs_data" -+static int split_squashfs(struct mtd_info *master, struct mtd_partition *old, -+ struct mtd_partition **new) -+{ -+ struct mtd_partition *part = NULL; -+ int len; -+ char buf[512]; -+ struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; -+ int ret; -+ -+ ret = master->read(master, old->offset, sizeof(*sb), &len, buf); -+ if (ret) { -+ printk(KERN_ALERT "split_squashfs: error occured while reading " -+ "from \"%s\"\n", master->name); -+ goto out; -+ } -+ -+ if (len != sizeof(*sb)) { -+ printk(KERN_ALERT "split_squashfs: unable to read superblock " -+ "from \"%s\"\n", master->name); -+ ret=-1; -+ goto out; -+ } -+ -+ if (*((u32 *) buf) != SQUASHFS_MAGIC) { -+ printk(KERN_ALERT "split_squasfs: no squashfs found in \"%s\"\n", -+ master->name); -+ ret=0; -+ goto out; -+ } -+ -+ if (sb->bytes_used <= 0) { -+ printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n", -+ master->name); -+ ret=0; -+ goto out; -+ } -+ -+ part = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL); -+ if (part == NULL) { -+ printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n", -+ ROOTFS_SPLIT_NAME); -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ memcpy(part, old, sizeof(*part)); -+ part->name = (unsigned char *)&part[1]; -+ strcpy(part->name, ROOTFS_SPLIT_NAME); -+ -+ len = (u32) sb->bytes_used; -+ len += (part->offset & 0x000fffff); -+ len += (master->erasesize - 1); -+ len &= ~(master->erasesize - 1); -+ len -= (part->offset & 0x000fffff); -+ part->offset += len; -+ part->size -= len; -+ -+ ret = 0; -+ -+out: -+ *new = part; -+ return ret; -+} -+ -+static int split_rootfs_data(struct mtd_info *master, struct mtd_partition *part, -+ int index) -+{ -+ struct mtd_partition *dpart; -+ int ret; -+ -+ ret = split_squashfs(master, part, &dpart); -+ if (ret) -+ return ret; -+ -+ if (dpart == NULL) -+ return 1; -+ -+ printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%X, len=%X \n", -+ ROOTFS_SPLIT_NAME, dpart->offset, dpart->size); -+ -+ ret = add_one_partition(master, dpart, index, NULL); -+ if (ret) -+ kfree(dpart); -+ -+ return ret; -+} -+#endif /* CONFIG_MTD_ROOTFS_SPLIT */ -+ - /* - * This function, given a master MTD object and a partition table, creates - * and registers slave MTD objects which are bound to the master according to -@@ -320,169 +582,31 @@ int add_mtd_partitions(struct mtd_info * - int nbparts) - { - struct mtd_part *slave; -- u_int32_t cur_offset = 0; -- int i; -+ struct mtd_partition *part; -+ int i, j, ret = 0; - - printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); - -- for (i = 0; i < nbparts; i++) { -- -- /* allocate the partition structure */ -- slave = kzalloc (sizeof(*slave), GFP_KERNEL); -- if (!slave) { -- printk ("memory allocation error while creating partitions for \"%s\"\n", -- master->name); -- del_mtd_partitions(master); -- return -ENOMEM; -- } -- list_add(&slave->list, &mtd_partitions); -- -- /* set up the MTD object for this partition */ -- slave->mtd.type = master->type; -- slave->mtd.flags = master->flags & ~parts[i].mask_flags; -- slave->mtd.size = parts[i].size; -- slave->mtd.writesize = master->writesize; -- slave->mtd.oobsize = master->oobsize; -- slave->mtd.oobavail = master->oobavail; -- slave->mtd.subpage_sft = master->subpage_sft; -- -- slave->mtd.name = parts[i].name; -- slave->mtd.bank_size = master->bank_size; -- slave->mtd.owner = master->owner; -- -- slave->mtd.read = part_read; -- slave->mtd.write = part_write; -- -- if(master->point && master->unpoint){ -- slave->mtd.point = part_point; -- slave->mtd.unpoint = part_unpoint; -- } -- -- if (master->read_oob) -- slave->mtd.read_oob = part_read_oob; -- if (master->write_oob) -- slave->mtd.write_oob = part_write_oob; -- if(master->read_user_prot_reg) -- slave->mtd.read_user_prot_reg = part_read_user_prot_reg; -- if(master->read_fact_prot_reg) -- slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; -- if(master->write_user_prot_reg) -- slave->mtd.write_user_prot_reg = part_write_user_prot_reg; -- if(master->lock_user_prot_reg) -- slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; -- if(master->get_user_prot_info) -- slave->mtd.get_user_prot_info = part_get_user_prot_info; -- if(master->get_fact_prot_info) -- slave->mtd.get_fact_prot_info = part_get_fact_prot_info; -- if (master->sync) -- slave->mtd.sync = part_sync; -- if (!i && master->suspend && master->resume) { -- slave->mtd.suspend = part_suspend; -- slave->mtd.resume = part_resume; -- } -- if (master->writev) -- slave->mtd.writev = part_writev; -- if (master->lock) -- slave->mtd.lock = part_lock; -- if (master->unlock) -- slave->mtd.unlock = part_unlock; -- if (master->block_isbad) -- slave->mtd.block_isbad = part_block_isbad; -- if (master->block_markbad) -- slave->mtd.block_markbad = part_block_markbad; -- slave->mtd.erase = part_erase; -- slave->master = master; -- slave->offset = parts[i].offset; -- slave->index = i; -- -- if (slave->offset == MTDPART_OFS_APPEND) -- slave->offset = cur_offset; -- if (slave->offset == MTDPART_OFS_NXTBLK) { -- slave->offset = cur_offset; -- if ((cur_offset % master->erasesize) != 0) { -- /* Round up to next erasesize */ -- slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; -- printk(KERN_NOTICE "Moving partition %d: " -- "0x%08x -> 0x%08x\n", i, -- cur_offset, slave->offset); -- } -- } -- if (slave->mtd.size == MTDPART_SIZ_FULL) -- slave->mtd.size = master->size - slave->offset; -- cur_offset = slave->offset + slave->mtd.size; -- -- printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, -- slave->offset + slave->mtd.size, slave->mtd.name); -- -- /* let's do some sanity checks */ -- if (slave->offset >= master->size) { -- /* let's register it anyway to preserve ordering */ -- slave->offset = 0; -- slave->mtd.size = 0; -- printk ("mtd: partition \"%s\" is out of reach -- disabled\n", -- parts[i].name); -- } -- if (slave->offset + slave->mtd.size > master->size) { -- slave->mtd.size = master->size - slave->offset; -- printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", -- parts[i].name, master->name, slave->mtd.size); -- } -- if (master->numeraseregions>1) { -- /* Deal with variable erase size stuff */ -- int i; -- struct mtd_erase_region_info *regions = master->eraseregions; -- -- /* Find the first erase regions which is part of this partition. */ -- for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) -- ; -- -- for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { -- if (slave->mtd.erasesize < regions[i].erasesize) { -- slave->mtd.erasesize = regions[i].erasesize; -- } -- } -- } else { -- /* Single erase size */ -- slave->mtd.erasesize = master->erasesize; -- } -- -- if ((slave->mtd.flags & MTD_WRITEABLE) && -- (slave->offset % slave->mtd.erasesize)) { -- /* Doesn't start on a boundary of major erase size */ -- /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ -- slave->mtd.flags &= ~MTD_WRITEABLE; -- printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", -- parts[i].name); -- } -- if ((slave->mtd.flags & MTD_WRITEABLE) && -- (slave->mtd.size % slave->mtd.erasesize)) { -- slave->mtd.flags &= ~MTD_WRITEABLE; -- printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", -- parts[i].name); -- } -- -- slave->mtd.ecclayout = master->ecclayout; -- if (master->block_isbad) { -- uint32_t offs = 0; -- -- while(offs < slave->mtd.size) { -- if (master->block_isbad(master, -- offs + slave->offset)) -- slave->mtd.ecc_stats.badblocks++; -- offs += slave->mtd.erasesize; -+ for (i = 0, j = 0; i < nbparts; i++) { -+ part = (struct mtd_partition *) &parts[i]; -+ ret = add_one_partition(master, part, j, &slave); -+ if (ret) -+ return ret; -+ j++; -+ -+ if (strcmp(part->name, "rootfs") == 0 && slave->registered) { -+#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV -+ if (ROOT_DEV == 0) { -+ printk(KERN_NOTICE "mtd: partition \"rootfs\" " -+ "set to be root filesystem\n"); -+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index); - } -- } -- -- if(parts[i].mtdp) -- { /* store the object pointer (caller may or may not register it */ -- *parts[i].mtdp = &slave->mtd; -- slave->registered = 0; -- } -- else -- { -- /* register our partition */ -- add_mtd_device(&slave->mtd); -- slave->registered = 1; -+#endif -+#ifdef CONFIG_MTD_ROOTFS_SPLIT -+ ret = split_rootfs_data(master, part, j); -+ if (ret == 0) -+ j++; -+#endif - } - } - diff --git a/target/linux/generic-2.6/patches/065-block2mtd_init.patch b/target/linux/generic-2.6/patches/065-block2mtd_init.patch deleted file mode 100644 index 0e36f9ad06..0000000000 --- a/target/linux/generic-2.6/patches/065-block2mtd_init.patch +++ /dev/null @@ -1,114 +0,0 @@ -Index: linux-2.6.21.7/drivers/mtd/devices/block2mtd.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/mtd/devices/block2mtd.c -+++ linux-2.6.21.7/drivers/mtd/devices/block2mtd.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -288,10 +289,11 @@ static void block2mtd_free_device(struct - - - /* FIXME: ensure that mtd->size % erase_size == 0 */ --static struct block2mtd_dev *add_device(char *devname, int erase_size) -+static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) - { - struct block_device *bdev; - struct block2mtd_dev *dev; -+ struct mtd_partition *part; - - if (!devname) - return NULL; -@@ -330,14 +332,18 @@ static struct block2mtd_dev *add_device( - - /* Setup the MTD structure */ - /* make the name contain the block device in */ -- dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), -- GFP_KERNEL); -+ -+ if (!mtdname) -+ mtdname = devname; -+ -+ dev->mtd.name = kmalloc(strlen(mtdname) + 1, GFP_KERNEL); -+ - if (!dev->mtd.name) - goto devinit_err; -+ -+ strcpy(dev->mtd.name, mtdname); - -- sprintf(dev->mtd.name, "block2mtd: %s", devname); -- -- dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; -+ dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK & ~(erase_size - 1); - dev->mtd.erasesize = erase_size; - dev->mtd.writesize = 1; - dev->mtd.type = MTD_RAM; -@@ -349,15 +355,18 @@ static struct block2mtd_dev *add_device( - dev->mtd.read = block2mtd_read; - dev->mtd.priv = dev; - dev->mtd.owner = THIS_MODULE; -- -- if (add_mtd_device(&dev->mtd)) { -+ -+ part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL); -+ part->name = dev->mtd.name; -+ part->offset = 0; -+ part->size = dev->mtd.size; -+ if (add_mtd_partitions(&dev->mtd, part, 1)) { - /* Device didnt get added, so free the entry */ - goto devinit_err; - } - list_add(&dev->list, &blkmtd_device_list); - INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, -- dev->mtd.name + strlen("blkmtd: "), -- dev->mtd.erasesize >> 10, dev->mtd.erasesize); -+ mtdname, dev->mtd.erasesize >> 10, dev->mtd.erasesize); - return dev; - - devinit_err: -@@ -430,9 +439,9 @@ static __initdata char block2mtd_paramli - - static int block2mtd_setup2(const char *val) - { -- char buf[80 + 12]; /* 80 for device, 12 for erase size */ -+ char buf[80 + 12 + 80]; /* 80 for device, 12 for erase size, 80 for name */ - char *str = buf; -- char *token[2]; -+ char *token[3]; - char *name; - size_t erase_size = PAGE_SIZE; - int i, ret; -@@ -443,7 +452,7 @@ static int block2mtd_setup2(const char * - strcpy(str, val); - kill_final_newline(str); - -- for (i = 0; i < 2; i++) -+ for (i = 0; i < 3; i++) - token[i] = strsep(&str, ","); - - if (str) -@@ -463,8 +472,10 @@ static int block2mtd_setup2(const char * - parse_err("illegal erase size"); - } - } -+ if (token[2] && (strlen(token[2]) + 1 > 80)) -+ parse_err("mtd device name too long"); - -- add_device(name, erase_size); -+ add_device(name, erase_size, token[2]); - - return 0; - } -@@ -498,7 +509,7 @@ static int block2mtd_setup(const char *v - - - module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); --MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); -+MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,[,]]\""); - - static int __init block2mtd_init(void) - { diff --git a/target/linux/generic-2.6/patches/100-netfilter_layer7_2.17.patch b/target/linux/generic-2.6/patches/100-netfilter_layer7_2.17.patch deleted file mode 100644 index 52908b3076..0000000000 --- a/target/linux/generic-2.6/patches/100-netfilter_layer7_2.17.patch +++ /dev/null @@ -1,2123 +0,0 @@ -Index: linux-2.6.21.7/net/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/Kconfig -+++ linux-2.6.21.7/net/netfilter/Kconfig -@@ -640,6 +640,27 @@ config NETFILTER_XT_MATCH_STATE - - To compile it as a module, choose M here. If unsure, say N. - -+config NETFILTER_XT_MATCH_LAYER7 -+ tristate '"layer7" match support' -+ depends on NETFILTER_XTABLES -+ depends on EXPERIMENTAL && (IP_NF_CONNTRACK || NF_CONNTRACK) -+ depends on NF_CT_ACCT -+ help -+ Say Y if you want to be able to classify connections (and their -+ packets) based on regular expression matching of their application -+ layer data. This is one way to classify applications such as -+ peer-to-peer filesharing systems that do not always use the same -+ port. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config NETFILTER_XT_MATCH_LAYER7_DEBUG -+ bool 'Layer 7 debugging output' -+ depends on NETFILTER_XT_MATCH_LAYER7 -+ help -+ Say Y to get lots of debugging output. -+ -+ - config NETFILTER_XT_MATCH_STATISTIC - tristate '"statistic" match support' - depends on NETFILTER_XTABLES -Index: linux-2.6.21.7/net/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/Makefile -+++ linux-2.6.21.7/net/netfilter/Makefile -@@ -68,6 +68,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) + - obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o - obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o - obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o -+obj-$(CONFIG_NETFILTER_XT_MATCH_LAYER7) += xt_layer7.o - obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o - obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o - obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o -Index: linux-2.6.21.7/net/netfilter/xt_layer7.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/xt_layer7.c -@@ -0,0 +1,634 @@ -+/* -+ Kernel module to match application layer (OSI layer 7) data in connections. -+ -+ http://l7-filter.sf.net -+ -+ (C) 2003, 2004, 2005, 2006, 2007 Matthew Strait and Ethan Sommer. -+ -+ This program is free software; you can redistribute it and/or -+ modify it under the terms of the GNU General Public License -+ as published by the Free Software Foundation; either version -+ 2 of the License, or (at your option) any later version. -+ http://www.gnu.org/licenses/gpl.txt -+ -+ Based on ipt_string.c (C) 2000 Emmanuel Roger , -+ xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait, -+ Ethan Sommer, Justin Levandoski. -+*/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "regexp/regexp.c" -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Matthew Strait , Ethan Sommer "); -+MODULE_DESCRIPTION("iptables application layer match module"); -+MODULE_ALIAS("ipt_layer7"); -+MODULE_VERSION("2.17"); -+ -+static int maxdatalen = 2048; // this is the default -+module_param(maxdatalen, int, 0444); -+MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter"); -+#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG -+ #define DPRINTK(format,args...) printk(format,##args) -+#else -+ #define DPRINTK(format,args...) -+#endif -+ -+#define TOTAL_PACKETS master_conntrack->counters[IP_CT_DIR_ORIGINAL].packets + \ -+ master_conntrack->counters[IP_CT_DIR_REPLY].packets -+ -+/* Number of packets whose data we look at. -+This can be modified through /proc/net/layer7_numpackets */ -+static int num_packets = 10; -+ -+static struct pattern_cache { -+ char * regex_string; -+ regexp * pattern; -+ struct pattern_cache * next; -+} * first_pattern_cache = NULL; -+ -+DEFINE_SPINLOCK(l7_lock); -+ -+#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG -+/* Converts an unfriendly string into a friendly one by -+replacing unprintables with periods and all whitespace with " ". */ -+static char * friendly_print(unsigned char * s) -+{ -+ char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); -+ int i; -+ -+ if(!f) { -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in " -+ "friendly_print, bailing.\n"); -+ return NULL; -+ } -+ -+ for(i = 0; i < strlen(s); i++){ -+ if(isprint(s[i]) && s[i] < 128) f[i] = s[i]; -+ else if(isspace(s[i])) f[i] = ' '; -+ else f[i] = '.'; -+ } -+ f[i] = '\0'; -+ return f; -+} -+ -+static char dec2hex(int i) -+{ -+ switch (i) { -+ case 0 ... 9: -+ return (i + '0'); -+ break; -+ case 10 ... 15: -+ return (i - 10 + 'a'); -+ break; -+ default: -+ if (net_ratelimit()) -+ printk("layer7: Problem in dec2hex\n"); -+ return '\0'; -+ } -+} -+ -+static char * hex_print(unsigned char * s) -+{ -+ char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); -+ int i; -+ -+ if(!g) { -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in hex_print, " -+ "bailing.\n"); -+ return NULL; -+ } -+ -+ for(i = 0; i < strlen(s); i++) { -+ g[i*3 ] = dec2hex(s[i]/16); -+ g[i*3 + 1] = dec2hex(s[i]%16); -+ g[i*3 + 2] = ' '; -+ } -+ g[i*3] = '\0'; -+ -+ return g; -+} -+#endif // DEBUG -+ -+/* Use instead of regcomp. As we expect to be seeing the same regexps over and -+over again, it make sense to cache the results. */ -+static regexp * compile_and_cache(const char * regex_string, -+ const char * protocol) -+{ -+ struct pattern_cache * node = first_pattern_cache; -+ struct pattern_cache * last_pattern_cache = first_pattern_cache; -+ struct pattern_cache * tmp; -+ unsigned int len; -+ -+ while (node != NULL) { -+ if (!strcmp(node->regex_string, regex_string)) -+ return node->pattern; -+ -+ last_pattern_cache = node;/* points at the last non-NULL node */ -+ node = node->next; -+ } -+ -+ /* If we reach the end of the list, then we have not yet cached -+ the pattern for this regex. Let's do that now. -+ Be paranoid about running out of memory to avoid list corruption. */ -+ tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); -+ -+ if(!tmp) { -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in " -+ "compile_and_cache, bailing.\n"); -+ return NULL; -+ } -+ -+ tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); -+ tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC); -+ tmp->next = NULL; -+ -+ if(!tmp->regex_string || !tmp->pattern) { -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in " -+ "compile_and_cache, bailing.\n"); -+ kfree(tmp->regex_string); -+ kfree(tmp->pattern); -+ kfree(tmp); -+ return NULL; -+ } -+ -+ /* Ok. The new node is all ready now. */ -+ node = tmp; -+ -+ if(first_pattern_cache == NULL) /* list is empty */ -+ first_pattern_cache = node; /* make node the beginning */ -+ else -+ last_pattern_cache->next = node; /* attach node to the end */ -+ -+ /* copy the string and compile the regex */ -+ len = strlen(regex_string); -+ DPRINTK("About to compile this: \"%s\"\n", regex_string); -+ node->pattern = regcomp((char *)regex_string, &len); -+ if ( !node->pattern ) { -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: Error compiling regexp " -+ "\"%s\" (%s)\n", -+ regex_string, protocol); -+ /* pattern is now cached as NULL, so we won't try again. */ -+ } -+ -+ strcpy(node->regex_string, regex_string); -+ return node->pattern; -+} -+ -+static int can_handle(const struct sk_buff *skb) -+{ -+ if(!ip_hdr(skb)) /* not IP */ -+ return 0; -+ if(ip_hdr(skb)->protocol != IPPROTO_TCP && -+ ip_hdr(skb)->protocol != IPPROTO_UDP && -+ ip_hdr(skb)->protocol != IPPROTO_ICMP) -+ return 0; -+ return 1; -+} -+ -+/* Returns offset the into the skb->data that the application data starts */ -+static int app_data_offset(const struct sk_buff *skb) -+{ -+ /* In case we are ported somewhere (ebtables?) where ip_hdr(skb) -+ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ -+ int ip_hl = 4*ip_hdr(skb)->ihl; -+ -+ if( ip_hdr(skb)->protocol == IPPROTO_TCP ) { -+ /* 12 == offset into TCP header for the header length field. -+ Can't get this with skb->h.th->doff because the tcphdr -+ struct doesn't get set when routing (this is confirmed to be -+ true in Netfilter as well as QoS.) */ -+ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); -+ -+ return ip_hl + tcp_hl; -+ } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) { -+ return ip_hl + 8; /* UDP header is always 8 bytes */ -+ } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) { -+ return ip_hl + 8; /* ICMP header is 8 bytes */ -+ } else { -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: tried to handle unknown " -+ "protocol!\n"); -+ return ip_hl + 8; /* something reasonable */ -+ } -+} -+ -+/* handles whether there's a match when we aren't appending data anymore */ -+static int match_no_append(struct nf_conn * conntrack, -+ struct nf_conn * master_conntrack, -+ enum ip_conntrack_info ctinfo, -+ enum ip_conntrack_info master_ctinfo, -+ const struct xt_layer7_info * info) -+{ -+ /* If we're in here, throw the app data away */ -+ if(master_conntrack->layer7.app_data != NULL) { -+ -+ #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG -+ if(!master_conntrack->layer7.app_proto) { -+ char * f = -+ friendly_print(master_conntrack->layer7.app_data); -+ char * g = -+ hex_print(master_conntrack->layer7.app_data); -+ DPRINTK("\nl7-filter gave up after %d bytes " -+ "(%d packets):\n%s\n", -+ strlen(f), TOTAL_PACKETS, f); -+ kfree(f); -+ DPRINTK("In hex: %s\n", g); -+ kfree(g); -+ } -+ #endif -+ -+ kfree(master_conntrack->layer7.app_data); -+ master_conntrack->layer7.app_data = NULL; /* don't free again */ -+ } -+ -+ if(master_conntrack->layer7.app_proto){ -+ /* Here child connections set their .app_proto (for /proc) */ -+ if(!conntrack->layer7.app_proto) { -+ conntrack->layer7.app_proto = -+ kmalloc(strlen(master_conntrack->layer7.app_proto)+1, -+ GFP_ATOMIC); -+ if(!conntrack->layer7.app_proto){ -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory " -+ "in match_no_append, " -+ "bailing.\n"); -+ return 1; -+ } -+ strcpy(conntrack->layer7.app_proto, -+ master_conntrack->layer7.app_proto); -+ } -+ -+ return (!strcmp(master_conntrack->layer7.app_proto, -+ info->protocol)); -+ } -+ else { -+ /* If not classified, set to "unknown" to distinguish from -+ connections that are still being tested. */ -+ master_conntrack->layer7.app_proto = -+ kmalloc(strlen("unknown")+1, GFP_ATOMIC); -+ if(!master_conntrack->layer7.app_proto){ -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in " -+ "match_no_append, bailing.\n"); -+ return 1; -+ } -+ strcpy(master_conntrack->layer7.app_proto, "unknown"); -+ return 0; -+ } -+} -+ -+/* add the new app data to the conntrack. Return number of bytes added. */ -+static int add_data(struct nf_conn * master_conntrack, -+ char * app_data, int appdatalen) -+{ -+ int length = 0, i; -+ int oldlength = master_conntrack->layer7.app_data_len; -+ -+ /* This is a fix for a race condition by Deti Fliegl. However, I'm not -+ clear on whether the race condition exists or whether this really -+ fixes it. I might just be being dense... Anyway, if it's not really -+ a fix, all it does is waste a very small amount of time. */ -+ if(!master_conntrack->layer7.app_data) return 0; -+ -+ /* Strip nulls. Make everything lower case (our regex lib doesn't -+ do case insensitivity). Add it to the end of the current data. */ -+ for(i = 0; i < maxdatalen-oldlength-1 && -+ i < appdatalen; i++) { -+ if(app_data[i] != '\0') { -+ /* the kernel version of tolower mungs 'upper ascii' */ -+ master_conntrack->layer7.app_data[length+oldlength] = -+ isascii(app_data[i])? -+ tolower(app_data[i]) : app_data[i]; -+ length++; -+ } -+ } -+ -+ master_conntrack->layer7.app_data[length+oldlength] = '\0'; -+ master_conntrack->layer7.app_data_len = length + oldlength; -+ -+ return length; -+} -+ -+/* taken from drivers/video/modedb.c */ -+static int my_atoi(const char *s) -+{ -+ int val = 0; -+ -+ for (;; s++) { -+ switch (*s) { -+ case '0'...'9': -+ val = 10*val+(*s-'0'); -+ break; -+ default: -+ return val; -+ } -+ } -+} -+ -+/* write out num_packets to userland. */ -+static int layer7_read_proc(char* page, char ** start, off_t off, int count, -+ int* eof, void * data) -+{ -+ if(num_packets > 99 && net_ratelimit()) -+ printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n"); -+ -+ page[0] = num_packets/10 + '0'; -+ page[1] = num_packets%10 + '0'; -+ page[2] = '\n'; -+ page[3] = '\0'; -+ -+ *eof=1; -+ -+ return 3; -+} -+ -+/* Read in num_packets from userland */ -+static int layer7_write_proc(struct file* file, const char* buffer, -+ unsigned long count, void *data) -+{ -+ char * foo = kmalloc(count, GFP_ATOMIC); -+ -+ if(!foo){ -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory, bailing. " -+ "num_packets unchanged.\n"); -+ return count; -+ } -+ -+ if(copy_from_user(foo, buffer, count)) { -+ return -EFAULT; -+ } -+ -+ -+ num_packets = my_atoi(foo); -+ kfree (foo); -+ -+ /* This has an arbitrary limit to make the math easier. I'm lazy. -+ But anyway, 99 is a LOT! If you want more, you're doing it wrong! */ -+ if(num_packets > 99) { -+ printk(KERN_WARNING "layer7: num_packets can't be > 99.\n"); -+ num_packets = 99; -+ } else if(num_packets < 1) { -+ printk(KERN_WARNING "layer7: num_packets can't be < 1.\n"); -+ num_packets = 1; -+ } -+ -+ return count; -+} -+ -+static int -+match(const struct sk_buff *skbin, -+ const struct net_device *in, -+ const struct net_device *out, -+ const struct xt_match *match, -+ const void *matchinfo, -+ int offset, -+ unsigned int protoff, -+ int *hotdrop) -+{ -+ /* sidestep const without getting a compiler warning... */ -+ struct sk_buff * skb = (struct sk_buff *)skbin; -+ -+ const struct xt_layer7_info * info = matchinfo; -+ enum ip_conntrack_info master_ctinfo, ctinfo; -+ struct nf_conn *master_conntrack, *conntrack; -+ unsigned char * app_data; -+ unsigned int pattern_result, appdatalen; -+ regexp * comppattern; -+ -+ /* Be paranoid/incompetent - lock the entire match function. */ -+ spin_lock_bh(&l7_lock); -+ -+ if(!can_handle(skb)){ -+ DPRINTK("layer7: This is some protocol I can't handle.\n"); -+ spin_unlock_bh(&l7_lock); -+ return info->invert; -+ } -+ -+ /* Treat parent & all its children together as one connection, except -+ for the purpose of setting conntrack->layer7.app_proto in the actual -+ connection. This makes /proc/net/ip_conntrack more satisfying. */ -+ if(!(conntrack = nf_ct_get(skb, &ctinfo)) || -+ !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){ -+ DPRINTK("layer7: couldn't get conntrack.\n"); -+ spin_unlock_bh(&l7_lock); -+ return info->invert; -+ } -+ -+ /* Try to get a master conntrack (and its master etc) for FTP, etc. */ -+ while (master_ct(master_conntrack) != NULL) -+ master_conntrack = master_ct(master_conntrack); -+ -+ /* if we've classified it or seen too many packets */ -+ if(TOTAL_PACKETS > num_packets || -+ master_conntrack->layer7.app_proto) { -+ -+ pattern_result = match_no_append(conntrack, master_conntrack, -+ ctinfo, master_ctinfo, info); -+ -+ /* skb->cb[0] == seen. Don't do things twice if there are -+ multiple l7 rules. I'm not sure that using cb for this purpose -+ is correct, even though it says "put your private variables -+ there". But it doesn't look like it is being used for anything -+ else in the skbs that make it here. */ -+ skb->cb[0] = 1; /* marking it seen here's probably irrelevant */ -+ -+ spin_unlock_bh(&l7_lock); -+ return (pattern_result ^ info->invert); -+ } -+ -+ if(skb_is_nonlinear(skb)){ -+ if(skb_linearize(skb) != 0){ -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: failed to linearize " -+ "packet, bailing.\n"); -+ spin_unlock_bh(&l7_lock); -+ return info->invert; -+ } -+ } -+ -+ /* now that the skb is linearized, it's safe to set these. */ -+ app_data = skb->data + app_data_offset(skb); -+ appdatalen = skb_tail_pointer(skb) - app_data; -+ -+ /* the return value gets checked later, when we're ready to use it */ -+ comppattern = compile_and_cache(info->pattern, info->protocol); -+ -+ /* On the first packet of a connection, allocate space for app data */ -+ if(TOTAL_PACKETS == 1 && !skb->cb[0] && -+ !master_conntrack->layer7.app_data){ -+ master_conntrack->layer7.app_data = -+ kmalloc(maxdatalen, GFP_ATOMIC); -+ if(!master_conntrack->layer7.app_data){ -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in " -+ "match, bailing.\n"); -+ spin_unlock_bh(&l7_lock); -+ return info->invert; -+ } -+ -+ master_conntrack->layer7.app_data[0] = '\0'; -+ } -+ -+ /* Can be here, but unallocated, if numpackets is increased near -+ the beginning of a connection */ -+ if(master_conntrack->layer7.app_data == NULL){ -+ spin_unlock_bh(&l7_lock); -+ return (info->invert); /* unmatched */ -+ } -+ -+ if(!skb->cb[0]){ -+ int newbytes; -+ newbytes = add_data(master_conntrack, app_data, appdatalen); -+ -+ if(newbytes == 0) { /* didn't add any data */ -+ skb->cb[0] = 1; -+ /* Didn't match before, not going to match now */ -+ spin_unlock_bh(&l7_lock); -+ return info->invert; -+ } -+ } -+ -+ /* If looking for "unknown", then never match. "Unknown" means that -+ we've given up; we're still trying with these packets. */ -+ if(!strcmp(info->protocol, "unknown")) { -+ pattern_result = 0; -+ /* If looking for "unset", then always match. "Unset" means that we -+ haven't yet classified the connection. */ -+ } else if(!strcmp(info->protocol, "unset")) { -+ pattern_result = 2; -+ DPRINTK("layer7: matched unset: not yet classified " -+ "(%d/%d packets)\n", TOTAL_PACKETS, num_packets); -+ /* If the regexp failed to compile, don't bother running it */ -+ } else if(comppattern && -+ regexec(comppattern, master_conntrack->layer7.app_data)){ -+ DPRINTK("layer7: matched %s\n", info->protocol); -+ pattern_result = 1; -+ } else pattern_result = 0; -+ -+ if(pattern_result == 1) { -+ master_conntrack->layer7.app_proto = -+ kmalloc(strlen(info->protocol)+1, GFP_ATOMIC); -+ if(!master_conntrack->layer7.app_proto){ -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in " -+ "match, bailing.\n"); -+ spin_unlock_bh(&l7_lock); -+ return (pattern_result ^ info->invert); -+ } -+ strcpy(master_conntrack->layer7.app_proto, info->protocol); -+ } else if(pattern_result > 1) { /* cleanup from "unset" */ -+ pattern_result = 1; -+ } -+ -+ /* mark the packet seen */ -+ skb->cb[0] = 1; -+ -+ spin_unlock_bh(&l7_lock); -+ return (pattern_result ^ info->invert); -+} -+ -+static int check(const char *tablename, -+ const void *inf, -+ const struct xt_match *match, -+ void *matchinfo, -+ unsigned int hook_mask) -+ -+{ -+ // load nf_conntrack_ipv4 -+ if (nf_ct_l3proto_try_module_get(match->family) < 0) { -+ printk(KERN_WARNING "can't load conntrack support for " -+ "proto=%d\n", match->family); -+ return false; -+ } -+ return true; -+} -+ -+static void -+destroy(const struct xt_match *match, void *matchinfo) -+{ -+ nf_ct_l3proto_module_put(match->family); -+} -+ -+static struct xt_match xt_layer7_match[] = { -+{ -+ .name = "layer7", -+ .family = AF_INET, -+ .checkentry = check, -+ .match = match, -+ .destroy = destroy, -+ .matchsize = sizeof(struct xt_layer7_info), -+ .me = THIS_MODULE -+} -+}; -+ -+static void layer7_cleanup_proc(void) -+{ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) -+ remove_proc_entry("layer7_numpackets", proc_net); -+#else -+ remove_proc_entry("layer7_numpackets", init_net.proc_net); -+#endif -+} -+ -+/* register the proc file */ -+static void layer7_init_proc(void) -+{ -+ struct proc_dir_entry* entry; -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) -+ entry = create_proc_entry("layer7_numpackets", 0644, proc_net); -+#else -+ entry = create_proc_entry("layer7_numpackets", 0644, init_net.proc_net); -+#endif -+ entry->read_proc = layer7_read_proc; -+ entry->write_proc = layer7_write_proc; -+} -+ -+static int __init xt_layer7_init(void) -+{ -+ need_conntrack(); -+ -+ layer7_init_proc(); -+ if(maxdatalen < 1) { -+ printk(KERN_WARNING "layer7: maxdatalen can't be < 1, " -+ "using 1\n"); -+ maxdatalen = 1; -+ } -+ /* This is not a hard limit. It's just here to prevent people from -+ bringing their slow machines to a grinding halt. */ -+ else if(maxdatalen > 65536) { -+ printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, " -+ "using 65536\n"); -+ maxdatalen = 65536; -+ } -+ return xt_register_matches(xt_layer7_match, -+ ARRAY_SIZE(xt_layer7_match)); -+} -+ -+static void __exit xt_layer7_fini(void) -+{ -+ layer7_cleanup_proc(); -+ xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match)); -+} -+ -+module_init(xt_layer7_init); -+module_exit(xt_layer7_fini); -Index: linux-2.6.21.7/net/netfilter/regexp/regexp.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/regexp/regexp.c -@@ -0,0 +1,1197 @@ -+/* -+ * regcomp and regexec -- regsub and regerror are elsewhere -+ * @(#)regexp.c 1.3 of 18 April 87 -+ * -+ * Copyright (c) 1986 by University of Toronto. -+ * Written by Henry Spencer. Not derived from licensed software. -+ * -+ * Permission is granted to anyone to use this software for any -+ * purpose on any computer system, and to redistribute it freely, -+ * subject to the following restrictions: -+ * -+ * 1. The author is not responsible for the consequences of use of -+ * this software, no matter how awful, even if they arise -+ * from defects in it. -+ * -+ * 2. The origin of this software must not be misrepresented, either -+ * by explicit claim or by omission. -+ * -+ * 3. Altered versions must be plainly marked as such, and must not -+ * be misrepresented as being the original software. -+ * -+ * Beware that some of this code is subtly aware of the way operator -+ * precedence is structured in regular expressions. Serious changes in -+ * regular-expression syntax might require a total rethink. -+ * -+ * This code was modified by Ethan Sommer to work within the kernel -+ * (it now uses kmalloc etc..) -+ * -+ * Modified slightly by Matthew Strait to use more modern C. -+ */ -+ -+#include "regexp.h" -+#include "regmagic.h" -+ -+/* added by ethan and matt. Lets it work in both kernel and user space. -+(So iptables can use it, for instance.) Yea, it goes both ways... */ -+#if __KERNEL__ -+ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) -+#else -+ #define printk(format,args...) printf(format,##args) -+#endif -+ -+void regerror(char * s) -+{ -+ printk("<3>Regexp: %s\n", s); -+ /* NOTREACHED */ -+} -+ -+/* -+ * The "internal use only" fields in regexp.h are present to pass info from -+ * compile to execute that permits the execute phase to run lots faster on -+ * simple cases. They are: -+ * -+ * regstart char that must begin a match; '\0' if none obvious -+ * reganch is the match anchored (at beginning-of-line only)? -+ * regmust string (pointer into program) that match must include, or NULL -+ * regmlen length of regmust string -+ * -+ * Regstart and reganch permit very fast decisions on suitable starting points -+ * for a match, cutting down the work a lot. Regmust permits fast rejection -+ * of lines that cannot possibly match. The regmust tests are costly enough -+ * that regcomp() supplies a regmust only if the r.e. contains something -+ * potentially expensive (at present, the only such thing detected is * or + -+ * at the start of the r.e., which can involve a lot of backup). Regmlen is -+ * supplied because the test in regexec() needs it and regcomp() is computing -+ * it anyway. -+ */ -+ -+/* -+ * Structure for regexp "program". This is essentially a linear encoding -+ * of a nondeterministic finite-state machine (aka syntax charts or -+ * "railroad normal form" in parsing technology). Each node is an opcode -+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of -+ * all nodes except BRANCH implement concatenation; a "next" pointer with -+ * a BRANCH on both ends of it is connecting two alternatives. (Here we -+ * have one of the subtle syntax dependencies: an individual BRANCH (as -+ * opposed to a collection of them) is never concatenated with anything -+ * because of operator precedence.) The operand of some types of node is -+ * a literal string; for others, it is a node leading into a sub-FSM. In -+ * particular, the operand of a BRANCH node is the first node of the branch. -+ * (NB this is *not* a tree structure: the tail of the branch connects -+ * to the thing following the set of BRANCHes.) The opcodes are: -+ */ -+ -+/* definition number opnd? meaning */ -+#define END 0 /* no End of program. */ -+#define BOL 1 /* no Match "" at beginning of line. */ -+#define EOL 2 /* no Match "" at end of line. */ -+#define ANY 3 /* no Match any one character. */ -+#define ANYOF 4 /* str Match any character in this string. */ -+#define ANYBUT 5 /* str Match any character not in this string. */ -+#define BRANCH 6 /* node Match this alternative, or the next... */ -+#define BACK 7 /* no Match "", "next" ptr points backward. */ -+#define EXACTLY 8 /* str Match this string. */ -+#define NOTHING 9 /* no Match empty string. */ -+#define STAR 10 /* node Match this (simple) thing 0 or more times. */ -+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ -+#define OPEN 20 /* no Mark this point in input as start of #n. */ -+ /* OPEN+1 is number 1, etc. */ -+#define CLOSE 30 /* no Analogous to OPEN. */ -+ -+/* -+ * Opcode notes: -+ * -+ * BRANCH The set of branches constituting a single choice are hooked -+ * together with their "next" pointers, since precedence prevents -+ * anything being concatenated to any individual branch. The -+ * "next" pointer of the last BRANCH in a choice points to the -+ * thing following the whole choice. This is also where the -+ * final "next" pointer of each individual branch points; each -+ * branch starts with the operand node of a BRANCH node. -+ * -+ * BACK Normal "next" pointers all implicitly point forward; BACK -+ * exists to make loop structures possible. -+ * -+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular -+ * BRANCH structures using BACK. Simple cases (one character -+ * per match) are implemented with STAR and PLUS for speed -+ * and to minimize recursive plunges. -+ * -+ * OPEN,CLOSE ...are numbered at compile time. -+ */ -+ -+/* -+ * A node is one char of opcode followed by two chars of "next" pointer. -+ * "Next" pointers are stored as two 8-bit pieces, high order first. The -+ * value is a positive offset from the opcode of the node containing it. -+ * An operand, if any, simply follows the node. (Note that much of the -+ * code generation knows about this implicit relationship.) -+ * -+ * Using two bytes for the "next" pointer is vast overkill for most things, -+ * but allows patterns to get big without disasters. -+ */ -+#define OP(p) (*(p)) -+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) -+#define OPERAND(p) ((p) + 3) -+ -+/* -+ * See regmagic.h for one further detail of program structure. -+ */ -+ -+ -+/* -+ * Utility definitions. -+ */ -+#ifndef CHARBITS -+#define UCHARAT(p) ((int)*(unsigned char *)(p)) -+#else -+#define UCHARAT(p) ((int)*(p)&CHARBITS) -+#endif -+ -+#define FAIL(m) { regerror(m); return(NULL); } -+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') -+#define META "^$.[()|?+*\\" -+ -+/* -+ * Flags to be passed up and down. -+ */ -+#define HASWIDTH 01 /* Known never to match null string. */ -+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ -+#define SPSTART 04 /* Starts with * or +. */ -+#define WORST 0 /* Worst case. */ -+ -+/* -+ * Global work variables for regcomp(). -+ */ -+struct match_globals { -+char *reginput; /* String-input pointer. */ -+char *regbol; /* Beginning of input, for ^ check. */ -+char **regstartp; /* Pointer to startp array. */ -+char **regendp; /* Ditto for endp. */ -+char *regparse; /* Input-scan pointer. */ -+int regnpar; /* () count. */ -+char regdummy; -+char *regcode; /* Code-emit pointer; ®dummy = don't. */ -+long regsize; /* Code size. */ -+}; -+ -+/* -+ * Forward declarations for regcomp()'s friends. -+ */ -+#ifndef STATIC -+#define STATIC static -+#endif -+STATIC char *reg(struct match_globals *g, int paren,int *flagp); -+STATIC char *regbranch(struct match_globals *g, int *flagp); -+STATIC char *regpiece(struct match_globals *g, int *flagp); -+STATIC char *regatom(struct match_globals *g, int *flagp); -+STATIC char *regnode(struct match_globals *g, char op); -+STATIC char *regnext(struct match_globals *g, char *p); -+STATIC void regc(struct match_globals *g, char b); -+STATIC void reginsert(struct match_globals *g, char op, char *opnd); -+STATIC void regtail(struct match_globals *g, char *p, char *val); -+STATIC void regoptail(struct match_globals *g, char *p, char *val); -+ -+ -+__kernel_size_t my_strcspn(const char *s1,const char *s2) -+{ -+ char *scan1; -+ char *scan2; -+ int count; -+ -+ count = 0; -+ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { -+ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ -+ if (*scan1 == *scan2++) -+ return(count); -+ count++; -+ } -+ return(count); -+} -+ -+/* -+ - regcomp - compile a regular expression into internal code -+ * -+ * We can't allocate space until we know how big the compiled form will be, -+ * but we can't compile it (and thus know how big it is) until we've got a -+ * place to put the code. So we cheat: we compile it twice, once with code -+ * generation turned off and size counting turned on, and once "for real". -+ * This also means that we don't allocate space until we are sure that the -+ * thing really will compile successfully, and we never have to move the -+ * code and thus invalidate pointers into it. (Note that it has to be in -+ * one piece because free() must be able to free it all.) -+ * -+ * Beware that the optimization-preparation code in here knows about some -+ * of the structure of the compiled regexp. -+ */ -+regexp * -+regcomp(char *exp,int *patternsize) -+{ -+ register regexp *r; -+ register char *scan; -+ register char *longest; -+ register int len; -+ int flags; -+ struct match_globals g; -+ -+ /* commented out by ethan -+ extern char *malloc(); -+ */ -+ -+ if (exp == NULL) -+ FAIL("NULL argument"); -+ -+ /* First pass: determine size, legality. */ -+ g.regparse = exp; -+ g.regnpar = 1; -+ g.regsize = 0L; -+ g.regcode = &g.regdummy; -+ regc(&g, MAGIC); -+ if (reg(&g, 0, &flags) == NULL) -+ return(NULL); -+ -+ /* Small enough for pointer-storage convention? */ -+ if (g.regsize >= 32767L) /* Probably could be 65535L. */ -+ FAIL("regexp too big"); -+ -+ /* Allocate space. */ -+ *patternsize=sizeof(regexp) + (unsigned)g.regsize; -+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); -+ if (r == NULL) -+ FAIL("out of space"); -+ -+ /* Second pass: emit code. */ -+ g.regparse = exp; -+ g.regnpar = 1; -+ g.regcode = r->program; -+ regc(&g, MAGIC); -+ if (reg(&g, 0, &flags) == NULL) -+ return(NULL); -+ -+ /* Dig out information for optimizations. */ -+ r->regstart = '\0'; /* Worst-case defaults. */ -+ r->reganch = 0; -+ r->regmust = NULL; -+ r->regmlen = 0; -+ scan = r->program+1; /* First BRANCH. */ -+ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ -+ scan = OPERAND(scan); -+ -+ /* Starting-point info. */ -+ if (OP(scan) == EXACTLY) -+ r->regstart = *OPERAND(scan); -+ else if (OP(scan) == BOL) -+ r->reganch++; -+ -+ /* -+ * If there's something expensive in the r.e., find the -+ * longest literal string that must appear and make it the -+ * regmust. Resolve ties in favor of later strings, since -+ * the regstart check works with the beginning of the r.e. -+ * and avoiding duplication strengthens checking. Not a -+ * strong reason, but sufficient in the absence of others. -+ */ -+ if (flags&SPSTART) { -+ longest = NULL; -+ len = 0; -+ for (; scan != NULL; scan = regnext(&g, scan)) -+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { -+ longest = OPERAND(scan); -+ len = strlen(OPERAND(scan)); -+ } -+ r->regmust = longest; -+ r->regmlen = len; -+ } -+ } -+ -+ return(r); -+} -+ -+/* -+ - reg - regular expression, i.e. main body or parenthesized thing -+ * -+ * Caller must absorb opening parenthesis. -+ * -+ * Combining parenthesis handling with the base level of regular expression -+ * is a trifle forced, but the need to tie the tails of the branches to what -+ * follows makes it hard to avoid. -+ */ -+static char * -+reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) -+{ -+ register char *ret; -+ register char *br; -+ register char *ender; -+ register int parno = 0; /* 0 makes gcc happy */ -+ int flags; -+ -+ *flagp = HASWIDTH; /* Tentatively. */ -+ -+ /* Make an OPEN node, if parenthesized. */ -+ if (paren) { -+ if (g->regnpar >= NSUBEXP) -+ FAIL("too many ()"); -+ parno = g->regnpar; -+ g->regnpar++; -+ ret = regnode(g, OPEN+parno); -+ } else -+ ret = NULL; -+ -+ /* Pick up the branches, linking them together. */ -+ br = regbranch(g, &flags); -+ if (br == NULL) -+ return(NULL); -+ if (ret != NULL) -+ regtail(g, ret, br); /* OPEN -> first. */ -+ else -+ ret = br; -+ if (!(flags&HASWIDTH)) -+ *flagp &= ~HASWIDTH; -+ *flagp |= flags&SPSTART; -+ while (*g->regparse == '|') { -+ g->regparse++; -+ br = regbranch(g, &flags); -+ if (br == NULL) -+ return(NULL); -+ regtail(g, ret, br); /* BRANCH -> BRANCH. */ -+ if (!(flags&HASWIDTH)) -+ *flagp &= ~HASWIDTH; -+ *flagp |= flags&SPSTART; -+ } -+ -+ /* Make a closing node, and hook it on the end. */ -+ ender = regnode(g, (paren) ? CLOSE+parno : END); -+ regtail(g, ret, ender); -+ -+ /* Hook the tails of the branches to the closing node. */ -+ for (br = ret; br != NULL; br = regnext(g, br)) -+ regoptail(g, br, ender); -+ -+ /* Check for proper termination. */ -+ if (paren && *g->regparse++ != ')') { -+ FAIL("unmatched ()"); -+ } else if (!paren && *g->regparse != '\0') { -+ if (*g->regparse == ')') { -+ FAIL("unmatched ()"); -+ } else -+ FAIL("junk on end"); /* "Can't happen". */ -+ /* NOTREACHED */ -+ } -+ -+ return(ret); -+} -+ -+/* -+ - regbranch - one alternative of an | operator -+ * -+ * Implements the concatenation operator. -+ */ -+static char * -+regbranch(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ register char *chain; -+ register char *latest; -+ int flags; -+ -+ *flagp = WORST; /* Tentatively. */ -+ -+ ret = regnode(g, BRANCH); -+ chain = NULL; -+ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { -+ latest = regpiece(g, &flags); -+ if (latest == NULL) -+ return(NULL); -+ *flagp |= flags&HASWIDTH; -+ if (chain == NULL) /* First piece. */ -+ *flagp |= flags&SPSTART; -+ else -+ regtail(g, chain, latest); -+ chain = latest; -+ } -+ if (chain == NULL) /* Loop ran zero times. */ -+ (void) regnode(g, NOTHING); -+ -+ return(ret); -+} -+ -+/* -+ - regpiece - something followed by possible [*+?] -+ * -+ * Note that the branching code sequences used for ? and the general cases -+ * of * and + are somewhat optimized: they use the same NOTHING node as -+ * both the endmarker for their branch list and the body of the last branch. -+ * It might seem that this node could be dispensed with entirely, but the -+ * endmarker role is not redundant. -+ */ -+static char * -+regpiece(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ register char op; -+ register char *next; -+ int flags; -+ -+ ret = regatom(g, &flags); -+ if (ret == NULL) -+ return(NULL); -+ -+ op = *g->regparse; -+ if (!ISMULT(op)) { -+ *flagp = flags; -+ return(ret); -+ } -+ -+ if (!(flags&HASWIDTH) && op != '?') -+ FAIL("*+ operand could be empty"); -+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); -+ -+ if (op == '*' && (flags&SIMPLE)) -+ reginsert(g, STAR, ret); -+ else if (op == '*') { -+ /* Emit x* as (x&|), where & means "self". */ -+ reginsert(g, BRANCH, ret); /* Either x */ -+ regoptail(g, ret, regnode(g, BACK)); /* and loop */ -+ regoptail(g, ret, ret); /* back */ -+ regtail(g, ret, regnode(g, BRANCH)); /* or */ -+ regtail(g, ret, regnode(g, NOTHING)); /* null. */ -+ } else if (op == '+' && (flags&SIMPLE)) -+ reginsert(g, PLUS, ret); -+ else if (op == '+') { -+ /* Emit x+ as x(&|), where & means "self". */ -+ next = regnode(g, BRANCH); /* Either */ -+ regtail(g, ret, next); -+ regtail(g, regnode(g, BACK), ret); /* loop back */ -+ regtail(g, next, regnode(g, BRANCH)); /* or */ -+ regtail(g, ret, regnode(g, NOTHING)); /* null. */ -+ } else if (op == '?') { -+ /* Emit x? as (x|) */ -+ reginsert(g, BRANCH, ret); /* Either x */ -+ regtail(g, ret, regnode(g, BRANCH)); /* or */ -+ next = regnode(g, NOTHING); /* null. */ -+ regtail(g, ret, next); -+ regoptail(g, ret, next); -+ } -+ g->regparse++; -+ if (ISMULT(*g->regparse)) -+ FAIL("nested *?+"); -+ -+ return(ret); -+} -+ -+/* -+ - regatom - the lowest level -+ * -+ * Optimization: gobbles an entire sequence of ordinary characters so that -+ * it can turn them into a single node, which is smaller to store and -+ * faster to run. Backslashed characters are exceptions, each becoming a -+ * separate node; the code is simpler that way and it's not worth fixing. -+ */ -+static char * -+regatom(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ int flags; -+ -+ *flagp = WORST; /* Tentatively. */ -+ -+ switch (*g->regparse++) { -+ case '^': -+ ret = regnode(g, BOL); -+ break; -+ case '$': -+ ret = regnode(g, EOL); -+ break; -+ case '.': -+ ret = regnode(g, ANY); -+ *flagp |= HASWIDTH|SIMPLE; -+ break; -+ case '[': { -+ register int class; -+ register int classend; -+ -+ if (*g->regparse == '^') { /* Complement of range. */ -+ ret = regnode(g, ANYBUT); -+ g->regparse++; -+ } else -+ ret = regnode(g, ANYOF); -+ if (*g->regparse == ']' || *g->regparse == '-') -+ regc(g, *g->regparse++); -+ while (*g->regparse != '\0' && *g->regparse != ']') { -+ if (*g->regparse == '-') { -+ g->regparse++; -+ if (*g->regparse == ']' || *g->regparse == '\0') -+ regc(g, '-'); -+ else { -+ class = UCHARAT(g->regparse-2)+1; -+ classend = UCHARAT(g->regparse); -+ if (class > classend+1) -+ FAIL("invalid [] range"); -+ for (; class <= classend; class++) -+ regc(g, class); -+ g->regparse++; -+ } -+ } else -+ regc(g, *g->regparse++); -+ } -+ regc(g, '\0'); -+ if (*g->regparse != ']') -+ FAIL("unmatched []"); -+ g->regparse++; -+ *flagp |= HASWIDTH|SIMPLE; -+ } -+ break; -+ case '(': -+ ret = reg(g, 1, &flags); -+ if (ret == NULL) -+ return(NULL); -+ *flagp |= flags&(HASWIDTH|SPSTART); -+ break; -+ case '\0': -+ case '|': -+ case ')': -+ FAIL("internal urp"); /* Supposed to be caught earlier. */ -+ break; -+ case '?': -+ case '+': -+ case '*': -+ FAIL("?+* follows nothing"); -+ break; -+ case '\\': -+ if (*g->regparse == '\0') -+ FAIL("trailing \\"); -+ ret = regnode(g, EXACTLY); -+ regc(g, *g->regparse++); -+ regc(g, '\0'); -+ *flagp |= HASWIDTH|SIMPLE; -+ break; -+ default: { -+ register int len; -+ register char ender; -+ -+ g->regparse--; -+ len = my_strcspn((const char *)g->regparse, (const char *)META); -+ if (len <= 0) -+ FAIL("internal disaster"); -+ ender = *(g->regparse+len); -+ if (len > 1 && ISMULT(ender)) -+ len--; /* Back off clear of ?+* operand. */ -+ *flagp |= HASWIDTH; -+ if (len == 1) -+ *flagp |= SIMPLE; -+ ret = regnode(g, EXACTLY); -+ while (len > 0) { -+ regc(g, *g->regparse++); -+ len--; -+ } -+ regc(g, '\0'); -+ } -+ break; -+ } -+ -+ return(ret); -+} -+ -+/* -+ - regnode - emit a node -+ */ -+static char * /* Location. */ -+regnode(struct match_globals *g, char op) -+{ -+ register char *ret; -+ register char *ptr; -+ -+ ret = g->regcode; -+ if (ret == &g->regdummy) { -+ g->regsize += 3; -+ return(ret); -+ } -+ -+ ptr = ret; -+ *ptr++ = op; -+ *ptr++ = '\0'; /* Null "next" pointer. */ -+ *ptr++ = '\0'; -+ g->regcode = ptr; -+ -+ return(ret); -+} -+ -+/* -+ - regc - emit (if appropriate) a byte of code -+ */ -+static void -+regc(struct match_globals *g, char b) -+{ -+ if (g->regcode != &g->regdummy) -+ *g->regcode++ = b; -+ else -+ g->regsize++; -+} -+ -+/* -+ - reginsert - insert an operator in front of already-emitted operand -+ * -+ * Means relocating the operand. -+ */ -+static void -+reginsert(struct match_globals *g, char op, char* opnd) -+{ -+ register char *src; -+ register char *dst; -+ register char *place; -+ -+ if (g->regcode == &g->regdummy) { -+ g->regsize += 3; -+ return; -+ } -+ -+ src = g->regcode; -+ g->regcode += 3; -+ dst = g->regcode; -+ while (src > opnd) -+ *--dst = *--src; -+ -+ place = opnd; /* Op node, where operand used to be. */ -+ *place++ = op; -+ *place++ = '\0'; -+ *place++ = '\0'; -+} -+ -+/* -+ - regtail - set the next-pointer at the end of a node chain -+ */ -+static void -+regtail(struct match_globals *g, char *p, char *val) -+{ -+ register char *scan; -+ register char *temp; -+ register int offset; -+ -+ if (p == &g->regdummy) -+ return; -+ -+ /* Find last node. */ -+ scan = p; -+ for (;;) { -+ temp = regnext(g, scan); -+ if (temp == NULL) -+ break; -+ scan = temp; -+ } -+ -+ if (OP(scan) == BACK) -+ offset = scan - val; -+ else -+ offset = val - scan; -+ *(scan+1) = (offset>>8)&0377; -+ *(scan+2) = offset&0377; -+} -+ -+/* -+ - regoptail - regtail on operand of first argument; nop if operandless -+ */ -+static void -+regoptail(struct match_globals *g, char *p, char *val) -+{ -+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ -+ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) -+ return; -+ regtail(g, OPERAND(p), val); -+} -+ -+/* -+ * regexec and friends -+ */ -+ -+ -+/* -+ * Forwards. -+ */ -+STATIC int regtry(struct match_globals *g, regexp *prog, char *string); -+STATIC int regmatch(struct match_globals *g, char *prog); -+STATIC int regrepeat(struct match_globals *g, char *p); -+ -+#ifdef DEBUG -+int regnarrate = 0; -+void regdump(); -+STATIC char *regprop(char *op); -+#endif -+ -+/* -+ - regexec - match a regexp against a string -+ */ -+int -+regexec(regexp *prog, char *string) -+{ -+ register char *s; -+ struct match_globals g; -+ -+ /* Be paranoid... */ -+ if (prog == NULL || string == NULL) { -+ printk("<3>Regexp: NULL parameter\n"); -+ return(0); -+ } -+ -+ /* Check validity of program. */ -+ if (UCHARAT(prog->program) != MAGIC) { -+ printk("<3>Regexp: corrupted program\n"); -+ return(0); -+ } -+ -+ /* If there is a "must appear" string, look for it. */ -+ if (prog->regmust != NULL) { -+ s = string; -+ while ((s = strchr(s, prog->regmust[0])) != NULL) { -+ if (strncmp(s, prog->regmust, prog->regmlen) == 0) -+ break; /* Found it. */ -+ s++; -+ } -+ if (s == NULL) /* Not present. */ -+ return(0); -+ } -+ -+ /* Mark beginning of line for ^ . */ -+ g.regbol = string; -+ -+ /* Simplest case: anchored match need be tried only once. */ -+ if (prog->reganch) -+ return(regtry(&g, prog, string)); -+ -+ /* Messy cases: unanchored match. */ -+ s = string; -+ if (prog->regstart != '\0') -+ /* We know what char it must start with. */ -+ while ((s = strchr(s, prog->regstart)) != NULL) { -+ if (regtry(&g, prog, s)) -+ return(1); -+ s++; -+ } -+ else -+ /* We don't -- general case. */ -+ do { -+ if (regtry(&g, prog, s)) -+ return(1); -+ } while (*s++ != '\0'); -+ -+ /* Failure. */ -+ return(0); -+} -+ -+/* -+ - regtry - try match at specific point -+ */ -+static int /* 0 failure, 1 success */ -+regtry(struct match_globals *g, regexp *prog, char *string) -+{ -+ register int i; -+ register char **sp; -+ register char **ep; -+ -+ g->reginput = string; -+ g->regstartp = prog->startp; -+ g->regendp = prog->endp; -+ -+ sp = prog->startp; -+ ep = prog->endp; -+ for (i = NSUBEXP; i > 0; i--) { -+ *sp++ = NULL; -+ *ep++ = NULL; -+ } -+ if (regmatch(g, prog->program + 1)) { -+ prog->startp[0] = string; -+ prog->endp[0] = g->reginput; -+ return(1); -+ } else -+ return(0); -+} -+ -+/* -+ - regmatch - main matching routine -+ * -+ * Conceptually the strategy is simple: check to see whether the current -+ * node matches, call self recursively to see whether the rest matches, -+ * and then act accordingly. In practice we make some effort to avoid -+ * recursion, in particular by going through "ordinary" nodes (that don't -+ * need to know whether the rest of the match failed) by a loop instead of -+ * by recursion. -+ */ -+static int /* 0 failure, 1 success */ -+regmatch(struct match_globals *g, char *prog) -+{ -+ register char *scan = prog; /* Current node. */ -+ char *next; /* Next node. */ -+ -+#ifdef DEBUG -+ if (scan != NULL && regnarrate) -+ fprintf(stderr, "%s(\n", regprop(scan)); -+#endif -+ while (scan != NULL) { -+#ifdef DEBUG -+ if (regnarrate) -+ fprintf(stderr, "%s...\n", regprop(scan)); -+#endif -+ next = regnext(g, scan); -+ -+ switch (OP(scan)) { -+ case BOL: -+ if (g->reginput != g->regbol) -+ return(0); -+ break; -+ case EOL: -+ if (*g->reginput != '\0') -+ return(0); -+ break; -+ case ANY: -+ if (*g->reginput == '\0') -+ return(0); -+ g->reginput++; -+ break; -+ case EXACTLY: { -+ register int len; -+ register char *opnd; -+ -+ opnd = OPERAND(scan); -+ /* Inline the first character, for speed. */ -+ if (*opnd != *g->reginput) -+ return(0); -+ len = strlen(opnd); -+ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) -+ return(0); -+ g->reginput += len; -+ } -+ break; -+ case ANYOF: -+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) -+ return(0); -+ g->reginput++; -+ break; -+ case ANYBUT: -+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) -+ return(0); -+ g->reginput++; -+ break; -+ case NOTHING: -+ case BACK: -+ break; -+ case OPEN+1: -+ case OPEN+2: -+ case OPEN+3: -+ case OPEN+4: -+ case OPEN+5: -+ case OPEN+6: -+ case OPEN+7: -+ case OPEN+8: -+ case OPEN+9: { -+ register int no; -+ register char *save; -+ -+ no = OP(scan) - OPEN; -+ save = g->reginput; -+ -+ if (regmatch(g, next)) { -+ /* -+ * Don't set startp if some later -+ * invocation of the same parentheses -+ * already has. -+ */ -+ if (g->regstartp[no] == NULL) -+ g->regstartp[no] = save; -+ return(1); -+ } else -+ return(0); -+ } -+ break; -+ case CLOSE+1: -+ case CLOSE+2: -+ case CLOSE+3: -+ case CLOSE+4: -+ case CLOSE+5: -+ case CLOSE+6: -+ case CLOSE+7: -+ case CLOSE+8: -+ case CLOSE+9: -+ { -+ register int no; -+ register char *save; -+ -+ no = OP(scan) - CLOSE; -+ save = g->reginput; -+ -+ if (regmatch(g, next)) { -+ /* -+ * Don't set endp if some later -+ * invocation of the same parentheses -+ * already has. -+ */ -+ if (g->regendp[no] == NULL) -+ g->regendp[no] = save; -+ return(1); -+ } else -+ return(0); -+ } -+ break; -+ case BRANCH: { -+ register char *save; -+ -+ if (OP(next) != BRANCH) /* No choice. */ -+ next = OPERAND(scan); /* Avoid recursion. */ -+ else { -+ do { -+ save = g->reginput; -+ if (regmatch(g, OPERAND(scan))) -+ return(1); -+ g->reginput = save; -+ scan = regnext(g, scan); -+ } while (scan != NULL && OP(scan) == BRANCH); -+ return(0); -+ /* NOTREACHED */ -+ } -+ } -+ break; -+ case STAR: -+ case PLUS: { -+ register char nextch; -+ register int no; -+ register char *save; -+ register int min; -+ -+ /* -+ * Lookahead to avoid useless match attempts -+ * when we know what character comes next. -+ */ -+ nextch = '\0'; -+ if (OP(next) == EXACTLY) -+ nextch = *OPERAND(next); -+ min = (OP(scan) == STAR) ? 0 : 1; -+ save = g->reginput; -+ no = regrepeat(g, OPERAND(scan)); -+ while (no >= min) { -+ /* If it could work, try it. */ -+ if (nextch == '\0' || *g->reginput == nextch) -+ if (regmatch(g, next)) -+ return(1); -+ /* Couldn't or didn't -- back up. */ -+ no--; -+ g->reginput = save + no; -+ } -+ return(0); -+ } -+ break; -+ case END: -+ return(1); /* Success! */ -+ break; -+ default: -+ printk("<3>Regexp: memory corruption\n"); -+ return(0); -+ break; -+ } -+ -+ scan = next; -+ } -+ -+ /* -+ * We get here only if there's trouble -- normally "case END" is -+ * the terminating point. -+ */ -+ printk("<3>Regexp: corrupted pointers\n"); -+ return(0); -+} -+ -+/* -+ - regrepeat - repeatedly match something simple, report how many -+ */ -+static int -+regrepeat(struct match_globals *g, char *p) -+{ -+ register int count = 0; -+ register char *scan; -+ register char *opnd; -+ -+ scan = g->reginput; -+ opnd = OPERAND(p); -+ switch (OP(p)) { -+ case ANY: -+ count = strlen(scan); -+ scan += count; -+ break; -+ case EXACTLY: -+ while (*opnd == *scan) { -+ count++; -+ scan++; -+ } -+ break; -+ case ANYOF: -+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { -+ count++; -+ scan++; -+ } -+ break; -+ case ANYBUT: -+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { -+ count++; -+ scan++; -+ } -+ break; -+ default: /* Oh dear. Called inappropriately. */ -+ printk("<3>Regexp: internal foulup\n"); -+ count = 0; /* Best compromise. */ -+ break; -+ } -+ g->reginput = scan; -+ -+ return(count); -+} -+ -+/* -+ - regnext - dig the "next" pointer out of a node -+ */ -+static char* -+regnext(struct match_globals *g, char *p) -+{ -+ register int offset; -+ -+ if (p == &g->regdummy) -+ return(NULL); -+ -+ offset = NEXT(p); -+ if (offset == 0) -+ return(NULL); -+ -+ if (OP(p) == BACK) -+ return(p-offset); -+ else -+ return(p+offset); -+} -+ -+#ifdef DEBUG -+ -+STATIC char *regprop(); -+ -+/* -+ - regdump - dump a regexp onto stdout in vaguely comprehensible form -+ */ -+void -+regdump(regexp *r) -+{ -+ register char *s; -+ register char op = EXACTLY; /* Arbitrary non-END op. */ -+ register char *next; -+ /* extern char *strchr(); */ -+ -+ -+ s = r->program + 1; -+ while (op != END) { /* While that wasn't END last time... */ -+ op = OP(s); -+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ -+ next = regnext(s); -+ if (next == NULL) /* Next ptr. */ -+ printf("(0)"); -+ else -+ printf("(%d)", (s-r->program)+(next-s)); -+ s += 3; -+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { -+ /* Literal string, where present. */ -+ while (*s != '\0') { -+ putchar(*s); -+ s++; -+ } -+ s++; -+ } -+ putchar('\n'); -+ } -+ -+ /* Header fields of interest. */ -+ if (r->regstart != '\0') -+ printf("start `%c' ", r->regstart); -+ if (r->reganch) -+ printf("anchored "); -+ if (r->regmust != NULL) -+ printf("must have \"%s\"", r->regmust); -+ printf("\n"); -+} -+ -+/* -+ - regprop - printable representation of opcode -+ */ -+static char * -+regprop(char *op) -+{ -+#define BUFLEN 50 -+ register char *p; -+ static char buf[BUFLEN]; -+ -+ strcpy(buf, ":"); -+ -+ switch (OP(op)) { -+ case BOL: -+ p = "BOL"; -+ break; -+ case EOL: -+ p = "EOL"; -+ break; -+ case ANY: -+ p = "ANY"; -+ break; -+ case ANYOF: -+ p = "ANYOF"; -+ break; -+ case ANYBUT: -+ p = "ANYBUT"; -+ break; -+ case BRANCH: -+ p = "BRANCH"; -+ break; -+ case EXACTLY: -+ p = "EXACTLY"; -+ break; -+ case NOTHING: -+ p = "NOTHING"; -+ break; -+ case BACK: -+ p = "BACK"; -+ break; -+ case END: -+ p = "END"; -+ break; -+ case OPEN+1: -+ case OPEN+2: -+ case OPEN+3: -+ case OPEN+4: -+ case OPEN+5: -+ case OPEN+6: -+ case OPEN+7: -+ case OPEN+8: -+ case OPEN+9: -+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); -+ p = NULL; -+ break; -+ case CLOSE+1: -+ case CLOSE+2: -+ case CLOSE+3: -+ case CLOSE+4: -+ case CLOSE+5: -+ case CLOSE+6: -+ case CLOSE+7: -+ case CLOSE+8: -+ case CLOSE+9: -+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); -+ p = NULL; -+ break; -+ case STAR: -+ p = "STAR"; -+ break; -+ case PLUS: -+ p = "PLUS"; -+ break; -+ default: -+ printk("<3>Regexp: corrupted opcode\n"); -+ break; -+ } -+ if (p != NULL) -+ strncat(buf, p, BUFLEN-strlen(buf)); -+ return(buf); -+} -+#endif -+ -+ -Index: linux-2.6.21.7/net/netfilter/regexp/regexp.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/regexp/regexp.h -@@ -0,0 +1,41 @@ -+/* -+ * Definitions etc. for regexp(3) routines. -+ * -+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], -+ * not the System V one. -+ */ -+ -+#ifndef REGEXP_H -+#define REGEXP_H -+ -+ -+/* -+http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , -+which contains a version of this library, says: -+ -+ * -+ * NSUBEXP must be at least 10, and no greater than 117 or the parser -+ * will not work properly. -+ * -+ -+However, it looks rather like this library is limited to 10. If you think -+otherwise, let us know. -+*/ -+ -+#define NSUBEXP 10 -+typedef struct regexp { -+ char *startp[NSUBEXP]; -+ char *endp[NSUBEXP]; -+ char regstart; /* Internal use only. */ -+ char reganch; /* Internal use only. */ -+ char *regmust; /* Internal use only. */ -+ int regmlen; /* Internal use only. */ -+ char program[1]; /* Unwarranted chumminess with compiler. */ -+} regexp; -+ -+regexp * regcomp(char *exp, int *patternsize); -+int regexec(regexp *prog, char *string); -+void regsub(regexp *prog, char *source, char *dest); -+void regerror(char *s); -+ -+#endif -Index: linux-2.6.21.7/net/netfilter/regexp/regmagic.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/regexp/regmagic.h -@@ -0,0 +1,5 @@ -+/* -+ * The first byte of the regexp internal "program" is actually this magic -+ * number; the start node begins in the second byte. -+ */ -+#define MAGIC 0234 -Index: linux-2.6.21.7/net/netfilter/regexp/regsub.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/regexp/regsub.c -@@ -0,0 +1,95 @@ -+/* -+ * regsub -+ * @(#)regsub.c 1.3 of 2 April 86 -+ * -+ * Copyright (c) 1986 by University of Toronto. -+ * Written by Henry Spencer. Not derived from licensed software. -+ * -+ * Permission is granted to anyone to use this software for any -+ * purpose on any computer system, and to redistribute it freely, -+ * subject to the following restrictions: -+ * -+ * 1. The author is not responsible for the consequences of use of -+ * this software, no matter how awful, even if they arise -+ * from defects in it. -+ * -+ * 2. The origin of this software must not be misrepresented, either -+ * by explicit claim or by omission. -+ * -+ * 3. Altered versions must be plainly marked as such, and must not -+ * be misrepresented as being the original software. -+ * -+ * -+ * This code was modified by Ethan Sommer to work within the kernel -+ * (it now uses kmalloc etc..) -+ * -+ */ -+#include "regexp.h" -+#include "regmagic.h" -+#include -+ -+ -+#ifndef CHARBITS -+#define UCHARAT(p) ((int)*(unsigned char *)(p)) -+#else -+#define UCHARAT(p) ((int)*(p)&CHARBITS) -+#endif -+ -+#if 0 -+//void regerror(char * s) -+//{ -+// printk("regexp(3): %s", s); -+// /* NOTREACHED */ -+//} -+#endif -+ -+/* -+ - regsub - perform substitutions after a regexp match -+ */ -+void -+regsub(regexp * prog, char * source, char * dest) -+{ -+ register char *src; -+ register char *dst; -+ register char c; -+ register int no; -+ register int len; -+ -+ /* Not necessary and gcc doesn't like it -MLS */ -+ /*extern char *strncpy();*/ -+ -+ if (prog == NULL || source == NULL || dest == NULL) { -+ regerror("NULL parm to regsub"); -+ return; -+ } -+ if (UCHARAT(prog->program) != MAGIC) { -+ regerror("damaged regexp fed to regsub"); -+ return; -+ } -+ -+ src = source; -+ dst = dest; -+ while ((c = *src++) != '\0') { -+ if (c == '&') -+ no = 0; -+ else if (c == '\\' && '0' <= *src && *src <= '9') -+ no = *src++ - '0'; -+ else -+ no = -1; -+ -+ if (no < 0) { /* Ordinary character. */ -+ if (c == '\\' && (*src == '\\' || *src == '&')) -+ c = *src++; -+ *dst++ = c; -+ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { -+ len = prog->endp[no] - prog->startp[no]; -+ (void) strncpy(dst, prog->startp[no], len); -+ dst += len; -+ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ -+ regerror("damaged match string"); -+ return; -+ } -+ } -+ } -+ *dst++ = '\0'; -+} -Index: linux-2.6.21.7/net/netfilter/nf_conntrack_core.c -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/nf_conntrack_core.c -+++ linux-2.6.21.7/net/netfilter/nf_conntrack_core.c -@@ -352,6 +352,14 @@ destroy_conntrack(struct nf_conntrack *n - * too. */ - nf_ct_remove_expectations(ct); - -+ #if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) -+ if(ct->layer7.app_proto) -+ kfree(ct->layer7.app_proto); -+ if(ct->layer7.app_data) -+ kfree(ct->layer7.app_data); -+ #endif -+ -+ - /* We overload first tuple to link into unconfirmed list. */ - if (!nf_ct_is_confirmed(ct)) { - BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list)); -Index: linux-2.6.21.7/net/netfilter/nf_conntrack_standalone.c -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/nf_conntrack_standalone.c -+++ linux-2.6.21.7/net/netfilter/nf_conntrack_standalone.c -@@ -195,7 +195,12 @@ static int ct_seq_show(struct seq_file * - return -ENOSPC; - #endif - -- if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) -+#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) -+ if(conntrack->layer7.app_proto) -+ if(seq_printf(s, "l7proto=%s ", conntrack->layer7.app_proto)) -+ return -ENOSPC; -+#endif -+ if (seq_printf(s, "asdfuse=%u\n", atomic_read(&conntrack->ct_general.use))) - return -ENOSPC; - - return 0; -Index: linux-2.6.21.7/include/net/netfilter/nf_conntrack.h -=================================================================== ---- linux-2.6.21.7.orig/include/net/netfilter/nf_conntrack.h -+++ linux-2.6.21.7/include/net/netfilter/nf_conntrack.h -@@ -128,6 +128,22 @@ struct nf_conn - u_int32_t secmark; - #endif - -+#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || \ -+ defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) -+ struct { -+ /* -+ * e.g. "http". NULL before decision. "unknown" after decision -+ * if no match. -+ */ -+ char *app_proto; -+ /* -+ * application layer data so far. NULL after match decision. -+ */ -+ char *app_data; -+ unsigned int app_data_len; -+ } layer7; -+#endif -+ - /* Storage reserved for other modules: */ - union nf_conntrack_proto proto; - -Index: linux-2.6.21.7/include/linux/netfilter/xt_layer7.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter/xt_layer7.h -@@ -0,0 +1,13 @@ -+#ifndef _XT_LAYER7_H -+#define _XT_LAYER7_H -+ -+#define MAX_PATTERN_LEN 8192 -+#define MAX_PROTOCOL_LEN 256 -+ -+struct xt_layer7_info { -+ char protocol[MAX_PROTOCOL_LEN]; -+ char pattern[MAX_PATTERN_LEN]; -+ u_int8_t invert; -+}; -+ -+#endif /* _XT_LAYER7_H */ diff --git a/target/linux/generic-2.6/patches/101-netfilter_layer7_pktmatch.patch b/target/linux/generic-2.6/patches/101-netfilter_layer7_pktmatch.patch deleted file mode 100644 index cabffacc14..0000000000 --- a/target/linux/generic-2.6/patches/101-netfilter_layer7_pktmatch.patch +++ /dev/null @@ -1,113 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netfilter/xt_layer7.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/netfilter/xt_layer7.h -+++ linux-2.6.21.7/include/linux/netfilter/xt_layer7.h -@@ -8,6 +8,7 @@ struct xt_layer7_info { - char protocol[MAX_PROTOCOL_LEN]; - char pattern[MAX_PATTERN_LEN]; - u_int8_t invert; -+ u_int8_t pkt; - }; - - #endif /* _XT_LAYER7_H */ -Index: linux-2.6.21.7/net/netfilter/xt_layer7.c -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/xt_layer7.c -+++ linux-2.6.21.7/net/netfilter/xt_layer7.c -@@ -297,34 +297,36 @@ static int match_no_append(struct nf_con - } - - /* add the new app data to the conntrack. Return number of bytes added. */ --static int add_data(struct nf_conn * master_conntrack, -- char * app_data, int appdatalen) -+static int add_datastr(char *target, int offset, char *app_data, int len) - { - int length = 0, i; -- int oldlength = master_conntrack->layer7.app_data_len; -- -- /* This is a fix for a race condition by Deti Fliegl. However, I'm not -- clear on whether the race condition exists or whether this really -- fixes it. I might just be being dense... Anyway, if it's not really -- a fix, all it does is waste a very small amount of time. */ -- if(!master_conntrack->layer7.app_data) return 0; -+ -+ if (!target) return 0; - - /* Strip nulls. Make everything lower case (our regex lib doesn't - do case insensitivity). Add it to the end of the current data. */ -- for(i = 0; i < maxdatalen-oldlength-1 && -- i < appdatalen; i++) { -+ for(i = 0; i < maxdatalen-offset-1 && i < len; i++) { - if(app_data[i] != '\0') { - /* the kernel version of tolower mungs 'upper ascii' */ -- master_conntrack->layer7.app_data[length+oldlength] = -+ target[length+offset] = - isascii(app_data[i])? - tolower(app_data[i]) : app_data[i]; - length++; - } - } -+ target[length+offset] = '\0'; -+ -+ return length; -+} - -- master_conntrack->layer7.app_data[length+oldlength] = '\0'; -- master_conntrack->layer7.app_data_len = length + oldlength; -+/* add the new app data to the conntrack. Return number of bytes added. */ -+static int add_data(struct nf_conn * master_conntrack, -+ char * app_data, int appdatalen) -+{ -+ int length; - -+ length = add_datastr(master_conntrack->layer7.app_data, master_conntrack->layer7.app_data_len, app_data, appdatalen); -+ master_conntrack->layer7.app_data_len += length; - return length; - } - -@@ -411,7 +413,7 @@ match(const struct sk_buff *skbin, - const struct xt_layer7_info * info = matchinfo; - enum ip_conntrack_info master_ctinfo, ctinfo; - struct nf_conn *master_conntrack, *conntrack; -- unsigned char * app_data; -+ unsigned char *app_data, *tmp_data; - unsigned int pattern_result, appdatalen; - regexp * comppattern; - -@@ -439,8 +441,8 @@ match(const struct sk_buff *skbin, - master_conntrack = master_ct(master_conntrack); - - /* if we've classified it or seen too many packets */ -- if(TOTAL_PACKETS > num_packets || -- master_conntrack->layer7.app_proto) { -+ if(!info->pkt && (TOTAL_PACKETS > num_packets || -+ master_conntrack->layer7.app_proto)) { - - pattern_result = match_no_append(conntrack, master_conntrack, - ctinfo, master_ctinfo, info); -@@ -473,6 +475,25 @@ match(const struct sk_buff *skbin, - /* the return value gets checked later, when we're ready to use it */ - comppattern = compile_and_cache(info->pattern, info->protocol); - -+ if (info->pkt) { -+ tmp_data = kmalloc(maxdatalen, GFP_ATOMIC); -+ if(!tmp_data){ -+ if (net_ratelimit()) -+ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); -+ return info->invert; -+ } -+ -+ tmp_data[0] = '\0'; -+ add_datastr(tmp_data, 0, app_data, appdatalen); -+ pattern_result = ((comppattern && regexec(comppattern, tmp_data)) ? 1 : 0); -+ -+ kfree(tmp_data); -+ tmp_data = NULL; -+ spin_unlock_bh(&l7_lock); -+ -+ return (pattern_result ^ info->invert); -+ } -+ - /* On the first packet of a connection, allocate space for app data */ - if(TOTAL_PACKETS == 1 && !skb->cb[0] && - !master_conntrack->layer7.app_data){ diff --git a/target/linux/generic-2.6/patches/110-ipp2p_0.8.1rc1.patch b/target/linux/generic-2.6/patches/110-ipp2p_0.8.1rc1.patch deleted file mode 100644 index 5f202ca99e..0000000000 --- a/target/linux/generic-2.6/patches/110-ipp2p_0.8.1rc1.patch +++ /dev/null @@ -1,952 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ipp2p.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ipp2p.h -@@ -0,0 +1,31 @@ -+#ifndef __IPT_IPP2P_H -+#define __IPT_IPP2P_H -+#define IPP2P_VERSION "0.8.1_rc1" -+ -+struct ipt_p2p_info { -+ int cmd; -+ int debug; -+}; -+ -+#endif //__IPT_IPP2P_H -+ -+#define SHORT_HAND_IPP2P 1 /* --ipp2p switch*/ -+//#define SHORT_HAND_DATA 4 /* --ipp2p-data switch*/ -+#define SHORT_HAND_NONE 5 /* no short hand*/ -+ -+#define IPP2P_EDK (1 << 1) -+#define IPP2P_DATA_KAZAA (1 << 2) -+#define IPP2P_DATA_EDK (1 << 3) -+#define IPP2P_DATA_DC (1 << 4) -+#define IPP2P_DC (1 << 5) -+#define IPP2P_DATA_GNU (1 << 6) -+#define IPP2P_GNU (1 << 7) -+#define IPP2P_KAZAA (1 << 8) -+#define IPP2P_BIT (1 << 9) -+#define IPP2P_APPLE (1 << 10) -+#define IPP2P_SOUL (1 << 11) -+#define IPP2P_WINMX (1 << 12) -+#define IPP2P_ARES (1 << 13) -+#define IPP2P_MUTE (1 << 14) -+#define IPP2P_WASTE (1 << 15) -+#define IPP2P_XDCC (1 << 16) -Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_ipp2p.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ipt_ipp2p.c -@@ -0,0 +1,882 @@ -+#if defined(MODVERSIONS) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define get_u8(X,O) (*(__u8 *)(X + O)) -+#define get_u16(X,O) (*(__u16 *)(X + O)) -+#define get_u32(X,O) (*(__u32 *)(X + O)) -+ -+MODULE_AUTHOR("Eicke Friedrich/Klaus Degner "); -+MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic."); -+MODULE_LICENSE("GPL"); -+ -+ -+/*Search for UDP eDonkey/eMule/Kad commands*/ -+int -+udp_search_edk (unsigned char *haystack, int packet_len) -+{ -+ unsigned char *t = haystack; -+ t += 8; -+ -+ switch (t[0]) { -+ case 0xe3: -+ { /*edonkey*/ -+ switch (t[1]) -+ { -+ /* client -> server status request */ -+ case 0x96: -+ if (packet_len == 14) return ((IPP2P_EDK * 100) + 50); -+ break; -+ /* server -> client status request */ -+ case 0x97: if (packet_len == 42) return ((IPP2P_EDK * 100) + 51); -+ break; -+ /* server description request */ -+ /* e3 2a ff f0 .. | size == 6 */ -+ case 0xa2: if ( (packet_len == 14) && ( get_u16(t,2) == __constant_htons(0xfff0) ) ) return ((IPP2P_EDK * 100) + 52); -+ break; -+ /* server description response */ -+ /* e3 a3 ff f0 .. | size > 40 && size < 200 */ -+ //case 0xa3: return ((IPP2P_EDK * 100) + 53); -+ // break; -+ case 0x9a: if (packet_len==26) return ((IPP2P_EDK * 100) + 54); -+ break; -+ -+ case 0x92: if (packet_len==18) return ((IPP2P_EDK * 100) + 55); -+ break; -+ } -+ break; -+ } -+ case 0xe4: -+ { -+ switch (t[1]) -+ { -+ /* e4 20 .. | size == 43 */ -+ case 0x20: if ((packet_len == 43) && (t[2] != 0x00) && (t[34] != 0x00)) return ((IPP2P_EDK * 100) + 60); -+ break; -+ /* e4 00 .. 00 | size == 35 ? */ -+ case 0x00: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 61); -+ break; -+ /* e4 10 .. 00 | size == 35 ? */ -+ case 0x10: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 62); -+ break; -+ /* e4 18 .. 00 | size == 35 ? */ -+ case 0x18: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 63); -+ break; -+ /* e4 52 .. | size = 44 */ -+ case 0x52: if (packet_len == 44 ) return ((IPP2P_EDK * 100) + 64); -+ break; -+ /* e4 58 .. | size == 6 */ -+ case 0x58: if (packet_len == 14 ) return ((IPP2P_EDK * 100) + 65); -+ break; -+ /* e4 59 .. | size == 2 */ -+ case 0x59: if (packet_len == 10 )return ((IPP2P_EDK * 100) + 66); -+ break; -+ /* e4 28 .. | packet_len == 52,77,102,127... */ -+ case 0x28: if (((packet_len-52) % 25) == 0) return ((IPP2P_EDK * 100) + 67); -+ break; -+ /* e4 50 xx xx | size == 4 */ -+ case 0x50: if (packet_len == 12) return ((IPP2P_EDK * 100) + 68); -+ break; -+ /* e4 40 xx xx | size == 48 */ -+ case 0x40: if (packet_len == 56) return ((IPP2P_EDK * 100) + 69); -+ break; -+ } -+ break; -+ } -+ } /* end of switch (t[0]) */ -+ return 0; -+}/*udp_search_edk*/ -+ -+ -+/*Search for UDP Gnutella commands*/ -+int -+udp_search_gnu (unsigned char *haystack, int packet_len) -+{ -+ unsigned char *t = haystack; -+ t += 8; -+ -+ if (memcmp(t, "GND", 3) == 0) return ((IPP2P_GNU * 100) + 51); -+ if (memcmp(t, "GNUTELLA ", 9) == 0) return ((IPP2P_GNU * 100) + 52); -+ return 0; -+}/*udp_search_gnu*/ -+ -+ -+/*Search for UDP KaZaA commands*/ -+int -+udp_search_kazaa (unsigned char *haystack, int packet_len) -+{ -+ unsigned char *t = haystack; -+ -+ if (t[packet_len-1] == 0x00){ -+ t += (packet_len - 6); -+ if (memcmp(t, "KaZaA", 5) == 0) return (IPP2P_KAZAA * 100 +50); -+ } -+ -+ return 0; -+}/*udp_search_kazaa*/ -+ -+/*Search for UDP DirectConnect commands*/ -+int -+udp_search_directconnect (unsigned char *haystack, int packet_len) -+{ -+ unsigned char *t = haystack; -+ if ((*(t + 8) == 0x24) && (*(t + packet_len - 1) == 0x7c)) { -+ t+=8; -+ if (memcmp(t, "SR ", 3) == 0) return ((IPP2P_DC * 100) + 60); -+ if (memcmp(t, "Ping ", 5) == 0) return ((IPP2P_DC * 100) + 61); -+ } -+ return 0; -+}/*udp_search_directconnect*/ -+ -+ -+ -+/*Search for UDP BitTorrent commands*/ -+int -+udp_search_bit (unsigned char *haystack, int packet_len) -+{ -+ switch(packet_len) -+ { -+ case 24: -+ /* ^ 00 00 04 17 27 10 19 80 */ -+ if ((ntohl(get_u32(haystack, 8)) == 0x00000417) && (ntohl(get_u32(haystack, 12)) == 0x27101980)) -+ return (IPP2P_BIT * 100 + 50); -+ break; -+ case 44: -+ if (get_u32(haystack, 16) == __constant_htonl(0x00000400) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) -+ return (IPP2P_BIT * 100 + 51); -+ if (get_u32(haystack, 16) == __constant_htonl(0x00000400)) -+ return (IPP2P_BIT * 100 + 61); -+ break; -+ case 65: -+ if (get_u32(haystack, 16) == __constant_htonl(0x00000404) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) -+ return (IPP2P_BIT * 100 + 52); -+ if (get_u32(haystack, 16) == __constant_htonl(0x00000404)) -+ return (IPP2P_BIT * 100 + 62); -+ break; -+ case 67: -+ if (get_u32(haystack, 16) == __constant_htonl(0x00000406) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) -+ return (IPP2P_BIT * 100 + 53); -+ if (get_u32(haystack, 16) == __constant_htonl(0x00000406)) -+ return (IPP2P_BIT * 100 + 63); -+ break; -+ case 211: -+ if (get_u32(haystack, 8) == __constant_htonl(0x00000405)) -+ return (IPP2P_BIT * 100 + 54); -+ break; -+ case 29: -+ if ((get_u32(haystack, 8) == __constant_htonl(0x00000401))) -+ return (IPP2P_BIT * 100 + 55); -+ break; -+ case 52: -+ if (get_u32(haystack,8) == __constant_htonl(0x00000827) && -+ get_u32(haystack,12) == __constant_htonl(0x37502950)) -+ return (IPP2P_BIT * 100 + 80); -+ break; -+ default: -+ /* this packet does not have a constant size */ -+ if (packet_len >= 40 && get_u32(haystack, 16) == __constant_htonl(0x00000402) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) -+ return (IPP2P_BIT * 100 + 56); -+ break; -+ } -+ -+ /* some extra-bitcomet rules: -+ * "d1:" [a|r] "d2:id20:" -+ */ -+ if (packet_len > 30 && get_u8(haystack, 8) == 'd' && get_u8(haystack, 9) == '1' && get_u8(haystack, 10) == ':' ) -+ { -+ if (get_u8(haystack, 11) == 'a' || get_u8(haystack, 11) == 'r') -+ { -+ if (memcmp(haystack+12,"d2:id20:",8)==0) -+ return (IPP2P_BIT * 100 + 57); -+ } -+ } -+ -+#if 0 -+ /* bitlord rules */ -+ /* packetlen must be bigger than 40 */ -+ /* first 4 bytes are zero */ -+ if (packet_len > 40 && get_u32(haystack, 8) == 0x00000000) -+ { -+ /* first rule: 00 00 00 00 01 00 00 xx xx xx xx 00 00 00 00*/ -+ if (get_u32(haystack, 12) == 0x00000000 && -+ get_u32(haystack, 16) == 0x00010000 && -+ get_u32(haystack, 24) == 0x00000000 ) -+ return (IPP2P_BIT * 100 + 71); -+ -+ /* 00 01 00 00 0d 00 00 xx xx xx xx 00 00 00 00*/ -+ if (get_u32(haystack, 12) == 0x00000001 && -+ get_u32(haystack, 16) == 0x000d0000 && -+ get_u32(haystack, 24) == 0x00000000 ) -+ return (IPP2P_BIT * 100 + 71); -+ -+ -+ } -+#endif -+ -+ return 0; -+}/*udp_search_bit*/ -+ -+ -+ -+/*Search for Ares commands*/ -+//#define IPP2P_DEBUG_ARES -+int -+search_ares (const unsigned char *payload, const u16 plen) -+//int search_ares (unsigned char *haystack, int packet_len, int head_len) -+{ -+// const unsigned char *t = haystack + head_len; -+ -+ /* all ares packets start with */ -+ if (payload[1] == 0 && (plen - payload[0]) == 3) -+ { -+ switch (payload[2]) -+ { -+ case 0x5a: -+ /* ares connect */ -+ if ( plen == 6 && payload[5] == 0x05 ) return ((IPP2P_ARES * 100) + 1); -+ break; -+ case 0x09: -+ /* ares search, min 3 chars --> 14 bytes -+ * lets define a search can be up to 30 chars --> max 34 bytes -+ */ -+ if ( plen >= 14 && plen <= 34 ) return ((IPP2P_ARES * 100) + 1); -+ break; -+#ifdef IPP2P_DEBUG_ARES -+ default: -+ printk(KERN_DEBUG "Unknown Ares command %x recognized, len: %u \n", (unsigned int) payload[2],plen); -+#endif /* IPP2P_DEBUG_ARES */ -+ } -+ } -+ -+#if 0 -+ /* found connect packet: 03 00 5a 04 03 05 */ -+ /* new version ares 1.8: 03 00 5a xx xx 05 */ -+ if ((plen) == 6){ /* possible connect command*/ -+ if ((payload[0] == 0x03) && (payload[1] == 0x00) && (payload[2] == 0x5a) && (payload[5] == 0x05)) -+ return ((IPP2P_ARES * 100) + 1); -+ } -+ if ((plen) == 60){ /* possible download command*/ -+ if ((payload[59] == 0x0a) && (payload[58] == 0x0a)){ -+ if (memcmp(t, "PUSH SHA1:", 10) == 0) /* found download command */ -+ return ((IPP2P_ARES * 100) + 2); -+ } -+ } -+#endif -+ -+ return 0; -+} /*search_ares*/ -+ -+/*Search for SoulSeek commands*/ -+int -+search_soul (const unsigned char *payload, const u16 plen) -+{ -+//#define IPP2P_DEBUG_SOUL -+ /* match: xx xx xx xx | xx = sizeof(payload) - 4 */ -+ if (get_u32(payload, 0) == (plen - 4)){ -+ const __u32 m=get_u32(payload, 4); -+ /* match 00 yy yy 00, yy can be everything */ -+ if ( get_u8(payload, 4) == 0x00 && get_u8(payload, 7) == 0x00 ) -+ { -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "0: Soulseek command 0x%x recognized\n",get_u32(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 1); -+ } -+ -+ /* next match: 01 yy 00 00 | yy can be everything */ -+ if ( get_u8(payload, 4) == 0x01 && get_u16(payload, 6) == 0x0000 ) -+ { -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "1: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 2); -+ } -+ -+ /* other soulseek commandos are: 1-5,7,9,13-18,22,23,26,28,35-37,40-46,50,51,60,62-69,91,92,1001 */ -+ /* try to do this in an intelligent way */ -+ /* get all small commandos */ -+ switch(m) -+ { -+ case 7: -+ case 9: -+ case 22: -+ case 23: -+ case 26: -+ case 28: -+ case 50: -+ case 51: -+ case 60: -+ case 91: -+ case 92: -+ case 1001: -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "2: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 3); -+ } -+ -+ if (m > 0 && m < 6 ) -+ { -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "3: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 4); -+ } -+ if (m > 12 && m < 19 ) -+ { -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "4: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 5); -+ } -+ -+ if (m > 34 && m < 38 ) -+ { -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "5: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 6); -+ } -+ -+ if (m > 39 && m < 47 ) -+ { -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "6: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 7); -+ } -+ -+ if (m > 61 && m < 70 ) -+ { -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "7: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 8); -+ } -+ -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "unknown SOULSEEK command: 0x%x, first 16 bit: 0x%x, first 8 bit: 0x%x ,soulseek ???\n",get_u32(payload, 4),get_u16(payload, 4) >> 16,get_u8(payload, 4) >> 24); -+#endif /* IPP2P_DEBUG_SOUL */ -+ } -+ -+ /* match 14 00 00 00 01 yy 00 00 00 STRING(YY) 01 00 00 00 00 46|50 00 00 00 00 */ -+ /* without size at the beginning !!! */ -+ if ( get_u32(payload, 0) == 0x14 && get_u8(payload, 4) == 0x01 ) -+ { -+ __u32 y=get_u32(payload, 5); -+ /* we need 19 chars + string */ -+ if ( (y + 19) <= (plen) ) -+ { -+ const unsigned char *w=payload+9+y; -+ if (get_u32(w, 0) == 0x01 && ( get_u16(w, 4) == 0x4600 || get_u16(w, 4) == 0x5000) && get_u32(w, 6) == 0x00); -+#ifdef IPP2P_DEBUG_SOUL -+ printk(KERN_DEBUG "Soulssek special client command recognized\n"); -+#endif /* IPP2P_DEBUG_SOUL */ -+ return ((IPP2P_SOUL * 100) + 9); -+ } -+ } -+ return 0; -+} -+ -+ -+/*Search for WinMX commands*/ -+int -+search_winmx (const unsigned char *payload, const u16 plen) -+{ -+//#define IPP2P_DEBUG_WINMX -+ if (((plen) == 4) && (memcmp(payload, "SEND", 4) == 0)) return ((IPP2P_WINMX * 100) + 1); -+ if (((plen) == 3) && (memcmp(payload, "GET", 3) == 0)) return ((IPP2P_WINMX * 100) + 2); -+ //if (packet_len < (head_len + 10)) return 0; -+ if (plen < 10) return 0; -+ -+ if ((memcmp(payload, "SEND", 4) == 0) || (memcmp(payload, "GET", 3) == 0)){ -+ u16 c=4; -+ const u16 end=plen-2; -+ u8 count=0; -+ while (c < end) -+ { -+ if (payload[c]== 0x20 && payload[c+1] == 0x22) -+ { -+ c++; -+ count++; -+ if (count>=2) return ((IPP2P_WINMX * 100) + 3); -+ } -+ c++; -+ } -+ } -+ -+ if ( plen == 149 && payload[0] == '8' ) -+ { -+#ifdef IPP2P_DEBUG_WINMX -+ printk(KERN_INFO "maybe WinMX\n"); -+#endif -+ if (get_u32(payload,17) == 0 && get_u32(payload,21) == 0 && get_u32(payload,25) == 0 && -+// get_u32(payload,33) == __constant_htonl(0x71182b1a) && get_u32(payload,37) == __constant_htonl(0x05050000) && -+// get_u32(payload,133) == __constant_htonl(0x31097edf) && get_u32(payload,145) == __constant_htonl(0xdcb8f792)) -+ get_u16(payload,39) == 0 && get_u16(payload,135) == __constant_htons(0x7edf) && get_u16(payload,147) == __constant_htons(0xf792)) -+ -+ { -+#ifdef IPP2P_DEBUG_WINMX -+ printk(KERN_INFO "got WinMX\n"); -+#endif -+ return ((IPP2P_WINMX * 100) + 4); -+ } -+ } -+ return 0; -+} /*search_winmx*/ -+ -+ -+/*Search for appleJuice commands*/ -+int -+search_apple (const unsigned char *payload, const u16 plen) -+{ -+ if ( (plen > 7) && (payload[6] == 0x0d) && (payload[7] == 0x0a) && (memcmp(payload, "ajprot", 6) == 0)) return (IPP2P_APPLE * 100); -+ -+ return 0; -+} -+ -+ -+/*Search for BitTorrent commands*/ -+int -+search_bittorrent (const unsigned char *payload, const u16 plen) -+{ -+ if (plen > 20) -+ { -+ /* test for match 0x13+"BitTorrent protocol" */ -+ if (payload[0] == 0x13) -+ { -+ if (memcmp(payload+1, "BitTorrent protocol", 19) == 0) return (IPP2P_BIT * 100); -+ } -+ -+ /* get tracker commandos, all starts with GET / -+ * then it can follow: scrape| announce -+ * and then ?hash_info= -+ */ -+ if (memcmp(payload,"GET /",5) == 0) -+ { -+ /* message scrape */ -+ if ( memcmp(payload+5,"scrape?info_hash=",17)==0 ) return (IPP2P_BIT * 100 + 1); -+ /* message announce */ -+ if ( memcmp(payload+5,"announce?info_hash=",19)==0 ) return (IPP2P_BIT * 100 + 2); -+ } -+ } -+ else -+ { -+ /* bitcomet encryptes the first packet, so we have to detect another -+ * one later in the flow */ -+ /* first try failed, too many missdetections */ -+ //if ( size == 5 && get_u32(t,0) == __constant_htonl(1) && t[4] < 3) return (IPP2P_BIT * 100 + 3); -+ -+ /* second try: block request packets */ -+ if ( plen == 17 && get_u32(payload,0) == __constant_htonl(0x0d) && payload[4] == 0x06 && get_u32(payload,13) == __constant_htonl(0x4000) ) return (IPP2P_BIT * 100 + 3); -+ } -+ -+ return 0; -+} -+ -+ -+ -+/*check for Kazaa get command*/ -+int -+search_kazaa (const unsigned char *payload, const u16 plen) -+ -+{ -+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a) && memcmp(payload, "GET /.hash=", 11) == 0) -+ return (IPP2P_DATA_KAZAA * 100); -+ -+ return 0; -+} -+ -+ -+/*check for gnutella get command*/ -+int -+search_gnu (const unsigned char *payload, const u16 plen) -+{ -+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) -+ { -+ if (memcmp(payload, "GET /get/", 9) == 0) return ((IPP2P_DATA_GNU * 100) + 1); -+ if (memcmp(payload, "GET /uri-res/", 13) == 0) return ((IPP2P_DATA_GNU * 100) + 2); -+ } -+ return 0; -+} -+ -+ -+/*check for gnutella get commands and other typical data*/ -+int -+search_all_gnu (const unsigned char *payload, const u16 plen) -+{ -+ -+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) -+ { -+ -+ if (memcmp(payload, "GNUTELLA CONNECT/", 17) == 0) return ((IPP2P_GNU * 100) + 1); -+ if (memcmp(payload, "GNUTELLA/", 9) == 0) return ((IPP2P_GNU * 100) + 2); -+ -+ -+ if ((memcmp(payload, "GET /get/", 9) == 0) || (memcmp(payload, "GET /uri-res/", 13) == 0)) -+ { -+ u16 c=8; -+ const u16 end=plen-22; -+ while (c < end) { -+ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Gnutella-", 11) == 0) || (memcmp(&payload[c+2], "X-Queue:", 8) == 0))) -+ return ((IPP2P_GNU * 100) + 3); -+ c++; -+ } -+ } -+ } -+ return 0; -+} -+ -+ -+/*check for KaZaA download commands and other typical data*/ -+int -+search_all_kazaa (const unsigned char *payload, const u16 plen) -+{ -+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) -+ { -+ -+ if (memcmp(payload, "GIVE ", 5) == 0) return ((IPP2P_KAZAA * 100) + 1); -+ -+ if (memcmp(payload, "GET /", 5) == 0) { -+ u16 c = 8; -+ const u16 end=plen-22; -+ while (c < end) { -+ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Kazaa-Username: ", 18) == 0) || (memcmp(&payload[c+2], "User-Agent: PeerEnabler/", 24) == 0))) -+ return ((IPP2P_KAZAA * 100) + 2); -+ c++; -+ } -+ } -+ } -+ return 0; -+} -+ -+/*fast check for edonkey file segment transfer command*/ -+int -+search_edk (const unsigned char *payload, const u16 plen) -+{ -+ if (payload[0] != 0xe3) -+ return 0; -+ else { -+ if (payload[5] == 0x47) -+ return (IPP2P_DATA_EDK * 100); -+ else -+ return 0; -+ } -+} -+ -+ -+ -+/*intensive but slower search for some edonkey packets including size-check*/ -+int -+search_all_edk (const unsigned char *payload, const u16 plen) -+{ -+ if (payload[0] != 0xe3) -+ return 0; -+ else { -+ //t += head_len; -+ const u16 cmd = get_u16(payload, 1); -+ if (cmd == (plen - 5)) { -+ switch (payload[5]) { -+ case 0x01: return ((IPP2P_EDK * 100) + 1); /*Client: hello or Server:hello*/ -+ case 0x4c: return ((IPP2P_EDK * 100) + 9); /*Client: Hello-Answer*/ -+ } -+ } -+ return 0; -+ } -+} -+ -+ -+/*fast check for Direct Connect send command*/ -+int -+search_dc (const unsigned char *payload, const u16 plen) -+{ -+ -+ if (payload[0] != 0x24 ) -+ return 0; -+ else { -+ if (memcmp(&payload[1], "Send|", 5) == 0) -+ return (IPP2P_DATA_DC * 100); -+ else -+ return 0; -+ } -+ -+} -+ -+ -+/*intensive but slower check for all direct connect packets*/ -+int -+search_all_dc (const unsigned char *payload, const u16 plen) -+{ -+// unsigned char *t = haystack; -+ -+ if (payload[0] == 0x24 && payload[plen-1] == 0x7c) -+ { -+ const unsigned char *t=&payload[1]; -+ /* Client-Hub-Protocol */ -+ if (memcmp(t, "Lock ", 5) == 0) return ((IPP2P_DC * 100) + 1); -+ /* Client-Client-Protocol, some are already recognized by client-hub (like lock) */ -+ if (memcmp(t, "MyNick ", 7) == 0) return ((IPP2P_DC * 100) + 38); -+ } -+ return 0; -+} -+ -+/*check for mute*/ -+int -+search_mute (const unsigned char *payload, const u16 plen) -+{ -+ if ( plen == 209 || plen == 345 || plen == 473 || plen == 609 || plen == 1121 ) -+ { -+ //printk(KERN_DEBUG "size hit: %u",size); -+ if (memcmp(payload,"PublicKey: ",11) == 0 ) -+ { -+ return ((IPP2P_MUTE * 100) + 0); -+ -+/* if (memcmp(t+size-14,"\x0aEndPublicKey\x0a",14) == 0) -+ { -+ printk(KERN_DEBUG "end pubic key hit: %u",size); -+ -+ }*/ -+ } -+ } -+ return 0; -+} -+ -+ -+/* check for xdcc */ -+int -+search_xdcc (const unsigned char *payload, const u16 plen) -+{ -+ /* search in small packets only */ -+ if (plen > 20 && plen < 200 && payload[plen-1] == 0x0a && payload[plen-2] == 0x0d && memcmp(payload,"PRIVMSG ",8) == 0) -+ { -+ -+ u16 x=10; -+ const u16 end=plen - 13; -+ -+ /* is seems to be a irc private massage, chedck for xdcc command */ -+ while (x < end) -+ { -+ if (payload[x] == ':') -+ { -+ if ( memcmp(&payload[x+1],"xdcc send #",11) == 0 ) -+ return ((IPP2P_XDCC * 100) + 0); -+ } -+ x++; -+ } -+ } -+ return 0; -+} -+ -+/* search for waste */ -+int search_waste(const unsigned char *payload, const u16 plen) -+{ -+ if ( plen >= 8 && memcmp(payload,"GET.sha1:",9) == 0) -+ return ((IPP2P_WASTE * 100) + 0); -+ -+ return 0; -+} -+ -+ -+static struct { -+ int command; -+ __u8 short_hand; /*for fucntions included in short hands*/ -+ int packet_len; -+ int (*function_name) (const unsigned char *, const u16); -+} matchlist[] = { -+ {IPP2P_EDK,SHORT_HAND_IPP2P,20, &search_all_edk}, -+// {IPP2P_DATA_KAZAA,SHORT_HAND_DATA,200, &search_kazaa}, -+// {IPP2P_DATA_EDK,SHORT_HAND_DATA,60, &search_edk}, -+// {IPP2P_DATA_DC,SHORT_HAND_DATA,26, &search_dc}, -+ {IPP2P_DC,SHORT_HAND_IPP2P,5, search_all_dc}, -+// {IPP2P_DATA_GNU,SHORT_HAND_DATA,40, &search_gnu}, -+ {IPP2P_GNU,SHORT_HAND_IPP2P,5, &search_all_gnu}, -+ {IPP2P_KAZAA,SHORT_HAND_IPP2P,5, &search_all_kazaa}, -+ {IPP2P_BIT,SHORT_HAND_IPP2P,20, &search_bittorrent}, -+ {IPP2P_APPLE,SHORT_HAND_IPP2P,5, &search_apple}, -+ {IPP2P_SOUL,SHORT_HAND_IPP2P,5, &search_soul}, -+ {IPP2P_WINMX,SHORT_HAND_IPP2P,2, &search_winmx}, -+ {IPP2P_ARES,SHORT_HAND_IPP2P,5, &search_ares}, -+ {IPP2P_MUTE,SHORT_HAND_NONE,200, &search_mute}, -+ {IPP2P_WASTE,SHORT_HAND_NONE,5, &search_waste}, -+ {IPP2P_XDCC,SHORT_HAND_NONE,5, &search_xdcc}, -+ {0,0,0,NULL} -+}; -+ -+ -+static struct { -+ int command; -+ __u8 short_hand; /*for fucntions included in short hands*/ -+ int packet_len; -+ int (*function_name) (unsigned char *, int); -+} udp_list[] = { -+ {IPP2P_KAZAA,SHORT_HAND_IPP2P,14, &udp_search_kazaa}, -+ {IPP2P_BIT,SHORT_HAND_IPP2P,23, &udp_search_bit}, -+ {IPP2P_GNU,SHORT_HAND_IPP2P,11, &udp_search_gnu}, -+ {IPP2P_EDK,SHORT_HAND_IPP2P,9, &udp_search_edk}, -+ {IPP2P_DC,SHORT_HAND_IPP2P,12, &udp_search_directconnect}, -+ {0,0,0,NULL} -+}; -+ -+ -+static int -+match(const struct sk_buff *skb, -+ const struct net_device *in, -+ const struct net_device *out, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ const struct xt_match *match, -+#endif -+ const void *matchinfo, -+ int offset, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ unsigned int protoff, -+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ const void *hdr, -+ u_int16_t datalen, -+#endif -+ int *hotdrop) -+{ -+ const struct ipt_p2p_info *info = matchinfo; -+ unsigned char *haystack; -+ struct iphdr *ip = ip_hdr(skb); -+ int p2p_result = 0, i = 0; -+// int head_len; -+ int hlen = ntohs(ip->tot_len)-(ip->ihl*4); /*hlen = packet-data length*/ -+ -+ /*must not be a fragment*/ -+ if (offset) { -+ if (info->debug) printk("IPP2P.match: offset found %i \n",offset); -+ return 0; -+ } -+ -+ /*make sure that skb is linear*/ -+ if(skb_is_nonlinear(skb)){ -+ if (info->debug) printk("IPP2P.match: nonlinear skb found\n"); -+ return 0; -+ } -+ -+ -+ haystack=(char *)ip+(ip->ihl*4); /*haystack = packet data*/ -+ -+ switch (ip->protocol){ -+ case IPPROTO_TCP: /*what to do with a TCP packet*/ -+ { -+ struct tcphdr *tcph = (void *) ip + ip->ihl * 4; -+ -+ if (tcph->fin) return 0; /*if FIN bit is set bail out*/ -+ if (tcph->syn) return 0; /*if SYN bit is set bail out*/ -+ if (tcph->rst) return 0; /*if RST bit is set bail out*/ -+ -+ haystack += tcph->doff * 4; /*get TCP-Header-Size*/ -+ hlen -= tcph->doff * 4; -+ while (matchlist[i].command) { -+ if ((((info->cmd & matchlist[i].command) == matchlist[i].command) || -+ ((info->cmd & matchlist[i].short_hand) == matchlist[i].short_hand)) && -+ (hlen > matchlist[i].packet_len)) { -+ p2p_result = matchlist[i].function_name(haystack, hlen); -+ if (p2p_result) -+ { -+ if (info->debug) printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", -+ p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen); -+ return p2p_result; -+ } -+ } -+ i++; -+ } -+ return p2p_result; -+ } -+ -+ case IPPROTO_UDP: /*what to do with an UDP packet*/ -+ { -+ struct udphdr *udph = (void *) ip + ip->ihl * 4; -+ -+ while (udp_list[i].command){ -+ if ((((info->cmd & udp_list[i].command) == udp_list[i].command) || -+ ((info->cmd & udp_list[i].short_hand) == udp_list[i].short_hand)) && -+ (hlen > udp_list[i].packet_len)) { -+ p2p_result = udp_list[i].function_name(haystack, hlen); -+ if (p2p_result){ -+ if (info->debug) printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", -+ p2p_result, NIPQUAD(ip->saddr),ntohs(udph->source), NIPQUAD(ip->daddr),ntohs(udph->dest),hlen); -+ return p2p_result; -+ } -+ } -+ i++; -+ } -+ return p2p_result; -+ } -+ -+ default: return 0; -+ } -+} -+ -+ -+ -+static int -+checkentry(const char *tablename, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ const void *ip, -+ const struct xt_match *match, -+#else -+ const struct ipt_ip *ip, -+#endif -+ void *matchinfo, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ unsigned int matchsize, -+#endif -+ unsigned int hook_mask) -+{ -+ /* Must specify -p tcp */ -+/* if (ip->proto != IPPROTO_TCP || (ip->invflags & IPT_INV_PROTO)) { -+ * printk("ipp2p: Only works on TCP packets, use -p tcp\n"); -+ * return 0; -+ * }*/ -+ return 1; -+} -+ -+ -+ -+ -+static struct ipt_match ipp2p_match = { -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ { NULL, NULL }, -+ "ipp2p", -+ &match, -+ &checkentry, -+ NULL, -+ THIS_MODULE -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ .name = "ipp2p", -+ .match = &match, -+ .family = AF_INET, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ .matchsize = sizeof(struct ipt_p2p_info), -+#endif -+ .checkentry = &checkentry, -+ .me = THIS_MODULE, -+#endif -+}; -+ -+ -+static int __init init(void) -+{ -+ printk(KERN_INFO "IPP2P v%s loading\n", IPP2P_VERSION); -+ return xt_register_match(&ipp2p_match); -+} -+ -+static void __exit fini(void) -+{ -+ xt_unregister_match(&ipp2p_match); -+ printk(KERN_INFO "IPP2P v%s unloaded\n", IPP2P_VERSION); -+} -+ -+module_init(init); -+module_exit(fini); -+ -+ -Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig -+++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig -@@ -245,6 +245,12 @@ config IP_NF_MATCH_IPRANGE - - To compile it as a module, choose M here. If unsure, say N. - -+config IP_NF_MATCH_IPP2P -+ tristate "IPP2P" -+ depends on IP_NF_IPTABLES -+ help -+ Module for matching traffic of various Peer-to-Peer applications -+ - config IP_NF_MATCH_TOS - tristate "TOS match support" - depends on IP_NF_IPTABLES -Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile -+++ linux-2.6.21.7/net/ipv4/netfilter/Makefile -@@ -91,6 +91,7 @@ obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn - obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o - obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o - obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o -+obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o - - # targets - obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o diff --git a/target/linux/generic-2.6/patches/120-openswan-2.4.0.kernel-2.6-natt.patch b/target/linux/generic-2.6/patches/120-openswan-2.4.0.kernel-2.6-natt.patch deleted file mode 100644 index 6df04a5a41..0000000000 --- a/target/linux/generic-2.6/patches/120-openswan-2.4.0.kernel-2.6-natt.patch +++ /dev/null @@ -1,172 +0,0 @@ -Index: linux-2.6.21.7/include/net/xfrmudp.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/net/xfrmudp.h -@@ -0,0 +1,10 @@ -+/* -+ * pointer to function for type that xfrm4_input wants, to permit -+ * decoupling of XFRM from udp.c -+ */ -+#define HAVE_XFRM4_UDP_REGISTER -+ -+typedef int (*xfrm4_rcv_encap_t)(struct sk_buff *skb, __u16 encap_type); -+extern int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func -+ , xfrm4_rcv_encap_t *oldfunc); -+extern int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func); -Index: linux-2.6.21.7/net/ipv4/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/Kconfig -+++ linux-2.6.21.7/net/ipv4/Kconfig -@@ -266,6 +266,12 @@ config NET_IPGRE_BROADCAST - Network), but can be distributed all over the Internet. If you want - to do that, say Y here and to "IP multicast routing" below. - -+config IPSEC_NAT_TRAVERSAL -+ bool "IPSEC NAT-Traversal (KLIPS compatible)" -+ depends on INET -+ ---help--- -+ Includes support for RFC3947/RFC3948 NAT-Traversal of ESP over UDP. -+ - config IP_MROUTE - bool "IP: multicast routing" - depends on IP_MULTICAST -Index: linux-2.6.21.7/net/ipv4/udp.c -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/udp.c -+++ linux-2.6.21.7/net/ipv4/udp.c -@@ -101,12 +101,15 @@ - #include - #include - #include -+#include - #include "udp_impl.h" - - /* - * Snmp MIB for the UDP layer - */ - -+static xfrm4_rcv_encap_t xfrm4_rcv_encap_func; -+ - DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly; - - struct hlist_head udp_hash[UDP_HTABLE_SIZE]; -@@ -915,6 +918,42 @@ int udp_disconnect(struct sock *sk, int - return 0; - } - -+#if defined(CONFIG_XFRM) || defined(CONFIG_IPSEC_NAT_TRAVERSAL) -+ -+/* if XFRM isn't a module, then register it directly. */ -+#if 0 && !defined(CONFIG_XFRM_MODULE) && !defined(CONFIG_IPSEC_NAT_TRAVERSAL) -+static xfrm4_rcv_encap_t xfrm4_rcv_encap_func = xfrm4_rcv_encap; -+#else -+static xfrm4_rcv_encap_t xfrm4_rcv_encap_func = NULL; -+#endif -+ -+int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func -+ , xfrm4_rcv_encap_t *oldfunc) -+{ -+ if(oldfunc != NULL) { -+ *oldfunc = xfrm4_rcv_encap_func; -+ } -+ -+#if 0 -+ if(xfrm4_rcv_encap_func != NULL) -+ return -1; -+#endif -+ -+ xfrm4_rcv_encap_func = func; -+ return 0; -+} -+ -+int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func) -+{ -+ if(xfrm4_rcv_encap_func != func) -+ return -1; -+ -+ xfrm4_rcv_encap_func = NULL; -+ return 0; -+} -+#endif /* CONFIG_XFRM_MODULE || CONFIG_IPSEC_NAT_TRAVERSAL */ -+ -+ - /* return: - * 1 if the the UDP system should process it - * 0 if we should drop this packet -@@ -922,7 +961,7 @@ int udp_disconnect(struct sock *sk, int - */ - static int udp_encap_rcv(struct sock * sk, struct sk_buff *skb) - { --#ifndef CONFIG_XFRM -+#if !defined(CONFIG_XFRM) && !defined(CONFIG_IPSEC_NAT_TRAVERSAL) - return 1; - #else - struct udp_sock *up = udp_sk(sk); -@@ -937,11 +976,11 @@ static int udp_encap_rcv(struct sock * s - /* if we're overly short, let UDP handle it */ - len = skb->len - sizeof(struct udphdr); - if (len <= 0) -- return 1; -+ return 2; - - /* if this is not encapsulated socket, then just return now */ - if (!encap_type) -- return 1; -+ return 3; - - /* If this is a paged skb, make sure we pull up - * whatever data we need to look at. */ -@@ -964,7 +1003,7 @@ static int udp_encap_rcv(struct sock * s - len = sizeof(struct udphdr); - } else - /* Must be an IKE packet.. pass it through */ -- return 1; -+ return 4; - break; - case UDP_ENCAP_ESPINUDP_NON_IKE: - /* Check if this is a keepalive packet. If so, eat it. */ -@@ -977,7 +1016,7 @@ static int udp_encap_rcv(struct sock * s - len = sizeof(struct udphdr) + 2 * sizeof(u32); - } else - /* Must be an IKE packet.. pass it through */ -- return 1; -+ return 5; - break; - } - -@@ -988,6 +1027,8 @@ static int udp_encap_rcv(struct sock * s - */ - if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) - return 0; -+ if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) -+ return 0; - - /* Now we can update and verify the packet length... */ - iph = skb->nh.iph; -@@ -1051,9 +1092,13 @@ int udp_queue_rcv_skb(struct sock * sk, - return 0; - } - if (ret < 0) { -- /* process the ESP packet */ -- ret = xfrm4_rcv_encap(skb, up->encap_type); -- UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag); -+ if(xfrm4_rcv_encap_func != NULL) { -+ ret = (*xfrm4_rcv_encap_func)(skb, up->encap_type); -+ UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag); -+ } else { -+ UDP_INC_STATS_BH(UDP_MIB_INERRORS, up->pcflag); -+ ret = 1; -+ } - return -ret; - } - /* FALLTHROUGH -- it's a UDP Packet */ -@@ -1733,3 +1778,9 @@ EXPORT_SYMBOL(udp_poll); - EXPORT_SYMBOL(udp_proc_register); - EXPORT_SYMBOL(udp_proc_unregister); - #endif -+ -+#if defined(CONFIG_IPSEC_NAT_TRAVERSAL) -+EXPORT_SYMBOL(udp4_register_esp_rcvencap); -+EXPORT_SYMBOL(udp4_unregister_esp_rcvencap); -+#endif -+ diff --git a/target/linux/generic-2.6/patches/130-netfilter_ipset.patch b/target/linux/generic-2.6/patches/130-netfilter_ipset.patch deleted file mode 100644 index b59c158652..0000000000 --- a/target/linux/generic-2.6/patches/130-netfilter_ipset.patch +++ /dev/null @@ -1,7714 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set.h -@@ -0,0 +1,498 @@ -+#ifndef _IP_SET_H -+#define _IP_SET_H -+ -+/* Copyright (C) 2000-2002 Joakim Axelsson -+ * Patrick Schaaf -+ * Martin Josefsson -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#if 0 -+#define IP_SET_DEBUG -+#endif -+ -+/* -+ * A sockopt of such quality has hardly ever been seen before on the open -+ * market! This little beauty, hardly ever used: above 64, so it's -+ * traditionally used for firewalling, not touched (even once!) by the -+ * 2.0, 2.2 and 2.4 kernels! -+ * -+ * Comes with its own certificate of authenticity, valid anywhere in the -+ * Free world! -+ * -+ * Rusty, 19.4.2000 -+ */ -+#define SO_IP_SET 83 -+ -+/* -+ * Heavily modify by Joakim Axelsson 08.03.2002 -+ * - Made it more modulebased -+ * -+ * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 -+ * - bindings added -+ * - in order to "deal with" backward compatibility, renamed to ipset -+ */ -+ -+/* -+ * Used so that the kernel module and ipset-binary can match their versions -+ */ -+#define IP_SET_PROTOCOL_VERSION 2 -+ -+#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ -+ -+/* Lets work with our own typedef for representing an IP address. -+ * We hope to make the code more portable, possibly to IPv6... -+ * -+ * The representation works in HOST byte order, because most set types -+ * will perform arithmetic operations and compare operations. -+ * -+ * For now the type is an uint32_t. -+ * -+ * Make sure to ONLY use the functions when translating and parsing -+ * in order to keep the host byte order and make it more portable: -+ * parse_ip() -+ * parse_mask() -+ * parse_ipandmask() -+ * ip_tostring() -+ * (Joakim: where are they???) -+ */ -+ -+typedef uint32_t ip_set_ip_t; -+ -+/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t -+ * and IP_SET_INVALID_ID if you want to increase the max number of sets. -+ */ -+typedef uint16_t ip_set_id_t; -+ -+#define IP_SET_INVALID_ID 65535 -+ -+/* How deep we follow bindings */ -+#define IP_SET_MAX_BINDINGS 6 -+ -+/* -+ * Option flags for kernel operations (ipt_set_info) -+ */ -+#define IPSET_SRC 0x01 /* Source match/add */ -+#define IPSET_DST 0x02 /* Destination match/add */ -+#define IPSET_MATCH_INV 0x04 /* Inverse matching */ -+ -+/* -+ * Set features -+ */ -+#define IPSET_TYPE_IP 0x01 /* IP address type of set */ -+#define IPSET_TYPE_PORT 0x02 /* Port type of set */ -+#define IPSET_DATA_SINGLE 0x04 /* Single data storage */ -+#define IPSET_DATA_DOUBLE 0x08 /* Double data storage */ -+ -+/* Reserved keywords */ -+#define IPSET_TOKEN_DEFAULT ":default:" -+#define IPSET_TOKEN_ALL ":all:" -+ -+/* SO_IP_SET operation constants, and their request struct types. -+ * -+ * Operation ids: -+ * 0-99: commands with version checking -+ * 100-199: add/del/test/bind/unbind -+ * 200-299: list, save, restore -+ */ -+ -+/* Single shot operations: -+ * version, create, destroy, flush, rename and swap -+ * -+ * Sets are identified by name. -+ */ -+ -+#define IP_SET_REQ_STD \ -+ unsigned op; \ -+ unsigned version; \ -+ char name[IP_SET_MAXNAMELEN] -+ -+#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ -+struct ip_set_req_create { -+ IP_SET_REQ_STD; -+ char typename[IP_SET_MAXNAMELEN]; -+}; -+ -+#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ -+struct ip_set_req_std { -+ IP_SET_REQ_STD; -+}; -+ -+#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */ -+/* Uses ip_set_req_std */ -+ -+#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */ -+/* Uses ip_set_req_create */ -+ -+#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */ -+/* Uses ip_set_req_create */ -+ -+union ip_set_name_index { -+ char name[IP_SET_MAXNAMELEN]; -+ ip_set_id_t index; -+}; -+ -+#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ -+struct ip_set_req_get_set { -+ unsigned op; -+ unsigned version; -+ union ip_set_name_index set; -+}; -+ -+#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ -+/* Uses ip_set_req_get_set */ -+ -+#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ -+struct ip_set_req_version { -+ unsigned op; -+ unsigned version; -+}; -+ -+/* Double shots operations: -+ * add, del, test, bind and unbind. -+ * -+ * First we query the kernel to get the index and type of the target set, -+ * then issue the command. Validity of IP is checked in kernel in order -+ * to minimalize sockopt operations. -+ */ -+ -+/* Get minimal set data for add/del/test/bind/unbind IP */ -+#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */ -+struct ip_set_req_adt_get { -+ unsigned op; -+ unsigned version; -+ union ip_set_name_index set; -+ char typename[IP_SET_MAXNAMELEN]; -+}; -+ -+#define IP_SET_REQ_BYINDEX \ -+ unsigned op; \ -+ ip_set_id_t index; -+ -+struct ip_set_req_adt { -+ IP_SET_REQ_BYINDEX; -+}; -+ -+#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */ -+/* Uses ip_set_req_adt, with type specific addage */ -+ -+#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */ -+/* Uses ip_set_req_adt, with type specific addage */ -+ -+#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */ -+/* Uses ip_set_req_adt, with type specific addage */ -+ -+#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */ -+/* Uses ip_set_req_bind, with type specific addage */ -+struct ip_set_req_bind { -+ IP_SET_REQ_BYINDEX; -+ char binding[IP_SET_MAXNAMELEN]; -+}; -+ -+#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */ -+/* Uses ip_set_req_bind, with type speficic addage -+ * index = 0 means unbinding for all sets */ -+ -+#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */ -+/* Uses ip_set_req_bind, with type specific addage */ -+ -+/* Multiple shots operations: list, save, restore. -+ * -+ * - check kernel version and query the max number of sets -+ * - get the basic information on all sets -+ * and size required for the next step -+ * - get actual set data: header, data, bindings -+ */ -+ -+/* Get max_sets and the index of a queried set -+ */ -+#define IP_SET_OP_MAX_SETS 0x00000020 -+struct ip_set_req_max_sets { -+ unsigned op; -+ unsigned version; -+ ip_set_id_t max_sets; /* max_sets */ -+ ip_set_id_t sets; /* real number of sets */ -+ union ip_set_name_index set; /* index of set if name used */ -+}; -+ -+/* Get the id and name of the sets plus size for next step */ -+#define IP_SET_OP_LIST_SIZE 0x00000201 -+#define IP_SET_OP_SAVE_SIZE 0x00000202 -+struct ip_set_req_setnames { -+ unsigned op; -+ ip_set_id_t index; /* set to list/save */ -+ size_t size; /* size to get setdata/bindings */ -+ /* followed by sets number of struct ip_set_name_list */ -+}; -+ -+struct ip_set_name_list { -+ char name[IP_SET_MAXNAMELEN]; -+ char typename[IP_SET_MAXNAMELEN]; -+ ip_set_id_t index; -+ ip_set_id_t id; -+}; -+ -+/* The actual list operation */ -+#define IP_SET_OP_LIST 0x00000203 -+struct ip_set_req_list { -+ IP_SET_REQ_BYINDEX; -+ /* sets number of struct ip_set_list in reply */ -+}; -+ -+struct ip_set_list { -+ ip_set_id_t index; -+ ip_set_id_t binding; -+ u_int32_t ref; -+ size_t header_size; /* Set header data of header_size */ -+ size_t members_size; /* Set members data of members_size */ -+ size_t bindings_size; /* Set bindings data of bindings_size */ -+}; -+ -+struct ip_set_hash_list { -+ ip_set_ip_t ip; -+ ip_set_id_t binding; -+}; -+ -+/* The save operation */ -+#define IP_SET_OP_SAVE 0x00000204 -+/* Uses ip_set_req_list, in the reply replaced by -+ * sets number of struct ip_set_save plus a marker -+ * ip_set_save followed by ip_set_hash_save structures. -+ */ -+struct ip_set_save { -+ ip_set_id_t index; -+ ip_set_id_t binding; -+ size_t header_size; /* Set header data of header_size */ -+ size_t members_size; /* Set members data of members_size */ -+}; -+ -+/* At restoring, ip == 0 means default binding for the given set: */ -+struct ip_set_hash_save { -+ ip_set_ip_t ip; -+ ip_set_id_t id; -+ ip_set_id_t binding; -+}; -+ -+/* The restore operation */ -+#define IP_SET_OP_RESTORE 0x00000205 -+/* Uses ip_set_req_setnames followed by ip_set_restore structures -+ * plus a marker ip_set_restore, followed by ip_set_hash_save -+ * structures. -+ */ -+struct ip_set_restore { -+ char name[IP_SET_MAXNAMELEN]; -+ char typename[IP_SET_MAXNAMELEN]; -+ ip_set_id_t index; -+ size_t header_size; /* Create data of header_size */ -+ size_t members_size; /* Set members data of members_size */ -+}; -+ -+static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) -+{ -+ return 4 * ((((b - a + 8) / 8) + 3) / 4); -+} -+ -+#ifdef __KERNEL__ -+ -+#define ip_set_printk(format, args...) \ -+ do { \ -+ printk("%s: %s: ", __FILE__, __FUNCTION__); \ -+ printk(format "\n" , ## args); \ -+ } while (0) -+ -+#if defined(IP_SET_DEBUG) -+#define DP(format, args...) \ -+ do { \ -+ printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ -+ printk(format "\n" , ## args); \ -+ } while (0) -+#define IP_SET_ASSERT(x) \ -+ do { \ -+ if (!(x)) \ -+ printk("IP_SET_ASSERT: %s:%i(%s)\n", \ -+ __FILE__, __LINE__, __FUNCTION__); \ -+ } while (0) -+#else -+#define DP(format, args...) -+#define IP_SET_ASSERT(x) -+#endif -+ -+struct ip_set; -+ -+/* -+ * The ip_set_type definition - one per set type, e.g. "ipmap". -+ * -+ * Each individual set has a pointer, set->type, going to one -+ * of these structures. Function pointers inside the structure implement -+ * the real behaviour of the sets. -+ * -+ * If not mentioned differently, the implementation behind the function -+ * pointers of a set_type, is expected to return 0 if ok, and a negative -+ * errno (e.g. -EINVAL) on error. -+ */ -+struct ip_set_type { -+ struct list_head list; /* next in list of set types */ -+ -+ /* test for IP in set (kernel: iptables -m set src|dst) -+ * return 0 if not in set, 1 if in set. -+ */ -+ int (*testip_kernel) (struct ip_set *set, -+ const struct sk_buff * skb, -+ ip_set_ip_t *ip, -+ const u_int32_t *flags, -+ unsigned char index); -+ -+ /* test for IP in set (userspace: ipset -T set IP) -+ * return 0 if not in set, 1 if in set. -+ */ -+ int (*testip) (struct ip_set *set, -+ const void *data, size_t size, -+ ip_set_ip_t *ip); -+ -+ /* -+ * Size of the data structure passed by when -+ * adding/deletin/testing an entry. -+ */ -+ size_t reqsize; -+ -+ /* Add IP into set (userspace: ipset -A set IP) -+ * Return -EEXIST if the address is already in the set, -+ * and -ERANGE if the address lies outside the set bounds. -+ * If the address was not already in the set, 0 is returned. -+ */ -+ int (*addip) (struct ip_set *set, -+ const void *data, size_t size, -+ ip_set_ip_t *ip); -+ -+ /* Add IP into set (kernel: iptables ... -j SET set src|dst) -+ * Return -EEXIST if the address is already in the set, -+ * and -ERANGE if the address lies outside the set bounds. -+ * If the address was not already in the set, 0 is returned. -+ */ -+ int (*addip_kernel) (struct ip_set *set, -+ const struct sk_buff * skb, -+ ip_set_ip_t *ip, -+ const u_int32_t *flags, -+ unsigned char index); -+ -+ /* remove IP from set (userspace: ipset -D set --entry x) -+ * Return -EEXIST if the address is NOT in the set, -+ * and -ERANGE if the address lies outside the set bounds. -+ * If the address really was in the set, 0 is returned. -+ */ -+ int (*delip) (struct ip_set *set, -+ const void *data, size_t size, -+ ip_set_ip_t *ip); -+ -+ /* remove IP from set (kernel: iptables ... -j SET --entry x) -+ * Return -EEXIST if the address is NOT in the set, -+ * and -ERANGE if the address lies outside the set bounds. -+ * If the address really was in the set, 0 is returned. -+ */ -+ int (*delip_kernel) (struct ip_set *set, -+ const struct sk_buff * skb, -+ ip_set_ip_t *ip, -+ const u_int32_t *flags, -+ unsigned char index); -+ -+ /* new set creation - allocated type specific items -+ */ -+ int (*create) (struct ip_set *set, -+ const void *data, size_t size); -+ -+ /* retry the operation after successfully tweaking the set -+ */ -+ int (*retry) (struct ip_set *set); -+ -+ /* set destruction - free type specific items -+ * There is no return value. -+ * Can be called only when child sets are destroyed. -+ */ -+ void (*destroy) (struct ip_set *set); -+ -+ /* set flushing - reset all bits in the set, or something similar. -+ * There is no return value. -+ */ -+ void (*flush) (struct ip_set *set); -+ -+ /* Listing: size needed for header -+ */ -+ size_t header_size; -+ -+ /* Listing: Get the header -+ * -+ * Fill in the information in "data". -+ * This function is always run after list_header_size() under a -+ * writelock on the set. Therefor is the length of "data" always -+ * correct. -+ */ -+ void (*list_header) (const struct ip_set *set, -+ void *data); -+ -+ /* Listing: Get the size for the set members -+ */ -+ int (*list_members_size) (const struct ip_set *set); -+ -+ /* Listing: Get the set members -+ * -+ * Fill in the information in "data". -+ * This function is always run after list_member_size() under a -+ * writelock on the set. Therefor is the length of "data" always -+ * correct. -+ */ -+ void (*list_members) (const struct ip_set *set, -+ void *data); -+ -+ char typename[IP_SET_MAXNAMELEN]; -+ unsigned char features; -+ int protocol_version; -+ -+ /* Set this to THIS_MODULE if you are a module, otherwise NULL */ -+ struct module *me; -+}; -+ -+extern int ip_set_register_set_type(struct ip_set_type *set_type); -+extern void ip_set_unregister_set_type(struct ip_set_type *set_type); -+ -+/* A generic ipset */ -+struct ip_set { -+ char name[IP_SET_MAXNAMELEN]; /* the name of the set */ -+ rwlock_t lock; /* lock for concurrency control */ -+ ip_set_id_t id; /* set id for swapping */ -+ ip_set_id_t binding; /* default binding for the set */ -+ atomic_t ref; /* in kernel and in hash references */ -+ struct ip_set_type *type; /* the set types */ -+ void *data; /* pooltype specific data */ -+}; -+ -+/* Structure to bind set elements to sets */ -+struct ip_set_hash { -+ struct list_head list; /* list of clashing entries in hash */ -+ ip_set_ip_t ip; /* ip from set */ -+ ip_set_id_t id; /* set id */ -+ ip_set_id_t binding; /* set we bind the element to */ -+}; -+ -+/* register and unregister set references */ -+extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]); -+extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id); -+extern void ip_set_put(ip_set_id_t id); -+ -+/* API for iptables set match, and SET target */ -+extern void ip_set_addip_kernel(ip_set_id_t id, -+ const struct sk_buff *skb, -+ const u_int32_t *flags); -+extern void ip_set_delip_kernel(ip_set_id_t id, -+ const struct sk_buff *skb, -+ const u_int32_t *flags); -+extern int ip_set_testip_kernel(ip_set_id_t id, -+ const struct sk_buff *skb, -+ const u_int32_t *flags); -+ -+#endif /* __KERNEL__ */ -+ -+#endif /*_IP_SET_H*/ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iphash.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iphash.h -@@ -0,0 +1,30 @@ -+#ifndef __IP_SET_IPHASH_H -+#define __IP_SET_IPHASH_H -+ -+#include -+ -+#define SETTYPE_NAME "iphash" -+#define MAX_RANGE 0x0000FFFF -+ -+struct ip_set_iphash { -+ ip_set_ip_t *members; /* the iphash proper */ -+ uint32_t elements; /* number of elements */ -+ uint32_t hashsize; /* hash size */ -+ uint16_t probes; /* max number of probes */ -+ uint16_t resize; /* resize factor in percent */ -+ ip_set_ip_t netmask; /* netmask */ -+ void *initval[0]; /* initvals for jhash_1word */ -+}; -+ -+struct ip_set_req_iphash_create { -+ uint32_t hashsize; -+ uint16_t probes; -+ uint16_t resize; -+ ip_set_ip_t netmask; -+}; -+ -+struct ip_set_req_iphash { -+ ip_set_ip_t ip; -+}; -+ -+#endif /* __IP_SET_IPHASH_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipmap.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipmap.h -@@ -0,0 +1,56 @@ -+#ifndef __IP_SET_IPMAP_H -+#define __IP_SET_IPMAP_H -+ -+#include -+ -+#define SETTYPE_NAME "ipmap" -+#define MAX_RANGE 0x0000FFFF -+ -+struct ip_set_ipmap { -+ void *members; /* the ipmap proper */ -+ ip_set_ip_t first_ip; /* host byte order, included in range */ -+ ip_set_ip_t last_ip; /* host byte order, included in range */ -+ ip_set_ip_t netmask; /* subnet netmask */ -+ ip_set_ip_t sizeid; /* size of set in IPs */ -+ ip_set_ip_t hosts; /* number of hosts in a subnet */ -+}; -+ -+struct ip_set_req_ipmap_create { -+ ip_set_ip_t from; -+ ip_set_ip_t to; -+ ip_set_ip_t netmask; -+}; -+ -+struct ip_set_req_ipmap { -+ ip_set_ip_t ip; -+}; -+ -+unsigned int -+mask_to_bits(ip_set_ip_t mask) -+{ -+ unsigned int bits = 32; -+ ip_set_ip_t maskaddr; -+ -+ if (mask == 0xFFFFFFFF) -+ return bits; -+ -+ maskaddr = 0xFFFFFFFE; -+ while (--bits >= 0 && maskaddr != mask) -+ maskaddr <<= 1; -+ -+ return bits; -+} -+ -+ip_set_ip_t -+range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) -+{ -+ ip_set_ip_t mask = 0xFFFFFFFE; -+ -+ *bits = 32; -+ while (--(*bits) >= 0 && mask && (to & mask) != from) -+ mask <<= 1; -+ -+ return mask; -+} -+ -+#endif /* __IP_SET_IPMAP_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipporthash.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_ipporthash.h -@@ -0,0 +1,34 @@ -+#ifndef __IP_SET_IPPORTHASH_H -+#define __IP_SET_IPPORTHASH_H -+ -+#include -+ -+#define SETTYPE_NAME "ipporthash" -+#define MAX_RANGE 0x0000FFFF -+#define INVALID_PORT (MAX_RANGE + 1) -+ -+struct ip_set_ipporthash { -+ ip_set_ip_t *members; /* the ipporthash proper */ -+ uint32_t elements; /* number of elements */ -+ uint32_t hashsize; /* hash size */ -+ uint16_t probes; /* max number of probes */ -+ uint16_t resize; /* resize factor in percent */ -+ ip_set_ip_t first_ip; /* host byte order, included in range */ -+ ip_set_ip_t last_ip; /* host byte order, included in range */ -+ void *initval[0]; /* initvals for jhash_1word */ -+}; -+ -+struct ip_set_req_ipporthash_create { -+ uint32_t hashsize; -+ uint16_t probes; -+ uint16_t resize; -+ ip_set_ip_t from; -+ ip_set_ip_t to; -+}; -+ -+struct ip_set_req_ipporthash { -+ ip_set_ip_t ip; -+ ip_set_ip_t port; -+}; -+ -+#endif /* __IP_SET_IPPORTHASH_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptree.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptree.h -@@ -0,0 +1,40 @@ -+#ifndef __IP_SET_IPTREE_H -+#define __IP_SET_IPTREE_H -+ -+#include -+ -+#define SETTYPE_NAME "iptree" -+#define MAX_RANGE 0x0000FFFF -+ -+struct ip_set_iptreed { -+ unsigned long expires[256]; /* x.x.x.ADDR */ -+}; -+ -+struct ip_set_iptreec { -+ struct ip_set_iptreed *tree[256]; /* x.x.ADDR.* */ -+}; -+ -+struct ip_set_iptreeb { -+ struct ip_set_iptreec *tree[256]; /* x.ADDR.*.* */ -+}; -+ -+struct ip_set_iptree { -+ unsigned int timeout; -+ unsigned int gc_interval; -+#ifdef __KERNEL__ -+ uint32_t elements; /* number of elements */ -+ struct timer_list gc; -+ struct ip_set_iptreeb *tree[256]; /* ADDR.*.*.* */ -+#endif -+}; -+ -+struct ip_set_req_iptree_create { -+ unsigned int timeout; -+}; -+ -+struct ip_set_req_iptree { -+ ip_set_ip_t ip; -+ unsigned int timeout; -+}; -+ -+#endif /* __IP_SET_IPTREE_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptreemap.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_iptreemap.h -@@ -0,0 +1,40 @@ -+#ifndef __IP_SET_IPTREEMAP_H -+#define __IP_SET_IPTREEMAP_H -+ -+#include -+ -+#define SETTYPE_NAME "iptreemap" -+ -+#ifdef __KERNEL__ -+struct ip_set_iptreemap_d { -+ unsigned char bitmap[32]; /* x.x.x.y */ -+}; -+ -+struct ip_set_iptreemap_c { -+ struct ip_set_iptreemap_d *tree[256]; /* x.x.y.x */ -+}; -+ -+struct ip_set_iptreemap_b { -+ struct ip_set_iptreemap_c *tree[256]; /* x.y.x.x */ -+ unsigned char dirty[32]; -+}; -+#endif -+ -+struct ip_set_iptreemap { -+ unsigned int gc_interval; -+#ifdef __KERNEL__ -+ struct timer_list gc; -+ struct ip_set_iptreemap_b *tree[256]; /* y.x.x.x */ -+#endif -+}; -+ -+struct ip_set_req_iptreemap_create { -+ unsigned int gc_interval; -+}; -+ -+struct ip_set_req_iptreemap { -+ ip_set_ip_t start; -+ ip_set_ip_t end; -+}; -+ -+#endif /* __IP_SET_IPTREEMAP_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_jhash.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_jhash.h -@@ -0,0 +1,148 @@ -+#ifndef _LINUX_IPSET_JHASH_H -+#define _LINUX_IPSET_JHASH_H -+ -+/* This is a copy of linux/jhash.h but the types u32/u8 are changed -+ * to __u32/__u8 so that the header file can be included into -+ * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) -+ */ -+ -+/* jhash.h: Jenkins hash support. -+ * -+ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) -+ * -+ * http://burtleburtle.net/bob/hash/ -+ * -+ * These are the credits from Bob's sources: -+ * -+ * lookup2.c, by Bob Jenkins, December 1996, Public Domain. -+ * hash(), hash2(), hash3, and mix() are externally useful functions. -+ * Routines to test the hash are included if SELF_TEST is defined. -+ * You can use this free for any purpose. It has no warranty. -+ * -+ * Copyright (C) 2003 David S. Miller (davem@redhat.com) -+ * -+ * I've modified Bob's hash to be useful in the Linux kernel, and -+ * any bugs present are surely my fault. -DaveM -+ */ -+ -+/* NOTE: Arguments are modified. */ -+#define __jhash_mix(a, b, c) \ -+{ \ -+ a -= b; a -= c; a ^= (c>>13); \ -+ b -= c; b -= a; b ^= (a<<8); \ -+ c -= a; c -= b; c ^= (b>>13); \ -+ a -= b; a -= c; a ^= (c>>12); \ -+ b -= c; b -= a; b ^= (a<<16); \ -+ c -= a; c -= b; c ^= (b>>5); \ -+ a -= b; a -= c; a ^= (c>>3); \ -+ b -= c; b -= a; b ^= (a<<10); \ -+ c -= a; c -= b; c ^= (b>>15); \ -+} -+ -+/* The golden ration: an arbitrary value */ -+#define JHASH_GOLDEN_RATIO 0x9e3779b9 -+ -+/* The most generic version, hashes an arbitrary sequence -+ * of bytes. No alignment or length assumptions are made about -+ * the input key. -+ */ -+static inline __u32 jhash(void *key, __u32 length, __u32 initval) -+{ -+ __u32 a, b, c, len; -+ __u8 *k = key; -+ -+ len = length; -+ a = b = JHASH_GOLDEN_RATIO; -+ c = initval; -+ -+ while (len >= 12) { -+ a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); -+ b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); -+ c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); -+ -+ __jhash_mix(a,b,c); -+ -+ k += 12; -+ len -= 12; -+ } -+ -+ c += length; -+ switch (len) { -+ case 11: c += ((__u32)k[10]<<24); -+ case 10: c += ((__u32)k[9]<<16); -+ case 9 : c += ((__u32)k[8]<<8); -+ case 8 : b += ((__u32)k[7]<<24); -+ case 7 : b += ((__u32)k[6]<<16); -+ case 6 : b += ((__u32)k[5]<<8); -+ case 5 : b += k[4]; -+ case 4 : a += ((__u32)k[3]<<24); -+ case 3 : a += ((__u32)k[2]<<16); -+ case 2 : a += ((__u32)k[1]<<8); -+ case 1 : a += k[0]; -+ }; -+ -+ __jhash_mix(a,b,c); -+ -+ return c; -+} -+ -+/* A special optimized version that handles 1 or more of __u32s. -+ * The length parameter here is the number of __u32s in the key. -+ */ -+static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) -+{ -+ __u32 a, b, c, len; -+ -+ a = b = JHASH_GOLDEN_RATIO; -+ c = initval; -+ len = length; -+ -+ while (len >= 3) { -+ a += k[0]; -+ b += k[1]; -+ c += k[2]; -+ __jhash_mix(a, b, c); -+ k += 3; len -= 3; -+ } -+ -+ c += length * 4; -+ -+ switch (len) { -+ case 2 : b += k[1]; -+ case 1 : a += k[0]; -+ }; -+ -+ __jhash_mix(a,b,c); -+ -+ return c; -+} -+ -+ -+/* A special ultra-optimized versions that knows they are hashing exactly -+ * 3, 2 or 1 word(s). -+ * -+ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally -+ * done at the end is not done here. -+ */ -+static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) -+{ -+ a += JHASH_GOLDEN_RATIO; -+ b += JHASH_GOLDEN_RATIO; -+ c += initval; -+ -+ __jhash_mix(a, b, c); -+ -+ return c; -+} -+ -+static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) -+{ -+ return jhash_3words(a, b, 0, initval); -+} -+ -+static inline __u32 jhash_1word(__u32 a, __u32 initval) -+{ -+ return jhash_3words(a, 0, 0, initval); -+} -+ -+#endif /* _LINUX_IPSET_JHASH_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_macipmap.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_macipmap.h -@@ -0,0 +1,38 @@ -+#ifndef __IP_SET_MACIPMAP_H -+#define __IP_SET_MACIPMAP_H -+ -+#include -+ -+#define SETTYPE_NAME "macipmap" -+#define MAX_RANGE 0x0000FFFF -+ -+/* general flags */ -+#define IPSET_MACIP_MATCHUNSET 1 -+ -+/* per ip flags */ -+#define IPSET_MACIP_ISSET 1 -+ -+struct ip_set_macipmap { -+ void *members; /* the macipmap proper */ -+ ip_set_ip_t first_ip; /* host byte order, included in range */ -+ ip_set_ip_t last_ip; /* host byte order, included in range */ -+ u_int32_t flags; -+}; -+ -+struct ip_set_req_macipmap_create { -+ ip_set_ip_t from; -+ ip_set_ip_t to; -+ u_int32_t flags; -+}; -+ -+struct ip_set_req_macipmap { -+ ip_set_ip_t ip; -+ unsigned char ethernet[ETH_ALEN]; -+}; -+ -+struct ip_set_macip { -+ unsigned short flags; -+ unsigned char ethernet[ETH_ALEN]; -+}; -+ -+#endif /* __IP_SET_MACIPMAP_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_malloc.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_malloc.h -@@ -0,0 +1,116 @@ -+#ifndef _IP_SET_MALLOC_H -+#define _IP_SET_MALLOC_H -+ -+#ifdef __KERNEL__ -+ -+/* Memory allocation and deallocation */ -+static size_t max_malloc_size = 0; -+ -+static inline void init_max_malloc_size(void) -+{ -+#define CACHE(x) max_malloc_size = x; -+#include -+#undef CACHE -+} -+ -+static inline void * ip_set_malloc(size_t bytes) -+{ -+ if (bytes > max_malloc_size) -+ return vmalloc(bytes); -+ else -+ return kmalloc(bytes, GFP_KERNEL); -+} -+ -+static inline void ip_set_free(void * data, size_t bytes) -+{ -+ if (bytes > max_malloc_size) -+ vfree(data); -+ else -+ kfree(data); -+} -+ -+struct harray { -+ size_t max_elements; -+ void *arrays[0]; -+}; -+ -+static inline void * -+harray_malloc(size_t hashsize, size_t typesize, int flags) -+{ -+ struct harray *harray; -+ size_t max_elements, size, i, j; -+ -+ if (!max_malloc_size) -+ init_max_malloc_size(); -+ -+ if (typesize > max_malloc_size) -+ return NULL; -+ -+ max_elements = max_malloc_size/typesize; -+ size = hashsize/max_elements; -+ if (hashsize % max_elements) -+ size++; -+ -+ /* Last pointer signals end of arrays */ -+ harray = kmalloc(sizeof(struct harray) + (size + 1) * sizeof(void *), -+ flags); -+ -+ if (!harray) -+ return NULL; -+ -+ for (i = 0; i < size - 1; i++) { -+ harray->arrays[i] = kmalloc(max_elements * typesize, flags); -+ if (!harray->arrays[i]) -+ goto undo; -+ memset(harray->arrays[i], 0, max_elements * typesize); -+ } -+ harray->arrays[i] = kmalloc((hashsize - i * max_elements) * typesize, -+ flags); -+ if (!harray->arrays[i]) -+ goto undo; -+ memset(harray->arrays[i], 0, (hashsize - i * max_elements) * typesize); -+ -+ harray->max_elements = max_elements; -+ harray->arrays[size] = NULL; -+ -+ return (void *)harray; -+ -+ undo: -+ for (j = 0; j < i; j++) { -+ kfree(harray->arrays[j]); -+ } -+ kfree(harray); -+ return NULL; -+} -+ -+static inline void harray_free(void *h) -+{ -+ struct harray *harray = (struct harray *) h; -+ size_t i; -+ -+ for (i = 0; harray->arrays[i] != NULL; i++) -+ kfree(harray->arrays[i]); -+ kfree(harray); -+} -+ -+static inline void harray_flush(void *h, size_t hashsize, size_t typesize) -+{ -+ struct harray *harray = (struct harray *) h; -+ size_t i; -+ -+ for (i = 0; harray->arrays[i+1] != NULL; i++) -+ memset(harray->arrays[i], 0, harray->max_elements * typesize); -+ memset(harray->arrays[i], 0, -+ (hashsize - i * harray->max_elements) * typesize); -+} -+ -+#define HARRAY_ELEM(h, type, which) \ -+({ \ -+ struct harray *__h = (struct harray *)(h); \ -+ ((type)((__h)->arrays[(which)/(__h)->max_elements]) \ -+ + (which)%(__h)->max_elements); \ -+}) -+ -+#endif /* __KERNEL__ */ -+ -+#endif /*_IP_SET_MALLOC_H*/ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_nethash.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_nethash.h -@@ -0,0 +1,55 @@ -+#ifndef __IP_SET_NETHASH_H -+#define __IP_SET_NETHASH_H -+ -+#include -+ -+#define SETTYPE_NAME "nethash" -+#define MAX_RANGE 0x0000FFFF -+ -+struct ip_set_nethash { -+ ip_set_ip_t *members; /* the nethash proper */ -+ uint32_t elements; /* number of elements */ -+ uint32_t hashsize; /* hash size */ -+ uint16_t probes; /* max number of probes */ -+ uint16_t resize; /* resize factor in percent */ -+ unsigned char cidr[30]; /* CIDR sizes */ -+ void *initval[0]; /* initvals for jhash_1word */ -+}; -+ -+struct ip_set_req_nethash_create { -+ uint32_t hashsize; -+ uint16_t probes; -+ uint16_t resize; -+}; -+ -+struct ip_set_req_nethash { -+ ip_set_ip_t ip; -+ unsigned char cidr; -+}; -+ -+static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; -+ -+static inline ip_set_ip_t -+pack(ip_set_ip_t ip, unsigned char cidr) -+{ -+ ip_set_ip_t addr, *paddr = &addr; -+ unsigned char n, t, *a; -+ -+ addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); -+#ifdef __KERNEL__ -+ DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); -+#endif -+ n = cidr / 8; -+ t = cidr % 8; -+ a = &((unsigned char *)paddr)[n]; -+ *a = *a /(1 << (8 - t)) + shifts[t]; -+#ifdef __KERNEL__ -+ DP("n: %u, t: %u, a: %u", n, t, *a); -+ DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", -+ HIPQUAD(ip), cidr, NIPQUAD(addr)); -+#endif -+ -+ return ntohl(addr); -+} -+ -+#endif /* __IP_SET_NETHASH_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_portmap.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_set_portmap.h -@@ -0,0 +1,25 @@ -+#ifndef __IP_SET_PORTMAP_H -+#define __IP_SET_PORTMAP_H -+ -+#include -+ -+#define SETTYPE_NAME "portmap" -+#define MAX_RANGE 0x0000FFFF -+#define INVALID_PORT (MAX_RANGE + 1) -+ -+struct ip_set_portmap { -+ void *members; /* the portmap proper */ -+ ip_set_ip_t first_port; /* host byte order, included in range */ -+ ip_set_ip_t last_port; /* host byte order, included in range */ -+}; -+ -+struct ip_set_req_portmap_create { -+ ip_set_ip_t from; -+ ip_set_ip_t to; -+}; -+ -+struct ip_set_req_portmap { -+ ip_set_ip_t port; -+}; -+ -+#endif /* __IP_SET_PORTMAP_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_set.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_set.h -@@ -0,0 +1,21 @@ -+#ifndef _IPT_SET_H -+#define _IPT_SET_H -+ -+#include -+ -+struct ipt_set_info { -+ ip_set_id_t index; -+ u_int32_t flags[IP_SET_MAX_BINDINGS + 1]; -+}; -+ -+/* match info */ -+struct ipt_set_info_match { -+ struct ipt_set_info match_set; -+}; -+ -+struct ipt_set_info_target { -+ struct ipt_set_info add_set; -+ struct ipt_set_info del_set; -+}; -+ -+#endif /*_IPT_SET_H*/ -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set.c -@@ -0,0 +1,2003 @@ -+/* Copyright (C) 2000-2002 Joakim Axelsson -+ * Patrick Schaaf -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module for IP set management */ -+ -+#include -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define ASSERT_READ_LOCK(x) -+#define ASSERT_WRITE_LOCK(x) -+#include -+ -+static struct list_head set_type_list; /* all registered sets */ -+static struct ip_set **ip_set_list; /* all individual sets */ -+static DEFINE_RWLOCK(ip_set_lock); /* protects the lists and the hash */ -+static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */ -+static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX; -+static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE; -+static struct list_head *ip_set_hash; /* hash of bindings */ -+static unsigned int ip_set_hash_random; /* random seed */ -+ -+/* -+ * Sets are identified either by the index in ip_set_list or by id. -+ * The id never changes and is used to find a key in the hash. -+ * The index may change by swapping and used at all other places -+ * (set/SET netfilter modules, binding value, etc.) -+ * -+ * Userspace requests are serialized by ip_set_mutex and sets can -+ * be deleted only from userspace. Therefore ip_set_list locking -+ * must obey the following rules: -+ * -+ * - kernel requests: read and write locking mandatory -+ * - user requests: read locking optional, write locking mandatory -+ */ -+ -+static inline void -+__ip_set_get(ip_set_id_t index) -+{ -+ atomic_inc(&ip_set_list[index]->ref); -+} -+ -+static inline void -+__ip_set_put(ip_set_id_t index) -+{ -+ atomic_dec(&ip_set_list[index]->ref); -+} -+ -+/* -+ * Binding routines -+ */ -+ -+static inline struct ip_set_hash * -+__ip_set_find(u_int32_t key, ip_set_id_t id, ip_set_ip_t ip) -+{ -+ struct ip_set_hash *set_hash; -+ -+ list_for_each_entry(set_hash, &ip_set_hash[key], list) -+ if (set_hash->id == id && set_hash->ip == ip) -+ return set_hash; -+ -+ return NULL; -+} -+ -+static ip_set_id_t -+ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip) -+{ -+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) -+ % ip_set_bindings_hash_size; -+ struct ip_set_hash *set_hash; -+ -+ ASSERT_READ_LOCK(&ip_set_lock); -+ IP_SET_ASSERT(ip_set_list[id]); -+ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); -+ -+ set_hash = __ip_set_find(key, id, ip); -+ -+ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, -+ HIPQUAD(ip), -+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); -+ -+ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID); -+} -+ -+static inline void -+__set_hash_del(struct ip_set_hash *set_hash) -+{ -+ ASSERT_WRITE_LOCK(&ip_set_lock); -+ IP_SET_ASSERT(ip_set_list[set_hash->binding]); -+ -+ __ip_set_put(set_hash->binding); -+ list_del(&set_hash->list); -+ kfree(set_hash); -+} -+ -+static int -+ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip) -+{ -+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) -+ % ip_set_bindings_hash_size; -+ struct ip_set_hash *set_hash; -+ -+ IP_SET_ASSERT(ip_set_list[id]); -+ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); -+ write_lock_bh(&ip_set_lock); -+ set_hash = __ip_set_find(key, id, ip); -+ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, -+ HIPQUAD(ip), -+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); -+ -+ if (set_hash != NULL) -+ __set_hash_del(set_hash); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+} -+ -+static int -+ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding) -+{ -+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) -+ % ip_set_bindings_hash_size; -+ struct ip_set_hash *set_hash; -+ int ret = 0; -+ -+ IP_SET_ASSERT(ip_set_list[id]); -+ IP_SET_ASSERT(ip_set_list[binding]); -+ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, -+ HIPQUAD(ip), ip_set_list[binding]->name); -+ write_lock_bh(&ip_set_lock); -+ set_hash = __ip_set_find(key, id, ip); -+ if (!set_hash) { -+ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_ATOMIC); -+ if (!set_hash) { -+ ret = -ENOMEM; -+ goto unlock; -+ } -+ INIT_LIST_HEAD(&set_hash->list); -+ set_hash->id = id; -+ set_hash->ip = ip; -+ list_add(&set_hash->list, &ip_set_hash[key]); -+ } else { -+ IP_SET_ASSERT(ip_set_list[set_hash->binding]); -+ DP("overwrite binding: %s", -+ ip_set_list[set_hash->binding]->name); -+ __ip_set_put(set_hash->binding); -+ } -+ set_hash->binding = binding; -+ __ip_set_get(set_hash->binding); -+ DP("stored: key %u, id %u (%s), ip %u.%u.%u.%u, binding %u (%s)", -+ key, id, ip_set_list[id]->name, -+ HIPQUAD(ip), binding, ip_set_list[binding]->name); -+ unlock: -+ write_unlock_bh(&ip_set_lock); -+ return ret; -+} -+ -+#define FOREACH_HASH_DO(fn, args...) \ -+({ \ -+ ip_set_id_t __key; \ -+ struct ip_set_hash *__set_hash; \ -+ \ -+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ -+ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \ -+ fn(__set_hash , ## args); \ -+ } \ -+}) -+ -+#define FOREACH_HASH_RW_DO(fn, args...) \ -+({ \ -+ ip_set_id_t __key; \ -+ struct ip_set_hash *__set_hash, *__n; \ -+ \ -+ ASSERT_WRITE_LOCK(&ip_set_lock); \ -+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ -+ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\ -+ fn(__set_hash , ## args); \ -+ } \ -+}) -+ -+/* Add, del and test set entries from kernel */ -+ -+#define follow_bindings(index, set, ip) \ -+((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \ -+ || (index = (set)->binding) != IP_SET_INVALID_ID) -+ -+int -+ip_set_testip_kernel(ip_set_id_t index, -+ const struct sk_buff *skb, -+ const u_int32_t *flags) -+{ -+ struct ip_set *set; -+ ip_set_ip_t ip; -+ int res; -+ unsigned char i = 0; -+ -+ IP_SET_ASSERT(flags[i]); -+ read_lock_bh(&ip_set_lock); -+ do { -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ DP("set %s, index %u", set->name, index); -+ read_lock_bh(&set->lock); -+ res = set->type->testip_kernel(set, skb, &ip, flags, i++); -+ read_unlock_bh(&set->lock); -+ i += !!(set->type->features & IPSET_DATA_DOUBLE); -+ } while (res > 0 -+ && flags[i] -+ && follow_bindings(index, set, ip)); -+ read_unlock_bh(&ip_set_lock); -+ -+ return res; -+} -+ -+void -+ip_set_addip_kernel(ip_set_id_t index, -+ const struct sk_buff *skb, -+ const u_int32_t *flags) -+{ -+ struct ip_set *set; -+ ip_set_ip_t ip; -+ int res; -+ unsigned char i = 0; -+ -+ IP_SET_ASSERT(flags[i]); -+ retry: -+ read_lock_bh(&ip_set_lock); -+ do { -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ DP("set %s, index %u", set->name, index); -+ write_lock_bh(&set->lock); -+ res = set->type->addip_kernel(set, skb, &ip, flags, i++); -+ write_unlock_bh(&set->lock); -+ i += !!(set->type->features & IPSET_DATA_DOUBLE); -+ } while ((res == 0 || res == -EEXIST) -+ && flags[i] -+ && follow_bindings(index, set, ip)); -+ read_unlock_bh(&ip_set_lock); -+ -+ if (res == -EAGAIN -+ && set->type->retry -+ && (res = set->type->retry(set)) == 0) -+ goto retry; -+} -+ -+void -+ip_set_delip_kernel(ip_set_id_t index, -+ const struct sk_buff *skb, -+ const u_int32_t *flags) -+{ -+ struct ip_set *set; -+ ip_set_ip_t ip; -+ int res; -+ unsigned char i = 0; -+ -+ IP_SET_ASSERT(flags[i]); -+ read_lock_bh(&ip_set_lock); -+ do { -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ DP("set %s, index %u", set->name, index); -+ write_lock_bh(&set->lock); -+ res = set->type->delip_kernel(set, skb, &ip, flags, i++); -+ write_unlock_bh(&set->lock); -+ i += !!(set->type->features & IPSET_DATA_DOUBLE); -+ } while ((res == 0 || res == -EEXIST) -+ && flags[i] -+ && follow_bindings(index, set, ip)); -+ read_unlock_bh(&ip_set_lock); -+} -+ -+/* Register and deregister settype */ -+ -+static inline struct ip_set_type * -+find_set_type(const char *name) -+{ -+ struct ip_set_type *set_type; -+ -+ list_for_each_entry(set_type, &set_type_list, list) -+ if (!strncmp(set_type->typename, name, IP_SET_MAXNAMELEN - 1)) -+ return set_type; -+ return NULL; -+} -+ -+int -+ip_set_register_set_type(struct ip_set_type *set_type) -+{ -+ int ret = 0; -+ -+ if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { -+ ip_set_printk("'%s' uses wrong protocol version %u (want %u)", -+ set_type->typename, -+ set_type->protocol_version, -+ IP_SET_PROTOCOL_VERSION); -+ return -EINVAL; -+ } -+ -+ write_lock_bh(&ip_set_lock); -+ if (find_set_type(set_type->typename)) { -+ /* Duplicate! */ -+ ip_set_printk("'%s' already registered!", -+ set_type->typename); -+ ret = -EINVAL; -+ goto unlock; -+ } -+ if (!try_module_get(THIS_MODULE)) { -+ ret = -EFAULT; -+ goto unlock; -+ } -+ list_add(&set_type->list, &set_type_list); -+ DP("'%s' registered.", set_type->typename); -+ unlock: -+ write_unlock_bh(&ip_set_lock); -+ return ret; -+} -+ -+void -+ip_set_unregister_set_type(struct ip_set_type *set_type) -+{ -+ write_lock_bh(&ip_set_lock); -+ if (!find_set_type(set_type->typename)) { -+ ip_set_printk("'%s' not registered?", -+ set_type->typename); -+ goto unlock; -+ } -+ list_del(&set_type->list); -+ module_put(THIS_MODULE); -+ DP("'%s' unregistered.", set_type->typename); -+ unlock: -+ write_unlock_bh(&ip_set_lock); -+ -+} -+ -+/* -+ * Userspace routines -+ */ -+ -+/* -+ * Find set by name, reference it once. The reference makes sure the -+ * thing pointed to, does not go away under our feet. Drop the reference -+ * later, using ip_set_put(). -+ */ -+ip_set_id_t -+ip_set_get_byname(const char *name) -+{ -+ ip_set_id_t i, index = IP_SET_INVALID_ID; -+ -+ down(&ip_set_app_mutex); -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL -+ && strcmp(ip_set_list[i]->name, name) == 0) { -+ __ip_set_get(i); -+ index = i; -+ break; -+ } -+ } -+ up(&ip_set_app_mutex); -+ return index; -+} -+ -+/* -+ * Find set by index, reference it once. The reference makes sure the -+ * thing pointed to, does not go away under our feet. Drop the reference -+ * later, using ip_set_put(). -+ */ -+ip_set_id_t -+ip_set_get_byindex(ip_set_id_t index) -+{ -+ down(&ip_set_app_mutex); -+ -+ if (index >= ip_set_max) -+ return IP_SET_INVALID_ID; -+ -+ if (ip_set_list[index]) -+ __ip_set_get(index); -+ else -+ index = IP_SET_INVALID_ID; -+ -+ up(&ip_set_app_mutex); -+ return index; -+} -+ -+/* -+ * If the given set pointer points to a valid set, decrement -+ * reference count by 1. The caller shall not assume the index -+ * to be valid, after calling this function. -+ */ -+void ip_set_put(ip_set_id_t index) -+{ -+ down(&ip_set_app_mutex); -+ if (ip_set_list[index]) -+ __ip_set_put(index); -+ up(&ip_set_app_mutex); -+} -+ -+/* Find a set by name or index */ -+static ip_set_id_t -+ip_set_find_byname(const char *name) -+{ -+ ip_set_id_t i, index = IP_SET_INVALID_ID; -+ -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL -+ && strcmp(ip_set_list[i]->name, name) == 0) { -+ index = i; -+ break; -+ } -+ } -+ return index; -+} -+ -+static ip_set_id_t -+ip_set_find_byindex(ip_set_id_t index) -+{ -+ if (index >= ip_set_max || ip_set_list[index] == NULL) -+ index = IP_SET_INVALID_ID; -+ -+ return index; -+} -+ -+/* -+ * Add, del, test, bind and unbind -+ */ -+ -+static inline int -+__ip_set_testip(struct ip_set *set, -+ const void *data, -+ size_t size, -+ ip_set_ip_t *ip) -+{ -+ int res; -+ -+ read_lock_bh(&set->lock); -+ res = set->type->testip(set, data, size, ip); -+ read_unlock_bh(&set->lock); -+ -+ return res; -+} -+ -+static int -+__ip_set_addip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ ip_set_ip_t ip; -+ int res; -+ -+ IP_SET_ASSERT(set); -+ do { -+ write_lock_bh(&set->lock); -+ res = set->type->addip(set, data, size, &ip); -+ write_unlock_bh(&set->lock); -+ } while (res == -EAGAIN -+ && set->type->retry -+ && (res = set->type->retry(set)) == 0); -+ -+ return res; -+} -+ -+static int -+ip_set_addip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ -+ return __ip_set_addip(index, -+ data + sizeof(struct ip_set_req_adt), -+ size - sizeof(struct ip_set_req_adt)); -+} -+ -+static int -+ip_set_delip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ ip_set_ip_t ip; -+ int res; -+ -+ IP_SET_ASSERT(set); -+ write_lock_bh(&set->lock); -+ res = set->type->delip(set, -+ data + sizeof(struct ip_set_req_adt), -+ size - sizeof(struct ip_set_req_adt), -+ &ip); -+ write_unlock_bh(&set->lock); -+ -+ return res; -+} -+ -+static int -+ip_set_testip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ ip_set_ip_t ip; -+ int res; -+ -+ IP_SET_ASSERT(set); -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_adt), -+ size - sizeof(struct ip_set_req_adt), -+ &ip); -+ -+ return (res > 0 ? -EEXIST : res); -+} -+ -+static int -+ip_set_bindip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ struct ip_set_req_bind *req_bind; -+ ip_set_id_t binding; -+ ip_set_ip_t ip; -+ int res; -+ -+ IP_SET_ASSERT(set); -+ if (size < sizeof(struct ip_set_req_bind)) -+ return -EINVAL; -+ -+ req_bind = (struct ip_set_req_bind *) data; -+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of a set */ -+ char *binding_name; -+ -+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) -+ return -EINVAL; -+ -+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); -+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ binding = ip_set_find_byname(binding_name); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ write_lock_bh(&ip_set_lock); -+ /* Sets as binding values are referenced */ -+ if (set->binding != IP_SET_INVALID_ID) -+ __ip_set_put(set->binding); -+ set->binding = binding; -+ __ip_set_get(set->binding); -+ write_unlock_bh(&ip_set_lock); -+ -+ return 0; -+ } -+ binding = ip_set_find_byname(req_bind->binding); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_bind), -+ size - sizeof(struct ip_set_req_bind), -+ &ip); -+ DP("set %s, ip: %u.%u.%u.%u, binding %s", -+ set->name, HIPQUAD(ip), ip_set_list[binding]->name); -+ -+ if (res >= 0) -+ res = ip_set_hash_add(set->id, ip, binding); -+ -+ return res; -+} -+ -+#define FOREACH_SET_DO(fn, args...) \ -+({ \ -+ ip_set_id_t __i; \ -+ struct ip_set *__set; \ -+ \ -+ for (__i = 0; __i < ip_set_max; __i++) { \ -+ __set = ip_set_list[__i]; \ -+ if (__set != NULL) \ -+ fn(__set , ##args); \ -+ } \ -+}) -+ -+static inline void -+__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id) -+{ -+ if (set_hash->id == id) -+ __set_hash_del(set_hash); -+} -+ -+static inline void -+__unbind_default(struct ip_set *set) -+{ -+ if (set->binding != IP_SET_INVALID_ID) { -+ /* Sets as binding values are referenced */ -+ __ip_set_put(set->binding); -+ set->binding = IP_SET_INVALID_ID; -+ } -+} -+ -+static int -+ip_set_unbindip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set; -+ struct ip_set_req_bind *req_bind; -+ ip_set_ip_t ip; -+ int res; -+ -+ DP(""); -+ if (size < sizeof(struct ip_set_req_bind)) -+ return -EINVAL; -+ -+ req_bind = (struct ip_set_req_bind *) data; -+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ DP("%u %s", index, req_bind->binding); -+ if (index == IP_SET_INVALID_ID) { -+ /* unbind :all: */ -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of sets */ -+ write_lock_bh(&ip_set_lock); -+ FOREACH_SET_DO(__unbind_default); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { -+ /* Flush all bindings of all sets*/ -+ write_lock_bh(&ip_set_lock); -+ FOREACH_HASH_RW_DO(__set_hash_del); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+ } -+ DP("unreachable reached!"); -+ return -EINVAL; -+ } -+ -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of set */ -+ ip_set_id_t binding = ip_set_find_byindex(set->binding); -+ -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ write_lock_bh(&ip_set_lock); -+ /* Sets in hash values are referenced */ -+ __ip_set_put(set->binding); -+ set->binding = IP_SET_INVALID_ID; -+ write_unlock_bh(&ip_set_lock); -+ -+ return 0; -+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { -+ /* Flush all bindings */ -+ -+ write_lock_bh(&ip_set_lock); -+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+ } -+ -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_bind), -+ size - sizeof(struct ip_set_req_bind), -+ &ip); -+ -+ DP("set %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); -+ if (res >= 0) -+ res = ip_set_hash_del(set->id, ip); -+ -+ return res; -+} -+ -+static int -+ip_set_testbind(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ struct ip_set_req_bind *req_bind; -+ ip_set_id_t binding; -+ ip_set_ip_t ip; -+ int res; -+ -+ IP_SET_ASSERT(set); -+ if (size < sizeof(struct ip_set_req_bind)) -+ return -EINVAL; -+ -+ req_bind = (struct ip_set_req_bind *) data; -+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of set */ -+ char *binding_name; -+ -+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) -+ return -EINVAL; -+ -+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); -+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ binding = ip_set_find_byname(binding_name); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ res = (set->binding == binding) ? -EEXIST : 0; -+ -+ return res; -+ } -+ binding = ip_set_find_byname(req_bind->binding); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_bind), -+ size - sizeof(struct ip_set_req_bind), -+ &ip); -+ DP("set %s, ip: %u.%u.%u.%u, binding %s", -+ set->name, HIPQUAD(ip), ip_set_list[binding]->name); -+ -+ if (res >= 0) -+ res = (ip_set_find_in_hash(set->id, ip) == binding) -+ ? -EEXIST : 0; -+ -+ return res; -+} -+ -+static struct ip_set_type * -+find_set_type_rlock(const char *typename) -+{ -+ struct ip_set_type *type; -+ -+ read_lock_bh(&ip_set_lock); -+ type = find_set_type(typename); -+ if (type == NULL) -+ read_unlock_bh(&ip_set_lock); -+ -+ return type; -+} -+ -+static int -+find_free_id(const char *name, -+ ip_set_id_t *index, -+ ip_set_id_t *id) -+{ -+ ip_set_id_t i; -+ -+ *id = IP_SET_INVALID_ID; -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] == NULL) { -+ if (*id == IP_SET_INVALID_ID) -+ *id = *index = i; -+ } else if (strcmp(name, ip_set_list[i]->name) == 0) -+ /* Name clash */ -+ return -EEXIST; -+ } -+ if (*id == IP_SET_INVALID_ID) -+ /* No free slot remained */ -+ return -ERANGE; -+ /* Check that index is usable as id (swapping) */ -+ check: -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL -+ && ip_set_list[i]->id == *id) { -+ *id = i; -+ goto check; -+ } -+ } -+ return 0; -+} -+ -+/* -+ * Create a set -+ */ -+static int -+ip_set_create(const char *name, -+ const char *typename, -+ ip_set_id_t restore, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set; -+ ip_set_id_t index = 0, id; -+ int res = 0; -+ -+ DP("setname: %s, typename: %s, id: %u", name, typename, restore); -+ /* -+ * First, and without any locks, allocate and initialize -+ * a normal base set structure. -+ */ -+ set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); -+ if (!set) -+ return -ENOMEM; -+ set->lock = RW_LOCK_UNLOCKED; -+ strncpy(set->name, name, IP_SET_MAXNAMELEN); -+ set->binding = IP_SET_INVALID_ID; -+ atomic_set(&set->ref, 0); -+ -+ /* -+ * Next, take the &ip_set_lock, check that we know the type, -+ * and take a reference on the type, to make sure it -+ * stays available while constructing our new set. -+ * -+ * After referencing the type, we drop the &ip_set_lock, -+ * and let the new set construction run without locks. -+ */ -+ set->type = find_set_type_rlock(typename); -+ if (set->type == NULL) { -+ /* Try loading the module */ -+ char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1]; -+ strcpy(modulename, "ip_set_"); -+ strcat(modulename, typename); -+ DP("try to load %s", modulename); -+ request_module(modulename); -+ set->type = find_set_type_rlock(typename); -+ } -+ if (set->type == NULL) { -+ ip_set_printk("no set type '%s', set '%s' not created", -+ typename, name); -+ res = -ENOENT; -+ goto out; -+ } -+ if (!try_module_get(set->type->me)) { -+ read_unlock_bh(&ip_set_lock); -+ res = -EFAULT; -+ goto out; -+ } -+ read_unlock_bh(&ip_set_lock); -+ -+ /* -+ * Without holding any locks, create private part. -+ */ -+ res = set->type->create(set, data, size); -+ if (res != 0) -+ goto put_out; -+ -+ /* BTW, res==0 here. */ -+ -+ /* -+ * Here, we have a valid, constructed set. &ip_set_lock again, -+ * find free id/index and check that it is not already in -+ * ip_set_list. -+ */ -+ write_lock_bh(&ip_set_lock); -+ if ((res = find_free_id(set->name, &index, &id)) != 0) { -+ DP("no free id!"); -+ goto cleanup; -+ } -+ -+ /* Make sure restore gets the same index */ -+ if (restore != IP_SET_INVALID_ID && index != restore) { -+ DP("Can't restore, sets are screwed up"); -+ res = -ERANGE; -+ goto cleanup; -+ } -+ -+ /* -+ * Finally! Add our shiny new set to the list, and be done. -+ */ -+ DP("create: '%s' created with index %u, id %u!", set->name, index, id); -+ set->id = id; -+ ip_set_list[index] = set; -+ write_unlock_bh(&ip_set_lock); -+ return res; -+ -+ cleanup: -+ write_unlock_bh(&ip_set_lock); -+ set->type->destroy(set); -+ put_out: -+ module_put(set->type->me); -+ out: -+ kfree(set); -+ return res; -+} -+ -+/* -+ * Destroy a given existing set -+ */ -+static void -+ip_set_destroy_set(ip_set_id_t index) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ -+ IP_SET_ASSERT(set); -+ DP("set: %s", set->name); -+ write_lock_bh(&ip_set_lock); -+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); -+ if (set->binding != IP_SET_INVALID_ID) -+ __ip_set_put(set->binding); -+ ip_set_list[index] = NULL; -+ write_unlock_bh(&ip_set_lock); -+ -+ /* Must call it without holding any lock */ -+ set->type->destroy(set); -+ module_put(set->type->me); -+ kfree(set); -+} -+ -+/* -+ * Destroy a set - or all sets -+ * Sets must not be referenced/used. -+ */ -+static int -+ip_set_destroy(ip_set_id_t index) -+{ -+ ip_set_id_t i; -+ -+ /* ref modification always protected by the mutex */ -+ if (index != IP_SET_INVALID_ID) { -+ if (atomic_read(&ip_set_list[index]->ref)) -+ return -EBUSY; -+ ip_set_destroy_set(index); -+ } else { -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL -+ && (atomic_read(&ip_set_list[i]->ref))) -+ return -EBUSY; -+ } -+ -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL) -+ ip_set_destroy_set(i); -+ } -+ } -+ return 0; -+} -+ -+static void -+ip_set_flush_set(struct ip_set *set) -+{ -+ DP("set: %s %u", set->name, set->id); -+ -+ write_lock_bh(&set->lock); -+ set->type->flush(set); -+ write_unlock_bh(&set->lock); -+} -+ -+/* -+ * Flush data in a set - or in all sets -+ */ -+static int -+ip_set_flush(ip_set_id_t index) -+{ -+ if (index != IP_SET_INVALID_ID) { -+ IP_SET_ASSERT(ip_set_list[index]); -+ ip_set_flush_set(ip_set_list[index]); -+ } else -+ FOREACH_SET_DO(ip_set_flush_set); -+ -+ return 0; -+} -+ -+/* Rename a set */ -+static int -+ip_set_rename(ip_set_id_t index, const char *name) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ ip_set_id_t i; -+ int res = 0; -+ -+ DP("set: %s to %s", set->name, name); -+ write_lock_bh(&ip_set_lock); -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL -+ && strncmp(ip_set_list[i]->name, -+ name, -+ IP_SET_MAXNAMELEN - 1) == 0) { -+ res = -EEXIST; -+ goto unlock; -+ } -+ } -+ strncpy(set->name, name, IP_SET_MAXNAMELEN); -+ unlock: -+ write_unlock_bh(&ip_set_lock); -+ return res; -+} -+ -+/* -+ * Swap two sets so that name/index points to the other. -+ * References are also swapped. -+ */ -+static int -+ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index) -+{ -+ struct ip_set *from = ip_set_list[from_index]; -+ struct ip_set *to = ip_set_list[to_index]; -+ char from_name[IP_SET_MAXNAMELEN]; -+ u_int32_t from_ref; -+ -+ DP("set: %s to %s", from->name, to->name); -+ /* Features must not change. Artifical restriction. */ -+ if (from->type->features != to->type->features) -+ return -ENOEXEC; -+ -+ /* No magic here: ref munging protected by the mutex */ -+ write_lock_bh(&ip_set_lock); -+ strncpy(from_name, from->name, IP_SET_MAXNAMELEN); -+ from_ref = atomic_read(&from->ref); -+ -+ strncpy(from->name, to->name, IP_SET_MAXNAMELEN); -+ atomic_set(&from->ref, atomic_read(&to->ref)); -+ strncpy(to->name, from_name, IP_SET_MAXNAMELEN); -+ atomic_set(&to->ref, from_ref); -+ -+ ip_set_list[from_index] = to; -+ ip_set_list[to_index] = from; -+ -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+} -+ -+/* -+ * List set data -+ */ -+ -+static inline void -+__set_hash_bindings_size_list(struct ip_set_hash *set_hash, -+ ip_set_id_t id, size_t *size) -+{ -+ if (set_hash->id == id) -+ *size += sizeof(struct ip_set_hash_list); -+} -+ -+static inline void -+__set_hash_bindings_size_save(struct ip_set_hash *set_hash, -+ ip_set_id_t id, size_t *size) -+{ -+ if (set_hash->id == id) -+ *size += sizeof(struct ip_set_hash_save); -+} -+ -+static inline void -+__set_hash_bindings(struct ip_set_hash *set_hash, -+ ip_set_id_t id, void *data, int *used) -+{ -+ if (set_hash->id == id) { -+ struct ip_set_hash_list *hash_list = -+ (struct ip_set_hash_list *)(data + *used); -+ -+ hash_list->ip = set_hash->ip; -+ hash_list->binding = set_hash->binding; -+ *used += sizeof(struct ip_set_hash_list); -+ } -+} -+ -+static int ip_set_list_set(ip_set_id_t index, -+ void *data, -+ int *used, -+ int len) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ struct ip_set_list *set_list; -+ -+ /* Pointer to our header */ -+ set_list = (struct ip_set_list *) (data + *used); -+ -+ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used); -+ -+ /* Get and ensure header size */ -+ if (*used + sizeof(struct ip_set_list) > len) -+ goto not_enough_mem; -+ *used += sizeof(struct ip_set_list); -+ -+ read_lock_bh(&set->lock); -+ /* Get and ensure set specific header size */ -+ set_list->header_size = set->type->header_size; -+ if (*used + set_list->header_size > len) -+ goto unlock_set; -+ -+ /* Fill in the header */ -+ set_list->index = index; -+ set_list->binding = set->binding; -+ set_list->ref = atomic_read(&set->ref); -+ -+ /* Fill in set spefific header data */ -+ set->type->list_header(set, data + *used); -+ *used += set_list->header_size; -+ -+ /* Get and ensure set specific members size */ -+ set_list->members_size = set->type->list_members_size(set); -+ if (*used + set_list->members_size > len) -+ goto unlock_set; -+ -+ /* Fill in set spefific members data */ -+ set->type->list_members(set, data + *used); -+ *used += set_list->members_size; -+ read_unlock_bh(&set->lock); -+ -+ /* Bindings */ -+ -+ /* Get and ensure set specific bindings size */ -+ set_list->bindings_size = 0; -+ FOREACH_HASH_DO(__set_hash_bindings_size_list, -+ set->id, &set_list->bindings_size); -+ if (*used + set_list->bindings_size > len) -+ goto not_enough_mem; -+ -+ /* Fill in set spefific bindings data */ -+ FOREACH_HASH_DO(__set_hash_bindings, set->id, data, used); -+ -+ return 0; -+ -+ unlock_set: -+ read_unlock_bh(&set->lock); -+ not_enough_mem: -+ DP("not enough mem, try again"); -+ return -EAGAIN; -+} -+ -+/* -+ * Save sets -+ */ -+static int ip_set_save_set(ip_set_id_t index, -+ void *data, -+ int *used, -+ int len) -+{ -+ struct ip_set *set; -+ struct ip_set_save *set_save; -+ -+ /* Pointer to our header */ -+ set_save = (struct ip_set_save *) (data + *used); -+ -+ /* Get and ensure header size */ -+ if (*used + sizeof(struct ip_set_save) > len) -+ goto not_enough_mem; -+ *used += sizeof(struct ip_set_save); -+ -+ set = ip_set_list[index]; -+ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len, -+ data, data + *used); -+ -+ read_lock_bh(&set->lock); -+ /* Get and ensure set specific header size */ -+ set_save->header_size = set->type->header_size; -+ if (*used + set_save->header_size > len) -+ goto unlock_set; -+ -+ /* Fill in the header */ -+ set_save->index = index; -+ set_save->binding = set->binding; -+ -+ /* Fill in set spefific header data */ -+ set->type->list_header(set, data + *used); -+ *used += set_save->header_size; -+ -+ DP("set header filled: %s, used: %u(%u) %p %p", set->name, *used, -+ set_save->header_size, data, data + *used); -+ /* Get and ensure set specific members size */ -+ set_save->members_size = set->type->list_members_size(set); -+ if (*used + set_save->members_size > len) -+ goto unlock_set; -+ -+ /* Fill in set spefific members data */ -+ set->type->list_members(set, data + *used); -+ *used += set_save->members_size; -+ read_unlock_bh(&set->lock); -+ DP("set members filled: %s, used: %u(%u) %p %p", set->name, *used, -+ set_save->members_size, data, data + *used); -+ return 0; -+ -+ unlock_set: -+ read_unlock_bh(&set->lock); -+ not_enough_mem: -+ DP("not enough mem, try again"); -+ return -EAGAIN; -+} -+ -+static inline void -+__set_hash_save_bindings(struct ip_set_hash *set_hash, -+ ip_set_id_t id, -+ void *data, -+ int *used, -+ int len, -+ int *res) -+{ -+ if (*res == 0 -+ && (id == IP_SET_INVALID_ID || set_hash->id == id)) { -+ struct ip_set_hash_save *hash_save = -+ (struct ip_set_hash_save *)(data + *used); -+ /* Ensure bindings size */ -+ if (*used + sizeof(struct ip_set_hash_save) > len) { -+ *res = -ENOMEM; -+ return; -+ } -+ hash_save->id = set_hash->id; -+ hash_save->ip = set_hash->ip; -+ hash_save->binding = set_hash->binding; -+ *used += sizeof(struct ip_set_hash_save); -+ } -+} -+ -+static int ip_set_save_bindings(ip_set_id_t index, -+ void *data, -+ int *used, -+ int len) -+{ -+ int res = 0; -+ struct ip_set_save *set_save; -+ -+ DP("used %u, len %u", *used, len); -+ /* Get and ensure header size */ -+ if (*used + sizeof(struct ip_set_save) > len) -+ return -ENOMEM; -+ -+ /* Marker */ -+ set_save = (struct ip_set_save *) (data + *used); -+ set_save->index = IP_SET_INVALID_ID; -+ set_save->header_size = 0; -+ set_save->members_size = 0; -+ *used += sizeof(struct ip_set_save); -+ -+ DP("marker added used %u, len %u", *used, len); -+ /* Fill in bindings data */ -+ if (index != IP_SET_INVALID_ID) -+ /* Sets are identified by id in hash */ -+ index = ip_set_list[index]->id; -+ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res); -+ -+ return res; -+} -+ -+/* -+ * Restore sets -+ */ -+static int ip_set_restore(void *data, -+ int len) -+{ -+ int res = 0; -+ int line = 0, used = 0, members_size; -+ struct ip_set *set; -+ struct ip_set_hash_save *hash_save; -+ struct ip_set_restore *set_restore; -+ ip_set_id_t index; -+ -+ /* Loop to restore sets */ -+ while (1) { -+ line++; -+ -+ DP("%u %u %u", used, sizeof(struct ip_set_restore), len); -+ /* Get and ensure header size */ -+ if (used + sizeof(struct ip_set_restore) > len) -+ return line; -+ set_restore = (struct ip_set_restore *) (data + used); -+ used += sizeof(struct ip_set_restore); -+ -+ /* Ensure data size */ -+ if (used -+ + set_restore->header_size -+ + set_restore->members_size > len) -+ return line; -+ -+ /* Check marker */ -+ if (set_restore->index == IP_SET_INVALID_ID) { -+ line--; -+ goto bindings; -+ } -+ -+ /* Try to create the set */ -+ DP("restore %s %s", set_restore->name, set_restore->typename); -+ res = ip_set_create(set_restore->name, -+ set_restore->typename, -+ set_restore->index, -+ data + used, -+ set_restore->header_size); -+ -+ if (res != 0) -+ return line; -+ used += set_restore->header_size; -+ -+ index = ip_set_find_byindex(set_restore->index); -+ DP("index %u, restore_index %u", index, set_restore->index); -+ if (index != set_restore->index) -+ return line; -+ /* Try to restore members data */ -+ set = ip_set_list[index]; -+ members_size = 0; -+ DP("members_size %u reqsize %u", -+ set_restore->members_size, set->type->reqsize); -+ while (members_size + set->type->reqsize <= -+ set_restore->members_size) { -+ line++; -+ DP("members: %u, line %u", members_size, line); -+ res = __ip_set_addip(index, -+ data + used + members_size, -+ set->type->reqsize); -+ if (!(res == 0 || res == -EEXIST)) -+ return line; -+ members_size += set->type->reqsize; -+ } -+ -+ DP("members_size %u %u", -+ set_restore->members_size, members_size); -+ if (members_size != set_restore->members_size) -+ return line++; -+ used += set_restore->members_size; -+ } -+ -+ bindings: -+ /* Loop to restore bindings */ -+ while (used < len) { -+ line++; -+ -+ DP("restore binding, line %u", line); -+ /* Get and ensure size */ -+ if (used + sizeof(struct ip_set_hash_save) > len) -+ return line; -+ hash_save = (struct ip_set_hash_save *) (data + used); -+ used += sizeof(struct ip_set_hash_save); -+ -+ /* hash_save->id is used to store the index */ -+ index = ip_set_find_byindex(hash_save->id); -+ DP("restore binding index %u, id %u, %u -> %u", -+ index, hash_save->id, hash_save->ip, hash_save->binding); -+ if (index != hash_save->id) -+ return line; -+ if (ip_set_find_byindex(hash_save->binding) == IP_SET_INVALID_ID) { -+ DP("corrupt binding set index %u", hash_save->binding); -+ return line; -+ } -+ set = ip_set_list[hash_save->id]; -+ /* Null valued IP means default binding */ -+ if (hash_save->ip) -+ res = ip_set_hash_add(set->id, -+ hash_save->ip, -+ hash_save->binding); -+ else { -+ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID); -+ write_lock_bh(&ip_set_lock); -+ set->binding = hash_save->binding; -+ __ip_set_get(set->binding); -+ write_unlock_bh(&ip_set_lock); -+ DP("default binding: %u", set->binding); -+ } -+ if (res != 0) -+ return line; -+ } -+ if (used != len) -+ return line; -+ -+ return 0; -+} -+ -+static int -+ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) -+{ -+ void *data; -+ int res = 0; /* Assume OK */ -+ unsigned *op; -+ struct ip_set_req_adt *req_adt; -+ ip_set_id_t index = IP_SET_INVALID_ID; -+ int (*adtfn)(ip_set_id_t index, -+ const void *data, size_t size); -+ struct fn_table { -+ int (*fn)(ip_set_id_t index, -+ const void *data, size_t size); -+ } adtfn_table[] = -+ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, -+ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind }, -+ }; -+ -+ DP("optval=%d, user=%p, len=%d", optval, user, len); -+ if (!capable(CAP_NET_ADMIN)) -+ return -EPERM; -+ if (optval != SO_IP_SET) -+ return -EBADF; -+ if (len <= sizeof(unsigned)) { -+ ip_set_printk("short userdata (want >%zu, got %u)", -+ sizeof(unsigned), len); -+ return -EINVAL; -+ } -+ data = vmalloc(len); -+ if (!data) { -+ DP("out of mem for %u bytes", len); -+ return -ENOMEM; -+ } -+ if (copy_from_user(data, user, len) != 0) { -+ res = -EFAULT; -+ goto done; -+ } -+ if (down_interruptible(&ip_set_app_mutex)) { -+ res = -EINTR; -+ goto done; -+ } -+ -+ op = (unsigned *)data; -+ DP("op=%x", *op); -+ -+ if (*op < IP_SET_OP_VERSION) { -+ /* Check the version at the beginning of operations */ -+ struct ip_set_req_version *req_version = -+ (struct ip_set_req_version *) data; -+ if (req_version->version != IP_SET_PROTOCOL_VERSION) { -+ res = -EPROTO; -+ goto done; -+ } -+ } -+ -+ switch (*op) { -+ case IP_SET_OP_CREATE:{ -+ struct ip_set_req_create *req_create -+ = (struct ip_set_req_create *) data; -+ -+ if (len < sizeof(struct ip_set_req_create)) { -+ ip_set_printk("short CREATE data (want >=%zu, got %u)", -+ sizeof(struct ip_set_req_create), len); -+ res = -EINVAL; -+ goto done; -+ } -+ req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0'; -+ res = ip_set_create(req_create->name, -+ req_create->typename, -+ IP_SET_INVALID_ID, -+ data + sizeof(struct ip_set_req_create), -+ len - sizeof(struct ip_set_req_create)); -+ goto done; -+ } -+ case IP_SET_OP_DESTROY:{ -+ struct ip_set_req_std *req_destroy -+ = (struct ip_set_req_std *) data; -+ -+ if (len != sizeof(struct ip_set_req_std)) { -+ ip_set_printk("invalid DESTROY data (want %zu, got %u)", -+ sizeof(struct ip_set_req_std), len); -+ res = -EINVAL; -+ goto done; -+ } -+ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) { -+ /* Destroy all sets */ -+ index = IP_SET_INVALID_ID; -+ } else { -+ req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ index = ip_set_find_byname(req_destroy->name); -+ -+ if (index == IP_SET_INVALID_ID) { -+ res = -ENOENT; -+ goto done; -+ } -+ } -+ -+ res = ip_set_destroy(index); -+ goto done; -+ } -+ case IP_SET_OP_FLUSH:{ -+ struct ip_set_req_std *req_flush = -+ (struct ip_set_req_std *) data; -+ -+ if (len != sizeof(struct ip_set_req_std)) { -+ ip_set_printk("invalid FLUSH data (want %zu, got %u)", -+ sizeof(struct ip_set_req_std), len); -+ res = -EINVAL; -+ goto done; -+ } -+ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) { -+ /* Flush all sets */ -+ index = IP_SET_INVALID_ID; -+ } else { -+ req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ index = ip_set_find_byname(req_flush->name); -+ -+ if (index == IP_SET_INVALID_ID) { -+ res = -ENOENT; -+ goto done; -+ } -+ } -+ res = ip_set_flush(index); -+ goto done; -+ } -+ case IP_SET_OP_RENAME:{ -+ struct ip_set_req_create *req_rename -+ = (struct ip_set_req_create *) data; -+ -+ if (len != sizeof(struct ip_set_req_create)) { -+ ip_set_printk("invalid RENAME data (want %zu, got %u)", -+ sizeof(struct ip_set_req_create), len); -+ res = -EINVAL; -+ goto done; -+ } -+ -+ req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ index = ip_set_find_byname(req_rename->name); -+ if (index == IP_SET_INVALID_ID) { -+ res = -ENOENT; -+ goto done; -+ } -+ res = ip_set_rename(index, req_rename->typename); -+ goto done; -+ } -+ case IP_SET_OP_SWAP:{ -+ struct ip_set_req_create *req_swap -+ = (struct ip_set_req_create *) data; -+ ip_set_id_t to_index; -+ -+ if (len != sizeof(struct ip_set_req_create)) { -+ ip_set_printk("invalid SWAP data (want %zu, got %u)", -+ sizeof(struct ip_set_req_create), len); -+ res = -EINVAL; -+ goto done; -+ } -+ -+ req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ index = ip_set_find_byname(req_swap->name); -+ if (index == IP_SET_INVALID_ID) { -+ res = -ENOENT; -+ goto done; -+ } -+ to_index = ip_set_find_byname(req_swap->typename); -+ if (to_index == IP_SET_INVALID_ID) { -+ res = -ENOENT; -+ goto done; -+ } -+ res = ip_set_swap(index, to_index); -+ goto done; -+ } -+ default: -+ break; /* Set identified by id */ -+ } -+ -+ /* There we may have add/del/test/bind/unbind/test_bind operations */ -+ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) { -+ res = -EBADMSG; -+ goto done; -+ } -+ adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn; -+ -+ if (len < sizeof(struct ip_set_req_adt)) { -+ ip_set_printk("short data in adt request (want >=%zu, got %u)", -+ sizeof(struct ip_set_req_adt), len); -+ res = -EINVAL; -+ goto done; -+ } -+ req_adt = (struct ip_set_req_adt *) data; -+ -+ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */ -+ if (!(*op == IP_SET_OP_UNBIND_SET -+ && req_adt->index == IP_SET_INVALID_ID)) { -+ index = ip_set_find_byindex(req_adt->index); -+ if (index == IP_SET_INVALID_ID) { -+ res = -ENOENT; -+ goto done; -+ } -+ } -+ res = adtfn(index, data, len); -+ -+ done: -+ up(&ip_set_app_mutex); -+ vfree(data); -+ if (res > 0) -+ res = 0; -+ DP("final result %d", res); -+ return res; -+} -+ -+static int -+ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) -+{ -+ int res = 0; -+ unsigned *op; -+ ip_set_id_t index = IP_SET_INVALID_ID; -+ void *data; -+ int copylen = *len; -+ -+ DP("optval=%d, user=%p, len=%d", optval, user, *len); -+ if (!capable(CAP_NET_ADMIN)) -+ return -EPERM; -+ if (optval != SO_IP_SET) -+ return -EBADF; -+ if (*len < sizeof(unsigned)) { -+ ip_set_printk("short userdata (want >=%zu, got %d)", -+ sizeof(unsigned), *len); -+ return -EINVAL; -+ } -+ data = vmalloc(*len); -+ if (!data) { -+ DP("out of mem for %d bytes", *len); -+ return -ENOMEM; -+ } -+ if (copy_from_user(data, user, *len) != 0) { -+ res = -EFAULT; -+ goto done; -+ } -+ if (down_interruptible(&ip_set_app_mutex)) { -+ res = -EINTR; -+ goto done; -+ } -+ -+ op = (unsigned *) data; -+ DP("op=%x", *op); -+ -+ if (*op < IP_SET_OP_VERSION) { -+ /* Check the version at the beginning of operations */ -+ struct ip_set_req_version *req_version = -+ (struct ip_set_req_version *) data; -+ if (req_version->version != IP_SET_PROTOCOL_VERSION) { -+ res = -EPROTO; -+ goto done; -+ } -+ } -+ -+ switch (*op) { -+ case IP_SET_OP_VERSION: { -+ struct ip_set_req_version *req_version = -+ (struct ip_set_req_version *) data; -+ -+ if (*len != sizeof(struct ip_set_req_version)) { -+ ip_set_printk("invalid VERSION (want %zu, got %d)", -+ sizeof(struct ip_set_req_version), -+ *len); -+ res = -EINVAL; -+ goto done; -+ } -+ -+ req_version->version = IP_SET_PROTOCOL_VERSION; -+ res = copy_to_user(user, req_version, -+ sizeof(struct ip_set_req_version)); -+ goto done; -+ } -+ case IP_SET_OP_GET_BYNAME: { -+ struct ip_set_req_get_set *req_get -+ = (struct ip_set_req_get_set *) data; -+ -+ if (*len != sizeof(struct ip_set_req_get_set)) { -+ ip_set_printk("invalid GET_BYNAME (want %zu, got %d)", -+ sizeof(struct ip_set_req_get_set), *len); -+ res = -EINVAL; -+ goto done; -+ } -+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ index = ip_set_find_byname(req_get->set.name); -+ req_get->set.index = index; -+ goto copy; -+ } -+ case IP_SET_OP_GET_BYINDEX: { -+ struct ip_set_req_get_set *req_get -+ = (struct ip_set_req_get_set *) data; -+ -+ if (*len != sizeof(struct ip_set_req_get_set)) { -+ ip_set_printk("invalid GET_BYINDEX (want %zu, got %d)", -+ sizeof(struct ip_set_req_get_set), *len); -+ res = -EINVAL; -+ goto done; -+ } -+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ index = ip_set_find_byindex(req_get->set.index); -+ strncpy(req_get->set.name, -+ index == IP_SET_INVALID_ID ? "" -+ : ip_set_list[index]->name, IP_SET_MAXNAMELEN); -+ goto copy; -+ } -+ case IP_SET_OP_ADT_GET: { -+ struct ip_set_req_adt_get *req_get -+ = (struct ip_set_req_adt_get *) data; -+ -+ if (*len != sizeof(struct ip_set_req_adt_get)) { -+ ip_set_printk("invalid ADT_GET (want %zu, got %d)", -+ sizeof(struct ip_set_req_adt_get), *len); -+ res = -EINVAL; -+ goto done; -+ } -+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ index = ip_set_find_byname(req_get->set.name); -+ if (index != IP_SET_INVALID_ID) { -+ req_get->set.index = index; -+ strncpy(req_get->typename, -+ ip_set_list[index]->type->typename, -+ IP_SET_MAXNAMELEN - 1); -+ } else { -+ res = -ENOENT; -+ goto done; -+ } -+ goto copy; -+ } -+ case IP_SET_OP_MAX_SETS: { -+ struct ip_set_req_max_sets *req_max_sets -+ = (struct ip_set_req_max_sets *) data; -+ ip_set_id_t i; -+ -+ if (*len != sizeof(struct ip_set_req_max_sets)) { -+ ip_set_printk("invalid MAX_SETS (want %zu, got %d)", -+ sizeof(struct ip_set_req_max_sets), *len); -+ res = -EINVAL; -+ goto done; -+ } -+ -+ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) { -+ req_max_sets->set.index = IP_SET_INVALID_ID; -+ } else { -+ req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ req_max_sets->set.index = -+ ip_set_find_byname(req_max_sets->set.name); -+ if (req_max_sets->set.index == IP_SET_INVALID_ID) { -+ res = -ENOENT; -+ goto done; -+ } -+ } -+ req_max_sets->max_sets = ip_set_max; -+ req_max_sets->sets = 0; -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL) -+ req_max_sets->sets++; -+ } -+ goto copy; -+ } -+ case IP_SET_OP_LIST_SIZE: -+ case IP_SET_OP_SAVE_SIZE: { -+ struct ip_set_req_setnames *req_setnames -+ = (struct ip_set_req_setnames *) data; -+ struct ip_set_name_list *name_list; -+ struct ip_set *set; -+ ip_set_id_t i; -+ int used; -+ -+ if (*len < sizeof(struct ip_set_req_setnames)) { -+ ip_set_printk("short LIST_SIZE (want >=%zu, got %d)", -+ sizeof(struct ip_set_req_setnames), *len); -+ res = -EINVAL; -+ goto done; -+ } -+ -+ req_setnames->size = 0; -+ used = sizeof(struct ip_set_req_setnames); -+ for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] == NULL) -+ continue; -+ name_list = (struct ip_set_name_list *) -+ (data + used); -+ used += sizeof(struct ip_set_name_list); -+ if (used > copylen) { -+ res = -EAGAIN; -+ goto done; -+ } -+ set = ip_set_list[i]; -+ /* Fill in index, name, etc. */ -+ name_list->index = i; -+ name_list->id = set->id; -+ strncpy(name_list->name, -+ set->name, -+ IP_SET_MAXNAMELEN - 1); -+ strncpy(name_list->typename, -+ set->type->typename, -+ IP_SET_MAXNAMELEN - 1); -+ DP("filled %s of type %s, index %u\n", -+ name_list->name, name_list->typename, -+ name_list->index); -+ if (!(req_setnames->index == IP_SET_INVALID_ID -+ || req_setnames->index == i)) -+ continue; -+ /* Update size */ -+ switch (*op) { -+ case IP_SET_OP_LIST_SIZE: { -+ req_setnames->size += sizeof(struct ip_set_list) -+ + set->type->header_size -+ + set->type->list_members_size(set); -+ /* Sets are identified by id in the hash */ -+ FOREACH_HASH_DO(__set_hash_bindings_size_list, -+ set->id, &req_setnames->size); -+ break; -+ } -+ case IP_SET_OP_SAVE_SIZE: { -+ req_setnames->size += sizeof(struct ip_set_save) -+ + set->type->header_size -+ + set->type->list_members_size(set); -+ FOREACH_HASH_DO(__set_hash_bindings_size_save, -+ set->id, &req_setnames->size); -+ break; -+ } -+ default: -+ break; -+ } -+ } -+ if (copylen != used) { -+ res = -EAGAIN; -+ goto done; -+ } -+ goto copy; -+ } -+ case IP_SET_OP_LIST: { -+ struct ip_set_req_list *req_list -+ = (struct ip_set_req_list *) data; -+ ip_set_id_t i; -+ int used; -+ -+ if (*len < sizeof(struct ip_set_req_list)) { -+ ip_set_printk("short LIST (want >=%zu, got %d)", -+ sizeof(struct ip_set_req_list), *len); -+ res = -EINVAL; -+ goto done; -+ } -+ index = req_list->index; -+ if (index != IP_SET_INVALID_ID -+ && ip_set_find_byindex(index) != index) { -+ res = -ENOENT; -+ goto done; -+ } -+ used = 0; -+ if (index == IP_SET_INVALID_ID) { -+ /* List all sets */ -+ for (i = 0; i < ip_set_max && res == 0; i++) { -+ if (ip_set_list[i] != NULL) -+ res = ip_set_list_set(i, data, &used, *len); -+ } -+ } else { -+ /* List an individual set */ -+ res = ip_set_list_set(index, data, &used, *len); -+ } -+ if (res != 0) -+ goto done; -+ else if (copylen != used) { -+ res = -EAGAIN; -+ goto done; -+ } -+ goto copy; -+ } -+ case IP_SET_OP_SAVE: { -+ struct ip_set_req_list *req_save -+ = (struct ip_set_req_list *) data; -+ ip_set_id_t i; -+ int used; -+ -+ if (*len < sizeof(struct ip_set_req_list)) { -+ ip_set_printk("short SAVE (want >=%zu, got %d)", -+ sizeof(struct ip_set_req_list), *len); -+ res = -EINVAL; -+ goto done; -+ } -+ index = req_save->index; -+ if (index != IP_SET_INVALID_ID -+ && ip_set_find_byindex(index) != index) { -+ res = -ENOENT; -+ goto done; -+ } -+ used = 0; -+ if (index == IP_SET_INVALID_ID) { -+ /* Save all sets */ -+ for (i = 0; i < ip_set_max && res == 0; i++) { -+ if (ip_set_list[i] != NULL) -+ res = ip_set_save_set(i, data, &used, *len); -+ } -+ } else { -+ /* Save an individual set */ -+ res = ip_set_save_set(index, data, &used, *len); -+ } -+ if (res == 0) -+ res = ip_set_save_bindings(index, data, &used, *len); -+ -+ if (res != 0) -+ goto done; -+ else if (copylen != used) { -+ res = -EAGAIN; -+ goto done; -+ } -+ goto copy; -+ } -+ case IP_SET_OP_RESTORE: { -+ struct ip_set_req_setnames *req_restore -+ = (struct ip_set_req_setnames *) data; -+ int line; -+ -+ if (*len < sizeof(struct ip_set_req_setnames) -+ || *len != req_restore->size) { -+ ip_set_printk("invalid RESTORE (want =%zu, got %d)", -+ req_restore->size, *len); -+ res = -EINVAL; -+ goto done; -+ } -+ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames), -+ req_restore->size - sizeof(struct ip_set_req_setnames)); -+ DP("ip_set_restore: %u", line); -+ if (line != 0) { -+ res = -EAGAIN; -+ req_restore->size = line; -+ copylen = sizeof(struct ip_set_req_setnames); -+ goto copy; -+ } -+ goto done; -+ } -+ default: -+ res = -EBADMSG; -+ goto done; -+ } /* end of switch(op) */ -+ -+ copy: -+ DP("set %s, copylen %u", index != IP_SET_INVALID_ID -+ && ip_set_list[index] -+ ? ip_set_list[index]->name -+ : ":all:", copylen); -+ res = copy_to_user(user, data, copylen); -+ -+ done: -+ up(&ip_set_app_mutex); -+ vfree(data); -+ if (res > 0) -+ res = 0; -+ DP("final result %d", res); -+ return res; -+} -+ -+static struct nf_sockopt_ops so_set = { -+ .pf = PF_INET, -+ .set_optmin = SO_IP_SET, -+ .set_optmax = SO_IP_SET + 1, -+ .set = &ip_set_sockfn_set, -+ .get_optmin = SO_IP_SET, -+ .get_optmax = SO_IP_SET + 1, -+ .get = &ip_set_sockfn_get, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ .owner = THIS_MODULE, -+#endif -+}; -+ -+static int max_sets, hash_size; -+module_param(max_sets, int, 0600); -+MODULE_PARM_DESC(max_sets, "maximal number of sets"); -+module_param(hash_size, int, 0600); -+MODULE_PARM_DESC(hash_size, "hash size for bindings"); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("module implementing core IP set support"); -+ -+static int __init ip_set_init(void) -+{ -+ int res; -+ ip_set_id_t i; -+ -+ get_random_bytes(&ip_set_hash_random, 4); -+ if (max_sets) -+ ip_set_max = max_sets; -+ ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max); -+ if (!ip_set_list) { -+ printk(KERN_ERR "Unable to create ip_set_list\n"); -+ return -ENOMEM; -+ } -+ memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max); -+ if (hash_size) -+ ip_set_bindings_hash_size = hash_size; -+ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size); -+ if (!ip_set_hash) { -+ printk(KERN_ERR "Unable to create ip_set_hash\n"); -+ vfree(ip_set_list); -+ return -ENOMEM; -+ } -+ for (i = 0; i < ip_set_bindings_hash_size; i++) -+ INIT_LIST_HEAD(&ip_set_hash[i]); -+ -+ INIT_LIST_HEAD(&set_type_list); -+ -+ res = nf_register_sockopt(&so_set); -+ if (res != 0) { -+ ip_set_printk("SO_SET registry failed: %d", res); -+ vfree(ip_set_list); -+ vfree(ip_set_hash); -+ return res; -+ } -+ return 0; -+} -+ -+static void __exit ip_set_fini(void) -+{ -+ /* There can't be any existing set or binding */ -+ nf_unregister_sockopt(&so_set); -+ vfree(ip_set_list); -+ vfree(ip_set_hash); -+ DP("these are the famous last words"); -+} -+ -+EXPORT_SYMBOL(ip_set_register_set_type); -+EXPORT_SYMBOL(ip_set_unregister_set_type); -+ -+EXPORT_SYMBOL(ip_set_get_byname); -+EXPORT_SYMBOL(ip_set_get_byindex); -+EXPORT_SYMBOL(ip_set_put); -+ -+EXPORT_SYMBOL(ip_set_addip_kernel); -+EXPORT_SYMBOL(ip_set_delip_kernel); -+EXPORT_SYMBOL(ip_set_testip_kernel); -+ -+module_init(ip_set_init); -+module_exit(ip_set_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_iphash.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_iphash.c -@@ -0,0 +1,429 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module implementing an ip hash set */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+static int limit = MAX_RANGE; -+ -+static inline __u32 -+jhash_ip(const struct ip_set_iphash *map, uint16_t i, ip_set_ip_t ip) -+{ -+ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); -+} -+ -+static inline __u32 -+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ __u32 id; -+ u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ *hash_ip = ip & map->netmask; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip), HIPQUAD(map->netmask)); -+ -+ for (i = 0; i < map->probes; i++) { -+ id = jhash_ip(map, i, *hash_ip) % map->hashsize; -+ DP("hash key: %u", id); -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ if (*elem == *hash_ip) -+ return id; -+ /* No shortcut at testing - there can be deleted -+ * entries. */ -+ } -+ return UINT_MAX; -+} -+ -+static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); -+} -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iphash *req = -+ (struct ip_set_req_iphash *) data; -+ -+ if (size != sizeof(struct ip_set_req_iphash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, hash_ip); -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static inline int -+__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ __u32 probe; -+ u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ if (!ip || map->elements >= limit) -+ return -ERANGE; -+ -+ *hash_ip = ip & map->netmask; -+ -+ for (i = 0; i < map->probes; i++) { -+ probe = jhash_ip(map, i, *hash_ip) % map->hashsize; -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); -+ if (*elem == *hash_ip) -+ return -EEXIST; -+ if (!*elem) { -+ *elem = *hash_ip; -+ map->elements++; -+ return 0; -+ } -+ } -+ /* Trigger rehashing */ -+ return -EAGAIN; -+} -+ -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iphash *req = -+ (struct ip_set_req_iphash *) data; -+ -+ if (size != sizeof(struct ip_set_req_iphash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash), -+ size); -+ return -EINVAL; -+ } -+ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __addip((struct ip_set_iphash *) set->data, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static int retry(struct ip_set *set) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ ip_set_ip_t hash_ip, *elem; -+ void *members; -+ u_int32_t i, hashsize = map->hashsize; -+ int res; -+ struct ip_set_iphash *tmp; -+ -+ if (map->resize == 0) -+ return -ERANGE; -+ -+ again: -+ res = 0; -+ -+ /* Calculate new hash size */ -+ hashsize += (hashsize * map->resize)/100; -+ if (hashsize == map->hashsize) -+ hashsize++; -+ -+ ip_set_printk("rehashing of set %s triggered: " -+ "hashsize grows from %u to %u", -+ set->name, map->hashsize, hashsize); -+ -+ tmp = kmalloc(sizeof(struct ip_set_iphash) -+ + map->probes * sizeof(uint32_t), GFP_ATOMIC); -+ if (!tmp) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_iphash) -+ + map->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); -+ if (!tmp->members) { -+ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); -+ kfree(tmp); -+ return -ENOMEM; -+ } -+ tmp->hashsize = hashsize; -+ tmp->elements = 0; -+ tmp->probes = map->probes; -+ tmp->resize = map->resize; -+ tmp->netmask = map->netmask; -+ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); -+ -+ write_lock_bh(&set->lock); -+ map = (struct ip_set_iphash *) set->data; /* Play safe */ -+ for (i = 0; i < map->hashsize && res == 0; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ if (*elem) -+ res = __addip(tmp, *elem, &hash_ip); -+ } -+ if (res) { -+ /* Failure, try again */ -+ write_unlock_bh(&set->lock); -+ harray_free(tmp->members); -+ kfree(tmp); -+ goto again; -+ } -+ -+ /* Success at resizing! */ -+ members = map->members; -+ -+ map->hashsize = tmp->hashsize; -+ map->members = tmp->members; -+ write_unlock_bh(&set->lock); -+ -+ harray_free(members); -+ kfree(tmp); -+ -+ return 0; -+} -+ -+static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ ip_set_ip_t id, *elem; -+ -+ if (!ip) -+ return -ERANGE; -+ -+ id = hash_id(set, ip, hash_ip); -+ if (id == UINT_MAX) -+ return -EEXIST; -+ -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ *elem = 0; -+ map->elements--; -+ -+ return 0; -+} -+ -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iphash *req = -+ (struct ip_set_req_iphash *) data; -+ -+ if (size != sizeof(struct ip_set_req_iphash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ struct ip_set_req_iphash_create *req = -+ (struct ip_set_req_iphash_create *) data; -+ struct ip_set_iphash *map; -+ uint16_t i; -+ -+ if (size != sizeof(struct ip_set_req_iphash_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash_create), -+ size); -+ return -EINVAL; -+ } -+ -+ if (req->hashsize < 1) { -+ ip_set_printk("hashsize too small"); -+ return -ENOEXEC; -+ } -+ -+ if (req->probes < 1) { -+ ip_set_printk("probes too small"); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_iphash) -+ + req->probes * sizeof(uint32_t), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_iphash) -+ + req->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ for (i = 0; i < req->probes; i++) -+ get_random_bytes(((uint32_t *) map->initval)+i, 4); -+ map->elements = 0; -+ map->hashsize = req->hashsize; -+ map->probes = req->probes; -+ map->resize = req->resize; -+ map->netmask = req->netmask; -+ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); -+ kfree(map); -+ return -ENOMEM; -+ } -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ -+ harray_free(map->members); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); -+ map->elements = 0; -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ struct ip_set_req_iphash_create *header = -+ (struct ip_set_req_iphash_create *) data; -+ -+ header->hashsize = map->hashsize; -+ header->probes = map->probes; -+ header->resize = map->resize; -+ header->netmask = map->netmask; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ -+ return (map->hashsize * sizeof(ip_set_ip_t)); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ ip_set_ip_t i, *elem; -+ -+ for (i = 0; i < map->hashsize; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ ((ip_set_ip_t *)data)[i] = *elem; -+ } -+} -+ -+static struct ip_set_type ip_set_iphash = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_iphash), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .retry = &retry, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_iphash_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("iphash type of IP sets"); -+module_param(limit, int, 0600); -+MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); -+ -+static int __init ip_set_iphash_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_iphash); -+} -+ -+static void __exit ip_set_iphash_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_iphash); -+} -+ -+module_init(ip_set_iphash_init); -+module_exit(ip_set_iphash_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipmap.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipmap.c -@@ -0,0 +1,336 @@ -+/* Copyright (C) 2000-2002 Joakim Axelsson -+ * Patrick Schaaf -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module implementing an IP set type: the single bitmap type */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+static inline ip_set_ip_t -+ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) -+{ -+ return (ip - map->first_ip)/map->hosts; -+} -+ -+static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ -+ *hash_ip = ip & map->netmask; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ return !!test_bit(ip_to_id(map, *hash_ip), map->members); -+} -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipmap *req = -+ (struct ip_set_req_ipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, hash_ip); -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ int res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+ return (res < 0 ? 0 : res); -+} -+ -+static inline int -+__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ -+ *hash_ip = ip & map->netmask; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members)) -+ return -EEXIST; -+ -+ return 0; -+} -+ -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipmap *req = -+ (struct ip_set_req_ipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap), -+ size); -+ return -EINVAL; -+ } -+ DP("%u.%u.%u.%u", HIPQUAD(req->ip)); -+ return __addip(set, req->ip, hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __addip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ -+ *hash_ip = ip & map->netmask; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members)) -+ return -EEXIST; -+ -+ return 0; -+} -+ -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipmap *req = -+ (struct ip_set_req_ipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ int newbytes; -+ struct ip_set_req_ipmap_create *req = -+ (struct ip_set_req_ipmap_create *) data; -+ struct ip_set_ipmap *map; -+ -+ if (size != sizeof(struct ip_set_req_ipmap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap_create), -+ size); -+ return -EINVAL; -+ } -+ -+ DP("from %u.%u.%u.%u to %u.%u.%u.%u", -+ HIPQUAD(req->from), HIPQUAD(req->to)); -+ -+ if (req->from > req->to) { -+ DP("bad ip range"); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_ipmap)); -+ return -ENOMEM; -+ } -+ map->first_ip = req->from; -+ map->last_ip = req->to; -+ map->netmask = req->netmask; -+ -+ if (req->netmask == 0xFFFFFFFF) { -+ map->hosts = 1; -+ map->sizeid = map->last_ip - map->first_ip + 1; -+ } else { -+ unsigned int mask_bits, netmask_bits; -+ ip_set_ip_t mask; -+ -+ map->first_ip &= map->netmask; /* Should we better bark? */ -+ -+ mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); -+ netmask_bits = mask_to_bits(map->netmask); -+ -+ if ((!mask && (map->first_ip || map->last_ip != 0xFFFFFFFF)) -+ || netmask_bits <= mask_bits) -+ return -ENOEXEC; -+ -+ DP("mask_bits %u, netmask_bits %u", -+ mask_bits, netmask_bits); -+ map->hosts = 2 << (32 - netmask_bits - 1); -+ map->sizeid = 2 << (netmask_bits - mask_bits - 1); -+ } -+ if (map->sizeid > MAX_RANGE + 1) { -+ ip_set_printk("range too big (max %d addresses)", -+ MAX_RANGE+1); -+ kfree(map); -+ return -ENOEXEC; -+ } -+ DP("hosts %u, sizeid %u", map->hosts, map->sizeid); -+ newbytes = bitmap_bytes(0, map->sizeid - 1); -+ map->members = kmalloc(newbytes, GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", newbytes); -+ kfree(map); -+ return -ENOMEM; -+ } -+ memset(map->members, 0, newbytes); -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ -+ kfree(map->members); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ struct ip_set_req_ipmap_create *header = -+ (struct ip_set_req_ipmap_create *) data; -+ -+ header->from = map->first_ip; -+ header->to = map->last_ip; -+ header->netmask = map->netmask; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ -+ return bitmap_bytes(0, map->sizeid - 1); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ int bytes = bitmap_bytes(0, map->sizeid - 1); -+ -+ memcpy(data, map->members, bytes); -+} -+ -+static struct ip_set_type ip_set_ipmap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_ipmap), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_ipmap_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("ipmap type of IP sets"); -+ -+static int __init ip_set_ipmap_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_ipmap); -+} -+ -+static void __exit ip_set_ipmap_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_ipmap); -+} -+ -+module_init(ip_set_ipmap_init); -+module_exit(ip_set_ipmap_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipporthash.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_ipporthash.c -@@ -0,0 +1,581 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module implementing an ip+port hash set */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+static int limit = MAX_RANGE; -+ -+/* We must handle non-linear skbs */ -+static inline ip_set_ip_t -+get_port(const struct sk_buff *skb, u_int32_t flags) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ struct iphdr *iph = ip_hdr(skb); -+#else -+ struct iphdr *iph = skb->nh.iph; -+#endif -+ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; -+ -+ switch (iph->protocol) { -+ case IPPROTO_TCP: { -+ struct tcphdr tcph; -+ -+ /* See comments at tcp_match in ip_tables.c */ -+ if (offset) -+ return INVALID_PORT; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ tcph.source : tcph.dest); -+ } -+ case IPPROTO_UDP: { -+ struct udphdr udph; -+ -+ if (offset) -+ return INVALID_PORT; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ udph.source : udph.dest); -+ } -+ default: -+ return INVALID_PORT; -+ } -+} -+ -+static inline __u32 -+jhash_ip(const struct ip_set_ipporthash *map, uint16_t i, ip_set_ip_t ip) -+{ -+ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); -+} -+ -+#define HASH_IP(map, ip, port) (port + ((ip - ((map)->first_ip)) << 16)) -+ -+static inline __u32 -+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_ipporthash *map = -+ (struct ip_set_ipporthash *) set->data; -+ __u32 id; -+ u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ *hash_ip = HASH_IP(map, ip, port); -+ DP("set: %s, ipport:%u.%u.%u.%u:%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), port, HIPQUAD(*hash_ip)); -+ -+ for (i = 0; i < map->probes; i++) { -+ id = jhash_ip(map, i, *hash_ip) % map->hashsize; -+ DP("hash key: %u", id); -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ if (*elem == *hash_ip) -+ return id; -+ /* No shortcut at testing - there can be deleted -+ * entries. */ -+ } -+ return UINT_MAX; -+} -+ -+static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ -+ return (hash_id(set, ip, port, hash_ip) != UINT_MAX); -+} -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipporthash *req = -+ (struct ip_set_req_ipporthash *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipporthash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, req->port, hash_ip); -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ ip_set_ip_t port; -+ int res; -+ -+ if (flags[index+1] == 0) -+ return 0; -+ -+ port = get_port(skb, flags[index+1]); -+ -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ DP("flag %s port %u", -+ flags[index+1] & IPSET_SRC ? "SRC" : "DST", -+ port); -+ if (port == INVALID_PORT) -+ return 0; -+ -+ res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ port, -+ hash_ip); -+ return (res < 0 ? 0 : res); -+ -+} -+ -+static inline int -+__add_haship(struct ip_set_ipporthash *map, ip_set_ip_t hash_ip) -+{ -+ __u32 probe; -+ u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ for (i = 0; i < map->probes; i++) { -+ probe = jhash_ip(map, i, hash_ip) % map->hashsize; -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); -+ if (*elem == hash_ip) -+ return -EEXIST; -+ if (!*elem) { -+ *elem = hash_ip; -+ map->elements++; -+ return 0; -+ } -+ } -+ /* Trigger rehashing */ -+ return -EAGAIN; -+} -+ -+static inline int -+__addip(struct ip_set_ipporthash *map, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) -+{ -+ if (map->elements > limit) -+ return -ERANGE; -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ -+ *hash_ip = HASH_IP(map, ip, port); -+ -+ return __add_haship(map, *hash_ip); -+} -+ -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipporthash *req = -+ (struct ip_set_req_ipporthash *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipporthash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash), -+ size); -+ return -EINVAL; -+ } -+ return __addip((struct ip_set_ipporthash *) set->data, -+ req->ip, req->port, hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ ip_set_ip_t port; -+ -+ if (flags[index+1] == 0) -+ return -EINVAL; -+ -+ port = get_port(skb, flags[index+1]); -+ -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ DP("flag %s port %u", -+ flags[index+1] & IPSET_SRC ? "SRC" : "DST", -+ port); -+ if (port == INVALID_PORT) -+ return -EINVAL; -+ -+ return __addip((struct ip_set_ipporthash *) set->data, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ port, -+ hash_ip); -+} -+ -+static int retry(struct ip_set *set) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ ip_set_ip_t *elem; -+ void *members; -+ u_int32_t i, hashsize = map->hashsize; -+ int res; -+ struct ip_set_ipporthash *tmp; -+ -+ if (map->resize == 0) -+ return -ERANGE; -+ -+ again: -+ res = 0; -+ -+ /* Calculate new hash size */ -+ hashsize += (hashsize * map->resize)/100; -+ if (hashsize == map->hashsize) -+ hashsize++; -+ -+ ip_set_printk("rehashing of set %s triggered: " -+ "hashsize grows from %u to %u", -+ set->name, map->hashsize, hashsize); -+ -+ tmp = kmalloc(sizeof(struct ip_set_ipporthash) -+ + map->probes * sizeof(uint32_t), GFP_ATOMIC); -+ if (!tmp) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_ipporthash) -+ + map->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); -+ if (!tmp->members) { -+ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); -+ kfree(tmp); -+ return -ENOMEM; -+ } -+ tmp->hashsize = hashsize; -+ tmp->elements = 0; -+ tmp->probes = map->probes; -+ tmp->resize = map->resize; -+ tmp->first_ip = map->first_ip; -+ tmp->last_ip = map->last_ip; -+ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); -+ -+ write_lock_bh(&set->lock); -+ map = (struct ip_set_ipporthash *) set->data; /* Play safe */ -+ for (i = 0; i < map->hashsize && res == 0; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ if (*elem) -+ res = __add_haship(tmp, *elem); -+ } -+ if (res) { -+ /* Failure, try again */ -+ write_unlock_bh(&set->lock); -+ harray_free(tmp->members); -+ kfree(tmp); -+ goto again; -+ } -+ -+ /* Success at resizing! */ -+ members = map->members; -+ -+ map->hashsize = tmp->hashsize; -+ map->members = tmp->members; -+ write_unlock_bh(&set->lock); -+ -+ harray_free(members); -+ kfree(tmp); -+ -+ return 0; -+} -+ -+static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ ip_set_ip_t id; -+ ip_set_ip_t *elem; -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ -+ id = hash_id(set, ip, port, hash_ip); -+ -+ if (id == UINT_MAX) -+ return -EEXIST; -+ -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ *elem = 0; -+ map->elements--; -+ -+ return 0; -+} -+ -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipporthash *req = -+ (struct ip_set_req_ipporthash *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipporthash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, req->port, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ ip_set_ip_t port; -+ -+ if (flags[index+1] == 0) -+ return -EINVAL; -+ -+ port = get_port(skb, flags[index+1]); -+ -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ DP("flag %s port %u", -+ flags[index+1] & IPSET_SRC ? "SRC" : "DST", -+ port); -+ if (port == INVALID_PORT) -+ return -EINVAL; -+ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ port, -+ hash_ip); -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ struct ip_set_req_ipporthash_create *req = -+ (struct ip_set_req_ipporthash_create *) data; -+ struct ip_set_ipporthash *map; -+ uint16_t i; -+ -+ if (size != sizeof(struct ip_set_req_ipporthash_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash_create), -+ size); -+ return -EINVAL; -+ } -+ -+ if (req->hashsize < 1) { -+ ip_set_printk("hashsize too small"); -+ return -ENOEXEC; -+ } -+ -+ if (req->probes < 1) { -+ ip_set_printk("probes too small"); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_ipporthash) -+ + req->probes * sizeof(uint32_t), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_ipporthash) -+ + req->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ for (i = 0; i < req->probes; i++) -+ get_random_bytes(((uint32_t *) map->initval)+i, 4); -+ map->elements = 0; -+ map->hashsize = req->hashsize; -+ map->probes = req->probes; -+ map->resize = req->resize; -+ map->first_ip = req->from; -+ map->last_ip = req->to; -+ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); -+ kfree(map); -+ return -ENOMEM; -+ } -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ -+ harray_free(map->members); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); -+ map->elements = 0; -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ struct ip_set_req_ipporthash_create *header = -+ (struct ip_set_req_ipporthash_create *) data; -+ -+ header->hashsize = map->hashsize; -+ header->probes = map->probes; -+ header->resize = map->resize; -+ header->from = map->first_ip; -+ header->to = map->last_ip; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ -+ return (map->hashsize * sizeof(ip_set_ip_t)); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ ip_set_ip_t i, *elem; -+ -+ for (i = 0; i < map->hashsize; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ ((ip_set_ip_t *)data)[i] = *elem; -+ } -+} -+ -+static struct ip_set_type ip_set_ipporthash = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_ipporthash), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .retry = &retry, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_ipporthash_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("ipporthash type of IP sets"); -+module_param(limit, int, 0600); -+MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); -+ -+static int __init ip_set_ipporthash_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_ipporthash); -+} -+ -+static void __exit ip_set_ipporthash_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_ipporthash); -+} -+ -+module_init(ip_set_ipporthash_init); -+module_exit(ip_set_ipporthash_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptree.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptree.c -@@ -0,0 +1,612 @@ -+/* Copyright (C) 2005 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module implementing an IP set type: the iptree type */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Backward compatibility */ -+#ifndef __nocast -+#define __nocast -+#endif -+ -+#include -+ -+static int limit = MAX_RANGE; -+ -+/* Garbage collection interval in seconds: */ -+#define IPTREE_GC_TIME 5*60 -+/* Sleep so many milliseconds before trying again -+ * to delete the gc timer at destroying/flushing a set */ -+#define IPTREE_DESTROY_SLEEP 100 -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+static struct kmem_cache *branch_cachep; -+static struct kmem_cache *leaf_cachep; -+#else -+static kmem_cache_t *branch_cachep; -+static kmem_cache_t *leaf_cachep; -+#endif -+ -+#if defined(__LITTLE_ENDIAN) -+#define ABCD(a,b,c,d,addrp) do { \ -+ a = ((unsigned char *)addrp)[3]; \ -+ b = ((unsigned char *)addrp)[2]; \ -+ c = ((unsigned char *)addrp)[1]; \ -+ d = ((unsigned char *)addrp)[0]; \ -+} while (0) -+#elif defined(__BIG_ENDIAN) -+#define ABCD(a,b,c,d,addrp) do { \ -+ a = ((unsigned char *)addrp)[0]; \ -+ b = ((unsigned char *)addrp)[1]; \ -+ c = ((unsigned char *)addrp)[2]; \ -+ d = ((unsigned char *)addrp)[3]; \ -+} while (0) -+#else -+#error "Please fix asm/byteorder.h" -+#endif /* __LITTLE_ENDIAN */ -+ -+#define TESTIP_WALK(map, elem, branch) do { \ -+ if ((map)->tree[elem]) { \ -+ branch = (map)->tree[elem]; \ -+ } else \ -+ return 0; \ -+} while (0) -+ -+static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_iptreeb *btree; -+ struct ip_set_iptreec *ctree; -+ struct ip_set_iptreed *dtree; -+ unsigned char a,b,c,d; -+ -+ if (!ip) -+ return -ERANGE; -+ -+ *hash_ip = ip; -+ ABCD(a, b, c, d, hash_ip); -+ DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); -+ TESTIP_WALK(map, a, btree); -+ TESTIP_WALK(btree, b, ctree); -+ TESTIP_WALK(ctree, c, dtree); -+ DP("%lu %lu", dtree->expires[d], jiffies); -+ return dtree->expires[d] -+ && (!map->timeout -+ || time_after(dtree->expires[d], jiffies)); -+} -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptree *req = -+ (struct ip_set_req_iptree *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptree)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iptree), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, hash_ip); -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ int res; -+ -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ -+ res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+ return (res < 0 ? 0 : res); -+} -+ -+#define ADDIP_WALK(map, elem, branch, type, cachep) do { \ -+ if ((map)->tree[elem]) { \ -+ DP("found %u", elem); \ -+ branch = (map)->tree[elem]; \ -+ } else { \ -+ branch = (type *) \ -+ kmem_cache_alloc(cachep, GFP_ATOMIC); \ -+ if (branch == NULL) \ -+ return -ENOMEM; \ -+ memset(branch, 0, sizeof(*branch)); \ -+ (map)->tree[elem] = branch; \ -+ DP("alloc %u", elem); \ -+ } \ -+} while (0) -+ -+static inline int -+__addip(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_iptreeb *btree; -+ struct ip_set_iptreec *ctree; -+ struct ip_set_iptreed *dtree; -+ unsigned char a,b,c,d; -+ int ret = 0; -+ -+ if (!ip || map->elements >= limit) -+ /* We could call the garbage collector -+ * but it's probably overkill */ -+ return -ERANGE; -+ -+ *hash_ip = ip; -+ ABCD(a, b, c, d, hash_ip); -+ DP("%u %u %u %u timeout %u", a, b, c, d, timeout); -+ ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep); -+ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep); -+ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep); -+ if (dtree->expires[d] -+ && (!map->timeout || time_after(dtree->expires[d], jiffies))) -+ ret = -EEXIST; -+ dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; -+ /* Lottery: I won! */ -+ if (dtree->expires[d] == 0) -+ dtree->expires[d] = 1; -+ DP("%u %lu", d, dtree->expires[d]); -+ if (ret == 0) -+ map->elements++; -+ return ret; -+} -+ -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_req_iptree *req = -+ (struct ip_set_req_iptree *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptree)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iptree), -+ size); -+ return -EINVAL; -+ } -+ DP("%u.%u.%u.%u %u", HIPQUAD(req->ip), req->timeout); -+ return __addip(set, req->ip, -+ req->timeout ? req->timeout : map->timeout, -+ hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ -+ return __addip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ map->timeout, -+ hash_ip); -+} -+ -+#define DELIP_WALK(map, elem, branch) do { \ -+ if ((map)->tree[elem]) { \ -+ branch = (map)->tree[elem]; \ -+ } else \ -+ return -EEXIST; \ -+} while (0) -+ -+static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_iptreeb *btree; -+ struct ip_set_iptreec *ctree; -+ struct ip_set_iptreed *dtree; -+ unsigned char a,b,c,d; -+ -+ if (!ip) -+ return -ERANGE; -+ -+ *hash_ip = ip; -+ ABCD(a, b, c, d, hash_ip); -+ DELIP_WALK(map, a, btree); -+ DELIP_WALK(btree, b, ctree); -+ DELIP_WALK(ctree, c, dtree); -+ -+ if (dtree->expires[d]) { -+ dtree->expires[d] = 0; -+ map->elements--; -+ return 0; -+ } -+ return -EEXIST; -+} -+ -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptree *req = -+ (struct ip_set_req_iptree *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptree)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iptree), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+#define LOOP_WALK_BEGIN(map, i, branch) \ -+ for (i = 0; i < 256; i++) { \ -+ if (!(map)->tree[i]) \ -+ continue; \ -+ branch = (map)->tree[i] -+ -+#define LOOP_WALK_END } -+ -+static void ip_tree_gc(unsigned long ul_set) -+{ -+ struct ip_set *set = (void *) ul_set; -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_iptreeb *btree; -+ struct ip_set_iptreec *ctree; -+ struct ip_set_iptreed *dtree; -+ unsigned int a,b,c,d; -+ unsigned char i,j,k; -+ -+ i = j = k = 0; -+ DP("gc: %s", set->name); -+ write_lock_bh(&set->lock); -+ LOOP_WALK_BEGIN(map, a, btree); -+ LOOP_WALK_BEGIN(btree, b, ctree); -+ LOOP_WALK_BEGIN(ctree, c, dtree); -+ for (d = 0; d < 256; d++) { -+ if (dtree->expires[d]) { -+ DP("gc: %u %u %u %u: expires %lu jiffies %lu", -+ a, b, c, d, -+ dtree->expires[d], jiffies); -+ if (map->timeout -+ && time_before(dtree->expires[d], jiffies)) { -+ dtree->expires[d] = 0; -+ map->elements--; -+ } else -+ k = 1; -+ } -+ } -+ if (k == 0) { -+ DP("gc: %s: leaf %u %u %u empty", -+ set->name, a, b, c); -+ kmem_cache_free(leaf_cachep, dtree); -+ ctree->tree[c] = NULL; -+ } else { -+ DP("gc: %s: leaf %u %u %u not empty", -+ set->name, a, b, c); -+ j = 1; -+ k = 0; -+ } -+ LOOP_WALK_END; -+ if (j == 0) { -+ DP("gc: %s: branch %u %u empty", -+ set->name, a, b); -+ kmem_cache_free(branch_cachep, ctree); -+ btree->tree[b] = NULL; -+ } else { -+ DP("gc: %s: branch %u %u not empty", -+ set->name, a, b); -+ i = 1; -+ j = k = 0; -+ } -+ LOOP_WALK_END; -+ if (i == 0) { -+ DP("gc: %s: branch %u empty", -+ set->name, a); -+ kmem_cache_free(branch_cachep, btree); -+ map->tree[a] = NULL; -+ } else { -+ DP("gc: %s: branch %u not empty", -+ set->name, a); -+ i = j = k = 0; -+ } -+ LOOP_WALK_END; -+ write_unlock_bh(&set->lock); -+ -+ map->gc.expires = jiffies + map->gc_interval * HZ; -+ add_timer(&map->gc); -+} -+ -+static inline void init_gc_timer(struct ip_set *set) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ -+ /* Even if there is no timeout for the entries, -+ * we still have to call gc because delete -+ * do not clean up empty branches */ -+ map->gc_interval = IPTREE_GC_TIME; -+ init_timer(&map->gc); -+ map->gc.data = (unsigned long) set; -+ map->gc.function = ip_tree_gc; -+ map->gc.expires = jiffies + map->gc_interval * HZ; -+ add_timer(&map->gc); -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ struct ip_set_req_iptree_create *req = -+ (struct ip_set_req_iptree_create *) data; -+ struct ip_set_iptree *map; -+ -+ if (size != sizeof(struct ip_set_req_iptree_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iptree_create), -+ size); -+ return -EINVAL; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_iptree)); -+ return -ENOMEM; -+ } -+ memset(map, 0, sizeof(*map)); -+ map->timeout = req->timeout; -+ map->elements = 0; -+ set->data = map; -+ -+ init_gc_timer(set); -+ -+ return 0; -+} -+ -+static void __flush(struct ip_set_iptree *map) -+{ -+ struct ip_set_iptreeb *btree; -+ struct ip_set_iptreec *ctree; -+ struct ip_set_iptreed *dtree; -+ unsigned int a,b,c; -+ -+ LOOP_WALK_BEGIN(map, a, btree); -+ LOOP_WALK_BEGIN(btree, b, ctree); -+ LOOP_WALK_BEGIN(ctree, c, dtree); -+ kmem_cache_free(leaf_cachep, dtree); -+ LOOP_WALK_END; -+ kmem_cache_free(branch_cachep, ctree); -+ LOOP_WALK_END; -+ kmem_cache_free(branch_cachep, btree); -+ LOOP_WALK_END; -+ map->elements = 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ -+ /* gc might be running */ -+ while (!del_timer(&map->gc)) -+ msleep(IPTREE_DESTROY_SLEEP); -+ __flush(map); -+ kfree(map); -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ unsigned int timeout = map->timeout; -+ -+ /* gc might be running */ -+ while (!del_timer(&map->gc)) -+ msleep(IPTREE_DESTROY_SLEEP); -+ __flush(map); -+ memset(map, 0, sizeof(*map)); -+ map->timeout = timeout; -+ -+ init_gc_timer(set); -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_req_iptree_create *header = -+ (struct ip_set_req_iptree_create *) data; -+ -+ header->timeout = map->timeout; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_iptreeb *btree; -+ struct ip_set_iptreec *ctree; -+ struct ip_set_iptreed *dtree; -+ unsigned int a,b,c,d; -+ unsigned int count = 0; -+ -+ LOOP_WALK_BEGIN(map, a, btree); -+ LOOP_WALK_BEGIN(btree, b, ctree); -+ LOOP_WALK_BEGIN(ctree, c, dtree); -+ for (d = 0; d < 256; d++) { -+ if (dtree->expires[d] -+ && (!map->timeout || time_after(dtree->expires[d], jiffies))) -+ count++; -+ } -+ LOOP_WALK_END; -+ LOOP_WALK_END; -+ LOOP_WALK_END; -+ -+ DP("members %u", count); -+ return (count * sizeof(struct ip_set_req_iptree)); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_iptreeb *btree; -+ struct ip_set_iptreec *ctree; -+ struct ip_set_iptreed *dtree; -+ unsigned int a,b,c,d; -+ size_t offset = 0; -+ struct ip_set_req_iptree *entry; -+ -+ LOOP_WALK_BEGIN(map, a, btree); -+ LOOP_WALK_BEGIN(btree, b, ctree); -+ LOOP_WALK_BEGIN(ctree, c, dtree); -+ for (d = 0; d < 256; d++) { -+ if (dtree->expires[d] -+ && (!map->timeout || time_after(dtree->expires[d], jiffies))) { -+ entry = (struct ip_set_req_iptree *)(data + offset); -+ entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); -+ entry->timeout = !map->timeout ? 0 -+ : (dtree->expires[d] - jiffies)/HZ; -+ offset += sizeof(struct ip_set_req_iptree); -+ } -+ } -+ LOOP_WALK_END; -+ LOOP_WALK_END; -+ LOOP_WALK_END; -+} -+ -+static struct ip_set_type ip_set_iptree = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_iptree), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_iptree_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("iptree type of IP sets"); -+module_param(limit, int, 0600); -+MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); -+ -+static int __init ip_set_iptree_init(void) -+{ -+ int ret; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ branch_cachep = kmem_cache_create("ip_set_iptreeb", -+ sizeof(struct ip_set_iptreeb), -+ 0, 0, NULL); -+#else -+ branch_cachep = kmem_cache_create("ip_set_iptreeb", -+ sizeof(struct ip_set_iptreeb), -+ 0, 0, NULL, NULL); -+#endif -+ if (!branch_cachep) { -+ printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); -+ ret = -ENOMEM; -+ goto out; -+ } -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ leaf_cachep = kmem_cache_create("ip_set_iptreed", -+ sizeof(struct ip_set_iptreed), -+ 0, 0, NULL); -+#else -+ leaf_cachep = kmem_cache_create("ip_set_iptreed", -+ sizeof(struct ip_set_iptreed), -+ 0, 0, NULL, NULL); -+#endif -+ if (!leaf_cachep) { -+ printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); -+ ret = -ENOMEM; -+ goto free_branch; -+ } -+ ret = ip_set_register_set_type(&ip_set_iptree); -+ if (ret == 0) -+ goto out; -+ -+ kmem_cache_destroy(leaf_cachep); -+ free_branch: -+ kmem_cache_destroy(branch_cachep); -+ out: -+ return ret; -+} -+ -+static void __exit ip_set_iptree_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_iptree); -+ kmem_cache_destroy(leaf_cachep); -+ kmem_cache_destroy(branch_cachep); -+} -+ -+module_init(ip_set_iptree_init); -+module_exit(ip_set_iptree_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptreemap.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_iptreemap.c -@@ -0,0 +1,829 @@ -+/* Copyright (C) 2007 Sven Wegener -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ */ -+ -+/* This modules implements the iptreemap ipset type. It uses bitmaps to -+ * represent every single IPv4 address as a single bit. The bitmaps are managed -+ * in a tree structure, where the first three octets of an addresses are used -+ * as an index to find the bitmap and the last octet is used as the bit number. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define IPTREEMAP_DEFAULT_GC_TIME (5 * 60) -+#define IPTREEMAP_DESTROY_SLEEP (100) -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+static struct kmem_cache *cachep_b; -+static struct kmem_cache *cachep_c; -+static struct kmem_cache *cachep_d; -+#else -+static kmem_cache_t *cachep_b; -+static kmem_cache_t *cachep_c; -+static kmem_cache_t *cachep_d; -+#endif -+ -+static struct ip_set_iptreemap_d *fullbitmap_d; -+static struct ip_set_iptreemap_c *fullbitmap_c; -+static struct ip_set_iptreemap_b *fullbitmap_b; -+ -+#if defined(__LITTLE_ENDIAN) -+#define ABCD(a, b, c, d, addr) \ -+ do { \ -+ a = ((unsigned char *)addr)[3]; \ -+ b = ((unsigned char *)addr)[2]; \ -+ c = ((unsigned char *)addr)[1]; \ -+ d = ((unsigned char *)addr)[0]; \ -+ } while (0) -+#elif defined(__BIG_ENDIAN) -+#define ABCD(a,b,c,d,addrp) do { \ -+ a = ((unsigned char *)addrp)[0]; \ -+ b = ((unsigned char *)addrp)[1]; \ -+ c = ((unsigned char *)addrp)[2]; \ -+ d = ((unsigned char *)addrp)[3]; \ -+} while (0) -+#else -+#error "Please fix asm/byteorder.h" -+#endif /* __LITTLE_ENDIAN */ -+ -+#define TESTIP_WALK(map, elem, branch, full) \ -+ do { \ -+ branch = (map)->tree[elem]; \ -+ if (!branch) \ -+ return 0; \ -+ else if (branch == full) \ -+ return 1; \ -+ } while (0) -+ -+#define ADDIP_WALK(map, elem, branch, type, cachep, full) \ -+ do { \ -+ branch = (map)->tree[elem]; \ -+ if (!branch) { \ -+ branch = (type *) kmem_cache_alloc(cachep, GFP_ATOMIC); \ -+ if (!branch) \ -+ return -ENOMEM; \ -+ memset(branch, 0, sizeof(*branch)); \ -+ (map)->tree[elem] = branch; \ -+ } else if (branch == full) { \ -+ return -EEXIST; \ -+ } \ -+ } while (0) -+ -+#define ADDIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free) \ -+ for (a = a1; a <= a2; a++) { \ -+ branch = (map)->tree[a]; \ -+ if (branch != full) { \ -+ if ((a > a1 && a < a2) || (hint)) { \ -+ if (branch) \ -+ free(branch); \ -+ (map)->tree[a] = full; \ -+ continue; \ -+ } else if (!branch) { \ -+ branch = kmem_cache_alloc(cachep, GFP_ATOMIC); \ -+ if (!branch) \ -+ return -ENOMEM; \ -+ memset(branch, 0, sizeof(*branch)); \ -+ (map)->tree[a] = branch; \ -+ } -+ -+#define ADDIP_RANGE_LOOP_END() \ -+ } \ -+ } -+ -+#define DELIP_WALK(map, elem, branch, cachep, full, flags) \ -+ do { \ -+ branch = (map)->tree[elem]; \ -+ if (!branch) { \ -+ return -EEXIST; \ -+ } else if (branch == full) { \ -+ branch = kmem_cache_alloc(cachep, flags); \ -+ if (!branch) \ -+ return -ENOMEM; \ -+ memcpy(branch, full, sizeof(*full)); \ -+ (map)->tree[elem] = branch; \ -+ } \ -+ } while (0) -+ -+#define DELIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free, flags) \ -+ for (a = a1; a <= a2; a++) { \ -+ branch = (map)->tree[a]; \ -+ if (branch) { \ -+ if ((a > a1 && a < a2) || (hint)) { \ -+ if (branch != full) \ -+ free(branch); \ -+ (map)->tree[a] = NULL; \ -+ continue; \ -+ } else if (branch == full) { \ -+ branch = kmem_cache_alloc(cachep, flags); \ -+ if (!branch) \ -+ return -ENOMEM; \ -+ memcpy(branch, full, sizeof(*branch)); \ -+ (map)->tree[a] = branch; \ -+ } -+ -+#define DELIP_RANGE_LOOP_END() \ -+ } \ -+ } -+ -+#define LOOP_WALK_BEGIN(map, i, branch) \ -+ for (i = 0; i < 256; i++) { \ -+ branch = (map)->tree[i]; \ -+ if (likely(!branch)) \ -+ continue; -+ -+#define LOOP_WALK_END() \ -+ } -+ -+#define LOOP_WALK_BEGIN_GC(map, i, branch, full, cachep, count) \ -+ count = -256; \ -+ for (i = 0; i < 256; i++) { \ -+ branch = (map)->tree[i]; \ -+ if (likely(!branch)) \ -+ continue; \ -+ count++; \ -+ if (branch == full) { \ -+ count++; \ -+ continue; \ -+ } -+ -+#define LOOP_WALK_END_GC(map, i, branch, full, cachep, count) \ -+ if (-256 == count) { \ -+ kmem_cache_free(cachep, branch); \ -+ (map)->tree[i] = NULL; \ -+ } else if (256 == count) { \ -+ kmem_cache_free(cachep, branch); \ -+ (map)->tree[i] = full; \ -+ } \ -+ } -+ -+#define LOOP_WALK_BEGIN_COUNT(map, i, branch, inrange, count) \ -+ for (i = 0; i < 256; i++) { \ -+ if (!(map)->tree[i]) { \ -+ if (inrange) { \ -+ count++; \ -+ inrange = 0; \ -+ } \ -+ continue; \ -+ } \ -+ branch = (map)->tree[i]; -+ -+#define LOOP_WALK_END_COUNT() \ -+ } -+ -+#define MIN(a, b) (a < b ? a : b) -+#define MAX(a, b) (a > b ? a : b) -+ -+#define GETVALUE1(a, a1, b1, r) \ -+ (a == a1 ? b1 : r) -+ -+#define GETVALUE2(a, b, a1, b1, c1, r) \ -+ (a == a1 && b == b1 ? c1 : r) -+ -+#define GETVALUE3(a, b, c, a1, b1, c1, d1, r) \ -+ (a == a1 && b == b1 && c == c1 ? d1 : r) -+ -+#define CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2) \ -+ ( \ -+ GETVALUE1(a, a1, b1, 0) == 0 \ -+ && GETVALUE1(a, a2, b2, 255) == 255 \ -+ && c1 == 0 \ -+ && c2 == 255 \ -+ && d1 == 0 \ -+ && d2 == 255 \ -+ ) -+ -+#define CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2) \ -+ ( \ -+ GETVALUE2(a, b, a1, b1, c1, 0) == 0 \ -+ && GETVALUE2(a, b, a2, b2, c2, 255) == 255 \ -+ && d1 == 0 \ -+ && d2 == 255 \ -+ ) -+ -+#define CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2) \ -+ ( \ -+ GETVALUE3(a, b, c, a1, b1, c1, d1, 0) == 0 \ -+ && GETVALUE3(a, b, c, a2, b2, c2, d2, 255) == 255 \ -+ ) -+ -+ -+static inline void -+free_d(struct ip_set_iptreemap_d *map) -+{ -+ kmem_cache_free(cachep_d, map); -+} -+ -+static inline void -+free_c(struct ip_set_iptreemap_c *map) -+{ -+ struct ip_set_iptreemap_d *dtree; -+ unsigned int i; -+ -+ LOOP_WALK_BEGIN(map, i, dtree) { -+ if (dtree != fullbitmap_d) -+ free_d(dtree); -+ } LOOP_WALK_END(); -+ -+ kmem_cache_free(cachep_c, map); -+} -+ -+static inline void -+free_b(struct ip_set_iptreemap_b *map) -+{ -+ struct ip_set_iptreemap_c *ctree; -+ unsigned int i; -+ -+ LOOP_WALK_BEGIN(map, i, ctree) { -+ if (ctree != fullbitmap_c) -+ free_c(ctree); -+ } LOOP_WALK_END(); -+ -+ kmem_cache_free(cachep_b, map); -+} -+ -+static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned char a, b, c, d; -+ -+ *hash_ip = ip; -+ -+ ABCD(a, b, c, d, hash_ip); -+ -+ TESTIP_WALK(map, a, btree, fullbitmap_b); -+ TESTIP_WALK(btree, b, ctree, fullbitmap_c); -+ TESTIP_WALK(ctree, c, dtree, fullbitmap_d); -+ -+ return !!test_bit(d, (void *) dtree->bitmap); -+} -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptreemap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); -+ return -EINVAL; -+ } -+ -+ return __testip(set, req->start, hash_ip); -+} -+ -+static int -+testip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) -+{ -+ int res; -+ -+ res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+ -+ return (res < 0 ? 0 : res); -+} -+ -+static inline int -+__addip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned char a, b, c, d; -+ -+ *hash_ip = ip; -+ -+ ABCD(a, b, c, d, hash_ip); -+ -+ ADDIP_WALK(map, a, btree, struct ip_set_iptreemap_b, cachep_b, fullbitmap_b); -+ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreemap_c, cachep_c, fullbitmap_c); -+ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreemap_d, cachep_d, fullbitmap_d); -+ -+ if (test_and_set_bit(d, (void *) dtree->bitmap)) -+ return -EEXIST; -+ -+ set_bit(b, (void *) btree->dirty); -+ -+ return 0; -+} -+ -+static inline int -+__addip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned int a, b, c, d; -+ unsigned char a1, b1, c1, d1; -+ unsigned char a2, b2, c2, d2; -+ -+ if (start == end) -+ return __addip_single(set, start, hash_ip); -+ -+ *hash_ip = start; -+ -+ ABCD(a1, b1, c1, d1, &start); -+ ABCD(a2, b2, c2, d2, &end); -+ -+ /* This is sooo ugly... */ -+ ADDIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b) { -+ ADDIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c) { -+ ADDIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d) { -+ for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) -+ set_bit(d, (void *) dtree->bitmap); -+ set_bit(b, (void *) btree->dirty); -+ } ADDIP_RANGE_LOOP_END(); -+ } ADDIP_RANGE_LOOP_END(); -+ } ADDIP_RANGE_LOOP_END(); -+ -+ return 0; -+} -+ -+static int -+addip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptreemap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); -+ return -EINVAL; -+ } -+ -+ return __addip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) -+{ -+ -+ return __addip_single(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static inline int -+__delip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip, unsigned int __nocast flags) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned char a,b,c,d; -+ -+ *hash_ip = ip; -+ -+ ABCD(a, b, c, d, hash_ip); -+ -+ DELIP_WALK(map, a, btree, cachep_b, fullbitmap_b, flags); -+ DELIP_WALK(btree, b, ctree, cachep_c, fullbitmap_c, flags); -+ DELIP_WALK(ctree, c, dtree, cachep_d, fullbitmap_d, flags); -+ -+ if (!test_and_clear_bit(d, (void *) dtree->bitmap)) -+ return -EEXIST; -+ -+ set_bit(b, (void *) btree->dirty); -+ -+ return 0; -+} -+ -+static inline int -+__delip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip, unsigned int __nocast flags) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned int a, b, c, d; -+ unsigned char a1, b1, c1, d1; -+ unsigned char a2, b2, c2, d2; -+ -+ if (start == end) -+ return __delip_single(set, start, hash_ip, flags); -+ -+ *hash_ip = start; -+ -+ ABCD(a1, b1, c1, d1, &start); -+ ABCD(a2, b2, c2, d2, &end); -+ -+ /* This is sooo ugly... */ -+ DELIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b, flags) { -+ DELIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c, flags) { -+ DELIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d, flags) { -+ for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) -+ clear_bit(d, (void *) dtree->bitmap); -+ set_bit(b, (void *) btree->dirty); -+ } DELIP_RANGE_LOOP_END(); -+ } DELIP_RANGE_LOOP_END(); -+ } DELIP_RANGE_LOOP_END(); -+ -+ return 0; -+} -+ -+static int -+delip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptreemap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); -+ return -EINVAL; -+ } -+ -+ return __delip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip, GFP_KERNEL); -+} -+ -+static int -+delip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) -+{ -+ return __delip_single(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip, -+ GFP_ATOMIC); -+} -+ -+/* Check the status of the bitmap -+ * -1 == all bits cleared -+ * 1 == all bits set -+ * 0 == anything else -+ */ -+static inline int -+bitmap_status(struct ip_set_iptreemap_d *dtree) -+{ -+ unsigned char first = dtree->bitmap[0]; -+ int a; -+ -+ for (a = 1; a < 32; a++) -+ if (dtree->bitmap[a] != first) -+ return 0; -+ -+ return (first == 0 ? -1 : (first == 255 ? 1 : 0)); -+} -+ -+static void -+gc(unsigned long addr) -+{ -+ struct ip_set *set = (struct ip_set *) addr; -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned int a, b, c; -+ int i, j, k; -+ -+ write_lock_bh(&set->lock); -+ -+ LOOP_WALK_BEGIN_GC(map, a, btree, fullbitmap_b, cachep_b, i) { -+ LOOP_WALK_BEGIN_GC(btree, b, ctree, fullbitmap_c, cachep_c, j) { -+ if (!test_and_clear_bit(b, (void *) btree->dirty)) -+ continue; -+ LOOP_WALK_BEGIN_GC(ctree, c, dtree, fullbitmap_d, cachep_d, k) { -+ switch (bitmap_status(dtree)) { -+ case -1: -+ kmem_cache_free(cachep_d, dtree); -+ ctree->tree[c] = NULL; -+ k--; -+ break; -+ case 1: -+ kmem_cache_free(cachep_d, dtree); -+ ctree->tree[c] = fullbitmap_d; -+ k++; -+ break; -+ } -+ } LOOP_WALK_END(); -+ } LOOP_WALK_END_GC(btree, b, ctree, fullbitmap_c, cachep_c, k); -+ } LOOP_WALK_END_GC(map, a, btree, fullbitmap_b, cachep_b, j); -+ -+ write_unlock_bh(&set->lock); -+ -+ map->gc.expires = jiffies + map->gc_interval * HZ; -+ add_timer(&map->gc); -+} -+ -+static inline void -+init_gc_timer(struct ip_set *set) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ -+ init_timer(&map->gc); -+ map->gc.data = (unsigned long) set; -+ map->gc.function = gc; -+ map->gc.expires = jiffies + map->gc_interval * HZ; -+ add_timer(&map->gc); -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ struct ip_set_req_iptreemap_create *req = (struct ip_set_req_iptreemap_create *) data; -+ struct ip_set_iptreemap *map; -+ -+ if (size != sizeof(struct ip_set_req_iptreemap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap_create), size); -+ return -EINVAL; -+ } -+ -+ map = kzalloc(sizeof(*map), GFP_KERNEL); -+ if (!map) -+ return -ENOMEM; -+ -+ map->gc_interval = req->gc_interval ? req->gc_interval : IPTREEMAP_DEFAULT_GC_TIME; -+ set->data = map; -+ -+ init_gc_timer(set); -+ -+ return 0; -+} -+ -+static inline void __flush(struct ip_set_iptreemap *map) -+{ -+ struct ip_set_iptreemap_b *btree; -+ unsigned int a; -+ -+ LOOP_WALK_BEGIN(map, a, btree); -+ if (btree != fullbitmap_b) -+ free_b(btree); -+ LOOP_WALK_END(); -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ -+ while (!del_timer(&map->gc)) -+ msleep(IPTREEMAP_DESTROY_SLEEP); -+ -+ __flush(map); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ -+ while (!del_timer(&map->gc)) -+ msleep(IPTREEMAP_DESTROY_SLEEP); -+ -+ __flush(map); -+ -+ memset(map, 0, sizeof(*map)); -+ -+ init_gc_timer(set); -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_req_iptreemap_create *header = (struct ip_set_req_iptreemap_create *) data; -+ -+ header->gc_interval = map->gc_interval; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned int a, b, c, d, inrange = 0, count = 0; -+ -+ LOOP_WALK_BEGIN_COUNT(map, a, btree, inrange, count) { -+ LOOP_WALK_BEGIN_COUNT(btree, b, ctree, inrange, count) { -+ LOOP_WALK_BEGIN_COUNT(ctree, c, dtree, inrange, count) { -+ for (d = 0; d < 256; d++) { -+ if (test_bit(d, (void *) dtree->bitmap)) { -+ inrange = 1; -+ } else if (inrange) { -+ count++; -+ inrange = 0; -+ } -+ } -+ } LOOP_WALK_END_COUNT(); -+ } LOOP_WALK_END_COUNT(); -+ } LOOP_WALK_END_COUNT(); -+ -+ if (inrange) -+ count++; -+ -+ return (count * sizeof(struct ip_set_req_iptreemap)); -+} -+ -+static inline size_t add_member(void *data, size_t offset, ip_set_ip_t start, ip_set_ip_t end) -+{ -+ struct ip_set_req_iptreemap *entry = (struct ip_set_req_iptreemap *) (data + offset); -+ -+ entry->start = start; -+ entry->end = end; -+ -+ return sizeof(*entry); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_iptreemap_b *btree; -+ struct ip_set_iptreemap_c *ctree; -+ struct ip_set_iptreemap_d *dtree; -+ unsigned int a, b, c, d, inrange = 0; -+ size_t offset = 0; -+ ip_set_ip_t start = 0, end = 0, ip; -+ -+ LOOP_WALK_BEGIN(map, a, btree) { -+ LOOP_WALK_BEGIN(btree, b, ctree) { -+ LOOP_WALK_BEGIN(ctree, c, dtree) { -+ for (d = 0; d < 256; d++) { -+ if (test_bit(d, (void *) dtree->bitmap)) { -+ ip = ((a << 24) | (b << 16) | (c << 8) | d); -+ if (!inrange) { -+ inrange = 1; -+ start = ip; -+ } else if (end < ip - 1) { -+ offset += add_member(data, offset, start, end); -+ start = ip; -+ } -+ end = ip; -+ } else if (inrange) { -+ offset += add_member(data, offset, start, end); -+ inrange = 0; -+ } -+ } -+ } LOOP_WALK_END(); -+ } LOOP_WALK_END(); -+ } LOOP_WALK_END(); -+ -+ if (inrange) -+ add_member(data, offset, start, end); -+} -+ -+static struct ip_set_type ip_set_iptreemap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = create, -+ .destroy = destroy, -+ .flush = flush, -+ .reqsize = sizeof(struct ip_set_req_iptreemap), -+ .addip = addip, -+ .addip_kernel = addip_kernel, -+ .delip = delip, -+ .delip_kernel = delip_kernel, -+ .testip = testip, -+ .testip_kernel = testip_kernel, -+ .header_size = sizeof(struct ip_set_req_iptreemap_create), -+ .list_header = list_header, -+ .list_members_size = list_members_size, -+ .list_members = list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Sven Wegener "); -+MODULE_DESCRIPTION("iptreemap type of IP sets"); -+ -+static int __init ip_set_iptreemap_init(void) -+{ -+ int ret = -ENOMEM; -+ int a; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ cachep_b = kmem_cache_create("ip_set_iptreemap_b", -+ sizeof(struct ip_set_iptreemap_b), -+ 0, 0, NULL); -+#else -+ cachep_b = kmem_cache_create("ip_set_iptreemap_b", -+ sizeof(struct ip_set_iptreemap_b), -+ 0, 0, NULL, NULL); -+#endif -+ if (!cachep_b) { -+ ip_set_printk("Unable to create ip_set_iptreemap_b slab cache"); -+ goto out; -+ } -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ cachep_c = kmem_cache_create("ip_set_iptreemap_c", -+ sizeof(struct ip_set_iptreemap_c), -+ 0, 0, NULL); -+#else -+ cachep_c = kmem_cache_create("ip_set_iptreemap_c", -+ sizeof(struct ip_set_iptreemap_c), -+ 0, 0, NULL, NULL); -+#endif -+ if (!cachep_c) { -+ ip_set_printk("Unable to create ip_set_iptreemap_c slab cache"); -+ goto outb; -+ } -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ cachep_d = kmem_cache_create("ip_set_iptreemap_d", -+ sizeof(struct ip_set_iptreemap_d), -+ 0, 0, NULL); -+#else -+ cachep_d = kmem_cache_create("ip_set_iptreemap_d", -+ sizeof(struct ip_set_iptreemap_d), -+ 0, 0, NULL, NULL); -+#endif -+ if (!cachep_d) { -+ ip_set_printk("Unable to create ip_set_iptreemap_d slab cache"); -+ goto outc; -+ } -+ -+ fullbitmap_d = kmem_cache_alloc(cachep_d, GFP_KERNEL); -+ if (!fullbitmap_d) -+ goto outd; -+ -+ fullbitmap_c = kmem_cache_alloc(cachep_c, GFP_KERNEL); -+ if (!fullbitmap_c) -+ goto outbitmapd; -+ -+ fullbitmap_b = kmem_cache_alloc(cachep_b, GFP_KERNEL); -+ if (!fullbitmap_b) -+ goto outbitmapc; -+ -+ ret = ip_set_register_set_type(&ip_set_iptreemap); -+ if (0 > ret) -+ goto outbitmapb; -+ -+ /* Now init our global bitmaps */ -+ memset(fullbitmap_d->bitmap, 0xff, sizeof(fullbitmap_d->bitmap)); -+ -+ for (a = 0; a < 256; a++) -+ fullbitmap_c->tree[a] = fullbitmap_d; -+ -+ for (a = 0; a < 256; a++) -+ fullbitmap_b->tree[a] = fullbitmap_c; -+ memset(fullbitmap_b->dirty, 0, sizeof(fullbitmap_b->dirty)); -+ -+ return 0; -+ -+outbitmapb: -+ kmem_cache_free(cachep_b, fullbitmap_b); -+outbitmapc: -+ kmem_cache_free(cachep_c, fullbitmap_c); -+outbitmapd: -+ kmem_cache_free(cachep_d, fullbitmap_d); -+outd: -+ kmem_cache_destroy(cachep_d); -+outc: -+ kmem_cache_destroy(cachep_c); -+outb: -+ kmem_cache_destroy(cachep_b); -+out: -+ -+ return ret; -+} -+ -+static void __exit ip_set_iptreemap_fini(void) -+{ -+ ip_set_unregister_set_type(&ip_set_iptreemap); -+ kmem_cache_free(cachep_d, fullbitmap_d); -+ kmem_cache_free(cachep_c, fullbitmap_c); -+ kmem_cache_free(cachep_b, fullbitmap_b); -+ kmem_cache_destroy(cachep_d); -+ kmem_cache_destroy(cachep_c); -+ kmem_cache_destroy(cachep_b); -+} -+ -+module_init(ip_set_iptreemap_init); -+module_exit(ip_set_iptreemap_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_macipmap.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_macipmap.c -@@ -0,0 +1,375 @@ -+/* Copyright (C) 2000-2002 Joakim Axelsson -+ * Patrick Schaaf -+ * Martin Josefsson -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module implementing an IP set type: the macipmap type */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = (struct ip_set_macip *) map->members; -+ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_macipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap), -+ size); -+ return -EINVAL; -+ } -+ -+ if (req->ip < map->first_ip || req->ip > map->last_ip) -+ return -ERANGE; -+ -+ *hash_ip = req->ip; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); -+ if (test_bit(IPSET_MACIP_ISSET, -+ (void *) &table[req->ip - map->first_ip].flags)) { -+ return (memcmp(req->ethernet, -+ &table[req->ip - map->first_ip].ethernet, -+ ETH_ALEN) == 0); -+ } else { -+ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); -+ } -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = -+ (struct ip_set_macip *) map->members; -+ ip_set_ip_t ip; -+ -+ ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return 0; -+ -+ *hash_ip = ip; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ if (test_bit(IPSET_MACIP_ISSET, -+ (void *) &table[ip - map->first_ip].flags)) { -+ /* Is mac pointer valid? -+ * If so, compare... */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ return (skb_mac_header(skb) >= skb->head -+ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data -+#else -+ return (skb->mac.raw >= skb->head -+ && (skb->mac.raw + ETH_HLEN) <= skb->data -+#endif -+ && (memcmp(eth_hdr(skb)->h_source, -+ &table[ip - map->first_ip].ethernet, -+ ETH_ALEN) == 0)); -+ } else { -+ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); -+ } -+} -+ -+/* returns 0 on success */ -+static inline int -+__addip(struct ip_set *set, -+ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = -+ (struct ip_set_macip *) map->members; -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ if (test_and_set_bit(IPSET_MACIP_ISSET, -+ (void *) &table[ip - map->first_ip].flags)) -+ return -EEXIST; -+ -+ *hash_ip = ip; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); -+ return 0; -+} -+ -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_macipmap *req = -+ (struct ip_set_req_macipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_macipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap), -+ size); -+ return -EINVAL; -+ } -+ return __addip(set, req->ip, req->ethernet, hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ ip_set_ip_t ip; -+ -+ ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (!(skb_mac_header(skb) >= skb->head -+ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data)) -+#else -+ if (!(skb->mac.raw >= skb->head -+ && (skb->mac.raw + ETH_HLEN) <= skb->data)) -+#endif -+ return -EINVAL; -+ -+ return __addip(set, ip, eth_hdr(skb)->h_source, hash_ip); -+} -+ -+static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = -+ (struct ip_set_macip *) map->members; -+ -+ if (ip < map->first_ip || ip > map->last_ip) -+ return -ERANGE; -+ if (!test_and_clear_bit(IPSET_MACIP_ISSET, -+ (void *)&table[ip - map->first_ip].flags)) -+ return -EEXIST; -+ -+ *hash_ip = ip; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ return 0; -+} -+ -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_macipmap *req = -+ (struct ip_set_req_macipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_macipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) -+{ -+ return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ int newbytes; -+ struct ip_set_req_macipmap_create *req = -+ (struct ip_set_req_macipmap_create *) data; -+ struct ip_set_macipmap *map; -+ -+ if (size != sizeof(struct ip_set_req_macipmap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap_create), -+ size); -+ return -EINVAL; -+ } -+ -+ DP("from %u.%u.%u.%u to %u.%u.%u.%u", -+ HIPQUAD(req->from), HIPQUAD(req->to)); -+ -+ if (req->from > req->to) { -+ DP("bad ip range"); -+ return -ENOEXEC; -+ } -+ -+ if (req->to - req->from > MAX_RANGE) { -+ ip_set_printk("range too big (max %d addresses)", -+ MAX_RANGE+1); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_macipmap)); -+ return -ENOMEM; -+ } -+ map->flags = req->flags; -+ map->first_ip = req->from; -+ map->last_ip = req->to; -+ newbytes = members_size(map->first_ip, map->last_ip); -+ map->members = ip_set_malloc(newbytes); -+ DP("members: %u %p", newbytes, map->members); -+ if (!map->members) { -+ DP("out of memory for %d bytes", newbytes); -+ kfree(map); -+ return -ENOMEM; -+ } -+ memset(map->members, 0, newbytes); -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ -+ ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ memset(map->members, 0, members_size(map->first_ip, map->last_ip)); -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_req_macipmap_create *header = -+ (struct ip_set_req_macipmap_create *) data; -+ -+ DP("list_header %x %x %u", map->first_ip, map->last_ip, -+ map->flags); -+ -+ header->from = map->first_ip; -+ header->to = map->last_ip; -+ header->flags = map->flags; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ -+ DP("%u", members_size(map->first_ip, map->last_ip)); -+ return members_size(map->first_ip, map->last_ip); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ -+ int bytes = members_size(map->first_ip, map->last_ip); -+ -+ DP("members: %u %p", bytes, map->members); -+ memcpy(data, map->members, bytes); -+} -+ -+static struct ip_set_type ip_set_macipmap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_macipmap), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_macipmap_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("macipmap type of IP sets"); -+ -+static int __init ip_set_macipmap_init(void) -+{ -+ init_max_malloc_size(); -+ return ip_set_register_set_type(&ip_set_macipmap); -+} -+ -+static void __exit ip_set_macipmap_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_macipmap); -+} -+ -+module_init(ip_set_macipmap_init); -+module_exit(ip_set_macipmap_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_nethash.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_nethash.c -@@ -0,0 +1,497 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module implementing a cidr nethash set */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+static int limit = MAX_RANGE; -+ -+static inline __u32 -+jhash_ip(const struct ip_set_nethash *map, uint16_t i, ip_set_ip_t ip) -+{ -+ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); -+} -+ -+static inline __u32 -+hash_id_cidr(struct ip_set_nethash *map, -+ ip_set_ip_t ip, -+ unsigned char cidr, -+ ip_set_ip_t *hash_ip) -+{ -+ __u32 id; -+ u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ *hash_ip = pack(ip, cidr); -+ -+ for (i = 0; i < map->probes; i++) { -+ id = jhash_ip(map, i, *hash_ip) % map->hashsize; -+ DP("hash key: %u", id); -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ if (*elem == *hash_ip) -+ return id; -+ } -+ return UINT_MAX; -+} -+ -+static inline __u32 -+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ __u32 id = UINT_MAX; -+ int i; -+ -+ for (i = 0; i < 30 && map->cidr[i]; i++) { -+ id = hash_id_cidr(map, ip, map->cidr[i], hash_ip); -+ if (id != UINT_MAX) -+ break; -+ } -+ return id; -+} -+ -+static inline int -+__testip_cidr(struct ip_set *set, ip_set_ip_t ip, unsigned char cidr, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ -+ return (ip && hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX); -+} -+ -+static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); -+} -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_nethash *req = -+ (struct ip_set_req_nethash *) data; -+ -+ if (size != sizeof(struct ip_set_req_nethash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash), -+ size); -+ return -EINVAL; -+ } -+ return (req->cidr == 32 ? __testip(set, req->ip, hash_ip) -+ : __testip_cidr(set, req->ip, req->cidr, hash_ip)); -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static inline int -+__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip) -+{ -+ __u32 probe; -+ u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ for (i = 0; i < map->probes; i++) { -+ probe = jhash_ip(map, i, ip) % map->hashsize; -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); -+ if (*elem == ip) -+ return -EEXIST; -+ if (!*elem) { -+ *elem = ip; -+ map->elements++; -+ return 0; -+ } -+ } -+ /* Trigger rehashing */ -+ return -EAGAIN; -+} -+ -+static inline int -+__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, -+ ip_set_ip_t *hash_ip) -+{ -+ if (!ip || map->elements >= limit) -+ return -ERANGE; -+ -+ *hash_ip = pack(ip, cidr); -+ DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip)); -+ -+ return __addip_base(map, *hash_ip); -+} -+ -+static void -+update_cidr_sizes(struct ip_set_nethash *map, unsigned char cidr) -+{ -+ unsigned char next; -+ int i; -+ -+ for (i = 0; i < 30 && map->cidr[i]; i++) { -+ if (map->cidr[i] == cidr) { -+ return; -+ } else if (map->cidr[i] < cidr) { -+ next = map->cidr[i]; -+ map->cidr[i] = cidr; -+ cidr = next; -+ } -+ } -+ if (i < 30) -+ map->cidr[i] = cidr; -+} -+ -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_nethash *req = -+ (struct ip_set_req_nethash *) data; -+ int ret; -+ -+ if (size != sizeof(struct ip_set_req_nethash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash), -+ size); -+ return -EINVAL; -+ } -+ ret = __addip((struct ip_set_nethash *) set->data, -+ req->ip, req->cidr, hash_ip); -+ -+ if (ret == 0) -+ update_cidr_sizes((struct ip_set_nethash *) set->data, -+ req->cidr); -+ -+ return ret; -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ int ret = -ERANGE; -+ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif -+ -+ if (map->cidr[0]) -+ ret = __addip(map, ip, map->cidr[0], hash_ip); -+ -+ return ret; -+} -+ -+static int retry(struct ip_set *set) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ ip_set_ip_t *elem; -+ void *members; -+ u_int32_t i, hashsize = map->hashsize; -+ int res; -+ struct ip_set_nethash *tmp; -+ -+ if (map->resize == 0) -+ return -ERANGE; -+ -+ again: -+ res = 0; -+ -+ /* Calculate new parameters */ -+ hashsize += (hashsize * map->resize)/100; -+ if (hashsize == map->hashsize) -+ hashsize++; -+ -+ ip_set_printk("rehashing of set %s triggered: " -+ "hashsize grows from %u to %u", -+ set->name, map->hashsize, hashsize); -+ -+ tmp = kmalloc(sizeof(struct ip_set_nethash) -+ + map->probes * sizeof(uint32_t), GFP_ATOMIC); -+ if (!tmp) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_nethash) -+ + map->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); -+ if (!tmp->members) { -+ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); -+ kfree(tmp); -+ return -ENOMEM; -+ } -+ tmp->hashsize = hashsize; -+ tmp->elements = 0; -+ tmp->probes = map->probes; -+ tmp->resize = map->resize; -+ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); -+ memcpy(tmp->cidr, map->cidr, 30 * sizeof(unsigned char)); -+ -+ write_lock_bh(&set->lock); -+ map = (struct ip_set_nethash *) set->data; /* Play safe */ -+ for (i = 0; i < map->hashsize && res == 0; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ if (*elem) -+ res = __addip_base(tmp, *elem); -+ } -+ if (res) { -+ /* Failure, try again */ -+ write_unlock_bh(&set->lock); -+ harray_free(tmp->members); -+ kfree(tmp); -+ goto again; -+ } -+ -+ /* Success at resizing! */ -+ members = map->members; -+ -+ map->hashsize = tmp->hashsize; -+ map->members = tmp->members; -+ write_unlock_bh(&set->lock); -+ -+ harray_free(members); -+ kfree(tmp); -+ -+ return 0; -+} -+ -+static inline int -+__delip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, -+ ip_set_ip_t *hash_ip) -+{ -+ ip_set_ip_t id, *elem; -+ -+ if (!ip) -+ return -ERANGE; -+ -+ id = hash_id_cidr(map, ip, cidr, hash_ip); -+ if (id == UINT_MAX) -+ return -EEXIST; -+ -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ *elem = 0; -+ map->elements--; -+ return 0; -+} -+ -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_nethash *req = -+ (struct ip_set_req_nethash *) data; -+ -+ if (size != sizeof(struct ip_set_req_nethash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash), -+ size); -+ return -EINVAL; -+ } -+ /* TODO: no garbage collection in map->cidr */ -+ return __delip((struct ip_set_nethash *) set->data, -+ req->ip, req->cidr, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ int ret = -ERANGE; -+ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif -+ -+ if (map->cidr[0]) -+ ret = __delip(map, ip, map->cidr[0], hash_ip); -+ -+ return ret; -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ struct ip_set_req_nethash_create *req = -+ (struct ip_set_req_nethash_create *) data; -+ struct ip_set_nethash *map; -+ uint16_t i; -+ -+ if (size != sizeof(struct ip_set_req_nethash_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash_create), -+ size); -+ return -EINVAL; -+ } -+ -+ if (req->hashsize < 1) { -+ ip_set_printk("hashsize too small"); -+ return -ENOEXEC; -+ } -+ if (req->probes < 1) { -+ ip_set_printk("probes too small"); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_nethash) -+ + req->probes * sizeof(uint32_t), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_nethash) -+ + req->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ for (i = 0; i < req->probes; i++) -+ get_random_bytes(((uint32_t *) map->initval)+i, 4); -+ map->elements = 0; -+ map->hashsize = req->hashsize; -+ map->probes = req->probes; -+ map->resize = req->resize; -+ memset(map->cidr, 0, 30 * sizeof(unsigned char)); -+ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); -+ kfree(map); -+ return -ENOMEM; -+ } -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ -+ harray_free(map->members); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); -+ memset(map->cidr, 0, 30 * sizeof(unsigned char)); -+ map->elements = 0; -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ struct ip_set_req_nethash_create *header = -+ (struct ip_set_req_nethash_create *) data; -+ -+ header->hashsize = map->hashsize; -+ header->probes = map->probes; -+ header->resize = map->resize; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ -+ return (map->hashsize * sizeof(ip_set_ip_t)); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ ip_set_ip_t i, *elem; -+ -+ for (i = 0; i < map->hashsize; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ ((ip_set_ip_t *)data)[i] = *elem; -+ } -+} -+ -+static struct ip_set_type ip_set_nethash = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_nethash), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .retry = &retry, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_nethash_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("nethash type of IP sets"); -+module_param(limit, int, 0600); -+MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); -+ -+static int __init ip_set_nethash_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_nethash); -+} -+ -+static void __exit ip_set_nethash_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_nethash); -+} -+ -+module_init(ip_set_nethash_init); -+module_exit(ip_set_nethash_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ip_set_portmap.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ip_set_portmap.c -@@ -0,0 +1,346 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module implementing a port set type as a bitmap */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+ -+/* We must handle non-linear skbs */ -+static inline ip_set_ip_t -+get_port(const struct sk_buff *skb, u_int32_t flags) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ struct iphdr *iph = ip_hdr(skb); -+#else -+ struct iphdr *iph = skb->nh.iph; -+#endif -+ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; -+ switch (iph->protocol) { -+ case IPPROTO_TCP: { -+ struct tcphdr tcph; -+ -+ /* See comments at tcp_match in ip_tables.c */ -+ if (offset) -+ return INVALID_PORT; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ tcph.source : tcph.dest); -+ } -+ case IPPROTO_UDP: { -+ struct udphdr udph; -+ -+ if (offset) -+ return INVALID_PORT; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ udph.source : udph.dest); -+ } -+ default: -+ return INVALID_PORT; -+ } -+} -+ -+static inline int -+__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ -+ if (port < map->first_port || port > map->last_port) -+ return -ERANGE; -+ -+ *hash_port = port; -+ DP("set: %s, port:%u, %u", set->name, port, *hash_port); -+ return !!test_bit(port - map->first_port, map->members); -+} -+ -+static int -+testport(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_port) -+{ -+ struct ip_set_req_portmap *req = -+ (struct ip_set_req_portmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_portmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap), -+ size); -+ return -EINVAL; -+ } -+ return __testport(set, req->port, hash_port); -+} -+ -+static int -+testport_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_port, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ int res; -+ ip_set_ip_t port = get_port(skb, flags[index]); -+ -+ DP("flag %s port %u", flags[index] & IPSET_SRC ? "SRC" : "DST", port); -+ if (port == INVALID_PORT) -+ return 0; -+ -+ res = __testport(set, port, hash_port); -+ -+ return (res < 0 ? 0 : res); -+} -+ -+static inline int -+__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ -+ if (port < map->first_port || port > map->last_port) -+ return -ERANGE; -+ if (test_and_set_bit(port - map->first_port, map->members)) -+ return -EEXIST; -+ -+ *hash_port = port; -+ DP("port %u", port); -+ return 0; -+} -+ -+static int -+addport(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_port) -+{ -+ struct ip_set_req_portmap *req = -+ (struct ip_set_req_portmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_portmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap), -+ size); -+ return -EINVAL; -+ } -+ return __addport(set, req->port, hash_port); -+} -+ -+static int -+addport_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_port, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ ip_set_ip_t port = get_port(skb, flags[index]); -+ -+ if (port == INVALID_PORT) -+ return -EINVAL; -+ -+ return __addport(set, port, hash_port); -+} -+ -+static inline int -+__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ -+ if (port < map->first_port || port > map->last_port) -+ return -ERANGE; -+ if (!test_and_clear_bit(port - map->first_port, map->members)) -+ return -EEXIST; -+ -+ *hash_port = port; -+ DP("port %u", port); -+ return 0; -+} -+ -+static int -+delport(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_port) -+{ -+ struct ip_set_req_portmap *req = -+ (struct ip_set_req_portmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_portmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap), -+ size); -+ return -EINVAL; -+ } -+ return __delport(set, req->port, hash_port); -+} -+ -+static int -+delport_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_port, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ ip_set_ip_t port = get_port(skb, flags[index]); -+ -+ if (port == INVALID_PORT) -+ return -EINVAL; -+ -+ return __delport(set, port, hash_port); -+} -+ -+static int create(struct ip_set *set, const void *data, size_t size) -+{ -+ int newbytes; -+ struct ip_set_req_portmap_create *req = -+ (struct ip_set_req_portmap_create *) data; -+ struct ip_set_portmap *map; -+ -+ if (size != sizeof(struct ip_set_req_portmap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap_create), -+ size); -+ return -EINVAL; -+ } -+ -+ DP("from %u to %u", req->from, req->to); -+ -+ if (req->from > req->to) { -+ DP("bad port range"); -+ return -ENOEXEC; -+ } -+ -+ if (req->to - req->from > MAX_RANGE) { -+ ip_set_printk("range too big (max %d ports)", -+ MAX_RANGE+1); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_portmap)); -+ return -ENOMEM; -+ } -+ map->first_port = req->from; -+ map->last_port = req->to; -+ newbytes = bitmap_bytes(req->from, req->to); -+ map->members = kmalloc(newbytes, GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", newbytes); -+ kfree(map); -+ return -ENOMEM; -+ } -+ memset(map->members, 0, newbytes); -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ -+ kfree(map->members); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ struct ip_set_req_portmap_create *header = -+ (struct ip_set_req_portmap_create *) data; -+ -+ DP("list_header %u %u", map->first_port, map->last_port); -+ -+ header->from = map->first_port; -+ header->to = map->last_port; -+} -+ -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ -+ return bitmap_bytes(map->first_port, map->last_port); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ int bytes = bitmap_bytes(map->first_port, map->last_port); -+ -+ memcpy(data, map->members, bytes); -+} -+ -+static struct ip_set_type ip_set_portmap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_PORT | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_portmap), -+ .addip = &addport, -+ .addip_kernel = &addport_kernel, -+ .delip = &delport, -+ .delip_kernel = &delport_kernel, -+ .testip = &testport, -+ .testip_kernel = &testport_kernel, -+ .header_size = sizeof(struct ip_set_req_portmap_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("portmap type of IP sets"); -+ -+static int __init ip_set_portmap_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_portmap); -+} -+ -+static void __exit ip_set_portmap_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_portmap); -+} -+ -+module_init(ip_set_portmap_init); -+module_exit(ip_set_portmap_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_set.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ipt_set.c -@@ -0,0 +1,160 @@ -+/* Copyright (C) 2000-2002 Joakim Axelsson -+ * Patrick Schaaf -+ * Martin Josefsson -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Kernel module to match an IP set. */ -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+static inline int -+match_set(const struct ipt_set_info *info, -+ const struct sk_buff *skb, -+ int inv) -+{ -+ if (ip_set_testip_kernel(info->index, skb, info->flags)) -+ inv = !inv; -+ return inv; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+static bool -+#else -+static int -+#endif -+match(const struct sk_buff *skb, -+ const struct net_device *in, -+ const struct net_device *out, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_match *match, -+#endif -+ const void *matchinfo, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ int offset, unsigned int protoff, bool *hotdrop) -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ int offset, unsigned int protoff, int *hotdrop) -+#else -+ int offset, int *hotdrop) -+#endif -+{ -+ const struct ipt_set_info_match *info = matchinfo; -+ -+ return match_set(&info->match_set, -+ skb, -+ info->match_set.flags[0] & IPSET_MATCH_INV); -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+bool -+#else -+static int -+#endif -+checkentry(const char *tablename, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ const void *inf, -+#else -+ const struct ipt_ip *ip, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_match *match, -+#endif -+ void *matchinfo, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ unsigned int matchsize, -+#endif -+ unsigned int hook_mask) -+{ -+ struct ipt_set_info_match *info = -+ (struct ipt_set_info_match *) matchinfo; -+ ip_set_id_t index; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { -+ ip_set_printk("invalid matchsize %d", matchsize); -+ return 0; -+ } -+#endif -+ -+ index = ip_set_get_byindex(info->match_set.index); -+ -+ if (index == IP_SET_INVALID_ID) { -+ ip_set_printk("Cannot find set indentified by id %u to match", -+ info->match_set.index); -+ return 0; /* error */ -+ } -+ if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { -+ ip_set_printk("That's nasty!"); -+ return 0; /* error */ -+ } -+ -+ return 1; -+} -+ -+static void destroy( -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_match *match, -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ void *matchinfo, unsigned int matchsize) -+#else -+ void *matchinfo) -+#endif -+{ -+ struct ipt_set_info_match *info = matchinfo; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { -+ ip_set_printk("invalid matchsize %d", matchsize); -+ return; -+ } -+#endif -+ ip_set_put(info->match_set.index); -+} -+ -+static struct ipt_match set_match = { -+ .name = "set", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+ .family = AF_INET, -+#endif -+ .match = &match, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ .matchsize = sizeof(struct ipt_set_info_match), -+#endif -+ .checkentry = &checkentry, -+ .destroy = &destroy, -+ .me = THIS_MODULE -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("iptables IP set match module"); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+#define ipt_register_match xt_register_match -+#define ipt_unregister_match xt_unregister_match -+#endif -+ -+static int __init ipt_ipset_init(void) -+{ -+ return ipt_register_match(&set_match); -+} -+ -+static void __exit ipt_ipset_fini(void) -+{ -+ ipt_unregister_match(&set_match); -+} -+ -+module_init(ipt_ipset_init); -+module_exit(ipt_ipset_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_SET.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ipt_SET.c -@@ -0,0 +1,172 @@ -+/* Copyright (C) 2000-2002 Joakim Axelsson -+ * Patrick Schaaf -+ * Martin Josefsson -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* ipt_SET.c - netfilter target to manipulate IP sets */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static unsigned int -+target(struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ unsigned int hooknum, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ const void *targinfo, -+ void *userinfo) -+#else -+ const void *targinfo) -+#endif -+{ -+ const struct ipt_set_info_target *info = targinfo; -+ -+ if (info->add_set.index != IP_SET_INVALID_ID) -+ ip_set_addip_kernel(info->add_set.index, -+ *pskb, -+ info->add_set.flags); -+ if (info->del_set.index != IP_SET_INVALID_ID) -+ ip_set_delip_kernel(info->del_set.index, -+ *pskb, -+ info->del_set.flags); -+ -+ return IPT_CONTINUE; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+static bool -+#else -+static int -+#endif -+checkentry(const char *tablename, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ const void *e, -+#else -+ const struct ipt_entry *e, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+ void *targinfo, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ unsigned int targinfosize, -+#endif -+ unsigned int hook_mask) -+{ -+ struct ipt_set_info_target *info = -+ (struct ipt_set_info_target *) targinfo; -+ ip_set_id_t index; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ if (targinfosize != IPT_ALIGN(sizeof(*info))) { -+ DP("bad target info size %u", targinfosize); -+ return 0; -+ } -+#endif -+ -+ if (info->add_set.index != IP_SET_INVALID_ID) { -+ index = ip_set_get_byindex(info->add_set.index); -+ if (index == IP_SET_INVALID_ID) { -+ ip_set_printk("cannot find add_set index %u as target", -+ info->add_set.index); -+ return 0; /* error */ -+ } -+ } -+ -+ if (info->del_set.index != IP_SET_INVALID_ID) { -+ index = ip_set_get_byindex(info->del_set.index); -+ if (index == IP_SET_INVALID_ID) { -+ ip_set_printk("cannot find del_set index %u as target", -+ info->del_set.index); -+ return 0; /* error */ -+ } -+ } -+ if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 -+ || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { -+ ip_set_printk("That's nasty!"); -+ return 0; /* error */ -+ } -+ -+ return 1; -+} -+ -+static void destroy( -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ void *targetinfo, unsigned int targetsize) -+#else -+ void *targetinfo) -+#endif -+{ -+ struct ipt_set_info_target *info = targetinfo; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { -+ ip_set_printk("invalid targetsize %d", targetsize); -+ return; -+ } -+#endif -+ if (info->add_set.index != IP_SET_INVALID_ID) -+ ip_set_put(info->add_set.index); -+ if (info->del_set.index != IP_SET_INVALID_ID) -+ ip_set_put(info->del_set.index); -+} -+ -+static struct ipt_target SET_target = { -+ .name = "SET", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+ .family = AF_INET, -+#endif -+ .target = target, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ .targetsize = sizeof(struct ipt_set_info_target), -+#endif -+ .checkentry = checkentry, -+ .destroy = destroy, -+ .me = THIS_MODULE -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("iptables IP set target module"); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+#define ipt_register_target xt_register_target -+#define ipt_unregister_target xt_unregister_target -+#endif -+ -+static int __init ipt_SET_init(void) -+{ -+ return ipt_register_target(&SET_target); -+} -+ -+static void __exit ipt_SET_fini(void) -+{ -+ ipt_unregister_target(&SET_target); -+} -+ -+module_init(ipt_SET_init); -+module_exit(ipt_SET_fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig -+++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig -@@ -663,5 +663,122 @@ config IP_NF_ARP_MANGLE - Allows altering the ARP packet payload: source and destination - hardware and network addresses. - -+config IP_NF_SET -+ tristate "IP set support" -+ depends on INET && NETFILTER -+ help -+ This option adds IP set support to the kernel. -+ In order to define and use sets, you need the userspace utility -+ ipset(8). -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_MAX -+ int "Maximum number of IP sets" -+ default 256 -+ range 2 65534 -+ depends on IP_NF_SET -+ help -+ You can define here default value of the maximum number -+ of IP sets for the kernel. -+ -+ The value can be overriden by the 'max_sets' module -+ parameter of the 'ip_set' module. -+ -+config IP_NF_SET_HASHSIZE -+ int "Hash size for bindings of IP sets" -+ default 1024 -+ depends on IP_NF_SET -+ help -+ You can define here default value of the hash size for -+ bindings of IP sets. -+ -+ The value can be overriden by the 'hash_size' module -+ parameter of the 'ip_set' module. -+ -+config IP_NF_SET_IPMAP -+ tristate "ipmap set support" -+ depends on IP_NF_SET -+ help -+ This option adds the ipmap set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_MACIPMAP -+ tristate "macipmap set support" -+ depends on IP_NF_SET -+ help -+ This option adds the macipmap set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_PORTMAP -+ tristate "portmap set support" -+ depends on IP_NF_SET -+ help -+ This option adds the portmap set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_IPHASH -+ tristate "iphash set support" -+ depends on IP_NF_SET -+ help -+ This option adds the iphash set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_NETHASH -+ tristate "nethash set support" -+ depends on IP_NF_SET -+ help -+ This option adds the nethash set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_IPPORTHASH -+ tristate "ipporthash set support" -+ depends on IP_NF_SET -+ help -+ This option adds the ipporthash set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_IPTREE -+ tristate "iptree set support" -+ depends on IP_NF_SET -+ help -+ This option adds the iptree set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_SET_IPTREEMAP -+ tristate "iptreemap set support" -+ depends on IP_NF_SET -+ help -+ This option adds the iptreemap set type support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_MATCH_SET -+ tristate "set match support" -+ depends on IP_NF_SET -+ help -+ Set matching matches against given IP sets. -+ You need the ipset utility to create and set up the sets. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+config IP_NF_TARGET_SET -+ tristate "SET target support" -+ depends on IP_NF_SET -+ help -+ The SET target makes possible to add/delete entries -+ in IP sets. -+ You need the ipset utility to create and set up the sets. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ -+ - endmenu - -Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile -+++ linux-2.6.21.7/net/ipv4/netfilter/Makefile -@@ -90,6 +90,7 @@ obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_ - obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o - obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o - obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o -+obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o - obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o - obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o - -@@ -106,6 +107,18 @@ obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LO - obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o - obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o - obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o -+obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o -+ -+# sets -+obj-$(CONFIG_IP_NF_SET) += ip_set.o -+obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o -+obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o -+obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o -+obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o -+obj-$(CONFIG_IP_NF_SET_NETHASH) += ip_set_nethash.o -+obj-$(CONFIG_IP_NF_SET_IPPORTHASH) += ip_set_ipporthash.o -+obj-$(CONFIG_IP_NF_SET_IPTREE) += ip_set_iptree.o -+obj-$(CONFIG_IP_NF_SET_IPTREEMAP) += ip_set_iptreemap.o - - # generic ARP tables - obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o diff --git a/target/linux/generic-2.6/patches/140-netfilter_time.patch b/target/linux/generic-2.6/patches/140-netfilter_time.patch deleted file mode 100644 index f1feaf6c0d..0000000000 --- a/target/linux/generic-2.6/patches/140-netfilter_time.patch +++ /dev/null @@ -1,245 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_time.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_time.h -@@ -0,0 +1,18 @@ -+#ifndef __ipt_time_h_included__ -+#define __ipt_time_h_included__ -+ -+ -+struct ipt_time_info { -+ u_int8_t days_match; /* 1 bit per day. -SMTWTFS */ -+ u_int16_t time_start; /* 0 < time_start < 23*60+59 = 1439 */ -+ u_int16_t time_stop; /* 0:0 < time_stat < 23:59 */ -+ -+ /* FIXME: Keep this one for userspace iptables binary compability: */ -+ u_int8_t kerneltime; /* ignore skb time (and use kerneltime) or not. */ -+ -+ time_t date_start; -+ time_t date_stop; -+}; -+ -+ -+#endif /* __ipt_time_h_included__ */ -Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_time.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ipt_time.c -@@ -0,0 +1,178 @@ -+/* -+ This is a module which is used for time matching -+ It is using some modified code from dietlibc (localtime() function) -+ that you can find at http://www.fefe.de/dietlibc/ -+ This file is distributed under the terms of the GNU General Public -+ License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL -+ 2001-05-04 Fabrice MARIE : initial development. -+ 2001-21-05 Fabrice MARIE : bug fix in the match code, -+ thanks to "Zeng Yu" for bug report. -+ 2001-26-09 Fabrice MARIE : force the match to be in LOCAL_IN or PRE_ROUTING only. -+ 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack, -+ added Nguyen Dang Phuoc Dong patch to support timezones. -+ 2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO. -+*/ -+ -+#include -+#include -+#include -+#include -+#include -+ -+MODULE_AUTHOR("Fabrice MARIE "); -+MODULE_DESCRIPTION("Match arrival timestamp/date"); -+MODULE_LICENSE("GPL"); -+ -+struct tm -+{ -+ int tm_sec; /* Seconds. [0-60] (1 leap second) */ -+ int tm_min; /* Minutes. [0-59] */ -+ int tm_hour; /* Hours. [0-23] */ -+ int tm_mday; /* Day. [1-31] */ -+ int tm_mon; /* Month. [0-11] */ -+ int tm_year; /* Year - 1900. */ -+ int tm_wday; /* Day of week. [0-6] */ -+ int tm_yday; /* Days in year.[0-365] */ -+ int tm_isdst; /* DST. [-1/0/1]*/ -+ -+ long int tm_gmtoff; /* we don't care, we count from GMT */ -+ const char *tm_zone; /* we don't care, we count from GMT */ -+}; -+ -+void -+localtime(const u32 time, struct tm *r); -+ -+static int -+match(const struct sk_buff *skb, -+ const struct net_device *in, -+ const struct net_device *out, -+ const struct xt_match *match, -+ const void *matchinfo, -+ int offset, -+ unsigned int protoff, -+ int *hotdrop) -+{ -+ const struct ipt_time_info *info = matchinfo; /* match info for rule */ -+ struct tm currenttime; /* time human readable */ -+ u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; -+ u_int16_t packet_time; -+ -+ /* We might not have a timestamp, get one */ -+ if (skb->tstamp.off_sec == 0) -+ __net_timestamp((struct sk_buff *)skb); -+ -+ /* First we make sure we are in the date start-stop boundaries */ -+ if ((skb->tstamp.off_sec < info->date_start) || (skb->tstamp.off_sec > info->date_stop)) -+ return 0; /* We are outside the date boundaries */ -+ -+ /* Transform the timestamp of the packet, in a human readable form */ -+ localtime(skb->tstamp.off_sec, ¤ttime); -+ -+ /* check if we match this timestamp, we start by the days... */ -+ if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday]) -+ return 0; /* the day doesn't match */ -+ -+ /* ... check the time now */ -+ packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min; -+ if ((packet_time < info->time_start) || (packet_time > info->time_stop)) -+ return 0; -+ -+ /* here we match ! */ -+ return 1; -+} -+ -+static int -+checkentry(const char *tablename, -+ const void *ip, -+ const struct xt_match *match, -+ void *matchinfo, -+ unsigned int hook_mask) -+{ -+ struct ipt_time_info *info = matchinfo; /* match info for rule */ -+ -+ /* First, check that we are in the correct hooks */ -+ if (hook_mask -+ & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) -+ { -+ printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n"); -+ return 0; -+ } -+ -+ /* Now check the coherence of the data ... */ -+ if ((info->time_start > 1439) || /* 23*60+59 = 1439*/ -+ (info->time_stop > 1439)) -+ { -+ printk(KERN_WARNING "ipt_time: invalid argument\n"); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+static struct ipt_match time_match = { -+ .name = "time", -+ .match = &match, -+ .matchsize = sizeof(struct ipt_time_info), -+ .checkentry = &checkentry, -+ .me = THIS_MODULE -+}; -+ -+static int __init init(void) -+{ -+ printk("ipt_time loading\n"); -+ return xt_register_match(&time_match); -+} -+ -+static void __exit fini(void) -+{ -+ xt_unregister_match(&time_match); -+ printk("ipt_time unloaded\n"); -+} -+ -+module_init(init); -+module_exit(fini); -+ -+ -+/* The part below is borowed and modified from dietlibc */ -+ -+/* seconds per day */ -+#define SPD 24*60*60 -+ -+void -+localtime(const u32 time, struct tm *r) { -+ u32 i, timep; -+ extern struct timezone sys_tz; -+ const unsigned int __spm[12] = -+ { 0, -+ (31), -+ (31+28), -+ (31+28+31), -+ (31+28+31+30), -+ (31+28+31+30+31), -+ (31+28+31+30+31+30), -+ (31+28+31+30+31+30+31), -+ (31+28+31+30+31+30+31+31), -+ (31+28+31+30+31+30+31+31+30), -+ (31+28+31+30+31+30+31+31+30+31), -+ (31+28+31+30+31+30+31+31+30+31+30), -+ }; -+ register u32 work; -+ -+ timep = time - (sys_tz.tz_minuteswest * 60); -+ work=timep%(SPD); -+ r->tm_sec=work%60; work/=60; -+ r->tm_min=work%60; r->tm_hour=work/60; -+ work=timep/(SPD); -+ r->tm_wday=(4+work)%7; -+ for (i=1970; ; ++i) { -+ register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365; -+ if (work>k) -+ work-=k; -+ else -+ break; -+ } -+ r->tm_year=i-1900; -+ for (i=11; i && __spm[i]>work; --i) ; -+ r->tm_mon=i; -+ r->tm_mday=work-__spm[i]+1; -+} -Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig -+++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig -@@ -260,6 +260,22 @@ config IP_NF_MATCH_TOS - - To compile it as a module, choose M here. If unsure, say N. - -+ -+config IP_NF_MATCH_TIME -+ tristate 'TIME match support' -+ depends on IP_NF_IPTABLES -+ help -+ This option adds a `time' match, which allows you -+ to match based on the packet arrival time/date -+ (arrival time/date at the machine which netfilter is running on) or -+ departure time/date (for locally generated packets). -+ -+ If you say Y here, try iptables -m time --help for more information. -+ If you want to compile it as a module, say M here and read -+ -+ Documentation/modules.txt. If unsure, say `N'. -+ -+ - config IP_NF_MATCH_RECENT - tristate "recent match support" - depends on IP_NF_IPTABLES -Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile -+++ linux-2.6.21.7/net/ipv4/netfilter/Makefile -@@ -86,6 +86,7 @@ obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o - obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o - obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o - obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o -+obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o - obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o - obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o - obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o diff --git a/target/linux/generic-2.6/patches/150-netfilter_imq.patch b/target/linux/generic-2.6/patches/150-netfilter_imq.patch deleted file mode 100644 index 66fb99bfb1..0000000000 --- a/target/linux/generic-2.6/patches/150-netfilter_imq.patch +++ /dev/null @@ -1,904 +0,0 @@ -Index: linux-2.6.21.7/drivers/net/imq.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/drivers/net/imq.c -@@ -0,0 +1,402 @@ -+/* -+ * Pseudo-driver for the intermediate queue device. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Authors: Patrick McHardy, -+ * -+ * The first version was written by Martin Devera, -+ * -+ * Credits: Jan Rafaj -+ * - Update patch to 2.4.21 -+ * Sebastian Strollo -+ * - Fix "Dead-loop on netdevice imq"-issue -+ * Marcel Sebek -+ * - Update to 2.6.2-rc1 -+ * -+ * After some time of inactivity there is a group taking care -+ * of IMQ again: http://www.linuximq.net -+ * -+ * -+ * 2004/06/30 - New version of IMQ patch to kernels <=2.6.7 including -+ * the following changes: -+ * -+ * - Correction of ipv6 support "+"s issue (Hasso Tepper) -+ * - Correction of imq_init_devs() issue that resulted in -+ * kernel OOPS unloading IMQ as module (Norbert Buchmuller) -+ * - Addition of functionality to choose number of IMQ devices -+ * during kernel config (Andre Correa) -+ * - Addition of functionality to choose how IMQ hooks on -+ * PRE and POSTROUTING (after or before NAT) (Andre Correa) -+ * - Cosmetic corrections (Norbert Buchmuller) (Andre Correa) -+ * -+ * -+ * 2005/12/16 - IMQ versions between 2.6.7 and 2.6.13 were -+ * released with almost no problems. 2.6.14-x was released -+ * with some important changes: nfcache was removed; After -+ * some weeks of trouble we figured out that some IMQ fields -+ * in skb were missing in skbuff.c - skb_clone and copy_skb_header. -+ * These functions are correctly patched by this new patch version. -+ * -+ * Thanks for all who helped to figure out all the problems with -+ * 2.6.14.x: Patrick McHardy, Rune Kock, VeNoMouS, Max CtRiX, -+ * Kevin Shanahan, Richard Lucassen, Valery Dachev (hopefully -+ * I didn't forget anybody). I apologize again for my lack of time. -+ * -+ * More info at: http://www.linuximq.net/ (Andre Correa) -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -+ #include -+#endif -+#include -+#include -+ -+extern int qdisc_restart1(struct net_device *dev); -+ -+static nf_hookfn imq_nf_hook; -+ -+static struct nf_hook_ops imq_ingress_ipv4 = { -+ .hook = imq_nf_hook, -+ .owner = THIS_MODULE, -+ .pf = PF_INET, -+ .hooknum = NF_IP_PRE_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ .priority = NF_IP_PRI_MANGLE + 1 -+#else -+ .priority = NF_IP_PRI_NAT_DST + 1 -+#endif -+}; -+ -+static struct nf_hook_ops imq_egress_ipv4 = { -+ .hook = imq_nf_hook, -+ .owner = THIS_MODULE, -+ .pf = PF_INET, -+ .hooknum = NF_IP_POST_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) -+ .priority = NF_IP_PRI_LAST -+#else -+ .priority = NF_IP_PRI_NAT_SRC - 1 -+#endif -+}; -+ -+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -+static struct nf_hook_ops imq_ingress_ipv6 = { -+ .hook = imq_nf_hook, -+ .owner = THIS_MODULE, -+ .pf = PF_INET6, -+ .hooknum = NF_IP6_PRE_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ .priority = NF_IP6_PRI_MANGLE + 1 -+#else -+ .priority = NF_IP6_PRI_NAT_DST + 1 -+#endif -+}; -+ -+static struct nf_hook_ops imq_egress_ipv6 = { -+ .hook = imq_nf_hook, -+ .owner = THIS_MODULE, -+ .pf = PF_INET6, -+ .hooknum = NF_IP6_POST_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) -+ .priority = NF_IP6_PRI_LAST -+#else -+ .priority = NF_IP6_PRI_NAT_SRC - 1 -+#endif -+}; -+#endif -+ -+#if defined(CONFIG_IMQ_NUM_DEVS) -+static unsigned int numdevs = CONFIG_IMQ_NUM_DEVS; -+#else -+static unsigned int numdevs = 2; -+#endif -+ -+static struct net_device *imq_devs; -+ -+static struct net_device_stats *imq_get_stats(struct net_device *dev) -+{ -+ return (struct net_device_stats *)dev->priv; -+} -+ -+/* called for packets kfree'd in qdiscs at places other than enqueue */ -+static void imq_skb_destructor(struct sk_buff *skb) -+{ -+ struct nf_info *info = skb->nf_info; -+ -+ if (info) { -+ if (info->indev) -+ dev_put(info->indev); -+ if (info->outdev) -+ dev_put(info->outdev); -+ kfree(info); -+ } -+} -+ -+static int imq_dev_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct net_device_stats *stats = (struct net_device_stats*) dev->priv; -+ -+ stats->tx_bytes += skb->len; -+ stats->tx_packets++; -+ -+ skb->imq_flags = 0; -+ skb->destructor = NULL; -+ -+ dev->trans_start = jiffies; -+ nf_reinject(skb, skb->nf_info, NF_ACCEPT); -+ return 0; -+} -+ -+static int imq_nf_queue(struct sk_buff *skb, struct nf_info *info, unsigned queue_num, void *data) -+{ -+ struct net_device *dev; -+ struct net_device_stats *stats; -+ struct sk_buff *skb2 = NULL; -+ struct Qdisc *q; -+ unsigned int index = skb->imq_flags&IMQ_F_IFMASK; -+ int ret = -1; -+ -+ if (index > numdevs) -+ return -1; -+ -+ dev = imq_devs + index; -+ if (!(dev->flags & IFF_UP)) { -+ skb->imq_flags = 0; -+ nf_reinject(skb, info, NF_ACCEPT); -+ return 0; -+ } -+ dev->last_rx = jiffies; -+ -+ if (skb->destructor) { -+ skb2 = skb; -+ skb = skb_clone(skb, GFP_ATOMIC); -+ if (!skb) -+ return -1; -+ } -+ skb->nf_info = info; -+ -+ stats = (struct net_device_stats *)dev->priv; -+ stats->rx_bytes+= skb->len; -+ stats->rx_packets++; -+ -+ spin_lock_bh(&dev->queue_lock); -+ q = dev->qdisc; -+ if (q->enqueue) { -+ q->enqueue(skb_get(skb), q); -+ if (skb_shared(skb)) { -+ skb->destructor = imq_skb_destructor; -+ kfree_skb(skb); -+ ret = 0; -+ } -+ } -+ if (spin_is_locked(&dev->_xmit_lock)) -+ netif_schedule(dev); -+ else -+ while (!netif_queue_stopped(dev) && qdisc_restart1(dev) < 0) -+ /* NOTHING */; -+ -+ spin_unlock_bh(&dev->queue_lock); -+ -+ if (skb2) -+ kfree_skb(ret ? skb : skb2); -+ -+ return ret; -+} -+ -+static struct nf_queue_handler nfqh = { -+ .name = "imq", -+ .outfn = imq_nf_queue, -+}; -+ -+static unsigned int imq_nf_hook(unsigned int hook, struct sk_buff **pskb, -+ const struct net_device *indev, -+ const struct net_device *outdev, -+ int (*okfn)(struct sk_buff *)) -+{ -+ if ((*pskb)->imq_flags & IMQ_F_ENQUEUE) -+ return NF_QUEUE; -+ -+ return NF_ACCEPT; -+} -+ -+ -+static int __init imq_init_hooks(void) -+{ -+ int err; -+ -+ err = nf_register_queue_handler(PF_INET, &nfqh); -+ if (err > 0) -+ goto err1; -+ if ((err = nf_register_hook(&imq_ingress_ipv4))) -+ goto err2; -+ if ((err = nf_register_hook(&imq_egress_ipv4))) -+ goto err3; -+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -+ if ((err = nf_register_queue_handler(PF_INET6, &nfqh))) -+ goto err4; -+ if ((err = nf_register_hook(&imq_ingress_ipv6))) -+ goto err5; -+ if ((err = nf_register_hook(&imq_egress_ipv6))) -+ goto err6; -+#endif -+ -+ return 0; -+ -+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -+err6: -+ nf_unregister_hook(&imq_ingress_ipv6); -+err5: -+ nf_unregister_queue_handler(PF_INET6); -+err4: -+ nf_unregister_hook(&imq_egress_ipv4); -+#endif -+err3: -+ nf_unregister_hook(&imq_ingress_ipv4); -+err2: -+ nf_unregister_queue_handler(PF_INET); -+err1: -+ return err; -+} -+ -+static void __exit imq_unhook(void) -+{ -+ nf_unregister_hook(&imq_ingress_ipv4); -+ nf_unregister_hook(&imq_egress_ipv4); -+ nf_unregister_queue_handler(PF_INET); -+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -+ nf_unregister_hook(&imq_ingress_ipv6); -+ nf_unregister_hook(&imq_egress_ipv6); -+ nf_unregister_queue_handler(PF_INET6); -+#endif -+} -+ -+static int __init imq_dev_init(struct net_device *dev) -+{ -+ dev->hard_start_xmit = imq_dev_xmit; -+ dev->type = ARPHRD_VOID; -+ dev->mtu = 1500; -+ dev->tx_queue_len = 30; -+ dev->flags = IFF_NOARP; -+ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); -+ if (dev->priv == NULL) -+ return -ENOMEM; -+ memset(dev->priv, 0, sizeof(struct net_device_stats)); -+ dev->get_stats = imq_get_stats; -+ -+ return 0; -+} -+ -+static void imq_dev_uninit(struct net_device *dev) -+{ -+ kfree(dev->priv); -+} -+ -+static int __init imq_init_devs(void) -+{ -+ struct net_device *dev; -+ int i,j; -+ j = numdevs; -+ -+ if (!numdevs || numdevs > IMQ_MAX_DEVS) { -+ printk(KERN_ERR "IMQ: numdevs has to be betweed 1 and %u\n", -+ IMQ_MAX_DEVS); -+ return -EINVAL; -+ } -+ -+ imq_devs = kmalloc(sizeof(struct net_device) * numdevs, GFP_KERNEL); -+ if (!imq_devs) -+ return -ENOMEM; -+ memset(imq_devs, 0, sizeof(struct net_device) * numdevs); -+ -+ /* we start counting at zero */ -+ numdevs--; -+ -+ for (i = 0, dev = imq_devs; i <= numdevs; i++, dev++) { -+ SET_MODULE_OWNER(dev); -+ strcpy(dev->name, "imq%d"); -+ dev->init = imq_dev_init; -+ dev->uninit = imq_dev_uninit; -+ -+ if (register_netdev(dev) < 0) -+ goto err_register; -+ } -+ printk(KERN_INFO "IMQ starting with %u devices...\n", j); -+ return 0; -+ -+err_register: -+ for (; i; i--) -+ unregister_netdev(--dev); -+ kfree(imq_devs); -+ return -EIO; -+} -+ -+static void imq_cleanup_devs(void) -+{ -+ int i; -+ struct net_device *dev = imq_devs; -+ -+ for (i = 0; i <= numdevs; i++) -+ unregister_netdev(dev++); -+ -+ kfree(imq_devs); -+} -+ -+static int __init imq_init_module(void) -+{ -+ int err; -+ -+ if ((err = imq_init_devs())) { -+ printk(KERN_ERR "IMQ: Error trying imq_init_devs()\n"); -+ return err; -+ } -+ if ((err = imq_init_hooks())) { -+ printk(KERN_ERR "IMQ: Error trying imq_init_hooks()\n"); -+ imq_cleanup_devs(); -+ return err; -+ } -+ -+ printk(KERN_INFO "IMQ driver loaded successfully.\n"); -+ -+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ printk(KERN_INFO "\tHooking IMQ before NAT on PREROUTING.\n"); -+#else -+ printk(KERN_INFO "\tHooking IMQ after NAT on PREROUTING.\n"); -+#endif -+#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ printk(KERN_INFO "\tHooking IMQ before NAT on POSTROUTING.\n"); -+#else -+ printk(KERN_INFO "\tHooking IMQ after NAT on POSTROUTING.\n"); -+#endif -+ -+ return 0; -+} -+ -+static void __exit imq_cleanup_module(void) -+{ -+ imq_unhook(); -+ imq_cleanup_devs(); -+ printk(KERN_INFO "IMQ driver unloaded successfully.\n"); -+} -+ -+ -+module_init(imq_init_module); -+module_exit(imq_cleanup_module); -+ -+module_param(numdevs, int, 0); -+MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)"); -+MODULE_AUTHOR("http://www.linuximq.net"); -+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.21.7/drivers/net/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/Kconfig -+++ linux-2.6.21.7/drivers/net/Kconfig -@@ -96,6 +96,129 @@ config EQUALIZER - To compile this driver as a module, choose M here: the module - will be called eql. If unsure, say N. - -+config IMQ -+ tristate "IMQ (intermediate queueing device) support" -+ depends on NETDEVICES && NETFILTER -+ ---help--- -+ The IMQ device(s) is used as placeholder for QoS queueing -+ disciplines. Every packet entering/leaving the IP stack can be -+ directed through the IMQ device where it's enqueued/dequeued to the -+ attached qdisc. This allows you to treat network devices as classes -+ and distribute bandwidth among them. Iptables is used to specify -+ through which IMQ device, if any, packets travel. -+ -+ More information at: http://www.linuximq.net/ -+ -+ To compile this driver as a module, choose M here: the module -+ will be called imq. If unsure, say N. -+ -+choice -+ prompt "IMQ behavior (PRE/POSTROUTING)" -+ depends on IMQ -+ default IMQ_BEHAVIOR_BA -+ help -+ -+ This settings defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ IMQ can work in any of the following ways: -+ -+ PREROUTING | POSTROUTING -+ -----------------|------------------- -+ #1 After NAT | After NAT -+ #2 After NAT | Before NAT -+ #3 Before NAT | After NAT -+ #4 Before NAT | Before NAT -+ -+ The default behavior is to hook before NAT on PREROUTING -+ and after NAT on POSTROUTING (#3). -+ -+ This settings are specially usefull when trying to use IMQ -+ to shape NATed clients. -+ -+ More information can be found at: www.linuximq.net -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_AA -+ bool "IMQ AA" -+ help -+ This settings defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: After NAT -+ POSTROUTING: After NAT -+ -+ More information can be found at: www.linuximq.net -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_AB -+ bool "IMQ AB" -+ help -+ This settings defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: After NAT -+ POSTROUTING: Before NAT -+ -+ More information can be found at: www.linuximq.net -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_BA -+ bool "IMQ BA" -+ help -+ This settings defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: Before NAT -+ POSTROUTING: After NAT -+ -+ More information can be found at: www.linuximq.net -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_BB -+ bool "IMQ BB" -+ help -+ This settings defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: Before NAT -+ POSTROUTING: Before NAT -+ -+ More information can be found at: www.linuximq.net -+ -+ If not sure leave the default settings alone. -+ -+endchoice -+ -+config IMQ_NUM_DEVS -+ -+ int "Number of IMQ devices" -+ range 2 8 -+ depends on IMQ -+ default "2" -+ help -+ -+ This settings defines how many IMQ devices will be -+ created. -+ -+ The default value is 2. -+ -+ More information can be found at: www.linuximq.net -+ -+ If not sure leave the default settings alone. -+ - config TUN - tristate "Universal TUN/TAP device driver support" - select CRC32 -Index: linux-2.6.21.7/drivers/net/Makefile -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/Makefile -+++ linux-2.6.21.7/drivers/net/Makefile -@@ -124,6 +124,7 @@ obj-$(CONFIG_SLIP) += slip.o - obj-$(CONFIG_SLHC) += slhc.o - - obj-$(CONFIG_DUMMY) += dummy.o -+obj-$(CONFIG_IMQ) += imq.o - obj-$(CONFIG_IFB) += ifb.o - obj-$(CONFIG_DE600) += de600.o - obj-$(CONFIG_DE620) += de620.o -Index: linux-2.6.21.7/include/linux/imq.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/imq.h -@@ -0,0 +1,9 @@ -+#ifndef _IMQ_H -+#define _IMQ_H -+ -+#define IMQ_MAX_DEVS 16 -+ -+#define IMQ_F_IFMASK 0x7f -+#define IMQ_F_ENQUEUE 0x80 -+ -+#endif /* _IMQ_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_IMQ.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_IMQ.h -@@ -0,0 +1,8 @@ -+#ifndef _IPT_IMQ_H -+#define _IPT_IMQ_H -+ -+struct ipt_imq_info { -+ unsigned int todev; /* target imq device */ -+}; -+ -+#endif /* _IPT_IMQ_H */ -Index: linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_IMQ.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_IMQ.h -@@ -0,0 +1,8 @@ -+#ifndef _IP6T_IMQ_H -+#define _IP6T_IMQ_H -+ -+struct ip6t_imq_info { -+ unsigned int todev; /* target imq device */ -+}; -+ -+#endif /* _IP6T_IMQ_H */ -Index: linux-2.6.21.7/include/linux/skbuff.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/skbuff.h -+++ linux-2.6.21.7/include/linux/skbuff.h -@@ -294,6 +294,10 @@ struct sk_buff { - #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) - struct sk_buff *nfct_reasm; - #endif -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ unsigned char imq_flags; -+ struct nf_info *nf_info; -+#endif - #ifdef CONFIG_BRIDGE_NETFILTER - struct nf_bridge_info *nf_bridge; - #endif -Index: linux-2.6.21.7/net/core/dev.c -=================================================================== ---- linux-2.6.21.7.orig/net/core/dev.c -+++ linux-2.6.21.7/net/core/dev.c -@@ -94,6 +94,9 @@ - #include - #include - #include -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+#include -+#endif - #include - #include - #include -@@ -1340,7 +1343,11 @@ static int dev_gso_segment(struct sk_buf - int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) - { - if (likely(!skb->next)) { -- if (netdev_nit) -+ if (netdev_nit -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ && !(skb->imq_flags & IMQ_F_ENQUEUE) -+#endif -+ ) - dev_queue_xmit_nit(skb, dev); - - if (netif_needs_gso(dev, skb)) { -Index: linux-2.6.21.7/net/core/skbuff.c -=================================================================== ---- linux-2.6.21.7.orig/net/core/skbuff.c -+++ linux-2.6.21.7/net/core/skbuff.c -@@ -430,6 +430,10 @@ struct sk_buff *skb_clone(struct sk_buff - C(nfct_reasm); - nf_conntrack_get_reasm(skb->nfct_reasm); - #endif -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ C(imq_flags); -+ C(nf_info); -+#endif /*CONFIG_IMQ*/ - #ifdef CONFIG_BRIDGE_NETFILTER - C(nf_bridge); - nf_bridge_get(skb->nf_bridge); -@@ -494,6 +498,10 @@ static void copy_skb_header(struct sk_bu - #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE) - new->ipvs_property = old->ipvs_property; - #endif -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ new->imq_flags = old->imq_flags; -+ new->nf_info = old->nf_info; -+#endif /*CONFIG_IMQ*/ - #ifdef CONFIG_BRIDGE_NETFILTER - new->nf_bridge = old->nf_bridge; - nf_bridge_get(old->nf_bridge); -Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_IMQ.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ipt_IMQ.c -@@ -0,0 +1,69 @@ -+/* -+ * This target marks packets to be enqueued to an imq device -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+static unsigned int imq_target(struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ unsigned int hooknum, -+ const struct xt_target *target, -+ const void *targinfo) -+{ -+ struct ipt_imq_info *mr = (struct ipt_imq_info*)targinfo; -+ -+ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; -+ -+ return XT_CONTINUE; -+} -+ -+static int imq_checkentry(const char *tablename, -+ const void *e, -+ const struct xt_target *target, -+ void *targinfo, -+ unsigned int hook_mask) -+{ -+ struct ipt_imq_info *mr; -+ -+ mr = (struct ipt_imq_info*)targinfo; -+ -+ if (mr->todev > IMQ_MAX_DEVS) { -+ printk(KERN_WARNING -+ "IMQ: invalid device specified, highest is %u\n", -+ IMQ_MAX_DEVS); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+static struct xt_target ipt_imq_reg = { -+ .name = "IMQ", -+ .family = AF_INET, -+ .target = imq_target, -+ .targetsize = sizeof(struct ipt_imq_info), -+ .checkentry = imq_checkentry, -+ .me = THIS_MODULE, -+ .table = "mangle" -+}; -+ -+static int __init init(void) -+{ -+ return xt_register_target(&ipt_imq_reg); -+} -+ -+static void __exit fini(void) -+{ -+ xt_unregister_target(&ipt_imq_reg); -+} -+ -+module_init(init); -+module_exit(fini); -+ -+MODULE_AUTHOR("http://www.linuximq.net"); -+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig -+++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig -@@ -587,6 +587,17 @@ config IP_NF_MANGLE - - To compile it as a module, choose M here. If unsure, say N. - -+config IP_NF_TARGET_IMQ -+ tristate "IMQ target support" -+ depends on IP_NF_MANGLE -+ help -+ This option adds a `IMQ' target which is used to specify if and -+ to which IMQ device packets should get enqueued/dequeued. -+ -+ For more information visit: http://www.linuximq.net/ -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ - config IP_NF_TARGET_TOS - tristate "TOS target support" - depends on IP_NF_MANGLE -Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile -+++ linux-2.6.21.7/net/ipv4/netfilter/Makefile -@@ -99,6 +99,7 @@ obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_i - obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o - obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o - obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o -+obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o - obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o - obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o - obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o -Index: linux-2.6.21.7/net/ipv6/netfilter/ip6t_IMQ.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv6/netfilter/ip6t_IMQ.c -@@ -0,0 +1,69 @@ -+/* -+ * This target marks packets to be enqueued to an imq device -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+static unsigned int imq_target(struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ unsigned int hooknum, -+ const struct xt_target *target, -+ const void *targinfo) -+{ -+ struct ip6t_imq_info *mr = (struct ip6t_imq_info*)targinfo; -+ -+ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; -+ -+ return XT_CONTINUE; -+} -+ -+static int imq_checkentry(const char *tablename, -+ const void *entry, -+ const struct xt_target *target, -+ void *targinfo, -+ unsigned int hook_mask) -+{ -+ struct ip6t_imq_info *mr; -+ -+ mr = (struct ip6t_imq_info*)targinfo; -+ -+ if (mr->todev > IMQ_MAX_DEVS) { -+ printk(KERN_WARNING -+ "IMQ: invalid device specified, highest is %u\n", -+ IMQ_MAX_DEVS); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+static struct xt_target ip6t_imq_reg = { -+ .name = "IMQ", -+ .family = AF_INET6, -+ .target = imq_target, -+ .targetsize = sizeof(struct ip6t_imq_info), -+ .table = "mangle", -+ .checkentry = imq_checkentry, -+ .me = THIS_MODULE -+}; -+ -+static int __init init(void) -+{ -+ return xt_register_target(&ip6t_imq_reg); -+} -+ -+static void __exit fini(void) -+{ -+ xt_unregister_target(&ip6t_imq_reg); -+} -+ -+module_init(init); -+module_exit(fini); -+ -+MODULE_AUTHOR("http://www.linuximq.net"); -+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.21.7/net/ipv6/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv6/netfilter/Kconfig -+++ linux-2.6.21.7/net/ipv6/netfilter/Kconfig -@@ -173,6 +173,15 @@ config IP6_NF_MANGLE - - To compile it as a module, choose M here. If unsure, say N. - -+config IP6_NF_TARGET_IMQ -+ tristate "IMQ target support" -+ depends on IP6_NF_MANGLE -+ help -+ This option adds a `IMQ' target which is used to specify if and -+ to which imq device packets should get enqueued/dequeued. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ - config IP6_NF_TARGET_HL - tristate 'HL (hoplimit) target support' - depends on IP6_NF_MANGLE -Index: linux-2.6.21.7/net/ipv6/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/ipv6/netfilter/Makefile -+++ linux-2.6.21.7/net/ipv6/netfilter/Makefile -@@ -13,6 +13,7 @@ obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t - obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o - obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o - obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o -+obj-$(CONFIG_IP6_NF_TARGET_IMQ) += ip6t_IMQ.o - obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o - obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o - obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o -Index: linux-2.6.21.7/net/sched/sch_generic.c -=================================================================== ---- linux-2.6.21.7.orig/net/sched/sch_generic.c -+++ linux-2.6.21.7/net/sched/sch_generic.c -@@ -87,7 +87,6 @@ void qdisc_unlock_tree(struct net_device - - NOTE: Called under dev->queue_lock with locally disabled BH. - */ -- - static inline int qdisc_restart(struct net_device *dev) - { - struct Qdisc *q = dev->qdisc; -@@ -181,6 +180,11 @@ requeue: - return q->q.qlen; - } - -+int qdisc_restart1(struct net_device *dev) -+{ -+ return qdisc_restart(dev); -+} -+ - void __qdisc_run(struct net_device *dev) - { - if (unlikely(dev->qdisc == &noop_qdisc)) -@@ -617,3 +621,4 @@ EXPORT_SYMBOL(qdisc_destroy); - EXPORT_SYMBOL(qdisc_reset); - EXPORT_SYMBOL(qdisc_lock_tree); - EXPORT_SYMBOL(qdisc_unlock_tree); -+EXPORT_SYMBOL(qdisc_restart1); diff --git a/target/linux/generic-2.6/patches/160-netfilter_route.patch b/target/linux/generic-2.6/patches/160-netfilter_route.patch deleted file mode 100644 index 9bd7d2a416..0000000000 --- a/target/linux/generic-2.6/patches/160-netfilter_route.patch +++ /dev/null @@ -1,965 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ROUTE.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ipt_ROUTE.h -@@ -0,0 +1,23 @@ -+/* Header file for iptables ipt_ROUTE target -+ * -+ * (C) 2002 by Cédric de Launois -+ * -+ * This software is distributed under GNU GPL v2, 1991 -+ */ -+#ifndef _IPT_ROUTE_H_target -+#define _IPT_ROUTE_H_target -+ -+#define IPT_ROUTE_IFNAMSIZ 16 -+ -+struct ipt_route_target_info { -+ char oif[IPT_ROUTE_IFNAMSIZ]; /* Output Interface Name */ -+ char iif[IPT_ROUTE_IFNAMSIZ]; /* Input Interface Name */ -+ u_int32_t gw; /* IP address of gateway */ -+ u_int8_t flags; -+}; -+ -+/* Values for "flags" field */ -+#define IPT_ROUTE_CONTINUE 0x01 -+#define IPT_ROUTE_TEE 0x02 -+ -+#endif /*_IPT_ROUTE_H_target*/ -Index: linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_ROUTE.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter_ipv6/ip6t_ROUTE.h -@@ -0,0 +1,23 @@ -+/* Header file for iptables ip6t_ROUTE target -+ * -+ * (C) 2003 by Cédric de Launois -+ * -+ * This software is distributed under GNU GPL v2, 1991 -+ */ -+#ifndef _IPT_ROUTE_H_target -+#define _IPT_ROUTE_H_target -+ -+#define IP6T_ROUTE_IFNAMSIZ 16 -+ -+struct ip6t_route_target_info { -+ char oif[IP6T_ROUTE_IFNAMSIZ]; /* Output Interface Name */ -+ char iif[IP6T_ROUTE_IFNAMSIZ]; /* Input Interface Name */ -+ u_int32_t gw[4]; /* IPv6 address of gateway */ -+ u_int8_t flags; -+}; -+ -+/* Values for "flags" field */ -+#define IP6T_ROUTE_CONTINUE 0x01 -+#define IP6T_ROUTE_TEE 0x02 -+ -+#endif /*_IP6T_ROUTE_H_target*/ -Index: linux-2.6.21.7/net/ipv4/netfilter/ipt_ROUTE.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv4/netfilter/ipt_ROUTE.c -@@ -0,0 +1,483 @@ -+/* -+ * This implements the ROUTE target, which enables you to setup unusual -+ * routes not supported by the standard kernel routing table. -+ * -+ * Copyright (C) 2002 Cedric de Launois -+ * -+ * v 1.11 2004/11/23 -+ * -+ * This software is distributed under GNU GPL v2, 1991 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if 0 -+#define DEBUGP printk -+#else -+#define DEBUGP(format, args...) -+#endif -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Cedric de Launois "); -+MODULE_DESCRIPTION("iptables ROUTE target module"); -+ -+/* Try to route the packet according to the routing keys specified in -+ * route_info. Keys are : -+ * - ifindex : -+ * 0 if no oif preferred, -+ * otherwise set to the index of the desired oif -+ * - route_info->gw : -+ * 0 if no gateway specified, -+ * otherwise set to the next host to which the pkt must be routed -+ * If success, skb->dev is the output device to which the packet must -+ * be sent and skb->dst is not NULL -+ * -+ * RETURN: -1 if an error occured -+ * 1 if the packet was succesfully routed to the -+ * destination desired -+ * 0 if the kernel routing table could not route the packet -+ * according to the keys specified -+ */ -+static int route(struct sk_buff *skb, -+ unsigned int ifindex, -+ const struct ipt_route_target_info *route_info) -+{ -+ int err; -+ struct rtable *rt; -+ struct iphdr *iph = skb->nh.iph; -+ struct flowi fl = { -+ .oif = ifindex, -+ .nl_u = { -+ .ip4_u = { -+ .daddr = iph->daddr, -+ .saddr = 0, -+ .tos = RT_TOS(iph->tos), -+ .scope = RT_SCOPE_UNIVERSE, -+ } -+ } -+ }; -+ -+ /* The destination address may be overloaded by the target */ -+ if (route_info->gw) -+ fl.fl4_dst = route_info->gw; -+ -+ /* Trying to route the packet using the standard routing table. */ -+ if ((err = ip_route_output_key(&rt, &fl))) { -+ if (net_ratelimit()) -+ DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err); -+ return -1; -+ } -+ -+ /* Drop old route. */ -+ dst_release(skb->dst); -+ skb->dst = NULL; -+ -+ /* Success if no oif specified or if the oif correspond to the -+ * one desired */ -+ if (!ifindex || rt->u.dst.dev->ifindex == ifindex) { -+ skb->dst = &rt->u.dst; -+ skb->dev = skb->dst->dev; -+ skb->protocol = htons(ETH_P_IP); -+ return 1; -+ } -+ -+ /* The interface selected by the routing table is not the one -+ * specified by the user. This may happen because the dst address -+ * is one of our own addresses. -+ */ -+ if (net_ratelimit()) -+ DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n", -+ NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex); -+ -+ return 0; -+} -+ -+ -+/* Stolen from ip_finish_output2 -+ * PRE : skb->dev is set to the device we are leaving by -+ * skb->dst is not NULL -+ * POST: the packet is sent with the link layer header pushed -+ * the packet is destroyed -+ */ -+static void ip_direct_send(struct sk_buff *skb) -+{ -+ struct dst_entry *dst = skb->dst; -+ struct hh_cache *hh = dst->hh; -+ struct net_device *dev = dst->dev; -+ int hh_len = LL_RESERVED_SPACE(dev); -+ -+ /* Be paranoid, rather than too clever. */ -+ if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) { -+ struct sk_buff *skb2; -+ -+ skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev)); -+ if (skb2 == NULL) { -+ kfree_skb(skb); -+ return; -+ } -+ if (skb->sk) -+ skb_set_owner_w(skb2, skb->sk); -+ kfree_skb(skb); -+ skb = skb2; -+ } -+ -+ if (hh) { -+ int hh_alen; -+ -+ read_lock_bh(&hh->hh_lock); -+ hh_alen = HH_DATA_ALIGN(hh->hh_len); -+ memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); -+ read_unlock_bh(&hh->hh_lock); -+ skb_push(skb, hh->hh_len); -+ hh->hh_output(skb); -+ } else if (dst->neighbour) -+ dst->neighbour->output(skb); -+ else { -+ if (net_ratelimit()) -+ DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n"); -+ kfree_skb(skb); -+ } -+} -+ -+ -+/* PRE : skb->dev is set to the device we are leaving by -+ * POST: - the packet is directly sent to the skb->dev device, without -+ * pushing the link layer header. -+ * - the packet is destroyed -+ */ -+static inline int dev_direct_send(struct sk_buff *skb) -+{ -+ return dev_queue_xmit(skb); -+} -+ -+ -+static unsigned int route_oif(const struct ipt_route_target_info *route_info, -+ struct sk_buff *skb) -+{ -+ unsigned int ifindex = 0; -+ struct net_device *dev_out = NULL; -+ -+ /* The user set the interface name to use. -+ * Getting the current interface index. -+ */ -+ if ((dev_out = dev_get_by_name(route_info->oif))) { -+ ifindex = dev_out->ifindex; -+ } else { -+ /* Unknown interface name : packet dropped */ -+ if (net_ratelimit()) -+ DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif); -+ return NF_DROP; -+ } -+ -+ /* Trying the standard way of routing packets */ -+ switch (route(skb, ifindex, route_info)) { -+ case 1: -+ dev_put(dev_out); -+ if (route_info->flags & IPT_ROUTE_CONTINUE) -+ return IPT_CONTINUE; -+ -+ ip_direct_send(skb); -+ return NF_STOLEN; -+ -+ case 0: -+ /* Failed to send to oif. Trying the hard way */ -+ if (route_info->flags & IPT_ROUTE_CONTINUE) -+ return NF_DROP; -+ -+ if (net_ratelimit()) -+ DEBUGP("ipt_ROUTE: forcing the use of %i\n", -+ ifindex); -+ -+ /* We have to force the use of an interface. -+ * This interface must be a tunnel interface since -+ * otherwise we can't guess the hw address for -+ * the packet. For a tunnel interface, no hw address -+ * is needed. -+ */ -+ if ((dev_out->type != ARPHRD_TUNNEL) -+ && (dev_out->type != ARPHRD_IPGRE)) { -+ if (net_ratelimit()) -+ DEBUGP("ipt_ROUTE: can't guess the hw addr !\n"); -+ dev_put(dev_out); -+ return NF_DROP; -+ } -+ -+ /* Send the packet. This will also free skb -+ * Do not go through the POST_ROUTING hook because -+ * skb->dst is not set and because it will probably -+ * get confused by the destination IP address. -+ */ -+ skb->dev = dev_out; -+ dev_direct_send(skb); -+ dev_put(dev_out); -+ return NF_STOLEN; -+ -+ default: -+ /* Unexpected error */ -+ dev_put(dev_out); -+ return NF_DROP; -+ } -+} -+ -+ -+static unsigned int route_iif(const struct ipt_route_target_info *route_info, -+ struct sk_buff *skb) -+{ -+ struct net_device *dev_in = NULL; -+ -+ /* Getting the current interface index. */ -+ if (!(dev_in = dev_get_by_name(route_info->iif))) { -+ if (net_ratelimit()) -+ DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->iif); -+ return NF_DROP; -+ } -+ -+ skb->dev = dev_in; -+ dst_release(skb->dst); -+ skb->dst = NULL; -+ -+ netif_rx(skb); -+ dev_put(dev_in); -+ return NF_STOLEN; -+} -+ -+ -+static unsigned int route_gw(const struct ipt_route_target_info *route_info, -+ struct sk_buff *skb) -+{ -+ if (route(skb, 0, route_info)!=1) -+ return NF_DROP; -+ -+ if (route_info->flags & IPT_ROUTE_CONTINUE) -+ return IPT_CONTINUE; -+ -+ ip_direct_send(skb); -+ return NF_STOLEN; -+} -+ -+ -+/* To detect and deter routed packet loopback when using the --tee option, -+ * we take a page out of the raw.patch book: on the copied skb, we set up -+ * a fake ->nfct entry, pointing to the local &route_tee_track. We skip -+ * routing packets when we see they already have that ->nfct. -+ */ -+ -+static struct ip_conntrack route_tee_track; -+ -+static unsigned int ipt_route_target(struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ unsigned int hooknum, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ const void *targinfo, -+ void *userinfo) -+#else -+ const void *targinfo) -+#endif -+{ -+ const struct ipt_route_target_info *route_info = targinfo; -+ struct sk_buff *skb = *pskb; -+ unsigned int res; -+ -+ if (skb->nfct == &route_tee_track.ct_general) { -+ /* Loopback - a packet we already routed, is to be -+ * routed another time. Avoid that, now. -+ */ -+ if (net_ratelimit()) -+ DEBUGP(KERN_DEBUG "ipt_ROUTE: loopback - DROP!\n"); -+ return NF_DROP; -+ } -+ -+ /* If we are at PREROUTING or INPUT hook -+ * the TTL isn't decreased by the IP stack -+ */ -+ if (hooknum == NF_IP_PRE_ROUTING || -+ hooknum == NF_IP_LOCAL_IN) { -+ -+ struct iphdr *iph = skb->nh.iph; -+ -+ if (iph->ttl <= 1) { -+ struct rtable *rt; -+ struct flowi fl = { -+ .oif = 0, -+ .nl_u = { -+ .ip4_u = { -+ .daddr = iph->daddr, -+ .saddr = iph->saddr, -+ .tos = RT_TOS(iph->tos), -+ .scope = ((iph->tos & RTO_ONLINK) ? -+ RT_SCOPE_LINK : -+ RT_SCOPE_UNIVERSE) -+ } -+ } -+ }; -+ -+ if (ip_route_output_key(&rt, &fl)) { -+ return NF_DROP; -+ } -+ -+ if (skb->dev == rt->u.dst.dev) { -+ /* Drop old route. */ -+ dst_release(skb->dst); -+ skb->dst = &rt->u.dst; -+ -+ /* this will traverse normal stack, and -+ * thus call conntrack on the icmp packet */ -+ icmp_send(skb, ICMP_TIME_EXCEEDED, -+ ICMP_EXC_TTL, 0); -+ } -+ -+ return NF_DROP; -+ } -+ -+ /* -+ * If we are at INPUT the checksum must be recalculated since -+ * the length could change as the result of a defragmentation. -+ */ -+ if(hooknum == NF_IP_LOCAL_IN) { -+ iph->ttl = iph->ttl - 1; -+ iph->check = 0; -+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); -+ } else { -+ ip_decrease_ttl(iph); -+ } -+ } -+ -+ if ((route_info->flags & IPT_ROUTE_TEE)) { -+ /* -+ * Copy the *pskb, and route the copy. Will later return -+ * IPT_CONTINUE for the original skb, which should continue -+ * on its way as if nothing happened. The copy should be -+ * independantly delivered to the ROUTE --gw. -+ */ -+ skb = skb_copy(*pskb, GFP_ATOMIC); -+ if (!skb) { -+ if (net_ratelimit()) -+ DEBUGP(KERN_DEBUG "ipt_ROUTE: copy failed!\n"); -+ return IPT_CONTINUE; -+ } -+ } -+ -+ /* Tell conntrack to forget this packet since it may get confused -+ * when a packet is leaving with dst address == our address. -+ * Good idea ? Dunno. Need advice. -+ * -+ * NEW: mark the skb with our &route_tee_track, so we avoid looping -+ * on any already routed packet. -+ */ -+ if (!(route_info->flags & IPT_ROUTE_CONTINUE)) { -+ nf_conntrack_put(skb->nfct); -+ skb->nfct = &route_tee_track.ct_general; -+ skb->nfctinfo = IP_CT_NEW; -+ nf_conntrack_get(skb->nfct); -+ } -+ -+ if (route_info->oif[0] != '\0') { -+ res = route_oif(route_info, skb); -+ } else if (route_info->iif[0] != '\0') { -+ res = route_iif(route_info, skb); -+ } else if (route_info->gw) { -+ res = route_gw(route_info, skb); -+ } else { -+ if (net_ratelimit()) -+ DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n"); -+ res = IPT_CONTINUE; -+ } -+ -+ if ((route_info->flags & IPT_ROUTE_TEE)) -+ res = IPT_CONTINUE; -+ -+ return res; -+} -+ -+ -+static int ipt_route_checkentry(const char *tablename, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ const void *e, -+#else -+ const struct ipt_ip *ip, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+ void *targinfo, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ unsigned int targinfosize, -+#endif -+ unsigned int hook_mask) -+{ -+ if (strcmp(tablename, "mangle") != 0) { -+ printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n", -+ tablename); -+ return 0; -+ } -+ -+ if (hook_mask & ~( (1 << NF_IP_PRE_ROUTING) -+ | (1 << NF_IP_LOCAL_IN) -+ | (1 << NF_IP_FORWARD) -+ | (1 << NF_IP_LOCAL_OUT) -+ | (1 << NF_IP_POST_ROUTING))) { -+ printk("ipt_ROUTE: bad hook\n"); -+ return 0; -+ } -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_route_target_info))) { -+ printk(KERN_WARNING "ipt_ROUTE: targinfosize %u != %Zu\n", -+ targinfosize, -+ IPT_ALIGN(sizeof(struct ipt_route_target_info))); -+ return 0; -+ } -+#endif -+ -+ return 1; -+} -+ -+ -+static struct ipt_target ipt_route_reg = { -+ .name = "ROUTE", -+ .target = ipt_route_target, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ .targetsize = sizeof(struct ipt_route_target_info), -+#endif -+ .checkentry = ipt_route_checkentry, -+ .me = THIS_MODULE, -+}; -+ -+static int __init init(void) -+{ -+ /* Set up fake conntrack (stolen from raw.patch): -+ - to never be deleted, not in any hashes */ -+ atomic_set(&route_tee_track.ct_general.use, 1); -+ /* - and look it like as a confirmed connection */ -+ set_bit(IPS_CONFIRMED_BIT, &route_tee_track.status); -+ /* Initialize fake conntrack so that NAT will skip it */ -+ route_tee_track.status |= IPS_NAT_DONE_MASK; -+ -+ return xt_register_target(&ipt_route_reg); -+} -+ -+ -+static void __exit fini(void) -+{ -+ xt_unregister_target(&ipt_route_reg); -+} -+ -+module_init(init); -+module_exit(fini); -Index: linux-2.6.21.7/net/ipv4/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Kconfig -+++ linux-2.6.21.7/net/ipv4/netfilter/Kconfig -@@ -807,5 +807,22 @@ config IP_NF_TARGET_SET - To compile it as a module, choose M here. If unsure, say N. - - -+config IP_NF_TARGET_ROUTE -+ tristate 'ROUTE target support' -+ depends on IP_NF_MANGLE -+ help -+ This option adds a `ROUTE' target, which enables you to setup unusual -+ routes. For example, the ROUTE lets you route a received packet through -+ an interface or towards a host, even if the regular destination of the -+ packet is the router itself. The ROUTE target is also able to change the -+ incoming interface of a packet. -+ -+ The target can be or not a final target. It has to be used inside the -+ mangle table. -+ -+ If you want to compile it as a module, say M here and read -+ Documentation/modules.txt. The module will be called ipt_ROUTE.o. -+ If unsure, say `N'. -+ - endmenu - -Index: linux-2.6.21.7/net/ipv4/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/netfilter/Makefile -+++ linux-2.6.21.7/net/ipv4/netfilter/Makefile -@@ -102,6 +102,7 @@ obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_EC - obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o - obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o - obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o -+obj-$(CONFIG_IP_NF_TARGET_ROUTE) += ipt_ROUTE.o - obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o - obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o - obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o -Index: linux-2.6.21.7/net/ipv6/ipv6_syms.c -=================================================================== ---- linux-2.6.21.7.orig/net/ipv6/ipv6_syms.c -+++ linux-2.6.21.7/net/ipv6/ipv6_syms.c -@@ -10,6 +10,7 @@ EXPORT_SYMBOL(icmpv6_send); - EXPORT_SYMBOL(icmpv6_statistics); - EXPORT_SYMBOL(icmpv6_err_convert); - EXPORT_SYMBOL(ndisc_mc_map); -+EXPORT_SYMBOL(nd_tbl); - EXPORT_SYMBOL(register_inet6addr_notifier); - EXPORT_SYMBOL(unregister_inet6addr_notifier); - EXPORT_SYMBOL(ip6_route_output); -Index: linux-2.6.21.7/net/ipv6/netfilter/ip6t_ROUTE.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/ipv6/netfilter/ip6t_ROUTE.c -@@ -0,0 +1,330 @@ -+/* -+ * This implements the ROUTE v6 target, which enables you to setup unusual -+ * routes not supported by the standard kernel routing table. -+ * -+ * Copyright (C) 2003 Cedric de Launois -+ * -+ * v 1.1 2004/11/23 -+ * -+ * This software is distributed under GNU GPL v2, 1991 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if 1 -+#define DEBUGP printk -+#else -+#define DEBUGP(format, args...) -+#endif -+ -+#define NIP6(addr) \ -+ ntohs((addr).s6_addr16[0]), \ -+ ntohs((addr).s6_addr16[1]), \ -+ ntohs((addr).s6_addr16[2]), \ -+ ntohs((addr).s6_addr16[3]), \ -+ ntohs((addr).s6_addr16[4]), \ -+ ntohs((addr).s6_addr16[5]), \ -+ ntohs((addr).s6_addr16[6]), \ -+ ntohs((addr).s6_addr16[7]) -+ -+/* Route the packet according to the routing keys specified in -+ * route_info. Keys are : -+ * - ifindex : -+ * 0 if no oif preferred, -+ * otherwise set to the index of the desired oif -+ * - route_info->gw : -+ * 0 if no gateway specified, -+ * otherwise set to the next host to which the pkt must be routed -+ * If success, skb->dev is the output device to which the packet must -+ * be sent and skb->dst is not NULL -+ * -+ * RETURN: 1 if the packet was succesfully routed to the -+ * destination desired -+ * 0 if the kernel routing table could not route the packet -+ * according to the keys specified -+ */ -+static int -+route6(struct sk_buff *skb, -+ unsigned int ifindex, -+ const struct ip6t_route_target_info *route_info) -+{ -+ struct rt6_info *rt = NULL; -+ struct ipv6hdr *ipv6h = skb->nh.ipv6h; -+ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; -+ -+ DEBUGP("ip6t_ROUTE: called with: "); -+ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); -+ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw)); -+ DEBUGP("OUT=%s\n", route_info->oif); -+ -+ if (ipv6_addr_any(gw)) -+ rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1); -+ else -+ rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1); -+ -+ if (!rt) -+ goto no_route; -+ -+ DEBUGP("ip6t_ROUTE: routing gives: "); -+ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr)); -+ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway)); -+ DEBUGP("OUT=%s\n", rt->rt6i_dev->name); -+ -+ if (ifindex && rt->rt6i_dev->ifindex!=ifindex) -+ goto wrong_route; -+ -+ if (!rt->rt6i_nexthop) { -+ DEBUGP("ip6t_ROUTE: discovering neighbour\n"); -+ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr); -+ } -+ -+ /* Drop old route. */ -+ dst_release(skb->dst); -+ skb->dst = &rt->u.dst; -+ skb->dev = rt->rt6i_dev; -+ return 1; -+ -+ wrong_route: -+ dst_release(&rt->u.dst); -+ no_route: -+ if (!net_ratelimit()) -+ return 0; -+ -+ printk("ip6t_ROUTE: no explicit route found "); -+ if (ifindex) -+ printk("via interface %s ", route_info->oif); -+ if (!ipv6_addr_any(gw)) -+ printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw)); -+ printk("\n"); -+ return 0; -+} -+ -+ -+/* Stolen from ip6_output_finish -+ * PRE : skb->dev is set to the device we are leaving by -+ * skb->dst is not NULL -+ * POST: the packet is sent with the link layer header pushed -+ * the packet is destroyed -+ */ -+static void ip_direct_send(struct sk_buff *skb) -+{ -+ struct dst_entry *dst = skb->dst; -+ struct hh_cache *hh = dst->hh; -+ -+ if (hh) { -+ read_lock_bh(&hh->hh_lock); -+ memcpy(skb->data - 16, hh->hh_data, 16); -+ read_unlock_bh(&hh->hh_lock); -+ skb_push(skb, hh->hh_len); -+ hh->hh_output(skb); -+ } else if (dst->neighbour) -+ dst->neighbour->output(skb); -+ else { -+ if (net_ratelimit()) -+ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n"); -+ kfree_skb(skb); -+ } -+} -+ -+ -+static unsigned int -+route6_oif(const struct ip6t_route_target_info *route_info, -+ struct sk_buff *skb) -+{ -+ unsigned int ifindex = 0; -+ struct net_device *dev_out = NULL; -+ -+ /* The user set the interface name to use. -+ * Getting the current interface index. -+ */ -+ if ((dev_out = dev_get_by_name(route_info->oif))) { -+ ifindex = dev_out->ifindex; -+ } else { -+ /* Unknown interface name : packet dropped */ -+ if (net_ratelimit()) -+ DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); -+ -+ if (route_info->flags & IP6T_ROUTE_CONTINUE) -+ return IP6T_CONTINUE; -+ else -+ return NF_DROP; -+ } -+ -+ /* Trying the standard way of routing packets */ -+ if (route6(skb, ifindex, route_info)) { -+ dev_put(dev_out); -+ if (route_info->flags & IP6T_ROUTE_CONTINUE) -+ return IP6T_CONTINUE; -+ -+ ip_direct_send(skb); -+ return NF_STOLEN; -+ } else -+ return NF_DROP; -+} -+ -+ -+static unsigned int -+route6_gw(const struct ip6t_route_target_info *route_info, -+ struct sk_buff *skb) -+{ -+ if (route6(skb, 0, route_info)) { -+ if (route_info->flags & IP6T_ROUTE_CONTINUE) -+ return IP6T_CONTINUE; -+ -+ ip_direct_send(skb); -+ return NF_STOLEN; -+ } else -+ return NF_DROP; -+} -+ -+ -+static unsigned int -+ip6t_route_target(struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ unsigned int hooknum, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ const void *targinfo, -+ void *userinfo) -+#else -+ const void *targinfo) -+#endif -+{ -+ const struct ip6t_route_target_info *route_info = targinfo; -+ struct sk_buff *skb = *pskb; -+ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; -+ unsigned int res; -+ -+ if (route_info->flags & IP6T_ROUTE_CONTINUE) -+ goto do_it; -+ -+ /* If we are at PREROUTING or INPUT hook -+ * the TTL isn't decreased by the IP stack -+ */ -+ if (hooknum == NF_IP6_PRE_ROUTING || -+ hooknum == NF_IP6_LOCAL_IN) { -+ -+ struct ipv6hdr *ipv6h = skb->nh.ipv6h; -+ -+ if (ipv6h->hop_limit <= 1) { -+ /* Force OUTPUT device used as source address */ -+ skb->dev = skb->dst->dev; -+ -+ icmpv6_send(skb, ICMPV6_TIME_EXCEED, -+ ICMPV6_EXC_HOPLIMIT, 0, skb->dev); -+ -+ return NF_DROP; -+ } -+ -+ ipv6h->hop_limit--; -+ } -+ -+ if ((route_info->flags & IP6T_ROUTE_TEE)) { -+ /* -+ * Copy the *pskb, and route the copy. Will later return -+ * IP6T_CONTINUE for the original skb, which should continue -+ * on its way as if nothing happened. The copy should be -+ * independantly delivered to the ROUTE --gw. -+ */ -+ skb = skb_copy(*pskb, GFP_ATOMIC); -+ if (!skb) { -+ if (net_ratelimit()) -+ DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n"); -+ return IP6T_CONTINUE; -+ } -+ } -+ -+do_it: -+ if (route_info->oif[0]) { -+ res = route6_oif(route_info, skb); -+ } else if (!ipv6_addr_any(gw)) { -+ res = route6_gw(route_info, skb); -+ } else { -+ if (net_ratelimit()) -+ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); -+ res = IP6T_CONTINUE; -+ } -+ -+ if ((route_info->flags & IP6T_ROUTE_TEE)) -+ res = IP6T_CONTINUE; -+ -+ return res; -+} -+ -+ -+static int -+ip6t_route_checkentry(const char *tablename, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ const void *entry, -+#else -+ const struct ip6t_entry *entry -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+ void *targinfo, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ unsigned int targinfosize, -+#endif -+ unsigned int hook_mask) -+{ -+ if (strcmp(tablename, "mangle") != 0) { -+ printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); -+ return 0; -+ } -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { -+ printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", -+ targinfosize, -+ IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); -+ return 0; -+ } -+#endif -+ -+ return 1; -+} -+ -+ -+static struct ip6t_target ip6t_route_reg = { -+ .name = "ROUTE", -+ .target = ip6t_route_target, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ .targetsize = sizeof(struct ip6t_route_target_info), -+#endif -+ .checkentry = ip6t_route_checkentry, -+ .me = THIS_MODULE -+}; -+ -+ -+static int __init init(void) -+{ -+ printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); -+ if (xt_register_target(&ip6t_route_reg)) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+ -+static void __exit fini(void) -+{ -+ xt_unregister_target(&ip6t_route_reg); -+} -+ -+module_init(init); -+module_exit(fini); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.21.7/net/ipv6/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/ipv6/netfilter/Kconfig -+++ linux-2.6.21.7/net/ipv6/netfilter/Kconfig -@@ -209,5 +209,18 @@ config IP6_NF_RAW - If you want to compile it as a module, say M here and read - . If unsure, say `N'. - -+config IP6_NF_TARGET_ROUTE -+ tristate 'ROUTE target support' -+ depends on IP6_NF_MANGLE -+ help -+ This option adds a `ROUTE' target, which enables you to setup unusual -+ routes. The ROUTE target is also able to change the incoming interface -+ of a packet. -+ -+ The target can be or not a final target. It has to be used inside the -+ mangle table. -+ -+ Not working as a module. -+ - endmenu - -Index: linux-2.6.21.7/net/ipv6/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/ipv6/netfilter/Makefile -+++ linux-2.6.21.7/net/ipv6/netfilter/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_ - obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o - obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o - obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o -+obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o - obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o - - # objects for l3 independent conntrack diff --git a/target/linux/generic-2.6/patches/170-netfilter_chaostables_0.8.patch b/target/linux/generic-2.6/patches/170-netfilter_chaostables_0.8.patch deleted file mode 100644 index 063bcf1e64..0000000000 --- a/target/linux/generic-2.6/patches/170-netfilter_chaostables_0.8.patch +++ /dev/null @@ -1,853 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netfilter/oot_conntrack.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter/oot_conntrack.h -@@ -0,0 +1,5 @@ -+#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) -+# include -+#else /* linux-2.6.20+ */ -+# include -+#endif -Index: linux-2.6.21.7/include/linux/netfilter/oot_trans.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter/oot_trans.h -@@ -0,0 +1,14 @@ -+/* Out of tree workarounds */ -+#include -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) -+# define HAVE_MATCHINFOSIZE 1 -+# define HAVE_TARGUSERINFO 1 -+# define HAVE_TARGINFOSIZE 1 -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -+# define nfmark mark -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) -+# define tcp_v4_check(tcph, tcph_sz, s, d, csp) \ -+ tcp_v4_check((tcph_sz), (s), (d), (csp)) -+#endif -Index: linux-2.6.21.7/include/linux/netfilter/xt_CHAOS.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter/xt_CHAOS.h -@@ -0,0 +1,14 @@ -+#ifndef _LINUX_NETFILTER_XT_CHAOS_H -+#define _LINUX_NETFILTER_XT_CHAOS_H 1 -+ -+enum xt_chaos_target_variant { -+ XTCHAOS_NORMAL, -+ XTCHAOS_TARPIT, -+ XTCHAOS_DELUDE, -+}; -+ -+struct xt_chaos_target_info { -+ uint8_t variant; -+}; -+ -+#endif /* _LINUX_NETFILTER_XT_CHAOS_H */ -Index: linux-2.6.21.7/include/linux/netfilter/xt_portscan.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/linux/netfilter/xt_portscan.h -@@ -0,0 +1,8 @@ -+#ifndef _LINUX_NETFILTER_XT_PORTSCAN_H -+#define _LINUX_NETFILTER_XT_PORTSCAN_H 1 -+ -+struct xt_portscan_match_info { -+ uint8_t match_stealth, match_syn, match_cn, match_gr; -+}; -+ -+#endif /* _LINUX_NETFILTER_XT_PORTSCAN_H */ -Index: linux-2.6.21.7/net/netfilter/find_match.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/find_match.c -@@ -0,0 +1,39 @@ -+/* -+ xt_request_find_match -+ by Jan Engelhardt , 2006 - 2007 -+ -+ Based upon linux-2.6.18.5/net/netfilter/x_tables.c: -+ Copyright (C) 2006-2006 Harald Welte -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation. -+*/ -+#include -+#include -+#include -+#include -+ -+/* -+ * Yeah this code is sub-optimal, but the function is missing in -+ * mainline so far. -jengelh -+ */ -+static struct xt_match *xt_request_find_match_lo(int af, const char *name, -+ u8 revision) -+{ -+ static const char *const xt_prefix[] = { -+ [AF_INET] = "ip", -+ [AF_INET6] = "ip6", -+ [NF_ARP] = "arp", -+ }; -+ struct xt_match *match; -+ -+ match = try_then_request_module(xt_find_match(af, name, revision), -+ "%st_%s", xt_prefix[af], name); -+ if (IS_ERR(match) || match == NULL) -+ return NULL; -+ -+ return match; -+} -+ -+/* In case it goes into mainline, let this out-of-tree package compile */ -+#define xt_request_find_match xt_request_find_match_lo -Index: linux-2.6.21.7/net/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/Kconfig -+++ linux-2.6.21.7/net/netfilter/Kconfig -@@ -287,6 +287,14 @@ config NETFILTER_XTABLES - - # alphabetically ordered list of targets - -+config NETFILTER_XT_TARGET_CHAOS -+ tristate '"CHAOS" target support' -+ depends on NETFILTER_XTABLES -+ help -+ This option adds a `CHAOS' target. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ - config NETFILTER_XT_TARGET_CLASSIFY - tristate '"CLASSIFY" target support' - depends on NETFILTER_XTABLES -@@ -315,6 +323,14 @@ config NETFILTER_XT_TARGET_CONNMARK - . The module will be called - ipt_CONNMARK.o. If unsure, say `N'. - -+config NETFILTER_XT_TARGET_DELUDE -+ tristate '"DELUDE" target support' -+ depends on NETFILTER_XTABLES -+ help -+ This option adds a `DELUDE' target. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ - config NETFILTER_XT_TARGET_DSCP - tristate '"DSCP" target support' - depends on NETFILTER_XTABLES -@@ -563,6 +579,14 @@ config NETFILTER_XT_MATCH_POLICY - - To compile it as a module, choose M here. If unsure, say N. - -+config NETFILTER_XT_MATCH_PORTSCAN -+ tristate '"portscan" match support' -+ depends on NETFILTER_XTABLES -+ help -+ This option adds a 'portscan' match support. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ - config NETFILTER_XT_MATCH_MULTIPORT - tristate "Multiple port match support" - depends on NETFILTER_XTABLES -Index: linux-2.6.21.7/net/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/Makefile -+++ linux-2.6.21.7/net/netfilter/Makefile -@@ -47,6 +47,8 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK - obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o - obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o - obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o -+obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o -+obj-$(CONFIG_NETFILTER_XT_TARGET_DELUDE) += xt_DELUDE.o - - # matches - obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o -@@ -74,3 +76,4 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) - obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o - obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o - obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o -+obj-$(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) += xt_portscan.o -Index: linux-2.6.21.7/net/netfilter/xt_CHAOS.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/xt_CHAOS.c -@@ -0,0 +1,200 @@ -+/* -+ * CHAOS target for netfilter -+ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 -+ * Contact: Jan Engelhardt -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License; either version -+ * 2 or 3 as published by the Free Software Foundation. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if defined(_LOCAL) -+# include "xt_CHAOS.h" -+# include "find_match.c" -+#elif defined(CONFIG_NETFILTER_XT_TARGET_CHAOS) || \ -+ defined(CONFIG_NETFILTER_XT_TARGET_CHAOS_MODULE) -+# include -+# include "find_match.c" -+#else -+# include "xt_CHAOS.h" -+# include "find_match.c" -+#endif -+#define PFX KBUILD_MODNAME ": " -+ -+/* Module parameters */ -+static unsigned int reject_percentage = ~0U * .01; -+static unsigned int delude_percentage = ~0U * .0101; -+module_param(reject_percentage, uint, S_IRUGO | S_IWUSR); -+module_param(delude_percentage, uint, S_IRUGO | S_IWUSR); -+ -+/* References to other matches/targets */ -+static struct xt_match *xm_tcp; -+static struct xt_target *xt_delude, *xt_reject, *xt_tarpit; -+ -+static int have_delude, have_tarpit; -+ -+/* Static data for other matches/targets */ -+static const struct ipt_reject_info reject_params = { -+ .with = ICMP_HOST_UNREACH, -+}; -+ -+static const struct xt_tcp tcp_params = { -+ .spts = {0, ~0}, -+ .dpts = {0, ~0}, -+}; -+ -+/* CHAOS functions */ -+static void xt_chaos_total(const struct xt_chaos_target_info *info, -+ struct sk_buff **pskb, const struct net_device *in, -+ const struct net_device *out, unsigned int hooknum) -+{ -+ const struct iphdr *iph = ip_hdr(*pskb); -+ const int protoff = 4 * iph->ihl; -+ const int offset = ntohs(iph->frag_off) & IP_OFFSET; -+ const struct xt_target *destiny; -+ int hotdrop = 0, ret; -+ -+ ret = xm_tcp->match(*pskb, in, out, xm_tcp, &tcp_params, -+ offset, protoff, &hotdrop); -+ if (!ret || hotdrop || (unsigned int)net_random() > delude_percentage) -+ return; -+ -+ destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude; -+ destiny->target(pskb, in, out, hooknum, destiny, NULL); -+ return; -+} -+ -+static unsigned int chaos_tg(struct sk_buff **pskb, -+ const struct net_device *in, const struct net_device *out, -+ unsigned int hooknum, const struct xt_target *target, const void *targinfo) -+{ -+ /* -+ * Equivalent to: -+ * -A chaos -m statistic --mode random --probability \ -+ * $reject_percentage -j REJECT --reject-with host-unreach; -+ * -A chaos -p tcp -m statistic --mode random --probability \ -+ * $delude_percentage -j DELUDE; -+ * -A chaos -j DROP; -+ */ -+ const struct xt_chaos_target_info *info = targinfo; -+ const struct iphdr *iph = ip_hdr(*pskb); -+ -+ if ((unsigned int)net_random() <= reject_percentage) -+ return xt_reject->target(pskb, in, out, hooknum, target, -+ &reject_params); -+ -+ /* TARPIT/DELUDE may not be called from the OUTPUT chain */ -+ if (iph->protocol == IPPROTO_TCP && -+ info->variant != XTCHAOS_NORMAL && hooknum != NF_IP_LOCAL_OUT) -+ xt_chaos_total(info, pskb, in, out, hooknum); -+ -+ return NF_DROP; -+} -+ -+static int chaos_tg_check(const char *tablename, const void *entry, -+ const struct xt_target *target, void *targinfo, unsigned int hook_mask) -+{ -+ const struct xt_chaos_target_info *info = targinfo; -+ -+ if (info->variant == XTCHAOS_DELUDE && !have_delude) { -+ printk(KERN_WARNING PFX "Error: Cannot use --delude when " -+ "DELUDE module not available\n"); -+ return false; -+ } -+ if (info->variant == XTCHAOS_TARPIT && !have_tarpit) { -+ printk(KERN_WARNING PFX "Error: Cannot use --tarpit when " -+ "TARPIT module not available\n"); -+ return false; -+ } -+ -+ return true; -+} -+ -+static struct xt_target chaos_tg_reg = { -+ .name = "CHAOS", -+ .family = AF_INET, -+ .table = "filter", -+ .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | -+ (1 << NF_IP_LOCAL_OUT), -+ .checkentry = chaos_tg_check, -+ .target = chaos_tg, -+ .targetsize = sizeof(struct xt_chaos_target_info), -+ .me = THIS_MODULE, -+}; -+ -+static int __init chaos_tg_init(void) -+{ -+ int ret = -EINVAL; -+ -+ xm_tcp = xt_request_find_match(AF_INET, "tcp", 0); -+ if (xm_tcp == NULL) { -+ printk(KERN_WARNING PFX "Error: Could not find or load " -+ "\"tcp\" match\n"); -+ return -EINVAL; -+ } -+ -+ xt_reject = xt_request_find_target(AF_INET, "REJECT", 0); -+ if (xt_reject == NULL) { -+ printk(KERN_WARNING PFX "Error: Could not find or load " -+ "\"REJECT\" target\n"); -+ goto out2; -+ } -+ -+ xt_tarpit = xt_request_find_target(AF_INET, "TARPIT", 0); -+ have_tarpit = xt_tarpit != NULL; -+ if (!have_tarpit) -+ printk(KERN_WARNING PFX "Warning: Could not find or load " -+ "\"TARPIT\" target\n"); -+ -+ xt_delude = xt_request_find_target(AF_INET, "DELUDE", 0); -+ have_delude = xt_delude != NULL; -+ if (!have_delude) -+ printk(KERN_WARNING PFX "Warning: Could not find or load " -+ "\"DELUDE\" target\n"); -+ -+ if ((ret = xt_register_target(&chaos_tg_reg)) != 0) { -+ printk(KERN_WARNING PFX "xt_register_target returned " -+ "error %d\n", ret); -+ goto out3; -+ } -+ -+ return 0; -+ -+ out3: -+ if (have_delude) -+ module_put(xt_delude->me); -+ if (have_tarpit) -+ module_put(xt_tarpit->me); -+ module_put(xt_reject->me); -+ out2: -+ module_put(xm_tcp->me); -+ return ret; -+} -+ -+static void __exit chaos_tg_exit(void) -+{ -+ xt_unregister_target(&chaos_tg_reg); -+ module_put(xm_tcp->me); -+ module_put(xt_reject->me); -+ if (have_delude) -+ module_put(xt_delude->me); -+ if (have_tarpit) -+ module_put(xt_tarpit->me); -+ return; -+} -+ -+module_init(chaos_tg_init); -+module_exit(chaos_tg_exit); -+MODULE_AUTHOR("Jan Engelhardt "); -+MODULE_DESCRIPTION("netfilter \"CHAOS\" target"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("ipt_CHAOS"); -Index: linux-2.6.21.7/net/netfilter/xt_DELUDE.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/xt_DELUDE.c -@@ -0,0 +1,197 @@ -+/* -+ * DELUDE target -+ * Copyright © CC Computer Consultants GmbH, 2007 -+ * Contact: Jan Engelhardt -+ * -+ * Based upon linux-2.6.18.5/net/ipv4/netfilter/ipt_REJECT.c: -+ * (C) 1999-2001 Paul `Rusty' Russell -+ * (C) 2002-2004 Netfilter Core Team -+ * -+ * xt_DELUDE acts like REJECT, but does reply with SYN-ACK on SYN. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_BRIDGE_NETFILTER -+# include -+#endif -+#include -+#define PFX KBUILD_MODNAME ": " -+ -+static void delude_send_reset(struct sk_buff *oldskb, unsigned int hook) -+{ -+ struct tcphdr _otcph, *oth, *tcph; -+ unsigned int addr_type; -+ struct sk_buff *nskb; -+ u_int16_t tmp_port; -+ u_int32_t tmp_addr; -+ struct iphdr *niph; -+ bool needs_ack; -+ -+ /* IP header checks: fragment. */ -+ if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) -+ return; -+ -+ oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), -+ sizeof(_otcph), &_otcph); -+ if (oth == NULL) -+ return; -+ -+ /* No RST for RST. */ -+ if (oth->rst) -+ return; -+ -+ /* Check checksum */ -+ if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) -+ return; -+ -+ /* We need a linear, writeable skb. We also need to expand -+ headroom in case hh_len of incoming interface < hh_len of -+ outgoing interface */ -+ nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), -+ GFP_ATOMIC); -+ if (!nskb) -+ return; -+ -+ /* This packet will not be the same as the other: clear nf fields */ -+ nf_reset(nskb); -+ nskb->mark = 0; -+ skb_init_secmark(nskb); -+ -+ skb_shinfo(nskb)->gso_size = 0; -+ skb_shinfo(nskb)->gso_segs = 0; -+ skb_shinfo(nskb)->gso_type = 0; -+ -+ tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); -+ -+ /* Swap source and dest */ -+ niph = ip_hdr(nskb); -+ tmp_addr = niph->saddr; -+ niph->saddr = niph->daddr; -+ niph->daddr = tmp_addr; -+ tmp_port = tcph->source; -+ tcph->source = tcph->dest; -+ tcph->dest = tmp_port; -+ -+ /* Truncate to length (no data) */ -+ tcph->doff = sizeof(struct tcphdr) / 4; -+ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); -+ niph->tot_len = htons(nskb->len); -+ -+ if (oth->syn && !oth->ack && !oth->rst && !oth->fin) { -+ /* DELUDE essential part */ -+ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + -+ oldskb->len - ip_hdrlen(oldskb) - -+ (oth->doff << 2)); -+ tcph->seq = false; -+ tcph->ack = true; -+ } else { -+ if (!tcph->ack) { -+ needs_ack = true; -+ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + -+ oth->fin + oldskb->len - -+ ip_hdrlen(oldskb) - (oth->doff<<2)); -+ tcph->seq = false; -+ } else { -+ needs_ack = false; -+ tcph->seq = oth->ack_seq; -+ tcph->ack_seq = false; -+ } -+ -+ /* Reset flags */ -+ ((u_int8_t *)tcph)[13] = 0; -+ tcph->rst = true; -+ tcph->ack = needs_ack; -+ } -+ -+ tcph->window = 0; -+ tcph->urg_ptr = 0; -+ -+ /* Adjust TCP checksum */ -+ tcph->check = 0; -+ tcph->check = tcp_v4_check(sizeof(struct tcphdr), niph->saddr, -+ niph->daddr, csum_partial((char *)tcph, -+ sizeof(struct tcphdr), 0)); -+ -+ /* Set DF, id = 0 */ -+ niph->frag_off = htons(IP_DF); -+ niph->id = 0; -+ -+ addr_type = RTN_UNSPEC; -+#ifdef CONFIG_BRIDGE_NETFILTER -+ if (hook != NF_IP_FORWARD || (nskb->nf_bridge != NULL && -+ nskb->nf_bridge->mask & BRNF_BRIDGED)) -+#else -+ if (hook != NF_IP_FORWARD) -+#endif -+ addr_type = RTN_LOCAL; -+ -+ if (ip_route_me_harder(&nskb, addr_type)) -+ goto free_nskb; -+ -+ nskb->ip_summed = CHECKSUM_NONE; -+ -+ /* Adjust IP TTL */ -+ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); -+ -+ /* Adjust IP checksum */ -+ niph->check = 0; -+ niph->check = ip_fast_csum(skb_network_header(nskb), niph->ihl); -+ -+ /* "Never happens" */ -+ if (nskb->len > dst_mtu(nskb->dst)) -+ goto free_nskb; -+ -+ nf_ct_attach(nskb, oldskb); -+ -+ NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev, -+ dst_output); -+ return; -+ -+ free_nskb: -+ kfree_skb(nskb); -+} -+ -+static unsigned int delude_tg(struct sk_buff **pskb, -+ const struct net_device *in, const struct net_device *out, -+ unsigned int hooknum, const struct xt_target *target, const void *targinfo) -+{ -+ /* WARNING: This code causes reentry within iptables. -+ This means that the iptables jump stack is now crap. We -+ must return an absolute verdict. --RR */ -+ delude_send_reset(*pskb, hooknum); -+ return NF_DROP; -+} -+ -+static struct xt_target delude_tg_reg = { -+ .name = "DELUDE", -+ .family = AF_INET, -+ .table = "filter", -+ .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD), -+ .target = delude_tg, -+ .proto = IPPROTO_TCP, -+ .me = THIS_MODULE, -+}; -+ -+static int __init delude_tg_init(void) -+{ -+ return xt_register_target(&delude_tg_reg); -+} -+ -+static void __exit delude_tg_exit(void) -+{ -+ xt_unregister_target(&delude_tg_reg); -+} -+ -+module_init(delude_tg_init); -+module_exit(delude_tg_exit); -+MODULE_AUTHOR("Jan Engelhardt "); -+MODULE_DESCRIPTION("netfilter \"DELUDE\" target"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("ipt_DELUDE"); -Index: linux-2.6.21.7/net/netfilter/xt_portscan.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/xt_portscan.c -@@ -0,0 +1,269 @@ -+/* -+ * portscan match for netfilter -+ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 -+ * Contact: Jan Engelhardt -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License; either version -+ * 2 or 3 as published by the Free Software Foundation. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if defined(_LOCAL) -+# include "xt_portscan.h" -+#elif defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) || \ -+ defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN_MODULE) -+# include -+#else -+# include "xt_portscan.h" -+#endif -+#define PFX KBUILD_MODNAME ": " -+ -+enum { -+ TCP_FLAGS_ALL3 = TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_SYN, -+ TCP_FLAGS_ALL4 = TCP_FLAGS_ALL3 | TCP_FLAG_ACK, -+ TCP_FLAGS_ALL6 = TCP_FLAGS_ALL4 | TCP_FLAG_PSH | TCP_FLAG_URG, -+}; -+ -+/* Module parameters */ -+static unsigned int -+ connmark_mask = ~0, -+ packet_mask = ~0, -+ mark_seen = 0x9, -+ mark_synrcv = 0x1, -+ mark_closed = 0x2, -+ mark_synscan = 0x3, -+ mark_estab1 = 0x4, -+ mark_estab2 = 0x5, -+ mark_cnscan = 0x6, -+ mark_grscan = 0x7, -+ mark_valid = 0x8; -+ -+module_param(connmark_mask, uint, S_IRUGO | S_IWUSR); -+module_param(packet_mask, uint, S_IRUGO | S_IWUSR); -+module_param(mark_seen, uint, S_IRUGO | S_IWUSR); -+module_param(mark_synrcv, uint, S_IRUGO | S_IWUSR); -+module_param(mark_closed, uint, S_IRUGO | S_IWUSR); -+module_param(mark_synscan, uint, S_IRUGO | S_IWUSR); -+module_param(mark_estab1, uint, S_IRUGO | S_IWUSR); -+module_param(mark_estab2, uint, S_IRUGO | S_IWUSR); -+module_param(mark_cnscan, uint, S_IRUGO | S_IWUSR); -+module_param(mark_grscan, uint, S_IRUGO | S_IWUSR); -+module_param(mark_valid, uint, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(connmark_mask, "only set specified bits in connection mark"); -+MODULE_PARM_DESC(packet_mask, "only set specified bits in packet mark"); -+MODULE_PARM_DESC(mark_seen, "nfmark value for packet-seen state"); -+MODULE_PARM_DESC(mark_synrcv, "connmark value for SYN Received state"); -+MODULE_PARM_DESC(mark_closed, "connmark value for closed state"); -+MODULE_PARM_DESC(mark_synscan, "connmark value for SYN Scan state"); -+MODULE_PARM_DESC(mark_estab1, "connmark value for Established-1 state"); -+MODULE_PARM_DESC(mark_estab2, "connmark value for Established-2 state"); -+MODULE_PARM_DESC(mark_cnscan, "connmark value for Connect Scan state"); -+MODULE_PARM_DESC(mark_grscan, "connmark value for Grab Scan state"); -+MODULE_PARM_DESC(mark_valid, "connmark value for Valid state"); -+ -+/* TCP flag functions */ -+static inline bool tflg_ack4(const struct tcphdr *th) -+{ -+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_ACK; -+} -+ -+static inline bool tflg_ack6(const struct tcphdr *th) -+{ -+ return (tcp_flag_word(th) & TCP_FLAGS_ALL6) == TCP_FLAG_ACK; -+} -+ -+static inline bool tflg_fin(const struct tcphdr *th) -+{ -+ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_FIN; -+} -+ -+static inline bool tflg_rst(const struct tcphdr *th) -+{ -+ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_RST; -+} -+ -+static inline bool tflg_rstack(const struct tcphdr *th) -+{ -+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == -+ (TCP_FLAG_ACK | TCP_FLAG_RST); -+} -+ -+static inline bool tflg_syn(const struct tcphdr *th) -+{ -+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_SYN; -+} -+ -+static inline bool tflg_synack(const struct tcphdr *th) -+{ -+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == -+ (TCP_FLAG_SYN | TCP_FLAG_ACK); -+} -+ -+/* portscan functions */ -+static inline bool portscan_mt_stealth(const struct tcphdr *th) -+{ -+ /* -+ * "Connection refused" replies to our own probes must not be matched. -+ */ -+ if (tflg_rstack(th)) -+ return false; -+ -+ if (tflg_rst(th) && printk_ratelimit()) { -+ printk(KERN_WARNING PFX "Warning: Pure RST received\n"); -+ return false; -+ } -+ -+ /* -+ * -p tcp ! --syn -m conntrack --ctstate INVALID: Looking for non-start -+ * packets that are not associated with any connection -- this will -+ * match most scan types (NULL, XMAS, FIN) and ridiculous flag -+ * combinations (SYN-RST, SYN-FIN, SYN-FIN-RST, FIN-RST, etc.). -+ */ -+ return !tflg_syn(th); -+} -+ -+static inline unsigned int portscan_mt_full(int mark, -+ enum ip_conntrack_info ctstate, bool loopback, const struct tcphdr *tcph, -+ unsigned int payload_len) -+{ -+ if (mark == mark_estab2) { -+ /* -+ * -m connmark --mark $ESTAB2 -+ */ -+ if (tflg_ack4(tcph) && payload_len == 0) -+ return mark; /* keep mark */ -+ else if (tflg_rst(tcph) || tflg_fin(tcph)) -+ return mark_grscan; -+ else -+ return mark_valid; -+ } else if (mark == mark_estab1) { -+ /* -+ * -m connmark --mark $ESTAB1 -+ */ -+ if (tflg_rst(tcph) || tflg_fin(tcph)) -+ return mark_cnscan; -+ else if (!loopback && tflg_ack4(tcph) && payload_len == 0) -+ return mark_estab2; -+ else -+ return mark_valid; -+ } else if (mark == mark_synrcv) { -+ /* -+ * -m connmark --mark $SYN -+ */ -+ if (loopback && tflg_synack(tcph)) -+ return mark; /* keep mark */ -+ else if (loopback && tflg_rstack(tcph)) -+ return mark_closed; -+ else if (tflg_ack6(tcph)) -+ return mark_estab1; -+ else -+ return mark_synscan; -+ } else if (ctstate == IP_CT_NEW && tflg_syn(tcph)) { -+ /* -+ * -p tcp --syn --ctstate NEW -+ */ -+ return mark_synrcv; -+ } -+ return mark; -+} -+ -+static int portscan_mt(const struct sk_buff *skb, -+ const struct net_device *in, const struct net_device *out, -+ const struct xt_match *match, const void *matchinfo, int offset, -+ unsigned int protoff, int *hotdrop) -+{ -+ const struct xt_portscan_match_info *info = matchinfo; -+ enum ip_conntrack_info ctstate; -+ const struct tcphdr *tcph; -+ struct nf_conn *ctdata; -+ struct tcphdr tcph_buf; -+ -+ tcph = skb_header_pointer(skb, protoff, sizeof(tcph_buf), &tcph_buf); -+ if (tcph == NULL) -+ return false; -+ -+ /* Check for invalid packets: -m conntrack --ctstate INVALID */ -+ if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) { -+ if (info->match_stealth) -+ return portscan_mt_stealth(tcph); -+ /* -+ * If @ctdata is NULL, we cannot match the other scan -+ * types, return. -+ */ -+ return false; -+ } -+ -+ /* -+ * If -m portscan was previously applied to this packet, the rules we -+ * simulate must not be run through again. And for speedup, do not call -+ * it either when the connection is already VALID. -+ */ -+ if ((ctdata->mark & connmark_mask) == mark_valid || -+ (skb->mark & packet_mask) != mark_seen) { -+ unsigned int n; -+ -+ n = portscan_mt_full(ctdata->mark & connmark_mask, ctstate, -+ in == &loopback_dev, tcph, -+ skb->len - protoff - 4 * tcph->doff); -+ -+ ctdata->mark = (ctdata->mark & ~connmark_mask) | n; -+ ((struct sk_buff *)skb)->mark = -+ (skb->mark & ~packet_mask) ^ mark_seen; -+ } -+ -+ return (info->match_syn && ctdata->mark == mark_synscan) || -+ (info->match_cn && ctdata->mark == mark_cnscan) || -+ (info->match_gr && ctdata->mark == mark_grscan); -+} -+ -+static int portscan_mt_check(const char *tablename, const void *entry, -+ const struct xt_match *match, void *matchinfo, unsigned int hook_mask) -+{ -+ const struct xt_portscan_match_info *info = matchinfo; -+ -+ if ((info->match_stealth & ~1) || (info->match_syn & ~1) || -+ (info->match_cn & ~1) || (info->match_gr & ~1)) { -+ printk(KERN_WARNING PFX "Invalid flags\n"); -+ return false; -+ } -+ return true; -+} -+ -+static struct xt_match portscan_mt_reg __read_mostly = { -+ .name = "portscan", -+ .family = AF_INET, -+ .match = portscan_mt, -+ .checkentry = portscan_mt_check, -+ .matchsize = sizeof(struct xt_portscan_match_info), -+ .proto = IPPROTO_TCP, -+ .me = THIS_MODULE, -+}; -+ -+static int __init portscan_mt_init(void) -+{ -+ return xt_register_match(&portscan_mt_reg); -+} -+ -+static void __exit portscan_mt_exit(void) -+{ -+ xt_unregister_match(&portscan_mt_reg); -+ return; -+} -+ -+module_init(portscan_mt_init); -+module_exit(portscan_mt_exit); -+MODULE_AUTHOR("Jan Engelhardt "); -+MODULE_DESCRIPTION("netfilter \"portscan\" match"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("ipt_portscan"); diff --git a/target/linux/generic-2.6/patches/171-netfilter_tarpit.patch b/target/linux/generic-2.6/patches/171-netfilter_tarpit.patch deleted file mode 100644 index a686bd3d6f..0000000000 --- a/target/linux/generic-2.6/patches/171-netfilter_tarpit.patch +++ /dev/null @@ -1,325 +0,0 @@ -Index: linux-2.6.21.7/net/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/Kconfig -+++ linux-2.6.21.7/net/netfilter/Kconfig -@@ -414,6 +414,23 @@ config NETFILTER_XT_TARGET_CONNSECMARK - - To compile it as a module, choose M here. If unsure, say N. - -+config NETFILTER_XT_TARGET_TARPIT -+ tristate '"TARPIT" target support' -+ depends on NETFILTER_XTABLES -+ ---help--- -+ Adds a TARPIT target to iptables, which captures and holds -+ incoming TCP connections using no local per-connection resources. -+ Connections are accepted, but immediately switched to the persist -+ state (0 byte window), in which the remote side stops sending data -+ and asks to continue every 60-240 seconds. Attempts to close the -+ connection are ignored, forcing the remote side to time out the -+ connection in 12-24 minutes. -+ -+ This offers similar functionality to LaBrea -+ , but does not require dedicated -+ hardware or IPs. Any TCP port that you would normally DROP or REJECT -+ can instead become a tarpit. -+ - config NETFILTER_XT_TARGET_TCPMSS - tristate '"TCPMSS" target support' - depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) -Index: linux-2.6.21.7/net/netfilter/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/Makefile -+++ linux-2.6.21.7/net/netfilter/Makefile -@@ -45,6 +45,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE - obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o - obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o - obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o -+obj-$(CONFIG_NETFILTER_XT_TARGET_TARPIT) += xt_TARPIT.o - obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o - obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o - obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o -Index: linux-2.6.21.7/net/netfilter/xt_TARPIT.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/netfilter/xt_TARPIT.c -@@ -0,0 +1,280 @@ -+/* -+ * Kernel module to capture and hold incoming TCP connections using -+ * no local per-connection resources. -+ * -+ * Based on ipt_REJECT.c and offering functionality similar to -+ * LaBrea . -+ * -+ * Copyright (c) 2002 Aaron Hopkins -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -+ * -+ * Goal: -+ * - Allow incoming TCP connections to be established. -+ * - Passing data should result in the connection being switched to the -+ * persist state (0 byte window), in which the remote side stops sending -+ * data and asks to continue every 60 seconds. -+ * - Attempts to shut down the connection should be ignored completely, so -+ * the remote side ends up having to time it out. -+ * -+ * This means: -+ * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes -+ * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing -+ * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+struct in_device; -+#include -+#include -+#include -+ -+#if 0 -+#define DEBUGP printk -+#else -+#define DEBUGP(format, args...) -+#endif -+ -+/* Stolen from ip_finish_output2 */ -+static int ip_direct_send(struct sk_buff *skb) -+{ -+ struct dst_entry *dst = skb->dst; -+ -+ if (dst->hh != NULL) -+ return neigh_hh_output(dst->hh, skb); -+ else if (dst->neighbour != NULL) -+ return dst->neighbour->output(skb); -+ -+ if (net_ratelimit()) -+ printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n"); -+ -+ kfree_skb(skb); -+ return -EINVAL; -+} -+ -+ -+/* Send reply */ -+static void tarpit_tcp(const struct sk_buff *oskb, struct rtable *ort, -+ unsigned int local) -+{ -+ struct sk_buff *nskb; -+ struct rtable *nrt; -+ struct tcphdr *otcph, *ntcph; -+ struct flowi fl = {}; -+ unsigned int otcplen; -+ u_int16_t tmp; -+ -+ const struct iphdr *oiph = ip_hdr(oskb); -+ struct iphdr *niph; -+ -+ /* A truncated TCP header is not going to be useful */ -+ if (oskb->len < ip_hdrlen(oskb) + sizeof(struct tcphdr)) -+ return; -+ -+ otcph = (void *)oiph + ip_hdrlen(oskb); -+ otcplen = oskb->len - ip_hdrlen(oskb); -+ -+ /* No replies for RST or FIN */ -+ if (otcph->rst || otcph->fin) -+ return; -+ -+ /* No reply to !SYN,!ACK. Rate-limit replies to !SYN,ACKs */ -+ if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ))) -+ return; -+ -+ /* Check checksum. */ -+ if (tcp_v4_check(otcplen, oiph->saddr, oiph->daddr, -+ csum_partial((char *)otcph, otcplen, 0)) != 0) -+ return; -+ -+ /* -+ * Copy skb (even if skb is about to be dropped, we cannot just -+ * clone it because there may be other things, such as tcpdump, -+ * interested in it) -+ */ -+ nskb = skb_copy(oskb, GFP_ATOMIC); -+ if (nskb == NULL) -+ return; -+ -+ niph = ip_hdr(nskb); -+ -+ /* This packet will not be the same as the other: clear nf fields */ -+ nf_conntrack_put(nskb->nfct); -+ nskb->nfct = NULL; -+#ifdef CONFIG_NETFILTER_DEBUG -+ nskb->nf_debug = 0; -+#endif -+ -+ ntcph = (void *)niph + ip_hdrlen(nskb); -+ -+ /* Truncate to length (no data) */ -+ ntcph->doff = sizeof(struct tcphdr)/4; -+ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); -+ niph->tot_len = htons(nskb->len); -+ -+ /* Swap source and dest */ -+ niph->daddr = xchg(&niph->saddr, niph->daddr); -+ tmp = ntcph->source; -+ ntcph->source = ntcph->dest; -+ ntcph->dest = tmp; -+ -+ /* Use supplied sequence number or make a new one */ -+ ntcph->seq = otcph->ack ? otcph->ack_seq -+ : htonl(secure_tcp_sequence_number(niph->saddr, -+ niph->daddr, -+ ntcph->source, -+ ntcph->dest)); -+ -+ /* Our SYN-ACKs must have a >0 window */ -+ ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0; -+ -+ ntcph->urg_ptr = 0; -+ -+ /* Reset flags */ -+ ((u_int8_t *)ntcph)[13] = 0; -+ -+ if (otcph->syn && otcph->ack) { -+ ntcph->rst = 1; -+ ntcph->ack_seq = 0; -+ } else { -+ ntcph->syn = otcph->syn; -+ ntcph->ack = 1; -+ ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn); -+ } -+ -+ /* Adjust TCP checksum */ -+ ntcph->check = 0; -+ ntcph->check = tcp_v4_check(sizeof(struct tcphdr), -+ niph->saddr, -+ niph->daddr, -+ csum_partial((char *)ntcph, -+ sizeof(struct tcphdr), 0)); -+ -+ fl.nl_u.ip4_u.daddr = niph->daddr; -+ fl.nl_u.ip4_u.saddr = local ? niph->saddr : 0; -+ fl.nl_u.ip4_u.tos = RT_TOS(niph->tos) | RTO_CONN; -+ fl.oif = 0; -+ -+ if (ip_route_output_key(&nrt, &fl)) -+ goto free_nskb; -+ -+ dst_release(nskb->dst); -+ nskb->dst = &nrt->u.dst; -+ -+ /* Adjust IP TTL */ -+ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); -+ -+ /* Set DF, id = 0 */ -+ niph->frag_off = htons(IP_DF); -+ niph->id = 0; -+ -+ /* Adjust IP checksum */ -+ niph->check = 0; -+ niph->check = ip_fast_csum((unsigned char *)niph, niph->ihl); -+ -+ /* "Never happens" */ -+ if (nskb->len > dst_mtu(nskb->dst)) -+ goto free_nskb; -+ -+ ip_direct_send(nskb); -+ return; -+ -+ free_nskb: -+ kfree_skb(nskb); -+} -+ -+static unsigned int xt_tarpit_target(struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ unsigned int hooknum, -+ const struct xt_target *target, -+ const void *targinfo) -+{ -+ const struct sk_buff *skb = *pskb; -+ const struct iphdr *iph = ip_hdr(skb); -+ struct rtable *rt = (void *)skb->dst; -+ -+ /* Do we have an input route cache entry? */ -+ if (rt == NULL) -+ return NF_DROP; -+ -+ /* No replies to physical multicast/broadcast */ -+ if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST) -+ return NF_DROP; -+ -+ /* Now check at the protocol level */ -+ if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) -+ return NF_DROP; -+ -+ /* -+ * Our naive response construction does not deal with IP -+ * options, and probably should not try. -+ */ -+ if (iph->ihl * 4 != sizeof(struct iphdr)) -+ return NF_DROP; -+ -+ /* We are not interested in fragments */ -+ if (iph->frag_off & htons(IP_OFFSET)) -+ return NF_DROP; -+ -+ tarpit_tcp(skb, rt, hooknum == NF_IP_LOCAL_IN); -+ return NF_DROP; -+} -+ -+static int xt_tarpit_check(const char *tablename, const void *entry, -+ const struct xt_target *target, void *targinfo, -+ unsigned int hook_mask) -+{ -+ bool invalid; -+ -+ if (strcmp(tablename, "raw") == 0 && hook_mask == NF_IP_PRE_ROUTING) -+ return true; -+ if (strcmp(tablename, "filter") != 0) -+ return false; -+ invalid = hook_mask & ~((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD)); -+ return !invalid; -+} -+ -+static struct xt_target xt_tarpit_reg = { -+ .name = "TARPIT", -+ .family = AF_INET, -+ .proto = IPPROTO_TCP, -+ .target = xt_tarpit_target, -+ .checkentry = xt_tarpit_check, -+ .me = THIS_MODULE, -+}; -+ -+static int __init xt_tarpit_init(void) -+{ -+ return xt_register_target(&xt_tarpit_reg); -+} -+ -+static void __exit xt_tarpit_exit(void) -+{ -+ xt_unregister_target(&xt_tarpit_reg); -+} -+ -+module_init(xt_tarpit_init); -+module_exit(xt_tarpit_exit); -+MODULE_DESCRIPTION("netfilter xt_TARPIT target module"); -+MODULE_AUTHOR("Jan Engelhardt "); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("ipt_TARPIT"); diff --git a/target/linux/generic-2.6/patches/180-netfilter_depends.patch b/target/linux/generic-2.6/patches/180-netfilter_depends.patch deleted file mode 100644 index e155ecc3c4..0000000000 --- a/target/linux/generic-2.6/patches/180-netfilter_depends.patch +++ /dev/null @@ -1,22 +0,0 @@ -Index: linux-2.6.21.7/net/netfilter/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/netfilter/Kconfig -+++ linux-2.6.21.7/net/netfilter/Kconfig -@@ -165,7 +165,7 @@ config NF_CONNTRACK_FTP - - config NF_CONNTRACK_H323 - tristate "H.323 protocol support (EXPERIMENTAL)" -- depends on EXPERIMENTAL && NF_CONNTRACK && (IPV6 || IPV6=n) -+ depends on EXPERIMENTAL && NF_CONNTRACK - help - H.323 is a VoIP signalling protocol from ITU-T. As one of the most - important VoIP protocols, it is widely used by voice hardware and -@@ -433,7 +433,7 @@ config NETFILTER_XT_TARGET_TARPIT - - config NETFILTER_XT_TARGET_TCPMSS - tristate '"TCPMSS" target support' -- depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) -+ depends on NETFILTER_XTABLES - ---help--- - This option adds a `TCPMSS' target, which allows you to alter the - MSS value of TCP SYN packets, to control the maximum size for that diff --git a/target/linux/generic-2.6/patches/200-sched_esfq.patch b/target/linux/generic-2.6/patches/200-sched_esfq.patch deleted file mode 100644 index 38efcea6d5..0000000000 --- a/target/linux/generic-2.6/patches/200-sched_esfq.patch +++ /dev/null @@ -1,797 +0,0 @@ -Index: linux-2.6.21.7/include/linux/pkt_sched.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/pkt_sched.h -+++ linux-2.6.21.7/include/linux/pkt_sched.h -@@ -146,8 +146,40 @@ struct tc_sfq_qopt - * - * The only reason for this is efficiency, it is possible - * to change these parameters in compile time. -+ * -+ * If you need to play with these values use esfq instead. - */ - -+/* ESFQ section */ -+ -+enum -+{ -+ /* traditional */ -+ TCA_SFQ_HASH_CLASSIC, -+ TCA_SFQ_HASH_DST, -+ TCA_SFQ_HASH_SRC, -+ TCA_SFQ_HASH_FWMARK, -+ /* direct */ -+ TCA_SFQ_HASH_DSTDIR, -+ TCA_SFQ_HASH_SRCDIR, -+ TCA_SFQ_HASH_FWMARKDIR, -+ /* conntrack */ -+ TCA_SFQ_HASH_CTORIGDST, -+ TCA_SFQ_HASH_CTORIGSRC, -+ TCA_SFQ_HASH_CTREPLDST, -+ TCA_SFQ_HASH_CTREPLSRC, -+}; -+ -+struct tc_esfq_qopt -+{ -+ unsigned quantum; /* Bytes per round allocated to flow */ -+ int perturb_period; /* Period of hash perturbation */ -+ __u32 limit; /* Maximal packets in queue */ -+ unsigned divisor; /* Hash divisor */ -+ unsigned flows; /* Maximal number of flows */ -+ unsigned hash_kind; /* Hash function to use for flow identification */ -+}; -+ - /* RED section */ - - enum -Index: linux-2.6.21.7/net/sched/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/net/sched/Kconfig -+++ linux-2.6.21.7/net/sched/Kconfig -@@ -189,6 +189,26 @@ config NET_SCH_SFQ - To compile this code as a module, choose M here: the - module will be called sch_sfq. - -+config NET_SCH_ESFQ -+ tristate "Enhanced Stochastic Fairness Queueing (ESFQ)" -+ ---help--- -+ Say Y here if you want to use the Enhanced Stochastic Fairness -+ Queueing (ESFQ) packet scheduling algorithm for some of your network -+ devices or as a leaf discipline for a classful qdisc such as HTB or -+ CBQ (see the top of for details and -+ references to the SFQ algorithm). -+ -+ This is an enchanced SFQ version which allows you to control some -+ hardcoded values in the SFQ scheduler. -+ -+ ESFQ also adds control of the hash function used to identify packet -+ flows. The original SFQ discipline hashes by connection; ESFQ add -+ several other hashing methods, such as by src IP or by dst IP, which -+ can be more fair to users in some networking situations. -+ -+ To compile this code as a module, choose M here: the -+ module will be called sch_esfq. -+ - config NET_SCH_TEQL - tristate "True Link Equalizer (TEQL)" - ---help--- -Index: linux-2.6.21.7/net/sched/Makefile -=================================================================== ---- linux-2.6.21.7.orig/net/sched/Makefile -+++ linux-2.6.21.7/net/sched/Makefile -@@ -23,6 +23,7 @@ obj-$(CONFIG_NET_SCH_GRED) += sch_gred.o - obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o - obj-$(CONFIG_NET_SCH_DSMARK) += sch_dsmark.o - obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o -+obj-$(CONFIG_NET_SCH_ESFQ) += sch_esfq.o - obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o - obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o - obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o -Index: linux-2.6.21.7/net/sched/sch_esfq.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/net/sched/sch_esfq.c -@@ -0,0 +1,704 @@ -+/* -+ * net/sched/sch_esfq.c Extended Stochastic Fairness Queueing discipline. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Authors: Alexey Kuznetsov, -+ * -+ * Changes: Alexander Atanasov, -+ * Added dynamic depth,limit,divisor,hash_kind options. -+ * Added dst and src hashes. -+ * -+ * Alexander Clouter, -+ * Ported ESFQ to Linux 2.6. -+ * -+ * Corey Hickey, -+ * Maintenance of the Linux 2.6 port. -+ * Added fwmark hash (thanks to Robert Kurjata). -+ * Added direct hashing for src, dst, and fwmark. -+ * Added usage of jhash. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef CONFIG_NF_CONNTRACK_ENABLED -+#include -+#endif -+ -+/* Stochastic Fairness Queuing algorithm. -+ For more comments look at sch_sfq.c. -+ The difference is that you can change limit, depth, -+ hash table size and choose alternate hash types. -+ -+ classic: same as in sch_sfq.c -+ dst: destination IP address -+ src: source IP address -+ fwmark: netfilter mark value -+ dst_direct: -+ src_direct: -+ fwmark_direct: direct hashing of the above sources -+ ctorigdst: original destination IP address -+ ctorigsrc: original source IP address -+ ctrepldst: reply destination IP address -+ ctreplsrc: reply source IP -+ -+*/ -+ -+ -+/* This type should contain at least SFQ_DEPTH*2 values */ -+typedef unsigned int esfq_index; -+ -+struct esfq_head -+{ -+ esfq_index next; -+ esfq_index prev; -+}; -+ -+struct esfq_sched_data -+{ -+/* Parameters */ -+ int perturb_period; -+ unsigned quantum; /* Allotment per round: MUST BE >= MTU */ -+ int limit; -+ unsigned depth; -+ unsigned hash_divisor; -+ unsigned hash_kind; -+/* Variables */ -+ struct timer_list perturb_timer; -+ int perturbation; -+ esfq_index tail; /* Index of current slot in round */ -+ esfq_index max_depth; /* Maximal depth */ -+ -+ esfq_index *ht; /* Hash table */ -+ esfq_index *next; /* Active slots link */ -+ short *allot; /* Current allotment per slot */ -+ unsigned short *hash; /* Hash value indexed by slots */ -+ struct sk_buff_head *qs; /* Slot queue */ -+ struct esfq_head *dep; /* Linked list of slots, indexed by depth */ -+ unsigned dyn_min; /* For dynamic divisor adjustment; minimum value seen */ -+ unsigned dyn_max; /* maximum value seen */ -+ unsigned dyn_range; /* saved range */ -+}; -+ -+/* This contains the info we will hash. */ -+struct esfq_packet_info -+{ -+ u32 proto; /* protocol or port */ -+ u32 src; /* source from packet header */ -+ u32 dst; /* destination from packet header */ -+ u32 ctorigsrc; /* original source from conntrack */ -+ u32 ctorigdst; /* original destination from conntrack */ -+ u32 ctreplsrc; /* reply source from conntrack */ -+ u32 ctrepldst; /* reply destination from conntrack */ -+ u32 mark; /* netfilter mark (fwmark) */ -+}; -+ -+/* Hash input values directly into the "nearest" slot, taking into account the -+ * range of input values seen. This is most useful when the hash table is at -+ * least as large as the range of possible values. -+ * Note: this functionality was added before the change to using jhash, and may -+ * no longer be useful. */ -+static __inline__ unsigned esfq_hash_direct(struct esfq_sched_data *q, u32 h) -+{ -+ /* adjust minimum and maximum */ -+ if (h < q->dyn_min || h > q->dyn_max) { -+ q->dyn_min = h < q->dyn_min ? h : q->dyn_min; -+ q->dyn_max = h > q->dyn_max ? h : q->dyn_max; -+ -+ /* find new range */ -+ if ((q->dyn_range = q->dyn_max - q->dyn_min) >= q->hash_divisor) -+ printk(KERN_WARNING "ESFQ: (direct hash) Input range %u is larger than hash " -+ "table. See ESFQ README for details.\n", q->dyn_range); -+ } -+ -+ /* hash input values into slot numbers */ -+ if (q->dyn_min == q->dyn_max) -+ return 0; /* only one value seen; avoid division by 0 */ -+ else -+ return (h - q->dyn_min) * (q->hash_divisor - 1) / q->dyn_range; -+} -+ -+static __inline__ unsigned esfq_jhash_1word(struct esfq_sched_data *q,u32 a) -+{ -+ return jhash_1word(a, q->perturbation) & (q->hash_divisor-1); -+} -+ -+static __inline__ unsigned esfq_jhash_2words(struct esfq_sched_data *q, u32 a, u32 b) -+{ -+ return jhash_2words(a, b, q->perturbation) & (q->hash_divisor-1); -+} -+ -+static __inline__ unsigned esfq_jhash_3words(struct esfq_sched_data *q, u32 a, u32 b, u32 c) -+{ -+ return jhash_3words(a, b, c, q->perturbation) & (q->hash_divisor-1); -+} -+ -+ -+static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb) -+{ -+ struct esfq_packet_info info; -+#ifdef CONFIG_NF_CONNTRACK_ENABLED -+ enum ip_conntrack_info ctinfo; -+ struct nf_conn *ct = nf_ct_get(skb, &ctinfo); -+#endif -+ -+ switch (skb->protocol) { -+ case __constant_htons(ETH_P_IP): -+ { -+ struct iphdr *iph = skb->nh.iph; -+ info.dst = iph->daddr; -+ info.src = iph->saddr; -+ if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && -+ (iph->protocol == IPPROTO_TCP || -+ iph->protocol == IPPROTO_UDP || -+ iph->protocol == IPPROTO_SCTP || -+ iph->protocol == IPPROTO_DCCP || -+ iph->protocol == IPPROTO_ESP)) -+ info.proto = *(((u32*)iph) + iph->ihl); -+ else -+ info.proto = iph->protocol; -+ break; -+ } -+ case __constant_htons(ETH_P_IPV6): -+ { -+ struct ipv6hdr *iph = skb->nh.ipv6h; -+ /* Hash ipv6 addresses into a u32. This isn't ideal, -+ * but the code is simple. */ -+ info.dst = jhash2(iph->daddr.s6_addr32, 4, q->perturbation); -+ info.src = jhash2(iph->saddr.s6_addr32, 4, q->perturbation); -+ if (iph->nexthdr == IPPROTO_TCP || -+ iph->nexthdr == IPPROTO_UDP || -+ iph->nexthdr == IPPROTO_SCTP || -+ iph->nexthdr == IPPROTO_DCCP || -+ iph->nexthdr == IPPROTO_ESP) -+ info.proto = *(u32*)&iph[1]; -+ else -+ info.proto = iph->nexthdr; -+ break; -+ } -+ default: -+ info.dst = (u32)(unsigned long)skb->dst; -+ info.src = (u32)(unsigned long)skb->sk; -+ info.proto = skb->protocol; -+ } -+ -+ info.mark = skb->mark; -+ -+#ifdef CONFIG_NF_CONNTRACK_ENABLED -+ /* defaults if there is no conntrack info */ -+ info.ctorigsrc = info.src; -+ info.ctorigdst = info.dst; -+ info.ctreplsrc = info.dst; -+ info.ctrepldst = info.src; -+ /* collect conntrack info */ -+ if (ct && ct != &nf_conntrack_untracked) { -+ if (skb->protocol == __constant_htons(ETH_P_IP)) { -+ info.ctorigsrc = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; -+ info.ctorigdst = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip; -+ info.ctreplsrc = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip; -+ info.ctrepldst = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; -+ } -+ else if (skb->protocol == __constant_htons(ETH_P_IPV6)) { -+ /* Again, hash ipv6 addresses into a single u32. */ -+ info.ctorigsrc = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6, 4, q->perturbation); -+ info.ctorigdst = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip6, 4, q->perturbation); -+ info.ctreplsrc = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6, 4, q->perturbation); -+ info.ctrepldst = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6, 4, q->perturbation); -+ } -+ -+ } -+#endif -+ -+ switch(q->hash_kind) -+ { -+ case TCA_SFQ_HASH_CLASSIC: -+ return esfq_jhash_3words(q, info.dst, info.src, info.proto); -+ case TCA_SFQ_HASH_DST: -+ return esfq_jhash_1word(q, info.dst); -+ case TCA_SFQ_HASH_DSTDIR: -+ return esfq_hash_direct(q, ntohl(info.dst)); -+ case TCA_SFQ_HASH_SRC: -+ return esfq_jhash_1word(q, info.src); -+ case TCA_SFQ_HASH_SRCDIR: -+ return esfq_hash_direct(q, ntohl(info.src)); -+ case TCA_SFQ_HASH_FWMARK: -+ return esfq_jhash_1word(q, info.mark); -+ case TCA_SFQ_HASH_FWMARKDIR: -+ return esfq_hash_direct(q, info.mark); -+#ifdef CONFIG_NF_CONNTRACK_ENABLED -+ case TCA_SFQ_HASH_CTORIGDST: -+ return esfq_jhash_1word(q, info.ctorigdst); -+ case TCA_SFQ_HASH_CTORIGSRC: -+ return esfq_jhash_1word(q, info.ctorigsrc); -+ case TCA_SFQ_HASH_CTREPLDST: -+ return esfq_jhash_1word(q, info.ctrepldst); -+ case TCA_SFQ_HASH_CTREPLSRC: -+ return esfq_jhash_1word(q, info.ctreplsrc); -+#endif -+ default: -+ if (net_ratelimit()) -+ printk(KERN_WARNING "ESFQ: Unknown hash method. Falling back to classic.\n"); -+ } -+ return esfq_jhash_3words(q, info.dst, info.src, info.proto); -+} -+ -+static inline void esfq_link(struct esfq_sched_data *q, esfq_index x) -+{ -+ esfq_index p, n; -+ int d = q->qs[x].qlen + q->depth; -+ -+ p = d; -+ n = q->dep[d].next; -+ q->dep[x].next = n; -+ q->dep[x].prev = p; -+ q->dep[p].next = q->dep[n].prev = x; -+} -+ -+static inline void esfq_dec(struct esfq_sched_data *q, esfq_index x) -+{ -+ esfq_index p, n; -+ -+ n = q->dep[x].next; -+ p = q->dep[x].prev; -+ q->dep[p].next = n; -+ q->dep[n].prev = p; -+ -+ if (n == p && q->max_depth == q->qs[x].qlen + 1) -+ q->max_depth--; -+ -+ esfq_link(q, x); -+} -+ -+static inline void esfq_inc(struct esfq_sched_data *q, esfq_index x) -+{ -+ esfq_index p, n; -+ int d; -+ -+ n = q->dep[x].next; -+ p = q->dep[x].prev; -+ q->dep[p].next = n; -+ q->dep[n].prev = p; -+ d = q->qs[x].qlen; -+ if (q->max_depth < d) -+ q->max_depth = d; -+ -+ esfq_link(q, x); -+} -+ -+static unsigned int esfq_drop(struct Qdisc *sch) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ esfq_index d = q->max_depth; -+ struct sk_buff *skb; -+ unsigned int len; -+ -+ /* Queue is full! Find the longest slot and -+ drop a packet from it */ -+ -+ if (d > 1) { -+ esfq_index x = q->dep[d+q->depth].next; -+ skb = q->qs[x].prev; -+ len = skb->len; -+ __skb_unlink(skb, &q->qs[x]); -+ kfree_skb(skb); -+ esfq_dec(q, x); -+ sch->q.qlen--; -+ sch->qstats.drops++; -+ sch->qstats.backlog -= len; -+ return len; -+ } -+ -+ if (d == 1) { -+ /* It is difficult to believe, but ALL THE SLOTS HAVE LENGTH 1. */ -+ d = q->next[q->tail]; -+ q->next[q->tail] = q->next[d]; -+ q->allot[q->next[d]] += q->quantum; -+ skb = q->qs[d].prev; -+ len = skb->len; -+ __skb_unlink(skb, &q->qs[d]); -+ kfree_skb(skb); -+ esfq_dec(q, d); -+ sch->q.qlen--; -+ q->ht[q->hash[d]] = q->depth; -+ sch->qstats.drops++; -+ sch->qstats.backlog -= len; -+ return len; -+ } -+ -+ return 0; -+} -+ -+static int -+esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ unsigned hash = esfq_hash(q, skb); -+ unsigned depth = q->depth; -+ esfq_index x; -+ -+ x = q->ht[hash]; -+ if (x == depth) { -+ q->ht[hash] = x = q->dep[depth].next; -+ q->hash[x] = hash; -+ } -+ sch->qstats.backlog += skb->len; -+ __skb_queue_tail(&q->qs[x], skb); -+ esfq_inc(q, x); -+ if (q->qs[x].qlen == 1) { /* The flow is new */ -+ if (q->tail == depth) { /* It is the first flow */ -+ q->tail = x; -+ q->next[x] = x; -+ q->allot[x] = q->quantum; -+ } else { -+ q->next[x] = q->next[q->tail]; -+ q->next[q->tail] = x; -+ q->tail = x; -+ } -+ } -+ if (++sch->q.qlen < q->limit-1) { -+ sch->bstats.bytes += skb->len; -+ sch->bstats.packets++; -+ return 0; -+ } -+ -+ esfq_drop(sch); -+ return NET_XMIT_CN; -+} -+ -+static int -+esfq_requeue(struct sk_buff *skb, struct Qdisc* sch) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ unsigned hash = esfq_hash(q, skb); -+ unsigned depth = q->depth; -+ esfq_index x; -+ -+ x = q->ht[hash]; -+ if (x == depth) { -+ q->ht[hash] = x = q->dep[depth].next; -+ q->hash[x] = hash; -+ } -+ sch->qstats.backlog += skb->len; -+ __skb_queue_head(&q->qs[x], skb); -+ esfq_inc(q, x); -+ if (q->qs[x].qlen == 1) { /* The flow is new */ -+ if (q->tail == depth) { /* It is the first flow */ -+ q->tail = x; -+ q->next[x] = x; -+ q->allot[x] = q->quantum; -+ } else { -+ q->next[x] = q->next[q->tail]; -+ q->next[q->tail] = x; -+ q->tail = x; -+ } -+ } -+ if (++sch->q.qlen < q->limit - 1) { -+ sch->qstats.requeues++; -+ return 0; -+ } -+ -+ sch->qstats.drops++; -+ esfq_drop(sch); -+ return NET_XMIT_CN; -+} -+ -+ -+ -+ -+static struct sk_buff * -+esfq_dequeue(struct Qdisc* sch) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ struct sk_buff *skb; -+ unsigned depth = q->depth; -+ esfq_index a, old_a; -+ -+ /* No active slots */ -+ if (q->tail == depth) -+ return NULL; -+ -+ a = old_a = q->next[q->tail]; -+ -+ /* Grab packet */ -+ skb = __skb_dequeue(&q->qs[a]); -+ esfq_dec(q, a); -+ sch->q.qlen--; -+ sch->qstats.backlog -= skb->len; -+ -+ /* Is the slot empty? */ -+ if (q->qs[a].qlen == 0) { -+ q->ht[q->hash[a]] = depth; -+ a = q->next[a]; -+ if (a == old_a) { -+ q->tail = depth; -+ return skb; -+ } -+ q->next[q->tail] = a; -+ q->allot[a] += q->quantum; -+ } else if ((q->allot[a] -= skb->len) <= 0) { -+ q->tail = a; -+ a = q->next[a]; -+ q->allot[a] += q->quantum; -+ } -+ -+ return skb; -+} -+ -+static void -+esfq_reset(struct Qdisc* sch) -+{ -+ struct sk_buff *skb; -+ -+ while ((skb = esfq_dequeue(sch)) != NULL) -+ kfree_skb(skb); -+} -+ -+static void esfq_perturbation(unsigned long arg) -+{ -+ struct Qdisc *sch = (struct Qdisc*)arg; -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ -+ q->perturbation = net_random()&0x1F; -+ -+ if (q->perturb_period) { -+ q->perturb_timer.expires = jiffies + q->perturb_period; -+ add_timer(&q->perturb_timer); -+ } -+} -+ -+static int esfq_change(struct Qdisc *sch, struct rtattr *opt) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ struct tc_esfq_qopt *ctl = RTA_DATA(opt); -+ int old_perturb = q->perturb_period; -+ -+ if (opt->rta_len < RTA_LENGTH(sizeof(*ctl))) -+ return -EINVAL; -+ -+ sch_tree_lock(sch); -+ q->quantum = ctl->quantum ? : psched_mtu(sch->dev); -+ q->perturb_period = ctl->perturb_period*HZ; -+// q->hash_divisor = ctl->divisor; -+// q->tail = q->limit = q->depth = ctl->flows; -+ -+ if (ctl->limit) -+ q->limit = min_t(u32, ctl->limit, q->depth); -+ -+ if (ctl->hash_kind) { -+ q->hash_kind = ctl->hash_kind; -+ if (q->hash_kind != TCA_SFQ_HASH_CLASSIC) -+ q->perturb_period = 0; -+ } -+ -+ // is sch_tree_lock enough to do this ? -+ while (sch->q.qlen >= q->limit-1) -+ esfq_drop(sch); -+ -+ if (old_perturb) -+ del_timer(&q->perturb_timer); -+ if (q->perturb_period) { -+ q->perturb_timer.expires = jiffies + q->perturb_period; -+ add_timer(&q->perturb_timer); -+ } else { -+ q->perturbation = 0; -+ } -+ sch_tree_unlock(sch); -+ return 0; -+} -+ -+static int esfq_init(struct Qdisc *sch, struct rtattr *opt) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ struct tc_esfq_qopt *ctl; -+ esfq_index p = ~0U/2; -+ int i; -+ -+ if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl))) -+ return -EINVAL; -+ -+ init_timer(&q->perturb_timer); -+ q->perturb_timer.data = (unsigned long)sch; -+ q->perturb_timer.function = esfq_perturbation; -+ q->perturbation = 0; -+ q->hash_kind = TCA_SFQ_HASH_CLASSIC; -+ q->max_depth = 0; -+ q->dyn_min = ~0U; /* maximum value for this type */ -+ q->dyn_max = 0; /* dyn_min/dyn_max will be set properly upon first packet */ -+ if (opt == NULL) { -+ q->quantum = psched_mtu(sch->dev); -+ q->perturb_period = 0; -+ q->hash_divisor = 1024; -+ q->tail = q->limit = q->depth = 128; -+ -+ } else { -+ ctl = RTA_DATA(opt); -+ q->quantum = ctl->quantum ? : psched_mtu(sch->dev); -+ q->perturb_period = ctl->perturb_period*HZ; -+ q->hash_divisor = ctl->divisor ? : 1024; -+ q->tail = q->limit = q->depth = ctl->flows ? : 128; -+ -+ if ( q->depth > p - 1 ) -+ return -EINVAL; -+ -+ if (ctl->limit) -+ q->limit = min_t(u32, ctl->limit, q->depth); -+ -+ if (ctl->hash_kind) { -+ q->hash_kind = ctl->hash_kind; -+ } -+ -+ if (q->perturb_period) { -+ q->perturb_timer.expires = jiffies + q->perturb_period; -+ add_timer(&q->perturb_timer); -+ } -+ } -+ -+ q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL); -+ if (!q->ht) -+ goto err_case; -+ -+ q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL); -+ if (!q->dep) -+ goto err_case; -+ q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL); -+ if (!q->next) -+ goto err_case; -+ -+ q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL); -+ if (!q->allot) -+ goto err_case; -+ q->hash = kmalloc(q->depth*sizeof(unsigned short), GFP_KERNEL); -+ if (!q->hash) -+ goto err_case; -+ q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL); -+ if (!q->qs) -+ goto err_case; -+ -+ for (i=0; i< q->hash_divisor; i++) -+ q->ht[i] = q->depth; -+ for (i=0; idepth; i++) { -+ skb_queue_head_init(&q->qs[i]); -+ q->dep[i+q->depth].next = i+q->depth; -+ q->dep[i+q->depth].prev = i+q->depth; -+ } -+ -+ for (i=0; idepth; i++) -+ esfq_link(q, i); -+ return 0; -+err_case: -+ del_timer(&q->perturb_timer); -+ if (q->ht) -+ kfree(q->ht); -+ if (q->dep) -+ kfree(q->dep); -+ if (q->next) -+ kfree(q->next); -+ if (q->allot) -+ kfree(q->allot); -+ if (q->hash) -+ kfree(q->hash); -+ if (q->qs) -+ kfree(q->qs); -+ return -ENOBUFS; -+} -+ -+static void esfq_destroy(struct Qdisc *sch) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ del_timer(&q->perturb_timer); -+ if(q->ht) -+ kfree(q->ht); -+ if(q->dep) -+ kfree(q->dep); -+ if(q->next) -+ kfree(q->next); -+ if(q->allot) -+ kfree(q->allot); -+ if(q->hash) -+ kfree(q->hash); -+ if(q->qs) -+ kfree(q->qs); -+} -+ -+static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb) -+{ -+ struct esfq_sched_data *q = qdisc_priv(sch); -+ unsigned char *b = skb->tail; -+ struct tc_esfq_qopt opt; -+ -+ opt.quantum = q->quantum; -+ opt.perturb_period = q->perturb_period/HZ; -+ -+ opt.limit = q->limit; -+ opt.divisor = q->hash_divisor; -+ opt.flows = q->depth; -+ opt.hash_kind = q->hash_kind; -+ -+ RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); -+ -+ return skb->len; -+ -+rtattr_failure: -+ skb_trim(skb, b - skb->data); -+ return -1; -+} -+ -+static struct Qdisc_ops esfq_qdisc_ops = -+{ -+ .next = NULL, -+ .cl_ops = NULL, -+ .id = "esfq", -+ .priv_size = sizeof(struct esfq_sched_data), -+ .enqueue = esfq_enqueue, -+ .dequeue = esfq_dequeue, -+ .requeue = esfq_requeue, -+ .drop = esfq_drop, -+ .init = esfq_init, -+ .reset = esfq_reset, -+ .destroy = esfq_destroy, -+ .change = NULL, /* esfq_change - needs more work */ -+ .dump = esfq_dump, -+ .owner = THIS_MODULE, -+}; -+ -+static int __init esfq_module_init(void) -+{ -+ return register_qdisc(&esfq_qdisc_ops); -+} -+static void __exit esfq_module_exit(void) -+{ -+ unregister_qdisc(&esfq_qdisc_ops); -+} -+module_init(esfq_module_init) -+module_exit(esfq_module_exit) -+MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/patches/201-multiple_default_gateways.patch b/target/linux/generic-2.6/patches/201-multiple_default_gateways.patch deleted file mode 100644 index b0acd9a6fa..0000000000 --- a/target/linux/generic-2.6/patches/201-multiple_default_gateways.patch +++ /dev/null @@ -1,360 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netfilter_ipv4/ip_nat.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/netfilter_ipv4/ip_nat.h -+++ linux-2.6.21.7/include/linux/netfilter_ipv4/ip_nat.h -@@ -64,6 +64,13 @@ struct ip_nat_info - - struct ip_conntrack; - -+/* Call input routing for SNAT-ed traffic */ -+extern unsigned int ip_nat_route_input(unsigned int hooknum, -+ struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ int (*okfn)(struct sk_buff *)); -+ - /* Set up the info structure to map into this range. */ - extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack, - const struct ip_nat_range *range, -Index: linux-2.6.21.7/include/linux/rtnetlink.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/rtnetlink.h -+++ linux-2.6.21.7/include/linux/rtnetlink.h -@@ -293,6 +293,8 @@ struct rtnexthop - #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ - #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ - #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ -+#define RTNH_F_SUSPECT 8 /* We don't know the real state */ -+#define RTNH_F_BADSTATE (RTNH_F_DEAD | RTNH_F_SUSPECT) - - /* Macros to handle hexthops */ - -Index: linux-2.6.21.7/include/net/flow.h -=================================================================== ---- linux-2.6.21.7.orig/include/net/flow.h -+++ linux-2.6.21.7/include/net/flow.h -@@ -19,6 +19,8 @@ struct flowi { - struct { - __be32 daddr; - __be32 saddr; -+ __u32 lsrc; -+ __u32 gw; - __u8 tos; - __u8 scope; - } ip4_u; -@@ -43,6 +45,8 @@ struct flowi { - #define fl6_flowlabel nl_u.ip6_u.flowlabel - #define fl4_dst nl_u.ip4_u.daddr - #define fl4_src nl_u.ip4_u.saddr -+#define fl4_lsrc nl_u.ip4_u.lsrc -+#define fl4_gw nl_u.ip4_u.gw - #define fl4_tos nl_u.ip4_u.tos - #define fl4_scope nl_u.ip4_u.scope - -Index: linux-2.6.21.7/net/ipv4/route.c -=================================================================== ---- linux-2.6.21.7.orig/net/ipv4/route.c -+++ linux-2.6.21.7/net/ipv4/route.c -@@ -1208,6 +1208,7 @@ void ip_rt_redirect(__be32 old_gw, __be3 - - /* Gateway is different ... */ - rt->rt_gateway = new_gw; -+ if (rt->fl.fl4_gw) rt->fl.fl4_gw = new_gw; - - /* Redirect received -> path was valid */ - dst_confirm(&rth->u.dst); -@@ -1643,6 +1644,7 @@ static int ip_route_input_mc(struct sk_b - rth->fl.fl4_tos = tos; - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; -+ rth->fl.fl4_lsrc = 0; - rth->rt_src = saddr; - #ifdef CONFIG_NET_CLS_ROUTE - rth->u.dst.tclassid = itag; -@@ -1653,6 +1655,7 @@ static int ip_route_input_mc(struct sk_b - dev_hold(rth->u.dst.dev); - rth->idev = in_dev_get(rth->u.dst.dev); - rth->fl.oif = 0; -+ rth->fl.fl4_gw = 0; - rth->rt_gateway = daddr; - rth->rt_spec_dst= spec_dst; - rth->rt_type = RTN_MULTICAST; -@@ -1716,7 +1719,7 @@ static void ip_handle_martian_source(str - static inline int __mkroute_input(struct sk_buff *skb, - struct fib_result* res, - struct in_device *in_dev, -- __be32 daddr, __be32 saddr, u32 tos, -+ __be32 daddr, __be32 saddr, u32 tos, u32 lsrc, - struct rtable **result) - { - -@@ -1751,6 +1754,7 @@ static inline int __mkroute_input(struct - flags |= RTCF_DIRECTSRC; - - if (out_dev == in_dev && err && !(flags & (RTCF_NAT | RTCF_MASQ)) && -+ !lsrc && - (IN_DEV_SHARED_MEDIA(out_dev) || - inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res)))) - flags |= RTCF_DOREDIRECT; -@@ -1788,6 +1792,7 @@ static inline int __mkroute_input(struct - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; - rth->rt_src = saddr; -+ rth->fl.fl4_lsrc = lsrc; - rth->rt_gateway = daddr; - rth->rt_iif = - rth->fl.iif = in_dev->dev->ifindex; -@@ -1795,6 +1800,7 @@ static inline int __mkroute_input(struct - dev_hold(rth->u.dst.dev); - rth->idev = in_dev_get(rth->u.dst.dev); - rth->fl.oif = 0; -+ rth->fl.fl4_gw = 0; - rth->rt_spec_dst= spec_dst; - - rth->u.dst.input = ip_forward; -@@ -1816,19 +1822,21 @@ static inline int ip_mkroute_input_def(s - struct fib_result* res, - const struct flowi *fl, - struct in_device *in_dev, -- __be32 daddr, __be32 saddr, u32 tos) -+ __be32 daddr, __be32 saddr, u32 tos, -+ u32 lsrc) - { - struct rtable* rth = NULL; - int err; - unsigned hash; - -+ fib_select_default(fl, res); - #ifdef CONFIG_IP_ROUTE_MULTIPATH -- if (res->fi && res->fi->fib_nhs > 1 && fl->oif == 0) -+ if (res->fi && res->fi->fib_nhs > 1) - fib_select_multipath(fl, res); - #endif - - /* create a routing cache entry */ -- err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth); -+ err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, lsrc, &rth); - if (err) - return err; - -@@ -1841,7 +1849,8 @@ static inline int ip_mkroute_input(struc - struct fib_result* res, - const struct flowi *fl, - struct in_device *in_dev, -- __be32 daddr, __be32 saddr, u32 tos) -+ __be32 daddr, __be32 saddr, u32 tos, -+ u32 lsrc) - { - #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED - struct rtable* rth = NULL, *rtres; -@@ -1857,7 +1866,7 @@ static inline int ip_mkroute_input(struc - /* distinguish between multipath and singlepath */ - if (hopcount < 2) - return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, -- saddr, tos); -+ saddr, tos, 0); - - /* add all alternatives to the routing cache */ - for (hop = 0; hop < hopcount; hop++) { -@@ -1869,7 +1878,7 @@ static inline int ip_mkroute_input(struc - - /* create a routing cache entry */ - err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, -- &rth); -+ 0, &rth); - if (err) - return err; - -@@ -1889,7 +1898,7 @@ static inline int ip_mkroute_input(struc - skb->dst = &rtres->u.dst; - return err; - #else /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ -- return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos); -+ return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos, lsrc); - #endif /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ - } - -@@ -1905,18 +1914,18 @@ static inline int ip_mkroute_input(struc - */ - - static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, -- u8 tos, struct net_device *dev) -+ u8 tos, struct net_device *dev, u32 lsrc) - { - struct fib_result res; - struct in_device *in_dev = in_dev_get(dev); - struct flowi fl = { .nl_u = { .ip4_u = - { .daddr = daddr, -- .saddr = saddr, -+ .saddr = lsrc ? : saddr, - .tos = tos, - .scope = RT_SCOPE_UNIVERSE, - } }, - .mark = skb->mark, -- .iif = dev->ifindex }; -+ .iif = lsrc? loopback_dev.ifindex : dev->ifindex }; - unsigned flags = 0; - u32 itag = 0; - struct rtable * rth; -@@ -1949,6 +1958,12 @@ static int ip_route_input_slow(struct sk - if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr)) - goto martian_destination; - -+ if (lsrc) { -+ if (MULTICAST(lsrc) || BADCLASS(lsrc) || -+ ZERONET(lsrc) || LOOPBACK(lsrc)) -+ goto e_inval; -+ } -+ - /* - * Now we are ready to route packet. - */ -@@ -1958,6 +1973,10 @@ static int ip_route_input_slow(struct sk - goto no_route; - } - free_res = 1; -+ if (lsrc && res.type != RTN_UNICAST && res.type != RTN_NAT) -+ goto e_inval; -+ fl.iif = dev->ifindex; -+ fl.fl4_src = saddr; - - RT_CACHE_STAT_INC(in_slow_tot); - -@@ -1982,7 +2001,7 @@ static int ip_route_input_slow(struct sk - if (res.type != RTN_UNICAST) - goto martian_destination; - -- err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); -+ err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos, lsrc); - if (err == -ENOBUFS) - goto e_nobufs; - if (err == -EINVAL) -@@ -1997,6 +2016,8 @@ out: return err; - brd_input: - if (skb->protocol != htons(ETH_P_IP)) - goto e_inval; -+ if (lsrc) -+ goto e_inval; - - if (ZERONET(saddr)) - spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); -@@ -2037,6 +2058,7 @@ local_input: - rth->u.dst.dev = &loopback_dev; - dev_hold(rth->u.dst.dev); - rth->idev = in_dev_get(rth->u.dst.dev); -+ rth->fl.fl4_gw = 0; - rth->rt_gateway = daddr; - rth->rt_spec_dst= spec_dst; - rth->u.dst.input= ip_local_deliver; -@@ -2086,8 +2108,9 @@ martian_source: - goto e_inval; - } - --int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr, -- u8 tos, struct net_device *dev) -+static inline int -+ip_route_input_cached(struct sk_buff *skb, __be32 daddr, __be32 saddr, -+ u8 tos, struct net_device *dev, u32 lsrc) - { - struct rtable * rth; - unsigned hash; -@@ -2102,6 +2125,7 @@ int ip_route_input(struct sk_buff *skb, - if (rth->fl.fl4_dst == daddr && - rth->fl.fl4_src == saddr && - rth->fl.iif == iif && -+ rth->fl.fl4_lsrc == lsrc && - rth->fl.oif == 0 && - rth->fl.mark == skb->mark && - rth->fl.fl4_tos == tos) { -@@ -2148,7 +2172,19 @@ int ip_route_input(struct sk_buff *skb, - rcu_read_unlock(); - return -EINVAL; - } -- return ip_route_input_slow(skb, daddr, saddr, tos, dev); -+ return ip_route_input_slow(skb, daddr, saddr, tos, dev, lsrc); -+} -+ -+int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, -+ u8 tos, struct net_device *dev) -+{ -+ return ip_route_input_cached(skb, daddr, saddr, tos, dev, 0); -+} -+ -+int ip_route_input_lookup(struct sk_buff *skb, u32 daddr, u32 saddr, -+ u8 tos, struct net_device *dev, u32 lsrc) -+{ -+ return ip_route_input_cached(skb, daddr, saddr, tos, dev, lsrc); - } - - static inline int __mkroute_output(struct rtable **result, -@@ -2227,6 +2263,7 @@ static inline int __mkroute_output(struc - rth->fl.fl4_tos = tos; - rth->fl.fl4_src = oldflp->fl4_src; - rth->fl.oif = oldflp->oif; -+ rth->fl.fl4_gw = oldflp->fl4_gw; - rth->fl.mark = oldflp->mark; - rth->rt_dst = fl->fl4_dst; - rth->rt_src = fl->fl4_src; -@@ -2367,6 +2404,7 @@ static int ip_route_output_slow(struct r - struct flowi fl = { .nl_u = { .ip4_u = - { .daddr = oldflp->fl4_dst, - .saddr = oldflp->fl4_src, -+ .gw = oldflp->fl4_gw, - .tos = tos & IPTOS_RT_MASK, - .scope = ((tos & RTO_ONLINK) ? - RT_SCOPE_LINK : -@@ -2470,6 +2508,7 @@ static int ip_route_output_slow(struct r - dev_out = &loopback_dev; - dev_hold(dev_out); - fl.oif = loopback_dev.ifindex; -+ fl.fl4_gw = 0; - res.type = RTN_LOCAL; - flags |= RTCF_LOCAL; - goto make_route; -@@ -2477,7 +2516,7 @@ static int ip_route_output_slow(struct r - - if (fib_lookup(&fl, &res)) { - res.fi = NULL; -- if (oldflp->oif) { -+ if (oldflp->oif && dev_out->flags & IFF_UP) { - /* Apparently, routing tables are wrong. Assume, - that the destination is on link. - -@@ -2517,6 +2556,7 @@ static int ip_route_output_slow(struct r - dev_out = &loopback_dev; - dev_hold(dev_out); - fl.oif = dev_out->ifindex; -+ fl.fl4_gw = 0; - if (res.fi) - fib_info_put(res.fi); - res.fi = NULL; -@@ -2524,13 +2564,12 @@ static int ip_route_output_slow(struct r - goto make_route; - } - -+ if (res.type == RTN_UNICAST) -+ fib_select_default(&fl, &res); - #ifdef CONFIG_IP_ROUTE_MULTIPATH -- if (res.fi->fib_nhs > 1 && fl.oif == 0) -+ if (res.fi->fib_nhs > 1) - fib_select_multipath(&fl, &res); -- else - #endif -- if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif) -- fib_select_default(&fl, &res); - - if (!fl.fl4_src) - fl.fl4_src = FIB_RES_PREFSRC(res); -@@ -2567,6 +2606,7 @@ int __ip_route_output_key(struct rtable - rth->fl.fl4_src == flp->fl4_src && - rth->fl.iif == 0 && - rth->fl.oif == flp->oif && -+ rth->fl.fl4_gw == flp->fl4_gw && - rth->fl.mark == flp->mark && - !((rth->fl.fl4_tos ^ flp->fl4_tos) & - (IPTOS_RT_MASK | RTO_ONLINK))) { -@@ -3199,3 +3239,4 @@ int __init ip_rt_init(void) - EXPORT_SYMBOL(__ip_select_ident); - EXPORT_SYMBOL(ip_route_input); - EXPORT_SYMBOL(ip_route_output_key); -+EXPORT_SYMBOL(ip_route_input_lookup); diff --git a/target/linux/generic-2.6/patches/202-mips-freestanding.patch b/target/linux/generic-2.6/patches/202-mips-freestanding.patch deleted file mode 100644 index 51a96a40ad..0000000000 --- a/target/linux/generic-2.6/patches/202-mips-freestanding.patch +++ /dev/null @@ -1,14 +0,0 @@ -Index: linux-2.6.21.7/arch/mips/Makefile -=================================================================== ---- linux-2.6.21.7.orig/arch/mips/Makefile -+++ linux-2.6.21.7/arch/mips/Makefile -@@ -605,6 +605,9 @@ core-$(CONFIG_TOSHIBA_RBTX4938) += arch/ - core-$(CONFIG_TOSHIBA_RBTX4938) += arch/mips/tx4938/common/ - load-$(CONFIG_TOSHIBA_RBTX4938) += 0xffffffff80100000 - -+# temporary until string.h is fixed -+cflags-y += -ffreestanding -+ - cflags-y += -Iinclude/asm-mips/mach-generic - drivers-$(CONFIG_PCI) += arch/mips/pci/ - diff --git a/target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch b/target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch deleted file mode 100644 index 5c301b47d2..0000000000 --- a/target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch +++ /dev/null @@ -1,60 +0,0 @@ -Index: linux-2.6.21.7/fs/jffs2/build.c -=================================================================== ---- linux-2.6.21.7.orig/fs/jffs2/build.c -+++ linux-2.6.21.7/fs/jffs2/build.c -@@ -107,6 +107,17 @@ static int jffs2_build_filesystem(struct - dbg_fsbuild("scanned flash completely\n"); - jffs2_dbg_dump_block_lists_nolock(c); - -+ if (c->flags & (1 << 7)) { -+ printk("%s(): unlocking the mtd device... ", __func__); -+ if (c->mtd->unlock) -+ c->mtd->unlock(c->mtd, 0, c->mtd->size); -+ printk("done.\n"); -+ -+ printk("%s(): erasing all blocks after the end marker... ", __func__); -+ jffs2_erase_pending_blocks(c, -1); -+ printk("done.\n"); -+ } -+ - dbg_fsbuild("pass 1 starting\n"); - c->flags |= JFFS2_SB_FLAG_BUILDING; - /* Now scan the directory tree, increasing nlink according to every dirent found. */ -Index: linux-2.6.21.7/fs/jffs2/scan.c -=================================================================== ---- linux-2.6.21.7.orig/fs/jffs2/scan.c -+++ linux-2.6.21.7/fs/jffs2/scan.c -@@ -143,9 +143,12 @@ int jffs2_scan_medium(struct jffs2_sb_in - - /* reset summary info for next eraseblock scan */ - jffs2_sum_reset_collected(s); -- -- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), -- buf_size, s); -+ -+ if (c->flags & (1 << 7)) -+ ret = BLK_STATE_ALLFF; -+ else -+ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), -+ buf_size, s); - - if (ret < 0) - goto out; -@@ -546,6 +549,17 @@ static int jffs2_scan_eraseblock (struct - return err; - } - -+ if ((buf[0] == 0xde) && -+ (buf[1] == 0xad) && -+ (buf[2] == 0xc0) && -+ (buf[3] == 0xde)) { -+ /* end of filesystem. erase everything after this point */ -+ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); -+ c->flags |= (1 << 7); -+ -+ return BLK_STATE_ALLFF; -+ } -+ - /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ - ofs = 0; - diff --git a/target/linux/generic-2.6/patches/207-powerpc_asm_segment_h.patch b/target/linux/generic-2.6/patches/207-powerpc_asm_segment_h.patch deleted file mode 100644 index 963ecd182b..0000000000 --- a/target/linux/generic-2.6/patches/207-powerpc_asm_segment_h.patch +++ /dev/null @@ -1,11 +0,0 @@ -Index: linux-2.6.21.7/include/asm-powerpc/segment.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/include/asm-powerpc/segment.h -@@ -0,0 +1,6 @@ -+#ifndef _ASM_SEGMENT_H -+#define _ASM_SEGMENT_H -+ -+/* Only here because we have some old header files that expect it.. */ -+ -+#endif /* _ASM_SEGMENT_H */ diff --git a/target/linux/generic-2.6/patches/208-rtl8110sb_fix.patch b/target/linux/generic-2.6/patches/208-rtl8110sb_fix.patch deleted file mode 100644 index 2b8cebe9df..0000000000 --- a/target/linux/generic-2.6/patches/208-rtl8110sb_fix.patch +++ /dev/null @@ -1,26 +0,0 @@ -Index: linux-2.6.21.7/drivers/net/r8169.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/r8169.c -+++ linux-2.6.21.7/drivers/net/r8169.c -@@ -494,7 +494,7 @@ static int rtl8169_poll(struct net_devic - #endif - - static const u16 rtl8169_intr_mask = -- SYSErr | LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; -+ LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; - static const u16 rtl8169_napi_event = - RxOK | RxOverflow | RxFIFOOver | TxOK | TxErr; - static const unsigned int rtl8169_rx_config = -@@ -2652,10 +2652,12 @@ rtl8169_interrupt(int irq, void *dev_ins - if (!(status & rtl8169_intr_mask)) - break; - -+#if 0 - if (unlikely(status & SYSErr)) { - rtl8169_pcierr_interrupt(dev); - break; - } -+#endif - - if (status & LinkChg) - rtl8169_check_link_status(dev, tp, ioaddr); diff --git a/target/linux/generic-2.6/patches/209-mini_fo.patch b/target/linux/generic-2.6/patches/209-mini_fo.patch deleted file mode 100644 index 10cabec40c..0000000000 --- a/target/linux/generic-2.6/patches/209-mini_fo.patch +++ /dev/null @@ -1,7817 +0,0 @@ -Index: linux-2.6.21.7/fs/Kconfig -=================================================================== ---- linux-2.6.21.7.orig/fs/Kconfig -+++ linux-2.6.21.7/fs/Kconfig -@@ -461,6 +461,9 @@ config OCFS2_DEBUG_MASKLOG - This option will enlarge your kernel, but it allows debugging of - ocfs2 filesystem issues. - -+config MINI_FO -+ tristate "Mini fanout overlay filesystem" -+ - config MINIX_FS - tristate "Minix fs support" - help -Index: linux-2.6.21.7/fs/Makefile -=================================================================== ---- linux-2.6.21.7.orig/fs/Makefile -+++ linux-2.6.21.7/fs/Makefile -@@ -72,6 +72,7 @@ obj-$(CONFIG_SQUASHFS) += squashfs/ - obj-$(CONFIG_RAMFS) += ramfs/ - obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ - obj-$(CONFIG_CODA_FS) += coda/ -+obj-$(CONFIG_MINI_FO) += mini_fo/ - obj-$(CONFIG_MINIX_FS) += minix/ - obj-$(CONFIG_FAT_FS) += fat/ - obj-$(CONFIG_MSDOS_FS) += msdos/ -Index: linux-2.6.21.7/fs/mini_fo/aux.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/aux.c -@@ -0,0 +1,580 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+/* check if file exists in storage */ -+int exists_in_storage(dentry_t *dentry) -+{ -+ check_mini_fo_dentry(dentry); -+ if(dtost(dentry) == MODIFIED || dtost(dentry) == CREATED || dtost(dentry) == DEL_REWRITTEN) -+ return 1; -+ return 0; -+} -+ -+/* check if dentry is in an existing state */ -+int is_mini_fo_existant(dentry_t *dentry) -+{ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) == DELETED || dtost(dentry) == NON_EXISTANT) -+ return 0; -+ else -+ return 1; -+} -+ -+/* -+ * This function will create a negative storage dentry for -+ * dentry, what is required for many create like options. -+ * It will create the storage structure if necessary. -+ */ -+int get_neg_sto_dentry(dentry_t *dentry) -+{ -+ int err = 0; -+ unsigned int len; -+ const unsigned char *name; -+ -+ if(!dentry || -+ !dtopd(dentry) || -+ !(dtost(dentry) == UNMODIFIED || -+ dtost(dentry) == NON_EXISTANT || -+ dtost(dentry) == DELETED)) { -+ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: invalid dentry passed.\n"); -+ err = -1; -+ goto out; -+ } -+ /* Have we got a neg. dentry already? */ -+ if(dtohd2(dentry)) { -+ err = 0; -+ goto out; -+ } -+ if(dtost(dentry->d_parent) == UNMODIFIED) { -+ /* build sto struct */ -+ err = build_sto_structure(dentry->d_parent->d_parent, dentry->d_parent); -+ if(err || -+ dtost(dentry->d_parent) != MODIFIED) { -+ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: ERROR building sto structure.\n"); -+ err = -1; -+ goto out; -+ } -+ } -+ -+ len = dentry->d_name.len; -+ name = dentry->d_name.name; -+ -+ dtohd2(dentry) = -+ lookup_one_len(name, dtohd2(dentry->d_parent), len); -+ -+ out: -+ return err; -+} -+ -+int check_mini_fo_dentry(dentry_t *dentry) -+{ -+ ASSERT(dentry != NULL); -+ ASSERT(dtopd(dentry) != NULL); -+ ASSERT((dtohd(dentry) != NULL) || (dtohd2(dentry) != NULL)); -+ -+/* if(dtost(dentry) == MODIFIED) { */ -+/* ASSERT(dentry->d_inode != NULL); */ -+/* ASSERT(dtohd(dentry) != NULL); */ -+/* ASSERT(dtohd(dentry)->d_inode != NULL); */ -+/* ASSERT(dtohd2(dentry) != NULL); */ -+/* ASSERT(dtohd2(dentry)->d_inode != NULL); */ -+/* } */ -+/* else if(dtost(dentry) == UNMODIFIED) { */ -+/* ASSERT(dentry->d_inode != NULL); */ -+/* ASSERT( */ -+/* } */ -+ return 0; -+} -+ -+int check_mini_fo_file(file_t *file) -+{ -+ ASSERT(file != NULL); -+ ASSERT(ftopd(file) != NULL); -+ ASSERT(file->f_dentry != NULL); -+ -+ /* violent checking, check depending of state and type -+ * if(S_ISDIR(file->f_dentry->d_inode->i_mode)) {} -+ */ -+ ASSERT((ftohf(file) != NULL) || (ftohf2(file) != NULL)); -+ return 0; -+} -+ -+int check_mini_fo_inode(inode_t *inode) -+{ -+ ASSERT(inode != NULL); -+ ASSERT(itopd(inode) != NULL); -+ ASSERT((itohi(inode) != NULL) || (itohi2(inode) != NULL)); -+ return 0; -+} -+ -+/* -+ * will walk a base path as provided by get_mini_fo_bpath and return -+ * the (hopefully ;-) ) positive dentry of the renamed base dir. -+ * -+ * This does some work of path_init. -+ */ -+dentry_t *bpath_walk(super_block_t *sb, char *bpath) -+{ -+ int err; -+ struct nameidata nd; -+ -+ /* be paranoid */ -+ if(!bpath || bpath[0] != '/') { -+ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid string.\n"); -+ return NULL; -+ } -+ if(!sb || !stopd(sb)) { -+ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid sb.\n"); -+ return NULL; -+ } -+ -+ /* setup nd as path_init does */ -+ nd.last_type = LAST_ROOT; /* if there are only slashes... */ -+ nd.flags = LOOKUP_FOLLOW; -+ /* fix this: how do I reach this lock? -+ * read_lock(¤t->fs->lock); */ -+ nd.mnt = mntget(stopd(sb)->hidden_mnt); -+ nd.dentry = dget(stopd(sb)->base_dir_dentry); -+ /* read_unlock(¤t->fs->lock); */ -+ -+ err = path_walk(bpath+1, &nd); -+ -+ /* validate */ -+ if (err || !nd.dentry || !nd.dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: bpath_walk: path_walk failed.\n"); -+ return NULL; -+ } -+ return nd.dentry; -+} -+ -+ -+/* returns the full path of the basefile incl. its name */ -+int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len) -+{ -+ char *buf_walker; -+ int len = 0; -+ dentry_t *sky_walker; -+ -+ if(!dentry || !dtohd(dentry)) { -+ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: invalid dentry passed.\n"); -+ return -1; -+ } -+ sky_walker = dtohd(dentry); -+ -+ do { -+ len += sky_walker->d_name.len + 1 ; /* 1 for '/' */ -+ sky_walker = sky_walker->d_parent; -+ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); -+ -+ /* 1 to oil the loop */ -+ *bpath = (char*) kmalloc(len + 1, GFP_KERNEL); -+ if(!*bpath) { -+ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: out of mem.\n"); -+ return -1; -+ } -+ buf_walker = *bpath+len; /* put it on last char */ -+ *buf_walker = '\n'; -+ sky_walker = dtohd(dentry); -+ -+ do { -+ buf_walker -= sky_walker->d_name.len; -+ strncpy(buf_walker, -+ sky_walker->d_name.name, -+ sky_walker->d_name.len); -+ *(--buf_walker) = '/'; -+ sky_walker = sky_walker->d_parent; -+ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); -+ -+ /* bpath_len doesn't count newline! */ -+ *bpath_len = len; -+ return 0; -+} -+ -+int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, -+ dentry_t *src_dentry, struct vfsmount *src_mnt) -+{ -+ void *buf; -+ mm_segment_t old_fs; -+ file_t *tgt_file; -+ file_t *src_file; -+ int bytes, len, tmp, err; -+ err = 0; -+ -+ if(!(tgt_dentry->d_inode && src_dentry->d_inode)) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, neg. dentry passed.\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ -+ dget(tgt_dentry); -+ dget(src_dentry); -+ mntget(tgt_mnt); -+ mntget(src_mnt); -+ -+ /* open file write only */ -+ tgt_file = dentry_open(tgt_dentry, tgt_mnt, 0x1); -+ if(!tgt_file || IS_ERR(tgt_file)) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening target file.\n"); -+ err = PTR_ERR(tgt_file); -+ goto out_err; -+ } -+ -+ /* open file read only */ -+ src_file = dentry_open(src_dentry, src_mnt, 0x0); -+ if(!src_file || IS_ERR(src_file)) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening source file.\n"); -+ err = PTR_ERR(src_file); -+ -+ /* close target file */ -+ fput(tgt_file); -+ goto out_err; -+ } -+ -+ /* check if the filesystem(s) support read respective write */ -+ if(!src_file->f_op->read || !tgt_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, no fs read or write support.\n"); -+ err = -EPERM; -+ goto out_close; -+ } -+ -+ /* allocate a page for transfering the data */ -+ buf = (void *) __get_free_page(GFP_KERNEL); -+ if(!buf) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, out of kernel mem.\n"); -+ goto out_err; -+ } -+ -+ tgt_file->f_pos = 0; -+ src_file->f_pos = 0; -+ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* Doing this I assume that a read operation will return a full -+ * buffer while there is still data to read, and a less than -+ * full buffer when all data has been read. -+ */ -+ bytes = len = PAGE_SIZE; -+ while(bytes == len) { -+ bytes = src_file->f_op->read(src_file, buf, len, -+ &src_file->f_pos); -+ tmp = tgt_file->f_op->write(tgt_file, buf, bytes, -+ &tgt_file->f_pos); -+ if(tmp != bytes) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR writing.\n"); -+ goto out_close_unset; -+ } -+ } -+ -+ free_page((unsigned long) buf); -+ set_fs(old_fs); -+ fput(tgt_file); -+ fput(src_file); -+ goto out; -+ -+ out_close_unset: -+ free_page((unsigned long) buf); -+ set_fs(old_fs); -+ -+ out_close: -+ fput(tgt_file); -+ fput(src_file); -+ -+ out_err: -+ dput(tgt_dentry); -+ dput(src_dentry); -+ -+ /* mk: not sure if this need to be done */ -+ mntput(tgt_mnt); -+ mntput(src_mnt); -+ -+ out: -+ return err; -+} -+ -+/* mk: -+ * ndl (no-duplicate list) stuff -+ * This is used in mini_fo_readdir, to save the storage directory contents -+ * and later when reading base, match them against the list in order -+ * to avoid duplicates. -+ */ -+ -+/* add a file specified by name and len to the ndl -+ * Return values: 0 on success, <0 on failure. -+ */ -+int ndl_add_entry(struct readdir_data *rd, const char *name, int len) -+{ -+ struct ndl_entry *tmp_entry; -+ -+ tmp_entry = (struct ndl_entry *) -+ kmalloc(sizeof(struct ndl_entry), GFP_KERNEL); -+ if(!tmp_entry) { -+ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); -+ return -ENOMEM; -+ } -+ tmp_entry->name = (char*) kmalloc(len, GFP_KERNEL); -+ if(!tmp_entry->name) { -+ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); -+ return -ENOMEM; -+ } -+ strncpy(tmp_entry->name, name, len); -+ tmp_entry->len = len; -+ -+ list_add(&tmp_entry->list, &rd->ndl_list); -+ rd->ndl_size++; -+ return 0; -+} -+ -+/* delete all list entries and free memory */ -+void ndl_put_list(struct readdir_data *rd) -+{ -+ struct list_head *tmp; -+ struct ndl_entry *tmp_entry; -+ -+ if(rd->ndl_size <= 0) -+ return; -+ while(!list_empty(&rd->ndl_list)) { -+ tmp = rd->ndl_list.next; -+ list_del(tmp); -+ tmp_entry = list_entry(tmp, struct ndl_entry, list); -+ kfree(tmp_entry->name); -+ kfree(tmp_entry); -+ } -+ rd->ndl_size = 0; -+} -+ -+/* Check if a file specified by name and len is in the ndl -+ * Return value: 0 if not in list, 1 if file is found in ndl. -+ */ -+int ndl_check_entry(struct readdir_data *rd, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct ndl_entry *tmp_entry; -+ -+ if(rd->ndl_size <= 0) -+ return 0; -+ -+ list_for_each(tmp, &rd->ndl_list) { -+ tmp_entry = list_entry(tmp, struct ndl_entry, list); -+ if(tmp_entry->len != len) -+ continue; -+ if(!strncmp(tmp_entry->name, name, len)) -+ return 1; -+ } -+ return 0; -+} -+ -+/* mk: -+ * Recursive function to create corresponding directorys in the storage fs. -+ * The function will build the storage directorys up to dentry. -+ */ -+int build_sto_structure(dentry_t *dir, dentry_t *dentry) -+{ -+ int err; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ if(dentry->d_parent != dir) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [1].\n"); -+ return 1; -+ } -+ -+ if(dtost(dir) != MODIFIED) { -+ err = build_sto_structure(dir->d_parent, dentry->d_parent); -+ if(err) -+ return err; -+ } -+ -+ /* ok, coming back again. */ -+ check_mini_fo_dentry(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(!hidden_sto_dentry) { -+ /* -+ * This is the case after creating the first -+ * hidden_sto_dentry. -+ * After one negative storage_dentry, all pointers to -+ * hidden_storage dentries are set to NULL. We need to -+ * create the negative dentry before we create the storage -+ * file. -+ */ -+ unsigned int len; -+ const unsigned char *name; -+ len = dtohd(dentry)->d_name.len; -+ name = dtohd(dentry)->d_name.name; -+ hidden_sto_dentry = lookup_one_len(name, dtohd2(dir), len); -+ dtohd2(dentry) = hidden_sto_dentry; -+ } -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ /* lets be safe */ -+ if(dtohd2(dir) != hidden_sto_dir_dentry) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [2].\n"); -+ return 1; -+ } -+ -+ /* check for errors in lock_parent */ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if(IS_ERR(hidden_sto_dir_dentry)) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: lock_parent failed.\n"); -+ return err; -+ } -+ -+ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dir->d_inode->i_mode); -+ -+ if(err) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [1].\n"); -+ /* was: unlock_dir(dir); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&dir->d_inode->i_mutex); -+#else -+ up(&dir->d_inode->i_sem); -+#endif -+ dput(dir); -+ return err; -+ } -+ -+ /* everything ok! */ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [2].\n"); -+ /* was: unlock_dir(dir); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&dir->d_inode->i_mutex); -+#else -+ up(&dir->d_inode->i_sem); -+#endif -+ dput(dir); -+ return 1; -+ } -+ -+ /* interpose the new inode and set new state */ -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ dtopd(dentry)->state = MODIFIED; -+ -+ /* initalize the wol list */ -+ itopd(dentry->d_inode)->deleted_list_size = -1; -+ itopd(dentry->d_inode)->renamed_list_size = -1; -+ meta_build_lists(dentry); -+ -+ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); -+ fist_copy_attr_timesizes(dir->d_inode, -+ hidden_sto_dir_dentry->d_inode); -+ dir->d_inode->i_nlink++; -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ return 0; -+} -+ -+ -+#if 0 /* unused */ -+ -+/* -+ * Read "len" bytes from "filename" into "buf". -+ * "buf" is in kernel space. -+ */ -+int -+mini_fo_read_file(const char *filename, void *buf, int len) -+{ -+ file_t *filp; -+ mm_segment_t oldfs; -+ int bytes; -+ /* Chroot? Maybe NULL isn't right here */ -+ filp = filp_open(filename, O_RDONLY, 0); -+ if (!filp || IS_ERR(filp)) { -+ printk("mini_fo_read_file err %d\n", (int) PTR_ERR(filp)); -+ return -1; /* or do something else */ -+ } -+ -+ if (!filp->f_op->read) -+ return -2; /* file(system) doesn't allow reads */ -+ -+ /* now read len bytes from offset 0 */ -+ filp->f_pos = 0; /* start offset */ -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ bytes = filp->f_op->read(filp, buf, len, &filp->f_pos); -+ set_fs(oldfs); -+ -+ /* close the file */ -+ fput(filp); -+ -+ return bytes; -+} -+ -+ -+ -+/* -+ * Write "len" bytes from "buf" to "filename" -+ * "buf" is in kernel space. -+ */ -+int -+mini_fo_write_file(const char *filename, void *buf, int len) -+{ -+ file_t *filp; -+ mm_segment_t oldfs; -+ int bytes; -+ /* Chroot? Maybe NULL isn't right here */ -+ filp = filp_open(filename, O_RDWR|O_CREAT, 0640); -+ if (!filp || IS_ERR(filp)) { -+ printk("mini_fo_write_file err %d\n", (int) PTR_ERR(filp)); -+ return -1; /* or do something else */ -+ } -+ -+ if (!filp->f_op->write) -+ return -2; /* file(system) doesn't allow writes */ -+ -+ /* now write len bytes from offset 0 */ -+ filp->f_pos = 0; /* start offset */ -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ bytes = filp->f_op->write(filp, buf, len, &filp->f_pos); -+ set_fs(oldfs); -+ -+ /* close the file */ -+ fput(filp); -+ -+ return bytes; -+} -+ -+#endif /* unused */ -+ -Index: linux-2.6.21.7/fs/mini_fo/ChangeLog -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/ChangeLog -@@ -0,0 +1,281 @@ -+2006-01-24 Markus Klotzbuecher -+ -+ * Add tons of ugly ifdefs to Ed L. Cashin's mutex patch to -+ retain backwards compatibility. -+ -+2006-01-24 Ed L. Cashin -+ -+ * Support for the new mutex infrastructure -+ (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) -+ -+2005-10-15 Markus Klotzbuecher -+ -+ * Bugfix for a serious memory leak in mini_fo_follow_link. -+ -+2005-09-21 Markus Klotzbuecher -+ -+ * new release 0.6.1 -+ -+ * fix of a compiler warning due to changes in 2.6.13 -+ -+2005-09-21 Klaus Wenninger -+ -+ * file.c: readdir: fix for a bug that caused directory entries -+ to show up twice when using storage filesystems such as -+ minixfs or pramfs. -+ -+2005-06-30 Eric Lammerts -+ -+ * fix for an oops when overwriting a binary thats beeing -+ executed. -+ -+2005-06-09 -+ -+ * Renamed overlay to mini_fo-overlay. -+ -+ * Added mini_fo-merge script to allow merging of storage and base -+ after making modifications. -+ -+2005-05-22 root -+ -+ * Added overlay script that allows to easily mount mini_fo ontop -+ of a given base directory -+ -+2005-05-10 -+ -+ * inode.c: xattr functions return -EOPNOSUPP instead of -+ -ENOSUPP, what confuses "ls -l" -+ -+ * Changed license from LGPL to GPL. -+ -+2005-05-08 root -+ -+ * Makefile: clean it up and added make install and make -+ uninstall. -+ -+2005-05-06 -+ -+ * merged devel branch back to main. [v0-6-0-pre3] -+ -+ * removed unused files print.c and fist_ioctl. [devel-0-0-18] -+ -+ * ioctl: removed fist_ioctl stuff, that is not needed for -+ now. -+ -+2005-05-03 -+ -+ * file.c: simplified mini_fo_open and mini_fo_setattr using -+ new state changing functions. [devel-0-0-17] -+ -+ * inode.c: Fixed getattr state bug (see below) in 2.4 function -+ mini_fo_inode revalidate. -+ -+ * inode.c: found an other bug in mini_fo_getattr. States are not -+ reliable in this function, as a file can be opened, unlinked and -+ the getattr function called. This results in a deleted dentry -+ with an inode. Fix is to ignore states and simply use the inode -+ available. -+ -+2005-04-29 -+ -+ * file.c: Bugfix and cleanup in fasync and fsync. [devel-0-0-16] -+ -+ * file.c: do not use mini_fo_lock so the generic version is -+ used (I guess). -+ -+ * inode.c: getattr, never call getattr on lower files, as this -+ will cause the inum to change. -+ -+ * inode.c: rename_reg_file renamed to rename_nondir, as it -+ doesn't matter as long it't not a dir. Removed all -+ rename_dev_file etc. -+ -+ * tagged as devel-0-0-15 -+ -+ * inode.c: added support for chosing support for extended -+ attrs at compile time by XATTR define in mini_fo.h . -+ -+ * inode.c: fixed mini_fo_getattr to use mini_fo inode and not -+ lower again, what avoids inode number changes that confused -+ rm again. This is the proper solution. -+ -+2005-04-24 -+ -+ * all files: updated Copyright notive to 2005. [devel-0-0-14] -+ -+ * inode.c: fixed mini_fo_getattr to not change the inode -+ number, even if lower files change. -+ -+ * super.c: fixed a bug that caused deleted base file to show -+ up suddenly after some time, or after creating a special -+ file. The problem was that after some time or after special -+ file creating sync_sb_inodes is called by the vfs, that -+ called our mini_fo_put_inode. There was (wrongly) called -+ __meta_put_lists, that nuked the lists, although the inode -+ was going to continue its life. Moving __meta_put_lists to -+ mini_fo_clear_inode, where an inode is really destroyed, -+ solved the problem. -+ -+ -+2005-04-23 -+ -+ * state.c, aux.c: more cleaning up and -+ simplifications. [devel-0-0-13] -+ -+ * inode.c: implemented mini_fo_getattr, that was required for -+ 2.6 because inode_revalidate has been remove there, and the -+ old "du" bug returned. -+ -+ -+2005-04-20 -+ -+ * aux.c: get_neg_sto_dentry(): allow to be called for dentries -+ in state UNMODIFIED, NON_EXISTANT _and_ DELETED. -+ -+2005-04-19 -+ -+ * Fixed a bug under 2.6 that caused files deleted via mini_fo -+ not to be deleted properly and therefore the fs filled up -+ untill no memory was left. [devel-0-0-12] -+ -+ * Added basic hard link support. This means that creating -+ hardlinks will work, but existing ones will be treated as -+ individual files. [devel-0-0-11] -+ -+2005-04-17 -+ -+ * Bugfixes -+ -+2005-04-13 root -+ -+ * Added file state.c for the state transition -+ functions. Doesn't work very well yet, though... -+ -+2005-04-12 -+ -+ * Porting to 2.6 started, which is easier than expected, also -+ due to Olivier previous work. -+ -+2005-04-08 -+ -+ * Fixed the bug that caused du to return invalid sizes of -+ directory trees. The problem was that -+ mini_fo_inode_revalidate didn't always copy the attributes -+ from the base inode properly. -+ -+2005-04-01 Markus Klotzbuecher -+ -+ * Merged devel branch back to main trunk and updated the -+ RELEASE notes. This will be 0-6-0-pre1. -+ -+2005-03-31 Markus Klotzbuecher -+ -+ * Fixed some bugs in rename_reg_file, that only showed up in -+ the kernel compile test. Kernel compiles cleanly ontop of -+ mini_fo, now also make mrproper etc. work. Seems pretty stable. -+ -+2005-03-28 Markus Klotzbuecher -+ -+ * Many, many directory renaming bugfixes and a lot of other -+ cleanup. Dir renaming seems to work relatively stable. -+ -+2005-03-22 Markus Klotzbuecher -+ -+ * Finished implementing lightweight directory renaming. Some -+ basic testing indicates it works fine. -+ Next is to implement testcases for the testsuite and confirm -+ everything is really working ok. -+ -+2005-03-18 Markus Klotzbuecher -+ -+ * Finished implementing meta.c stuff required for directory -+ renaming. -+ -+2005-03-17 Markus Klotzbuecher -+ -+ * Fixed all compile warnings + an extremly old bug that -+ somehow crept in while reworking the wol stuff to the META -+ system. Turning on -Werror again... :-) -+ -+ * Fixed some bugs in the new rename_reg_file function. -+ -+ * Rewrote mini_fo rename and split it into several -+ subfunctions, that handle the different types -+ seperately. Rewrote the regular file function aswell, as it -+ was implemented somewhat inefficient. -+ -+2005-03-16 Markus Klotzbuecher -+ -+ * Implemented new META subsystem, removed old WOL stuff in favor -+ if it. -+ -+ * After some basic testing everything seems ok... -+ -+2005-03-11 Markus Klotzbuecher -+ -+ * Renaming a non regular file caused trouble because I always -+ tried to copy the contents. Now I only do this for regular -+ files. mini_fo_rename still isn't implemented properly, renaming -+ of device files, symlinks etc. results in a empty regular file -+ instead of the proper type. -+ -+ * Directory renaming suddenly works! What a surprise! I guess -+ this is because renaming is implemented as making a copy and -+ removing the original. Still this might not work -+ everywhere... -+ -+2005-03-09 Markus Klotzbuecher -+ -+ * Bugfix, when a mini_fo directory that exists in storage -+ (state: MODIFIED, CREATED and DEL_REWRITTEN) is deleted, a -+ possibly existing WOL file contained in it needs to be -+ deleted too. -+ -+ * Starting cleanup: defined state names in order to get rid of -+ the state numbers. -+ -+2005-03-08 Markus Klotzbuecher -+ -+ * Makefile fix, fist_ioctl was built against wrong sources if ARCH=um -+ -+ * Fixed a bug in dentry.c, mini_fo_d_hash. In state 4 = -+ DEL_REWRITTEN the hash was calculated from the base dentry, -+ which was wrong and and caused assertions in -+ __mini_fo_hidden_dentry to fail. -+ -+2005-02-21 -+ -+ * Implemented directory deleting (inode.c) -+ -+ * main.c: made mini_fo_parse_options a little more robust. -+ -+2004-12-22 -+ -+ * Makefile cleanup and uml stuff, removed unneccessary files -+ -+ * Created a new and hopefully more informative README -+ -+ * CHANGELOG: created a new CHANGELOG and added old entries reversely -+ -+ -+2004-10-24 Gleb Natapov -+ -+ * Fix: owner and group where not correctly copied from base to -+ storage. -+ -+ -+2004-10-05 Gleb Natapov -+ -+ * Implementation of fsync, fasync and lock mini_fo functions. -+ -+ -+2004-09-29 Bob Lee -+ -+ * Fix of a serious pointer bug -+ -+ -+2004-09-28 Gleb Natapov -+ -+ * Implementation of mini_fo_mknod and mini_fo_rename, support -+ for device files. -+ -Index: linux-2.6.21.7/fs/mini_fo/dentry.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/dentry.c -@@ -0,0 +1,244 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+/* -+ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. -+ */ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_d_revalidate(dentry_t *dentry, struct nameidata *nd) -+#else -+mini_fo_d_revalidate(dentry_t *dentry, int flags) -+#endif -+{ -+ int err1 = 1; /* valid = 1, invalid = 0 */ -+ int err2 = 1; -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ -+ check_mini_fo_dentry(dentry); -+ -+ hidden_dentry = dtohd(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(hidden_dentry && -+ hidden_dentry->d_op && -+ hidden_dentry->d_op->d_revalidate) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, nd); -+#else -+ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags); -+#endif -+ } -+ if(hidden_sto_dentry && -+ hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_revalidate) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, -+ nd); -+#else -+ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, -+ flags); -+#endif -+ } -+ -+ /* mk: if one of the lower level dentries are valid, -+ * the mini_fo dentry is too. -+ */ -+ return (err1 || err2); -+} -+ -+ -+STATIC int -+mini_fo_d_hash(dentry_t *dentry, qstr_t *name) -+{ -+ int err = 0; -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ /* hidden_dentry = mini_fo_hidden_dentry(dentry); -+ * hidden_sto_dentry = mini_fo_hidden_sto_dentry(dentry); */ -+ -+ /* state 1, 3, 4, 5: build the hash for the storage dentry */ -+ if((dtopd(dentry)->state == MODIFIED) || -+ (dtopd(dentry)->state == CREATED) || -+ (dtopd(dentry)->state == DEL_REWRITTEN) || -+ (dtopd(dentry)->state == DELETED)) { -+ hidden_sto_dentry = dtohd2(dentry); -+ if(hidden_sto_dentry && -+ hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_hash) { -+ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); -+ } -+ goto out; -+ } -+ /* state 2: build the hash for the base dentry */ -+ if(dtopd(dentry)->state == UNMODIFIED) { -+ hidden_dentry = dtohd(dentry); -+ if(hidden_dentry && -+ hidden_dentry->d_op && -+ hidden_dentry->d_op->d_hash) { -+ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); -+ } -+ goto out; -+ } -+ /* state 6: build hash for the dentry that exists */ -+ if(dtopd(dentry)->state == NON_EXISTANT) { -+ hidden_sto_dentry = dtohd2(dentry); -+ if(hidden_sto_dentry && -+ hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_hash) { -+ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); -+ goto out; -+ } -+ hidden_dentry = dtohd(dentry); -+ if(hidden_dentry && -+ hidden_dentry->d_op && -+ hidden_dentry->d_op->d_hash) { -+ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); -+ goto out; -+ } -+ } -+ -+ printk(KERN_CRIT "mini_fo: d_hash: invalid state detected.\n"); -+ -+ out: -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_d_compare(dentry_t *dentry, qstr_t *a, qstr_t *b) -+{ -+ int err; -+ dentry_t *hidden_dentry=NULL; -+ -+ /* hidden_dentry = mini_fo_hidden_dentry(dentry); */ -+ if(dtohd2(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else if(dtohd(dentry)) -+ hidden_dentry = dtohd(dentry); -+ -+ if (hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_compare) { -+ err = hidden_dentry->d_op->d_compare(hidden_dentry, a, b); -+ } else { -+ err = ((a->len != b->len) || memcmp(a->name, b->name, b->len)); -+ } -+ -+ return err; -+} -+ -+ -+int -+mini_fo_d_delete(dentry_t *dentry) -+{ -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ int err = 0; -+ -+ /* this could be a negative dentry, so check first */ -+ if (!dtopd(dentry)) { -+ printk(KERN_CRIT "mini_fo_d_delete: negative dentry passed.\n"); -+ goto out; -+ } -+ hidden_dentry = dtohd(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(hidden_dentry) { -+ if(hidden_dentry->d_op && -+ hidden_dentry->d_op->d_delete) { -+ err = hidden_dentry->d_op->d_delete(hidden_dentry); -+ } -+ } -+ if(hidden_sto_dentry) { -+ if(hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_delete) { -+ err = hidden_sto_dentry->d_op->d_delete(hidden_sto_dentry); -+ } -+ } -+ -+ out: -+ return err; -+} -+ -+ -+void -+mini_fo_d_release(dentry_t *dentry) -+{ -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ -+ /* this could be a negative dentry, so check first */ -+ if (!dtopd(dentry)) { -+ printk(KERN_CRIT "mini_fo_d_release: no private data.\n"); -+ goto out; -+ } -+ hidden_dentry = dtohd(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(hidden_dentry) { -+ /* decrement hidden dentry's counter and free its inode */ -+ dput(hidden_dentry); -+ } -+ if(hidden_sto_dentry) { -+ /* decrement hidden dentry's counter and free its inode */ -+ dput(hidden_sto_dentry); -+ } -+ -+ /* free private data (mini_fo_dentry_info) here */ -+ kfree(dtopd(dentry)); -+ __dtopd(dentry) = NULL; /* just to be safe */ -+ out: -+ return; -+} -+ -+ -+/* -+ * we don't really need mini_fo_d_iput, because dentry_iput will call iput() if -+ * mini_fo_d_iput is not defined. We left this implemented for ease of -+ * tracing/debugging. -+ */ -+void -+mini_fo_d_iput(dentry_t *dentry, inode_t *inode) -+{ -+ iput(inode); -+} -+ -+ -+struct dentry_operations mini_fo_dops = { -+ d_revalidate: mini_fo_d_revalidate, -+ d_hash: mini_fo_d_hash, -+ d_compare: mini_fo_d_compare, -+ d_release: mini_fo_d_release, -+ d_delete: mini_fo_d_delete, -+ d_iput: mini_fo_d_iput, -+}; -Index: linux-2.6.21.7/fs/mini_fo/file.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/file.c -@@ -0,0 +1,713 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) -+ -+/******************* -+ * File Operations * -+ *******************/ -+ -+STATIC loff_t -+mini_fo_llseek(file_t *file, loff_t offset, int origin) -+{ -+ loff_t err; -+ file_t *hidden_file = NULL; -+ -+ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { -+ /* Check if trying to llseek from a directory */ -+ err = -EISDIR; -+ goto out; -+ } -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ /* always set hidden position to this one */ -+ hidden_file->f_pos = file->f_pos; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ memcpy(&(hidden_file->f_ra), -+ &(file->f_ra), -+ sizeof(struct file_ra_state)); -+#else -+ if (file->f_reada) { /* update readahead information if needed */ -+ hidden_file->f_reada = file->f_reada; -+ hidden_file->f_ramax = file->f_ramax; -+ hidden_file->f_raend = file->f_raend; -+ hidden_file->f_ralen = file->f_ralen; -+ hidden_file->f_rawin = file->f_rawin; -+ } -+#endif -+ if (hidden_file->f_op && hidden_file->f_op->llseek) -+ err = hidden_file->f_op->llseek(hidden_file, offset, origin); -+ else -+ err = generic_file_llseek(hidden_file, offset, origin); -+ -+ if (err < 0) -+ goto out; -+ -+ if (err != file->f_pos) { -+ file->f_pos = err; -+ // ION maybe this? -+ // file->f_pos = hidden_file->f_pos; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ file->f_reada = 0; -+#endif -+ file->f_version++; -+ } -+ -+ out: -+ return err; -+} -+ -+ -+/* mk: fanout capable */ -+STATIC ssize_t -+mini_fo_read(file_t *file, char *buf, size_t count, loff_t *ppos) -+{ -+ int err = -EINVAL; -+ file_t *hidden_file = NULL; -+ loff_t pos = *ppos; -+ -+ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { -+ /* Check if trying to read from a directory */ -+ /* printk(KERN_CRIT "mini_fo_read: ERROR: trying to read data from a directory.\n"); */ -+ err = -EISDIR; -+ goto out; -+ } -+ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ if (!hidden_file->f_op || !hidden_file->f_op->read) -+ goto out; -+ -+ err = hidden_file->f_op->read(hidden_file, buf, count, &pos); -+ *ppos = pos; -+ -+ if (err >= 0) { -+ /* atime should also be updated for reads of size zero or more */ -+ fist_copy_attr_atime(file->f_dentry->d_inode, -+ hidden_file->f_dentry->d_inode); -+ } -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ /* -+ * MAJOR HACK -+ * because pread() does not have any way to tell us that it is -+ * our caller, then we don't know for sure if we have to update -+ * the file positions. This hack relies on read() having passed us -+ * the "real" pointer of its struct file's f_pos field. -+ */ -+ if (ppos == &file->f_pos) -+ hidden_file->f_pos = *ppos = pos; -+ if (hidden_file->f_reada) { /* update readahead information if needed */ -+ file->f_reada = hidden_file->f_reada; -+ file->f_ramax = hidden_file->f_ramax; -+ file->f_raend = hidden_file->f_raend; -+ file->f_ralen = hidden_file->f_ralen; -+ file->f_rawin = hidden_file->f_rawin; -+ } -+#else -+ memcpy(&(file->f_ra),&(hidden_file->f_ra),sizeof(struct file_ra_state)); -+#endif -+ -+ out: -+ return err; -+} -+ -+ -+/* this mini_fo_write() does not modify data pages! */ -+STATIC ssize_t -+mini_fo_write(file_t *file, const char *buf, size_t count, loff_t *ppos) -+{ -+ int err = -EINVAL; -+ file_t *hidden_file = NULL; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ loff_t pos = *ppos; -+ -+ /* mk: fan out: */ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ /* This is bad! We have no storage file to write to. This -+ * should never happen because if a file is opened for -+ * writing, a copy should have been made earlier. -+ */ -+ printk(KERN_CRIT "mini_fo: write : ERROR, no storage file to write.\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ } -+ -+ inode = file->f_dentry->d_inode; -+ hidden_inode = itohi2(inode); -+ if(!hidden_inode) { -+ printk(KERN_CRIT "mini_fo: write: no sto inode found, not good.\n"); -+ goto out; -+ } -+ -+ if (!hidden_file->f_op || !hidden_file->f_op->write) -+ goto out; -+ -+ /* adjust for append -- seek to the end of the file */ -+ if (file->f_flags & O_APPEND) -+ pos = inode->i_size; -+ -+ err = hidden_file->f_op->write(hidden_file, buf, count, &pos); -+ -+ /* -+ * copy ctime and mtime from lower layer attributes -+ * atime is unchanged for both layers -+ */ -+ if (err >= 0) -+ fist_copy_attr_times(inode, hidden_inode); -+ -+ *ppos = pos; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ /* -+ * XXX: MAJOR HACK -+ * -+ * because pwrite() does not have any way to tell us that it is -+ * our caller, then we don't know for sure if we have to update -+ * the file positions. This hack relies on write() having passed us -+ * the "real" pointer of its struct file's f_pos field. -+ */ -+ if (ppos == &file->f_pos) -+ hidden_file->f_pos = *ppos = pos; -+#endif -+ /* update this inode's size */ -+ if (pos > inode->i_size) -+ inode->i_size = pos; -+ -+ out: -+ return err; -+} -+ -+/* Global variable to hold a file_t pointer. -+ * This serves to allow mini_fo_filldir function to know which file is -+ * beeing read, which is required for two reasons: -+ * -+ * - be able to call wol functions in order to avoid listing deleted -+ * base files. -+ * - if we're reading a directory which is in state 1, we need to -+ * maintain a list (in mini_fo_filldir) of which files allready -+ * have been copied to userspace,to detect files existing in base -+ * and storage and not list them twice. -+ */ -+filldir_t mini_fo_filldir_orig; -+file_t *mini_fo_filldir_file; -+ -+/* mainly copied from fs/readdir.c */ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, -+ u64 ino, unsigned int d_type) -+#else -+mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, -+ ino_t ino, unsigned int d_type) -+#endif -+{ -+ struct getdents_callback * buf = (struct getdents_callback *) __buf; -+ file_t* file = mini_fo_filldir_file; -+ -+ /* In theses states we filter meta files in storage (WOL) */ -+ if(file && (dtopd(file->f_dentry)->state == MODIFIED || -+ dtopd(file->f_dentry)->state == CREATED || -+ dtopd(file->f_dentry)->state == DEL_REWRITTEN)) { -+ -+ int tmp = strlen(META_FILENAME); -+ if(tmp == namlen) { -+ if(!strncmp(name, META_FILENAME, namlen)) -+ return 0; -+ } -+ } -+ -+ /* check if we are merging the contents of storage and base */ -+ if(file && dtopd(file->f_dentry)->state == MODIFIED) { -+ /* check if we are still reading storage contents, if -+ * yes, we just save the name of the file for duplicate -+ * checking later. */ -+ -+ if(!ftopd(file)->rd.sto_done) { -+ /* put file into ndl list */ -+ if(ndl_add_entry(&ftopd(file)->rd, name, namlen)) -+ printk(KERN_CRIT "mini_fo_filldir: Error adding to ndl.\n"); -+ } else { -+ /* check if file has been deleted */ -+ if(meta_check_d_entry(file->f_dentry, name, namlen)) -+ return 0; -+ -+ /* do duplicate checking */ -+ if(ndl_check_entry(&ftopd(file)->rd, name, namlen)) -+ return 0; -+ } -+ } -+ -+ return mini_fo_filldir_orig(buf, name, namlen, offset, ino, d_type); -+} -+ -+ -+STATIC int -+mini_fo_readdir(file_t *file, void *dirent, filldir_t filldir) -+{ -+ int err = 0;/* mk: ??? -ENOTDIR; */ -+ file_t *hidden_file = NULL; -+ file_t *hidden_sto_file = NULL; -+ inode_t *inode; -+ struct getdents_callback *buf; -+ int oldcount; -+ -+#if defined(FIST_FILTER_NAME) || defined(FIST_FILTER_SCA) -+ struct mini_fo_getdents_callback buf; -+#endif /* FIST_FILTER_NAME || FIST_FILTER_SCA */ -+ -+ buf = (struct getdents_callback *) dirent; -+ oldcount = buf->count; -+ inode = file->f_dentry->d_inode; -+ mini_fo_filldir_file = file; -+ mini_fo_filldir_orig = filldir; -+ -+ ftopd(file)->rd.sto_done = 0; -+ do { -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_sto_file = ftohf2(file); -+ err = vfs_readdir(hidden_sto_file, mini_fo_filldir, dirent); -+ file->f_pos = hidden_sto_file->f_pos; -+ if (err > 0) -+ fist_copy_attr_atime(inode, hidden_sto_file->f_dentry->d_inode); -+ /* not finshed yet, we'll be called again */ -+ if (buf->count != oldcount) -+ break; -+ } -+ -+ ftopd(file)->rd.sto_done = 1; -+ -+ if(ftohf(file)) { -+ hidden_file = ftohf(file); -+ err = vfs_readdir(hidden_file, mini_fo_filldir, dirent); -+ file->f_pos = hidden_file->f_pos; -+ if (err > 0) -+ fist_copy_attr_atime(inode, hidden_file->f_dentry->d_inode); -+ } -+ -+ } -+ } while (0); -+ -+ /* mk: -+ * we need to check if all the directory data has been copied to userspace, -+ * or if we will be called again by userspace to complete the operation. -+ */ -+ if(buf->count == oldcount) { -+ ndl_put_list(&ftopd(file)->rd); -+ } -+ -+ /* unset this, safe */ -+ mini_fo_filldir_file = NULL; -+ return err; -+} -+ -+ -+STATIC unsigned int -+mini_fo_poll(file_t *file, poll_table *wait) -+{ -+ unsigned int mask = DEFAULT_POLLMASK; -+ file_t *hidden_file = NULL; -+ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ if (!hidden_file->f_op || !hidden_file->f_op->poll) -+ goto out; -+ -+ mask = hidden_file->f_op->poll(hidden_file, wait); -+ -+ out: -+ return mask; -+} -+ -+/* FIST-LITE special version of mmap */ -+STATIC int -+mini_fo_mmap(file_t *file, vm_area_t *vma) -+{ -+ int err = 0; -+ file_t *hidden_file = NULL; -+ -+ /* fanout capability */ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ ASSERT(hidden_file != NULL); -+ ASSERT(hidden_file->f_op != NULL); -+ ASSERT(hidden_file->f_op->mmap != NULL); -+ -+ vma->vm_file = hidden_file; -+ err = hidden_file->f_op->mmap(hidden_file, vma); -+ get_file(hidden_file); /* make sure it doesn't get freed on us */ -+ fput(file); /* no need to keep extra ref on ours */ -+ -+ return err; -+} -+ -+ -+ -+STATIC int -+mini_fo_open(inode_t *inode, file_t *file) -+{ -+ int err = 0; -+ int hidden_flags; -+ file_t *hidden_file = NULL; -+ dentry_t *hidden_dentry = NULL; -+ -+ /* fanout stuff */ -+ file_t *hidden_sto_file = NULL; -+ dentry_t *hidden_sto_dentry = NULL; -+ -+ __ftopd(file) = -+ kmalloc(sizeof(struct mini_fo_file_info), GFP_KERNEL); -+ if (!ftopd(file)) { -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ /* init the readdir_helper structure */ -+ INIT_LIST_HEAD(&ftopd(file)->rd.ndl_list); -+ ftopd(file)->rd.ndl_size = 0; -+ -+ /* In certain paths this could stay uninitalized and cause trouble */ -+ ftohf(file) = NULL; -+ ftohf2(file) = NULL; -+ hidden_flags = file->f_flags; -+ -+ /* create storage files? */ -+ if(dtost(file->f_dentry) == UNMODIFIED) { -+ if(!IS_WRITE_FLAG(file->f_flags)) { -+ hidden_dentry = dtohd(file->f_dentry); -+ dget(hidden_dentry); -+ /* dentry_open will decrement mnt refcnt if err. -+ * otherwise fput() will do an mntput() for us upon file close. */ -+ mntget(stopd(inode->i_sb)->hidden_mnt); -+ hidden_file = dentry_open(hidden_dentry, -+ stopd(inode->i_sb)->hidden_mnt, -+ hidden_flags); -+ if (IS_ERR(hidden_file)) { -+ err = PTR_ERR(hidden_file); -+ dput(hidden_dentry); -+ goto out; -+ } -+ ftohf(file) = hidden_file; /* link two files */ -+ goto out; -+ } -+ else { -+ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { -+ err = dir_unmod_to_mod(file->f_dentry); -+ } else -+ err = nondir_unmod_to_mod(file->f_dentry, 1); -+ -+ if (err) { -+ printk("mini_fo_open: ERROR creating storage file.\n"); -+ goto out; -+ } -+ } -+ } -+ hidden_sto_dentry = dtohd2(file->f_dentry); -+ dget(hidden_sto_dentry); -+ -+ if(dtopd(file->f_dentry)->state == MODIFIED) { -+ /* Directorys are special, interpose on both lower level files */ -+ if(S_ISDIR(itohi(inode)->i_mode)) { -+ /* check for invalid file types of lower level files */ -+ if(!(S_ISDIR(itohi(inode)->i_mode) && S_ISDIR(itohi2(inode)->i_mode))) { -+ printk(KERN_CRIT "mini_fo_open: meta data corruption detected.\n"); -+ dput(hidden_sto_dentry); -+ err = -EINVAL; -+ goto out; -+ } -+ -+ /* lower level directorys are ok, open the base file */ -+ hidden_dentry = dtohd(file->f_dentry); -+ dget(hidden_dentry); -+ -+ mntget(stopd(inode->i_sb)->hidden_mnt); -+ hidden_file = dentry_open(hidden_dentry, -+ stopd(inode->i_sb)->hidden_mnt, -+ hidden_flags); -+ if (IS_ERR(hidden_file)) { -+ err = PTR_ERR(hidden_file); -+ dput(hidden_dentry); -+ dput(hidden_sto_dentry); -+ goto out; -+ } -+ ftohf(file) = hidden_file; /* link the two files */ -+ } -+ } -+ -+ if(!exists_in_storage(file->f_dentry)) { -+ printk(KERN_CRIT "mini_fo_open: invalid file state detected.\n"); -+ err = -EINVAL; -+ dput(hidden_sto_dentry); -+ -+ /* If the base file has been opened, we need to close it here */ -+ if(ftohf(file)) { -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ hidden_file->f_op->flush(hidden_file, NULL); -+#else -+ hidden_file->f_op->flush(hidden_file); -+#endif -+ dput(hidden_dentry); -+ } -+ goto out; -+ } -+ -+ /* ok, now we can safely open the storage file */ -+ mntget(stopd(inode->i_sb)->hidden_mnt2); -+ hidden_sto_file = dentry_open(hidden_sto_dentry, -+ stopd(inode->i_sb)->hidden_mnt2, -+ hidden_flags); -+ -+ /* dentry_open dputs the dentry if it fails */ -+ if (IS_ERR(hidden_sto_file)) { -+ err = PTR_ERR(hidden_sto_file); -+ /* close base file if open */ -+ if(ftohf(file)) { -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ hidden_file->f_op->flush(hidden_file, NULL); -+#else -+ hidden_file->f_op->flush(hidden_file); -+#endif -+ dput(hidden_dentry); -+ } -+ goto out; -+ } -+ ftohf2(file) = hidden_sto_file; /* link storage file */ -+ -+ out: -+ if (err < 0 && ftopd(file)) { -+ kfree(ftopd(file)); -+ } -+ return err; -+} -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_flush(file_t *file, fl_owner_t id) -+#else -+mini_fo_flush(file_t *file) -+#endif -+{ -+ int err1 = 0; /* assume ok (see open.c:close_fp) */ -+ int err2 = 0; -+ file_t *hidden_file = NULL; -+ -+ check_mini_fo_file(file); -+ -+ /* mk: we don't do any state checking here, as its not worth the time. -+ * Just flush the lower level files if they exist. -+ */ -+ if(ftopd(file) != NULL) { -+ if(ftohf(file) != NULL) { -+ hidden_file = ftohf(file); -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ err1 = hidden_file->f_op->flush(hidden_file, id); -+#else -+ err1 = hidden_file->f_op->flush(hidden_file); -+#endif -+ } -+ if(ftohf2(file) != NULL) { -+ hidden_file = ftohf2(file); -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ err2 = hidden_file->f_op->flush(hidden_file, id); -+#else -+ err2 = hidden_file->f_op->flush(hidden_file); -+#endif -+ } -+ } -+ return (err1 | err2); -+} -+ -+ -+STATIC int -+mini_fo_release(inode_t *inode, file_t *file) -+{ -+ int err = 0; -+ file_t *hidden_file = NULL; -+ -+ if (ftopd(file) != NULL) { -+ if(ftohf(file)) { -+ hidden_file = ftohf(file); -+ fput(hidden_file); -+ } -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ fput(hidden_file); -+ } -+ kfree(ftopd(file)); -+ } -+ return err; -+} -+ -+STATIC int -+mini_fo_fsync(file_t *file, dentry_t *dentry, int datasync) -+{ -+ int err1 = 0; -+ int err2 = 0; -+ file_t *hidden_file = NULL; -+ dentry_t *hidden_dentry; -+ -+ check_mini_fo_file(file); -+ -+ if ((hidden_file = ftohf(file)) != NULL) { -+ hidden_dentry = dtohd(dentry); -+ if (hidden_file->f_op && hidden_file->f_op->fsync) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ err1 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ } -+ -+ if ((hidden_file = ftohf2(file)) != NULL) { -+ hidden_dentry = dtohd2(dentry); -+ if (hidden_file->f_op && hidden_file->f_op->fsync) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ err2 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ } -+ else -+ goto err; -+ -+err: -+ return (err1 || err2); -+} -+ -+ -+STATIC int -+mini_fo_fasync(int fd, file_t *file, int flag) -+{ -+ int err1 = 0; -+ int err2 = 0; -+ -+ file_t *hidden_file = NULL; -+ -+ check_mini_fo_file(file); -+ -+ if((hidden_file = ftohf(file)) != NULL) { -+ err1 = hidden_file->f_op->fasync(fd, hidden_file, flag); -+ } -+ if((hidden_file = ftohf2(file)) != NULL) { -+ err2 = hidden_file->f_op->fasync(fd, hidden_file, flag); -+ } -+ -+ return (err1 || err2); -+} -+ -+ -+ -+struct file_operations mini_fo_dir_fops = -+ { -+ read: generic_read_dir, -+ write: mini_fo_write, -+ readdir: mini_fo_readdir, -+ poll: mini_fo_poll, -+ /* ioctl: mini_fo_ioctl, */ -+ mmap: mini_fo_mmap, -+ open: mini_fo_open, -+ flush: mini_fo_flush, -+ release: mini_fo_release, -+ fsync: mini_fo_fsync, -+ fasync: mini_fo_fasync, -+ /* not needed lock: mini_fo_lock, */ -+ /* not needed: readv */ -+ /* not needed: writev */ -+ /* not implemented: sendpage */ -+ /* not implemented: get_unmapped_area */ -+ }; -+ -+struct file_operations mini_fo_main_fops = -+ { -+ llseek: mini_fo_llseek, -+ read: mini_fo_read, -+ write: mini_fo_write, -+ readdir: mini_fo_readdir, -+ poll: mini_fo_poll, -+ /* ioctl: mini_fo_ioctl, */ -+ mmap: mini_fo_mmap, -+ open: mini_fo_open, -+ flush: mini_fo_flush, -+ release: mini_fo_release, -+ fsync: mini_fo_fsync, -+ fasync: mini_fo_fasync, -+ /* not needed: lock: mini_fo_lock, */ -+ /* not needed: readv */ -+ /* not needed: writev */ -+ /* not implemented: sendpage */ -+ /* not implemented: get_unmapped_area */ -+ }; -Index: linux-2.6.21.7/fs/mini_fo/fist.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/fist.h -@@ -0,0 +1,252 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifndef __FIST_H_ -+#define __FIST_H_ -+ -+/* -+ * KERNEL ONLY CODE: -+ */ -+#ifdef __KERNEL__ -+#include -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) -+#include -+#else -+#include -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+#ifdef CONFIG_MODVERSIONS -+# define MODVERSIONS -+# include -+#endif /* CONFIG_MODVERSIONS */ -+#endif /* KERNEL_VERSION < 2.6.0 */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+#include -+#else -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) -+#include -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#include -+#endif -+ -+#include -+ -+#include -+/* #include */ -+#include -+#include -+ -+/* -+ * MACROS: -+ */ -+ -+/* those mapped to ATTR_* were copied from linux/fs.h */ -+#define FA_MODE ATTR_MODE -+#define FA_UID ATTR_UID -+#define FA_GID ATTR_GID -+#define FA_SIZE ATTR_SIZE -+#define FA_ATIME ATTR_ATIME -+#define FA_MTIME ATTR_MTIME -+#define FA_CTIME ATTR_CTIME -+#define FA_ATIME_SET ATTR_ATIME_SET -+#define FA_MTIME_SET ATTR_MTIME_SET -+#define FA_FORCE ATTR_FORCE -+#define FA_ATTR_FLAGS ATTR_ATTR_FLAG -+ -+/* must be greater than all other ATTR_* flags! */ -+#define FA_NLINK 2048 -+#define FA_BLKSIZE 4096 -+#define FA_BLOCKS 8192 -+#define FA_TIMES (FA_ATIME|FA_MTIME|FA_CTIME) -+#define FA_ALL 0 -+ -+/* macros to manage changes between kernels */ -+#define INODE_DATA(i) (&(i)->i_data) -+ -+#define MIN(x,y) ((x < y) ? (x) : (y)) -+#define MAX(x,y) ((x > y) ? (x) : (y)) -+#define MAXPATHLEN PATH_MAX -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) -+# define lookup_one_len(a,b,c) lookup_one(a,b) -+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) */ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) -+# define generic_file_llseek default_llseek -+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) */ -+ -+#ifndef SEEK_SET -+# define SEEK_SET 0 -+#endif /* not SEEK_SET */ -+ -+#ifndef SEEK_CUR -+# define SEEK_CUR 1 -+#endif /* not SEEK_CUR */ -+ -+#ifndef SEEK_END -+# define SEEK_END 2 -+#endif /* not SEEK_END */ -+ -+#ifndef DEFAULT_POLLMASK -+# define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) -+#endif /* not DEFAULT_POLLMASK */ -+ -+/* XXX: fix this so fistgen generates kfree() code directly */ -+#define kfree_s(a,b) kfree(a) -+ -+/* -+ * TYPEDEFS: -+ */ -+typedef struct dentry dentry_t; -+typedef struct file file_t; -+typedef struct inode inode_t; -+typedef inode_t vnode_t; -+typedef struct page page_t; -+typedef struct qstr qstr_t; -+typedef struct super_block super_block_t; -+typedef super_block_t vfs_t; -+typedef struct vm_area_struct vm_area_t; -+ -+ -+/* -+ * EXTERNALS: -+ */ -+ -+#define FPPF(str,page) printk("PPF %s 0x%x/%d: Lck:%d Err:%d Ref:%d Upd:%d Other::%d:%d:%d:%d:\n", \ -+ str, \ -+ (int) page, \ -+ (int) page->index, \ -+ (PageLocked(page) ? 1 : 0), \ -+ (PageError(page) ? 1 : 0), \ -+ (PageReferenced(page) ? 1 : 0), \ -+ (Page_Uptodate(page) ? 1 : 0), \ -+ (PageDecrAfter(page) ? 1 : 0), \ -+ (PageSlab(page) ? 1 : 0), \ -+ (PageSwapCache(page) ? 1 : 0), \ -+ (PageReserved(page) ? 1 : 0) \ -+ ) -+#define EZKDBG printk("EZK %s:%d:%s\n",__FILE__,__LINE__,__FUNCTION__) -+#if 0 -+# define EZKDBG1 printk("EZK %s:%d\n",__FILE__,__LINE__) -+#else -+# define EZKDBG1 -+#endif -+ -+extern int fist_get_debug_value(void); -+extern int fist_set_debug_value(int val); -+#if 0 /* mini_fo doesn't need these */ -+extern void fist_dprint_internal(int level, char *str,...); -+extern void fist_print_dentry(char *str, const dentry_t *dentry); -+extern void fist_print_inode(char *str, const inode_t *inode); -+extern void fist_print_file(char *str, const file_t *file); -+extern void fist_print_buffer_flags(char *str, struct buffer_head *buffer); -+extern void fist_print_page_flags(char *str, page_t *page); -+extern void fist_print_page_bytes(char *str, page_t *page); -+extern void fist_print_pte_flags(char *str, const page_t *page); -+extern void fist_checkinode(inode_t *inode, char *msg); -+extern void fist_print_sb(char *str, const super_block_t *sb); -+ -+/* §$% by mk: special debug functions */ -+extern void fist_mk_print_dentry(char *str, const dentry_t *dentry); -+extern void fist_mk_print_inode(char *str, const inode_t *inode); -+ -+extern char *add_indent(void); -+extern char *del_indent(void); -+#endif/* mini_fo doesn't need these */ -+ -+ -+#define STATIC -+#define ASSERT(EX) \ -+do { \ -+ if (!(EX)) { \ -+ printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \ -+ __FILE__, __LINE__, __FUNCTION__); \ -+ (*((char *)0))=0; \ -+ } \ -+} while (0) -+/* same ASSERT, but tell me who was the caller of the function */ -+#define ASSERT2(EX) \ -+do { \ -+ if (!(EX)) { \ -+ printk(KERN_CRIT "ASSERTION FAILED (caller): %s at %s:%d (%s)\n", #EX, \ -+ file, line, func); \ -+ (*((char *)0))=0; \ -+ } \ -+} while (0) -+ -+#if 0 /* mini_fo doesn't need these */ -+#define dprintk(format, args...) printk(KERN_DEBUG format, ##args) -+#define fist_dprint(level, str, args...) fist_dprint_internal(level, KERN_DEBUG str, ## args) -+#define print_entry_location() fist_dprint(4, "%sIN: %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__) -+#define print_exit_location() fist_dprint(4, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__) -+#define print_exit_status(status) fist_dprint(4, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status) -+#define print_exit_pointer(status) \ -+do { \ -+ if (IS_ERR(status)) \ -+ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ -+ else \ -+ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: 0x%x\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ -+} while (0) -+#endif/* mini_fo doesn't need these */ -+ -+#endif /* __KERNEL__ */ -+ -+ -+/* -+ * DEFINITIONS FOR USER AND KERNEL CODE: -+ * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest -+ * are auto-generated automatically based on the user's .fist file.) -+ */ -+# define FIST_IOCTL_GET_DEBUG_VALUE _IOR(0x15, 1, int) -+# define FIST_IOCTL_SET_DEBUG_VALUE _IOW(0x15, 2, int) -+ -+#endif /* not __FIST_H_ */ -Index: linux-2.6.21.7/fs/mini_fo/inode.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/inode.c -@@ -0,0 +1,1564 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd) -+#else -+mini_fo_create(inode_t *dir, dentry_t *dentry, int mode) -+#endif -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = create_sto_reg_file(dentry, mode, nd); -+#else -+ err = create_sto_reg_file(dentry, mode); -+#endif -+ check_mini_fo_dentry(dentry); -+ return err; -+} -+ -+ -+STATIC dentry_t * -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_lookup(inode_t *dir, dentry_t *dentry, struct nameidata* nd) -+#else -+mini_fo_lookup(inode_t *dir, dentry_t *dentry) -+#endif -+{ -+ int err = 0; -+ dentry_t *hidden_dir_dentry; -+ dentry_t *hidden_dentry = NULL; -+ -+ dentry_t *hidden_sto_dir_dentry; -+ dentry_t *hidden_sto_dentry = NULL; -+ -+ /* whiteout flag */ -+ int del_flag = 0; -+ char *bpath = NULL; -+ -+ const char *name; -+ unsigned int namelen; -+ -+ /* Don't allow lookups of META-files */ -+ namelen = strlen(META_FILENAME); -+ if(namelen == dentry->d_name.len) { -+ if(!strncmp(dentry->d_name.name, META_FILENAME, namelen)) { -+ err = -ENOENT; -+ goto out; -+ } -+ } -+ -+ hidden_dir_dentry = dtohd(dentry->d_parent); -+ hidden_sto_dir_dentry = dtohd2(dentry->d_parent); -+ -+ name = dentry->d_name.name; -+ namelen = dentry->d_name.len; -+ -+ /* must initialize dentry operations */ -+ dentry->d_op = &mini_fo_dops; -+ -+ /* setup the del_flag */ -+ del_flag = __meta_check_d_entry(dir, name, namelen); -+ bpath = __meta_check_r_entry(dir, name, namelen); -+ -+ /* perform the lookups of base and storage files: -+ * -+ * This caused some serious trouble, as a lookup_one_len passing -+ * a negative dentry oopses. Solution is to only do the lookup -+ * if the dentry is positive, else we set it to NULL -+ * More trouble, who said a *_dir_dentry can't be NULL? -+ */ -+ if(bpath) { -+ /* Cross-Interposing (C), yeah! */ -+ hidden_dentry = bpath_walk(dir->i_sb, bpath); -+ if(!hidden_dentry || !hidden_dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo_lookup: bpath_walk failed.\n"); -+ err= -EINVAL; -+ goto out; -+ } -+ -+ /* this can be set up safely without fear of spaghetti -+ * interposing as it is only used for copying times */ -+ hidden_dir_dentry = hidden_dentry->d_parent; -+ kfree(bpath); -+ } -+ else if(hidden_dir_dentry && hidden_dir_dentry->d_inode) -+ hidden_dentry = -+ lookup_one_len(name, hidden_dir_dentry, namelen); -+ else -+ hidden_dentry = NULL; -+ -+ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) -+ hidden_sto_dentry = -+ lookup_one_len(name, hidden_sto_dir_dentry, namelen); -+ else -+ hidden_sto_dentry = NULL; -+ -+ /* catch error in lookup */ -+ if (IS_ERR(hidden_dentry) || IS_ERR(hidden_sto_dentry)) { -+ /* mk: we need to call dput on the dentry, whose -+ * lookup_one_len operation failed, in order to avoid -+ * unmount trouble. -+ */ -+ if(IS_ERR(hidden_dentry)) { -+ printk(KERN_CRIT "mini_fo_lookup: ERR from base dentry, lookup failed.\n"); -+ err = PTR_ERR(hidden_dentry); -+ } else { -+ dput(hidden_dentry); -+ } -+ if(IS_ERR(hidden_sto_dentry)) { -+ printk(KERN_CRIT "mini_fo_lookup: ERR from storage dentry, lookup failed.\n"); -+ err = PTR_ERR(hidden_sto_dentry); -+ } else { -+ dput(hidden_sto_dentry); -+ } -+ goto out; -+ } -+ -+ /* allocate dentry private data */ -+ __dtopd(dentry) = (struct mini_fo_dentry_info *) -+ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); -+ -+ if (!dtopd(dentry)) { -+ err = -ENOMEM; -+ goto out_dput; -+ } -+ -+ /* check for different states of the mini_fo file to be looked up. */ -+ -+ /* state 1, file has been modified */ -+ if(hidden_dentry && hidden_sto_dentry && -+ hidden_dentry->d_inode && hidden_sto_dentry->d_inode && !del_flag) { -+ -+ /* update parent directory's atime */ -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = MODIFIED; -+ dtohd(dentry) = hidden_dentry; -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ err = mini_fo_tri_interpose(hidden_dentry, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state1).\n"); -+ goto out_free; -+ } -+ goto out; -+ } -+ /* state 2, file is unmodified */ -+ if(hidden_dentry && hidden_dentry->d_inode && !del_flag) { -+ -+ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = UNMODIFIED; -+ dtohd(dentry) = hidden_dentry; -+ dtohd2(dentry) = hidden_sto_dentry; /* could be negative */ -+ -+ err = mini_fo_tri_interpose(hidden_dentry, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state2).\n"); -+ goto out_free; -+ } -+ goto out; -+ } -+ /* state 3, file has been newly created */ -+ if(hidden_sto_dentry && hidden_sto_dentry->d_inode && !del_flag) { -+ -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ dtopd(dentry)->state = CREATED; -+ dtohd(dentry) = hidden_dentry; /* could be negative */ -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ err = mini_fo_tri_interpose(hidden_dentry, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state3).\n"); -+ goto out_free; -+ } -+ goto out; -+ } -+ -+ /* state 4, file has deleted and created again. */ -+ if(hidden_dentry && hidden_sto_dentry && -+ hidden_dentry->d_inode && -+ hidden_sto_dentry->d_inode && del_flag) { -+ -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ dtopd(dentry)->state = DEL_REWRITTEN; -+ dtohd(dentry) = NULL; -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ err = mini_fo_tri_interpose(NULL, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state4).\n"); -+ goto out_free; -+ } -+ /* We will never need this dentry again, as the file has been -+ * deleted from base */ -+ dput(hidden_dentry); -+ goto out; -+ } -+ /* state 5, file has been deleted in base */ -+ if(hidden_dentry && hidden_sto_dentry && -+ hidden_dentry->d_inode && -+ !hidden_sto_dentry->d_inode && del_flag) { -+ -+ /* check which parents atime we need for updating */ -+ if(hidden_sto_dir_dentry->d_inode) -+ fist_copy_attr_atime(dir, -+ hidden_sto_dir_dentry->d_inode); -+ else -+ fist_copy_attr_atime(dir, -+ hidden_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = DELETED; -+ dtohd(dentry) = NULL; -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ /* add negative dentry to dcache to speed up lookups */ -+ d_add(dentry, NULL); -+ dput(hidden_dentry); -+ goto out; -+ } -+ /* state 6, file does not exist */ -+ if(((hidden_dentry && !hidden_dentry->d_inode) || -+ (hidden_sto_dentry && !hidden_sto_dentry->d_inode)) && !del_flag) -+ { -+ /* check which parents atime we need for updating */ -+ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ else -+ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = NON_EXISTANT; -+ dtohd(dentry) = hidden_dentry; -+ dtohd2(dentry) = hidden_sto_dentry; -+ d_add(dentry, NULL); -+ goto out; -+ } -+ -+ /* if we get to here, were in an invalid state. bad. */ -+ printk(KERN_CRIT "mini_fo_lookup: ERROR, meta data corruption detected.\n"); -+ -+ /* end state checking */ -+ out_free: -+ d_drop(dentry); /* so that our bad dentry will get destroyed */ -+ kfree(dtopd(dentry)); -+ __dtopd(dentry) = NULL; /* be safe */ -+ -+ out_dput: -+ if(hidden_dentry) -+ dput(hidden_dentry); -+ if(hidden_sto_dentry) -+ dput(hidden_sto_dentry); /* drops usage count and marks for release */ -+ -+ out: -+ /* initalize wol if file exists and is directory */ -+ if(dentry->d_inode) { -+ if(S_ISDIR(dentry->d_inode->i_mode)) { -+ itopd(dentry->d_inode)->deleted_list_size = -1; -+ itopd(dentry->d_inode)->renamed_list_size = -1; -+ meta_build_lists(dentry); -+ } -+ } -+ return ERR_PTR(err); -+} -+ -+ -+STATIC int -+mini_fo_link(dentry_t *old_dentry, inode_t *dir, dentry_t *new_dentry) -+{ -+ int err; -+ dentry_t *hidden_old_dentry; -+ dentry_t *hidden_new_dentry; -+ dentry_t *hidden_dir_dentry; -+ -+ -+ check_mini_fo_dentry(old_dentry); -+ check_mini_fo_dentry(new_dentry); -+ check_mini_fo_inode(dir); -+ -+ /* no links to directorys and existing targets target allowed */ -+ if(S_ISDIR(old_dentry->d_inode->i_mode) || -+ is_mini_fo_existant(new_dentry)) { -+ err = -EPERM; -+ goto out; -+ } -+ -+ /* bring it directly from unmod to del_rew */ -+ if(dtost(old_dentry) == UNMODIFIED) { -+ err = nondir_unmod_to_mod(old_dentry, 1); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ err = meta_add_d_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ dput(dtohd(old_dentry)); -+ dtohd(old_dentry) = NULL; -+ dtost(old_dentry) = DEL_REWRITTEN; -+ } -+ -+ err = get_neg_sto_dentry(new_dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ hidden_old_dentry = dtohd2(old_dentry); -+ hidden_new_dentry = dtohd2(new_dentry); -+ -+ dget(hidden_old_dentry); -+ dget(hidden_new_dentry); -+ -+ /* was: hidden_dir_dentry = lock_parent(hidden_new_dentry); */ -+ hidden_dir_dentry = dget(hidden_new_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = vfs_link(hidden_old_dentry, -+ hidden_dir_dentry->d_inode, -+ hidden_new_dentry); -+ if (err || !hidden_new_dentry->d_inode) -+ goto out_lock; -+ -+ dtost(new_dentry) = CREATED; -+ err = mini_fo_tri_interpose(NULL, hidden_new_dentry, new_dentry, dir->i_sb, 0); -+ if (err) -+ goto out_lock; -+ -+ fist_copy_attr_timesizes(dir, hidden_new_dentry->d_inode); -+ /* propagate number of hard-links */ -+ old_dentry->d_inode->i_nlink = itohi2(old_dentry->d_inode)->i_nlink; -+ -+ out_lock: -+ /* was: unlock_dir(hidden_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_dir_dentry); -+ -+ dput(hidden_new_dentry); -+ dput(hidden_old_dentry); -+ if (!new_dentry->d_inode) -+ d_drop(new_dentry); -+ -+ out: -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_unlink(inode_t *dir, dentry_t *dentry) -+{ -+ int err = 0; -+ -+ dget(dentry); -+ if(dtopd(dentry)->state == MODIFIED) { -+ err = nondir_mod_to_del(dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == UNMODIFIED) { -+ err = nondir_unmod_to_del(dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == CREATED) { -+ err = nondir_creat_to_del(dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == DEL_REWRITTEN) { -+ err = nondir_del_rew_to_del(dentry); -+ goto out; -+ } -+ -+ printk(KERN_CRIT "mini_fo_unlink: ERROR, invalid state detected.\n"); -+ -+ out: -+ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); -+ -+ if(!err) { -+ /* is this causing my pain? d_delete(dentry); */ -+ d_drop(dentry); -+ } -+ -+ dput(dentry); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_symlink(inode_t *dir, dentry_t *dentry, const char *symname) -+{ -+ int err=0; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ umode_t mode; -+#endif -+ -+ /* Fail if the symlink file exists */ -+ if(!(dtost(dentry) == DELETED || -+ dtost(dentry) == NON_EXISTANT)) { -+ err = -EEXIST; -+ goto out; -+ } -+ -+ err = get_neg_sto_dentry(dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ dget(hidden_sto_dentry); -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ mode = S_IALLUGO; -+ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, symname, mode); -+#else -+ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ symname); -+#endif -+ if (err || !hidden_sto_dentry->d_inode) -+ goto out_lock; -+ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } else if(dtost(dentry) == NON_EXISTANT) { -+ dtost(dentry) = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); -+ -+ out_lock: -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ dput(hidden_sto_dentry); -+ if (!dentry->d_inode) -+ d_drop(dentry); -+ out: -+ return err; -+} -+ -+STATIC int -+mini_fo_mkdir(inode_t *dir, dentry_t *dentry, int mode) -+{ -+ int err; -+ -+ err = create_sto_dir(dentry, mode); -+ -+ check_mini_fo_dentry(dentry); -+ -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_rmdir(inode_t *dir, dentry_t *dentry) -+{ -+ int err = 0; -+ -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ dentry_t *meta_dentry; -+ inode_t *hidden_sto_dir = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ check_mini_fo_inode(dir); -+ -+ dget(dentry); -+ if(dtopd(dentry)->state == MODIFIED) { -+ /* XXX: disabled, because it does not bother to check files on -+ * the original filesystem - just a hack, but better than simply -+ * removing it without testing */ -+ err = -EINVAL; -+ goto out; -+ -+ hidden_sto_dir = itohi2(dir); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was:hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* Delete an old WOL file contained in the storage dir */ -+ meta_dentry = lookup_one_len(META_FILENAME, -+ hidden_sto_dentry, -+ strlen(META_FILENAME)); -+ if(meta_dentry->d_inode) { -+ err = vfs_unlink(hidden_sto_dentry->d_inode, meta_dentry); -+ dput(meta_dentry); -+ if(!err) -+ d_delete(meta_dentry); -+ } -+ -+ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ -+ dput(dtohd(dentry)); -+ -+ dtohd(dentry) = NULL; -+ dtopd(dentry)->state = DELETED; -+ -+ /* carefull with R files */ -+ if( __meta_is_r_entry(dir, -+ dentry->d_name.name, -+ dentry->d_name.len) == 1) { -+ err = meta_remove_r_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: rmdir: meta_remove_r_entry failed.\n"); -+ goto out; -+ } -+ } -+ else { -+ /* ok, add deleted file to META */ -+ meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ } -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == UNMODIFIED) { -+ /* XXX: simply adding it to the delete list here is fscking dangerous! -+ * as a temporary hack, i will disable rmdir on unmodified directories -+ * for now. -+ */ -+ err = -EINVAL; -+ goto out; -+ -+ err = get_neg_sto_dentry(dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ /* dput base dentry, this will relase the inode and free the -+ * dentry, as we will never need it again. */ -+ dput(dtohd(dentry)); -+ dtohd(dentry) = NULL; -+ dtopd(dentry)->state = DELETED; -+ -+ /* add deleted file to META-file */ -+ meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == CREATED) { -+ hidden_sto_dir = itohi2(dir); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* Delete an old WOL file contained in the storage dir */ -+ meta_dentry = lookup_one_len(META_FILENAME, -+ hidden_sto_dentry, -+ strlen(META_FILENAME)); -+ if(meta_dentry->d_inode) { -+ /* is this necessary? dget(meta_dentry); */ -+ err = vfs_unlink(hidden_sto_dentry->d_inode, -+ meta_dentry); -+ dput(meta_dentry); -+ if(!err) -+ d_delete(meta_dentry); -+ } -+ -+ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ dtopd(dentry)->state = NON_EXISTANT; -+ -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ goto out; -+ } -+ else if(dtopd(dentry)->state == DEL_REWRITTEN) { -+ hidden_sto_dir = itohi2(dir); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* Delete an old WOL file contained in the storage dir */ -+ meta_dentry = lookup_one_len(META_FILENAME, -+ hidden_sto_dentry, -+ strlen(META_FILENAME)); -+ if(meta_dentry->d_inode) { -+ /* is this necessary? dget(meta_dentry); */ -+ err = vfs_unlink(hidden_sto_dentry->d_inode, -+ meta_dentry); -+ dput(meta_dentry); -+ if(!err) -+ d_delete(meta_dentry); -+ } -+ -+ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ dtopd(dentry)->state = DELETED; -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ goto out; -+ } -+ -+ printk(KERN_CRIT "mini_fo_rmdir: ERROR, invalid state detected.\n"); -+ -+ out: -+ if(!err) { -+ d_drop(dentry); -+ } -+ -+ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); -+ dput(dentry); -+ -+ return err; -+} -+ -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, dev_t dev) -+#else -+mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, int dev) -+#endif -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+ err = create_sto_nod(dentry, mode, dev); -+ if(err) { -+ printk(KERN_CRIT "mini_fo_mknod: creating sto nod failed.\n"); -+ err = -EINVAL; -+ } -+ -+ check_mini_fo_dentry(dentry); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_rename(inode_t *old_dir, dentry_t *old_dentry, -+ inode_t *new_dir, dentry_t *new_dentry) -+{ -+ /* dispatch */ -+ if(S_ISDIR(old_dentry->d_inode->i_mode)) -+ return rename_directory(old_dir, old_dentry, new_dir, new_dentry); -+ return rename_nondir(old_dir, old_dentry, new_dir, new_dentry); -+ -+} -+ -+int rename_directory(inode_t *old_dir, dentry_t *old_dentry, -+ inode_t *new_dir, dentry_t *new_dentry) -+{ -+ int err, bpath_len; -+ char *bpath; -+ -+ dentry_t *hidden_old_dentry; -+ dentry_t *hidden_new_dentry; -+ dentry_t *hidden_old_dir_dentry; -+ dentry_t *hidden_new_dir_dentry; -+ -+ err = 0; -+ bpath = NULL; -+ bpath_len = 0; -+ -+ /* this is a test, chuck out if it works */ -+ if(!(dtopd(new_dentry)->state == DELETED || -+ dtopd(new_dentry)->state == NON_EXISTANT)) { -+ printk(KERN_CRIT "mini_fo: rename_directory: \ -+ uh, ah, new_dentry not negative.\n"); -+ /* return -1; */ -+ } -+ -+ /* state = UNMODIFIED */ -+ if(dtopd(old_dentry)->state == UNMODIFIED) { -+ err = dir_unmod_to_mod(old_dentry); -+ if (err) -+ goto out; -+ } -+ -+ /* state = MODIFIED */ -+ if(dtopd(old_dentry)->state == MODIFIED) { -+ bpath = meta_check_r_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if(bpath) { -+ err = meta_remove_r_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: rename_directory:\ -+ meta_remove_r_entry \ -+ failed.\n"); -+ goto out; -+ } -+ err = meta_add_r_entry(new_dentry->d_parent, -+ bpath, -+ strlen(bpath), -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ kfree(bpath); -+ } -+ else {/* wol it */ -+ err = meta_add_d_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if (err) -+ goto out; -+ /* put it on rename list */ -+ err = get_mini_fo_bpath(old_dentry, -+ &bpath, -+ &bpath_len); -+ if (err) -+ goto out; -+ err = meta_add_r_entry(new_dentry->d_parent, -+ bpath, bpath_len, -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ if (err) -+ goto out; -+ } -+ /* no state change, MODIFIED stays MODIFIED */ -+ } -+ /* state = CREATED */ -+ if(dtopd(old_dentry)->state == CREATED || -+ dtopd(old_dentry)->state == DEL_REWRITTEN) { -+ if(dtohd(old_dentry)) -+ dput(dtohd(old_dentry)); -+ -+ if(dtopd(new_dentry)->state == DELETED) { -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ dtohd(old_dentry) = NULL; -+ } -+ else if(dtopd(new_dentry)->state == NON_EXISTANT) { -+ dtopd(old_dentry)->state = CREATED; -+ /* steal new dentry's neg. base dentry */ -+ dtohd(old_dentry) = dtohd(new_dentry); -+ dtohd(new_dentry) = NULL; -+ } -+ } -+ if(dtopd(new_dentry)->state == UNMODIFIED || -+ dtopd(new_dentry)->state == NON_EXISTANT) { -+ err = get_neg_sto_dentry(new_dentry); -+ if(err) -+ goto out; -+ } -+ -+ /* now move sto file */ -+ hidden_old_dentry = dtohd2(old_dentry); -+ hidden_new_dentry = dtohd2(new_dentry); -+ -+ dget(hidden_old_dentry); -+ dget(hidden_new_dentry); -+ -+ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); -+ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); -+ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ -+ err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, -+ hidden_new_dir_dentry->d_inode, hidden_new_dentry); -+ if(err) -+ goto out_lock; -+ -+ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); -+ if (new_dir != old_dir) -+ fist_copy_attr_all(old_dir, -+ hidden_old_dir_dentry->d_inode); -+ -+ out_lock: -+ /* double_unlock will dput the new/old parent dentries -+ * whose refcnts were incremented via get_parent above. */ -+ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ dput(hidden_new_dentry); -+ dput(hidden_old_dentry); -+ -+ out: -+ return err; -+} -+ -+int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, -+ inode_t *new_dir, dentry_t *new_dentry) -+{ -+ int err=0; -+ -+ check_mini_fo_dentry(old_dentry); -+ check_mini_fo_dentry(new_dentry); -+ check_mini_fo_inode(old_dir); -+ check_mini_fo_inode(new_dir); -+ -+ /* state: UNMODIFIED */ -+ if(dtost(old_dentry) == UNMODIFIED) { -+ err = nondir_unmod_to_mod(old_dentry, 1); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ } -+ -+ /* the easy states */ -+ if(exists_in_storage(old_dentry)) { -+ -+ dentry_t *hidden_old_dentry; -+ dentry_t *hidden_new_dentry; -+ dentry_t *hidden_old_dir_dentry; -+ dentry_t *hidden_new_dir_dentry; -+ -+ /* if old file is MODIFIED, add it to the deleted_list */ -+ if(dtopd(old_dentry)->state == MODIFIED) { -+ meta_add_d_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ -+ dput(dtohd(old_dentry)); -+ } -+ /* if old file is CREATED, we only release the base dentry */ -+ if(dtopd(old_dentry)->state == CREATED) { -+ if(dtohd(old_dentry)) -+ dput(dtohd(old_dentry)); -+ } -+ -+ /* now setup the new states (depends on new_dentry state) */ -+ /* new dentry state = MODIFIED */ -+ if(dtopd(new_dentry)->state == MODIFIED) { -+ meta_add_d_entry(new_dentry->d_parent, -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ -+ /* new dentry will be d_put'ed later by the vfs -+ * so don't do it here -+ * dput(dtohd(new_dentry)); -+ */ -+ dtohd(old_dentry) = NULL; -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ } -+ /* new dentry state = UNMODIFIED */ -+ else if(dtopd(new_dentry)->state == UNMODIFIED) { -+ if(get_neg_sto_dentry(new_dentry)) -+ return -EINVAL; -+ -+ meta_add_d_entry(new_dentry->d_parent, -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ -+ /* is this right??? */ -+ /*dput(dtohd(new_dentry));*/ -+ dtohd(old_dentry) = NULL; -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ } -+ /* new dentry state = CREATED */ -+ else if(dtopd(new_dentry)->state == CREATED) { -+ /* we keep the neg. base dentry (if exists) */ -+ dtohd(old_dentry) = dtohd(new_dentry); -+ /* ...and set it to Null, or we'll get -+ * dcache.c:345 if it gets dput twice... */ -+ dtohd(new_dentry) = NULL; -+ dtopd(old_dentry)->state = CREATED; -+ } -+ /* new dentry state = NON_EXISTANT */ -+ else if(dtopd(new_dentry)->state == NON_EXISTANT) { -+ if(get_neg_sto_dentry(new_dentry)) -+ return -EINVAL; -+ -+ /* we keep the neg. base dentry (if exists) */ -+ dtohd(old_dentry) = dtohd(new_dentry); -+ /* ...and set it to Null, or we'll get -+ * Dr. dcache.c:345 if it gets dput twice... */ -+ dtohd(new_dentry) = NULL; -+ dtopd(old_dentry)->state = CREATED; -+ } -+ /* new dentry state = DEL_REWRITTEN or DELETED */ -+ else if(dtopd(new_dentry)->state == DEL_REWRITTEN || -+ dtopd(new_dentry)->state == DELETED) { -+ dtohd(old_dentry) = NULL; -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ } -+ else { /* not possible, uhh, ahh */ -+ printk(KERN_CRIT -+ "mini_fo: rename_reg_file: invalid state detected [1].\n"); -+ return -1; -+ } -+ -+ /* now we definitely have a sto file */ -+ hidden_old_dentry = dtohd2(old_dentry); -+ hidden_new_dentry = dtohd2(new_dentry); -+ -+ dget(hidden_old_dentry); -+ dget(hidden_new_dentry); -+ -+ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); -+ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); -+ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ -+ err = vfs_rename(hidden_old_dir_dentry->d_inode, -+ hidden_old_dentry, -+ hidden_new_dir_dentry->d_inode, -+ hidden_new_dentry); -+ if(err) -+ goto out_lock; -+ -+ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); -+ if (new_dir != old_dir) -+ fist_copy_attr_all(old_dir, hidden_old_dir_dentry->d_inode); -+ -+ out_lock: -+ /* double_unlock will dput the new/old parent dentries -+ * whose refcnts were incremented via get_parent above. -+ */ -+ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ dput(hidden_new_dentry); -+ dput(hidden_old_dentry); -+ out: -+ return err; -+ } -+ else { /* invalid state */ -+ printk(KERN_CRIT "mini_fo: rename_reg_file: ERROR: invalid state detected [2].\n"); -+ return -1; -+ } -+} -+ -+ -+STATIC int -+mini_fo_readlink(dentry_t *dentry, char *buf, int bufsiz) -+{ -+ int err=0; -+ dentry_t *hidden_dentry = NULL; -+ -+ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { -+ hidden_dentry = dtohd2(dentry); -+ } else if(dtohd(dentry) && dtohd(dentry)->d_inode) { -+ hidden_dentry = dtohd(dentry); -+ } else { -+ goto out; -+ } -+ -+ if (!hidden_dentry->d_inode->i_op || -+ !hidden_dentry->d_inode->i_op->readlink) { -+ err = -EINVAL; goto out; -+ } -+ -+ err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, -+ buf, -+ bufsiz); -+ if (err > 0) -+ fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode); -+ -+ out: -+ return err; -+} -+ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) -+static int mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) -+#else -+static void* mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) -+#endif -+{ -+ char *buf; -+ int len = PAGE_SIZE, err; -+ mm_segment_t old_fs; -+ -+ /* in 2.6 this is freed by mini_fo_put_link called by __do_follow_link */ -+ buf = kmalloc(len, GFP_KERNEL); -+ if (!buf) { -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ /* read the symlink, and then we will follow it */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = dentry->d_inode->i_op->readlink(dentry, buf, len); -+ set_fs(old_fs); -+ if (err < 0) { -+ kfree(buf); -+ buf = NULL; -+ goto out; -+ } -+ buf[err] = 0; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ nd_set_link(nd, buf); -+ err = 0; -+#else -+ err = vfs_follow_link(nd, buf); -+#endif -+ -+ out: -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ kfree(buf); -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) -+ return err; -+#else -+ return ERR_PTR(err); -+#endif -+} -+ -+STATIC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) -+void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd) -+#else -+void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) -+#endif -+{ -+ char *link; -+ link = nd_get_link(nd); -+ kfree(link); -+} -+#endif -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_permission(inode_t *inode, int mask, struct nameidata *nd) -+#else -+mini_fo_permission(inode_t *inode, int mask) -+#endif -+{ -+ inode_t *hidden_inode; -+ int mode; -+ int err; -+ -+ if(itohi2(inode)) { -+ hidden_inode = itohi2(inode); -+ } else { -+ hidden_inode = itohi(inode); -+ } -+ mode = inode->i_mode; -+ -+ /* not really needed, as permission handles everything: -+ * err = vfs_permission(inode, mask); -+ * if (err) -+ * goto out; -+ */ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = permission(hidden_inode, mask, nd); -+#else -+ err = permission(hidden_inode, mask); -+#endif -+ -+ /* out: */ -+ return err; -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+STATIC int -+mini_fo_inode_revalidate(dentry_t *dentry) -+{ -+ int err = 0; -+ dentry_t *hidden_dentry; -+ inode_t *hidden_inode; -+ -+ ASSERT(dentry->d_inode); -+ ASSERT(itopd(dentry->d_inode)); -+ -+ if(itohi2(dentry->d_inode)) { -+ hidden_dentry = dtohd2(dentry); -+ hidden_inode = hidden_dentry->d_inode; -+ } else if(itohi(dentry->d_inode)) { -+ hidden_dentry = dtohd(dentry); -+ hidden_inode = hidden_dentry->d_inode; -+ } else { -+ printk(KERN_CRIT "mini_fo_inode_revalidate: ERROR, invalid state detected.\n"); -+ err = -ENOENT; -+ goto out; -+ } -+ if (hidden_inode && hidden_inode->i_op && hidden_inode->i_op->revalidate){ -+ err = hidden_inode->i_op->revalidate(hidden_dentry); -+ if (err) -+ goto out; -+ } -+ fist_copy_attr_all(dentry->d_inode, hidden_inode); -+ out: -+ return err; -+} -+#endif -+ -+STATIC int -+mini_fo_setattr(dentry_t *dentry, struct iattr *ia) -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(!is_mini_fo_existant(dentry)) { -+ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [1].\n"); -+ goto out; -+ } -+ -+ if(dtost(dentry) == UNMODIFIED) { -+ if(!IS_COPY_FLAG(ia->ia_valid)) -+ goto out; /* we ignore these changes to base */ -+ -+ if(S_ISDIR(dentry->d_inode->i_mode)) { -+ err = dir_unmod_to_mod(dentry); -+ } else { -+ /* we copy contents if file is not beeing truncated */ -+ if(S_ISREG(dentry->d_inode->i_mode) && -+ !(ia->ia_size == 0 && (ia->ia_valid & ATTR_SIZE))) { -+ err = nondir_unmod_to_mod(dentry, 1); -+ } else -+ err = nondir_unmod_to_mod(dentry, 0); -+ } -+ if(err) { -+ err = -EINVAL; -+ printk(KERN_CRIT "mini_fo_setattr: ERROR changing states.\n"); -+ goto out; -+ } -+ } -+ if(!exists_in_storage(dentry)) { -+ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [2].\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ ASSERT(dentry->d_inode); -+ ASSERT(dtohd2(dentry)); -+ ASSERT(itopd(dentry->d_inode)); -+ ASSERT(itohi2(dentry->d_inode)); -+ -+ err = notify_change(dtohd2(dentry), ia); -+ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); -+ out: -+ return err; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+STATIC int -+mini_fo_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) -+{ -+ int err = 0; -+ dentry_t *hidden_dentry; -+ -+ ASSERT(dentry->d_inode); -+ ASSERT(itopd(dentry->d_inode)); -+ -+ if(itohi2(dentry->d_inode)) { -+ hidden_dentry = dtohd2(dentry); -+ } else if(itohi(dentry->d_inode)) { -+ hidden_dentry = dtohd(dentry); -+ } else { -+ printk(KERN_CRIT "mini_fo_getattr: ERROR, invalid state detected.\n"); -+ err = -ENOENT; -+ goto out; -+ } -+ fist_copy_attr_all(dentry->d_inode, hidden_dentry->d_inode); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ generic_fillattr(dentry->d_inode, stat); -+ if (!stat->blksize) { -+ struct super_block *s = hidden_dentry->d_inode->i_sb; -+ unsigned blocks; -+ blocks = (stat->size+s->s_blocksize-1) >> s->s_blocksize_bits; -+ stat->blocks = (s->s_blocksize / 512) * blocks; -+ stat->blksize = s->s_blocksize; -+ } -+ out: -+ return err; -+} -+#endif -+ -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+#if 0 /* no xattr_alloc() and xattr_free() */ -+/* This is lifted from fs/xattr.c */ -+static void * -+xattr_alloc(size_t size, size_t limit) -+{ -+ void *ptr; -+ -+ if (size > limit) -+ return ERR_PTR(-E2BIG); -+ -+ if (!size) /* size request, no buffer is needed */ -+ return NULL; -+ else if (size <= PAGE_SIZE) -+ ptr = kmalloc((unsigned long) size, GFP_KERNEL); -+ else -+ ptr = vmalloc((unsigned long) size); -+ if (!ptr) -+ return ERR_PTR(-ENOMEM); -+ return ptr; -+} -+ -+static void -+xattr_free(void *ptr, size_t size) -+{ -+ if (!size) /* size request, no buffer was needed */ -+ return; -+ else if (size <= PAGE_SIZE) -+ kfree(ptr); -+ else -+ vfree(ptr); -+} -+#endif /* no xattr_alloc() and xattr_free() */ -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+mini_fo_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ /* Define these anyway so we don't need as much ifdef'ed code. */ -+ char *encoded_name = NULL; -+ char *encoded_value = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->getxattr) { -+ encoded_name = (char *)name; -+ encoded_value = (char *)value; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, encoded_name, encoded_value, size); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) \ -+ && LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,23)) \ -+ || LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) -+mini_fo_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+#else -+mini_fo_setxattr(struct dentry *dentry, const char *name, -+ void *value, size_t size, int flags) -+#endif -+ -+{ -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ -+ /* Define these anyway, so we don't have as much ifdef'ed code. */ -+ char *encoded_value = NULL; -+ char *encoded_name = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->setxattr) { -+ encoded_name = (char *)name; -+ encoded_value = (char *)value; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->setxattr(hidden_dentry, encoded_name, encoded_value, size, flags); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+mini_fo_removexattr(struct dentry *dentry, const char *name) { -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ char *encoded_name; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->removexattr) { -+ encoded_name = (char *)name; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, encoded_name); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+mini_fo_listxattr(struct dentry *dentry, char *list, size_t size) { -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ char *encoded_list = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->listxattr) { -+ encoded_list = list; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->listxattr(hidden_dentry, encoded_list, size); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ -+ -+struct inode_operations mini_fo_symlink_iops = -+ { -+ readlink: mini_fo_readlink, -+ follow_link: mini_fo_follow_link, -+ /* mk: permission: mini_fo_permission, */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ revalidate: mini_fo_inode_revalidate, -+#endif -+ setattr: mini_fo_setattr, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ getattr: mini_fo_getattr, -+ put_link: mini_fo_put_link, -+#endif -+ -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+ setxattr: mini_fo_setxattr, -+ getxattr: mini_fo_getxattr, -+ listxattr: mini_fo_listxattr, -+ removexattr: mini_fo_removexattr -+# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ -+ }; -+ -+struct inode_operations mini_fo_dir_iops = -+ { -+ create: mini_fo_create, -+ lookup: mini_fo_lookup, -+ link: mini_fo_link, -+ unlink: mini_fo_unlink, -+ symlink: mini_fo_symlink, -+ mkdir: mini_fo_mkdir, -+ rmdir: mini_fo_rmdir, -+ mknod: mini_fo_mknod, -+ rename: mini_fo_rename, -+ /* no readlink/follow_link for non-symlinks */ -+ // off because we have setattr -+ // truncate: mini_fo_truncate, -+ /* mk:permission: mini_fo_permission, */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ revalidate: mini_fo_inode_revalidate, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ getattr: mini_fo_getattr, -+#endif -+ setattr: mini_fo_setattr, -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+ setxattr: mini_fo_setxattr, -+ getxattr: mini_fo_getxattr, -+ listxattr: mini_fo_listxattr, -+ removexattr: mini_fo_removexattr -+# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ -+ }; -+ -+struct inode_operations mini_fo_main_iops = -+ { -+ /* permission: mini_fo_permission, */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ revalidate: mini_fo_inode_revalidate, -+#endif -+ setattr: mini_fo_setattr, -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ getattr: mini_fo_getattr, -+#endif -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+ setxattr: mini_fo_setxattr, -+ getxattr: mini_fo_getxattr, -+ listxattr: mini_fo_listxattr, -+ removexattr: mini_fo_removexattr -+# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ -+ }; -Index: linux-2.6.21.7/fs/mini_fo/main.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/main.c -@@ -0,0 +1,423 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+#include -+ -+/* This definition must only appear after we include */ -+#ifndef MODULE_LICENSE -+# define MODULE_LICENSE(bison) -+#endif /* not MODULE_LICENSE */ -+ -+/* -+ * This is the mini_fo tri interpose function, which extends the -+ * functionality of the regular interpose by interposing a higher -+ * level inode on top of two lower level ones: the base filesystem -+ * inode and the storage filesystem inode. -+ * -+ * sb we pass is mini_fo's super_block -+ */ -+int -+mini_fo_tri_interpose(dentry_t *hidden_dentry, -+ dentry_t *hidden_sto_dentry, -+ dentry_t *dentry, super_block_t *sb, int flag) -+{ -+ inode_t *hidden_inode = NULL; -+ inode_t *hidden_sto_inode = NULL; /* store corresponding storage inode */ -+ int err = 0; -+ inode_t *inode; -+ -+ /* Pointer to hidden_sto_inode if exists, else to hidden_inode. -+ * This is used to copy the attributes of the correct inode. */ -+ inode_t *master_inode; -+ -+ if(hidden_dentry) -+ hidden_inode = hidden_dentry->d_inode; -+ if(hidden_sto_dentry) -+ hidden_sto_inode = hidden_sto_dentry->d_inode; -+ -+ ASSERT(dentry->d_inode == NULL); -+ -+ /* mk: One of the inodes associated with the dentrys is likely to -+ * be NULL, so carefull: -+ */ -+ ASSERT((hidden_inode != NULL) || (hidden_sto_inode != NULL)); -+ -+ if(hidden_sto_inode) -+ master_inode = hidden_sto_inode; -+ else -+ master_inode = hidden_inode; -+ -+ /* -+ * We allocate our new inode below, by calling iget. -+ * iget will call our read_inode which will initialize some -+ * of the new inode's fields -+ */ -+ -+ /* -+ * original: inode = iget(sb, hidden_inode->i_ino); -+ */ -+ inode = iget(sb, iunique(sb, 25)); -+ if (!inode) { -+ err = -EACCES; /* should be impossible??? */ -+ goto out; -+ } -+ -+ /* -+ * interpose the inode if not already interposed -+ * this is possible if the inode is being reused -+ * XXX: what happens if we get_empty_inode() but there's another already? -+ * for now, ASSERT() that this can't happen; fix later. -+ */ -+ if (itohi(inode) != NULL) { -+ printk(KERN_CRIT "mini_fo_tri_interpose: itohi(inode) != NULL.\n"); -+ } -+ if (itohi2(inode) != NULL) { -+ printk(KERN_CRIT "mini_fo_tri_interpose: itohi2(inode) != NULL.\n"); -+ } -+ -+ /* mk: Carefull, igrab can't handle NULL inodes (ok, why should it?), so -+ * we need to check here: -+ */ -+ if(hidden_inode) -+ itohi(inode) = igrab(hidden_inode); -+ else -+ itohi(inode) = NULL; -+ -+ if(hidden_sto_inode) -+ itohi2(inode) = igrab(hidden_sto_inode); -+ else -+ itohi2(inode) = NULL; -+ -+ -+ /* Use different set of inode ops for symlinks & directories*/ -+ if (S_ISLNK(master_inode->i_mode)) -+ inode->i_op = &mini_fo_symlink_iops; -+ else if (S_ISDIR(master_inode->i_mode)) -+ inode->i_op = &mini_fo_dir_iops; -+ -+ /* Use different set of file ops for directories */ -+ if (S_ISDIR(master_inode->i_mode)) -+ inode->i_fop = &mini_fo_dir_fops; -+ -+ /* properly initialize special inodes */ -+ if (S_ISBLK(master_inode->i_mode) || S_ISCHR(master_inode->i_mode) || -+ S_ISFIFO(master_inode->i_mode) || S_ISSOCK(master_inode->i_mode)) { -+ init_special_inode(inode, master_inode->i_mode, master_inode->i_rdev); -+ } -+ -+ /* Fix our inode's address operations to that of the lower inode */ -+ if (inode->i_mapping->a_ops != master_inode->i_mapping->a_ops) { -+ inode->i_mapping->a_ops = master_inode->i_mapping->a_ops; -+ } -+ -+ /* only (our) lookup wants to do a d_add */ -+ if (flag) -+ d_add(dentry, inode); -+ else -+ d_instantiate(dentry, inode); -+ -+ ASSERT(dtopd(dentry) != NULL); -+ -+ /* all well, copy inode attributes */ -+ fist_copy_attr_all(inode, master_inode); -+ -+ out: -+ return err; -+} -+ -+/* parse mount options "base=" and "sto=" */ -+dentry_t * -+mini_fo_parse_options(super_block_t *sb, char *options) -+{ -+ dentry_t *hidden_root = ERR_PTR(-EINVAL); -+ dentry_t *hidden_root2 = ERR_PTR(-EINVAL); -+ struct nameidata nd, nd2; -+ char *name, *tmp, *end; -+ int err = 0; -+ -+ /* We don't want to go off the end of our arguments later on. */ -+ for (end = options; *end; end++); -+ -+ while (options < end) { -+ tmp = options; -+ while (*tmp && *tmp != ',') -+ tmp++; -+ *tmp = '\0'; -+ if (!strncmp("base=", options, 5)) { -+ name = options + 5; -+ printk(KERN_INFO "mini_fo: using base directory: %s\n", name); -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ if (path_init(name, LOOKUP_FOLLOW, &nd)) -+ err = path_walk(name, &nd); -+#else -+ err = path_lookup(name, LOOKUP_FOLLOW, &nd); -+#endif -+ if (err) { -+ printk(KERN_CRIT "mini_fo: error accessing hidden directory '%s'\n", name); -+ hidden_root = ERR_PTR(err); -+ goto out; -+ } -+ hidden_root = nd.dentry; -+ stopd(sb)->base_dir_dentry = nd.dentry; -+ stopd(sb)->hidden_mnt = nd.mnt; -+ -+ } else if(!strncmp("sto=", options, 4)) { -+ /* parse the storage dir */ -+ name = options + 4; -+ printk(KERN_INFO "mini_fo: using storage directory: %s\n", name); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ if(path_init(name, LOOKUP_FOLLOW, &nd2)) -+ err = path_walk(name, &nd2); -+#else -+ err = path_lookup(name, LOOKUP_FOLLOW, &nd2); -+#endif -+ if(err) { -+ printk(KERN_CRIT "mini_fo: error accessing hidden storage directory '%s'\n", name); -+ -+ hidden_root2 = ERR_PTR(err); -+ goto out; -+ } -+ hidden_root2 = nd2.dentry; -+ stopd(sb)->storage_dir_dentry = nd2.dentry; -+ stopd(sb)->hidden_mnt2 = nd2.mnt; -+ stohs2(sb) = hidden_root2->d_sb; -+ -+ /* validate storage dir, this is done in -+ * mini_fo_read_super for the base directory. -+ */ -+ if (IS_ERR(hidden_root2)) { -+ printk(KERN_WARNING "mini_fo_parse_options: storage dentry lookup failed (err = %ld)\n", PTR_ERR(hidden_root2)); -+ goto out; -+ } -+ if (!hidden_root2->d_inode) { -+ printk(KERN_WARNING "mini_fo_parse_options: no storage dir to interpose on.\n"); -+ goto out; -+ } -+ stohs2(sb) = hidden_root2->d_sb; -+ } else { -+ printk(KERN_WARNING "mini_fo: unrecognized option '%s'\n", options); -+ hidden_root = ERR_PTR(-EINVAL); -+ goto out; -+ } -+ options = tmp + 1; -+ } -+ -+ out: -+ if(IS_ERR(hidden_root2)) -+ return hidden_root2; -+ return hidden_root; -+} -+ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+static int -+#else -+super_block_t * -+#endif -+mini_fo_read_super(super_block_t *sb, void *raw_data, int silent) -+{ -+ dentry_t *hidden_root; -+ int err = 0; -+ -+ if (!raw_data) { -+ printk(KERN_WARNING "mini_fo_read_super: missing argument\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ /* -+ * Allocate superblock private data -+ */ -+ __stopd(sb) = kmalloc(sizeof(struct mini_fo_sb_info), GFP_KERNEL); -+ if (!stopd(sb)) { -+ printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__); -+ err = -ENOMEM; -+ goto out; -+ } -+ stohs(sb) = NULL; -+ -+ hidden_root = mini_fo_parse_options(sb, raw_data); -+ if (IS_ERR(hidden_root)) { -+ printk(KERN_WARNING "mini_fo_read_super: lookup_dentry failed (err = %ld)\n", PTR_ERR(hidden_root)); -+ err = PTR_ERR(hidden_root); -+ goto out_free; -+ } -+ if (!hidden_root->d_inode) { -+ printk(KERN_WARNING "mini_fo_read_super: no directory to interpose on\n"); -+ goto out_free; -+ } -+ stohs(sb) = hidden_root->d_sb; -+ -+ /* -+ * Linux 2.4.2-ac3 and beyond has code in -+ * mm/filemap.c:generic_file_write() that requires sb->s_maxbytes -+ * to be populated. If not set, all write()s under that sb will -+ * return 0. -+ * -+ * Linux 2.4.4+ automatically sets s_maxbytes to MAX_NON_LFS; -+ * the filesystem should override it only if it supports LFS. -+ */ -+ /* non-SCA code is good to go with LFS */ -+ sb->s_maxbytes = hidden_root->d_sb->s_maxbytes; -+ -+ sb->s_op = &mini_fo_sops; -+ /* -+ * we can't use d_alloc_root if we want to use -+ * our own interpose function unchanged, -+ * so we simply replicate *most* of the code in d_alloc_root here -+ */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); -+#else -+ sb->s_root = d_alloc(NULL, &(const struct qstr){hash: 0, name: "/", len : 1}); -+#endif -+ if (IS_ERR(sb->s_root)) { -+ printk(KERN_WARNING "mini_fo_read_super: d_alloc failed\n"); -+ err = -ENOMEM; -+ goto out_dput; -+ } -+ -+ sb->s_root->d_op = &mini_fo_dops; -+ sb->s_root->d_sb = sb; -+ sb->s_root->d_parent = sb->s_root; -+ -+ /* link the upper and lower dentries */ -+ __dtopd(sb->s_root) = (struct mini_fo_dentry_info *) -+ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); -+ if (!dtopd(sb->s_root)) { -+ err = -ENOMEM; -+ goto out_dput2; -+ } -+ dtopd(sb->s_root)->state = MODIFIED; -+ dtohd(sb->s_root) = hidden_root; -+ -+ /* fanout relevant, interpose on storage root dentry too */ -+ dtohd2(sb->s_root) = stopd(sb)->storage_dir_dentry; -+ -+ /* ...and call tri-interpose to interpose root dir inodes -+ * if (mini_fo_interpose(hidden_root, sb->s_root, sb, 0)) -+ */ -+ if(mini_fo_tri_interpose(hidden_root, dtohd2(sb->s_root), sb->s_root, sb, 0)) -+ goto out_dput2; -+ -+ /* initalize the wol list */ -+ itopd(sb->s_root->d_inode)->deleted_list_size = -1; -+ itopd(sb->s_root->d_inode)->renamed_list_size = -1; -+ meta_build_lists(sb->s_root); -+ -+ goto out; -+ -+ out_dput2: -+ dput(sb->s_root); -+ out_dput: -+ dput(hidden_root); -+ dput(dtohd2(sb->s_root)); /* release the hidden_sto_dentry too */ -+ out_free: -+ kfree(stopd(sb)); -+ __stopd(sb) = NULL; -+ out: -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ return err; -+#else -+ if (err) { -+ return ERR_PTR(err); -+ } else { -+ return sb; -+ } -+#endif -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+static int mini_fo_get_sb(struct file_system_type *fs_type, -+ int flags, const char *dev_name, -+ void *raw_data, struct vfsmount *mnt) -+{ -+ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super, mnt); -+} -+#else -+static struct super_block *mini_fo_get_sb(struct file_system_type *fs_type, -+ int flags, const char *dev_name, -+ void *raw_data) -+{ -+ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super); -+} -+#endif -+ -+void mini_fo_kill_block_super(struct super_block *sb) -+{ -+ generic_shutdown_super(sb); -+ /* -+ * XXX: BUG: Halcrow: Things get unstable sometime after this point: -+ * lib/rwsem-spinlock.c:127: spin_is_locked on uninitialized -+ * fs/fs-writeback.c:402: spin_lock(fs/super.c:a0381828) already -+ * locked by fs/fs-writeback.c/402 -+ * -+ * Apparently, someone's not releasing a lock on sb_lock... -+ */ -+} -+ -+static struct file_system_type mini_fo_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "mini_fo", -+ .get_sb = mini_fo_get_sb, -+ .kill_sb = mini_fo_kill_block_super, -+ .fs_flags = 0, -+}; -+ -+ -+#else -+static DECLARE_FSTYPE(mini_fo_fs_type, "mini_fo", mini_fo_read_super, 0); -+#endif -+ -+static int __init init_mini_fo_fs(void) -+{ -+ printk("Registering mini_fo version $Id$\n"); -+ return register_filesystem(&mini_fo_fs_type); -+} -+static void __exit exit_mini_fo_fs(void) -+{ -+ printk("Unregistering mini_fo version $Id$\n"); -+ unregister_filesystem(&mini_fo_fs_type); -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+EXPORT_NO_SYMBOLS; -+#endif -+ -+MODULE_AUTHOR("Erez Zadok "); -+MODULE_DESCRIPTION("FiST-generated mini_fo filesystem"); -+MODULE_LICENSE("GPL"); -+ -+/* MODULE_PARM(fist_debug_var, "i"); */ -+/* MODULE_PARM_DESC(fist_debug_var, "Debug level"); */ -+ -+module_init(init_mini_fo_fs) -+module_exit(exit_mini_fo_fs) -Index: linux-2.6.21.7/fs/mini_fo/Makefile -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/Makefile -@@ -0,0 +1,17 @@ -+# -+# Makefile for mini_fo 2.4 and 2.6 Linux kernels -+# -+# Copyright (C) 2004, 2005 Markus Klotzbuecher -+# -+# This program is free software; you can redistribute it and/or -+# modify it under the terms of the GNU General Public License -+# as published by the Free Software Foundation; either version -+# 2 of the License, or (at your option) any later version. -+# -+ -+obj-$(CONFIG_MINI_FO) := mini_fo.o -+mini_fo-objs := meta.o dentry.o file.o inode.o main.o super.o state.o aux.o -+ -+# dependencies -+${mini_fo-objs}: mini_fo.h fist.h -+ -Index: linux-2.6.21.7/fs/mini_fo/meta.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/meta.c -@@ -0,0 +1,1000 @@ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif /* HAVE_CONFIG_H */ -+#include "fist.h" -+#include "mini_fo.h" -+ -+int meta_build_lists(dentry_t *dentry) -+{ -+ struct mini_fo_inode_info *inode_info; -+ -+ dentry_t *meta_dentry = 0; -+ file_t *meta_file = 0; -+ mm_segment_t old_fs; -+ void *buf; -+ -+ int bytes, len; -+ struct vfsmount *meta_mnt; -+ char *entry; -+ -+ inode_info = itopd(dentry->d_inode); -+ if(!(inode_info->deleted_list_size == -1 && -+ inode_info->renamed_list_size == -1)) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ Error, list(s) not virgin.\n"); -+ return -1; -+ } -+ -+ /* init our meta lists */ -+ INIT_LIST_HEAD(&inode_info->deleted_list); -+ inode_info->deleted_list_size = 0; -+ -+ INIT_LIST_HEAD(&inode_info->renamed_list); -+ inode_info->renamed_list_size = 0; -+ -+ /* might there be a META-file? */ -+ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen(META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ dput(meta_dentry); -+ goto out_ok; -+ } -+ /* $%& err, is this correct? */ -+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ mntget(meta_mnt); -+ -+ -+ /* open META-file for reading */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x0); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ ERROR opening META file.\n"); -+ goto out_err; -+ } -+ -+ /* check if fs supports reading */ -+ if(!meta_file->f_op->read) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ ERROR, fs does not support reading.\n"); -+ goto out_err_close; -+ } -+ -+ /* allocate a page for transfering the data */ -+ buf = (void *) __get_free_page(GFP_KERNEL); -+ if(!buf) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ ERROR, out of mem.\n"); -+ goto out_err_close; -+ } -+ meta_file->f_pos = 0; -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ char *c; -+ bytes = meta_file->f_op->read(meta_file, buf, PAGE_SIZE, &meta_file->f_pos); -+ if(bytes == PAGE_SIZE) { -+ /* trim a cut off filename and adjust f_pos to get it next time */ -+ for(c = (char*) buf+PAGE_SIZE; -+ *c != '\n'; -+ c--, bytes--, meta_file->f_pos--); -+ } -+ entry = (char *) buf; -+ while(entry < (char *) buf+bytes) { -+ -+ char *old_path; -+ char *dir_name; -+ int old_len, new_len; -+ -+ /* len without '\n'*/ -+ len = (int) (strchr(entry, '\n') - entry); -+ switch (*entry) { -+ case 'D': -+ /* format: "D filename" */ -+ meta_list_add_d_entry(dentry, -+ entry+2, -+ len-2); -+ break; -+ case 'R': -+ /* format: "R path/xy/dir newDir" */ -+ old_path = entry+2; -+ dir_name = strchr(old_path, ' ') + 1; -+ old_len = dir_name - old_path - 1; -+ new_len = ((int) entry) + len - ((int ) dir_name); -+ meta_list_add_r_entry(dentry, -+ old_path, -+ old_len, -+ dir_name, -+ new_len); -+ break; -+ default: -+ /* unknown entry type detected */ -+ break; -+ } -+ entry += len+1; -+ } -+ -+ } while(meta_file->f_pos < meta_dentry->d_inode->i_size); -+ -+ free_page((unsigned long) buf); -+ set_fs(old_fs); -+ fput(meta_file); -+ } -+ goto out_ok; -+ -+ out_err_close: -+ fput(meta_file); -+ out_err: -+ mntput(meta_mnt); -+ dput(meta_dentry); -+ return -1; -+ out_ok: -+ return 1; /* check this!!! inode_info->wol_size; */ -+} -+ -+/* cleanups up all lists and free's the mem by dentry */ -+int meta_put_lists(dentry_t *dentry) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk("mini_fo: meta_put_lists: invalid dentry passed.\n"); -+ return -1; -+ } -+ return __meta_put_lists(dentry->d_inode); -+} -+ -+/* cleanups up all lists and free's the mem by inode */ -+int __meta_put_lists(inode_t *inode) -+{ -+ int err = 0; -+ if(!inode || !itopd(inode)) { -+ printk("mini_fo: __meta_put_lists: invalid inode passed.\n"); -+ return -1; -+ } -+ err = __meta_put_d_list(inode); -+ err |= __meta_put_r_list(inode); -+ return err; -+} -+ -+int meta_sync_lists(dentry_t *dentry) -+{ -+ int err = 0; -+ if(!dentry || !dentry->d_inode) { -+ printk("mini_fo: meta_sync_lists: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ err = meta_sync_d_list(dentry, 0); -+ err |= meta_sync_r_list(dentry, 1); -+ return err; -+} -+ -+ -+/* remove all D entries from the renamed list and free the mem */ -+int __meta_put_d_list(inode_t *inode) -+{ -+ struct list_head *tmp; -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: __meta_put_d_list: \ -+ invalid inode passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(inode); -+ -+ /* nuke the DELETED-list */ -+ if(inode_info->deleted_list_size <= 0) -+ return 0; -+ -+ while(!list_empty(&inode_info->deleted_list)) { -+ tmp = inode_info->deleted_list.next; -+ list_del(tmp); -+ del_entry = list_entry(tmp, struct deleted_entry, list); -+ kfree(del_entry->name); -+ kfree(del_entry); -+ } -+ inode_info->deleted_list_size = 0; -+ -+ return 0; -+} -+ -+/* remove all R entries from the renamed list and free the mem */ -+int __meta_put_r_list(inode_t *inode) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: meta_put_r_list: invalid inode.\n"); -+ return -1; -+ } -+ inode_info = itopd(inode); -+ -+ /* nuke the RENAMED-list */ -+ if(inode_info->renamed_list_size <= 0) -+ return 0; -+ -+ while(!list_empty(&inode_info->renamed_list)) { -+ tmp = inode_info->renamed_list.next; -+ list_del(tmp); -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ kfree(ren_entry->new_name); -+ kfree(ren_entry->old_name); -+ kfree(ren_entry); -+ } -+ inode_info->renamed_list_size = 0; -+ -+ return 0; -+} -+ -+int meta_add_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ int err = 0; -+ err = meta_list_add_d_entry(dentry, name, len); -+ err |= meta_write_d_entry(dentry,name,len); -+ return err; -+} -+ -+/* add a D entry to the deleted list */ -+int meta_list_add_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->deleted_list_size < 0) -+ return -1; -+ -+ del_entry = (struct deleted_entry *) -+ kmalloc(sizeof(struct deleted_entry), GFP_KERNEL); -+ del_entry->name = (char*) kmalloc(len, GFP_KERNEL); -+ if(!del_entry || !del_entry->name) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ -+ out of mem.\n"); -+ kfree(del_entry->name); -+ kfree(del_entry); -+ return -ENOMEM; -+ } -+ -+ strncpy(del_entry->name, name, len); -+ del_entry->len = len; -+ -+ list_add(&del_entry->list, &inode_info->deleted_list); -+ inode_info->deleted_list_size++; -+ return 0; -+} -+ -+int meta_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len) -+{ -+ int err = 0; -+ err = meta_list_add_r_entry(dentry, -+ old_name, old_len, -+ new_name, new_len); -+ err |= meta_write_r_entry(dentry, -+ old_name, old_len, -+ new_name, new_len); -+ return err; -+} -+ -+/* add a R entry to the renamed list */ -+int meta_list_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len) -+{ -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->renamed_list_size < 0) -+ return -1; -+ -+ ren_entry = (struct renamed_entry *) -+ kmalloc(sizeof(struct renamed_entry), GFP_KERNEL); -+ ren_entry->old_name = (char*) kmalloc(old_len, GFP_KERNEL); -+ ren_entry->new_name = (char*) kmalloc(new_len, GFP_KERNEL); -+ -+ if(!ren_entry || !ren_entry->old_name || !ren_entry->new_name) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ -+ out of mem.\n"); -+ kfree(ren_entry->new_name); -+ kfree(ren_entry->old_name); -+ kfree(ren_entry); -+ return -ENOMEM; -+ } -+ -+ strncpy(ren_entry->old_name, old_name, old_len); -+ ren_entry->old_len = old_len; -+ strncpy(ren_entry->new_name, new_name, new_len); -+ ren_entry->new_len = new_len; -+ -+ list_add(&ren_entry->list, &inode_info->renamed_list); -+ inode_info->renamed_list_size++; -+ return 0; -+} -+ -+ -+int meta_remove_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ int err = 0; -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT -+ "mini_fo: meta_remove_r_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ -+ err = meta_list_remove_r_entry(dentry, name, len); -+ err |= meta_sync_lists(dentry); -+ return err; -+} -+ -+int meta_list_remove_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT -+ "mini_fo: meta_list_remove_r_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ return __meta_list_remove_r_entry(dentry->d_inode, name, len); -+} -+ -+int __meta_list_remove_r_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) -+ printk(KERN_CRIT -+ "mini_fo: __meta_list_remove_r_entry: \ -+ invalid inode passed.\n"); -+ inode_info = itopd(inode); -+ -+ if(inode_info->renamed_list_size < 0) -+ return -1; -+ if(inode_info->renamed_list_size == 0) -+ return 1; -+ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ if(ren_entry->new_len != len) -+ continue; -+ -+ if(!strncmp(ren_entry->new_name, name, len)) { -+ list_del(tmp); -+ kfree(ren_entry->new_name); -+ kfree(ren_entry->old_name); -+ kfree(ren_entry); -+ inode_info->renamed_list_size--; -+ return 0; -+ } -+ } -+ return 1; -+} -+ -+ -+/* append a single D entry to the meta file */ -+int meta_write_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ dentry_t *meta_dentry = 0; -+ file_t *meta_file = 0; -+ mm_segment_t old_fs; -+ -+ int bytes, err; -+ struct vfsmount *meta_mnt = 0; -+ char *buf; -+ -+ err = 0; -+ -+ if(itopd(dentry->d_inode)->deleted_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ if(dtopd(dentry)->state == UNMODIFIED) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), strlen (META_FILENAME)); -+ -+ /* We need to create a META-file */ -+ if(!meta_dentry->d_inode) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, -+ S_IRUSR | S_IWUSR, -+ NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, -+ S_IRUSR | S_IWUSR); -+#endif -+ } -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ ERROR opening meta file.\n"); -+ mntput(meta_mnt); /* $%& is this necessary? */ -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* size: len for name, 1 for \n and 2 for "D " */ -+ buf = (char *) kmalloc(len+3, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ out of mem.\n"); -+ return -ENOMEM; -+ } -+ -+ buf[0] = 'D'; -+ buf[1] = ' '; -+ strncpy(buf+2, name, len); -+ buf[len+2] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, len+3, -+ &meta_file->f_pos); -+ if(bytes != len+3) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ ERROR writing.\n"); -+ err = -1; -+ } -+ kfree(buf); -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+} -+ -+/* append a single R entry to the meta file */ -+int meta_write_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len) -+{ -+ dentry_t *meta_dentry = 0; -+ file_t *meta_file = 0; -+ mm_segment_t old_fs; -+ -+ int bytes, err, buf_len; -+ struct vfsmount *meta_mnt = 0; -+ char *buf; -+ -+ -+ err = 0; -+ -+ if(itopd(dentry->d_inode)->renamed_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ /* build the storage structure? */ -+ if(dtopd(dentry)->state == UNMODIFIED) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen (META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ /* We need to create a META-file */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR, NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR); -+#endif -+ } -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ -+ ERROR opening meta file.\n"); -+ mntput(meta_mnt); -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* size: 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ -+ buf_len = old_len + new_len + 4; -+ buf = (char *) kmalloc(buf_len, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: out of mem.\n"); -+ return -ENOMEM; -+ } -+ -+ buf[0] = 'R'; -+ buf[1] = ' '; -+ strncpy(buf + 2, old_name, old_len); -+ buf[old_len + 2] = ' '; -+ strncpy(buf + old_len + 3, new_name, new_len); -+ buf[buf_len -1] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, buf_len, &meta_file->f_pos); -+ if(bytes != buf_len) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: ERROR writing.\n"); -+ err = -1; -+ } -+ -+ kfree(buf); -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+} -+ -+/* sync D list to disk, append data if app_flag is 1 */ -+/* check the meta_mnt, which seems not to be used (properly) */ -+ -+int meta_sync_d_list(dentry_t *dentry, int app_flag) -+{ -+ dentry_t *meta_dentry; -+ file_t *meta_file; -+ mm_segment_t old_fs; -+ -+ int bytes, err; -+ struct vfsmount *meta_mnt; -+ char *buf; -+ -+ struct list_head *tmp; -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ err = 0; -+ meta_file=0; -+ meta_mnt=0; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ invalid inode passed.\n"); -+ err = -1; -+ goto out; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->deleted_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ /* ok, there is something to sync */ -+ -+ /* build the storage structure? */ -+ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen(META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ /* We need to create a META-file */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR, NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR); -+#endif -+ app_flag = 0; -+ } -+ /* need we truncate the meta file? */ -+ if(!app_flag) { -+ struct iattr newattrs; -+ newattrs.ia_size = 0; -+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&meta_dentry->d_inode->i_mutex); -+#else -+ down(&meta_dentry->d_inode->i_sem); -+#endif -+ err = notify_change(meta_dentry, &newattrs); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&meta_dentry->d_inode->i_mutex); -+#else -+ up(&meta_dentry->d_inode->i_sem); -+#endif -+ -+ if(err || meta_dentry->d_inode->i_size != 0) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR truncating meta file.\n"); -+ goto out_err_close; -+ } -+ } -+ -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR opening meta file.\n"); -+ /* we don't mntget so we dont't mntput (for now) -+ * mntput(meta_mnt); -+ */ -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* here we go... */ -+ list_for_each(tmp, &inode_info->deleted_list) { -+ del_entry = list_entry(tmp, struct deleted_entry, list); -+ -+ /* size: len for name, 1 for \n and 2 for "D " */ -+ buf = (char *) kmalloc(del_entry->len+3, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ out of mem.\n"); -+ return -ENOMEM; -+ } -+ -+ buf[0] = 'D'; -+ buf[1] = ' '; -+ strncpy(buf+2, del_entry->name, del_entry->len); -+ buf[del_entry->len+2] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, -+ del_entry->len+3, -+ &meta_file->f_pos); -+ if(bytes != del_entry->len+3) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR writing.\n"); -+ err |= -1; -+ } -+ kfree(buf); -+ } -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+ -+} -+ -+int meta_sync_r_list(dentry_t *dentry, int app_flag) -+{ -+ dentry_t *meta_dentry; -+ file_t *meta_file; -+ mm_segment_t old_fs; -+ -+ int bytes, err, buf_len; -+ struct vfsmount *meta_mnt; -+ char *buf; -+ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ err = 0; -+ meta_file=0; -+ meta_mnt=0; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ invalid dentry passed.\n"); -+ err = -1; -+ goto out; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->deleted_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ /* ok, there is something to sync */ -+ -+ /* build the storage structure? */ -+ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen(META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ /* We need to create a META-file */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR, NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR); -+#endif -+ app_flag = 0; -+ } -+ /* need we truncate the meta file? */ -+ if(!app_flag) { -+ struct iattr newattrs; -+ newattrs.ia_size = 0; -+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&meta_dentry->d_inode->i_mutex); -+#else -+ down(&meta_dentry->d_inode->i_sem); -+#endif -+ err = notify_change(meta_dentry, &newattrs); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&meta_dentry->d_inode->i_mutex); -+#else -+ up(&meta_dentry->d_inode->i_sem); -+#endif -+ if(err || meta_dentry->d_inode->i_size != 0) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR truncating meta file.\n"); -+ goto out_err_close; -+ } -+ } -+ -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR opening meta file.\n"); -+ /* we don't mntget so we dont't mntput (for now) -+ * mntput(meta_mnt); -+ */ -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* here we go... */ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ /* size: -+ * 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ -+ buf_len = ren_entry->old_len + ren_entry->new_len + 4; -+ buf = (char *) kmalloc(buf_len, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ out of mem.\n"); -+ return -ENOMEM; -+ } -+ buf[0] = 'R'; -+ buf[1] = ' '; -+ strncpy(buf + 2, ren_entry->old_name, ren_entry->old_len); -+ buf[ren_entry->old_len + 2] = ' '; -+ strncpy(buf + ren_entry->old_len + 3, -+ ren_entry->new_name, ren_entry->new_len); -+ buf[buf_len - 1] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, -+ buf_len, &meta_file->f_pos); -+ if(bytes != buf_len) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR writing.\n"); -+ err |= -1; -+ } -+ kfree(buf); -+ } -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+} -+ -+int meta_check_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) -+ printk(KERN_CRIT "mini_fo: meta_check_d_dentry: \ -+ invalid dentry passed.\n"); -+ return __meta_check_d_entry(dentry->d_inode, name, len); -+} -+ -+int __meta_check_d_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) -+ printk(KERN_CRIT "mini_fo: __meta_check_d_dentry: \ -+ invalid inode passed.\n"); -+ -+ inode_info = itopd(inode); -+ -+ if(inode_info->deleted_list_size <= 0) -+ return 0; -+ -+ list_for_each(tmp, &inode_info->deleted_list) { -+ del_entry = list_entry(tmp, struct deleted_entry, list); -+ if(del_entry->len != len) -+ continue; -+ -+ if(!strncmp(del_entry->name, name, len)) -+ return 1; -+ } -+ return 0; -+} -+ -+/* -+ * check if file has been renamed and return path to orig. base dir. -+ * Implements no error return values so far, what of course sucks. -+ * String is null terminated.' -+ */ -+char* meta_check_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ -+ invalid dentry passed.\n"); -+ return NULL; -+ } -+ return __meta_check_r_entry(dentry->d_inode, name, len); -+} -+ -+char* __meta_check_r_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ char *old_path; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ -+ invalid inode passed.\n"); -+ return NULL; -+ } -+ inode_info = itopd(inode); -+ -+ if(inode_info->renamed_list_size <= 0) -+ return NULL; -+ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ if(ren_entry->new_len != len) -+ continue; -+ -+ if(!strncmp(ren_entry->new_name, name, len)) { -+ old_path = (char *) -+ kmalloc(ren_entry->old_len+1, GFP_KERNEL); -+ strncpy(old_path, -+ ren_entry->old_name, -+ ren_entry->old_len); -+ old_path[ren_entry->old_len]='\0'; -+ return old_path; -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * This version only checks if entry exists and return: -+ * 1 if exists, -+ * 0 if not, -+ * -1 if error. -+ */ -+int meta_is_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ return __meta_is_r_entry(dentry->d_inode, name, len); -+} -+ -+int __meta_is_r_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ -+ invalid inode passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(inode); -+ -+ if(inode_info->renamed_list_size <= 0) -+ return -1; -+ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ if(ren_entry->new_len != len) -+ continue; -+ -+ if(!strncmp(ren_entry->new_name, name, len)) -+ return 1; -+ } -+ return 0; -+} -+ -Index: linux-2.6.21.7/fs/mini_fo/mini_fo.h -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/mini_fo.h -@@ -0,0 +1,510 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifndef __MINI_FO_H_ -+#define __MINI_FO_H_ -+ -+#ifdef __KERNEL__ -+ -+/* META stuff */ -+#define META_FILENAME "META_dAfFgHE39ktF3HD2sr" -+ -+/* use xattrs? */ -+#define XATTR -+ -+/* File attributes that when changed, result in a file beeing copied to storage */ -+#define COPY_FLAGS ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_SIZE -+ -+/* -+ * mini_fo filestates -+ */ -+#define MODIFIED 1 -+#define UNMODIFIED 2 -+#define CREATED 3 -+#define DEL_REWRITTEN 4 -+#define DELETED 5 -+#define NON_EXISTANT 6 -+ -+/* fist file systems superblock magic */ -+# define MINI_FO_SUPER_MAGIC 0xf15f -+ -+/* -+ * STRUCTURES: -+ */ -+ -+/* mini_fo inode data in memory */ -+struct mini_fo_inode_info { -+ inode_t *wii_inode; -+ inode_t *wii_inode2; /* pointer to storage inode */ -+ -+ /* META-data lists */ -+ /* deleted list, ex wol */ -+ struct list_head deleted_list; -+ int deleted_list_size; -+ -+ /* renamed list */ -+ struct list_head renamed_list; -+ int renamed_list_size; -+ -+ /* add other lists here ... */ -+}; -+ -+/* mini_fo dentry data in memory */ -+struct mini_fo_dentry_info { -+ dentry_t *wdi_dentry; -+ dentry_t *wdi_dentry2; /* pointer to storage dentry */ -+ unsigned int state; /* state of the mini_fo dentry */ -+}; -+ -+ -+/* mini_fo super-block data in memory */ -+struct mini_fo_sb_info { -+ super_block_t *wsi_sb, *wsi_sb2; /* mk: might point to the same sb */ -+ struct vfsmount *hidden_mnt, *hidden_mnt2; -+ dentry_t *base_dir_dentry; -+ dentry_t *storage_dir_dentry; -+ ; -+}; -+ -+/* readdir_data, readdir helper struct */ -+struct readdir_data { -+ struct list_head ndl_list; /* linked list head ptr */ -+ int ndl_size; /* list size */ -+ int sto_done; /* flag to show that the storage dir entries have -+ * all been read an now follow base entries */ -+}; -+ -+/* file private data. */ -+struct mini_fo_file_info { -+ struct file *wfi_file; -+ struct file *wfi_file2; /* pointer to storage file */ -+ struct readdir_data rd; -+}; -+ -+/* struct ndl_entry */ -+struct ndl_entry { -+ struct list_head list; -+ char *name; -+ int len; -+}; -+ -+/******************************** -+ * META-data structures -+ ********************************/ -+ -+/* deleted entry */ -+struct deleted_entry { -+ struct list_head list; -+ char *name; -+ int len; -+}; -+ -+/* renamed entry */ -+struct renamed_entry { -+ struct list_head list; -+ char *old_name; /* old directory with full path */ -+ int old_len; /* length of above string */ -+ char *new_name; /* new directory name */ -+ int new_len; /* length of above string */ -+}; -+ -+/* attr_change entry */ -+struct attr_change_entry { -+ struct list_head list; -+ char *name; -+ int len; -+}; -+ -+/* link entry */ -+struct link_entry { -+ struct list_head list; -+ int links_moved; -+ int inum_base; -+ int inum_sto; -+ char *weird_name; -+ int weird_name_len; -+}; -+ -+ -+/* Some other stuff required for mini_fo_filldir64, copied from -+ * fs/readdir.c -+ */ -+ -+#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) -+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -+ -+ -+struct linux_dirent64 { -+ u64 d_ino; -+ s64 d_off; -+ unsigned short d_reclen; -+ unsigned char d_type; -+ char d_name[0]; -+}; -+ -+ -+struct getdents_callback64 { -+ struct linux_dirent64 * current_dir; -+ struct linux_dirent64 * previous; -+ int count; -+ int error; -+}; -+ -+struct linux_dirent { -+ unsigned long d_ino; -+ unsigned long d_off; -+ unsigned short d_reclen; -+ char d_name[1]; -+}; -+ -+struct getdents_callback { -+ struct linux_dirent * current_dir; -+ struct linux_dirent * previous; -+ int count; -+ int error; -+}; -+ -+ -+/* -+ * MACROS: -+ */ -+ -+/* file TO private_data */ -+# define ftopd(file) ((struct mini_fo_file_info *)((file)->private_data)) -+# define __ftopd(file) ((file)->private_data) -+/* file TO hidden_file */ -+# define ftohf(file) ((ftopd(file))->wfi_file) -+# define ftohf2(file) ((ftopd(file))->wfi_file2) -+ -+/* inode TO private_data */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->i_private) -+# define __itopd(ino) ((ino)->i_private) -+#else -+# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->u.generic_ip) -+# define __itopd(ino) ((ino)->u.generic_ip) -+#endif -+/* inode TO hidden_inode */ -+# define itohi(ino) (itopd(ino)->wii_inode) -+# define itohi2(ino) (itopd(ino)->wii_inode2) -+ -+/* superblock TO private_data */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+# define stopd(super) ((struct mini_fo_sb_info *)(super)->s_fs_info) -+# define __stopd(super) ((super)->s_fs_info) -+#else -+# define stopd(super) ((struct mini_fo_sb_info *)(super)->u.generic_sbp) -+# define __stopd(super) ((super)->u.generic_sbp) -+#endif -+ -+/* unused? # define vfs2priv stopd */ -+/* superblock TO hidden_superblock */ -+ -+# define stohs(super) (stopd(super)->wsi_sb) -+# define stohs2(super) (stopd(super)->wsi_sb2) -+ -+/* dentry TO private_data */ -+# define dtopd(dentry) ((struct mini_fo_dentry_info *)(dentry)->d_fsdata) -+# define __dtopd(dentry) ((dentry)->d_fsdata) -+/* dentry TO hidden_dentry */ -+# define dtohd(dent) (dtopd(dent)->wdi_dentry) -+# define dtohd2(dent) (dtopd(dent)->wdi_dentry2) -+ -+/* dentry to state */ -+# define dtost(dent) (dtopd(dent)->state) -+# define sbt(sb) ((sb)->s_type->name) -+ -+#define IS_WRITE_FLAG(flag) (flag & (O_RDWR | O_WRONLY | O_APPEND)) -+#define IS_COPY_FLAG(flag) (flag & (COPY_FLAGS)) -+ -+/* macros to simplify non-SCA code */ -+# define MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages) -+# define MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages) -+# define FREE_PAGE_POINTERS(hidden_pages, num) -+# define FREE_PAGEDATA_POINTERS(hidden_pages_data, num) -+# define FOR_EACH_PAGE -+# define CURRENT_HIDDEN_PAGE hidden_page -+# define CURRENT_HIDDEN_PAGEDATA hidden_page_data -+# define CURRENT_HIDDEN_PAGEINDEX page->index -+ -+/* -+ * EXTERNALS: -+ */ -+extern struct file_operations mini_fo_main_fops; -+extern struct file_operations mini_fo_dir_fops; -+extern struct inode_operations mini_fo_main_iops; -+extern struct inode_operations mini_fo_dir_iops; -+extern struct inode_operations mini_fo_symlink_iops; -+extern struct super_operations mini_fo_sops; -+extern struct dentry_operations mini_fo_dops; -+extern struct vm_operations_struct mini_fo_shared_vmops; -+extern struct vm_operations_struct mini_fo_private_vmops; -+extern struct address_space_operations mini_fo_aops; -+ -+#if 0 /* unused by mini_fo */ -+extern int mini_fo_interpose(dentry_t *hidden_dentry, dentry_t *this_dentry, super_block_t *sb, int flag); -+#if defined(FIST_FILTER_DATA) || defined(FIST_FILTER_SCA) -+extern page_t *mini_fo_get1page(file_t *file, int index); -+extern int mini_fo_fill_zeros(file_t *file, page_t *page, unsigned from); -+# endif /* FIST_FILTER_DATA || FIST_FILTER_SCA */ -+ -+ -+# define mini_fo_hidden_dentry(d) __mini_fo_hidden_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) -+# define mini_fo_hidden_sto_dentry(d) __mini_fo_hidden_sto_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) -+ -+extern dentry_t *__mini_fo_hidden_dentry(char *file, char *func, int line, dentry_t *this_dentry); -+extern dentry_t *__mini_fo_hidden_sto_dentry(char *file, char *func, int line, dentry_t *this_dentry); -+ -+extern int mini_fo_read_file(const char *filename, void *buf, int len); -+extern int mini_fo_write_file(const char *filename, void *buf, int len); -+extern dentry_t *fist_lookup(dentry_t *dir, const char *name, vnode_t **out, uid_t uid, gid_t gid); -+#endif /* unused by mini_fo */ -+ -+/* state transition functions */ -+extern int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag); -+extern int nondir_del_rew_to_del(dentry_t *dentry); -+extern int nondir_creat_to_del(dentry_t *dentry); -+extern int nondir_mod_to_del(dentry_t *dentry); -+extern int nondir_unmod_to_del(dentry_t *dentry); -+ -+extern int dir_unmod_to_mod(dentry_t *dentry); -+ -+/* rename specials */ -+extern int rename_directory(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); -+extern int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); -+ -+/* misc stuff */ -+extern int mini_fo_tri_interpose(dentry_t *hidden_dentry, -+ dentry_t *hidden_sto_dentry, -+ dentry_t *dentry, -+ super_block_t *sb, int flag); -+ -+extern int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, -+ dentry_t *src_dentry, struct vfsmount *src_mnt); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd); -+ -+extern int create_sto_nod(dentry_t *dentry, int mode, dev_t dev); -+extern int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd); -+#else -+extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode); -+ -+extern int create_sto_nod(dentry_t *dentry, int mode, int dev); -+extern int create_sto_reg_file(dentry_t *dentry, int mode); -+#endif -+ -+extern int create_sto_dir(dentry_t *dentry, int mode); -+ -+extern int exists_in_storage(dentry_t *dentry); -+extern int is_mini_fo_existant(dentry_t *dentry); -+extern int get_neg_sto_dentry(dentry_t *dentry); -+extern int build_sto_structure(dentry_t *dir, dentry_t *dentry); -+extern int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len); -+extern dentry_t *bpath_walk(super_block_t *sb, char *bpath); -+extern int bpath_put(dentry_t *dentry); -+ -+/* check_mini_fo types functions */ -+extern int check_mini_fo_dentry(dentry_t *dentry); -+extern int check_mini_fo_file(file_t *file); -+extern int check_mini_fo_inode(inode_t *inode); -+ -+/* General meta functions, can be called from outside of meta.c */ -+extern int meta_build_lists(dentry_t *dentry); -+extern int meta_put_lists(dentry_t *dentry); -+extern int __meta_put_lists(inode_t *inode); -+ -+extern int meta_add_d_entry(dentry_t *dentry, const char *name, int len); -+extern int meta_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len); -+ -+extern int meta_remove_r_entry(dentry_t *dentry, const char *name, int len); -+ -+extern int meta_check_d_entry(dentry_t *dentry, const char *name, int len); -+extern int __meta_check_d_entry(inode_t *inode, const char *name, int len); -+ -+extern char* meta_check_r_entry(dentry_t *dentry, const char *name, int len); -+extern char* __meta_check_r_entry(inode_t *inode, const char *name, int len); -+extern int meta_is_r_entry(dentry_t *dentry, const char *name, int len); -+extern int __meta_is_r_entry(inode_t *inode, const char *name, int len); -+ -+/* Specific meta functions, should be called only inside meta.c */ -+extern int __meta_put_d_list(inode_t *inode); -+extern int __meta_put_r_list(inode_t *inode); -+ -+extern int meta_list_add_d_entry(dentry_t *dentry, -+ const char *name, int len); -+extern int meta_list_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len); -+ -+extern int meta_list_remove_r_entry(dentry_t *dentry, -+ const char *name, int len); -+ -+extern int __meta_list_remove_r_entry(inode_t *inode, -+ const char *name, int len); -+ -+extern int meta_write_d_entry(dentry_t *dentry, const char *name, int len); -+extern int meta_write_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len); -+ -+extern int meta_sync_lists(dentry_t *dentry); -+extern int meta_sync_d_list(dentry_t *dentry, int app_flag); -+extern int meta_sync_r_list(dentry_t *dentry, int app_flag); -+ -+/* ndl stuff */ -+extern int ndl_add_entry(struct readdir_data *rd, const char *name, int len); -+extern void ndl_put_list(struct readdir_data *rd); -+extern int ndl_check_entry(struct readdir_data *rd, -+ const char *name, int len); -+ -+ -+# define copy_inode_size(dst, src) \ -+ dst->i_size = src->i_size; \ -+ dst->i_blocks = src->i_blocks; -+ -+static inline void -+fist_copy_attr_atime(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_atime = src->i_atime; -+} -+static inline void -+fist_copy_attr_times(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_atime = src->i_atime; -+ dest->i_mtime = src->i_mtime; -+ dest->i_ctime = src->i_ctime; -+} -+static inline void -+fist_copy_attr_timesizes(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_atime = src->i_atime; -+ dest->i_mtime = src->i_mtime; -+ dest->i_ctime = src->i_ctime; -+ copy_inode_size(dest, src); -+} -+static inline void -+fist_copy_attr_all(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_mode = src->i_mode; -+ dest->i_nlink = src->i_nlink; -+ dest->i_uid = src->i_uid; -+ dest->i_gid = src->i_gid; -+ dest->i_rdev = src->i_rdev; -+ dest->i_atime = src->i_atime; -+ dest->i_mtime = src->i_mtime; -+ dest->i_ctime = src->i_ctime; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ dest->i_blksize = src->i_blksize; -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12) -+ dest->i_blkbits = src->i_blkbits; -+# endif /* linux 2.4.12 and newer */ -+ copy_inode_size(dest, src); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ dest->i_attr_flags = src->i_attr_flags; -+#else -+ dest->i_flags = src->i_flags; -+#endif -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+/* copied from linux/fs.h */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+static inline void double_lock(struct dentry *d1, struct dentry *d2) -+{ -+ struct mutex *m1 = &d1->d_inode->i_mutex; -+ struct mutex *m2 = &d2->d_inode->i_mutex; -+ if (m1 != m2) { -+ if ((unsigned long) m1 < (unsigned long) m2) { -+ struct mutex *tmp = m2; -+ m2 = m1; m1 = tmp; -+ } -+ mutex_lock(m1); -+ } -+ mutex_lock(m2); -+} -+ -+static inline void double_unlock(struct dentry *d1, struct dentry *d2) -+{ -+ struct mutex *m1 = &d1->d_inode->i_mutex; -+ struct mutex *m2 = &d2->d_inode->i_mutex; -+ mutex_unlock(m1); -+ if (m1 != m2) -+ mutex_unlock(m2); -+ dput(d1); -+ dput(d2); -+} -+ -+#else -+static inline void double_down(struct semaphore *s1, struct semaphore *s2) -+{ -+ if (s1 != s2) { -+ if ((unsigned long) s1 < (unsigned long) s2) { -+ struct semaphore *tmp = s2; -+ s2 = s1; s1 = tmp; -+ } -+ down(s1); -+ } -+ down(s2); -+} -+ -+static inline void double_up(struct semaphore *s1, struct semaphore *s2) -+{ -+ up(s1); -+ if (s1 != s2) -+ up(s2); -+} -+ -+static inline void double_lock(struct dentry *d1, struct dentry *d2) -+{ -+ double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem); -+} -+ -+static inline void double_unlock(struct dentry *d1, struct dentry *d2) -+{ -+ double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem); -+ dput(d1); -+ dput(d2); -+} -+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) */ -+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ -+#endif /* __KERNEL__ */ -+ -+/* -+ * Definitions for user and kernel code -+ */ -+ -+/* ioctls */ -+ -+#endif /* not __MINI_FO_H_ */ -Index: linux-2.6.21.7/fs/mini_fo/mini_fo-merge -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/mini_fo-merge -@@ -0,0 +1,180 @@ -+#!/bin/bash -+# -+# Copyright (C) 2005 Markus Klotzbuecher -+# This program is free software; you can redistribute it and/or -+# modify it under the terms of the GNU General Public License -+# as published by the Free Software Foundation; either version -+# 2 of the License, or (at your option) any later version. -+# -+ -+BASE= -+STO= -+HELP= -+DRYRUN= -+VERBOSE= -+TMP="/tmp/" -+META_NAME="META_dAfFgHE39ktF3HD2sr" -+SKIP_DEL_LIST="skip-delete-list.mini_fo-merge" -+ -+COMMAND= -+exec_command() -+{ -+ if [ x$DRYRUN == "xset" ]; then -+ echo " would run: $COMMAND" -+ elif ! [ x$DRYRUN == "xset" ]; then -+ if [ x$VERBOSE == "xset" ]; then -+ echo " running: $COMMAND" -+ fi -+ eval $COMMAND -+ fi -+} -+ -+usage() -+{ -+cat < -s -+Version 0.1 -+ -+This script merges the contents of a mini_fo storage file system back -+to the base file system. -+ -+!!! Warning: This will modify the base filesystem and can destroy data -+ if used wrongly. -+ -+Options: -+ -b -+ the directory of the base file system. -+ -+ -s -+ the directory of the storage file system. -+ -+ -d dry run, will not change anything and print the commands that -+ would be executed. -+ -+ -t tmp dir for storing temporary file. default: $TMP -+ -+ -v show what operations are performed. -+ -+ -h displays this message. -+ -+EOF -+} -+ -+# parse parameters -+while getopts hdvt:b:s: OPTS -+ do -+ case $OPTS in -+ h) HELP="set";; -+ d) DRYRUN="set";; -+ v) VERBOSE="set";; -+ b) BASE="$OPTARG";; -+ s) STO="$OPTARG";; -+ t) TMP="$OPTARG";; -+ ?) usage -+ exit 1;; -+ esac -+done -+ -+if [ "x$HELP" == "xset" ]; then -+ usage -+ exit -1 -+fi -+ -+if ! [ -d "$BASE" ] || ! [ -d "$STO" ]; then -+ echo -e "$0:\n Error, -s and/or -b argument missing. type $0 -h for help." -+ exit -1; -+fi -+ -+# get full paths -+pushd $STO; STO=`pwd`; popd -+pushd $BASE; BASE=`pwd`; popd -+TMP=${TMP%/} -+ -+ -+cat< /dev/null -+find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^R ' | tr -s ':R' ' ' | while read ENTRY; do -+ echo "entry: $ENTRY" -+ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` -+ OLD_B_DIR=`echo $ENTRY | cut -d ' ' -f 2 | sed -e 's/\///'` -+ NEW_NAME=`echo $ENTRY | cut -d ' ' -f 3` -+ NEW_B_DIR=`echo $META_FILE | sed -e "s/$META_NAME/$NEW_NAME/" | sed -e 's/^\.\///'` -+ echo "META_FILE: $META_FILE" -+ echo "OLD_B_DIR: $OLD_B_DIR" -+ echo "NEW_NAME: $NEW_NAME" -+ echo "NEW_B_DIR: $NEW_B_DIR" -+ -+ pushd $BASE &> /dev/null -+ # remove an existing dir in storage -+ COMMAND="rm -rf $NEW_B_DIR"; exec_command -+ COMMAND="cp -R $OLD_B_DIR $NEW_B_DIR"; exec_command -+ echo "" -+ popd &> /dev/null -+ -+ # remember this dir to exclude it from deleting later -+ echo $NEW_B_DIR >> $TMP/$SKIP_DEL_LIST -+done -+ -+# delete all whiteouted files from base -+echo -e "\nDeleting whiteout'ed files from base file system..." -+find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^D ' | sed -e 's/:D//' | while read ENTRY; do -+ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` -+ DEL_NAME=`echo $ENTRY | cut -d ' ' -f 2` -+ DEL_FILE=`echo $META_FILE | sed -e "s/$META_NAME/$DEL_NAME/" | sed -e 's/^\.\///'` -+ grep -x $DEL_FILE $TMP/$SKIP_DEL_LIST &> /dev/null -+ if [ $? -ne 0 ]; then -+ pushd $BASE &> /dev/null -+ COMMAND="rm -rf $DEL_FILE"; exec_command -+ popd &> /dev/null -+ else -+ echo " excluding: $DEL_FILE as in skip-del-list." -+ fi -+done -+ -+# create all dirs and update permissions -+echo -e "\nSetting up directory structures in base file system..." -+find . -type d | sed -e 's/^\.\///' | while read DIR; do -+ PERMS=`stat -c %a $DIR` -+ DIR_UID=`stat -c %u $DIR` -+ DIR_GID=`stat -c %g $DIR` -+ pushd $BASE &> /dev/null -+ if ! [ -d $DIR ]; then -+ COMMAND="mkdir -p $DIR"; exec_command -+ fi -+ COMMAND="chmod $PERMS $DIR"; exec_command -+ COMMAND="chown $DIR_UID:$DIR_GID $DIR"; exec_command -+ popd &> /dev/null -+done -+ -+# merge all non-directory files -+echo -e "\nMerging all non-directory files...." -+for i in b c p f l s; do -+ find . -type $i | sed -e 's/^\.\///' | grep -v "$META_NAME" | while read FILE; do -+ pushd $BASE #&> /dev/null -+ COMMAND="cp -df $STO/$FILE $BASE/$FILE"; exec_command -+ popd &> /dev/null -+ done -+done -+popd &> /dev/null -+ -+#rm $TMP/$SKIP_DEL_LIST -+ -+echo "Done!" -Index: linux-2.6.21.7/fs/mini_fo/mini_fo-overlay -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/mini_fo-overlay -@@ -0,0 +1,130 @@ -+#!/bin/bash -+# -+# Copyright (C) 2005 Markus Klotzbuecher -+# This program is free software; you can redistribute it and/or -+# modify it under the terms of the GNU General Public License -+# as published by the Free Software Foundation; either version -+# 2 of the License, or (at your option) any later version. -+# -+ -+HELP= -+SUFF= -+MNTP= -+MNT_DIR="/mnt" -+STO= -+STO_DIR="/tmp" -+BASE= -+ -+usage() -+{ -+cat <" in $STO_DIR, -+and mount point "mini_fo-" in $MNT_DIR. -+ -+Options: -+ -s -+ add given suffix to storage directory and the mount -+ point. This is usefull for overlaying one base directory -+ several times and avoiding conflicts with storage directory -+ names and mount points. -+ -+ -d -+ change the directory in which the storage directory will be -+ created (default is currently "$STO_DIR". -+ -+ -m -+ use an alternative directory to create the mini_fo -+ mountpoint (default is currently "$MNT_DIR". -+ -+ -h displays this message. -+ -+EOF -+exit 1; -+} -+ -+while getopts hm:s:d: OPTS -+ do -+ case $OPTS in -+ s) SUFF="$OPTARG";; -+ d) STO_DIR="$OPTARG";; -+ m) MNT_DIR="$OPTARG";; -+ h) HELP="set";; -+ ?) usage -+ exit 1;; -+ esac -+done -+shift $(($OPTIND - 1)) -+ -+BASE="$1" -+ -+if [ "x$HELP" == "xset" ]; then -+ usage -+ exit -1 -+fi -+ -+# fix suffix -+if [ "x$SUFF" != "x" ]; then -+ SUFF="-$SUFF" -+fi -+ -+# kill trailing slashes -+MNT_DIR=${MNT_DIR%/} -+STO_DIR=${STO_DIR%/} -+BASE=${BASE%/} -+ -+ -+if ! [ -d "$BASE" ]; then -+ echo "invalid base dir $BASE, run $0 -h for help." -+ exit -1 -+fi -+ -+# check opts -+if ! [ -d "$MNT_DIR" ]; then -+ echo "invalid mount dir $MNT_DIR, run $0 -h for help." -+ exit -1 -+fi -+ -+if ! [ -d "$STO_DIR" ]; then -+ echo "invalid sto_dir_dir $STO_DIR, run $0 -h for help." -+ exit -1 -+fi -+ -+MNTP="$MNT_DIR/mini_fo-`basename $BASE`$SUFF" -+STO="$STO_DIR/sto-`basename $BASE`$SUFF" -+ -+# create the mount point if it doesn't exist -+mkdir -p $MNTP -+if [ $? -ne 0 ]; then -+ echo "Error, failed to create mount point $MNTP" -+fi -+ -+mkdir -p $STO -+if [ $? -ne 0 ]; then -+ echo "Error, failed to create storage dir $STO" -+fi -+ -+# check if fs is already mounted -+mount | grep mini_fo | grep $MNTP &> /dev/null -+if [ $? -eq 0 ]; then -+ echo "Error, existing mini_fo mount at $MNTP." -+ exit -1 -+fi -+ -+mount | grep mini_fo | grep $STO &> /dev/null -+if [ $? -eq 0 ]; then -+ echo "Error, $STO seems to be used already." -+ exit -1 -+fi -+ -+# mount -+mount -t mini_fo -o base=$BASE,sto=$STO $BASE $MNTP -+ -+if [ $? -ne 0 ]; then -+ echo "Error, mounting failed, maybe no permisson to mount?" -+fi -Index: linux-2.6.21.7/fs/mini_fo/mmap.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/mmap.c -@@ -0,0 +1,637 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif /* HAVE_CONFIG_H */ -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+ -+#ifdef FIST_COUNT_WRITES -+/* for counting writes in the middle vs. regular writes */ -+unsigned long count_writes = 0, count_writes_middle = 0; -+#endif /* FIST_COUNT_WRITES */ -+ -+/* forward declaration of commit write and prepare write */ -+STATIC int mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to); -+STATIC int mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to); -+ -+ -+/* -+ * Function for handling creation of holes when lseek-ing past the -+ * end of the file and then writing some data. -+ */ -+int -+mini_fo_fill_zeros(file_t* file, page_t *page, unsigned from) -+{ -+ int err = 0; -+ dentry_t *dentry = file->f_dentry; -+ inode_t *inode = dentry->d_inode; -+ page_t *tmp_page; -+ int index; -+ -+ print_entry_location(); -+ -+ for (index = inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { -+ tmp_page = mini_fo_get1page(file, index); -+ if (IS_ERR(tmp_page)) { -+ err = PTR_ERR(tmp_page); -+ goto out; -+ } -+ -+ /* -+ * zero out rest of the contents of the page between the appropriate -+ * offsets. -+ */ -+ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); -+ -+ if (! (err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) -+ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); -+ -+ page_cache_release(tmp_page); -+ if (err < 0) -+ goto out; -+ if (current->need_resched) -+ schedule(); -+ } -+ -+ /* zero out appropriate parts of last page */ -+ -+ /* -+ * if the encoding type is block, then adjust the 'from' (where the -+ * zeroing will start) offset appropriately -+ */ -+ from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); -+ -+ if ((from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) { -+ -+ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, from - (inode->i_size & ~PAGE_CACHE_MASK)); -+ if (! (err = mini_fo_prepare_write(file, page, 0, PAGE_CACHE_SIZE))) -+ err = mini_fo_commit_write(file, page, 0, PAGE_CACHE_SIZE); -+ -+ if (err < 0) -+ goto out; -+ if (current->need_resched) -+ schedule(); -+ } -+ -+ out: -+ print_exit_status(err); -+ return err; -+} -+ -+ -+ -+STATIC int -+mini_fo_writepage(page_t *page) -+{ -+ int err = -EIO; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ page_t *hidden_page; -+ char *kaddr, *hidden_kaddr; -+ -+ print_entry_location(); -+ -+ inode = page->mapping->host; -+ hidden_inode = itohi(inode); -+ -+ /* -+ * writepage is called when shared mmap'ed files need to write -+ * their pages, while prepare/commit_write are called from the -+ * non-paged write() interface. (However, in 2.3 the two interfaces -+ * share the same cache, while in 2.2 they didn't.) -+ * -+ * So we pretty much have to duplicate much of what commit_write does. -+ */ -+ -+ /* find lower page (returns a locked page) */ -+ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); -+ if (!hidden_page) -+ goto out; -+ -+ /* get page address, and encode it */ -+ kaddr = (char *) kmap(page); -+ hidden_kaddr = (char*) kmap(hidden_page); -+ mini_fo_encode_block(kaddr, hidden_kaddr, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); -+ /* if encode_block could fail, then return error */ -+ kunmap(page); -+ kunmap(hidden_page); -+ -+ /* call lower writepage (expects locked page) */ -+ err = hidden_inode->i_mapping->a_ops->writepage(hidden_page); -+ -+ /* -+ * update mtime and ctime of lower level file system -+ * mini_fo' mtime and ctime are updated by generic_file_write -+ */ -+ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,1) -+ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ -+# endif /* kernel older than 2.4.1 */ -+ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ -+ -+ if (err) -+ ClearPageUptodate(page); -+ else -+ SetPageUptodate(page); -+ out: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1) -+ UnlockPage(page); -+# endif /* kernel 2.4.1 and newer */ -+ print_exit_status(err); -+ return err; -+} -+ -+ -+/* -+ * get one page from cache or lower f/s, return error otherwise. -+ * returns unlocked, up-to-date page (if ok), with increased refcnt. -+ */ -+page_t * -+mini_fo_get1page(file_t *file, int index) -+{ -+ page_t *page; -+ dentry_t *dentry; -+ inode_t *inode; -+ struct address_space *mapping; -+ int err; -+ -+ print_entry_location(); -+ -+ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ -+ inode = dentry->d_inode; -+ mapping = inode->i_mapping; -+ -+ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); -+ if (index < 0) { -+ printk("%s BUG: index=%d\n", __FUNCTION__, index); -+ page = ERR_PTR(-EIO); -+ goto out; -+ } -+ page = read_cache_page(mapping, -+ index, -+ (filler_t *) mapping->a_ops->readpage, -+ (void *) file); -+ if (IS_ERR(page)) -+ goto out; -+ wait_on_page(page); -+ if (!Page_Uptodate(page)) { -+ lock_page(page); -+ err = mapping->a_ops->readpage(file, page); -+ if (err) { -+ page = ERR_PTR(err); -+ goto out; -+ } -+ wait_on_page(page); -+ if (!Page_Uptodate(page)) { -+ page = ERR_PTR(-EIO); -+ goto out; -+ } -+ } -+ -+ out: -+ print_exit_pointer(page); -+ return page; -+} -+ -+ -+/* -+ * get one page from cache or lower f/s, return error otherwise. -+ * similar to get1page, but doesn't guarantee that it will return -+ * an unlocked page. -+ */ -+page_t * -+mini_fo_get1page_cached(file_t *file, int index) -+{ -+ page_t *page; -+ dentry_t *dentry; -+ inode_t *inode; -+ struct address_space *mapping; -+ int err; -+ -+ print_entry_location(); -+ -+ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ -+ inode = dentry->d_inode; -+ mapping = inode->i_mapping; -+ -+ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); -+ if (index < 0) { -+ printk("%s BUG: index=%d\n", __FUNCTION__, index); -+ page = ERR_PTR(-EIO); -+ goto out; -+ } -+ page = read_cache_page(mapping, -+ index, -+ (filler_t *) mapping->a_ops->readpage, -+ (void *) file); -+ if (IS_ERR(page)) -+ goto out; -+ -+ out: -+ print_exit_pointer(page); -+ return page; -+} -+ -+ -+/* -+ * readpage is called from generic_page_read and the fault handler. -+ * If your file system uses generic_page_read for the read op, it -+ * must implement readpage. -+ * -+ * Readpage expects a locked page, and must unlock it. -+ */ -+STATIC int -+mini_fo_do_readpage(file_t *file, page_t *page) -+{ -+ int err = -EIO; -+ dentry_t *dentry; -+ file_t *hidden_file = NULL; -+ dentry_t *hidden_dentry; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ char *page_data; -+ page_t *hidden_page; -+ char *hidden_page_data; -+ int real_size; -+ -+ print_entry_location(); -+ -+ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ -+ if (ftopd(file) != NULL) -+ hidden_file = ftohf(file); -+ hidden_dentry = dtohd(dentry); -+ inode = dentry->d_inode; -+ hidden_inode = itohi(inode); -+ -+ fist_dprint(7, "%s: requesting page %d from file %s\n", __FUNCTION__, page->index, dentry->d_name.name); -+ -+ MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages); -+ MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); -+ FOR_EACH_PAGE -+ CURRENT_HIDDEN_PAGE = NULL; -+ -+ /* find lower page (returns a locked page) */ -+ FOR_EACH_PAGE { -+ fist_dprint(8, "%s: Current page index = %d\n", __FUNCTION__, CURRENT_HIDDEN_PAGEINDEX); -+ CURRENT_HIDDEN_PAGE = read_cache_page(hidden_inode->i_mapping, -+ CURRENT_HIDDEN_PAGEINDEX, -+ (filler_t *) hidden_inode->i_mapping->a_ops->readpage, -+ (void *) hidden_file); -+ if (IS_ERR(CURRENT_HIDDEN_PAGE)) { -+ err = PTR_ERR(CURRENT_HIDDEN_PAGE); -+ CURRENT_HIDDEN_PAGE = NULL; -+ goto out_release; -+ } -+ } -+ -+ /* -+ * wait for the page data to show up -+ * (signaled by readpage as unlocking the page) -+ */ -+ FOR_EACH_PAGE { -+ wait_on_page(CURRENT_HIDDEN_PAGE); -+ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { -+ /* -+ * call readpage() again if we returned from wait_on_page with a -+ * page that's not up-to-date; that can happen when a partial -+ * page has a few buffers which are ok, but not the whole -+ * page. -+ */ -+ lock_page(CURRENT_HIDDEN_PAGE); -+ err = hidden_inode->i_mapping->a_ops->readpage(hidden_file, -+ CURRENT_HIDDEN_PAGE); -+ if (err) { -+ CURRENT_HIDDEN_PAGE = NULL; -+ goto out_release; -+ } -+ wait_on_page(CURRENT_HIDDEN_PAGE); -+ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { -+ err = -EIO; -+ goto out_release; -+ } -+ } -+ } -+ -+ /* map pages, get their addresses */ -+ page_data = (char *) kmap(page); -+ FOR_EACH_PAGE -+ CURRENT_HIDDEN_PAGEDATA = (char *) kmap(CURRENT_HIDDEN_PAGE); -+ -+ /* if decode_block could fail, then return error */ -+ err = 0; -+ real_size = hidden_inode->i_size - (page->index << PAGE_CACHE_SHIFT); -+ if (real_size <= 0) -+ memset(page_data, 0, PAGE_CACHE_SIZE); -+ else if (real_size < PAGE_CACHE_SIZE) { -+ mini_fo_decode_block(hidden_page_data, page_data, real_size, inode, inode->i_sb, page->index); -+ memset(page_data + real_size, 0, PAGE_CACHE_SIZE - real_size); -+ } else -+ mini_fo_decode_block(hidden_page_data, page_data, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); -+ -+ FOR_EACH_PAGE -+ kunmap(CURRENT_HIDDEN_PAGE); -+ kunmap(page); -+ -+ out_release: -+ FOR_EACH_PAGE -+ if (CURRENT_HIDDEN_PAGE) -+ page_cache_release(CURRENT_HIDDEN_PAGE); /* undo read_cache_page */ -+ -+ FREE_PAGE_POINTERS(hidden_pages, num_hidden_pages); -+ FREE_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); -+ -+ out: -+ if (err == 0) -+ SetPageUptodate(page); -+ else -+ ClearPageUptodate(page); -+ -+ print_exit_status(err); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_readpage(file_t *file, page_t *page) -+{ -+ int err; -+ print_entry_location(); -+ -+ err = mini_fo_do_readpage(file, page); -+ -+ /* -+ * we have to unlock our page, b/c we _might_ have gotten a locked page. -+ * but we no longer have to wakeup on our page here, b/c UnlockPage does -+ * it -+ */ -+ UnlockPage(page); -+ -+ print_exit_status(err); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to) -+{ -+ int err = 0; -+ -+ print_entry_location(); -+ -+ /* -+ * we call kmap(page) only here, and do the kunmap -+ * and the actual downcalls, including unlockpage and uncache -+ * in commit_write. -+ */ -+ kmap(page); -+ -+ /* fast path for whole page writes */ -+ if (from == 0 && to == PAGE_CACHE_SIZE) -+ goto out; -+ /* read the page to "revalidate" our data */ -+ /* call the helper function which doesn't unlock the page */ -+ if (!Page_Uptodate(page)) -+ err = mini_fo_do_readpage(file, page); -+ -+ out: -+ print_exit_status(err); -+ return err; -+} -+ -+ -+ -+STATIC int -+mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to) -+{ -+ int err = -ENOMEM; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ page_t *hidden_page; -+ file_t *hidden_file = NULL; -+ loff_t pos; -+ unsigned bytes = to - from; -+ unsigned hidden_from, hidden_to, hidden_bytes; -+ -+ print_entry_location(); -+ -+ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ -+ hidden_inode = itohi(inode); -+ -+ ASSERT(file != NULL); -+ /* -+ * here we have a kmapped page, with data from the user copied -+ * into it. we need to encode_block it, and then call the lower -+ * commit_write. We also need to simulate same behavior of -+ * generic_file_write, and call prepare_write on the lower f/s first. -+ */ -+#ifdef FIST_COUNT_WRITES -+ count_writes++; -+# endif /* FIST_COUNT_WRITES */ -+ -+ /* this is append and/or extend -- we can't have holes so fill them in */ -+ if (page->index > (hidden_inode->i_size >> PAGE_CACHE_SHIFT)) { -+ page_t *tmp_page; -+ int index; -+ for (index = hidden_inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { -+ tmp_page = mini_fo_get1page(file, index); -+ if (IS_ERR(tmp_page)) { -+ err = PTR_ERR(tmp_page); -+ goto out; -+ } -+ /* zero out the contents of the page at the appropriate offsets */ -+ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); -+ if (!(err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) -+ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); -+ page_cache_release(tmp_page); -+ if (err < 0) -+ goto out; -+ if (current->need_resched) -+ schedule(); -+ } -+ } -+ -+ if (ftopd(file) != NULL) -+ hidden_file = ftohf(file); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_inode->i_mutex); -+#else -+ down(&hidden_inode->i_sem); -+#endif -+ /* find lower page (returns a locked page) */ -+ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); -+ if (!hidden_page) -+ goto out; -+ -+#if FIST_ENCODING_BLOCKSIZE > 1 -+# error encoding_blocksize greater than 1 is not yet supported -+# endif /* FIST_ENCODING_BLOCKSIZE > 1 */ -+ -+ hidden_from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); -+ hidden_to = ((to + FIST_ENCODING_BLOCKSIZE - 1) & (~(FIST_ENCODING_BLOCKSIZE - 1))); -+ if ((page->index << PAGE_CACHE_SHIFT) + to > hidden_inode->i_size) { -+ -+ /* -+ * if this call to commit_write had introduced holes and the code -+ * for handling holes was invoked, then the beginning of this page -+ * must be zeroed out -+ * zero out bytes from 'size_of_file%pagesize' to 'from'. -+ */ -+ if ((hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) -+ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)); -+ -+ } -+ hidden_bytes = hidden_to - hidden_from; -+ -+ /* call lower prepare_write */ -+ err = -EINVAL; -+ if (hidden_inode->i_mapping && -+ hidden_inode->i_mapping->a_ops && -+ hidden_inode->i_mapping->a_ops->prepare_write) -+ err = hidden_inode->i_mapping->a_ops->prepare_write(hidden_file, -+ hidden_page, -+ hidden_from, -+ hidden_to); -+ if (err) -+ /* don't leave locked pages behind, esp. on an ENOSPC */ -+ goto out_unlock; -+ -+ fist_dprint(8, "%s: encoding %d bytes\n", __FUNCTION__, hidden_bytes); -+ mini_fo_encode_block((char *) page_address(page) + hidden_from, (char*) page_address(hidden_page) + hidden_from, hidden_bytes, inode, inode->i_sb, page->index); -+ /* if encode_block could fail, then goto unlock and return error */ -+ -+ /* call lower commit_write */ -+ err = hidden_inode->i_mapping->a_ops->commit_write(hidden_file, -+ hidden_page, -+ hidden_from, -+ hidden_to); -+ -+ if (err < 0) -+ goto out_unlock; -+ -+ err = bytes; /* convert error to no. of bytes */ -+ -+ inode->i_blocks = hidden_inode->i_blocks; -+ /* we may have to update i_size */ -+ pos = (page->index << PAGE_CACHE_SHIFT) + to; -+ if (pos > inode->i_size) -+ inode->i_size = pos; -+ -+ /* -+ * update mtime and ctime of lower level file system -+ * mini_fo' mtime and ctime are updated by generic_file_write -+ */ -+ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; -+ -+ mark_inode_dirty_sync(inode); -+ -+ out_unlock: -+ UnlockPage(hidden_page); -+ page_cache_release(hidden_page); -+ kunmap(page); /* kmap was done in prepare_write */ -+ out: -+ /* we must set our page as up-to-date */ -+ if (err < 0) -+ ClearPageUptodate(page); -+ else -+ SetPageUptodate(page); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_inode->i_mutex); -+#else -+ up(&hidden_inode->i_sem); -+#endif -+ print_exit_status(err); -+ return err; /* assume all is ok */ -+} -+ -+ -+STATIC int -+mini_fo_bmap(struct address_space *mapping, long block) -+{ -+ int err = 0; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ -+ print_entry_location(); -+ -+ inode = (inode_t *) mapping->host; -+ hidden_inode = itohi(inode); -+ -+ if (hidden_inode->i_mapping->a_ops->bmap) -+ err = hidden_inode->i_mapping->a_ops->bmap(hidden_inode->i_mapping, block); -+ print_exit_location(); -+ return err; -+} -+ -+ -+/* -+ * This function is copied verbatim from mm/filemap.c. -+ * XXX: It should be simply moved to some header file instead -- bug Al about it! -+ */ -+static inline int sync_page(struct page *page) -+{ -+ struct address_space *mapping = page->mapping; -+ -+ if (mapping && mapping->a_ops && mapping->a_ops->sync_page) -+ return mapping->a_ops->sync_page(page); -+ return 0; -+} -+ -+ -+/* -+ * XXX: we may not need this function if not FIST_FILTER_DATA. -+ * FIXME: for FIST_FILTER_SCA, get all lower pages and sync them each. -+ */ -+STATIC int -+mini_fo_sync_page(page_t *page) -+{ -+ int err = 0; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ page_t *hidden_page; -+ -+ print_entry_location(); -+ -+ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ -+ hidden_inode = itohi(inode); -+ -+ /* find lower page (returns a locked page) */ -+ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); -+ if (!hidden_page) -+ goto out; -+ -+ err = sync_page(hidden_page); -+ -+ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ -+ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ -+ -+ out: -+ print_exit_status(err); -+ return err; -+} -Index: linux-2.6.21.7/fs/mini_fo/README -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/README -@@ -0,0 +1,163 @@ -+README for the mini_fo overlay file system -+========================================= -+ -+ -+WHAT IS MINI_FO? -+---------------- -+ -+mini_fo is a virtual kernel file system that can make read-only -+file systems writable. This is done by redirecting modifying operations -+to a writeable location called "storage directory", and leaving the -+original data in the "base directory" untouched. When reading, the -+file system merges the modifed and original data so that only the -+newest versions will appear. This occurs transparently to the user, -+who can access the data like on any other read-write file system. -+ -+Base and storage directories may be located on the same or on -+different partitions and may be of different file system types. While -+the storage directory obviously needs to be writable, the base may or -+may not be writable, what doesn't matter as it will no be modified -+anyway. -+ -+ -+WHAT IS GOOD FOR? -+----------------- -+ -+The primary purpose of the mini_fo file system is to allow easy -+software updates to embedded systems, that often store their root -+file system in a read-only flash file system, but there are many -+more as for example sandboxing, or for allowing live-cds to -+permanently store information. -+ -+ -+BUILDING -+-------- -+This should be simple. Adjust the Makefile to point to the correct -+kernel headers you want to build the module for. Then: -+ -+ # make -+ -+should build "mini_fo.o" for a 2.4 kernel or "mini_fo.ko" for a 2.6 -+kernel. -+ -+If you are building the module for you current kernel, you can install -+the module (as root): -+ -+ # make install -+ -+or uninstall with -+ -+ # make uninstall -+ -+ -+USING THE FILE SYSTEM -+-------------------- -+ -+the general mount syntax is: -+ -+ mount -t mini_fo -o base=,sto=\ -+ -+ -+Example: -+ -+You have mounted a cdrom to /mnt/cdrom and want to modifiy some files -+on it: -+ -+load the module (as root) -+ -+ # insmod mini_fo.o for a 2.4 kernel or -+ -+ # insmod mini_fo.ko for a 2.6 kernel -+ -+ -+create a storage dir in tmp and a mountpoint for mini_fo: -+ -+ # mkdir /tmp/sto -+ # mkdir /mnt/mini_fo -+ -+and mount the mini_fo file system: -+ -+ # mount -t mini_fo -o base=/mnt/cdrom,sto=/tmp/sto /mnt/cdrom /mnt/mini_fo -+ -+ -+Now the data stored on the cd can be accessed via the mini_fo -+mountpoint just like any read-write file system, files can be modified -+and deleted, new ones can be created and so on. When done unmount the -+file system: -+ -+ # unmount /mnt/mini_fo -+ -+Note that if the file system is mounted again using the same storage -+file system, of course it will appear in the modified state again. If -+you remount it using an new empty storage directory, it will be -+unmodified. Therefore by executing: -+ -+ # cd /tmp/sto -+ # rm -rf * -+ -+you can nuke all the changes you made to the original file system. But -+ remember NEVER do this while the mini_fo file system is mounted! -+ -+ -+Alternatively you can use the mini_fo-overlay bash script, that -+simplifies managing mini_fo mounts. See TOOLS Section. -+ -+ -+TOOLS -+----- -+ -+mini_fo-merge (experimental): -+ -+This is a bash script that will merge changes contained in the storage -+directory back to the base directory. This allows mini_fo to function -+as a cache file system by overlaying a slow (network, ...) file system -+and using a fast (ramdisk, ...) as storage. When done, changes can be -+merged back to the (slow) base with mini_fo-merge. See "mini_fo-merge -+-h" for details. -+ -+It can be usefull for merging changes back after a successfull test -+(patches, software updates...) -+ -+ -+mini_fo-overlay: -+ -+This bash script simplifies managing one or more mini_fo mounts. For -+overlaying a directory called "basedir1", you can just call: -+ -+ # mini_fo-overlay basedir1 -+ -+This will mount mini_fo with "basedir1" as base, "/tmp/sto-basedir1/" -+as storage to "/mnt/mini_fo-basedir1/". It has more options though, -+type "mini_fo-overlay -h" for details. -+ -+ -+DOCUMENTATION, REPORTING BUGS, GETTING HELP -+------------------------------------------- -+ -+Please visit the mini_fo project page at: -+ -+http://www.denx.de/twiki/bin/view/Know/MiniFOHome -+ -+ -+WARNINGS -+-------- -+ -+Never modify the base or the storage directorys while the mini_fo -+file system is mounted, or you might crash you system. Simply accessing -+and reading should not cause any trouble. -+ -+Exporting a mini_fo mount point via NFS has not been tested, and may -+or may not work. -+ -+Check the RELEASE_NOTES for details on bugs and features. -+ -+ -+ -+Copyright (C) 2004, 2005 Markus Klotzbuecher -+ -+This program is free software; you can redistribute it and/or -+modify it under the terms of the GNU General Public License -+as published by the Free Software Foundation; either version -+2 of the License, or (at your option) any later version. -+ -+ -Index: linux-2.6.21.7/fs/mini_fo/RELEASE_NOTES -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/RELEASE_NOTES -@@ -0,0 +1,111 @@ -+Release: mini_fo-0.6.1 (v0-6-1) -+Date: 21.09.2005 -+ -+ -+Changes: -+-------- -+v0-6-1: -+ -+- bugfixes (see ChangeLog) -+ -+- two helper scripts "mini_fo_merge" and "mini_fo_overlay" (see -+ README for details). -+ -+v0-6-0: -+ -+- Support for 2.4 and 2.6 (see Makefile) -+ -+- Partial hard link support (creating works as expected, but already -+ existing links in the base file system will be treated as if they -+ were individual files). -+ -+- Various bugfixes and cleanups. -+ -+ -+v0-6-0-pre1: -+ -+- This is mini_fo-0-6-0-pre1! This release is a complete rewrite of -+ many vital mini_fo parts such as the old whiteout list code which -+ has been replaced by the new META subsystem. -+ -+- Light weight directory renaming implemented. This means if a -+ directory is renamed via the mini_fo filesystem this will no longer -+ result in a complete copy in storage, instead only one empty -+ directory will be created. All base filed contained in the original -+ directory stay there until modified. -+ -+- Special files (creating, renaming, deleting etc.) now working. -+ -+- Many bugfixes and cleanup, mini_fo is now a lot more stable. -+ -+ -+v0-5-10: -+ -+- Final release of the 0-5-* versions. Next will be a complete rewrite -+ of many features. This release contains several bugfixes related to -+ directory renaming. -+ -+ -+v0-5-10-pre6: -+ -+- Lots of cleanup and several bugfixes related to directory deleting -+ -+- Directory renaming suddenly works, what is most likely due to the -+ fact tha that "mv" is smart: if the classic rename doesn't work it -+ will assume that source and target file are on different fs and will -+ copy the directory and try to remove the source directory. Until -+ directory removing wasn't implemented, it would fail to do this and -+ rollback. -+ So, directory renaming works for now, but it doesn't yet do what you -+ would expect from a overlay fs, so use with care. -+ -+ -+v0-5-10-pre5: -+ -+- implemented directory deleting -+- made parsing of mount options more stable -+- New format of mount options! (See README) -+- I can't reproduce the unknown panic with 2.4.25 anymore, so I'll -+ happily assume it never existed! -+ -+ -+Implemented features: -+--------------------- -+ -+- creating hard links (see BUGS on already existing hard links) -+- lightweight directory renaming -+- renaming device files, pipes, sockets, etc. -+- creating, renaming, deleting of special files -+- deleting directorys -+- general directory reading (simple "ls" ) -+- creating files in existing directorys -+- creating directorys -+- renaming files. -+- reading and writing files (involves opening) -+- appending to files (creates copy in storage) -+- deleting files -+- llseek works too, what allows editors to work -+- persistency (a deleted file stay deleted over remounts) -+- use of symbolic links -+- creating of device files -+ -+ -+Not (yet) implemented features: -+------------------------------- -+ -+- full hard link support. -+ -+ -+ -+BUGS: -+----- -+ -+Hard links in the base file system will be treated as individual -+files, not as links to one inode. -+ -+The main problem with hard links isn't allowing to create them, but -+their pure existence. If you modify a base hard link, the changes made -+will only show up on this link, the other link will remain in the -+original state. I hope to fix this someday. Please note that this does -+not effect the special hard links '.' and '..', that are handled -+seperately by the lower fs. -Index: linux-2.6.21.7/fs/mini_fo/state.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/state.c -@@ -0,0 +1,620 @@ -+/* -+ * Copyright (C) 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif /* HAVE_CONFIG_H */ -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+ -+/* create the storage file, setup new states */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd) -+#else -+int create_sto_reg_file(dentry_t *dentry, int mode) -+#endif -+{ -+ int err = 0; -+ inode_t *dir; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ if(exists_in_storage(dentry)) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: wrong type or state.\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ err = get_neg_sto_dentry(dentry); -+ -+ if (err) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR getting neg. sto dentry.\n"); -+ goto out; -+ } -+ -+ dir = dentry->d_parent->d_inode; -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* lock parent */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ mode, nd); -+#else -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ mode); -+#endif -+ if(err) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file.\n"); -+ goto out_lock; -+ } -+ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file [2].\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ -+ /* interpose the new inode */ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == NON_EXISTANT) { -+ dtost(dentry) = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == UNMODIFIED) { -+ dtost(dentry) = MODIFIED; -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ } -+ fist_copy_attr_timesizes(dentry->d_parent->d_inode, -+ hidden_sto_dir_dentry->d_inode); -+ -+ out_lock: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+/* create the sto dir, setup states */ -+int create_sto_dir(dentry_t *dentry, int mode) -+{ -+ int err = 0; -+ inode_t *dir; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ /* had to take the "!S_ISDIR(mode))" check out, because it failed */ -+ if(exists_in_storage(dentry)) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: wrong type or state.\\ -+n"); -+ err = -EINVAL; -+ goto out; -+ } -+ -+ err = get_neg_sto_dentry(dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ dir = dentry->d_parent->d_inode; -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ mode); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir.\n"); -+ goto out_lock; -+ } -+ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir [2].\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ -+ /* interpose the new inode */ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtopd(dentry)->state == NON_EXISTANT) { -+ dtopd(dentry)->state = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtopd(dentry)->state == UNMODIFIED) { -+ dtopd(dentry)->state = MODIFIED; -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR, invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ } -+ -+ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); -+ -+ /* initalize the wol list */ -+ itopd(dentry->d_inode)->deleted_list_size = -1; -+ itopd(dentry->d_inode)->renamed_list_size = -1; -+ meta_build_lists(dentry); -+ -+ -+ out_lock: -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+int create_sto_nod(dentry_t *dentry, int mode, dev_t dev) -+#else -+int create_sto_nod(dentry_t *dentry, int mode, int dev) -+#endif -+{ -+ int err = 0; -+ inode_t *dir; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ if(exists_in_storage(dentry)) { -+ err = -EEXIST; -+ goto out; -+ } -+ err = get_neg_sto_dentry(dentry); -+ -+ if (err) { -+ printk(KERN_CRIT "mini_fo: create_sto_nod: ERROR getting neg. sto dentry.\n"); -+ goto out; -+ } -+ -+ dir = dentry->d_parent->d_inode; -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* lock parent */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode, dev); -+ if(err) -+ goto out_lock; -+ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: create_sto_nod: creating storage inode failed [1].\n"); -+ err = -EINVAL; /* return something indicating failure */ -+ goto out_lock; -+ } -+ -+ /* interpose the new inode */ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == NON_EXISTANT) { -+ dtost(dentry) = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == UNMODIFIED) { -+ dtost(dentry) = MODIFIED; -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: create_sto_nod: error, invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ } -+ -+ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); -+ -+ out_lock: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+ -+/* unimplemented (and possibly not usefull): -+ -+ nondir-del_to_del_rew -+ nondir-non_exist_to_creat -+ -+ dir-unmod_to_del -+ dir-mod_to_del -+ dir-creat_to_del -+ dir-del_rew_to_del -+ dir-del_to_del_rew -+ dir-non_exist_to_creat -+*/ -+ -+ -+/* bring a file of any type from state UNMODIFIED to MODIFIED */ -+int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag) -+{ -+ int err = 0; -+ struct vfsmount *tgt_mnt; -+ struct vfsmount *src_mnt; -+ dentry_t *tgt_dentry; -+ dentry_t *src_dentry; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if((dtost(dentry) != UNMODIFIED) || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ err = get_neg_sto_dentry(dentry); -+ -+ if (err) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR getting neg. sto dentry.\n"); -+ goto out; -+ } -+ -+ /* create sto file */ -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* lock parent */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+ /* handle different types of nondirs */ -+ if(S_ISCHR(dentry->d_inode->i_mode) || -+ S_ISBLK(dentry->d_inode->i_mode)) { -+ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dtohd(dentry)->d_inode->i_mode, -+ dtohd(dentry)->d_inode->i_rdev); -+ } -+ -+ else if(S_ISREG(dentry->d_inode->i_mode)) { -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dtohd(dentry)->d_inode->i_mode, NULL); -+#else -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dtohd(dentry)->d_inode->i_mode); -+#endif -+ } -+ if(err) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR creating sto file.\n"); -+ goto out_lock; -+ } -+ -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR, invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ -+ fist_copy_attr_timesizes(dentry->d_parent->d_inode, -+ hidden_sto_dir_dentry->d_inode); -+ dtost(dentry) = MODIFIED; -+ -+ /* copy contents if regular file and cp_flag = 1 */ -+ if((cp_flag == 1) && S_ISREG(dentry->d_inode->i_mode)) { -+ -+ /* unlock first */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ dput(hidden_sto_dir_dentry); -+ -+ tgt_dentry = dtohd2(dentry); -+ tgt_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ src_dentry = dtohd(dentry); -+ src_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt; -+ -+ err = mini_fo_cp_cont(tgt_dentry, tgt_mnt, -+ src_dentry, src_mnt); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR copying contents.\n"); -+ } -+ goto out; -+ } -+ -+ out_lock: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+/* this function is currently identical to nondir_creat_to_del */ -+int nondir_del_rew_to_del(dentry_t *dentry) -+{ -+ return nondir_creat_to_del(dentry); -+} -+ -+int nondir_creat_to_del(dentry_t *dentry) -+{ -+ int err = 0; -+ -+ inode_t *hidden_sto_dir_inode; -+ dentry_t *hidden_sto_dir_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ check_mini_fo_dentry(dentry); -+ -+ /* for now this function serves for both state DEL_REWRITTEN and -+ * CREATED */ -+ if(!(dtost(dentry) == CREATED || (dtost(dentry) == DEL_REWRITTEN)) || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_mod_to_del/del_rew_to_del: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* avoid destroying the hidden inode if the file is in use */ -+ dget(hidden_sto_dentry); -+ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ -+ dtost(dentry) = NON_EXISTANT; -+ -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ out: -+ return err; -+} -+ -+int nondir_mod_to_del(dentry_t *dentry) -+{ -+ int err; -+ dentry_t *hidden_sto_dentry; -+ inode_t *hidden_sto_dir_inode; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) != MODIFIED || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_mod_to_del: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* avoid destroying the hidden inode if the file is in use */ -+ dget(hidden_sto_dentry); -+ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ -+ /* dput base dentry, this will relase the inode and free the -+ * dentry, as we will never need it again. */ -+ dput(dtohd(dentry)); -+ dtohd(dentry) = NULL; -+ dtost(dentry) = DELETED; -+ -+ /* add deleted file to META-file */ -+ meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ out: -+ return err; -+} -+ -+int nondir_unmod_to_del(dentry_t *dentry) -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) != UNMODIFIED || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_del: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ /* next we have to get a negative dentry for the storage file */ -+ err = get_neg_sto_dentry(dentry); -+ -+ if(err) -+ goto out; -+ -+ /* add deleted file to META lists */ -+ err = meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ -+ if(err) -+ goto out; -+ -+ /* dput base dentry, this will relase the inode and free the -+ * dentry, as we will never need it again. */ -+ dput(dtohd(dentry)); -+ dtohd(dentry) = NULL; -+ dtost(dentry) = DELETED; -+ -+ out: -+ return err; -+} -+ -+/* bring a dir from state UNMODIFIED to MODIFIED */ -+int dir_unmod_to_mod(dentry_t *dentry) -+{ -+ int err; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) != UNMODIFIED || -+ !S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ /* this creates our dir incl. sto. structure */ -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ out: -+ return err; -+} -+ -Index: linux-2.6.21.7/fs/mini_fo/super.c -=================================================================== ---- /dev/null -+++ linux-2.6.21.7/fs/mini_fo/super.c -@@ -0,0 +1,281 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+ -+STATIC void -+mini_fo_read_inode(inode_t *inode) -+{ -+ static struct address_space_operations mini_fo_empty_aops; -+ -+ __itopd(inode) = kmalloc(sizeof(struct mini_fo_inode_info), GFP_KERNEL); -+ if (!itopd(inode)) { -+ printk("<0>%s:%s:%d: No kernel memory!\n", __FILE__, __FUNCTION__, __LINE__); -+ ASSERT(NULL); -+ } -+ itohi(inode) = NULL; -+ itohi2(inode) = NULL; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ inode->i_version++; -+#else -+ inode->i_version = ++event; /* increment inode version */ -+#endif -+ inode->i_op = &mini_fo_main_iops; -+ inode->i_fop = &mini_fo_main_fops; -+#if 0 -+ /* -+ * XXX: To export a file system via NFS, it has to have the -+ * FS_REQUIRES_DEV flag, so turn it on. But should we inherit it from -+ * the lower file system, or can we allow our file system to be exported -+ * even if the lower one cannot be natively exported. -+ */ -+ inode->i_sb->s_type->fs_flags |= FS_REQUIRES_DEV; -+ /* -+ * OK, the above was a hack, which is now turned off because it may -+ * cause a panic/oops on some systems. The correct way to export a -+ * "nodev" filesystem is via using nfs-utils > 1.0 and the "fsid=" export -+ * parameter, which requires 2.4.20 or later. -+ */ -+#endif -+ /* I don't think ->a_ops is ever allowed to be NULL */ -+ inode->i_mapping->a_ops = &mini_fo_empty_aops; -+} -+ -+ -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+/* -+ * No need to call write_inode() on the lower inode, as it -+ * will have been marked 'dirty' anyway. But we might need -+ * to write some of our own stuff to disk. -+ */ -+STATIC void -+mini_fo_write_inode(inode_t *inode, int sync) -+{ -+ print_entry_location(); -+ print_exit_location(); -+} -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ -+ -+STATIC void -+mini_fo_put_inode(inode_t *inode) -+{ -+ /* -+ * This is really funky stuff: -+ * Basically, if i_count == 1, iput will then decrement it and this inode will be destroyed. -+ * It is currently holding a reference to the hidden inode. -+ * Therefore, it needs to release that reference by calling iput on the hidden inode. -+ * iput() _will_ do it for us (by calling our clear_inode), but _only_ if i_nlink == 0. -+ * The problem is, NFS keeps i_nlink == 1 for silly_rename'd files. -+ * So we must for our i_nlink to 0 here to trick iput() into calling our clear_inode. -+ */ -+ if (atomic_read(&inode->i_count) == 1) -+ inode->i_nlink = 0; -+} -+ -+ -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+/* -+ * we now define delete_inode, because there are two VFS paths that may -+ * destroy an inode: one of them calls clear inode before doing everything -+ * else that's needed, and the other is fine. This way we truncate the inode -+ * size (and its pages) and then clear our own inode, which will do an iput -+ * on our and the lower inode. -+ */ -+STATIC void -+mini_fo_delete_inode(inode_t *inode) -+{ -+ print_entry_location(); -+ -+ fist_checkinode(inode, "mini_fo_delete_inode IN"); -+ inode->i_size = 0; /* every f/s seems to do that */ -+ clear_inode(inode); -+ -+ print_exit_location(); -+} -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ -+ -+/* final actions when unmounting a file system */ -+STATIC void -+mini_fo_put_super(super_block_t *sb) -+{ -+ if (stopd(sb)) { -+ mntput(stopd(sb)->hidden_mnt); -+ mntput(stopd(sb)->hidden_mnt2); -+ -+ /* mk: no! dput(stopd(sb)->base_dir_dentry); -+ dput(stopd(sb)->storage_dir_dentry); */ -+ -+ kfree(stopd(sb)); -+ __stopd(sb) = NULL; -+ } -+} -+ -+ -+#ifdef NOT_NEEDED -+/* -+ * This is called in do_umount before put_super. -+ * The superblock lock is not held yet. -+ * We probably do not need to define this or call write_super -+ * on the hidden_sb, because sync_supers() will get to hidden_sb -+ * sooner or later. But it is also called from file_fsync()... -+ */ -+STATIC void -+mini_fo_write_super(super_block_t *sb) -+{ -+ return; -+} -+#endif /* NOT_NEEDED */ -+ -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_statfs(struct dentry *d, struct kstatfs *buf) -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_statfs(super_block_t *sb, struct kstatfs *buf) -+#else -+mini_fo_statfs(super_block_t *sb, struct statfs *buf) -+#endif -+{ -+ int err = 0; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ struct dentry *hidden_d; -+ -+ hidden_d = dtohd(d); -+ err = vfs_statfs(hidden_d, buf); -+#else -+ super_block_t *hidden_sb; -+ -+ hidden_sb = stohs(sb); -+ err = vfs_statfs(hidden_sb, buf); -+#endif -+ -+ return err; -+} -+ -+ -+/* -+ * XXX: not implemented. This is not allowed yet. -+ * Should we call this on the hidden_sb? Probably not. -+ */ -+STATIC int -+mini_fo_remount_fs(super_block_t *sb, int *flags, char *data) -+{ -+ //printk(KERN_CRIT "mini_fo_remount_fs: WARNING, this function is umimplemented.\n"); -+ return -ENOSYS; -+} -+ -+ -+/* -+ * Called by iput() when the inode reference count reached zero -+ * and the inode is not hashed anywhere. Used to clear anything -+ * that needs to be, before the inode is completely destroyed and put -+ * on the inode free list. -+ */ -+STATIC void -+mini_fo_clear_inode(inode_t *inode) -+{ -+ /* -+ * Decrement a reference to a hidden_inode, which was incremented -+ * by our read_inode when it was created initially. -+ */ -+ -+ /* release the wol_list */ -+ if(S_ISDIR(inode->i_mode)) { -+ __meta_put_lists(inode); -+ } -+ -+ /* mk: fan out fun */ -+ if(itohi(inode)) -+ iput(itohi(inode)); -+ if(itohi2(inode)) -+ iput(itohi2(inode)); -+ -+ // XXX: why this assertion fails? -+ // because it doesn't like us -+ // ASSERT((inode->i_state & I_DIRTY) == 0); -+ kfree(itopd(inode)); -+ __itopd(inode) = NULL; -+} -+ -+ -+/* -+ * Called in do_umount() if the MNT_FORCE flag was used and this -+ * function is defined. See comment in linux/fs/super.c:do_umount(). -+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent -+ * code can actually succeed and won't leave tasks that need handling. -+ * -+ * PS. I wonder if this is somehow useful to undo damage that was -+ * left in the kernel after a user level file server (such as amd) -+ * dies. -+ */ -+STATIC void -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_umount_begin(struct vfsmount *mnt, int flags) -+{ -+ struct vfsmount *hidden_mnt; -+ -+ hidden_mnt = stopd(mnt->mnt_sb)->hidden_mnt; -+ -+ if (hidden_mnt->mnt_sb->s_op->umount_begin) -+ hidden_mnt->mnt_sb->s_op->umount_begin(hidden_mnt, flags); -+ -+} -+#else -+mini_fo_umount_begin(super_block_t *sb) -+{ -+ super_block_t *hidden_sb; -+ -+ hidden_sb = stohs(sb); -+ -+ if (hidden_sb->s_op->umount_begin) -+ hidden_sb->s_op->umount_begin(hidden_sb); -+ -+} -+#endif -+ -+ -+struct super_operations mini_fo_sops = -+{ -+ read_inode: mini_fo_read_inode, -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+ write_inode: mini_fo_write_inode, -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ put_inode: mini_fo_put_inode, -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+ delete_inode: mini_fo_delete_inode, -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ put_super: mini_fo_put_super, -+ statfs: mini_fo_statfs, -+ remount_fs: mini_fo_remount_fs, -+ clear_inode: mini_fo_clear_inode, -+ umount_begin: mini_fo_umount_begin, -+}; diff --git a/target/linux/generic-2.6/patches/210-d80211_compat.patch b/target/linux/generic-2.6/patches/210-d80211_compat.patch deleted file mode 100644 index 9d57f1fa7a..0000000000 --- a/target/linux/generic-2.6/patches/210-d80211_compat.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: linux-2.6.21.7/include/linux/netdevice.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/netdevice.h -+++ linux-2.6.21.7/include/linux/netdevice.h -@@ -532,6 +532,8 @@ struct net_device - struct device dev; - /* space for optional statistics and wireless sysfs groups */ - struct attribute_group *sysfs_groups[3]; -+ -+ void *ieee80211_ptr; - }; - #define to_net_dev(d) container_of(d, struct net_device, dev) - diff --git a/target/linux/generic-2.6/patches/211-no_block2mtd_readahead.patch b/target/linux/generic-2.6/patches/211-no_block2mtd_readahead.patch deleted file mode 100644 index 3cad44ed3a..0000000000 --- a/target/linux/generic-2.6/patches/211-no_block2mtd_readahead.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: linux-2.6.21.7/drivers/mtd/devices/block2mtd.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/mtd/devices/block2mtd.c -+++ linux-2.6.21.7/drivers/mtd/devices/block2mtd.c -@@ -41,7 +41,7 @@ struct block2mtd_dev { - static LIST_HEAD(blkmtd_device_list); - - --#define PAGE_READAHEAD 64 -+#define PAGE_READAHEAD 0 - static void cache_readahead(struct address_space *mapping, int index) - { - filler_t *filler = (filler_t*)mapping->a_ops->readpage; diff --git a/target/linux/generic-2.6/patches/212-block2mtd_erase_scan.patch b/target/linux/generic-2.6/patches/212-block2mtd_erase_scan.patch deleted file mode 100644 index 6c68f99fe0..0000000000 --- a/target/linux/generic-2.6/patches/212-block2mtd_erase_scan.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: linux-2.6.21.7/drivers/mtd/devices/block2mtd.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/mtd/devices/block2mtd.c -+++ linux-2.6.21.7/drivers/mtd/devices/block2mtd.c -@@ -112,7 +112,7 @@ static int _block2mtd_erase(struct block - if (IS_ERR(page)) - return PTR_ERR(page); - -- max = (u_long*)page_address(page) + PAGE_SIZE; -+ max = (u_long*) ((u8 *) page_address(page) + PAGE_SIZE); - for (p=(u_long*)page_address(page); pf_version = inode->i_version; - } - -- list_for_each(i, &obj->variant.directoryVariant.children) { -+ list_for_each(i, (struct list_head *)&obj->variant.directoryVariant.children) { - curoffs++; - if (curoffs >= offset) { - l = list_entry(i, yaffs_Object, siblings); -@@ -1273,7 +1275,7 @@ - - if (target && - target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && -- !list_empty(&target->variant.directoryVariant.children)) { -+ !list_empty((struct list_head *)&target->variant.directoryVariant.children)) { - - T(YAFFS_TRACE_OS, (KERN_DEBUG "target is non-empty dir\n")); - -@@ -1529,7 +1531,7 @@ - yaffs_GrossUnlock(dev); - - /* we assume this is protected by lock_kernel() in mount/umount */ -- list_del(&dev->devList); -+ list_del((struct list_head *)&dev->devList); - - if(dev->spareBuffer){ - YFREE(dev->spareBuffer); -@@ -1864,7 +1866,7 @@ - dev->skipCheckpointWrite = options.skip_checkpoint_write; - - /* we assume this is protected by lock_kernel() in mount/umount */ -- list_add_tail(&dev->devList, &yaffs_dev_list); -+ list_add_tail((struct list_head *)&dev->devList, &yaffs_dev_list); - - init_MUTEX(&dev->grossLock); - ---- a/fs/yaffs2/yaffs_mtdif1.c -+++ b/fs/yaffs2/yaffs_mtdif1.c -@@ -323,7 +323,7 @@ - * Always returns YAFFS_OK. - */ - int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, -- yaffs_BlockState * pState, int *pSequenceNumber) -+ yaffs_BlockState * pState, __u32 *pSequenceNumber) - { - struct mtd_info * mtd = dev->genericDevice; - int chunkNo = blockNo * dev->nChunksPerBlock; ---- a/fs/yaffs2/yaffs_mtdif1.h -+++ b/fs/yaffs2/yaffs_mtdif1.h -@@ -23,6 +23,6 @@ - int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); - - int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, -- yaffs_BlockState * state, int *sequenceNumber); -+ yaffs_BlockState * state, __u32 *sequenceNumber); - - #endif ---- a/fs/yaffs2/yaffs_mtdif2.c -+++ b/fs/yaffs2/yaffs_mtdif2.c -@@ -204,7 +204,7 @@ - } - - int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, -- yaffs_BlockState * state, int *sequenceNumber) -+ yaffs_BlockState * state, __u32 *sequenceNumber) - { - struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); - int retval; ---- a/fs/yaffs2/yaffs_mtdif2.h -+++ b/fs/yaffs2/yaffs_mtdif2.h -@@ -24,6 +24,6 @@ - __u8 * data, yaffs_ExtendedTags * tags); - int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); - int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, -- yaffs_BlockState * state, int *sequenceNumber); -+ yaffs_BlockState * state, __u32 *sequenceNumber); - - #endif diff --git a/target/linux/generic-2.6/patches/900-headers_type_and_time.patch b/target/linux/generic-2.6/patches/900-headers_type_and_time.patch deleted file mode 100644 index 3029ce28ec..0000000000 --- a/target/linux/generic-2.6/patches/900-headers_type_and_time.patch +++ /dev/null @@ -1,50 +0,0 @@ -Index: linux-2.6.21.7/include/linux/time.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/time.h -+++ linux-2.6.21.7/include/linux/time.h -@@ -1,6 +1,10 @@ - #ifndef _LINUX_TIME_H - #define _LINUX_TIME_H - -+#ifndef __KERNEL__ -+#include -+#else -+ - #include - - #ifdef __KERNEL__ -@@ -224,4 +228,6 @@ struct itimerval { - */ - #define TIMER_ABSTIME 0x01 - -+#endif /* __KERNEL__ DEBIAN */ -+ - #endif -Index: linux-2.6.21.7/include/linux/types.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/types.h -+++ linux-2.6.21.7/include/linux/types.h -@@ -1,6 +1,14 @@ - #ifndef _LINUX_TYPES_H - #define _LINUX_TYPES_H - -+/* Debian: Use userland types instead. */ -+#ifndef __KERNEL__ -+# include -+/* For other kernel headers. */ -+# include -+# include -+#else -+ - #ifdef __KERNEL__ - - #define BITS_TO_LONGS(bits) \ -@@ -162,6 +170,8 @@ typedef unsigned long blkcnt_t; - - #endif /* __KERNEL_STRICT_NAMES */ - -+#endif /* __KERNEL__ DEBIAN */ -+ - /* - * Below are truly Linux-specific types that should never collide with - * any application/library that wants linux/types.h. diff --git a/target/linux/generic-2.6/patches/902-darwin_scripts_include.patch b/target/linux/generic-2.6/patches/902-darwin_scripts_include.patch deleted file mode 100644 index 0e981def57..0000000000 --- a/target/linux/generic-2.6/patches/902-darwin_scripts_include.patch +++ /dev/null @@ -1,154 +0,0 @@ -Index: linux-2.6.21.7/scripts/genksyms/parse.c_shipped -=================================================================== ---- linux-2.6.21.7.orig/scripts/genksyms/parse.c_shipped -+++ linux-2.6.21.7/scripts/genksyms/parse.c_shipped -@@ -144,7 +144,9 @@ - - - #include -+#ifndef __APPLE__ - #include -+#endif - #include "genksyms.h" - - static int is_typedef; -Index: linux-2.6.21.7/scripts/genksyms/parse.y -=================================================================== ---- linux-2.6.21.7.orig/scripts/genksyms/parse.y -+++ linux-2.6.21.7/scripts/genksyms/parse.y -@@ -24,7 +24,9 @@ - %{ - - #include -+#ifndef __APPLE__ - #include -+#endif - #include "genksyms.h" - - static int is_typedef; -Index: linux-2.6.21.7/scripts/kallsyms.c -=================================================================== ---- linux-2.6.21.7.orig/scripts/kallsyms.c -+++ linux-2.6.21.7/scripts/kallsyms.c -@@ -30,6 +30,35 @@ - #include - #include - #include -+#ifdef __APPLE__ -+/* Darwin has no memmem implementation, this one is ripped of the uClibc-0.9.28 source */ -+void *memmem (const void *haystack, size_t haystack_len, -+ const void *needle, size_t needle_len) -+{ -+ const char *begin; -+ const char *const last_possible -+ = (const char *) haystack + haystack_len - needle_len; -+ -+ if (needle_len == 0) -+ /* The first occurrence of the empty string is deemed to occur at -+ the beginning of the string. */ -+ return (void *) haystack; -+ -+ /* Sanity check, otherwise the loop might search through the whole -+ memory. */ -+ if (__builtin_expect (haystack_len < needle_len, 0)) -+ return NULL; -+ -+ for (begin = (const char *) haystack; begin <= last_possible; ++begin) -+ if (begin[0] == ((const char *) needle)[0] && -+ !memcmp ((const void *) &begin[1], -+ (const void *) ((const char *) needle + 1), -+ needle_len - 1)) -+ return (void *) begin; -+ -+ return NULL; -+} -+#endif - - #define KSYM_NAME_LEN 127 - -Index: linux-2.6.21.7/scripts/kconfig/Makefile -=================================================================== ---- linux-2.6.21.7.orig/scripts/kconfig/Makefile -+++ linux-2.6.21.7/scripts/kconfig/Makefile -@@ -87,6 +87,9 @@ check-lxdialog := $(srctree)/$(src)/lxd - # we really need to do so. (Do not call gcc as part of make mrproper) - HOST_EXTRACFLAGS = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) - HOST_LOADLIBES = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC)) -+ifeq ($(shell uname -s),Darwin) -+HOST_LOADLIBES += -lncurses -+endif - - HOST_EXTRACFLAGS += -DLOCALE - -Index: linux-2.6.21.7/scripts/mod/file2alias.c -=================================================================== ---- linux-2.6.21.7.orig/scripts/mod/file2alias.c -+++ linux-2.6.21.7/scripts/mod/file2alias.c -@@ -37,7 +37,21 @@ typedef unsigned char __u8; - * even potentially has different endianness and word sizes, since - * we handle those differences explicitly below */ - #include "../../include/linux/mod_devicetable.h" -+#ifndef __APPLE__ - #include "../../include/linux/input.h" -+#else -+#define EV_MAX 0x1f -+#define KEY_MUTE 113 -+#define KEY_MIN_INTERESTING KEY_MUTE -+#define KEY_MAX 0x1ff -+#define REL_MAX 0x0f -+#define ABS_MAX 0x3f -+#define MSC_MAX 0x07 -+#define LED_MAX 0x0f -+#define SND_MAX 0x07 -+#define FF_MAX 0x7f -+#define SW_MAX 0x0f -+#endif - - #define ADD(str, sep, cond, field) \ - do { \ -Index: linux-2.6.21.7/scripts/mod/mk_elfconfig.c -=================================================================== ---- linux-2.6.21.7.orig/scripts/mod/mk_elfconfig.c -+++ linux-2.6.21.7/scripts/mod/mk_elfconfig.c -@@ -1,7 +1,11 @@ - #include - #include - #include -+#ifndef __APPLE__ - #include -+#else -+#include "../../../../../tools/sstrip/include/elf.h" -+#endif - - int - main(int argc, char **argv) -Index: linux-2.6.21.7/scripts/mod/modpost.h -=================================================================== ---- linux-2.6.21.7.orig/scripts/mod/modpost.h -+++ linux-2.6.21.7/scripts/mod/modpost.h -@@ -7,7 +7,11 @@ - #include - #include - #include -+#ifndef __APPLE__ - #include -+#else -+#include "../../../../../tools/sstrip/include/elf.h" -+#endif - - #include "elfconfig.h" - -Index: linux-2.6.21.7/scripts/mod/sumversion.c -=================================================================== ---- linux-2.6.21.7.orig/scripts/mod/sumversion.c -+++ linux-2.6.21.7/scripts/mod/sumversion.c -@@ -8,6 +8,9 @@ - #include - #include - #include "modpost.h" -+#ifdef __APPLE__ -+#include -+#endif - - /* - * Stolen form Cryptographic API. diff --git a/target/linux/generic-2.6/patches/903-hostap_txpower.patch b/target/linux/generic-2.6/patches/903-hostap_txpower.patch deleted file mode 100644 index 474fbeea0b..0000000000 --- a/target/linux/generic-2.6/patches/903-hostap_txpower.patch +++ /dev/null @@ -1,166 +0,0 @@ -Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ap.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_ap.c -+++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ap.c -@@ -2346,13 +2346,13 @@ int prism2_ap_get_sta_qual(local_info_t - addr[count].sa_family = ARPHRD_ETHER; - memcpy(addr[count].sa_data, sta->addr, ETH_ALEN); - if (sta->last_rx_silence == 0) -- qual[count].qual = sta->last_rx_signal < 27 ? -- 0 : (sta->last_rx_signal - 27) * 92 / 127; -+ qual[count].qual = (sta->last_rx_signal - 156) == 0 ? -+ 0 : (sta->last_rx_signal - 156) * 92 / 64; - else -- qual[count].qual = sta->last_rx_signal - -- sta->last_rx_silence - 35; -- qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); -- qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); -+ qual[count].qual = (sta->last_rx_signal - -+ sta->last_rx_silence) * 92 / 64; -+ qual[count].level = sta->last_rx_signal; -+ qual[count].noise = sta->last_rx_silence; - qual[count].updated = sta->last_rx_updated; - - sta->last_rx_updated = IW_QUAL_DBM; -@@ -2417,13 +2417,13 @@ int prism2_ap_translate_scan(struct net_ - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVQUAL; - if (sta->last_rx_silence == 0) -- iwe.u.qual.qual = sta->last_rx_signal < 27 ? -- 0 : (sta->last_rx_signal - 27) * 92 / 127; -+ iwe.u.qual.qual = (sta->last_rx_signal -156) == 0 ? -+ 0 : (sta->last_rx_signal - 156) * 92 / 64; - else -- iwe.u.qual.qual = sta->last_rx_signal - -- sta->last_rx_silence - 35; -- iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); -- iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); -+ iwe.u.qual.qual = (sta->last_rx_signal - -+ sta->last_rx_silence) * 92 / 64; -+ iwe.u.qual.level = sta->last_rx_signal; -+ iwe.u.qual.noise = sta->last_rx_silence; - iwe.u.qual.updated = sta->last_rx_updated; - iwe.len = IW_EV_QUAL_LEN; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, -Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_config.h -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_config.h -+++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_config.h -@@ -47,4 +47,9 @@ - */ - /* #define PRISM2_NO_STATION_MODES */ - -+/* Enable TX power Setting functions -+ * (min att = -128 , max att = 127) -+ */ -+#define RAW_TXPOWER_SETTING -+ - #endif /* HOSTAP_CONFIG_H */ -Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap.h -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap.h -+++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap.h -@@ -90,6 +90,7 @@ extern const struct iw_handler_def hosta - extern const struct ethtool_ops prism2_ethtool_ops; - - int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); -+int hostap_restore_power(struct net_device *dev); - - - #endif /* HOSTAP_H */ -Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_hw.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_hw.c -+++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_hw.c -@@ -933,6 +933,7 @@ static int hfa384x_set_rid(struct net_de - prism2_hw_reset(dev); - } - -+ hostap_restore_power(dev); - return res; - } - -Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_info.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_info.c -+++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_info.c -@@ -428,6 +428,11 @@ static void handle_info_queue_linkstatus - } - - /* Get BSSID if we have a valid AP address */ -+ -+ if ( val == HFA384X_LINKSTATUS_CONNECTED || -+ val == HFA384X_LINKSTATUS_DISCONNECTED ) -+ hostap_restore_power(local->dev); -+ - if (connected) { - netif_carrier_on(local->dev); - netif_carrier_on(local->ddev); -Index: linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ioctl.c -=================================================================== ---- linux-2.6.21.7.orig/drivers/net/wireless/hostap/hostap_ioctl.c -+++ linux-2.6.21.7/drivers/net/wireless/hostap/hostap_ioctl.c -@@ -1504,23 +1504,20 @@ static int prism2_txpower_hfa386x_to_dBm - val = 255; - - tmp = val; -- tmp >>= 2; - -- return -12 - tmp; -+ return tmp; - } - - static u16 prism2_txpower_dBm_to_hfa386x(int val) - { - signed char tmp; - -- if (val > 20) -- return 128; -- else if (val < -43) -+ if (val > 127) - return 127; -+ else if (val < -128) -+ return 128; - - tmp = val; -- tmp = -12 - tmp; -- tmp <<= 2; - - return (unsigned char) tmp; - } -@@ -4080,3 +4077,35 @@ int hostap_ioctl(struct net_device *dev, - - return ret; - } -+ -+/* BUG FIX: Restore power setting value when lost due to F/W bug */ -+ -+int hostap_restore_power(struct net_device *dev) -+{ -+ struct hostap_interface *iface = dev->priv; -+ local_info_t *local = iface->local; -+ -+ u16 val; -+ int ret = 0; -+ -+ if (local->txpower_type == PRISM2_TXPOWER_OFF) { -+ val = 0xff; /* use all standby and sleep modes */ -+ ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, -+ HFA386X_CR_A_D_TEST_MODES2, -+ &val, NULL); -+ } -+ -+#ifdef RAW_TXPOWER_SETTING -+ if (local->txpower_type == PRISM2_TXPOWER_FIXED) { -+ val = HFA384X_TEST_CFG_BIT_ALC; -+ local->func->cmd(dev, HFA384X_CMDCODE_TEST | -+ (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL); -+ val = prism2_txpower_dBm_to_hfa386x(local->txpower); -+ ret = (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, -+ HFA386X_CR_MANUAL_TX_POWER, &val, NULL)); -+ } -+#endif /* RAW_TXPOWER_SETTING */ -+ return (ret ? -EOPNOTSUPP : 0); -+} -+ -+EXPORT_SYMBOL(hostap_restore_power); diff --git a/target/linux/generic-2.6/patches/903-stddef_include.patch b/target/linux/generic-2.6/patches/903-stddef_include.patch deleted file mode 100644 index 3b40ced971..0000000000 --- a/target/linux/generic-2.6/patches/903-stddef_include.patch +++ /dev/null @@ -1,19 +0,0 @@ -Index: linux-2.6.21.7/include/linux/stddef.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/stddef.h -+++ linux-2.6.21.7/include/linux/stddef.h -@@ -16,6 +16,7 @@ enum { - false = 0, - true = 1 - }; -+#endif /* __KERNEL__ */ - - #undef offsetof - #ifdef __compiler_offsetof -@@ -23,6 +24,5 @@ enum { - #else - #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) - #endif --#endif /* __KERNEL__ */ - - #endif diff --git a/target/linux/generic-2.6/patches/904-ls_time_locale.patch b/target/linux/generic-2.6/patches/904-ls_time_locale.patch deleted file mode 100644 index c34e180d13..0000000000 --- a/target/linux/generic-2.6/patches/904-ls_time_locale.patch +++ /dev/null @@ -1,22 +0,0 @@ -Index: linux-2.6.21.7/scripts/gen_initramfs_list.sh -=================================================================== ---- linux-2.6.21.7.orig/scripts/gen_initramfs_list.sh -+++ linux-2.6.21.7/scripts/gen_initramfs_list.sh -@@ -125,7 +125,7 @@ parse() { - str="${ftype} ${name} ${location} ${str}" - ;; - "nod") -- local dev=`LC_ALL=C ls -l "${location}"` -+ local dev=`LC_ALL=C ls -l --time-style=locale "${location}"` - local maj=`field 5 ${dev}` - local min=`field 6 ${dev}` - maj=${maj%,} -@@ -135,7 +135,7 @@ parse() { - str="${ftype} ${name} ${str} ${dev} ${maj} ${min}" - ;; - "slink") -- local target=`field 11 $(LC_ALL=C ls -l "${location}")` -+ local target=`field 11 $(LC_ALL=C ls -l --time-style=locale "${location}")` - str="${ftype} ${name} ${target} ${str}" - ;; - *) diff --git a/target/linux/generic-2.6/patches/999-backport_ip_hdr_and_friends.patch b/target/linux/generic-2.6/patches/999-backport_ip_hdr_and_friends.patch deleted file mode 100644 index bb8f4f3342..0000000000 --- a/target/linux/generic-2.6/patches/999-backport_ip_hdr_and_friends.patch +++ /dev/null @@ -1,58 +0,0 @@ -Index: linux-2.6.21.7/include/linux/ip.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/ip.h -+++ linux-2.6.21.7/include/linux/ip.h -@@ -104,6 +104,16 @@ struct iphdr { - /*The options start here. */ - }; - -+#ifdef __KERNEL__ -+#include -+ -+static inline struct iphdr *ip_hdr(const struct sk_buff *skb) -+{ -+ return (struct iphdr *)skb_network_header(skb); -+} -+ -+#endif -+ - struct ip_auth_hdr { - __u8 nexthdr; - __u8 hdrlen; /* This one is measured in 32 bit units! */ -Index: linux-2.6.21.7/include/linux/skbuff.h -=================================================================== ---- linux-2.6.21.7.orig/include/linux/skbuff.h -+++ linux-2.6.21.7/include/linux/skbuff.h -@@ -966,6 +966,16 @@ static inline void skb_reserve(struct sk - skb->tail += len; - } - -+static inline unsigned char *skb_network_header(const struct sk_buff *skb) -+{ -+ return skb->nh.raw; -+} -+ -+static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) -+{ -+ return skb->tail; -+} -+ - /* - * CPUs often take a performance hit when accessing unaligned memory - * locations. The actual performance hit varies, it can be small if the -Index: linux-2.6.21.7/include/net/ip.h -=================================================================== ---- linux-2.6.21.7.orig/include/net/ip.h -+++ linux-2.6.21.7/include/net/ip.h -@@ -43,6 +43,11 @@ struct inet_skb_parm - #define IPSKB_REROUTED 16 - }; - -+static inline unsigned int ip_hdrlen(const struct sk_buff *skb) -+{ -+ return ip_hdr(skb)->ihl * 4; -+} -+ - struct ipcm_cookie - { - __be32 addr; -- cgit v1.2.3