diff options
author | Imre Kaloz <kaloz@openwrt.org> | 2008-12-08 13:23:09 +0000 |
---|---|---|
committer | Imre Kaloz <kaloz@openwrt.org> | 2008-12-08 13:23:09 +0000 |
commit | f21f51d858ab97a4784f76481c336613abc127f6 (patch) | |
tree | e1d1a3380f15b3a411c3eeffb9adea9f6f82628b /target | |
parent | 206ccdf4ae2f236a752950a701bb3eacbdaf2cc8 (diff) | |
download | mtk-20170518-f21f51d858ab97a4784f76481c336613abc127f6.zip mtk-20170518-f21f51d858ab97a4784f76481c336613abc127f6.tar.gz mtk-20170518-f21f51d858ab97a4784f76481c336613abc127f6.tar.bz2 |
add generic 2.6.28 patches
SVN-Revision: 13546
Diffstat (limited to 'target')
80 files changed, 36878 insertions, 0 deletions
diff --git a/target/linux/generic-2.6/config-2.6.28 b/target/linux/generic-2.6/config-2.6.28 new file mode 100644 index 0000000..995392a --- /dev/null +++ b/target/linux/generic-2.6/config-2.6.28 @@ -0,0 +1,2049 @@ +# CONFIG_6PACK is not set +# CONFIG_8139CP is not set +# CONFIG_9P_FS is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_ACENIC is not set +# CONFIG_ACORN_PARTITION is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADM6996_PHY is not set +# CONFIG_AFFS_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_AF_RXRPC is not set +CONFIG_AIO=y +CONFIG_AIRO=m +CONFIG_AIRO_CS=m +# CONFIG_ALIM7101_WDT is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_AMIGA_PARTITION is not set +CONFIG_ANON_INODES=y +# CONFIG_APPLICOM is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_ARCNET is not set +CONFIG_ARPD=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_AT24 is not set +# CONFIG_ATA is not set +# CONFIG_ATALK is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_ATA_ACPI is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_ATA_SFF is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL2 is not set +CONFIG_ATM=m +CONFIG_ATMEL=m +# CONFIG_ATM_AMBASSADOR is not set +CONFIG_ATM_BR2684=m +CONFIG_ATM_BR2684_IPFILTER=y +CONFIG_ATM_CLIP=m +CONFIG_ATM_CLIP_NO_ICMP=y +# CONFIG_ATM_DRIVERS is not set +CONFIG_ATM_DUMMY=m +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_FORE200E is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_LANAI is not set +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +# CONFIG_ATM_NICSTAR is not set +CONFIG_ATM_TCP=m +# CONFIG_ATM_ZATM is not set +# CONFIG_AUDIT is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +CONFIG_AX25=m +# CONFIG_AX25_DAMA_SLAVE is not set +# CONFIG_AX88796 is not set +# CONFIG_B44 is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +CONFIG_BASE_FULL=y +# CONFIG_BASLER_EXCITE is not set +# CONFIG_BAYCOM_EPP is not set +# CONFIG_BAYCOM_PAR is not set +# CONFIG_BAYCOM_SER_FDX is not set +# CONFIG_BAYCOM_SER_HDX is not set +CONFIG_BCM43XX=m +CONFIG_BCM43XX_DEBUG=y +CONFIG_BCM43XX_DMA=y +CONFIG_BCM43XX_DMA_AND_PIO_MODE=y +# CONFIG_BCM43XX_DMA_MODE is not set +CONFIG_BCM43XX_PIO=y +# CONFIG_BCM43XX_PIO_MODE is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BLINK is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_CPQ_DA is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_CS5535 is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_DELKIN is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_GENERIC is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD_ONLY is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_IDEACPI is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_BLK_DEV_IDEPNP is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_IT8213 is not set +# CONFIG_BLK_DEV_IT821X is not set +# CONFIG_BLK_DEV_JMICRON is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_SD=m +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_TC86C001 is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +CONFIG_BLOCK=y +# CONFIG_BNX2 is not set +CONFIG_BONDING=m +# CONFIG_BPQETHER is not set +CONFIG_BRIDGE=y +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_IP=m +# CONFIG_BRIDGE_EBT_IP6 is not set +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_ULOG=m +CONFIG_BRIDGE_EBT_VLAN=m +# CONFIG_BRIDGE_NETFILTER is not set +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BROKEN_ON_SMP=y +CONFIG_BSD_DISKLABEL=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_BT=m +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +# CONFIG_BT_CMTP is not set +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBLUECARD is not set +CONFIG_BT_HCIBPA10X=m +# CONFIG_BT_HCIBT3C is not set +# CONFIG_BT_HCIBTSDIO is not set +# CONFIG_BT_HCIBTUART is not set +# CONFIG_BT_HCIDTL1 is not set +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_LL is not set +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIVHCI=m +CONFIG_BT_HIDP=m +CONFIG_BT_L2CAP=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_SCO=m +CONFIG_BUG=y +# CONFIG_C2PORT is not set +# CONFIG_CAN is not set +# CONFIG_CAPI_AVM is not set +# CONFIG_CAPI_EICON is not set +# CONFIG_CAPI_TRACE is not set +CONFIG_CARDBUS=y +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_CASSINI is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_CFG80211 is not set +# CONFIG_CGROUPS is not set +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CICADA_PHY is not set +CONFIG_CIFS=m +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_STATS=y +# CONFIG_CIFS_STATS2 is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_XATTR is not set +CONFIG_CLS_U32_MARK=y +CONFIG_CLS_U32_PERF=y +CONFIG_CMDLINE="" +# CONFIG_CODA_FS is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_CONFIGFS_FS=y +# CONFIG_CONNECTOR is not set +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_CRAMFS is not set +CONFIG_CRC16=m +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_CRC_CCITT=m +CONFIG_CRC_ITU_T=m +# CONFIG_CRC_T10DIF is not set +CONFIG_CROSSCOMPILE=y +CONFIG_CRYPTO=y +CONFIG_CRYPTO_AEAD=m +CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_ALGAPI=m +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_AUTHENC=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_CBC=m +# CONFIG_CRYPTO_CCM is not set +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_DES=m +# CONFIG_CRYPTO_DEV_HIFN_795X is not set +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_FIPS is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_HASH=m +CONFIG_CRYPTO_HMAC=m +# CONFIG_CRYPTO_HW is not set +CONFIG_CRYPTO_KHAZAD=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_NULL=m +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_PRNG is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SEQIV is not set +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m +CONFIG_CRYPTO_WP512=m +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_DAB is not set +# CONFIG_DAVICOM_PHY is not set +CONFIG_DEBUG_FS=y +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DECNET is not set +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_BIC is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_CUBIC is not set +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_HTCP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +# CONFIG_DEFAULT_NOOP is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="vegas" +CONFIG_DEFAULT_VEGAS=y +# CONFIG_DEFAULT_WESTWOOD is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +# CONFIG_DEVKMEM is not set +# CONFIG_DGRS is not set +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_DL2K is not set +# CONFIG_DLM is not set +# CONFIG_DMADEVICES is not set +# CONFIG_DMA_ENGINE is not set +# CONFIG_DNOTIFY is not set +# CONFIG_DRM is not set +# CONFIG_DS1682 is not set +# CONFIG_DTLK is not set +# CONFIG_DUMMY is not set +# CONFIG_DVB is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_E100 is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_ECONET is not set +# CONFIG_EEPRO100 is not set +CONFIG_EEPROM_93CX6=m +# CONFIG_EFI_PARTITION is not set +# CONFIG_EFS_FS is not set +# CONFIG_ELF_CORE is not set +CONFIG_EMBEDDED=y +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +# CONFIG_ENC28J60 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_EPIC100 is not set +CONFIG_EPOLL=y +# CONFIG_EQUALIZER is not set +CONFIG_EVENTFD=y +CONFIG_EXPERIMENTAL=y +CONFIG_EXPORTFS=m +CONFIG_EXT2_FS=m +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=m +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4_FS is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_EXTRA_FIRMWARE="" +CONFIG_EXTRA_TARGETS="" +# CONFIG_FAIR_GROUP_SCHED is not set +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_FAT_FS=m +# CONFIG_FB is not set +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FDDI is not set +# CONFIG_FEALNX is not set +CONFIG_FIB_RULES=y +CONFIG_FILE_LOCKING=y +# CONFIG_FIREWIRE is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_FLATMEM=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_FORCEDETH is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_FTL is not set +# CONFIG_FTRACE is not set +# CONFIG_FUSE_FS is not set +# CONFIG_FUSION is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set +# CONFIG_FUSION_SPI is not set +CONFIG_FUTEX=y +CONFIG_FW_LOADER=y +CONFIG_GACT_PROB=y +# CONFIG_GAMEPORT is not set +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_TIME=y +# CONFIG_GFS2_FS is not set +CONFIG_GPIO_DEVICE=m +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SYSFS is not set +# CONFIG_GPIOLIB is not set +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GROUP_SCHED is not set +# CONFIG_HAMACHI is not set +CONFIG_HAMRADIO=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_HDLC is not set +# CONFIG_HDLC_CISCO is not set +# CONFIG_HDLC_FR is not set +# CONFIG_HDLC_PPP is not set +# CONFIG_HDLC_RAW is not set +# CONFIG_HDLC_RAW_ETH is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_HERMES=m +CONFIG_HFSPLUS_FS=m +CONFIG_HFS_FS=m +# CONFIG_HID is not set +# CONFIG_HIDRAW is not set +# CONFIG_HID_DEBUG is not set +# CONFIG_HID_FF is not set +# CONFIG_HID_SUPPORT is not set +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_HIPPI is not set +# CONFIG_HOSTAP is not set +# CONFIG_HOSTAP_CS is not set +# CONFIG_HOSTAP_FIRMWARE is not set +# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set +# CONFIG_HOSTAP_PCI is not set +# CONFIG_HOSTAP_PLX is not set +CONFIG_HOTPLUG=y +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HP100 is not set +# CONFIG_HPFS_FS is not set +# CONFIG_HP_ILO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set +CONFIG_HZ=100 +CONFIG_HZ_100=y +# CONFIG_HZ_1000 is not set +# CONFIG_HZ_1024 is not set +# CONFIG_HZ_128 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_256 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_48 is not set +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCA is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_ELEKTOR is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_HELPER_AUTO is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_IBM_IIC is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_MPC is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2O is not set +# CONFIG_I82092 is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_ICS932S401 is not set +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDMA_IVB is not set +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_IDEPCI_SHARE_IRQ is not set +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_IDE_PROC_FS=y +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IEEE1394 is not set +# CONFIG_IEEE1394_DV1394 is not set +# CONFIG_IEEE1394_ETH1394 is not set +# CONFIG_IEEE1394_OHCI1394 is not set +# CONFIG_IEEE1394_PCILYNX is not set +# CONFIG_IEEE1394_RAWIO is not set +# CONFIG_IEEE1394_VERBOSEDEBUG is not set +# CONFIG_IEEE1394_VIDEO1394 is not set +CONFIG_IEEE80211=m +CONFIG_IEEE80211_CRYPT_CCMP=m +CONFIG_IEEE80211_CRYPT_TKIP=m +CONFIG_IEEE80211_CRYPT_WEP=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_SOFTMAC=m +# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set +CONFIG_IFB=m +# CONFIG_IGB is not set +# CONFIG_IKCONFIG is not set +# CONFIG_IKCONFIG_PROC is not set +# CONFIG_IMQ is not set +# CONFIG_IMQ_BEHAVIOR_AA is not set +# CONFIG_IMQ_BEHAVIOR_AB is not set +# CONFIG_IMQ_BEHAVIOR_BA is not set +# CONFIG_IMQ_BEHAVIOR_BB is not set +CONFIG_IMQ_NUM_DEVS=2 +CONFIG_INET=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET_AH=m +CONFIG_INET_DIAG=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +# CONFIG_INET_LRO is not set +CONFIG_INET_TCP_DIAG=m +CONFIG_INET_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_TUNNEL=m +# CONFIG_INFINIBAND is not set +# CONFIG_INFTL is not set +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_INOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_INPUT is not set +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_ATLAS_BTNS is not set +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_EVDEV=m +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_MISC is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_INSTRUMENTATION is not set +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_NOOP=y +# CONFIG_IP1000 is not set +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_IPTABLES=m +# CONFIG_IP6_NF_MANGLE is not set +CONFIG_IP6_NF_MATCH_AH=m +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +CONFIG_IP6_NF_MATCH_LIMIT=m +CONFIG_IP6_NF_MATCH_MH=m +# CONFIG_IP6_NF_MATCH_OPTS is not set +CONFIG_IP6_NF_MATCH_OWNER=m +# CONFIG_IP6_NF_MATCH_RT is not set +CONFIG_IP6_NF_QUEUE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_TARGET_IMQ=m +# CONFIG_IP6_NF_TARGET_LOG is not set +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_ROUTE=m +# CONFIG_IPC_NS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_IPSEC_NAT_TRAVERSAL=y +CONFIG_IPV6=m +# CONFIG_IPV6_MIP6 is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_SIT=m +# CONFIG_IPV6_TUNNEL is not set +CONFIG_IPW2100=m +# CONFIG_IPW2100_DEBUG is not set +CONFIG_IPW2100_MONITOR=y +CONFIG_IPW2200=m +# CONFIG_IPW2200_DEBUG is not set +CONFIG_IPW2200_MONITOR=y +# CONFIG_IPW2200_PROMISCUOUS is not set +# CONFIG_IPW2200_QOS is not set +# CONFIG_IPW2200_RADIOTAP is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_IPX is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_DCCP is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_NF_AMANDA=m +CONFIG_IP_NF_ARPFILTER=m +# CONFIG_IP_NF_ARPTABLES is not set +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_IP_NF_CONNTRACK=y +# CONFIG_IP_NF_CONNTRACK_EVENTS is not set +CONFIG_IP_NF_CONNTRACK_MARK=y +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_H323=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_IRC=m +CONFIG_IP_NF_MANGLE=m +# CONFIG_IP_NF_MATCH_ADDRTYPE is not set +CONFIG_IP_NF_MATCH_AH=m +# CONFIG_IP_NF_MATCH_ECN is not set +CONFIG_IP_NF_MATCH_HASHLIMIT=m +CONFIG_IP_NF_MATCH_IPP2P=m +CONFIG_IP_NF_MATCH_OWNER=m +# CONFIG_IP_NF_MATCH_RECENT is not set +CONFIG_IP_NF_MATCH_SET=m +# CONFIG_IP_NF_MATCH_TIME is not set +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_AMANDA=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_NAT_H323=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_NAT_PPTP=m +CONFIG_IP_NF_NAT_SIP=m +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_TFTP=m +# CONFIG_IP_NF_NETBIOS_NS is not set +CONFIG_IP_NF_PPTP=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_RAW=m +# CONFIG_IP_NF_SET is not set +CONFIG_IP_NF_SET_HASHSIZE=1024 +CONFIG_IP_NF_SET_IPHASH=m +CONFIG_IP_NF_SET_IPMAP=m +CONFIG_IP_NF_SET_IPPORTHASH=m +CONFIG_IP_NF_SET_IPTREE=m +CONFIG_IP_NF_SET_IPTREEMAP=m +CONFIG_IP_NF_SET_MACIPMAP=m +CONFIG_IP_NF_SET_MAX=256 +CONFIG_IP_NF_SET_NETHASH=m +CONFIG_IP_NF_SET_PORTMAP=m +CONFIG_IP_NF_SIP=m +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +CONFIG_IP_NF_TARGET_IMQ=m +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_MASQUERADE=m +# CONFIG_IP_NF_TARGET_NETMAP is not set +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_ROUTE=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_IP_NF_TARGET_SET=m +CONFIG_IP_NF_TARGET_TOS=m +# CONFIG_IP_NF_TARGET_TTL is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_IP_NF_TFTP=m +# CONFIG_IP_PNP is not set +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_MULTIPATH_CACHED=y +CONFIG_IP_ROUTE_MULTIPATH_DRR=m +CONFIG_IP_ROUTE_MULTIPATH_RANDOM=m +CONFIG_IP_ROUTE_MULTIPATH_RR=m +CONFIG_IP_ROUTE_MULTIPATH_WRANDOM=m +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_SCTP is not set +# CONFIG_IP_VS is not set +# CONFIG_IRDA is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_ISCSI_TCP is not set +CONFIG_ISDN=y +CONFIG_ISDN_CAPI=m +CONFIG_ISDN_CAPI_CAPI20=m +CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIFS_BOOL=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +# CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON is not set +# CONFIG_ISDN_I4L is not set +CONFIG_ISO9660_FS=m +# CONFIG_IWLWIFI_LEDS is not set +# CONFIG_IXGB is not set +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_JME is not set +CONFIG_JOLIET=y +# CONFIG_KALLSYMS is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_KEXEC is not set +# CONFIG_KEYS is not set +# CONFIG_KMOD is not set +# CONFIG_KPROBES is not set +# CONFIG_LANMEDIA is not set +# CONFIG_LAPB is not set +# CONFIG_LASAT is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_LBD is not set +# CONFIG_LDM_PARTITION is not set +CONFIG_LEDS_CLASS=y +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_PCA955X is not set +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_IDE_DISK is not set +# CONFIG_LEDS_TRIGGER_MORSE is not set +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_LIBCRC32C=m +# CONFIG_LIBERTAS is not set +# CONFIG_LIBERTAS_USB is not set +CONFIG_LLC=y +CONFIG_LLC2=m +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCKD=m +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_LOCKD_V4=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_LSF is not set +# CONFIG_LXT_PHY is not set +# CONFIG_MAC80211 is not set +# CONFIG_MAC80211_DEBUG is not set +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MACVLAN is not set +# CONFIG_MAC_EMUMOUSEBTN is not set +CONFIG_MAC_PARTITION=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_MARKEINS is not set +# CONFIG_MARKERS is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_MD is not set +# CONFIG_MEDIA_ATTACH is not set +# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_MEMSTICK is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MIGRATION is not set +CONFIG_MII=y +CONFIG_MINIX_FS=m +# CONFIG_MINIX_SUBPARTITION is not set +CONFIG_MINI_FO=y +CONFIG_MISC_DEVICES=y +# CONFIG_MISDN is not set +CONFIG_MKISS=m +# CONFIG_MMC is not set +# CONFIG_MMC_ARMMMCI is not set +# CONFIG_MMC_SDRICOH_CS is not set +# CONFIG_MMC_TEST is not set +CONFIG_MMU=y +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +CONFIG_MSDOS_FS=m +CONFIG_MSDOS_PARTITION=y +# CONFIG_MTD_AR7_PARTS is not set +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_INTEL_VR_NOR is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_MYLOADER_PARTS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_OOPS is not set +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +CONFIG_MTD_ROOTFS_ROOT_DEV=y +CONFIG_MTD_ROOTFS_SPLIT=y +# CONFIG_MTD_UBI is not set +# CONFIG_MVSWITCH_PHY is not set +# CONFIG_MWAVE is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_NCP_FS is not set +# CONFIG_NE2K_PCI is not set +CONFIG_NET=y +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_DSA is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETDEBUG is not set +CONFIG_NETDEVICES=y +CONFIG_NETDEVICES_MULTIQUEUE=y +CONFIG_NETDEV_1000=y +# CONFIG_NETDEV_10000 is not set +CONFIG_NETFILTER=y +CONFIG_NETFILTER_ADVANCED=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_TPROXY is not set +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LAYER7=m +# CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PORTSCAN=m +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_NETFILTER_XT_TARGET_CHAOS=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_DELUDE=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +CONFIG_NETFILTER_XT_TARGET_TARPIT=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +# CONFIG_NETPOLL is not set +# CONFIG_NETROM is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NET_9P is not set +CONFIG_NET_ACT_GACT=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_MIRRED=m +# CONFIG_NET_ACT_NAT is not set +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_POLICE=y +# CONFIG_NET_ACT_SIMP is not set +CONFIG_NET_CLS=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_POLICE=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_U32=m +# CONFIG_NET_EMATCH is not set +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_ETHERNET=y +# CONFIG_NET_FC is not set +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_KEY=m +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_NET_PCI=y +# CONFIG_NET_PCMCIA is not set +CONFIG_NET_PKTGEN=m +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_NET_RADIO=y +# CONFIG_NET_SB1000 is not set +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_CBQ=m +# CONFIG_NET_SCH_CLK_CPU is not set +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +CONFIG_NET_SCH_CLK_JIFFIES=y +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_ESFQ=m +CONFIG_NET_SCH_ESFQ_NFCT=y +CONFIG_NET_SCH_FIFO=y +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_INGRESS=m +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_NETEM is not set +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_RR=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_TEQL=m +# CONFIG_NET_TULIP is not set +# CONFIG_NET_VENDOR_3COM is not set +CONFIG_NET_WIRELESS=y +CONFIG_NET_WIRELESS_RTNETLINK=y +# CONFIG_NEW_GPIO is not set +CONFIG_NEW_LEDS=y +CONFIG_NFSD=m +CONFIG_NFSD_TCP=y +# CONFIG_NFSD_V2_ACL is not set +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V3_ACL is not set +CONFIG_NFSD_V4=y +# CONFIG_NFS_ACL_SUPPORT is not set +CONFIG_NFS_COMMON=y +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFTL is not set +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_ENABLED=y +# CONFIG_NF_CONNTRACK_EVENTS is not set +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_NF_CONNTRACK_RTSP=m +# CONFIG_NF_CONNTRACK_SANE is not set +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_SUPPORT=y +CONFIG_NF_CONNTRACK_TFTP=m +# CONFIG_NF_CT_ACCT is not set +# CONFIG_NF_CT_NETLINK is not set +# CONFIG_NF_CT_PROTO_DCCP is not set +CONFIG_NF_CT_PROTO_GRE=m +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +CONFIG_NF_NAT=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_H323=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_RTSP=m +CONFIG_NF_NAT_SIP=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NLS=m +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_CODEPAGE_1250=m +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_CODEPAGE_437=m +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=m +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_ISO8859_1=m +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +CONFIG_NLS_ISO8859_15=m +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +CONFIG_NLS_KOI8_R=m +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=m +CONFIG_NORTEL_HERMES=m +# CONFIG_NOZOMI is not set +# CONFIG_NO_HZ is not set +# CONFIG_NS83820 is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_OCF_BENCH is not set +# CONFIG_OCF_EP80579 is not set +# CONFIG_OCF_HIFN is not set +# CONFIG_OCF_HIFNHIPP is not set +# CONFIG_OCF_IXP4XX is not set +# CONFIG_OCF_OCF is not set +# CONFIG_OCF_OCFNULL is not set +# CONFIG_OCF_SAFE is not set +# CONFIG_OCF_TALITOS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_OSF_PARTITION is not set +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_PARPORT is not set +# CONFIG_PARPORT_PC is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_ISAPNP is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_LEGACY is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PCMCIA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +CONFIG_PATA_PLATFORM=m +# CONFIG_PATA_QDI is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set +# CONFIG_PATA_WINBOND_VLB is not set +# CONFIG_PC300TOO is not set +# CONFIG_PCCARD is not set +# CONFIG_PCF8575 is not set +# CONFIG_PCI is not set +# CONFIG_PCI200SYN is not set +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_PCI_ATMEL is not set +CONFIG_PCI_HERMES=m +# CONFIG_PCI_LEGACY is not set +# CONFIG_PCI_MSI is not set +CONFIG_PCI_QUIRKS=y +# CONFIG_PCMCIA is not set +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_ATMEL is not set +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_HERMES is not set +# CONFIG_PCMCIA_IOCTL is not set +# CONFIG_PCMCIA_LOAD_CIS is not set +# CONFIG_PCMCIA_NETWAVE is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_PCMCIA_SPECTRUM is not set +# CONFIG_PCMCIA_SYM53C500 is not set +# CONFIG_PCMCIA_WAVELAN is not set +# CONFIG_PCMCIA_WL3501 is not set +# CONFIG_PCNET32 is not set +# CONFIG_PD6729 is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_PHANTOM is not set +# CONFIG_PHONE is not set +# CONFIG_PHONET is not set +# CONFIG_PHYLIB is not set +# CONFIG_PID_NS is not set +CONFIG_PLIST=y +CONFIG_PLX_HERMES=m +# CONFIG_PM is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_POWER_SUPPLY is not set +CONFIG_PPP=m +CONFIG_PPPOATM=m +CONFIG_PPPOE=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_SYNC_TTY=m +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_PRINTK=y +# CONFIG_PRINTK_TIME is not set +# CONFIG_PRISM54 is not set +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_PROC_SYSCTL=y +# CONFIG_PROFILING is not set +# CONFIG_QEMU is not set +# CONFIG_QLA3XXX is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_R3964 is not set +# CONFIG_R8169 is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_RADIO_AZTECH is not set +# CONFIG_RADIO_CADET is not set +# CONFIG_RADIO_GEMTEK is not set +# CONFIG_RADIO_GEMTEK_PCI is not set +# CONFIG_RADIO_MAESTRO is not set +# CONFIG_RADIO_MAXIRADIO is not set +# CONFIG_RADIO_RTRACK is not set +# CONFIG_RADIO_RTRACK2 is not set +# CONFIG_RADIO_SF16FMI is not set +# CONFIG_RADIO_SF16FMR2 is not set +# CONFIG_RADIO_TERRATEC is not set +# CONFIG_RADIO_TRUST is not set +# CONFIG_RADIO_TYPHOON is not set +# CONFIG_RADIO_ZOLTRIX is not set +# CONFIG_RAID_ATTRS is not set +CONFIG_RAMFS=y +# CONFIG_RAW_DRIVER is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_REGULATOR is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_RELAY is not set +# CONFIG_RESOURCES_64BIT is not set +# CONFIG_RFD_FTL is not set +CONFIG_RFKILL=m +# CONFIG_RFKILL_INPUT is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_ROSE is not set +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_RTC_CLASS is not set +# CONFIG_RTC_DRV_BQ4802 is not set +CONFIG_RTC_DRV_CMOS=y +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTL8187 is not set +CONFIG_RT_MUTEXES=y +# CONFIG_S2IO is not set +# CONFIG_SAMPLES is not set +# CONFIG_SATA_AHCI is not set +# CONFIG_SATA_INIC162X is not set +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PMP is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIL24 is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_SX4 is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SC92031 is not set +# CONFIG_SCHED_TRACER is not set +CONFIG_SCSI=m +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_ESP_CORE is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_SCSI_LPFC is not set +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_MVSAS is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_PAS16 is not set +CONFIG_SCSI_PROC_FS=y +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_SRP is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SECCOMP is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_SECURITYFS is not set +CONFIG_SELECT_MEMORY_MODEL=y +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_APPLESMC is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_CORETEMP is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCHMD is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_SENSORS_I5K_AMB is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_M41T00 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_CS=m +CONFIG_SERIAL_8250_NR_UARTS=2 +# CONFIG_SERIAL_8250_PCI is not set +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_SERIAL_UARTLITE is not set +# CONFIG_SERIO is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SHAPER is not set +CONFIG_SHMEM=y +CONFIG_SIGNALFD=y +# CONFIG_SIS190 is not set +# CONFIG_SIS900 is not set +# CONFIG_SK98LIN is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +CONFIG_SLAB=y +CONFIG_SLABINFO=y +CONFIG_SLHC=m +# CONFIG_SLIP is not set +# CONFIG_SLOB is not set +# CONFIG_SLUB is not set +# CONFIG_SLUB_DEBUG is not set +# CONFIG_SMB_FS is not set +# CONFIG_SMB_NLS_DEFAULT is not set +# CONFIG_SMC911X is not set +# CONFIG_SMC91X is not set +CONFIG_SND=m +# CONFIG_SND_AC97_POWER_SAVE is not set +# CONFIG_SND_AD1816A is not set +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ADLIB is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ALS100 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT2320 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_DT019X is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_ES968 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_HIFIER is not set +CONFIG_SND_HWDEP=m +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIA is not set +# CONFIG_SND_MIPS is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SND_MIXART is not set +CONFIG_SND_MIXER_OSS=m +# CONFIG_SND_MONA is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +CONFIG_SND_OSSEMUL=y +# CONFIG_SND_OXYGEN is not set +# CONFIG_SND_PCI is not set +CONFIG_SND_PCM=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_PCMCIA is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_PDAUDIOCF is not set +CONFIG_SND_RAWMIDI=m +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_RTCTIMER is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_SGALAXY is not set +# CONFIG_SND_SOC is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_SPI is not set +# CONFIG_SND_SSCAPE is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +CONFIG_SND_TIMER=m +# CONFIG_SND_TRIDENT is not set +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SNI_RM is not set +# CONFIG_SOC_CAMERA is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +CONFIG_SOUND=m +# CONFIG_SOUND_PRIME is not set +# CONFIG_SPARSEMEM_MANUAL is not set +# CONFIG_SPI is not set +# CONFIG_SPI_AT25 is not set +# CONFIG_SPI_DEBUG is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_SPI_TLE62X0 is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_SQUASHFS_VMALLOC is not set +# CONFIG_SSB is not set +# CONFIG_SSB_DEBUG is not set +# CONFIG_SSB_PCMCIAHOST is not set +# CONFIG_SSB_SILENT is not set +# CONFIG_SSFDC is not set +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_STANDALONE=y +CONFIG_STP=y +# CONFIG_STRIP is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNGEM is not set +CONFIG_SUNRPC=m +# CONFIG_SUNRPC_BIND34 is not set +CONFIG_SUNRPC_GSS=m +# CONFIG_SUN_PARTITION is not set +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SWAP=y +# CONFIG_SWCONFIG is not set +# CONFIG_SYNCLINK_CS is not set +CONFIG_SYN_COOKIES=y +CONFIG_SYSCTL=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_SYSFS=y +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_SYSVIPC=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_TASKSTATS is not set +# CONFIG_TCG_TPM is not set +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=m +CONFIG_TCP_CONG_CUBIC=m +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_ILLINOIS=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_VEGAS=y +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_WESTWOOD=m +CONFIG_TCP_CONG_YEAH=m +# CONFIG_TCP_MD5SIG is not set +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_TEXTSEARCH_KMP=m +# CONFIG_THERMAL_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_TIFM_CORE is not set +# CONFIG_TIGON3 is not set +CONFIG_TIMERFD=y +# CONFIG_TINY_SHMEM is not set +# CONFIG_TIPC is not set +# CONFIG_TLAN is not set +CONFIG_TMD_HERMES=m +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TPS65010 is not set +# CONFIG_TR is not set +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_TUN=m +# CONFIG_TUNER_3036 is not set +# CONFIG_TUNER_TEA5761 is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_UFS_FS is not set +# CONFIG_UIO is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_UNEVICTABLE_LRU is not set +CONFIG_UNIX=y +CONFIG_UNIX98_PTYS=y +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_USB is not set +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_USB_ACECAD is not set +CONFIG_USB_ACM=m +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AIPTEK is not set +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_APPLETOUCH is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set +CONFIG_USB_ATM=m +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_BANDWIDTH is not set +CONFIG_USB_BELKIN=y +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_CATC=m +# CONFIG_USB_CXACRU is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_DEBUG is not set +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ET61X251 is not set +CONFIG_USB_EZUSB=y +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_GADGET is not set +# CONFIG_USB_GSPCA is not set +CONFIG_USB_HID=m +# CONFIG_USB_HIDDEV is not set +CONFIG_USB_HIDINPUT=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +CONFIG_USB_HSO=m +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +CONFIG_USB_KAWETH=m +# CONFIG_USB_KBD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LIBUSUAL is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_MOUSE is not set +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_RNDIS_HOST=m +# CONFIG_USB_NET_RNDIS_WLAN is not set +CONFIG_USB_NET_ZAURUS=m +# CONFIG_USB_OHCI_BIG_ENDIAN is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_OHCI_HCD_SSB is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OV511 is not set +CONFIG_USB_PEGASUS=m +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_POWERMATE is not set +CONFIG_USB_PRINTER=m +# CONFIG_USB_QUICKCAM_MESSENGER is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SE401 is not set +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_AIRPRIME=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_CP2101=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +# CONFIG_USB_SERIAL_DEBUG is not set +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +# CONFIG_USB_SERIAL_MOTOROLA is not set +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +# CONFIG_USB_SERIAL_SPCP8X5 is not set +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_XIRCOM=m +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_SN9C102 is not set +CONFIG_USB_SPEEDTOUCH=m +# CONFIG_USB_STKWEBCAM is not set +CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE_ALAUDA=y +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +CONFIG_USB_STORAGE_DATAFAB=y +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_FREECOM=y +# CONFIG_USB_STORAGE_ISD200 is not set +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_KARMA=y +# CONFIG_USB_STORAGE_ONETOUCH is not set +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +# CONFIG_USB_STORAGE_SIERRA is not set +CONFIG_USB_STORAGE_USBAT=y +# CONFIG_USB_STV680 is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_TOUCHSCREEN is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_USBNET=m +CONFIG_USB_USBNET_MII=m +# CONFIG_USB_VICAM is not set +# CONFIG_USB_VIDEO_CLASS is not set +# CONFIG_USB_W9968CF is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_XUSBATM is not set +CONFIG_USB_YEALINK=m +# CONFIG_USB_ZC0301 is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USE_GENERIC_SMP_HELPERS is not set +# CONFIG_UTS_NS is not set +# CONFIG_UWB is not set +# CONFIG_VETH is not set +CONFIG_VFAT_FS=m +# CONFIG_VIA_VELOCITY is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_ALLOW_V4L1 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_CAFE_CCIC is not set +# CONFIG_VIDEO_CAPTURE_DRIVERS is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_CX2341X is not set +# CONFIG_VIDEO_CX25840 is not set +# CONFIG_VIDEO_CX88 is not set +CONFIG_VIDEO_DEV=m +# CONFIG_VIDEO_DPC is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set +# CONFIG_VIDEO_HEXIUM_GEMINI is not set +# CONFIG_VIDEO_HEXIUM_ORION is not set +# CONFIG_VIDEO_IVTV is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_MEDIA is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_MXB is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_OVCAMCHIP is not set +# CONFIG_VIDEO_PMS is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA7111 is not set +# CONFIG_VIDEO_SAA7114 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7134 is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_VIDEO_STRADIS is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TDA9875 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_VIDEO_V4L1 is not set +CONFIG_VIDEO_V4L1_COMPAT=y +# CONFIG_VIDEO_V4L2 is not set +# CONFIG_VIDEO_V4L2_COMMON is not set +# CONFIG_VIDEO_VIVI is not set +# CONFIG_VIDEO_VP27SMPX is not set +# CONFIG_VIDEO_VPX3220 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_ZORAN is not set +# CONFIG_VIRQ_DEBUG is not set +# CONFIG_VIRTUALIZATION is not set +CONFIG_VIRT_TO_BUS=y +# CONFIG_VITESSE_PHY is not set +CONFIG_VLAN_8021Q=y +# CONFIG_VLAN_8021Q_GVRP is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_VMSPLIT_1G is not set +# CONFIG_VMSPLIT_2G is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VT is not set +# CONFIG_VXFS_FS is not set +CONFIG_W1=m +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_GPIO is not set +# CONFIG_W1_MASTER_MATROX is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W83627HF_WDT is not set +# CONFIG_W83697HF_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_W83977F_WDT is not set +# CONFIG_WAN is not set +# CONFIG_WANXL is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +# CONFIG_WDTPCI is not set +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_WIRELESS_OLD_REGULATORY=y +CONFIG_WIRELESS=y +CONFIG_WLAN_80211=y +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WR_PPMC is not set +# CONFIG_X25 is not set +CONFIG_XFRM=y +CONFIG_XFRM_IPCOMP=m +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_USER=m +# CONFIG_XFS_DEBUG is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_SECURITY is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_YAFFS_FS is not set +# CONFIG_YAM is not set +# CONFIG_YELLOWFIN is not set +CONFIG_YENTA=m +# CONFIG_YENTA_O2 is not set +# CONFIG_YENTA_RICOH is not set +# CONFIG_YENTA_TI is not set +# CONFIG_YENTA_TOSHIBA is not set +CONFIG_ZD1211RW=m +# CONFIG_ZD1211RW_DEBUG is not set +CONFIG_ZISOFS=y +CONFIG_ZISOFS_FS=m +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA=y +CONFIG_ZONE_DMA_FLAG=1 diff --git a/target/linux/generic-2.6/files-2.6.28/drivers/char/gpio_dev.c b/target/linux/generic-2.6/files-2.6.28/drivers/char/gpio_dev.c new file mode 100644 index 0000000..8392fcf --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/drivers/char/gpio_dev.c @@ -0,0 +1,201 @@ +/* + * character device wrapper for generic gpio layer + * + * 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., 59 Temple Place, Suite 330, Boston, MA02111-1307USA + * + * Feedback, Bugs... blogic@openwrt.org + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/atomic.h> +#include <linux/init.h> +#include <linux/genhd.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/gpio_dev.h> + +#define DRVNAME "gpiodev" +#define DEVNAME "gpio" + +static int dev_major; +static unsigned int gpio_access_mask; +static struct class *gpiodev_class; + +/* Counter is 1, if the device is not opened and zero (or less) if opened. */ +static atomic_t gpio_open_cnt = ATOMIC_INIT(1); + +static int +gpio_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + + if (((1 << arg) & gpio_access_mask) != (1 << arg)) + { + retval = -EINVAL; + goto out; + } + + switch (cmd) + { + case GPIO_GET: + retval = gpio_get_value(arg); + break; + + case GPIO_SET: + gpio_set_value(arg, 1); + break; + + case GPIO_CLEAR: + gpio_set_value(arg, 0); + break; + + case GPIO_DIR_IN: + gpio_direction_input(arg); + break; + + case GPIO_DIR_OUT: + gpio_direction_output(arg, 0); + break; + + default: + retval = -EINVAL; + break; + } + +out: + return retval; +} + +static int +gpio_open(struct inode *inode, struct file *file) +{ + int result = 0; + unsigned int dev_minor = MINOR(inode->i_rdev); + + if (dev_minor != 0) + { + printk(KERN_ERR DRVNAME ": trying to access unknown minor device -> %d\n", dev_minor); + result = -ENODEV; + goto out; + } + + /* FIXME: We should really allow multiple applications to open the device + * at the same time, as long as the apps access different IO pins. + * The generic gpio-registration functions can be used for that. + * Two new IOCTLs have to be introduced for that. Need to check userspace + * compatibility first. --mb */ + if (!atomic_dec_and_test(&gpio_open_cnt)) { + atomic_inc(&gpio_open_cnt); + printk(KERN_ERR DRVNAME ": Device with minor ID %d already in use\n", dev_minor); + result = -EBUSY; + goto out; + } + +out: + return result; +} + +static int +gpio_close(struct inode * inode, struct file * file) +{ + smp_mb__before_atomic_inc(); + atomic_inc(&gpio_open_cnt); + + return 0; +} + +struct file_operations gpio_fops = { + ioctl: gpio_ioctl, + open: gpio_open, + release: gpio_close +}; + +static int +gpio_probe(struct platform_device *dev) +{ + int result = 0; + + dev_major = register_chrdev(0, DEVNAME, &gpio_fops); + if (!dev_major) + { + printk(KERN_ERR DRVNAME ": Error whilst opening %s \n", DEVNAME); + result = -ENODEV; + goto out; + } + + gpiodev_class = class_create(THIS_MODULE, DRVNAME); + device_create(gpiodev_class, NULL, MKDEV(dev_major, 0), dev, DEVNAME); + + printk(KERN_INFO DRVNAME ": gpio device registered with major %d\n", dev_major); + + if (dev->num_resources != 1) + { + printk(KERN_ERR DRVNAME ": device may only have 1 resource\n"); + result = -ENODEV; + goto out; + } + + gpio_access_mask = dev->resource[0].start; + + printk(KERN_INFO DRVNAME ": gpio platform device registered with access mask %08X\n", gpio_access_mask); +out: + return result; +} + +static int +gpio_remove(struct platform_device *dev) +{ + unregister_chrdev(dev_major, DEVNAME); + return 0; +} + +static struct +platform_driver gpio_driver = { + .probe = gpio_probe, + .remove = gpio_remove, + .driver = { + .name = "GPIODEV", + .owner = THIS_MODULE, + }, +}; + +static int __init +gpio_mod_init(void) +{ + int ret = platform_driver_register(&gpio_driver); + if (ret) + printk(KERN_INFO DRVNAME ": Error registering platfom driver!"); + + return ret; +} + +static void __exit +gpio_mod_exit(void) +{ + platform_driver_unregister(&gpio_driver); +} + +module_init (gpio_mod_init); +module_exit (gpio_mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Crispin / OpenWrt"); +MODULE_DESCRIPTION("Character device for for generic gpio api"); diff --git a/target/linux/generic-2.6/files-2.6.28/drivers/input/misc/gpio_buttons.c b/target/linux/generic-2.6/files-2.6.28/drivers/input/misc/gpio_buttons.c new file mode 100644 index 0000000..83a8178 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/drivers/input/misc/gpio_buttons.c @@ -0,0 +1,209 @@ +/* + * Driver for buttons on GPIO lines not capable of generating interrupts + * + * Copyright (C) 2007,2008 Gabor Juhos <juhosg at openwrt.org> + * + * This file was based on: /drivers/input/misc/cobalt_btns.c + * Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> + * + * also was based on: /drivers/input/keyboard/gpio_keys.c + * Copyright 2005 Phil Blundell + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> + +#include <linux/gpio_buttons.h> + +#include <asm/gpio.h> + +#define DRV_NAME "gpio-buttons" +#define DRV_VERSION "0.1.1" +#define PFX DRV_NAME ": " + +struct gpio_buttons_dev { + struct input_polled_dev *poll_dev; + struct gpio_buttons_platform_data *pdata; +}; + +static void gpio_buttons_poll(struct input_polled_dev *dev) +{ + struct gpio_buttons_dev *bdev = dev->private; + struct gpio_buttons_platform_data *pdata = bdev->pdata; + struct input_dev *input = dev->input; + int i; + + for (i = 0; i < bdev->pdata->nbuttons; i++) { + struct gpio_button *button = &pdata->buttons[i]; + unsigned int type = button->type ?: EV_KEY; + int state; + + state = gpio_get_value(button->gpio) ? 1 : 0; + state ^= button->active_low; + + if (state) { + button->count++; + } else { + if (button->count >= button->threshold) { + input_event(input, type, button->code, 1); + input_sync(input); + } + button->count = 0; + } + + if (button->count == button->threshold) { + input_event(input, type, button->code, 0); + input_sync(input); + } + } +} + +static int __devinit gpio_buttons_probe(struct platform_device *pdev) +{ + struct gpio_buttons_platform_data *pdata = pdev->dev.platform_data; + struct gpio_buttons_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + int error, i; + + + if (!pdata) + return -ENXIO; + + bdev = kzalloc(sizeof(*bdev), GFP_KERNEL); + if (!bdev) { + printk(KERN_ERR DRV_NAME "no memory for device\n"); + return -ENOMEM; + } + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + printk(KERN_ERR DRV_NAME "no memory for polled device\n"); + error = -ENOMEM; + goto err_free_bdev; + } + + poll_dev->private = bdev; + poll_dev->poll = gpio_buttons_poll; + poll_dev->poll_interval = pdata->poll_interval; + + input = poll_dev->input; + + input->evbit[0] = BIT(EV_KEY); + input->name = pdev->name; + input->phys = "gpio-buttons/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_button *button = &pdata->buttons[i]; + unsigned int gpio = button->gpio; + unsigned int type = button->type ?: EV_KEY; + + error = gpio_request(gpio, button->desc ? + button->desc : DRV_NAME); + if (error) { + printk(KERN_ERR PFX "unable to claim gpio %u, " + "error %d\n", gpio, error); + goto err_free_gpio; + } + + error = gpio_direction_input(gpio); + if (error) { + printk(KERN_ERR PFX "unable to set direction on " + "gpio %u, error %d\n", gpio, error); + goto err_free_gpio; + } + + input_set_capability(input, type, button->code); + button->count = 0; + } + + bdev->poll_dev = poll_dev; + bdev->pdata = pdata; + platform_set_drvdata(pdev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) { + printk(KERN_ERR PFX "unable to register polled device, " + "error %d\n", error); + goto err_free_gpio; + } + + return 0; + +err_free_gpio: + for (i = i - 1; i >= 0; i--) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(poll_dev); + +err_free_bdev: + kfree(bdev); + + platform_set_drvdata(pdev, NULL); + return error; +} + +static int __devexit gpio_buttons_remove(struct platform_device *pdev) +{ + struct gpio_buttons_dev *bdev = platform_get_drvdata(pdev); + struct gpio_buttons_platform_data *pdata = bdev->pdata; + int i; + + input_unregister_polled_device(bdev->poll_dev); + + for (i = 0; i < pdata->nbuttons; i++) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(bdev->poll_dev); + + kfree(bdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver gpio_buttons_driver = { + .probe = gpio_buttons_probe, + .remove = __devexit_p(gpio_buttons_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_buttons_init(void) +{ + printk(KERN_INFO DRV_NAME " driver version " DRV_VERSION "\n"); + return platform_driver_register(&gpio_buttons_driver); +} + +static void __exit gpio_buttons_exit(void) +{ + platform_driver_unregister(&gpio_buttons_driver); +} + +module_init(gpio_buttons_init); +module_exit(gpio_buttons_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Polled buttons driver for CPU GPIOs"); + diff --git a/target/linux/generic-2.6/files-2.6.28/drivers/leds/leds-alix.c b/target/linux/generic-2.6/files-2.6.28/drivers/leds/leds-alix.c new file mode 100644 index 0000000..103ca7d --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/drivers/leds/leds-alix.c @@ -0,0 +1,172 @@ +/* + * LEDs driver for PCEngines ALIX 2/3 series + * + * Copyright (C) 2007 Petr Liebman + * + * Based on leds-wrap.c + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <asm/io.h> + +#define DRVNAME "alix-led" + +#define ALIX_LED1_PORT (0x6100) +#define ALIX_LED1_ON (1<<22) +#define ALIX_LED1_OFF (1<<6) + +#define ALIX_LED2_PORT (0x6180) +#define ALIX_LED2_ON (1<<25) +#define ALIX_LED2_OFF (1<<9) + +#define ALIX_LED3_PORT (0x6180) +#define ALIX_LED3_ON (1<<27) +#define ALIX_LED3_OFF (1<<11) + + +static struct platform_device *pdev; + +static void alix_led_set_1(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value) + outl(ALIX_LED1_ON, ALIX_LED1_PORT); + else + outl(ALIX_LED1_OFF, ALIX_LED1_PORT); +} + +static void alix_led_set_2(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value) + outl(ALIX_LED2_ON, ALIX_LED2_PORT); + else + outl(ALIX_LED2_OFF, ALIX_LED2_PORT); +} + +static void alix_led_set_3(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value) + outl(ALIX_LED3_ON, ALIX_LED3_PORT); + else + outl(ALIX_LED3_OFF, ALIX_LED3_PORT); +} + +static struct led_classdev alix_led_1 = { + .name = "alix:1", + .brightness_set = alix_led_set_1, +}; + +static struct led_classdev alix_led_2 = { + .name = "alix:2", + .brightness_set = alix_led_set_2, +}; + +static struct led_classdev alix_led_3 = { + .name = "alix:3", + .brightness_set = alix_led_set_3, +}; + + +#ifdef CONFIG_PM +static int alix_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + led_classdev_suspend(&alix_led_1); + led_classdev_suspend(&alix_led_2); + led_classdev_suspend(&alix_led_3); + return 0; +} + +static int alix_led_resume(struct platform_device *dev) +{ + led_classdev_resume(&alix_led_1); + led_classdev_resume(&alix_led_2); + led_classdev_resume(&alix_led_3); + return 0; +} +#else +#define alix_led_suspend NULL +#define alix_led_resume NULL +#endif + +static int alix_led_probe(struct platform_device *pdev) +{ + int ret; + + ret = led_classdev_register(&pdev->dev, &alix_led_1); + if (ret >= 0) + { + ret = led_classdev_register(&pdev->dev, &alix_led_2); + if (ret >= 0) + { + ret = led_classdev_register(&pdev->dev, &alix_led_3); + if (ret < 0) + led_classdev_unregister(&alix_led_2); + } + if (ret < 0) + led_classdev_unregister(&alix_led_1); + } + return ret; +} + +static int alix_led_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&alix_led_1); + led_classdev_unregister(&alix_led_2); + led_classdev_unregister(&alix_led_3); + return 0; +} + +static struct platform_driver alix_led_driver = { + .probe = alix_led_probe, + .remove = alix_led_remove, + .suspend = alix_led_suspend, + .resume = alix_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init alix_led_init(void) +{ + int ret; + + ret = platform_driver_register(&alix_led_driver); + if (ret < 0) + goto out; + + pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + platform_driver_unregister(&alix_led_driver); + goto out; + } + +out: + return ret; +} + +static void __exit alix_led_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&alix_led_driver); +} + +module_init(alix_led_init); +module_exit(alix_led_exit); + +MODULE_AUTHOR("Petr Liebman"); +MODULE_DESCRIPTION("PCEngines ALIX LED driver"); +MODULE_LICENSE("GPL"); + diff --git a/target/linux/generic-2.6/files-2.6.28/drivers/leds/ledtrig-morse.c b/target/linux/generic-2.6/files-2.6.28/drivers/leds/ledtrig-morse.c new file mode 100644 index 0000000..cb47480 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/drivers/leds/ledtrig-morse.c @@ -0,0 +1,365 @@ +/* + * LED Morse Trigger + * + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * + * This file was based on: drivers/led/ledtrig-timer.c + * Copyright 2005-2006 Openedhand Ltd. + * Author: Richard Purdie <rpurdie@openedhand.com> + * + * also based on the patch '[PATCH] 2.5.59 morse code panics' posted + * in the LKML by Tomas Szepe at Thu, 30 Jan 2003 + * Copyright (C) 2002 Andrew Rodland <arodland@noln.com> + * Copyright (C) 2003 Tomas Szepe <szepe@pinerecords.com> + * + * 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 <linux/kernel.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/sysdev.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/leds.h> + +#include "leds.h" + +#define MORSE_DELAY_BASE (HZ/2) + +#define MORSE_STATE_BLINK_START 0 +#define MORSE_STATE_BLINK_STOP 1 + +#define MORSE_DIT_LEN 1 +#define MORSE_DAH_LEN 3 +#define MORSE_SPACE_LEN 7 + +struct morse_trig_data { + unsigned long delay; + char *msg; + + unsigned char morse; + unsigned char state; + char *msgpos; + struct timer_list timer; +}; + +const unsigned char morsetable[] = { + 0122, 0, 0310, 0, 0, 0163, /* "#$%&' */ + 055, 0155, 0, 0, 0163, 0141, 0152, 0051, /* ()*+,-./ */ + 077, 076, 074, 070, 060, 040, 041, 043, 047, 057, /* 0-9 */ + 0107, 0125, 0, 0061, 0, 0114, 0, /* :;<=>?@ */ + 006, 021, 025, 011, 002, 024, 013, 020, 004, /* A-I */ + 036, 015, 022, 007, 005, 017, 026, 033, 012, /* J-R */ + 010, 003, 014, 030, 016, 031, 035, 023, /* S-Z */ + 0, 0, 0, 0, 0154 /* [\]^_ */ +}; + +static inline unsigned char tomorse(char c) { + if (c >= 'a' && c <= 'z') + c = c - 'a' + 'A'; + if (c >= '"' && c <= '_') { + return morsetable[c - '"']; + } else + return 0; +} + +static inline unsigned long dit_len(struct morse_trig_data *morse_data) +{ + return MORSE_DIT_LEN*morse_data->delay; +} + +static inline unsigned long dah_len(struct morse_trig_data *morse_data) +{ + return MORSE_DAH_LEN*morse_data->delay; +} + +static inline unsigned long space_len(struct morse_trig_data *morse_data) +{ + return MORSE_SPACE_LEN*morse_data->delay; +} + +static void morse_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (struct led_classdev *)data; + struct morse_trig_data *morse_data = led_cdev->trigger_data; + unsigned long brightness = LED_OFF; + unsigned long delay = 0; + + if (!morse_data->msg) + goto set_led; + + switch (morse_data->state) { + case MORSE_STATE_BLINK_START: + /* Starting a new blink. We have a valid code in morse. */ + delay = (morse_data->morse & 001) ? dah_len(morse_data): + dit_len(morse_data); + brightness = LED_FULL; + morse_data->state = MORSE_STATE_BLINK_STOP; + morse_data->morse >>= 1; + break; + case MORSE_STATE_BLINK_STOP: + /* Coming off of a blink. */ + morse_data->state = MORSE_STATE_BLINK_START; + + if (morse_data->morse > 1) { + /* Not done yet, just a one-dit pause. */ + delay = dit_len(morse_data); + break; + } + + /* Get a new char, figure out how much space. */ + /* First time through */ + if (!morse_data->msgpos) + morse_data->msgpos = (char *)morse_data->msg; + + if (!*morse_data->msgpos) { + /* Repeating */ + morse_data->msgpos = (char *)morse_data->msg; + delay = space_len(morse_data); + } else { + /* Inter-letter space */ + delay = dah_len(morse_data); + } + + if (!(morse_data->morse = tomorse(*morse_data->msgpos))) { + delay = space_len(morse_data); + /* And get us back here */ + morse_data->state = MORSE_STATE_BLINK_STOP; + } + morse_data->msgpos++; + break; + } + + mod_timer(&morse_data->timer, jiffies + msecs_to_jiffies(delay)); + +set_led: + led_set_brightness(led_cdev, brightness); +} + +static ssize_t _morse_delay_show(struct led_classdev *led_cdev, char *buf) +{ + struct morse_trig_data *morse_data = led_cdev->trigger_data; + + sprintf(buf, "%lu\n", morse_data->delay); + + return strlen(buf) + 1; +} + +static ssize_t _morse_delay_store(struct led_classdev *led_cdev, + const char *buf, size_t size) +{ + struct morse_trig_data *morse_data = led_cdev->trigger_data; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + int ret = -EINVAL; + + if (*after && isspace(*after)) + count++; + + if (count == size) { + morse_data->delay = state; + mod_timer(&morse_data->timer, jiffies + 1); + ret = count; + } + + return ret; +} + +static ssize_t _morse_msg_show(struct led_classdev *led_cdev, char *buf) +{ + struct morse_trig_data *morse_data = led_cdev->trigger_data; + + if (!morse_data->msg) + sprintf(buf, "<none>\n"); + else + sprintf(buf, "%s\n", morse_data->msg); + + return strlen(buf) + 1; +} + +static ssize_t _morse_msg_store(struct led_classdev *led_cdev, + const char *buf, size_t size) +{ + struct morse_trig_data *morse_data = led_cdev->trigger_data; + char *m; + + m = kmalloc(size, GFP_KERNEL); + if (!m) + return -ENOMEM; + + memcpy(m,buf,size); + m[size]='\0'; + + if (morse_data->msg) + kfree(morse_data->msg); + + morse_data->msg = m; + morse_data->msgpos = NULL; + morse_data->state = MORSE_STATE_BLINK_STOP; + + mod_timer(&morse_data->timer, jiffies + 1); + + return size; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) +static ssize_t morse_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return _morse_delay_show(led_cdev, buf); +} + +static ssize_t morse_delay_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return _morse_delay_store(led_cdev, buf, size); +} + +static ssize_t morse_msg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return _morse_msg_show(led_cdev, buf); +} + +static ssize_t morse_msg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return _morse_msg_store(led_cdev, buf, size); +} + +static DEVICE_ATTR(delay, 0644, morse_delay_show, morse_delay_store); +static DEVICE_ATTR(message, 0644, morse_msg_show, morse_msg_store); + +#define led_device_create_file(leddev, attr) \ + device_create_file(leddev->dev, &dev_attr_ ## attr) +#define led_device_remove_file(leddev, attr) \ + device_remove_file(leddev->dev, &dev_attr_ ## attr) + +#else +static ssize_t morse_delay_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + + return _morse_delay_show(led_cdev, buf); +} + +static ssize_t morse_delay_store(struct class_device *dev, const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + + return _morse_delay_store(led_cdev, buf, size); +} + +static ssize_t morse_msg_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + + return _morse_msg_show(led_cdev, buf); +} + +static ssize_t morse_msg_store(struct class_device *dev, const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + + return _morse_msg_store(led_cdev, buf, size); +} + +static CLASS_DEVICE_ATTR(delay, 0644, morse_delay_show, morse_delay_store); +static CLASS_DEVICE_ATTR(message, 0644, morse_msg_show, morse_msg_store); + +#define led_device_create_file(leddev, attr) \ + class_device_create_file(leddev->class_dev, &class_device_attr_ ## attr) +#define led_device_remove_file(leddev, attr) \ + class_device_remove_file(leddev->class_dev, &class_device_attr_ ## attr) + +#endif + +static void morse_trig_activate(struct led_classdev *led_cdev) +{ + struct morse_trig_data *morse_data; + int rc; + + morse_data = kzalloc(sizeof(*morse_data), GFP_KERNEL); + if (!morse_data) + return; + + morse_data->delay = MORSE_DELAY_BASE; + init_timer(&morse_data->timer); + morse_data->timer.function = morse_timer_function; + morse_data->timer.data = (unsigned long)led_cdev; + + rc = led_device_create_file(led_cdev, delay); + if (rc) goto err; + + rc = led_device_create_file(led_cdev, message); + if (rc) goto err_delay; + + led_cdev->trigger_data = morse_data; + + return; + +err_delay: + led_device_remove_file(led_cdev, delay); +err: + kfree(morse_data); +} + +static void morse_trig_deactivate(struct led_classdev *led_cdev) +{ + struct morse_trig_data *morse_data = led_cdev->trigger_data; + + if (!morse_data) + return; + + led_device_remove_file(led_cdev, message); + led_device_remove_file(led_cdev, delay); + + del_timer_sync(&morse_data->timer); + if (morse_data->msg) + kfree(morse_data->msg); + + kfree(morse_data); +} + +static struct led_trigger morse_led_trigger = { + .name = "morse", + .activate = morse_trig_activate, + .deactivate = morse_trig_deactivate, +}; + +static int __init morse_trig_init(void) +{ + return led_trigger_register(&morse_led_trigger); +} + +static void __exit morse_trig_exit(void) +{ + led_trigger_unregister(&morse_led_trigger); +} + +module_init(morse_trig_init); +module_exit(morse_trig_exit); + +MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); +MODULE_DESCRIPTION("Morse LED trigger"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/files-2.6.28/drivers/leds/ledtrig-netdev.c b/target/linux/generic-2.6/files-2.6.28/drivers/leds/ledtrig-netdev.c new file mode 100644 index 0000000..fac3571 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/drivers/leds/ledtrig-netdev.c @@ -0,0 +1,451 @@ +/* + * LED Kernel Netdev Trigger + * + * Toggles the LED to reflect the link and traffic state of a named net device + * + * Copyright 2007 Oliver Jowett <oliver@opencloud.com> + * + * Derived from ledtrig-timer.c which is: + * Copyright 2005-2006 Openedhand Ltd. + * Author: Richard Purdie <rpurdie@openedhand.com> + * + * 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 <linux/module.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/sysdev.h> +#include <linux/netdevice.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <net/net_namespace.h> +#endif + +#include "leds.h" + +/* + * Configurable sysfs attributes: + * + * device_name - network device name to monitor + * + * interval - duration of LED blink, in milliseconds + * + * mode - either "none" (LED is off) or a space separated list of one or more of: + * link: LED's normal state reflects whether the link is up (has carrier) or not + * tx: LED blinks on transmitted data + * rx: LED blinks on receive data + * + * Some suggestions: + * + * Simple link status LED: + * $ echo netdev >someled/trigger + * $ echo eth0 >someled/device_name + * $ echo link >someled/mode + * + * Ethernet-style link/activity LED: + * $ echo netdev >someled/trigger + * $ echo eth0 >someled/device_name + * $ echo "link tx rx" >someled/mode + * + * Modem-style tx/rx LEDs: + * $ echo netdev >led1/trigger + * $ echo ppp0 >led1/device_name + * $ echo tx >led1/mode + * $ echo netdev >led2/trigger + * $ echo ppp0 >led2/device_name + * $ echo rx >led2/mode + * + */ + +#define MODE_LINK 1 +#define MODE_TX 2 +#define MODE_RX 4 + +struct led_netdev_data { + rwlock_t lock; + + struct timer_list timer; + struct notifier_block notifier; + + struct led_classdev *led_cdev; + struct net_device *net_dev; + + char device_name[IFNAMSIZ]; + unsigned interval; + unsigned mode; + unsigned link_up; + unsigned last_activity; +}; + +static void set_baseline_state(struct led_netdev_data *trigger_data) +{ + if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) + led_set_brightness(trigger_data->led_cdev, LED_FULL); + else + led_set_brightness(trigger_data->led_cdev, LED_OFF); + + if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up) + mod_timer(&trigger_data->timer, jiffies + trigger_data->interval); + else + del_timer(&trigger_data->timer); +} + +static ssize_t led_device_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_netdev_data *trigger_data = led_cdev->trigger_data; + + read_lock(&trigger_data->lock); + sprintf(buf, "%s\n", trigger_data->device_name); + read_unlock(&trigger_data->lock); + + return strlen(buf) + 1; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) +extern struct net init_net; +#endif + +static ssize_t led_device_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_netdev_data *trigger_data = led_cdev->trigger_data; + + if (size < 0 || size >= IFNAMSIZ) + return -EINVAL; + + write_lock(&trigger_data->lock); + + strcpy(trigger_data->device_name, buf); + if (size > 0 && trigger_data->device_name[size-1] == '\n') + trigger_data->device_name[size-1] = 0; + + if (trigger_data->device_name[0] != 0) { + /* check for existing device to update from */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name); +#else + trigger_data->net_dev = dev_get_by_name(trigger_data->device_name); +#endif + if (trigger_data->net_dev != NULL) + trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0; + set_baseline_state(trigger_data); /* updates LEDs, may start timers */ + } + + write_unlock(&trigger_data->lock); + return size; +} + +static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store); + +static ssize_t led_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_netdev_data *trigger_data = led_cdev->trigger_data; + + read_lock(&trigger_data->lock); + + if (trigger_data->mode == 0) { + strcpy(buf, "none\n"); + } else { + if (trigger_data->mode & MODE_LINK) + strcat(buf, "link "); + if (trigger_data->mode & MODE_TX) + strcat(buf, "tx "); + if (trigger_data->mode & MODE_RX) + strcat(buf, "rx "); + strcat(buf, "\n"); + } + + read_unlock(&trigger_data->lock); + + return strlen(buf)+1; +} + +static ssize_t led_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_netdev_data *trigger_data = led_cdev->trigger_data; + char copybuf[1024]; + int new_mode = -1; + char *p, *token; + + /* take a copy since we don't want to trash the inbound buffer when using strsep */ + strncpy(copybuf, buf, sizeof(copybuf)); + copybuf[1023] = 0; + p = copybuf; + + while ((token = strsep(&p, " \t\n")) != NULL) { + if (!*token) + continue; + + if (new_mode == -1) + new_mode = 0; + + if (!strcmp(token, "none")) + new_mode = 0; + else if (!strcmp(token, "tx")) + new_mode |= MODE_TX; + else if (!strcmp(token, "rx")) + new_mode |= MODE_RX; + else if (!strcmp(token, "link")) + new_mode |= MODE_LINK; + else + return -EINVAL; + } + + if (new_mode == -1) + return -EINVAL; + + write_lock(&trigger_data->lock); + trigger_data->mode = new_mode; + set_baseline_state(trigger_data); + write_unlock(&trigger_data->lock); + + return size; +} + +static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store); + +static ssize_t led_interval_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_netdev_data *trigger_data = led_cdev->trigger_data; + + read_lock(&trigger_data->lock); + sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval)); + read_unlock(&trigger_data->lock); + + return strlen(buf) + 1; +} + +static ssize_t led_interval_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_netdev_data *trigger_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long value = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (*after && isspace(*after)) + count++; + + /* impose some basic bounds on the timer interval */ + if (count == size && value >= 5 && value <= 10000) { + write_lock(&trigger_data->lock); + trigger_data->interval = msecs_to_jiffies(value); + set_baseline_state(trigger_data); // resets timer + write_unlock(&trigger_data->lock); + ret = count; + } + + return ret; +} + +static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store); + +static int netdev_trig_notify(struct notifier_block *nb, + unsigned long evt, + void *dv) +{ + struct net_device *dev = dv; + struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier); + + if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER) + return NOTIFY_DONE; + + write_lock(&trigger_data->lock); + + if (strcmp(dev->name, trigger_data->device_name)) + goto done; + + if (evt == NETDEV_REGISTER) { + if (trigger_data->net_dev != NULL) + dev_put(trigger_data->net_dev); + dev_hold(dev); + trigger_data->net_dev = dev; + trigger_data->link_up = 0; + goto done; + } + + if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) { + dev_put(trigger_data->net_dev); + trigger_data->net_dev = NULL; + goto done; + } + + /* UP / DOWN / CHANGE */ + + trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev)); + set_baseline_state(trigger_data); + +done: + write_unlock(&trigger_data->lock); + return NOTIFY_DONE; +} + +/* here's the real work! */ +static void netdev_trig_timer(unsigned long arg) +{ + struct led_netdev_data *trigger_data = (struct led_netdev_data *)arg; + struct net_device_stats *dev_stats; + unsigned new_activity; + + write_lock(&trigger_data->lock); + + if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) { + /* we don't need to do timer work, just reflect link state. */ + led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF); + goto no_restart; + } + + dev_stats = trigger_data->net_dev->get_stats(trigger_data->net_dev); + new_activity = + ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) + + ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0); + + if (trigger_data->mode & MODE_LINK) { + /* base state is ON (link present) */ + /* if there's no link, we don't get this far and the LED is off */ + + /* OFF -> ON always */ + /* ON -> OFF on activity */ + if (trigger_data->led_cdev->brightness == LED_OFF) { + led_set_brightness(trigger_data->led_cdev, LED_FULL); + } else if (trigger_data->last_activity != new_activity) { + led_set_brightness(trigger_data->led_cdev, LED_OFF); + } + } else { + /* base state is OFF */ + /* ON -> OFF always */ + /* OFF -> ON on activity */ + if (trigger_data->led_cdev->brightness == LED_FULL) { + led_set_brightness(trigger_data->led_cdev, LED_OFF); + } else if (trigger_data->last_activity != new_activity) { + led_set_brightness(trigger_data->led_cdev, LED_FULL); + } + } + + trigger_data->last_activity = new_activity; + mod_timer(&trigger_data->timer, jiffies + trigger_data->interval); + +no_restart: + write_unlock(&trigger_data->lock); +} + +static void netdev_trig_activate(struct led_classdev *led_cdev) +{ + struct led_netdev_data *trigger_data; + int rc; + + trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); + if (!trigger_data) + return; + + rwlock_init(&trigger_data->lock); + + trigger_data->notifier.notifier_call = netdev_trig_notify; + trigger_data->notifier.priority = 10; + + setup_timer(&trigger_data->timer, netdev_trig_timer, (unsigned long) trigger_data); + + trigger_data->led_cdev = led_cdev; + trigger_data->net_dev = NULL; + trigger_data->device_name[0] = 0; + + trigger_data->mode = 0; + trigger_data->interval = msecs_to_jiffies(50); + trigger_data->link_up = 0; + trigger_data->last_activity = 0; + + led_cdev->trigger_data = trigger_data; + + rc = device_create_file(led_cdev->dev, &dev_attr_device_name); + if (rc) + goto err_out; + rc = device_create_file(led_cdev->dev, &dev_attr_mode); + if (rc) + goto err_out_device_name; + rc = device_create_file(led_cdev->dev, &dev_attr_interval); + if (rc) + goto err_out_mode; + + register_netdevice_notifier(&trigger_data->notifier); + return; + +err_out_mode: + device_remove_file(led_cdev->dev, &dev_attr_mode); +err_out_device_name: + device_remove_file(led_cdev->dev, &dev_attr_device_name); +err_out: + led_cdev->trigger_data = NULL; + kfree(trigger_data); +} + +static void netdev_trig_deactivate(struct led_classdev *led_cdev) +{ + struct led_netdev_data *trigger_data = led_cdev->trigger_data; + + if (trigger_data) { + unregister_netdevice_notifier(&trigger_data->notifier); + + device_remove_file(led_cdev->dev, &dev_attr_device_name); + device_remove_file(led_cdev->dev, &dev_attr_mode); + device_remove_file(led_cdev->dev, &dev_attr_interval); + + write_lock(&trigger_data->lock); + + if (trigger_data->net_dev) { + dev_put(trigger_data->net_dev); + trigger_data->net_dev = NULL; + } + + write_unlock(&trigger_data->lock); + + del_timer_sync(&trigger_data->timer); + + kfree(trigger_data); + } +} + +static struct led_trigger netdev_led_trigger = { + .name = "netdev", + .activate = netdev_trig_activate, + .deactivate = netdev_trig_deactivate, +}; + +static int __init netdev_trig_init(void) +{ + return led_trigger_register(&netdev_led_trigger); +} + +static void __exit netdev_trig_exit(void) +{ + led_trigger_unregister(&netdev_led_trigger); +} + +module_init(netdev_trig_init); +module_exit(netdev_trig_exit); + +MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>"); +MODULE_DESCRIPTION("Netdev LED trigger"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/files-2.6.28/include/linux/gpio_buttons.h b/target/linux/generic-2.6/files-2.6.28/include/linux/gpio_buttons.h new file mode 100644 index 0000000..f5e6297 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/include/linux/gpio_buttons.h @@ -0,0 +1,35 @@ +/* + * Definitions for the GPIO buttons interface driver + * + * Copyright (C) 2007,2008 Gabor Juhos <juhosg at openwrt.org> + * + * This file was based on: /include/linux/gpio_keys.h + * The original gpio_keys.h seems not to have a license. + * + * 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. + * + */ + +#ifndef _GPIO_BUTTONS_H_ +#define _GPIO_BUTTONS_H_ + +struct gpio_button { + int gpio; /* GPIO line number */ + int active_low; + char *desc; /* button description */ + int type; /* input event type (EV_KEY, EV_SW) */ + int code; /* input event code (KEY_*, SW_*) */ + int count; + int threshold; /* count threshold */ +}; + +struct gpio_buttons_platform_data { + struct gpio_button *buttons; + int nbuttons; /* number of buttons */ + int poll_interval; /* polling interval */ +}; + +#endif /* _GPIO_BUTTONS_H_ */ + diff --git a/target/linux/generic-2.6/files-2.6.28/include/linux/gpio_dev.h b/target/linux/generic-2.6/files-2.6.28/include/linux/gpio_dev.h new file mode 100644 index 0000000..3f3c9c7 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/include/linux/gpio_dev.h @@ -0,0 +1,11 @@ +#ifndef _GPIODEV_H__ +#define _GPIODEV_H__ + +#define IOC_GPIODEV_MAGIC 'B' +#define GPIO_GET _IO(IOC_GPIODEV_MAGIC, 10) +#define GPIO_SET _IO(IOC_GPIODEV_MAGIC, 11) +#define GPIO_CLEAR _IO(IOC_GPIODEV_MAGIC, 12) +#define GPIO_DIR_IN _IO(IOC_GPIODEV_MAGIC, 13) +#define GPIO_DIR_OUT _IO(IOC_GPIODEV_MAGIC, 14) + +#endif diff --git a/target/linux/generic-2.6/files-2.6.28/include/linux/switch.h b/target/linux/generic-2.6/files-2.6.28/include/linux/switch.h new file mode 100644 index 0000000..9411e84 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.28/include/linux/switch.h @@ -0,0 +1,168 @@ +/* + * switch.h: Switch configuration API + * + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> + * + * 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. + */ + +#ifndef __LINUX_SWITCH_H +#define __LINUX_SWITCH_H + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#ifndef __KERNEL__ +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#else +#include <net/genetlink.h> +#endif + +/* main attributes */ +enum { + SWITCH_ATTR_UNSPEC, + /* global */ + SWITCH_ATTR_TYPE, + /* device */ + SWITCH_ATTR_ID, + SWITCH_ATTR_NAME, + SWITCH_ATTR_DEV_NAME, + SWITCH_ATTR_VLANS, + SWITCH_ATTR_PORTS, + /* attributes */ + SWITCH_ATTR_OP_ID, + SWITCH_ATTR_OP_TYPE, + SWITCH_ATTR_OP_NAME, + SWITCH_ATTR_OP_PORT, + SWITCH_ATTR_OP_VLAN, + SWITCH_ATTR_OP_VALUE_INT, + SWITCH_ATTR_OP_VALUE_STR, + SWITCH_ATTR_OP_VALUE_PORTS, + SWITCH_ATTR_OP_DESCRIPTION, + /* port lists */ + SWITCH_ATTR_PORT, + SWITCH_ATTR_MAX +}; + +/* commands */ +enum { + SWITCH_CMD_UNSPEC, + SWITCH_CMD_GET_SWITCH, + SWITCH_CMD_NEW_ATTR, + SWITCH_CMD_LIST_GLOBAL, + SWITCH_CMD_GET_GLOBAL, + SWITCH_CMD_SET_GLOBAL, + SWITCH_CMD_LIST_PORT, + SWITCH_CMD_GET_PORT, + SWITCH_CMD_SET_PORT, + SWITCH_CMD_LIST_VLAN, + SWITCH_CMD_GET_VLAN, + SWITCH_CMD_SET_VLAN +}; + +/* data types */ +enum switch_val_type { + SWITCH_TYPE_UNSPEC, + SWITCH_TYPE_INT, + SWITCH_TYPE_STRING, + SWITCH_TYPE_PORTS, + SWITCH_TYPE_NOVAL, +}; + +/* port nested attributes */ +enum { + SWITCH_PORT_UNSPEC, + SWITCH_PORT_ID, + SWITCH_PORT_FLAG_TAGGED, + SWITCH_PORT_ATTR_MAX +}; + +#define SWITCH_ATTR_DEFAULTS_OFFSET 0x1000 + +#ifdef __KERNEL__ + +struct switch_dev; +struct switch_op; +struct switch_val; +struct switch_attr; +struct switch_attrlist; + +int register_switch(struct switch_dev *dev, struct net_device *netdev); +void unregister_switch(struct switch_dev *dev); + +struct switch_attrlist { + /* filled in by the driver */ + int n_attr; + struct switch_attr *attr; +}; + + +struct switch_dev { + int id; + void *priv; + const char *name; + + /* NB: either devname or netdev must be set */ + const char *devname; + struct net_device *netdev; + + int ports; + int vlans; + int cpu_port; + struct switch_attrlist attr_global, attr_port, attr_vlan; + + spinlock_t lock; + struct switch_port *portbuf; + struct list_head dev_list; + unsigned long def_global, def_port, def_vlan; + + int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val); + int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val); + int (*apply_config)(struct switch_dev *dev); +}; + +struct switch_port { + u32 id; + u32 flags; +}; + +struct switch_val { + struct switch_attr *attr; + int port_vlan; + int len; + union { + const char *s; + u32 i; + struct switch_port *ports; + } value; +}; + +struct switch_attr { + int disabled; + int type; + const char *name; + const char *description; + + int (*set)(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val); + int (*get)(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val); + + /* for driver internal use */ + int id; + int ofs; + int max; +}; + +#endif + +#endif diff --git a/target/linux/generic-2.6/patches-2.6.28/001-squashfs.patch b/target/linux/generic-2.6/patches-2.6.28/001-squashfs.patch new file mode 100644 index 0000000..606f150 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/001-squashfs.patch @@ -0,0 +1,4170 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -931,6 +931,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 <file:Documentation/modules.txt>. 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 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -76,6 +76,7 @@ obj-$(CONFIG_JBD) += jbd/ + obj-$(CONFIG_JBD2) += jbd2/ + obj-$(CONFIG_EXT2_FS) += ext2/ + obj-$(CONFIG_CRAMFS) += cramfs/ ++obj-$(CONFIG_SQUASHFS) += squashfs/ + obj-y += ramfs/ + obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ + obj-$(CONFIG_CODA_FS) += coda/ +--- /dev/null ++++ b/fs/squashfs/inode.c +@@ -0,0 +1,2122 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * 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 <linux/types.h> ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/fs.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++#include <linux/buffer_head.h> ++#include <linux/vfs.h> ++#include <linux/init.h> ++#include <linux/dcache.h> ++#include <linux/wait.h> ++#include <linux/zlib.h> ++#include <linux/blkdev.h> ++#include <linux/vmalloc.h> ++#include <asm/uaccess.h> ++#include <asm/semaphore.h> ++ ++#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; ++ ++ 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); ++ 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 <phillip@lougher.org.uk>"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/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 +--- /dev/null ++++ b/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 <phillip@lougher.org.uk> ++ * ++ * 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 <linux/types.h> ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/fs.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++#include <linux/buffer_head.h> ++#include <linux/vfs.h> ++#include <linux/init.h> ++#include <linux/dcache.h> ++#include <linux/wait.h> ++#include <linux/zlib.h> ++#include <linux/blkdev.h> ++#include <linux/vmalloc.h> ++#include <asm/uaccess.h> ++#include <asm/semaphore.h> ++ ++#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; ++} +--- /dev/null ++++ b/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 <phillip@lougher.org.uk> ++ * ++ * 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 +--- a/include/linux/magic.h ++++ b/include/linux/magic.h +@@ -39,6 +39,9 @@ + #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" + #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + ++#define SQUASHFS_MAGIC 0x73717368 ++#define SQUASHFS_MAGIC_SWAP 0x68737173 ++ + #define SMB_SUPER_MAGIC 0x517B + #define USBDEVICE_SUPER_MAGIC 0x9fa2 + #define CGROUP_SUPER_MAGIC 0x27e0eb +--- /dev/null ++++ b/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 <phillip@lougher.org.uk> ++ * ++ * 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_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 ++ ++#include <linux/magic.h> ++ ++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 <asm/byteorder.h> ++ ++#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 +--- /dev/null ++++ b/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 <phillip@lougher.org.uk> ++ * ++ * 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 +--- /dev/null ++++ b/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 <phillip@lougher.org.uk> ++ * ++ * 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 <linux/squashfs_fs.h> ++ ++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 +--- a/init/do_mounts_rd.c ++++ b/init/do_mounts_rd.c +@@ -5,6 +5,7 @@ + #include <linux/ext2_fs.h> + #include <linux/romfs_fs.h> + #include <linux/cramfs_fs.h> ++#include <linux/squashfs_fs.h> + #include <linux/initrd.h> + #include <linux/string.h> + +@@ -37,6 +38,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 +@@ -51,6 +53,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; + +@@ -62,6 +65,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); + + /* +@@ -99,6 +103,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.28/002-lzma_decompress.patch b/target/linux/generic-2.6/patches-2.6.28/002-lzma_decompress.patch new file mode 100644 index 0000000..4c1578e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/002-lzma_decompress.patch @@ -0,0 +1,780 @@ +--- /dev/null ++++ b/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 +--- /dev/null ++++ b/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 <linux/LzmaDecode.h> ++ ++#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; ++} +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -20,7 +20,7 @@ lib-y += kobject.o kref.o klist.o + + obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ + bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ +- string_helpers.o ++ string_helpers.o LzmaDecode.o + + ifeq ($(CONFIG_DEBUG_KOBJECT),y) + CFLAGS_kobject.o += -DDEBUG diff --git a/target/linux/generic-2.6/patches-2.6.28/003-squashfs_lzma.patch b/target/linux/generic-2.6/patches-2.6.28/003-squashfs_lzma.patch new file mode 100644 index 0000000..9050e37 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/003-squashfs_lzma.patch @@ -0,0 +1,107 @@ +--- a/fs/squashfs/inode.c ++++ b/fs/squashfs/inode.c +@@ -4,6 +4,9 @@ + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher <phillip@lougher.org.uk> + * ++ * LZMA decompressor support added by Oleg I. Vdovikin ++ * Copyright (c) 2005 Oleg I.Vdovikin <oleg@cs.msu.su> ++ * + * 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 <linux/types.h> + #include <linux/squashfs_fs.h> + #include <linux/module.h> +@@ -44,6 +48,19 @@ + + #include "squashfs.h" + ++#ifdef SQUASHFS_LZMA ++#include <linux/LzmaDecode.h> ++ ++/* 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.28/004-extra_optimization.patch b/target/linux/generic-2.6/patches-2.6.28/004-extra_optimization.patch new file mode 100644 index 0000000..d686689 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/004-extra_optimization.patch @@ -0,0 +1,12 @@ +--- a/Makefile ++++ b/Makefile +@@ -549,6 +549,9 @@ endif + 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 + KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) + diff --git a/target/linux/generic-2.6/patches-2.6.28/005-squashfs_fix.patch b/target/linux/generic-2.6/patches-2.6.28/005-squashfs_fix.patch new file mode 100644 index 0000000..5147486 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/005-squashfs_fix.patch @@ -0,0 +1,29 @@ +--- a/fs/squashfs/inode.c ++++ b/fs/squashfs/inode.c +@@ -33,6 +33,7 @@ + #include <linux/fs.h> + #include <linux/smp_lock.h> + #include <linux/slab.h> ++#include <linux/exportfs.h> + #include <linux/squashfs_fs_sb.h> + #include <linux/squashfs_fs_i.h> + #include <linux/buffer_head.h> +@@ -43,8 +44,8 @@ + #include <linux/zlib.h> + #include <linux/blkdev.h> + #include <linux/vmalloc.h> ++#include <linux/semaphore.h> + #include <asm/uaccess.h> +-#include <asm/semaphore.h> + + #include "squashfs.h" + +@@ -2125,7 +2126,7 @@ static void squashfs_destroy_inode(struc + } + + +-static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) ++static void init_once(void *foo) + { + struct squashfs_inode_info *ei = foo; + diff --git a/target/linux/generic-2.6/patches-2.6.28/006-gcc4_inline_fix.patch b/target/linux/generic-2.6/patches-2.6.28/006-gcc4_inline_fix.patch new file mode 100644 index 0000000..642f11b --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/006-gcc4_inline_fix.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/include/asm/system.h ++++ b/arch/mips/include/asm/system.h +@@ -187,7 +187,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.28/007-samsung_flash.patch b/target/linux/generic-2.6/patches-2.6.28/007-samsung_flash.patch new file mode 100644 index 0000000..abb753c --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/007-samsung_flash.patch @@ -0,0 +1,36 @@ +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/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 *); +@@ -365,12 +366,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.28/010-disable_old_squashfs_compatibility.patch b/target/linux/generic-2.6/patches-2.6.28/010-disable_old_squashfs_compatibility.patch new file mode 100644 index 0000000..01e2757 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/010-disable_old_squashfs_compatibility.patch @@ -0,0 +1,19 @@ +--- a/fs/squashfs/Makefile ++++ b/fs/squashfs/Makefile +@@ -4,4 +4,3 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += inode.o +-squashfs-y += squashfs2_0.o +--- a/fs/squashfs/squashfs.h ++++ b/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.28/011-mips_boot.patch b/target/linux/generic-2.6/patches-2.6.28/011-mips_boot.patch new file mode 100644 index 0000000..c2a043a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/011-mips_boot.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -121,6 +121,8 @@ + #endif + .endm + ++ j kernel_entry ++ nop + #ifndef CONFIG_NO_EXCEPT_FILL + /* + * Reserved space for exception handlers. diff --git a/target/linux/generic-2.6/patches-2.6.28/020-mips_multi_machine_support.patch b/target/linux/generic-2.6/patches-2.6.28/020-mips_multi_machine_support.patch new file mode 100644 index 0000000..4e783ff --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/020-mips_multi_machine_support.patch @@ -0,0 +1,133 @@ +--- /dev/null ++++ b/include/asm-mips/mips_machine.h +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef __ASM_MIPS_MACHINE_H ++#define __ASM_MIPS_MACHINE_H ++ ++#include <linux/init.h> ++#include <linux/list.h> ++ ++#define MIPS_MACHINE_NAME_LEN 64 ++ ++struct mips_machine { ++ unsigned long mach_type; ++ void (*mach_setup)(void); ++ unsigned char mach_name[MIPS_MACHINE_NAME_LEN]; ++ struct list_head list; ++}; ++ ++void mips_machine_register(struct mips_machine *) __init; ++void mips_machine_setup(unsigned long machtype) __init; ++ ++extern unsigned char mips_machine_name[MIPS_MACHINE_NAME_LEN]; ++ ++#define MIPS_MACHINE(_type, _name, _setup) \ ++static struct mips_machine machine_##_type __initdata = \ ++{ \ ++ .mach_type = _type, \ ++ .mach_name = _name, \ ++ .mach_setup = _setup, \ ++}; \ ++ \ ++static int __init register_machine_##_type(void) \ ++{ \ ++ mips_machine_register(&machine_##_type); \ ++ return 0; \ ++} \ ++ \ ++pure_initcall(register_machine_##_type) ++ ++#endif /* __ASM_MIPS_MACHINE_H */ ++ +--- /dev/null ++++ b/arch/mips/kernel/mips_machine.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * 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 <asm/mips_machine.h> ++#include <asm/bootinfo.h> ++ ++static struct list_head mips_machines __initdata = ++ LIST_HEAD_INIT(mips_machines); ++ ++unsigned char mips_machine_name[MIPS_MACHINE_NAME_LEN] = "Unknown"; ++ ++static struct mips_machine * __init mips_machine_find(unsigned long machtype) ++{ ++ struct list_head *this; ++ ++ list_for_each(this, &mips_machines) { ++ struct mips_machine *mach; ++ ++ mach = list_entry(this, struct mips_machine, list); ++ if (mach->mach_type == machtype) ++ return mach; ++ } ++ ++ return NULL; ++} ++ ++void __init mips_machine_register(struct mips_machine *mach) ++{ ++ list_add_tail(&mach->list, &mips_machines); ++} ++ ++void __init mips_machine_setup(unsigned long machtype) ++{ ++ struct mips_machine *mach; ++ ++ mach = mips_machine_find(machtype); ++ if (!mach) { ++ printk(KERN_ALERT "MIPS: no machine registered for " ++ "machtype %lu\n", machtype); ++ return; ++ } ++ ++ if (mach->mach_name[0]) ++ strncpy(mips_machine_name, mach->mach_name, ++ MIPS_MACHINE_NAME_LEN); ++ ++ printk(KERN_INFO "MIPS: machine is %s\n", mips_machine_name); ++ ++ if (mach->mach_setup) ++ mach->mach_setup(); ++} ++ +--- a/arch/mips/kernel/Makefile ++++ b/arch/mips/kernel/Makefile +@@ -84,6 +84,7 @@ obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o + + obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o ++obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o + + CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -763,6 +763,9 @@ config MIPS_DISABLE_OBSOLETE_IDE + config SYNC_R4K + bool + ++config MIPS_MACHINE ++ def_bool n ++ + config NO_IOPORT + def_bool n + diff --git a/target/linux/generic-2.6/patches-2.6.28/060-block2mtd_init.patch b/target/linux/generic-2.6/patches-2.6.28/060-block2mtd_init.patch new file mode 100644 index 0000000..50df4aa --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/060-block2mtd_init.patch @@ -0,0 +1,110 @@ +--- a/drivers/mtd/devices/block2mtd.c ++++ b/drivers/mtd/devices/block2mtd.c +@@ -14,6 +14,7 @@ + #include <linux/list.h> + #include <linux/init.h> + #include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> + #include <linux/buffer_head.h> + #include <linux/mutex.h> + #include <linux/mount.h> +@@ -232,10 +233,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, const char *mtdname) + { + struct block_device *bdev; + struct block2mtd_dev *dev; ++ struct mtd_partition *part; + char *name; + + if (!devname) +@@ -273,17 +275,17 @@ static struct block2mtd_dev *add_device( + + mutex_init(&dev->write_mutex); + +- /* Setup the MTD structure */ +- /* make the name contain the block device in */ +- name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1, +- GFP_KERNEL); ++ if (!mtdname) ++ mtdname = devname; ++ ++ name = kmalloc(strlen(mtdname) + 1, GFP_KERNEL); + if (!name) + goto devinit_err; + +- sprintf(name, "block2mtd: %s", devname); ++ strcpy(name, mtdname); + dev->mtd.name = name; + +- 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; +@@ -296,14 +298,17 @@ static struct block2mtd_dev *add_device( + 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("block2mtd: "), +- dev->mtd.erasesize >> 10, dev->mtd.erasesize); ++ mtdname, dev->mtd.erasesize >> 10, dev->mtd.erasesize); + return dev; + + devinit_err: +@@ -376,9 +381,9 @@ static char block2mtd_paramline[80 + 12] + + 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; +@@ -389,7 +394,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) +@@ -408,8 +413,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; + } +@@ -443,7 +450,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=<dev>[,<erasesize>]\""); ++MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>[,<name>]]\""); + + static int __init block2mtd_init(void) + { diff --git a/target/linux/generic-2.6/patches-2.6.28/065-rootfs_split.patch b/target/linux/generic-2.6/patches-2.6.28/065-rootfs_split.patch new file mode 100644 index 0000000..176bed8 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/065-rootfs_split.patch @@ -0,0 +1,625 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -45,6 +45,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 +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -18,6 +18,8 @@ + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> + #include <linux/mtd/compatmac.h> ++#include <linux/squashfs_fs.h> ++#include <linux/root_dev.h> + + /* Our partition linked list */ + static LIST_HEAD(mtd_partitions); +@@ -37,7 +39,7 @@ struct mtd_part { + * the pointer to that structure with this macro. + */ + #define PART(x) ((struct mtd_part *)(x)) +- ++#define IS_PART(mtd) (mtd->read == part_read) + + /* + * MTD methods which simply translate the effective address and pass through +@@ -489,6 +491,148 @@ out_register: + return slave; + } + ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++#define ROOTFS_SPLIT_NAME "rootfs_data" ++#define ROOTFS_REMOVED_NAME "<removed>" ++static int split_squashfs(struct mtd_info *master, int offset, int *split_offset) ++{ ++ char buf[512]; ++ struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; ++ int len, ret; ++ ++ ret = master->read(master, offset, sizeof(*sb), &len, buf); ++ if (ret || (len != sizeof(*sb))) { ++ printk(KERN_ALERT "split_squashfs: error occured while reading " ++ "from \"%s\"\n", master->name); ++ return -EINVAL; ++ } ++ ++ if (*((u32 *) buf) != SQUASHFS_MAGIC) { ++ printk(KERN_ALERT "split_squashfs: no squashfs found in \"%s\"\n", ++ master->name); ++ *split_offset = 0; ++ return 0; ++ } ++ ++ if (sb->bytes_used <= 0) { ++ printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n", ++ master->name); ++ *split_offset = 0; ++ return 0; ++ } ++ ++ len = (u32) sb->bytes_used; ++ len += (offset & 0x000fffff); ++ len += (master->erasesize - 1); ++ len &= ~(master->erasesize - 1); ++ len -= (offset & 0x000fffff); ++ *split_offset = offset + len; ++ ++ return 0; ++} ++ ++static int split_rootfs_data(struct mtd_info *master, struct mtd_info *rpart, const struct mtd_partition *part, ++ int index) ++{ ++ struct mtd_partition *dpart; ++ struct mtd_part *slave = NULL; ++ int split_offset = 0; ++ int ret; ++ ++ ret = split_squashfs(master, part->offset, &split_offset); ++ if (ret) ++ return ret; ++ ++ if (split_offset <= 0) ++ return 0; ++ ++ dpart = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL); ++ if (dpart == NULL) { ++ printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n", ++ ROOTFS_SPLIT_NAME); ++ return -ENOMEM; ++ } ++ ++ memcpy(dpart, part, sizeof(*part)); ++ dpart->name = (unsigned char *)&dpart[1]; ++ strcpy(dpart->name, ROOTFS_SPLIT_NAME); ++ ++ dpart->size -= split_offset - dpart->offset; ++ dpart->offset = split_offset; ++ ++ 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); ++ ++ slave = add_one_partition(master, dpart, index, split_offset); ++ if (!slave) { ++ kfree(dpart); ++ return -ENOMEM; ++ } ++ rpart->split = &slave->mtd; ++ ++ return 0; ++} ++ ++static int refresh_rootfs_split(struct mtd_info *mtd) ++{ ++ struct mtd_partition tpart; ++ struct mtd_part *part; ++ char *name; ++ int index = 0; ++ int offset, size; ++ int ret; ++ ++ part = PART(mtd); ++ ++ /* check for the new squashfs offset first */ ++ ret = split_squashfs(part->master, part->offset, &offset); ++ if (ret) ++ return ret; ++ ++ if ((offset > 0) && !mtd->split) { ++ printk(KERN_INFO "%s: creating new split partition for \"%s\"\n", __func__, mtd->name); ++ /* if we don't have a rootfs split partition, create a new one */ ++ tpart.name = (char *) mtd->name; ++ tpart.size = mtd->size; ++ tpart.offset = part->offset; ++ ++ /* find the index of the last partition */ ++ if (!list_empty(&mtd_partitions)) ++ index = list_first_entry(&mtd_partitions, struct mtd_part, list)->index + 1; ++ ++ return split_rootfs_data(part->master, &part->mtd, &tpart, index); ++ } else if ((offset > 0) && mtd->split) { ++ /* update the offsets of the existing partition */ ++ size = mtd->size + part->offset - offset; ++ ++ part = PART(mtd->split); ++ part->offset = offset; ++ part->mtd.size = size; ++ printk(KERN_INFO "%s: %s partition \"" ROOTFS_SPLIT_NAME "\", offset: 0x%06x (0x%06x)\n", ++ __func__, (!strcmp(part->mtd.name, ROOTFS_SPLIT_NAME) ? "updating" : "creating"), ++ part->offset, part->mtd.size); ++ name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL); ++ strcpy(name, ROOTFS_SPLIT_NAME); ++ part->mtd.name = name; ++ } else if ((offset <= 0) && mtd->split) { ++ printk(KERN_INFO "%s: removing partition \"%s\"\n", __func__, mtd->split->name); ++ ++ /* mark existing partition as removed */ ++ part = PART(mtd->split); ++ name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL); ++ strcpy(name, ROOTFS_REMOVED_NAME); ++ part->mtd.name = name; ++ part->offset = 0; ++ part->mtd.size = 0; ++ } ++ ++ return 0; ++} ++#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 +@@ -502,14 +646,29 @@ int add_mtd_partitions(struct mtd_info * + { + struct mtd_part *slave; + u_int32_t cur_offset = 0; +- int i; ++ int i, j, ret; + + printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + +- for (i = 0; i < nbparts; i++) { +- slave = add_one_partition(master, parts + i, i, cur_offset); ++ for (i = 0, j = 0; i < nbparts; i++) { ++ slave = add_one_partition(master, parts + i, j++, cur_offset); + if (!slave) + return -ENOMEM; ++ ++ if (!strcmp(parts[i].name, "rootfs") && 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); ++ } ++#endif ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++ ret = split_rootfs_data(master, &slave->mtd, &parts[i], j); ++ if (ret == 0) ++ j++; ++#endif ++ } + cur_offset = slave->offset + slave->mtd.size; + } + +@@ -517,6 +676,32 @@ int add_mtd_partitions(struct mtd_info * + } + EXPORT_SYMBOL(add_mtd_partitions); + ++int refresh_mtd_partitions(struct mtd_info *mtd) ++{ ++ int ret = 0; ++ ++ if (IS_PART(mtd)) { ++ struct mtd_part *part; ++ struct mtd_info *master; ++ ++ part = PART(mtd); ++ master = part->master; ++ if (master->refresh_device) ++ ret = master->refresh_device(master); ++ } ++ ++ if (!ret && mtd->refresh_device) ++ ret = mtd->refresh_device(mtd); ++ ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++ if (!ret && IS_PART(mtd) && !strcmp(mtd->name, "rootfs")) ++ refresh_rootfs_split(mtd); ++#endif ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(refresh_mtd_partitions); ++ + static DEFINE_SPINLOCK(part_parser_lock); + static LIST_HEAD(part_parsers); + +--- a/drivers/mtd/devices/block2mtd.c ++++ b/drivers/mtd/devices/block2mtd.c +@@ -29,6 +29,8 @@ struct block2mtd_dev { + struct block_device *blkdev; + struct mtd_info mtd; + struct mutex write_mutex; ++ rwlock_t bdev_mutex; ++ char devname[0]; + }; + + +@@ -81,6 +83,12 @@ static int block2mtd_erase(struct mtd_in + size_t len = instr->len; + int err; + ++ read_lock(&dev->bdev_mutex); ++ if (!dev->blkdev) { ++ err = -EINVAL; ++ goto done; ++ } ++ + instr->state = MTD_ERASING; + mutex_lock(&dev->write_mutex); + err = _block2mtd_erase(dev, from, len); +@@ -93,6 +101,10 @@ static int block2mtd_erase(struct mtd_in + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); ++ ++done: ++ read_unlock(&dev->bdev_mutex); ++ + return err; + } + +@@ -104,10 +116,14 @@ static int block2mtd_read(struct mtd_inf + struct page *page; + int index = from >> PAGE_SHIFT; + int offset = from & (PAGE_SIZE-1); +- int cpylen; ++ int cpylen, err = 0; ++ ++ read_lock(&dev->bdev_mutex); ++ if (!dev->blkdev || (from > mtd->size)) { ++ err = -EINVAL; ++ goto done; ++ } + +- if (from > mtd->size) +- return -EINVAL; + if (from + len > mtd->size) + len = mtd->size - from; + +@@ -122,10 +138,14 @@ static int block2mtd_read(struct mtd_inf + len = len - cpylen; + + page = page_read(dev->blkdev->bd_inode->i_mapping, index); +- if (!page) +- return -ENOMEM; +- if (IS_ERR(page)) +- return PTR_ERR(page); ++ if (!page) { ++ err = -ENOMEM; ++ goto done; ++ } ++ if (IS_ERR(page)) { ++ err = PTR_ERR(page); ++ goto done; ++ } + + memcpy(buf, page_address(page) + offset, cpylen); + page_cache_release(page); +@@ -136,7 +156,10 @@ static int block2mtd_read(struct mtd_inf + offset = 0; + index++; + } +- return 0; ++ ++done: ++ read_unlock(&dev->bdev_mutex); ++ return err; + } + + +@@ -188,12 +211,22 @@ static int block2mtd_write(struct mtd_in + size_t *retlen, const u_char *buf) + { + struct block2mtd_dev *dev = mtd->priv; +- int err; ++ int err = 0; ++ ++ read_lock(&dev->bdev_mutex); ++ if (!dev->blkdev) { ++ err = -EINVAL; ++ goto done; ++ } + + if (!len) +- return 0; +- if (to >= mtd->size) +- return -ENOSPC; ++ goto done; ++ ++ if (to >= mtd->size) { ++ err = -ENOSPC; ++ goto done; ++ } ++ + if (to + len > mtd->size) + len = mtd->size - to; + +@@ -202,6 +235,9 @@ static int block2mtd_write(struct mtd_in + mutex_unlock(&dev->write_mutex); + if (err > 0) + err = 0; ++ ++done: ++ read_unlock(&dev->bdev_mutex); + return err; + } + +@@ -210,52 +246,29 @@ static int block2mtd_write(struct mtd_in + static void block2mtd_sync(struct mtd_info *mtd) + { + struct block2mtd_dev *dev = mtd->priv; +- sync_blockdev(dev->blkdev); +- return; +-} +- +- +-static void block2mtd_free_device(struct block2mtd_dev *dev) +-{ +- if (!dev) +- return; +- +- kfree(dev->mtd.name); + +- if (dev->blkdev) { +- invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, +- 0, -1); +- close_bdev_exclusive(dev->blkdev, FMODE_READ|FMODE_WRITE); +- } ++ read_lock(&dev->bdev_mutex); ++ if (dev->blkdev) ++ sync_blockdev(dev->blkdev); ++ read_unlock(&dev->bdev_mutex); + +- kfree(dev); ++ return; + } + + +-/* FIXME: ensure that mtd->size % erase_size == 0 */ +-static struct block2mtd_dev *add_device(char *devname, int erase_size, const char *mtdname) ++static int _open_bdev(struct block2mtd_dev *dev) + { + struct block_device *bdev; +- struct block2mtd_dev *dev; +- struct mtd_partition *part; +- char *name; +- +- if (!devname) +- return NULL; +- +- dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); +- if (!dev) +- return NULL; + + /* Get a handle on the device */ +- bdev = open_bdev_exclusive(devname, FMODE_READ|FMODE_WRITE, NULL); ++ bdev = open_bdev_exclusive(dev->devname, FMODE_READ|FMODE_WRITE, NULL); + #ifndef MODULE + if (IS_ERR(bdev)) { + + /* We might not have rootfs mounted at this point. Try + to resolve the device name by other means. */ + +- dev_t devt = name_to_dev_t(devname); ++ dev_t devt = name_to_dev_t(dev->devname); + if (devt) { + bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); + } +@@ -263,17 +276,97 @@ static struct block2mtd_dev *add_device( + #endif + + if (IS_ERR(bdev)) { +- ERROR("error: cannot open device %s", devname); +- goto devinit_err; ++ ERROR("error: cannot open device %s", dev->devname); ++ return 1; + } + dev->blkdev = bdev; + + if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { + ERROR("attempting to use an MTD device as a block device"); +- goto devinit_err; ++ return 1; + } + ++ return 0; ++} ++ ++static void _close_bdev(struct block2mtd_dev *dev) ++{ ++ struct block_device *bdev; ++ ++ if (!dev->blkdev) ++ return; ++ ++ bdev = dev->blkdev; ++ invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, 0, -1); ++ close_bdev_exclusive(dev->blkdev, FMODE_READ|FMODE_WRITE); ++ dev->blkdev = NULL; ++} ++ ++static void block2mtd_free_device(struct block2mtd_dev *dev) ++{ ++ if (!dev) ++ return; ++ ++ kfree(dev->mtd.name); ++ _close_bdev(dev); ++ kfree(dev); ++} ++ ++ ++static int block2mtd_refresh(struct mtd_info *mtd) ++{ ++ struct block2mtd_dev *dev = mtd->priv; ++ struct block_device *bdev; ++ dev_t devt; ++ int err = 0; ++ ++ /* no other mtd function can run at this point */ ++ write_lock(&dev->bdev_mutex); ++ ++ /* get the device number for the whole disk */ ++ devt = MKDEV(MAJOR(dev->blkdev->bd_dev), 0); ++ ++ /* close the old block device */ ++ _close_bdev(dev); ++ ++ /* open the whole disk, issue a partition rescan, then */ ++ bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); ++ if (!bdev || !bdev->bd_disk) ++ err = -EINVAL; ++ else { ++ err = rescan_partitions(bdev->bd_disk, bdev); ++ } ++ if (bdev) ++ close_bdev_exclusive(bdev, FMODE_READ|FMODE_WRITE); ++ ++ /* try to open the partition block device again */ ++ _open_bdev(dev); ++ write_unlock(&dev->bdev_mutex); ++ ++ return err; ++} ++ ++/* FIXME: ensure that mtd->size % erase_size == 0 */ ++static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) ++{ ++ struct block2mtd_dev *dev; ++ struct mtd_partition *part; ++ char *name; ++ ++ if (!devname) ++ return NULL; ++ ++ dev = kzalloc(sizeof(struct block2mtd_dev) + strlen(devname) + 1, GFP_KERNEL); ++ if (!dev) ++ return NULL; ++ ++ strcpy(dev->devname, devname); ++ ++ if (_open_bdev(dev)) ++ goto devinit_err; ++ + mutex_init(&dev->write_mutex); ++ rwlock_init(&dev->bdev_mutex); + + if (!mtdname) + mtdname = devname; +@@ -297,6 +390,7 @@ static struct block2mtd_dev *add_device( + dev->mtd.read = block2mtd_read; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; ++ dev->mtd.refresh_device = block2mtd_refresh; + + part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL); + part->name = dev->mtd.name; +--- a/drivers/mtd/mtdchar.c ++++ b/drivers/mtd/mtdchar.c +@@ -16,6 +16,7 @@ + + #include <linux/mtd/mtd.h> + #include <linux/mtd/compatmac.h> ++#include <linux/mtd/partitions.h> + + #include <asm/uaccess.h> + +@@ -769,6 +770,13 @@ static int mtd_ioctl(struct inode *inode + file->f_pos = 0; + break; + } ++#ifdef CONFIG_MTD_PARTITIONS ++ case MTDREFRESH: ++ { ++ ret = refresh_mtd_partitions(mtd); ++ break; ++ } ++#endif + + default: + ret = -ENOTTY; +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -98,6 +98,7 @@ struct mtd_oob_ops { + uint8_t *oobbuf; + }; + ++struct mtd_info; + struct mtd_info { + u_char type; + u_int32_t flags; +@@ -213,6 +214,9 @@ struct mtd_info { + struct module *owner; + int usecount; + ++ int (*refresh_device)(struct mtd_info *mtd); ++ struct mtd_info *split; ++ + /* If the driver is something smart, like UBI, it may need to maintain + * its own reference counting. The below functions are only for driver. + * The driver may register its callbacks. These callbacks are not +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -34,6 +34,7 @@ + * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). + */ + ++struct mtd_partition; + struct mtd_partition { + char *name; /* identifier string */ + u_int32_t size; /* partition size */ +@@ -41,6 +42,7 @@ struct mtd_partition { + u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ + struct mtd_info **mtdp; /* pointer to store the MTD object */ ++ int (*refresh_partition)(struct mtd_info *); + }; + + #define MTDPART_OFS_NXTBLK (-2) +@@ -50,6 +52,7 @@ struct mtd_partition { + + int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); + int del_mtd_partitions(struct mtd_info *); ++int refresh_mtd_partitions(struct mtd_info *); + + /* + * Functions dealing with the various ways of partitioning the space +--- a/include/mtd/mtd-abi.h ++++ b/include/mtd/mtd-abi.h +@@ -93,6 +93,7 @@ struct otp_info { + #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) + #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) + #define MTDFILEMODE _IO('M', 19) ++#define MTDREFRESH _IO('M', 23) + + /* + * Obsolete legacy interface. Keep it in order not to break userspace diff --git a/target/linux/generic-2.6/patches-2.6.28/070-redboot_space.patch b/target/linux/generic-2.6/patches-2.6.28/070-redboot_space.patch new file mode 100644 index 0000000..6ace83c --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/070-redboot_space.patch @@ -0,0 +1,30 @@ +--- a/drivers/mtd/redboot.c ++++ b/drivers/mtd/redboot.c +@@ -249,14 +249,21 @@ static int parse_redboot_partitions(stru + #endif + names += strlen(names)+1; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { +- i++; +- parts[i].offset = parts[i-1].size + parts[i-1].offset; +- parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = nullname; +- } ++ if (!strcmp(parts[i].name, "rootfs")) { ++ parts[i].size = fl->next->img->flash_base; ++ parts[i].size &= ~(master->erasesize - 1); ++ parts[i].size -= parts[i].offset; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ nrparts--; ++ } else { ++ i++; ++ parts[i].offset = parts[i-1].size + parts[i-1].offset; ++ parts[i].size = fl->next->img->flash_base - parts[i].offset; ++ parts[i].name = nullname; + #endif ++ } ++ } + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); diff --git a/target/linux/generic-2.6/patches-2.6.28/071-redboot_boardconfig.patch b/target/linux/generic-2.6/patches-2.6.28/071-redboot_boardconfig.patch new file mode 100644 index 0000000..f9bc4fe --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/071-redboot_boardconfig.patch @@ -0,0 +1,60 @@ +--- a/drivers/mtd/redboot.c ++++ b/drivers/mtd/redboot.c +@@ -11,6 +11,8 @@ + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> + ++#define BOARD_CONFIG_PART "boardconfig" ++ + struct fis_image_desc { + unsigned char name[16]; // Null terminated name + uint32_t flash_base; // Address within FLASH of image +@@ -41,6 +43,7 @@ static int parse_redboot_partitions(stru + struct mtd_partition **pparts, + unsigned long fis_origin) + { ++ unsigned long max_offset = 0; + int nrparts = 0; + struct fis_image_desc *buf; + struct mtd_partition *parts; +@@ -209,14 +212,14 @@ static int parse_redboot_partitions(stru + } + } + #endif +- parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); ++ parts = kzalloc(sizeof(*parts) * (nrparts + 1) + nulllen + namelen + sizeof(BOARD_CONFIG_PART), GFP_KERNEL); + + if (!parts) { + ret = -ENOMEM; + goto out; + } + +- nullname = (char *)&parts[nrparts]; ++ nullname = (char *)&parts[nrparts + 1]; + #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (nulllen > 0) { + strcpy(nullname, nullstring); +@@ -235,6 +238,8 @@ static int parse_redboot_partitions(stru + } + #endif + for ( ; i<nrparts; i++) { ++ if(max_offset < buf[i].flash_base + buf[i].size) ++ max_offset = buf[i].flash_base + buf[i].size; + parts[i].size = fl->img->size; + parts[i].offset = fl->img->flash_base; + parts[i].name = names; +@@ -268,6 +273,14 @@ static int parse_redboot_partitions(stru + fl = fl->next; + kfree(tmp_fl); + } ++ if(master->size - max_offset >= master->erasesize) ++ { ++ parts[nrparts].size = master->size - max_offset; ++ parts[nrparts].offset = max_offset; ++ parts[nrparts].name = names; ++ strcpy(names, BOARD_CONFIG_PART); ++ nrparts++; ++ } + ret = nrparts; + *pparts = parts; + out: diff --git a/target/linux/generic-2.6/patches-2.6.28/080-mtd_plat_nand_chip_fixup.patch b/target/linux/generic-2.6/patches-2.6.28/080-mtd_plat_nand_chip_fixup.patch new file mode 100644 index 0000000..9e4a3c5 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/080-mtd_plat_nand_chip_fixup.patch @@ -0,0 +1,32 @@ +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -579,6 +579,7 @@ struct platform_nand_chip { + int chip_delay; + unsigned int options; + const char **part_probe_types; ++ int (*chip_fixup)(struct mtd_info *mtd); + void *priv; + }; + +--- a/drivers/mtd/nand/plat_nand.c ++++ b/drivers/mtd/nand/plat_nand.c +@@ -71,7 +71,18 @@ static int __init plat_nand_probe(struct + platform_set_drvdata(pdev, data); + + /* Scan to find existance of the device */ +- if (nand_scan(&data->mtd, 1)) { ++ if (nand_scan_ident(&data->mtd, 1)) { ++ res = -ENXIO; ++ goto out; ++ } ++ ++ if (pdata->chip.chip_fixup) { ++ res = pdata->chip.chip_fixup(&data->mtd); ++ if (res) ++ goto out; ++ } ++ ++ if (nand_scan_tail(&data->mtd)) { + res = -ENXIO; + goto out; + } diff --git a/target/linux/generic-2.6/patches-2.6.28/081-mtd_myloader_partition_parser.patch b/target/linux/generic-2.6/patches-2.6.28/081-mtd_myloader_partition_parser.patch new file mode 100644 index 0000000..7a5addd --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/081-mtd_myloader_partition_parser.patch @@ -0,0 +1,35 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -172,6 +172,22 @@ config MTD_AR7_PARTS + ---help--- + TI AR7 partitioning support + ++config MTD_MYLOADER_PARTS ++ tristate "MyLoader partition parsing" ++ depends on MTD_PARTITIONS && (ADM5120 || ATHEROS || ATHEROS_AR71XX) ++ ---help--- ++ MyLoader is a bootloader which allows the user to define partitions ++ in flash devices, by putting a table in the second erase block ++ on the device, similar to a partition table. This table gives the ++ offsets and lengths of the user defined partitions. ++ ++ If you need code which can detect and parse these tables, and ++ register MTD 'partitions' corresponding to each image detected, ++ enable this option. ++ ++ You will still need the parsing functions to be called by the driver ++ for your particular device. It won't happen automatically. ++ + comment "User Modules And Translation Layers" + + config MTD_CHAR +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdli + obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o ++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o diff --git a/target/linux/generic-2.6/patches-2.6.28/091-ds1672_missing_id_table.patch b/target/linux/generic-2.6/patches-2.6.28/091-ds1672_missing_id_table.patch new file mode 100644 index 0000000..78a160c --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/091-ds1672_missing_id_table.patch @@ -0,0 +1,21 @@ +--- a/drivers/rtc/rtc-ds1672.c ++++ b/drivers/rtc/rtc-ds1672.c +@@ -209,12 +209,18 @@ static int ds1672_probe(struct i2c_clien + return err; + } + ++static struct i2c_device_id ds1672_id[] = { ++ { "ds1672", 0 }, ++ { } ++}; ++ + static struct i2c_driver ds1672_driver = { + .driver = { + .name = "rtc-ds1672", + }, + .probe = &ds1672_probe, + .remove = &ds1672_remove, ++ .id_table = ds1672_id, + }; + + static int __init ds1672_init(void) diff --git a/target/linux/generic-2.6/patches-2.6.28/100-netfilter_layer7_2.17.patch b/target/linux/generic-2.6/patches-2.6.28/100-netfilter_layer7_2.17.patch new file mode 100644 index 0000000..5af9d05 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/100-netfilter_layer7_2.17.patch @@ -0,0 +1,2117 @@ +--- /dev/null ++++ b/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 */ +--- a/include/net/netfilter/nf_conntrack.h ++++ b/include/net/netfilter/nf_conntrack.h +@@ -118,6 +118,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; + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -795,6 +795,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_ADVANCED +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -84,6 +84,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) + obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o + obj-$(CONFIG_NETFILTER_XT_MATCH_SOCKET) += xt_socket.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 +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -201,6 +201,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(hlist_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode)); +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -165,6 +165,12 @@ static int ct_seq_show(struct seq_file * + return -ENOSPC; + #endif + ++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++ if(ct->layer7.app_proto && ++ seq_printf(s, "l7proto=%s ", ct->layer7.app_proto)) ++ return -ENOSPC; ++#endif ++ + if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) + return -ENOSPC; + +--- /dev/null ++++ b/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 ++ ++ +--- /dev/null ++++ b/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 +--- /dev/null ++++ b/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 +--- /dev/null ++++ b/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 <linux/string.h> ++ ++ ++#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'; ++} +--- /dev/null ++++ b/net/netfilter/xt_layer7.c +@@ -0,0 +1,651 @@ ++/* ++ 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 <winfield@freegates.be>, ++ xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait, ++ Ethan Sommer, Justin Levandoski. ++*/ ++ ++#include <linux/spinlock.h> ++#include <linux/version.h> ++#include <net/ip.h> ++#include <net/tcp.h> ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter.h> ++#include <net/netfilter/nf_conntrack.h> ++#include <net/netfilter/nf_conntrack_core.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++#include <net/netfilter/nf_conntrack_extend.h> ++#include <net/netfilter/nf_conntrack_acct.h> ++#endif ++#include <linux/netfilter/x_tables.h> ++#include <linux/netfilter/xt_layer7.h> ++#include <linux/ctype.h> ++#include <linux/proc_fs.h> ++ ++#include "regexp/regexp.c" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>"); ++MODULE_DESCRIPTION("iptables application layer match module"); ++MODULE_ALIAS("ipt_layer7"); ++MODULE_VERSION("2.19"); ++ ++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 ++ ++/* 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); ++ ++static int total_acct_packets(struct nf_conn *ct) ++{ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) ++ BUG_ON(ct == NULL); ++ return (ct->counters[IP_CT_DIR_ORIGINAL].packets + ct->counters[IP_CT_DIR_REPLY].packets); ++#else ++ struct nf_conn_counter *acct; ++ ++ BUG_ON(ct == NULL); ++ acct = nf_conn_acct_find(ct); ++ if (!acct) ++ return 0; ++ return (acct[IP_CT_DIR_ORIGINAL].packets + acct[IP_CT_DIR_REPLY].packets); ++#endif ++} ++ ++#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_acct_packets(master_conntrack), 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 bool ++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, ++ bool *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_acct_packets(master_conntrack) > 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_acct_packets(master_conntrack) == 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_acct_packets(master_conntrack), 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 bool 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 0; ++ } ++ return 1; ++} ++ ++static void ++destroy(const struct xt_match *match, void *matchinfo) ++{ ++ nf_ct_l3proto_module_put(match->family); ++} ++ ++static struct xt_match xt_layer7_match[] __read_mostly = { ++{ ++ .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); diff --git a/target/linux/generic-2.6/patches-2.6.28/101-netfilter_layer7_pktmatch.patch b/target/linux/generic-2.6/patches-2.6.28/101-netfilter_layer7_pktmatch.patch new file mode 100644 index 0000000..9e49924 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/101-netfilter_layer7_pktmatch.patch @@ -0,0 +1,108 @@ +--- a/include/linux/netfilter/xt_layer7.h ++++ b/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 */ +--- a/net/netfilter/xt_layer7.c ++++ b/net/netfilter/xt_layer7.c +@@ -314,33 +314,35 @@ 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; ++} ++ ++/* 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; + +- master_conntrack->layer7.app_data[length+oldlength] = '\0'; +- master_conntrack->layer7.app_data_len = length + oldlength; ++ 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; + } +@@ -428,7 +430,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; + +@@ -456,8 +458,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_acct_packets(master_conntrack) > num_packets || +- master_conntrack->layer7.app_proto) { ++ if(!info->pkt && (total_acct_packets(master_conntrack) > num_packets || ++ master_conntrack->layer7.app_proto)) { + + pattern_result = match_no_append(conntrack, master_conntrack, + ctinfo, master_ctinfo, info); +@@ -490,6 +492,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_acct_packets(master_conntrack) == 1 && !skb->cb[0] && + !master_conntrack->layer7.app_data){ diff --git a/target/linux/generic-2.6/patches-2.6.28/110-ipp2p_0.8.1rc1.patch b/target/linux/generic-2.6/patches-2.6.28/110-ipp2p_0.8.1rc1.patch new file mode 100644 index 0000000..cacc5c1 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/110-ipp2p_0.8.1rc1.patch @@ -0,0 +1,919 @@ +--- /dev/null ++++ b/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) +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_ipp2p.c +@@ -0,0 +1,856 @@ ++#if defined(MODVERSIONS) ++#include <linux/modversions.h> ++#endif ++#include <linux/module.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/version.h> ++#include <linux/netfilter_ipv4/ipt_ipp2p.h> ++#include <net/tcp.h> ++#include <net/udp.h> ++ ++#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 <ipp2p@ipp2p.org>"); ++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 bool ++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, ++ bool *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 bool ++checkentry(const char *tablename, ++ const void *ip, ++ const struct xt_match *match, ++ void *matchinfo, ++ 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 = { ++ .name = "ipp2p", ++ .match = &match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_p2p_info), ++ .checkentry = &checkentry, ++ .me = THIS_MODULE, ++}; ++ ++ ++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); ++ ++ +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -64,6 +64,12 @@ config IP_NF_IPTABLES + if IP_NF_IPTABLES + + # The matches. ++config IP_NF_MATCH_IPP2P ++ tristate "IPP2P" ++ depends on NETFILTER_ADVANCED ++ help ++ Module for matching traffic of various Peer-to-Peer applications ++ + config IP_NF_MATCH_ADDRTYPE + tristate '"addrtype" address type match support' + depends on NETFILTER_ADVANCED +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -53,6 +53,8 @@ obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o + ++obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o ++ + # targets + obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o + obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o diff --git a/target/linux/generic-2.6/patches-2.6.28/130-netfilter_ipset.patch b/target/linux/generic-2.6/patches-2.6.28/130-netfilter_ipset.patch new file mode 100644 index 0000000..1409c6d --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/130-netfilter_ipset.patch @@ -0,0 +1,7688 @@ +--- a/include/linux/netfilter_ipv4/Kbuild ++++ b/include/linux/netfilter_ipv4/Kbuild +@@ -45,3 +45,14 @@ header-y += ipt_ttl.h + + unifdef-y += ip_queue.h + unifdef-y += ip_tables.h ++ ++unifdef-y += ip_set.h ++header-y += ip_set_iphash.h ++header-y += ip_set_ipmap.h ++header-y += ip_set_ipporthash.h ++unifdef-y += ip_set_iptree.h ++unifdef-y += ip_set_iptreemap.h ++header-y += ip_set_jhash.h ++header-y += ip_set_macipmap.h ++unifdef-y += ip_set_nethash.h ++header-y += ip_set_portmap.h +--- /dev/null ++++ b/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 <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_iphash.h +@@ -0,0 +1,30 @@ ++#ifndef __IP_SET_IPHASH_H ++#define __IP_SET_IPHASH_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_ipmap.h +@@ -0,0 +1,56 @@ ++#ifndef __IP_SET_IPMAP_H ++#define __IP_SET_IPMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_ipporthash.h +@@ -0,0 +1,34 @@ ++#ifndef __IP_SET_IPPORTHASH_H ++#define __IP_SET_IPPORTHASH_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_iptree.h +@@ -0,0 +1,40 @@ ++#ifndef __IP_SET_IPTREE_H ++#define __IP_SET_IPTREE_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_iptreemap.h +@@ -0,0 +1,40 @@ ++#ifndef __IP_SET_IPTREEMAP_H ++#define __IP_SET_IPTREEMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/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 */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_macipmap.h +@@ -0,0 +1,38 @@ ++#ifndef __IP_SET_MACIPMAP_H ++#define __IP_SET_MACIPMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/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 <linux/kmalloc_sizes.h> ++#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*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_nethash.h +@@ -0,0 +1,55 @@ ++#ifndef __IP_SET_NETHASH_H ++#define __IP_SET_NETHASH_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_portmap.h +@@ -0,0 +1,25 @@ ++#ifndef __IP_SET_PORTMAP_H ++#define __IP_SET_PORTMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#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 */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_set.h +@@ -0,0 +1,21 @@ ++#ifndef _IPT_SET_H ++#define _IPT_SET_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++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*/ +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set.c +@@ -0,0 +1,2003 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/version.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++#include <linux/config.h> ++#endif ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/kmod.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/random.h> ++#include <linux/jhash.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/errno.h> ++#include <linux/semaphore.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++ ++#define ASSERT_READ_LOCK(x) ++#define ASSERT_WRITE_LOCK(x) ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_iphash.c +@@ -0,0 +1,429 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/version.h> ++#include <linux/jhash.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++#include <linux/random.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_iphash.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_ipmap.c +@@ -0,0 +1,336 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/version.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++ ++#include <linux/netfilter_ipv4/ip_set_ipmap.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_ipporthash.c +@@ -0,0 +1,581 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/module.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <linux/udp.h> ++#include <linux/skbuff.h> ++#include <linux/version.h> ++#include <linux/jhash.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++#include <linux/random.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_ipporthash.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_iptree.c +@@ -0,0 +1,612 @@ ++/* Copyright (C) 2005 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/version.h> ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++ ++/* Backward compatibility */ ++#ifndef __nocast ++#define __nocast ++#endif ++ ++#include <linux/netfilter_ipv4/ip_set_iptree.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_iptreemap.c +@@ -0,0 +1,829 @@ ++/* Copyright (C) 2007 Sven Wegener <sven.wegener@stealer.net> ++ * ++ * 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 <linux/version.h> ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++ ++#include <linux/netfilter_ipv4/ip_set_iptreemap.h> ++ ++#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 <sven.wegener@stealer.net>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_macipmap.c +@@ -0,0 +1,375 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/version.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/if_ether.h> ++#include <linux/vmalloc.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_macipmap.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_nethash.c +@@ -0,0 +1,497 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/version.h> ++#include <linux/jhash.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++#include <linux/random.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_nethash.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_portmap.c +@@ -0,0 +1,346 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/module.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <linux/udp.h> ++#include <linux/skbuff.h> ++#include <linux/version.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_portmap.h> ++ ++/* 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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_set.c +@@ -0,0 +1,160 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/version.h> ++ ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/netfilter_ipv4/ipt_set.h> ++ ++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 <kadlec@blackhole.kfki.hu>"); ++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); +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_SET.c +@@ -0,0 +1,179 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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 <linux/types.h> ++#include <linux/ip.h> ++#include <linux/timer.h> ++#include <linux/module.h> ++#include <linux/netfilter.h> ++#include <linux/netdevice.h> ++#include <linux/if.h> ++#include <linux/inetdevice.h> ++#include <linux/version.h> ++#include <net/protocol.h> ++#include <net/checksum.h> ++#include <linux/netfilter_ipv4.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_set.h> ++ ++static unsigned int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++target(struct sk_buff *skb, ++#else ++target(struct sk_buff **pskb, ++#endif ++ 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 LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ++ struct sk_buff *skb = *pskb; ++#endif ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_addip_kernel(info->add_set.index, ++ skb, ++ info->add_set.flags); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_delip_kernel(info->del_set.index, ++ skb, ++ 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 <kadlec@blackhole.kfki.hu>"); ++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); +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -400,5 +400,122 @@ config IP_NF_ARP_MANGLE + + endif # IP_NF_ARPTABLES + ++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 + +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ip + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o ++obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o + + obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o + +@@ -65,6 +66,18 @@ obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += i + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o + obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o + obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.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.28/140-netfilter_time.patch b/target/linux/generic-2.6/patches-2.6.28/140-netfilter_time.patch new file mode 100644 index 0000000..e377ca7 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/140-netfilter_time.patch @@ -0,0 +1,239 @@ +--- /dev/null ++++ b/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__ */ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_time.c +@@ -0,0 +1,180 @@ ++/* ++ 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 <fabrice@netfilter.org> : initial development. ++ 2001-21-05 Fabrice MARIE <fabrice@netfilter.org> : bug fix in the match code, ++ thanks to "Zeng Yu" <zengy@capitel.com.cn> for bug report. ++ 2001-26-09 Fabrice MARIE <fabrice@netfilter.org> : 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 <dongnd@tlnet.com.vn> patch to support timezones. ++ 2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO. ++*/ ++ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_time.h> ++#include <linux/time.h> ++ ++MODULE_AUTHOR("Fabrice MARIE <fabrice@netfilter.org>"); ++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 bool ++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, ++ bool *hotdrop) ++{ ++ const struct ipt_time_info *info = matchinfo; /* match info for rule */ ++ struct timeval tv; ++ 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.tv64 == 0) ++ __net_timestamp((struct sk_buff *)skb); ++ ++ skb_get_timestamp(skb, &tv); ++ /* First we make sure we are in the date start-stop boundaries */ ++ if ((tv.tv_sec < info->date_start) || (tv.tv_sec > info->date_stop)) ++ return 0; /* We are outside the date boundaries */ ++ ++ /* Transform the timestamp of the packet, in a human readable form */ ++ localtime(tv.tv_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 bool ++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_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | (1 << NF_INET_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; ++} +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -70,6 +70,22 @@ config IP_NF_MATCH_IPP2P + help + Module for matching traffic of various Peer-to-Peer applications + ++ ++config IP_NF_MATCH_TIME ++ tristate 'TIME match support' ++ depends on NETFILTER_ADVANCED ++ 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_ADDRTYPE + tristate '"addrtype" address type match support' + depends on NETFILTER_ADVANCED +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o + obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o ++obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o + + obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o + diff --git a/target/linux/generic-2.6/patches-2.6.28/150-netfilter_imq.patch b/target/linux/generic-2.6/patches-2.6.28/150-netfilter_imq.patch new file mode 100644 index 0000000..bce33fb --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/150-netfilter_imq.patch @@ -0,0 +1,914 @@ +--- /dev/null ++++ b/drivers/net/imq.c +@@ -0,0 +1,474 @@ ++/* ++ * 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, <kaber@trash.net> ++ * ++ * The first version was written by Martin Devera, <devik@cdi.cz> ++ * ++ * Credits: Jan Rafaj <imq2t@cedric.vabo.cz> ++ * - Update patch to 2.4.21 ++ * Sebastian Strollo <sstrollo@nortelnetworks.com> ++ * - Fix "Dead-loop on netdevice imq"-issue ++ * Marcel Sebek <sebek64@post.cz> ++ * - 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. ++ * ++ * ++ * 2008/06/17 - 2.6.25 - Changed imq.c to use qdisc_run() instead ++ * of qdisc_restart() and moved qdisc_run() to tasklet to avoid ++ * recursive locking. New initialization routines to fix 'rmmod' not ++ * working anymore. Used code from ifb.c. (Jussi Kivilinna) ++ * ++ * Also, many thanks to pablo Sebastian Greco for making the initial ++ * patch and to those who helped the testing. ++ * ++ * More info at: http://www.linuximq.net/ (Andre Correa) ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/moduleparam.h> ++#include <linux/skbuff.h> ++#include <linux/netdevice.h> ++#include <linux/rtnetlink.h> ++#include <linux/if_arp.h> ++#include <linux/netfilter.h> ++#include <linux/netfilter_ipv4.h> ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ #include <linux/netfilter_ipv6.h> ++#endif ++#include <linux/imq.h> ++#include <net/pkt_sched.h> ++#include <net/netfilter/nf_queue.h> ++ ++struct imq_private { ++ struct tasklet_struct tasklet; ++ unsigned long tasklet_pending; ++}; ++ ++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_INET_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_INET_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_INET_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_INET_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 = IMQ_MAX_DEVS; ++#endif ++ ++static struct net_device *imq_devs_cache[IMQ_MAX_DEVS]; ++ ++static struct net_device_stats *imq_get_stats(struct net_device *dev) ++{ ++ return &dev->stats; ++} ++ ++/* called for packets kfree'd in qdiscs at places other than enqueue */ ++static void imq_skb_destructor(struct sk_buff *skb) ++{ ++ struct nf_queue_entry *entry = skb->nf_queue_entry; ++ ++ if (entry) { ++ if (entry->indev) ++ dev_put(entry->indev); ++ if (entry->outdev) ++ dev_put(entry->outdev); ++ kfree(entry); ++ } ++} ++ ++static int imq_dev_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ dev->stats.tx_bytes += skb->len; ++ dev->stats.tx_packets++; ++ ++ skb->imq_flags = 0; ++ skb->destructor = NULL; ++ ++ dev->trans_start = jiffies; ++ nf_reinject(skb->nf_queue_entry, NF_ACCEPT); ++ return 0; ++} ++ ++static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num) ++{ ++ struct net_device *dev; ++ struct imq_private *priv; ++ struct sk_buff *skb2 = NULL; ++ struct Qdisc *q; ++ unsigned int index = entry->skb->imq_flags & IMQ_F_IFMASK; ++ int ret = -1; ++ ++ if (index > numdevs) ++ return -1; ++ ++ /* check for imq device by index from cache */ ++ dev = imq_devs_cache[index]; ++ if (!dev) { ++ char buf[8]; ++ ++ /* get device by name and cache result */ ++ snprintf(buf, sizeof(buf), "imq%d", index); ++ dev = dev_get_by_name(&init_net, buf); ++ if (!dev) { ++ /* not found ?!*/ ++ BUG(); ++ return -1; ++ } ++ ++ imq_devs_cache[index] = dev; ++ } ++ ++ priv = netdev_priv(dev); ++ if (!(dev->flags & IFF_UP)) { ++ entry->skb->imq_flags = 0; ++ nf_reinject(entry, NF_ACCEPT); ++ return 0; ++ } ++ dev->last_rx = jiffies; ++ ++ if (entry->skb->destructor) { ++ skb2 = entry->skb; ++ entry->skb = skb_clone(entry->skb, GFP_ATOMIC); ++ if (!entry->skb) ++ return -1; ++ } ++ entry->skb->nf_queue_entry = entry; ++ ++ dev->stats.rx_bytes += entry->skb->len; ++ dev->stats.rx_packets++; ++ ++ spin_lock_bh(&dev->queue_lock); ++ q = dev->qdisc; ++ if (q->enqueue) { ++ q->enqueue(skb_get(entry->skb), q); ++ if (skb_shared(entry->skb)) { ++ entry->skb->destructor = imq_skb_destructor; ++ kfree_skb(entry->skb); ++ ret = 0; ++ } ++ } ++ if (!test_and_set_bit(1, &priv->tasklet_pending)) ++ tasklet_schedule(&priv->tasklet); ++ spin_unlock_bh(&dev->queue_lock); ++ ++ if (skb2) ++ kfree_skb(ret ? entry->skb : skb2); ++ ++ return ret; ++} ++ ++static struct nf_queue_handler nfqh = { ++ .name = "imq", ++ .outfn = imq_nf_queue, ++}; ++ ++static void qdisc_run_tasklet(unsigned long arg) ++{ ++ struct net_device *dev = (struct net_device *)arg; ++ struct imq_private *priv = netdev_priv(dev); ++ ++ spin_lock(&dev->queue_lock); ++ qdisc_run(dev); ++ clear_bit(1, &priv->tasklet_pending); ++ spin_unlock(&dev->queue_lock); ++} ++ ++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 imq_close(struct net_device *dev) ++{ ++ struct imq_private *priv = netdev_priv(dev); ++ ++ tasklet_kill(&priv->tasklet); ++ netif_stop_queue(dev); ++ ++ return 0; ++} ++ ++static int imq_open(struct net_device *dev) ++{ ++ struct imq_private *priv = netdev_priv(dev); ++ ++ tasklet_init(&priv->tasklet, qdisc_run_tasklet, (unsigned long)dev); ++ netif_start_queue(dev); ++ ++ return 0; ++} ++ ++static void imq_setup(struct net_device *dev) ++{ ++ dev->hard_start_xmit = imq_dev_xmit; ++ dev->open = imq_open; ++ dev->get_stats = imq_get_stats; ++ dev->stop = imq_close; ++ dev->type = ARPHRD_VOID; ++ dev->mtu = 16000; ++ dev->tx_queue_len = 11000; ++ dev->flags = IFF_NOARP; ++} ++ ++static struct rtnl_link_ops imq_link_ops __read_mostly = { ++ .kind = "imq", ++ .priv_size = sizeof(struct imq_private), ++ .setup = imq_setup, ++}; ++ ++static int __init imq_init_hooks(void) ++{ ++ int err; ++ ++ err = nf_register_queue_handler(PF_INET, &nfqh); ++ if (err) ++ goto err1; ++ ++ err = nf_register_hook(&imq_ingress_ipv4); ++ if (err) ++ goto err2; ++ ++ err = nf_register_hook(&imq_egress_ipv4); ++ if (err) ++ goto err3; ++ ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ err = nf_register_queue_handler(PF_INET6, &nfqh); ++ if (err) ++ goto err4; ++ ++ err = nf_register_hook(&imq_ingress_ipv6); ++ if (err) ++ goto err5; ++ ++ err = nf_register_hook(&imq_egress_ipv6); ++ if (err) ++ 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, &nfqh); ++err4: ++ nf_unregister_hook(&imq_egress_ipv4); ++#endif ++err3: ++ nf_unregister_hook(&imq_ingress_ipv4); ++err2: ++ nf_unregister_queue_handler(PF_INET, &nfqh); ++err1: ++ return err; ++} ++ ++static int __init imq_init_one(int index) ++{ ++ struct net_device *dev; ++ int ret; ++ ++ dev = alloc_netdev(sizeof(struct imq_private), "imq%d", imq_setup); ++ if (!dev) ++ return -ENOMEM; ++ ++ ret = dev_alloc_name(dev, dev->name); ++ if (ret < 0) ++ goto fail; ++ ++ dev->rtnl_link_ops = &imq_link_ops; ++ ret = register_netdevice(dev); ++ if (ret < 0) ++ goto fail; ++ ++ return 0; ++fail: ++ free_netdev(dev); ++ return ret; ++} ++ ++static int __init imq_init_devs(void) ++{ ++ int err, i; ++ ++ if (!numdevs || numdevs > IMQ_MAX_DEVS) { ++ printk(KERN_ERR "IMQ: numdevs has to be betweed 1 and %u\n", ++ IMQ_MAX_DEVS); ++ return -EINVAL; ++ } ++ ++ rtnl_lock(); ++ err = __rtnl_link_register(&imq_link_ops); ++ ++ for (i = 0; i < numdevs && !err; i++) ++ err = imq_init_one(i); ++ ++ if (err) { ++ __rtnl_link_unregister(&imq_link_ops); ++ memset(imq_devs_cache, 0, sizeof(imq_devs_cache)); ++ } ++ rtnl_unlock(); ++ ++ return err; ++} ++ ++static int __init imq_init_module(void) ++{ ++ int err; ++ ++ err = imq_init_devs(); ++ if (err) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_devs(net)\n"); ++ return err; ++ } ++ ++ err = imq_init_hooks(); ++ if (err) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_hooks()\n"); ++ rtnl_link_unregister(&imq_link_ops); ++ memset(imq_devs_cache, 0, sizeof(imq_devs_cache)); ++ 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_unhook(void) ++{ ++#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, &nfqh); ++#endif ++ nf_unregister_hook(&imq_ingress_ipv4); ++ nf_unregister_hook(&imq_egress_ipv4); ++ nf_unregister_queue_handler(PF_INET, &nfqh); ++} ++ ++static void __exit imq_cleanup_devs(void) ++{ ++ rtnl_link_unregister(&imq_link_ops); ++ memset(imq_devs_cache, 0, sizeof(imq_devs_cache)); ++} ++ ++static void __exit imq_exit_module(void) ++{ ++ imq_unhook(); ++ imq_cleanup_devs(); ++ printk(KERN_INFO "IMQ driver unloaded successfully.\n"); ++} ++ ++module_init(imq_init_module); ++module_exit(imq_exit_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"); ++MODULE_ALIAS_RTNL_LINK("imq"); ++ +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -109,6 +109,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_AB ++ 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 16 ++ depends on IMQ ++ default "16" ++ help ++ ++ This settings defines how many IMQ devices will be ++ created. ++ ++ The default value is 16. ++ ++ 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 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -148,6 +148,7 @@ obj-$(CONFIG_SLHC) += slhc.o + obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o + + obj-$(CONFIG_DUMMY) += dummy.o ++obj-$(CONFIG_IMQ) += imq.o + obj-$(CONFIG_IFB) += ifb.o + obj-$(CONFIG_MACVLAN) += macvlan.o + obj-$(CONFIG_DE600) += de600.o +--- /dev/null ++++ b/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 */ +--- /dev/null ++++ b/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 */ +--- /dev/null ++++ b/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 */ +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -308,6 +308,10 @@ struct sk_buff { + struct nf_conntrack *nfct; + struct sk_buff *nfct_reasm; + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ unsigned char imq_flags; ++ struct nf_queue_entry *nf_queue_entry; ++#endif + #ifdef CONFIG_BRIDGE_NETFILTER + struct nf_bridge_info *nf_bridge; + #endif +@@ -1813,6 +1817,10 @@ static inline void __nf_copy(struct sk_b + dst->nfct_reasm = src->nfct_reasm; + nf_conntrack_get_reasm(src->nfct_reasm); + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ dst->imq_flags = src->imq_flags; ++ dst->nf_queue_entry = src->nf_queue_entry; ++#endif + #ifdef CONFIG_BRIDGE_NETFILTER + dst->nf_bridge = src->nf_bridge; + nf_bridge_get(src->nf_bridge); +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -96,6 +96,9 @@ + #include <net/net_namespace.h> + #include <net/sock.h> + #include <linux/rtnetlink.h> ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++#include <linux/imq.h> ++#endif + #include <linux/proc_fs.h> + #include <linux/seq_file.h> + #include <linux/stat.h> +@@ -1655,7 +1658,11 @@ int dev_hard_start_xmit(struct sk_buff * + struct netdev_queue *txq) + { + if (likely(!skb->next)) { +- if (!list_empty(&ptype_all)) ++ if (!list_empty(&ptype_all) ++#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)) { +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_IMQ.c +@@ -0,0 +1,69 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_IMQ.h> ++#include <linux/imq.h> ++ ++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 bool 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"); +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -134,6 +134,17 @@ config IP_NF_FILTER + + 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 && IMQ ++ 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_REJECT + tristate "REJECT target support" + depends on IP_NF_FILTER +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -61,6 +61,7 @@ obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_i + obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o + obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o + obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o ++obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o +--- /dev/null ++++ b/net/ipv6/netfilter/ip6t_IMQ.c +@@ -0,0 +1,69 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv6/ip6_tables.h> ++#include <linux/netfilter_ipv6/ip6t_IMQ.h> ++#include <linux/imq.h> ++ ++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 bool 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"); +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -170,6 +170,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 && IMQ ++ 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 +--- a/net/ipv6/netfilter/Makefile ++++ b/net/ipv6/netfilter/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.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_QUEUE) += ip6_queue.o + obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o + obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o +--- a/net/sched/sch_generic.c ++++ b/net/sched/sch_generic.c +@@ -195,6 +195,7 @@ void __qdisc_run(struct Qdisc *q) + + clear_bit(__QDISC_STATE_RUNNING, &q->state); + } ++EXPORT_SYMBOL(__qdisc_run); + + static void dev_watchdog(unsigned long arg) + { diff --git a/target/linux/generic-2.6/patches-2.6.28/170-netfilter_chaostables_0.8.patch b/target/linux/generic-2.6/patches-2.6.28/170-netfilter_chaostables_0.8.patch new file mode 100644 index 0000000..60d3e27 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/170-netfilter_chaostables_0.8.patch @@ -0,0 +1,857 @@ +--- a/include/linux/netfilter/Kbuild ++++ b/include/linux/netfilter/Kbuild +@@ -3,6 +3,7 @@ header-y += nf_conntrack_tuple_common.h + header-y += nfnetlink_conntrack.h + header-y += nfnetlink_log.h + header-y += nfnetlink_queue.h ++header-y += xt_CHAOS.h + header-y += xt_CLASSIFY.h + header-y += xt_CONNMARK.h + header-y += xt_CONNSECMARK.h +--- /dev/null ++++ b/include/linux/netfilter/oot_conntrack.h +@@ -0,0 +1,5 @@ ++#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) ++# include <linux/netfilter_ipv4/ip_conntrack.h> ++#else /* linux-2.6.20+ */ ++# include <net/netfilter/nf_nat_rule.h> ++#endif +--- /dev/null ++++ b/include/linux/netfilter/oot_trans.h +@@ -0,0 +1,14 @@ ++/* Out of tree workarounds */ ++#include <linux/version.h> ++#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 +--- /dev/null ++++ b/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 */ +--- /dev/null ++++ b/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 */ +--- /dev/null ++++ b/net/netfilter/find_match.c +@@ -0,0 +1,39 @@ ++/* ++ xt_request_find_match ++ by Jan Engelhardt <jengelh [at] gmx de>, 2006 - 2007 ++ ++ Based upon linux-2.6.18.5/net/netfilter/x_tables.c: ++ Copyright (C) 2006-2006 Harald Welte <laforge@netfilter.org> ++ 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 <linux/err.h> ++#include <linux/netfilter_arp.h> ++#include <linux/socket.h> ++#include <linux/netfilter/x_tables.h> ++ ++/* ++ * 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 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -303,6 +303,14 @@ if 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_ADVANCED +@@ -341,6 +349,14 @@ config NETFILTER_XT_TARGET_CONNSECMARK + + To compile it as a module, choose M here. 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" and "TOS" target support' + depends on IP_NF_MANGLE || IP6_NF_MANGLE +@@ -654,6 +670,14 @@ config NETFILTER_XT_MATCH_MARK + + 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 '"multiport" Multiple port match support' + depends on NETFILTER_ADVANCED +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -55,6 +55,8 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TPROXY) + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.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 +@@ -90,6 +92,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) + obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o + obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o + obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) += xt_portscan.o + + # IPVS + obj-$(CONFIG_IP_VS) += ipvs/ +--- /dev/null ++++ b/net/netfilter/xt_CHAOS.c +@@ -0,0 +1,200 @@ ++/* ++ * CHAOS target for netfilter ++ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 ++ * Contact: Jan Engelhardt <jengelh@computergmbh.de> ++ * ++ * 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 <linux/icmp.h> ++#include <linux/in.h> ++#include <linux/ip.h> ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/stat.h> ++#include <linux/netfilter/x_tables.h> ++#include <linux/netfilter/xt_tcpudp.h> ++#include <linux/netfilter_ipv4/ipt_REJECT.h> ++#include <net/ip.h> ++#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 <linux/netfilter/xt_CHAOS.h> ++# 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 *skb, const struct net_device *in, ++ const struct net_device *out, unsigned int hooknum) ++{ ++ const struct iphdr *iph = ip_hdr(skb); ++ const int protoff = 4 * iph->ihl; ++ const int offset = ntohs(iph->frag_off) & IP_OFFSET; ++ const struct xt_target *destiny; ++ bool hotdrop = false, ret; ++ ++ ret = xm_tcp->match(skb, 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(skb, in, out, hooknum, destiny, NULL); ++ return; ++} ++ ++static unsigned int chaos_tg(struct sk_buff *skb, ++ 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(skb); ++ ++ if ((unsigned int)net_random() <= reject_percentage) ++ return xt_reject->target(skb, 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_INET_LOCAL_OUT) ++ xt_chaos_total(info, skb, in, out, hooknum); ++ ++ return NF_DROP; ++} ++ ++static bool 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_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | ++ (1 << NF_INET_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 <jengelh@computergmbh.de>"); ++MODULE_DESCRIPTION("netfilter \"CHAOS\" target"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_CHAOS"); +--- /dev/null ++++ b/net/netfilter/xt_DELUDE.c +@@ -0,0 +1,197 @@ ++/* ++ * DELUDE target ++ * Copyright © CC Computer Consultants GmbH, 2007 ++ * Contact: Jan Engelhardt <jengelh@computergmbh.de> ++ * ++ * 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 <coreteam@netfilter.org> ++ * ++ * 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 <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <linux/netfilter/x_tables.h> ++#ifdef CONFIG_BRIDGE_NETFILTER ++# include <linux/netfilter_bridge.h> ++#endif ++#include <net/tcp.h> ++#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_INET_FORWARD || (nskb->nf_bridge != NULL && ++ nskb->nf_bridge->mask & BRNF_BRIDGED)) ++#else ++ if (hook != NF_INET_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_INET_LOCAL_OUT, nskb, NULL, nskb->dst->dev, ++ dst_output); ++ return; ++ ++ free_nskb: ++ kfree_skb(nskb); ++} ++ ++static unsigned int delude_tg(struct sk_buff *skb, ++ 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(skb, hooknum); ++ return NF_DROP; ++} ++ ++static struct xt_target delude_tg_reg = { ++ .name = "DELUDE", ++ .family = AF_INET, ++ .table = "filter", ++ .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_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 <jengelh@computergmbh.de>"); ++MODULE_DESCRIPTION("netfilter \"DELUDE\" target"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_DELUDE"); +--- /dev/null ++++ b/net/netfilter/xt_portscan.c +@@ -0,0 +1,269 @@ ++/* ++ * portscan match for netfilter ++ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 ++ * Contact: Jan Engelhardt <jengelh@computergmbh.de> ++ * ++ * 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 <linux/in.h> ++#include <linux/ip.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/skbuff.h> ++#include <linux/stat.h> ++#include <linux/tcp.h> ++#include <linux/types.h> ++#include <linux/version.h> ++#include <linux/netfilter/x_tables.h> ++#include <linux/netfilter/xt_tcpudp.h> ++#include <net/netfilter/nf_nat_rule.h> ++#if defined(_LOCAL) ++# include "xt_portscan.h" ++#elif defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) || \ ++ defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN_MODULE) ++# include <linux/netfilter/xt_portscan.h> ++#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 bool 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, bool *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->flags & IFF_LOOPBACK) == IFF_LOOPBACK, 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 bool 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 <jengelh@computergmbh.de>"); ++MODULE_DESCRIPTION("netfilter \"portscan\" match"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_portscan"); +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -1574,6 +1574,8 @@ __u32 secure_tcp_sequence_number(__be32 + return seq; + } + ++EXPORT_SYMBOL(secure_tcp_sequence_number); ++ + /* Generate secure starting point for ephemeral IPV4 transport port search */ + u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) + { diff --git a/target/linux/generic-2.6/patches-2.6.28/171-netfilter_tarpit.patch b/target/linux/generic-2.6/patches-2.6.28/171-netfilter_tarpit.patch new file mode 100644 index 0000000..803bad5 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/171-netfilter_tarpit.patch @@ -0,0 +1,318 @@ +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -469,6 +469,23 @@ config NETFILTER_XT_TARGET_SECMARK + + 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 ++ <http://www.hackbusters.net/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 (IPV6 || IPV6=n) +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -51,6 +51,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE + obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_RATEEST) += xt_RATEEST.o + obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_TARPIT) += xt_TARPIT.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TPROXY) += xt_TPROXY.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o +--- /dev/null ++++ b/net/netfilter/xt_TARPIT.c +@@ -0,0 +1,279 @@ ++/* ++ * 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 <http://www.hackbusters.net/LaBrea/>. ++ * ++ * Copyright (c) 2002 Aaron Hopkins <tools@die.net> ++ * ++ * 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 <linux/version.h> ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/ip.h> ++#include <net/ip.h> ++#include <net/tcp.h> ++#include <net/icmp.h> ++struct in_device; ++#include <net/route.h> ++#include <linux/random.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++ ++#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(&init_net, &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 *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++{ ++ 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_INET_LOCAL_IN); ++ return NF_DROP; ++} ++ ++static bool 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_INET_PRE_ROUTING) ++ return true; ++ if (strcmp(tablename, "filter") != 0) ++ return false; ++ invalid = hook_mask & ~((1 << NF_INET_LOCAL_IN) | (1 << NF_INET_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 <jengelh@gmx.de>"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_TARPIT"); diff --git a/target/linux/generic-2.6/patches-2.6.28/180-netfilter_depends.patch b/target/linux/generic-2.6/patches-2.6.28/180-netfilter_depends.patch new file mode 100644 index 0000000..44f9fcb --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/180-netfilter_depends.patch @@ -0,0 +1,18 @@ +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -160,7 +160,6 @@ config NF_CONNTRACK_FTP + + config NF_CONNTRACK_H323 + tristate "H.323 protocol support" +- depends on (IPV6 || IPV6=n) + depends on NETFILTER_ADVANCED + help + H.323 is a VoIP signalling protocol from ITU-T. As one of the most +@@ -488,7 +487,6 @@ config NETFILTER_XT_TARGET_TARPIT + + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' +- depends on (IPV6 || IPV6=n) + default m if NETFILTER_ADVANCED=n + ---help--- + This option adds a `TCPMSS' target, which allows you to alter the diff --git a/target/linux/generic-2.6/patches-2.6.28/190-netfilter_rtsp.patch b/target/linux/generic-2.6/patches-2.6.28/190-netfilter_rtsp.patch new file mode 100644 index 0000000..43314f9 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/190-netfilter_rtsp.patch @@ -0,0 +1,1366 @@ +--- /dev/null ++++ b/include/linux/netfilter/nf_conntrack_rtsp.h +@@ -0,0 +1,63 @@ ++/* ++ * RTSP extension for IP connection tracking. ++ * (C) 2003 by Tom Marshall <tmarshall at real.com> ++ * based on ip_conntrack_irc.h ++ * ++ * 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. ++ */ ++#ifndef _IP_CONNTRACK_RTSP_H ++#define _IP_CONNTRACK_RTSP_H ++ ++//#define IP_NF_RTSP_DEBUG 1 ++#define IP_NF_RTSP_VERSION "0.6.21" ++ ++#ifdef __KERNEL__ ++/* port block types */ ++typedef enum { ++ pb_single, /* client_port=x */ ++ pb_range, /* client_port=x-y */ ++ pb_discon /* client_port=x/y (rtspbis) */ ++} portblock_t; ++ ++/* We record seq number and length of rtsp headers here, all in host order. */ ++ ++/* ++ * This structure is per expected connection. It is a member of struct ++ * ip_conntrack_expect. The TCP SEQ for the conntrack expect is stored ++ * there and we are expected to only store the length of the data which ++ * needs replaced. If a packet contains multiple RTSP messages, we create ++ * one expected connection per message. ++ * ++ * We use these variables to mark the entire header block. This may seem ++ * like overkill, but the nature of RTSP requires it. A header may appear ++ * multiple times in a message. We must treat two Transport headers the ++ * same as one Transport header with two entries. ++ */ ++struct ip_ct_rtsp_expect ++{ ++ u_int32_t len; /* length of header block */ ++ portblock_t pbtype; /* Type of port block that was requested */ ++ u_int16_t loport; /* Port that was requested, low or first */ ++ u_int16_t hiport; /* Port that was requested, high or second */ ++#if 0 ++ uint method; /* RTSP method */ ++ uint cseq; /* CSeq from request */ ++#endif ++}; ++ ++extern unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb, ++ enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen, ++ struct ip_ct_rtsp_expect *prtspexp, ++ struct nf_conntrack_expect *exp); ++ ++extern void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp); ++ ++#define RTSP_PORT 554 ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _IP_CONNTRACK_RTSP_H */ +--- /dev/null ++++ b/include/linux/netfilter_helpers.h +@@ -0,0 +1,133 @@ ++/* ++ * Helpers for netfiler modules. This file provides implementations for basic ++ * functions such as strncasecmp(), etc. ++ * ++ * gcc will warn for defined but unused functions, so we only include the ++ * functions requested. The following macros are used: ++ * NF_NEED_STRNCASECMP nf_strncasecmp() ++ * NF_NEED_STRTOU16 nf_strtou16() ++ * NF_NEED_STRTOU32 nf_strtou32() ++ */ ++#ifndef _NETFILTER_HELPERS_H ++#define _NETFILTER_HELPERS_H ++ ++/* Only include these functions for kernel code. */ ++#ifdef __KERNEL__ ++ ++#include <linux/ctype.h> ++#define iseol(c) ( (c) == '\r' || (c) == '\n' ) ++ ++/* ++ * The standard strncasecmp() ++ */ ++#ifdef NF_NEED_STRNCASECMP ++static int ++nf_strncasecmp(const char* s1, const char* s2, u_int32_t len) ++{ ++ if (s1 == NULL || s2 == NULL) ++ { ++ if (s1 == NULL && s2 == NULL) ++ { ++ return 0; ++ } ++ return (s1 == NULL) ? -1 : 1; ++ } ++ while (len > 0 && tolower(*s1) == tolower(*s2)) ++ { ++ len--; ++ s1++; ++ s2++; ++ } ++ return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) ); ++} ++#endif /* NF_NEED_STRNCASECMP */ ++ ++/* ++ * Parse a string containing a 16-bit unsigned integer. ++ * Returns the number of chars used, or zero if no number is found. ++ */ ++#ifdef NF_NEED_STRTOU16 ++static int ++nf_strtou16(const char* pbuf, u_int16_t* pval) ++{ ++ int n = 0; ++ ++ *pval = 0; ++ while (isdigit(pbuf[n])) ++ { ++ *pval = (*pval * 10) + (pbuf[n] - '0'); ++ n++; ++ } ++ ++ return n; ++} ++#endif /* NF_NEED_STRTOU16 */ ++ ++/* ++ * Parse a string containing a 32-bit unsigned integer. ++ * Returns the number of chars used, or zero if no number is found. ++ */ ++#ifdef NF_NEED_STRTOU32 ++static int ++nf_strtou32(const char* pbuf, u_int32_t* pval) ++{ ++ int n = 0; ++ ++ *pval = 0; ++ while (pbuf[n] >= '0' && pbuf[n] <= '9') ++ { ++ *pval = (*pval * 10) + (pbuf[n] - '0'); ++ n++; ++ } ++ ++ return n; ++} ++#endif /* NF_NEED_STRTOU32 */ ++ ++/* ++ * Given a buffer and length, advance to the next line and mark the current ++ * line. ++ */ ++#ifdef NF_NEED_NEXTLINE ++static int ++nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) ++{ ++ uint off = *poff; ++ uint physlen = 0; ++ ++ if (off >= len) ++ { ++ return 0; ++ } ++ ++ while (p[off] != '\n') ++ { ++ if (len-off <= 1) ++ { ++ return 0; ++ } ++ ++ physlen++; ++ off++; ++ } ++ ++ /* if we saw a crlf, physlen needs adjusted */ ++ if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') ++ { ++ physlen--; ++ } ++ ++ /* advance past the newline */ ++ off++; ++ ++ *plineoff = *poff; ++ *plinelen = physlen; ++ *poff = off; ++ ++ return 1; ++} ++#endif /* NF_NEED_NEXTLINE */ ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _NETFILTER_HELPERS_H */ +--- /dev/null ++++ b/include/linux/netfilter_mime.h +@@ -0,0 +1,89 @@ ++/* ++ * MIME functions for netfilter modules. This file provides implementations ++ * for basic MIME parsing. MIME headers are used in many protocols, such as ++ * HTTP, RTSP, SIP, etc. ++ * ++ * gcc will warn for defined but unused functions, so we only include the ++ * functions requested. The following macros are used: ++ * NF_NEED_MIME_NEXTLINE nf_mime_nextline() ++ */ ++#ifndef _NETFILTER_MIME_H ++#define _NETFILTER_MIME_H ++ ++/* Only include these functions for kernel code. */ ++#ifdef __KERNEL__ ++ ++#include <linux/ctype.h> ++ ++/* ++ * Given a buffer and length, advance to the next line and mark the current ++ * line. If the current line is empty, *plinelen will be set to zero. If ++ * not, it will be set to the actual line length (including CRLF). ++ * ++ * 'line' in this context means logical line (includes LWS continuations). ++ * Returns 1 on success, 0 on failure. ++ */ ++#ifdef NF_NEED_MIME_NEXTLINE ++static int ++nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) ++{ ++ uint off = *poff; ++ uint physlen = 0; ++ int is_first_line = 1; ++ ++ if (off >= len) ++ { ++ return 0; ++ } ++ ++ do ++ { ++ while (p[off] != '\n') ++ { ++ if (len-off <= 1) ++ { ++ return 0; ++ } ++ ++ physlen++; ++ off++; ++ } ++ ++ /* if we saw a crlf, physlen needs adjusted */ ++ if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') ++ { ++ physlen--; ++ } ++ ++ /* advance past the newline */ ++ off++; ++ ++ /* check for an empty line */ ++ if (physlen == 0) ++ { ++ break; ++ } ++ ++ /* check for colon on the first physical line */ ++ if (is_first_line) ++ { ++ is_first_line = 0; ++ if (memchr(p+(*poff), ':', physlen) == NULL) ++ { ++ return 0; ++ } ++ } ++ } ++ while (p[off] == ' ' || p[off] == '\t'); ++ ++ *plineoff = *poff; ++ *plinelen = (physlen == 0) ? 0 : (off - *poff); ++ *poff = off; ++ ++ return 1; ++} ++#endif /* NF_NEED_MIME_NEXTLINE */ ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _NETFILTER_MIME_H */ +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -26,6 +26,7 @@ obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_am + obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o + obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o + obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o ++obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o + obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o + obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o + obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -267,6 +267,16 @@ config NF_CONNTRACK_TFTP + + To compile it as a module, choose M here. If unsure, say N. + ++config NF_CONNTRACK_RTSP ++ tristate "RTSP protocol support" ++ depends on NF_CONNTRACK ++ help ++ Support the RTSP protocol. This allows UDP transports to be setup ++ properly, including RTP and RDT. ++ ++ If you want to compile it as a module, say 'M' here and read ++ Documentation/modules.txt. If unsure, say 'Y'. ++ + config NF_CT_NETLINK + tristate 'Connection tracking netlink interface' + select NETFILTER_NETLINK +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_co + obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o + obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o + obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o ++obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o + + # transparent proxy support + obj-$(CONFIG_NETFILTER_TPROXY) += nf_tproxy_core.o +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -290,6 +290,11 @@ config NF_NAT_IRC + depends on NF_CONNTRACK && NF_NAT + default NF_NAT && NF_CONNTRACK_IRC + ++config NF_NAT_RTSP ++ tristate ++ depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT ++ default NF_NAT && NF_CONNTRACK_RTSP ++ + config NF_NAT_TFTP + tristate + depends on NF_CONNTRACK && NF_NAT +--- /dev/null ++++ b/net/netfilter/nf_conntrack_rtsp.c +@@ -0,0 +1,517 @@ ++/* ++ * RTSP extension for IP connection tracking ++ * (C) 2003 by Tom Marshall <tmarshall at real.com> ++ * based on ip_conntrack_irc.c ++ * ++ * 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. ++ * ++ * Module load syntax: ++ * insmod nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS> ++ * max_outstanding=n setup_timeout=secs ++ * ++ * If no ports are specified, the default will be port 554. ++ * ++ * With max_outstanding you can define the maximum number of not yet ++ * answered SETUP requests per RTSP session (default 8). ++ * With setup_timeout you can specify how long the system waits for ++ * an expected data channel (default 300 seconds). ++ * ++ * 2005-02-13: Harald Welte <laforge at netfilter.org> ++ * - port to 2.6 ++ * - update to recent post-2.6.11 api changes ++ * 2006-09-14: Steven Van Acker <deepstar at singularity.be> ++ * - removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack ++ * 2007-04-18: Michael Guntsche <mike at it-loops.com> ++ * - Port to new NF API ++ */ ++ ++#include <linux/module.h> ++#include <linux/netfilter.h> ++#include <linux/ip.h> ++#include <linux/inet.h> ++#include <net/tcp.h> ++ ++#include <net/netfilter/nf_conntrack.h> ++#include <net/netfilter/nf_conntrack_expect.h> ++#include <net/netfilter/nf_conntrack_helper.h> ++#include <linux/netfilter/nf_conntrack_rtsp.h> ++ ++#define NF_NEED_STRNCASECMP ++#define NF_NEED_STRTOU16 ++#define NF_NEED_STRTOU32 ++#define NF_NEED_NEXTLINE ++#include <linux/netfilter_helpers.h> ++#define NF_NEED_MIME_NEXTLINE ++#include <linux/netfilter_mime.h> ++ ++#include <linux/ctype.h> ++#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */ ++#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#if 0 ++#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#else ++#define DEBUGP(fmt, args...) ++#endif ++ ++#define MAX_PORTS 8 ++static int ports[MAX_PORTS]; ++static int num_ports = 0; ++static int max_outstanding = 8; ++static unsigned int setup_timeout = 300; ++ ++MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>"); ++MODULE_DESCRIPTION("RTSP connection tracking module"); ++MODULE_LICENSE("GPL"); ++module_param_array(ports, int, &num_ports, 0400); ++MODULE_PARM_DESC(ports, "port numbers of RTSP servers"); ++module_param(max_outstanding, int, 0400); ++MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session"); ++module_param(setup_timeout, int, 0400); ++MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels"); ++ ++static char *rtsp_buffer; ++static DEFINE_SPINLOCK(rtsp_buffer_lock); ++ ++unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb, ++ enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen,struct ip_ct_rtsp_expect* prtspexp, ++ struct nf_conntrack_expect *exp); ++void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp); ++ ++EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook); ++ ++/* ++ * Max mappings we will allow for one RTSP connection (for RTP, the number ++ * of allocated ports is twice this value). Note that SMIL burns a lot of ++ * ports so keep this reasonably high. If this is too low, you will see a ++ * lot of "no free client map entries" messages. ++ */ ++#define MAX_PORT_MAPS 16 ++ ++/*** default port list was here in the masq code: 554, 3030, 4040 ***/ ++ ++#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } ++ ++/* ++ * Parse an RTSP packet. ++ * ++ * Returns zero if parsing failed. ++ * ++ * Parameters: ++ * IN ptcp tcp data pointer ++ * IN tcplen tcp data len ++ * IN/OUT ptcpoff points to current tcp offset ++ * OUT phdrsoff set to offset of rtsp headers ++ * OUT phdrslen set to length of rtsp headers ++ * OUT pcseqoff set to offset of CSeq header ++ * OUT pcseqlen set to length of CSeq header ++ */ ++static int ++rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff, ++ uint* phdrsoff, uint* phdrslen, ++ uint* pcseqoff, uint* pcseqlen, ++ uint* transoff, uint* translen) ++{ ++ uint entitylen = 0; ++ uint lineoff; ++ uint linelen; ++ ++ if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) ++ return 0; ++ ++ *phdrsoff = *ptcpoff; ++ while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) { ++ if (linelen == 0) { ++ if (entitylen > 0) ++ *ptcpoff += min(entitylen, tcplen - *ptcpoff); ++ break; ++ } ++ if (lineoff+linelen > tcplen) { ++ INFOP("!! overrun !!\n"); ++ break; ++ } ++ ++ if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) { ++ *pcseqoff = lineoff; ++ *pcseqlen = linelen; ++ } ++ ++ if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) { ++ *transoff = lineoff; ++ *translen = linelen; ++ } ++ ++ if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) { ++ uint off = lineoff+15; ++ SKIP_WSPACE(ptcp+lineoff, linelen, off); ++ nf_strtou32(ptcp+off, &entitylen); ++ } ++ } ++ *phdrslen = (*ptcpoff) - (*phdrsoff); ++ ++ return 1; ++} ++ ++/* ++ * Find lo/hi client ports (if any) in transport header ++ * In: ++ * ptcp, tcplen = packet ++ * tranoff, tranlen = buffer to search ++ * ++ * Out: ++ * pport_lo, pport_hi = lo/hi ports (host endian) ++ * ++ * Returns nonzero if any client ports found ++ * ++ * Note: it is valid (and expected) for the client to request multiple ++ * transports, so we need to parse the entire line. ++ */ ++static int ++rtsp_parse_transport(char* ptran, uint tranlen, ++ struct ip_ct_rtsp_expect* prtspexp) ++{ ++ int rc = 0; ++ uint off = 0; ++ ++ if (tranlen < 10 || !iseol(ptran[tranlen-1]) || ++ nf_strncasecmp(ptran, "Transport:", 10) != 0) { ++ INFOP("sanity check failed\n"); ++ return 0; ++ } ++ ++ DEBUGP("tran='%.*s'\n", (int)tranlen, ptran); ++ off += 10; ++ SKIP_WSPACE(ptran, tranlen, off); ++ ++ /* Transport: tran;field;field=val,tran;field;field=val,... */ ++ while (off < tranlen) { ++ const char* pparamend; ++ uint nextparamoff; ++ ++ pparamend = memchr(ptran+off, ',', tranlen-off); ++ pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; ++ nextparamoff = pparamend-ptran; ++ ++ while (off < nextparamoff) { ++ const char* pfieldend; ++ uint nextfieldoff; ++ ++ pfieldend = memchr(ptran+off, ';', nextparamoff-off); ++ nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; ++ ++ if (strncmp(ptran+off, "client_port=", 12) == 0) { ++ u_int16_t port; ++ uint numlen; ++ ++ off += 12; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ if (prtspexp->loport != 0 && prtspexp->loport != port) ++ DEBUGP("multiple ports found, port %hu ignored\n", port); ++ else { ++ DEBUGP("lo port found : %hu\n", port); ++ prtspexp->loport = prtspexp->hiport = port; ++ if (ptran[off] == '-') { ++ off++; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ prtspexp->pbtype = pb_range; ++ prtspexp->hiport = port; ++ ++ // If we have a range, assume rtp: ++ // loport must be even, hiport must be loport+1 ++ if ((prtspexp->loport & 0x0001) != 0 || ++ prtspexp->hiport != prtspexp->loport+1) { ++ DEBUGP("incorrect range: %hu-%hu, correcting\n", ++ prtspexp->loport, prtspexp->hiport); ++ prtspexp->loport &= 0xfffe; ++ prtspexp->hiport = prtspexp->loport+1; ++ } ++ } else if (ptran[off] == '/') { ++ off++; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ prtspexp->pbtype = pb_discon; ++ prtspexp->hiport = port; ++ } ++ rc = 1; ++ } ++ } ++ ++ /* ++ * Note we don't look for the destination parameter here. ++ * If we are using NAT, the NAT module will handle it. If not, ++ * and the client is sending packets elsewhere, the expectation ++ * will quietly time out. ++ */ ++ ++ off = nextfieldoff; ++ } ++ ++ off = nextparamoff; ++ } ++ ++ return rc; ++} ++ ++void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp) ++{ ++ if(nf_nat_rtsp_hook_expectfn) { ++ nf_nat_rtsp_hook_expectfn(ct,exp); ++ } ++} ++ ++/*** conntrack functions ***/ ++ ++/* outbound packet: client->server */ ++ ++static inline int ++help_out(struct sk_buff *skb, unsigned char *rb_ptr, unsigned int datalen, ++ struct nf_conn *ct, enum ip_conntrack_info ctinfo) ++{ ++ struct ip_ct_rtsp_expect expinfo; ++ ++ int dir = CTINFO2DIR(ctinfo); /* = IP_CT_DIR_ORIGINAL */ ++ //struct tcphdr* tcph = (void*)iph + iph->ihl * 4; ++ //uint tcplen = pktlen - iph->ihl * 4; ++ char* pdata = rb_ptr; ++ //uint datalen = tcplen - tcph->doff * 4; ++ uint dataoff = 0; ++ int ret = NF_ACCEPT; ++ ++ struct nf_conntrack_expect *exp; ++ ++ __be16 be_loport; ++ ++ memset(&expinfo, 0, sizeof(expinfo)); ++ ++ while (dataoff < datalen) { ++ uint cmdoff = dataoff; ++ uint hdrsoff = 0; ++ uint hdrslen = 0; ++ uint cseqoff = 0; ++ uint cseqlen = 0; ++ uint transoff = 0; ++ uint translen = 0; ++ uint off; ++ ++ if (!rtsp_parse_message(pdata, datalen, &dataoff, ++ &hdrsoff, &hdrslen, ++ &cseqoff, &cseqlen, ++ &transoff, &translen)) ++ break; /* not a valid message */ ++ ++ if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0) ++ continue; /* not a SETUP message */ ++ DEBUGP("found a setup message\n"); ++ ++ off = 0; ++ if(translen) { ++ rtsp_parse_transport(pdata+transoff, translen, &expinfo); ++ } ++ ++ if (expinfo.loport == 0) { ++ DEBUGP("no udp transports found\n"); ++ continue; /* no udp transports found */ ++ } ++ ++ DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n", ++ (int)expinfo.pbtype, expinfo.loport, expinfo.hiport); ++ ++ exp = nf_ct_expect_alloc(ct); ++ if (!exp) { ++ ret = NF_DROP; ++ goto out; ++ } ++ ++ be_loport = htons(expinfo.loport); ++ ++ nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, ++ ct->tuplehash[!dir].tuple.src.l3num, ++ &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, ++ IPPROTO_UDP, NULL, &be_loport); ++ ++ exp->master = ct; ++ ++ exp->expectfn = expected; ++ exp->flags = 0; ++ ++ if (expinfo.pbtype == pb_range) { ++ DEBUGP("Changing expectation mask to handle multiple ports\n"); ++ exp->mask.src.u.udp.port = 0xfffe; ++ } ++ ++ DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", ++ NIPQUAD(exp->tuple.src.u3.ip), ++ ntohs(exp->tuple.src.u.udp.port), ++ NIPQUAD(exp->tuple.dst.u3.ip), ++ ntohs(exp->tuple.dst.u.udp.port)); ++ ++ if (nf_nat_rtsp_hook) ++ /* pass the request off to the nat helper */ ++ ret = nf_nat_rtsp_hook(skb, ctinfo, hdrsoff, hdrslen, &expinfo, exp); ++ else if (nf_ct_expect_related(exp) != 0) { ++ INFOP("nf_ct_expect_related failed\n"); ++ ret = NF_DROP; ++ } ++ nf_ct_expect_put(exp); ++ goto out; ++ } ++out: ++ ++ return ret; ++} ++ ++ ++static inline int ++help_in(struct sk_buff *skb, size_t pktlen, ++ struct nf_conn* ct, enum ip_conntrack_info ctinfo) ++{ ++ return NF_ACCEPT; ++} ++ ++static int help(struct sk_buff *skb, unsigned int protoff, ++ struct nf_conn *ct, enum ip_conntrack_info ctinfo) ++{ ++ struct tcphdr _tcph, *th; ++ unsigned int dataoff, datalen; ++ char *rb_ptr; ++ int ret = NF_DROP; ++ ++ /* Until there's been traffic both ways, don't look in packets. */ ++ if (ctinfo != IP_CT_ESTABLISHED && ++ ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { ++ DEBUGP("conntrackinfo = %u\n", ctinfo); ++ return NF_ACCEPT; ++ } ++ ++ /* Not whole TCP header? */ ++ th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); ++ ++ if (!th) ++ return NF_ACCEPT; ++ ++ /* No data ? */ ++ dataoff = protoff + th->doff*4; ++ datalen = skb->len - dataoff; ++ if (dataoff >= skb->len) ++ return NF_ACCEPT; ++ ++ spin_lock_bh(&rtsp_buffer_lock); ++ rb_ptr = skb_header_pointer(skb, dataoff, ++ skb->len - dataoff, rtsp_buffer); ++ BUG_ON(rb_ptr == NULL); ++ ++#if 0 ++ /* Checksum invalid? Ignore. */ ++ /* FIXME: Source route IP option packets --RR */ ++ if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, ++ csum_partial((char*)tcph, tcplen, 0))) ++ { ++ DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", ++ tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ return NF_ACCEPT; ++ } ++#endif ++ ++ switch (CTINFO2DIR(ctinfo)) { ++ case IP_CT_DIR_ORIGINAL: ++ ret = help_out(skb, rb_ptr, datalen, ct, ctinfo); ++ break; ++ case IP_CT_DIR_REPLY: ++ DEBUGP("IP_CT_DIR_REPLY\n"); ++ /* inbound packet: server->client */ ++ ret = NF_ACCEPT; ++ break; ++ } ++ ++ spin_unlock_bh(&rtsp_buffer_lock); ++ ++ return ret; ++} ++ ++static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS]; ++static char rtsp_names[MAX_PORTS][10]; ++static struct nf_conntrack_expect_policy rtsp_expect_policy; ++ ++/* This function is intentionally _NOT_ defined as __exit */ ++static void ++fini(void) ++{ ++ int i; ++ for (i = 0; i < num_ports; i++) { ++ DEBUGP("unregistering port %d\n", ports[i]); ++ nf_conntrack_helper_unregister(&rtsp_helpers[i]); ++ } ++ kfree(rtsp_buffer); ++} ++ ++static int __init ++init(void) ++{ ++ int i, ret; ++ struct nf_conntrack_helper *hlpr; ++ char *tmpname; ++ ++ printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n"); ++ ++ if (max_outstanding < 1) { ++ printk("nf_conntrack_rtsp: max_outstanding must be a positive integer\n"); ++ return -EBUSY; ++ } ++ if (setup_timeout < 0) { ++ printk("nf_conntrack_rtsp: setup_timeout must be a positive integer\n"); ++ return -EBUSY; ++ } ++ ++ rtsp_expect_policy.max_expected = max_outstanding; ++ rtsp_expect_policy.timeout = setup_timeout; ++ ++ rtsp_buffer = kmalloc(65536, GFP_KERNEL); ++ if (!rtsp_buffer) ++ return -ENOMEM; ++ ++ /* If no port given, default to standard rtsp port */ ++ if (ports[0] == 0) { ++ ports[0] = RTSP_PORT; ++ } ++ ++ for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { ++ hlpr = &rtsp_helpers[i]; ++ memset(hlpr, 0, sizeof(struct nf_conntrack_helper)); ++ hlpr->tuple.src.u.tcp.port = htons(ports[i]); ++ hlpr->tuple.dst.protonum = IPPROTO_TCP; ++ hlpr->expect_policy = &rtsp_expect_policy; ++ hlpr->me = THIS_MODULE; ++ hlpr->help = help; ++ ++ tmpname = &rtsp_names[i][0]; ++ if (ports[i] == RTSP_PORT) { ++ sprintf(tmpname, "rtsp"); ++ } else { ++ sprintf(tmpname, "rtsp-%d", i); ++ } ++ hlpr->name = tmpname; ++ ++ DEBUGP("port #%d: %d\n", i, ports[i]); ++ ++ ret = nf_conntrack_helper_register(hlpr); ++ ++ if (ret) { ++ printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]); ++ fini(); ++ return -EBUSY; ++ } ++ num_ports++; ++ } ++ return 0; ++} ++ ++module_init(init); ++module_exit(fini); ++ ++EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn); ++ +--- /dev/null ++++ b/net/ipv4/netfilter/nf_nat_rtsp.c +@@ -0,0 +1,496 @@ ++/* ++ * RTSP extension for TCP NAT alteration ++ * (C) 2003 by Tom Marshall <tmarshall at real.com> ++ * based on ip_nat_irc.c ++ * ++ * 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. ++ * ++ * Module load syntax: ++ * insmod nf_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS> ++ * stunaddr=<address> ++ * destaction=[auto|strip|none] ++ * ++ * If no ports are specified, the default will be port 554 only. ++ * ++ * stunaddr specifies the address used to detect that a client is using STUN. ++ * If this address is seen in the destination parameter, it is assumed that ++ * the client has already punched a UDP hole in the firewall, so we don't ++ * mangle the client_port. If none is specified, it is autodetected. It ++ * only needs to be set if you have multiple levels of NAT. It should be ++ * set to the external address that the STUN clients detect. Note that in ++ * this case, it will not be possible for clients to use UDP with servers ++ * between the NATs. ++ * ++ * If no destaction is specified, auto is used. ++ * destaction=auto: strip destination parameter if it is not stunaddr. ++ * destaction=strip: always strip destination parameter (not recommended). ++ * destaction=none: do not touch destination parameter (not recommended). ++ */ ++ ++#include <linux/module.h> ++#include <net/tcp.h> ++#include <net/netfilter/nf_nat_helper.h> ++#include <net/netfilter/nf_nat_rule.h> ++#include <linux/netfilter/nf_conntrack_rtsp.h> ++#include <net/netfilter/nf_conntrack_expect.h> ++ ++#include <linux/inet.h> ++#include <linux/ctype.h> ++#define NF_NEED_STRNCASECMP ++#define NF_NEED_STRTOU16 ++#include <linux/netfilter_helpers.h> ++#define NF_NEED_MIME_NEXTLINE ++#include <linux/netfilter_mime.h> ++ ++#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#if 0 ++#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#else ++#define DEBUGP(fmt, args...) ++#endif ++ ++#define MAX_PORTS 8 ++#define DSTACT_AUTO 0 ++#define DSTACT_STRIP 1 ++#define DSTACT_NONE 2 ++ ++static char* stunaddr = NULL; ++static char* destaction = NULL; ++ ++static u_int32_t extip = 0; ++static int dstact = 0; ++ ++MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>"); ++MODULE_DESCRIPTION("RTSP network address translation module"); ++MODULE_LICENSE("GPL"); ++module_param(stunaddr, charp, 0644); ++MODULE_PARM_DESC(stunaddr, "Address for detecting STUN"); ++module_param(destaction, charp, 0644); ++MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)"); ++ ++#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } ++ ++/*** helper functions ***/ ++ ++static void ++get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen) ++{ ++ struct iphdr* iph = ip_hdr(skb); ++ struct tcphdr* tcph = (void *)iph + ip_hdrlen(skb); ++ ++ *pptcpdata = (char*)tcph + tcph->doff*4; ++ *ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata; ++} ++ ++/*** nat functions ***/ ++ ++/* ++ * Mangle the "Transport:" header: ++ * - Replace all occurences of "client_port=<spec>" ++ * - Handle destination parameter ++ * ++ * In: ++ * ct, ctinfo = conntrack context ++ * skb = packet ++ * tranoff = Transport header offset from TCP data ++ * tranlen = Transport header length (incl. CRLF) ++ * rport_lo = replacement low port (host endian) ++ * rport_hi = replacement high port (host endian) ++ * ++ * Returns packet size difference. ++ * ++ * Assumes that a complete transport header is present, ending with CR or LF ++ */ ++static int ++rtsp_mangle_tran(enum ip_conntrack_info ctinfo, ++ struct nf_conntrack_expect* exp, ++ struct ip_ct_rtsp_expect* prtspexp, ++ struct sk_buff* skb, uint tranoff, uint tranlen) ++{ ++ char* ptcp; ++ uint tcplen; ++ char* ptran; ++ char rbuf1[16]; /* Replacement buffer (one port) */ ++ uint rbuf1len; /* Replacement len (one port) */ ++ char rbufa[16]; /* Replacement buffer (all ports) */ ++ uint rbufalen; /* Replacement len (all ports) */ ++ u_int32_t newip; ++ u_int16_t loport, hiport; ++ uint off = 0; ++ uint diff; /* Number of bytes we removed */ ++ ++ struct nf_conn *ct = exp->master; ++ struct nf_conntrack_tuple *t; ++ ++ char szextaddr[15+1]; ++ uint extaddrlen; ++ int is_stun; ++ ++ get_skb_tcpdata(skb, &ptcp, &tcplen); ++ ptran = ptcp+tranoff; ++ ++ if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen || ++ tranlen < 10 || !iseol(ptran[tranlen-1]) || ++ nf_strncasecmp(ptran, "Transport:", 10) != 0) ++ { ++ INFOP("sanity check failed\n"); ++ return 0; ++ } ++ off += 10; ++ SKIP_WSPACE(ptcp+tranoff, tranlen, off); ++ ++ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; ++ t = &exp->tuple; ++ t->dst.u3.ip = newip; ++ ++ extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip)) ++ : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip)); ++ DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto")); ++ ++ rbuf1len = rbufalen = 0; ++ switch (prtspexp->pbtype) ++ { ++ case pb_single: ++ for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(loport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ DEBUGP("using port %hu\n", loport); ++ break; ++ } ++ } ++ if (loport != 0) ++ { ++ rbuf1len = sprintf(rbuf1, "%hu", loport); ++ rbufalen = sprintf(rbufa, "%hu", loport); ++ } ++ break; ++ case pb_range: ++ for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(loport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ hiport = loport + ~exp->mask.src.u.udp.port; ++ DEBUGP("using ports %hu-%hu\n", loport, hiport); ++ break; ++ } ++ } ++ if (loport != 0) ++ { ++ rbuf1len = sprintf(rbuf1, "%hu", loport); ++ rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1); ++ } ++ break; ++ case pb_discon: ++ for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(loport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ DEBUGP("using port %hu (1 of 2)\n", loport); ++ break; ++ } ++ } ++ for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(hiport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ DEBUGP("using port %hu (2 of 2)\n", hiport); ++ break; ++ } ++ } ++ if (loport != 0 && hiport != 0) ++ { ++ rbuf1len = sprintf(rbuf1, "%hu", loport); ++ if (hiport == loport+1) ++ { ++ rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); ++ } ++ else ++ { ++ rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport); ++ } ++ } ++ break; ++ } ++ ++ if (rbuf1len == 0) ++ { ++ return 0; /* cannot get replacement port(s) */ ++ } ++ ++ /* Transport: tran;field;field=val,tran;field;field=val,... */ ++ while (off < tranlen) ++ { ++ uint saveoff; ++ const char* pparamend; ++ uint nextparamoff; ++ ++ pparamend = memchr(ptran+off, ',', tranlen-off); ++ pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; ++ nextparamoff = pparamend-ptcp; ++ ++ /* ++ * We pass over each param twice. On the first pass, we look for a ++ * destination= field. It is handled by the security policy. If it ++ * is present, allowed, and equal to our external address, we assume ++ * that STUN is being used and we leave the client_port= field alone. ++ */ ++ is_stun = 0; ++ saveoff = off; ++ while (off < nextparamoff) ++ { ++ const char* pfieldend; ++ uint nextfieldoff; ++ ++ pfieldend = memchr(ptran+off, ';', nextparamoff-off); ++ nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; ++ ++ if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) ++ { ++ if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0) ++ { ++ is_stun = 1; ++ } ++ if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) ++ { ++ diff = nextfieldoff-off; ++ if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, ++ off, diff, NULL, 0)) ++ { ++ /* mangle failed, all we can do is bail */ ++ nf_ct_unexpect_related(exp); ++ return 0; ++ } ++ get_skb_tcpdata(skb, &ptcp, &tcplen); ++ ptran = ptcp+tranoff; ++ tranlen -= diff; ++ nextparamoff -= diff; ++ nextfieldoff -= diff; ++ } ++ } ++ ++ off = nextfieldoff; ++ } ++ if (is_stun) ++ { ++ continue; ++ } ++ off = saveoff; ++ while (off < nextparamoff) ++ { ++ const char* pfieldend; ++ uint nextfieldoff; ++ ++ pfieldend = memchr(ptran+off, ';', nextparamoff-off); ++ nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; ++ ++ if (strncmp(ptran+off, "client_port=", 12) == 0) ++ { ++ u_int16_t port; ++ uint numlen; ++ uint origoff; ++ uint origlen; ++ char* rbuf = rbuf1; ++ uint rbuflen = rbuf1len; ++ ++ off += 12; ++ origoff = (ptran-ptcp)+off; ++ origlen = 0; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ origlen += numlen; ++ if (port != prtspexp->loport) ++ { ++ DEBUGP("multiple ports found, port %hu ignored\n", port); ++ } ++ else ++ { ++ if (ptran[off] == '-' || ptran[off] == '/') ++ { ++ off++; ++ origlen++; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ origlen += numlen; ++ rbuf = rbufa; ++ rbuflen = rbufalen; ++ } ++ ++ /* ++ * note we cannot just memcpy() if the sizes are the same. ++ * the mangle function does skb resizing, checks for a ++ * cloned skb, and updates the checksums. ++ * ++ * parameter 4 below is offset from start of tcp data. ++ */ ++ diff = origlen-rbuflen; ++ if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, ++ origoff, origlen, rbuf, rbuflen)) ++ { ++ /* mangle failed, all we can do is bail */ ++ nf_ct_unexpect_related(exp); ++ return 0; ++ } ++ get_skb_tcpdata(skb, &ptcp, &tcplen); ++ ptran = ptcp+tranoff; ++ tranlen -= diff; ++ nextparamoff -= diff; ++ nextfieldoff -= diff; ++ } ++ } ++ ++ off = nextfieldoff; ++ } ++ ++ off = nextparamoff; ++ } ++ ++ return 1; ++} ++ ++static uint ++help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, ++ struct nf_conntrack_expect* exp) ++{ ++ char* ptcp; ++ uint tcplen; ++ uint hdrsoff; ++ uint hdrslen; ++ uint lineoff; ++ uint linelen; ++ uint off; ++ ++ //struct iphdr* iph = (struct iphdr*)skb->nh.iph; ++ //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4); ++ ++ get_skb_tcpdata(skb, &ptcp, &tcplen); ++ hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq); ++ hdrslen = matchlen; ++ off = hdrsoff; ++ DEBUGP("NAT rtsp help_out\n"); ++ ++ while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) ++ { ++ if (linelen == 0) ++ { ++ break; ++ } ++ if (off > hdrsoff+hdrslen) ++ { ++ INFOP("!! overrun !!"); ++ break; ++ } ++ DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); ++ ++ if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) ++ { ++ uint oldtcplen = tcplen; ++ DEBUGP("hdr: Transport\n"); ++ if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, skb, lineoff, linelen)) ++ { ++ DEBUGP("hdr: Transport mangle failed"); ++ break; ++ } ++ get_skb_tcpdata(skb, &ptcp, &tcplen); ++ hdrslen -= (oldtcplen-tcplen); ++ off -= (oldtcplen-tcplen); ++ lineoff -= (oldtcplen-tcplen); ++ linelen -= (oldtcplen-tcplen); ++ DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); ++ } ++ } ++ ++ return NF_ACCEPT; ++} ++ ++static unsigned int ++help(struct sk_buff *skb, enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, ++ struct nf_conntrack_expect* exp) ++{ ++ int dir = CTINFO2DIR(ctinfo); ++ int rc = NF_ACCEPT; ++ ++ switch (dir) ++ { ++ case IP_CT_DIR_ORIGINAL: ++ rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp, exp); ++ break; ++ case IP_CT_DIR_REPLY: ++ DEBUGP("unmangle ! %u\n", ctinfo); ++ /* XXX: unmangle */ ++ rc = NF_ACCEPT; ++ break; ++ } ++ //UNLOCK_BH(&ip_rtsp_lock); ++ ++ return rc; ++} ++ ++static void expected(struct nf_conn* ct, struct nf_conntrack_expect *exp) ++{ ++ struct nf_nat_multi_range_compat mr; ++ u_int32_t newdstip, newsrcip, newip; ++ ++ struct nf_conn *master = ct->master; ++ ++ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; ++ newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; ++ //FIXME (how to port that ?) ++ //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip; ++ newip = newdstip; ++ ++ DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n", ++ NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip)); ++ ++ mr.rangesize = 1; ++ // We don't want to manip the per-protocol, just the IPs. ++ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; ++ mr.range[0].min_ip = mr.range[0].max_ip = newip; ++ ++ nf_nat_setup_info(ct, &mr.range[0], IP_NAT_MANIP_DST); ++} ++ ++ ++static void __exit fini(void) ++{ ++ nf_nat_rtsp_hook = NULL; ++ nf_nat_rtsp_hook_expectfn = NULL; ++ synchronize_net(); ++} ++ ++static int __init init(void) ++{ ++ printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n"); ++ ++ BUG_ON(nf_nat_rtsp_hook); ++ nf_nat_rtsp_hook = help; ++ nf_nat_rtsp_hook_expectfn = &expected; ++ ++ if (stunaddr != NULL) ++ extip = in_aton(stunaddr); ++ ++ if (destaction != NULL) { ++ if (strcmp(destaction, "auto") == 0) ++ dstact = DSTACT_AUTO; ++ ++ if (strcmp(destaction, "strip") == 0) ++ dstact = DSTACT_STRIP; ++ ++ if (strcmp(destaction, "none") == 0) ++ dstact = DSTACT_NONE; ++ } ++ ++ return 0; ++} ++ ++module_init(init); ++module_exit(fini); diff --git a/target/linux/generic-2.6/patches-2.6.28/200-sched_esfq.patch b/target/linux/generic-2.6/patches-2.6.28/200-sched_esfq.patch new file mode 100644 index 0000000..f9ce66e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/200-sched_esfq.patch @@ -0,0 +1,795 @@ +--- a/include/linux/pkt_sched.h ++++ b/include/linux/pkt_sched.h +@@ -180,8 +180,37 @@ struct tc_sfq_xstats + * + * 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, ++ /* conntrack */ ++ TCA_SFQ_HASH_CTORIGDST, ++ TCA_SFQ_HASH_CTORIGSRC, ++ TCA_SFQ_HASH_CTREPLDST, ++ TCA_SFQ_HASH_CTREPLSRC, ++ TCA_SFQ_HASH_CTNATCHG, ++}; ++ ++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 +--- a/net/sched/Kconfig ++++ b/net/sched/Kconfig +@@ -137,6 +137,37 @@ 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 <file:net/sched/sch_esfq.c> 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_ESFQ_NFCT ++ bool "Connection Tracking Hash Types" ++ depends on NET_SCH_ESFQ && NF_CONNTRACK ++ ---help--- ++ Say Y here to enable support for hashing based on netfilter connection ++ tracking information. This is useful for a router that is also using ++ NAT to connect privately-addressed hosts to the Internet. If you want ++ to provide fair distribution of upstream bandwidth, ESFQ must use ++ connection tracking information, since all outgoing packets will share ++ the same source address. ++ + config NET_SCH_TEQL + tristate "True Link Equalizer (TEQL)" + ---help--- +--- a/net/sched/Makefile ++++ b/net/sched/Makefile +@@ -24,6 +24,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 +--- /dev/null ++++ b/net/sched/sch_esfq.c +@@ -0,0 +1,702 @@ ++/* ++ * 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, <kuznet@ms2.inr.ac.ru> ++ * ++ * Changes: Alexander Atanasov, <alex@ssi.bg> ++ * Added dynamic depth,limit,divisor,hash_kind options. ++ * Added dst and src hashes. ++ * ++ * Alexander Clouter, <alex@digriz.org.uk> ++ * Ported ESFQ to Linux 2.6. ++ * ++ * Corey Hickey, <bugfood-c@fatooh.org> ++ * Maintenance of the Linux 2.6 port. ++ * Added fwmark hash (thanks to Robert Kurjata). ++ * Added usage of jhash. ++ * Added conntrack support. ++ * Added ctnatchg hash (thanks to Ben Pfountz). ++ */ ++ ++#include <linux/module.h> ++#include <asm/uaccess.h> ++#include <asm/system.h> ++#include <linux/bitops.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/jiffies.h> ++#include <linux/string.h> ++#include <linux/mm.h> ++#include <linux/socket.h> ++#include <linux/sockios.h> ++#include <linux/in.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/if_ether.h> ++#include <linux/inet.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/notifier.h> ++#include <linux/init.h> ++#include <net/ip.h> ++#include <linux/ipv6.h> ++#include <net/route.h> ++#include <linux/skbuff.h> ++#include <net/sock.h> ++#include <net/pkt_sched.h> ++#include <linux/jhash.h> ++#include <net/netfilter/nf_conntrack.h> ++ ++/* 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 ++ ctorigdst: original destination IP address ++ ctorigsrc: original source IP address ++ ctrepldst: reply destination IP address ++ ctreplsrc: reply source IP ++ ++*/ ++ ++#define ESFQ_HEAD 0 ++#define ESFQ_TAIL 1 ++ ++/* 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 */ ++}; ++ ++/* 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) */ ++}; ++ ++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_NET_SCH_ESFQ_NFCT ++ 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 = ip_hdr(skb); ++ 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 = ipv6_hdr(skb); ++ /* 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_NET_SCH_ESFQ_NFCT ++ /* 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_SRC: ++ return esfq_jhash_1word(q, info.src); ++ case TCA_SFQ_HASH_FWMARK: ++ return esfq_jhash_1word(q, info.mark); ++#ifdef CONFIG_NET_SCH_ESFQ_NFCT ++ 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); ++ case TCA_SFQ_HASH_CTNATCHG: ++ { ++ if (info.ctorigdst == info.ctreplsrc) ++ return esfq_jhash_1word(q, info.ctorigsrc); ++ 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 void esfq_q_enqueue(struct sk_buff *skb, struct esfq_sched_data *q, unsigned int end) ++{ ++ 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; ++ } ++ ++ if (end == ESFQ_TAIL) ++ __skb_queue_tail(&q->qs[x], skb); ++ else ++ __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; ++ } ++ } ++} ++ ++static int esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_q_enqueue(skb, q, ESFQ_TAIL); ++ sch->qstats.backlog += skb->len; ++ if (++sch->q.qlen < q->limit-1) { ++ sch->bstats.bytes += skb->len; ++ sch->bstats.packets++; ++ return 0; ++ } ++ ++ sch->qstats.drops++; ++ 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); ++ esfq_q_enqueue(skb, q, ESFQ_HEAD); ++ sch->qstats.backlog += skb->len; ++ 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_q_dequeue(struct esfq_sched_data *q) ++{ ++ 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); ++ ++ /* 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 struct sk_buff *esfq_dequeue(struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct sk_buff *skb; ++ ++ skb = esfq_q_dequeue(q); ++ if (skb == NULL) ++ return NULL; ++ sch->q.qlen--; ++ sch->qstats.backlog -= skb->len; ++ return skb; ++} ++ ++static void esfq_q_destroy(struct esfq_sched_data *q) ++{ ++ 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 void esfq_destroy(struct Qdisc *sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_q_destroy(q); ++} ++ ++ ++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 unsigned int esfq_check_hash(unsigned int kind) ++{ ++ switch (kind) { ++ case TCA_SFQ_HASH_CTORIGDST: ++ case TCA_SFQ_HASH_CTORIGSRC: ++ case TCA_SFQ_HASH_CTREPLDST: ++ case TCA_SFQ_HASH_CTREPLSRC: ++ case TCA_SFQ_HASH_CTNATCHG: ++#ifndef CONFIG_NET_SCH_ESFQ_NFCT ++ { ++ if (net_ratelimit()) ++ printk(KERN_WARNING "ESFQ: Conntrack hash types disabled in kernel config. Falling back to classic.\n"); ++ return TCA_SFQ_HASH_CLASSIC; ++ } ++#endif ++ case TCA_SFQ_HASH_CLASSIC: ++ case TCA_SFQ_HASH_DST: ++ case TCA_SFQ_HASH_SRC: ++ case TCA_SFQ_HASH_FWMARK: ++ return kind; ++ default: ++ { ++ if (net_ratelimit()) ++ printk(KERN_WARNING "ESFQ: Unknown hash type. Falling back to classic.\n"); ++ return TCA_SFQ_HASH_CLASSIC; ++ } ++ } ++} ++ ++static int esfq_q_init(struct esfq_sched_data *q, struct rtattr *opt) ++{ ++ struct tc_esfq_qopt *ctl = RTA_DATA(opt); ++ esfq_index p = ~0U/2; ++ int i; ++ ++ if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl))) ++ return -EINVAL; ++ ++ q->perturbation = 0; ++ q->hash_kind = TCA_SFQ_HASH_CLASSIC; ++ q->max_depth = 0; ++ if (opt == NULL) { ++ q->perturb_period = 0; ++ q->hash_divisor = 1024; ++ q->tail = q->limit = q->depth = 128; ++ ++ } else { ++ struct tc_esfq_qopt *ctl = RTA_DATA(opt); ++ if (ctl->quantum) ++ q->quantum = ctl->quantum; ++ 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 = esfq_check_hash(ctl->hash_kind); ++ } ++ } ++ ++ 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; i<q->depth; 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; i<q->depth; i++) ++ esfq_link(q, i); ++ return 0; ++err_case: ++ esfq_q_destroy(q); ++ return -ENOBUFS; ++} ++ ++static int esfq_init(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ int err; ++ ++ q->quantum = psched_mtu(qdisc_dev(sch)); /* default */ ++ if ((err = esfq_q_init(q, opt))) ++ return err; ++ ++ init_timer(&q->perturb_timer); ++ q->perturb_timer.data = (unsigned long)sch; ++ q->perturb_timer.function = esfq_perturbation; ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } ++ ++ return 0; ++} ++ ++static int esfq_change(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct esfq_sched_data new; ++ struct sk_buff *skb; ++ int err; ++ ++ /* set up new queue */ ++ memset(&new, 0, sizeof(struct esfq_sched_data)); ++ new.quantum = psched_mtu(qdisc_dev(sch)); /* default */ ++ if ((err = esfq_q_init(&new, opt))) ++ return err; ++ ++ /* copy all packets from the old queue to the new queue */ ++ sch_tree_lock(sch); ++ while ((skb = esfq_q_dequeue(q)) != NULL) ++ esfq_q_enqueue(skb, &new, ESFQ_TAIL); ++ ++ /* clean up the old queue */ ++ esfq_q_destroy(q); ++ ++ /* copy elements of the new queue into the old queue */ ++ q->perturb_period = new.perturb_period; ++ q->quantum = new.quantum; ++ q->limit = new.limit; ++ q->depth = new.depth; ++ q->hash_divisor = new.hash_divisor; ++ q->hash_kind = new.hash_kind; ++ q->tail = new.tail; ++ q->max_depth = new.max_depth; ++ q->ht = new.ht; ++ q->dep = new.dep; ++ q->next = new.next; ++ q->allot = new.allot; ++ q->hash = new.hash; ++ q->qs = new.qs; ++ ++ /* finish up */ ++ 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_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 = esfq_change, ++ .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.28/202-mips-freestanding.patch b/target/linux/generic-2.6/patches-2.6.28/202-mips-freestanding.patch new file mode 100644 index 0000000..34dd2b3 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/202-mips-freestanding.patch @@ -0,0 +1,12 @@ +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -586,6 +586,9 @@ core-$(CONFIG_TOSHIBA_RBTX4927) += arch/ + core-$(CONFIG_TOSHIBA_RBTX4938) += arch/mips/txx9/rbtx4938/ + core-$(CONFIG_TOSHIBA_RBTX4939) += arch/mips/txx9/rbtx4939/ + ++# temporary until string.h is fixed ++cflags-y += -ffreestanding ++ + cflags-y += -I$(srctree)/arch/mips/include/asm/mach-generic + drivers-$(CONFIG_PCI) += arch/mips/pci/ + diff --git a/target/linux/generic-2.6/patches-2.6.28/204-jffs2_eofdetect.patch b/target/linux/generic-2.6/patches-2.6.28/204-jffs2_eofdetect.patch new file mode 100644 index 0000000..16d3e1f --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/204-jffs2_eofdetect.patch @@ -0,0 +1,132 @@ +--- a/fs/jffs2/build.c ++++ b/fs/jffs2/build.c +@@ -111,6 +111,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. */ +--- a/fs/jffs2/scan.c ++++ b/fs/jffs2/scan.c +@@ -72,7 +72,7 @@ static int file_dirty(struct jffs2_sb_in + return ret; + if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size))) + return ret; +- /* Turned wasted size into dirty, since we apparently ++ /* Turned wasted size into dirty, since we apparently + think it's recoverable now. */ + jeb->dirty_size += jeb->wasted_size; + c->dirty_size += jeb->wasted_size; +@@ -144,8 +144,11 @@ 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; +@@ -400,7 +403,7 @@ static int jffs2_scan_xref_node(struct j + if (!ref) + return -ENOMEM; + +- /* BEFORE jffs2_build_xattr_subsystem() called, ++ /* BEFORE jffs2_build_xattr_subsystem() called, + * and AFTER xattr_ref is marked as a dead xref, + * ref->xid is used to store 32bit xid, xd is not used + * ref->ino is used to store 32bit inode-number, ic is not used +@@ -473,7 +476,7 @@ static int jffs2_scan_eraseblock (struct + struct jffs2_sum_marker *sm; + void *sumptr = NULL; + uint32_t sumlen; +- ++ + if (!buf_size) { + /* XIP case. Just look, point at the summary if it's there */ + sm = (void *)buf + c->sector_size - sizeof(*sm); +@@ -489,9 +492,9 @@ static int jffs2_scan_eraseblock (struct + buf_len = sizeof(*sm); + + /* Read as much as we want into the _end_ of the preallocated buffer */ +- err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, ++ err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, + jeb->offset + c->sector_size - buf_len, +- buf_len); ++ buf_len); + if (err) + return err; + +@@ -510,9 +513,9 @@ static int jffs2_scan_eraseblock (struct + } + if (buf_len < sumlen) { + /* Need to read more so that the entire summary node is present */ +- err = jffs2_fill_scan_buf(c, sumptr, ++ err = jffs2_fill_scan_buf(c, sumptr, + jeb->offset + c->sector_size - sumlen, +- sumlen - buf_len); ++ sumlen - buf_len); + if (err) + return err; + } +@@ -525,7 +528,7 @@ static int jffs2_scan_eraseblock (struct + + if (buf_size && sumlen > buf_size) + kfree(sumptr); +- /* If it returns with a real error, bail. ++ /* If it returns with a real error, bail. + If it returns positive, that's a block classification + (i.e. BLK_STATE_xxx) so return that too. + If it returns zero, fall through to full scan. */ +@@ -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; + +@@ -671,7 +685,7 @@ scan_more: + scan_end = buf_len; + goto more_empty; + } +- ++ + /* See how much more there is to read in this eraseblock... */ + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + if (!buf_len) { +@@ -907,7 +921,7 @@ scan_more: + + D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n", + jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size)); +- ++ + /* mark_node_obsolete can add to wasted !! */ + if (jeb->wasted_size) { + jeb->dirty_size += jeb->wasted_size; diff --git a/target/linux/generic-2.6/patches-2.6.28/207-powerpc_asm_segment_h.patch b/target/linux/generic-2.6/patches-2.6.28/207-powerpc_asm_segment_h.patch new file mode 100644 index 0000000..1272e82 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/207-powerpc_asm_segment_h.patch @@ -0,0 +1,9 @@ +--- /dev/null ++++ b/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.28/208-rtl8110sb_fix.patch b/target/linux/generic-2.6/patches-2.6.28/208-rtl8110sb_fix.patch new file mode 100644 index 0000000..ca25aed --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/208-rtl8110sb_fix.patch @@ -0,0 +1,42 @@ +--- a/drivers/net/r8169.c ++++ b/drivers/net/r8169.c +@@ -1862,7 +1862,7 @@ static const struct rtl_cfg_info { + .hw_start = rtl_hw_start_8169, + .region = 1, + .align = 0, +- .intr_event = SYSErr | LinkChg | RxOverflow | ++ .intr_event = LinkChg | RxOverflow | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + .features = RTL_FEATURE_GMII +@@ -1871,7 +1871,7 @@ static const struct rtl_cfg_info { + .hw_start = rtl_hw_start_8168, + .region = 2, + .align = 8, +- .intr_event = SYSErr | LinkChg | RxOverflow | ++ .intr_event = LinkChg | RxOverflow | + TxErr | TxOK | RxOK | RxErr, + .napi_event = TxErr | TxOK | RxOK | RxOverflow, + .features = RTL_FEATURE_GMII | RTL_FEATURE_MSI +@@ -1880,7 +1880,7 @@ static const struct rtl_cfg_info { + .hw_start = rtl_hw_start_8101, + .region = 2, + .align = 8, +- .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout | ++ .intr_event = LinkChg | RxOverflow | PCSTimeout | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + .features = RTL_FEATURE_MSI +@@ -3554,10 +3554,12 @@ static irqreturn_t rtl8169_interrupt(int + goto out; + } + ++#if 0 + if (unlikely(status & SYSErr)) { + rtl8169_pcierr_interrupt(dev); + goto out; + } ++#endif + + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); diff --git a/target/linux/generic-2.6/patches-2.6.28/209-mini_fo.patch b/target/linux/generic-2.6/patches-2.6.28/209-mini_fo.patch new file mode 100644 index 0000000..8b6df7a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/209-mini_fo.patch @@ -0,0 +1,7776 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -1013,6 +1013,9 @@ config VXFS_FS + To compile this as a module, choose M here: the module will be + called freevxfs. If unsure, say N. + ++config MINI_FO ++ tristate "Mini fanout overlay filesystem" ++ + config MINIX_FS + tristate "Minix file system support" + depends on BLOCK +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -80,6 +80,7 @@ obj-$(CONFIG_SQUASHFS) += squashfs/ + obj-y += 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_BFS_FS) += bfs/ +--- /dev/null ++++ b/fs/mini_fo/aux.c +@@ -0,0 +1,577 @@ ++/* ++ * 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 <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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 vfsmount *mnt; ++ 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; ++ } ++ ++ /* fix this: how do I reach this lock? ++ * read_lock(¤t->fs->lock); */ ++ mnt = mntget(stopd(sb)->hidden_mnt); ++ /* read_unlock(¤t->fs->lock); */ ++ ++ err = vfs_path_lookup(mnt->mnt_root, mnt, bpath+1, 0, &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 */ ++ +--- /dev/null ++++ b/fs/mini_fo/ChangeLog +@@ -0,0 +1,281 @@ ++2006-01-24 Markus Klotzbuecher <mk@mary.denx.de> ++ ++ * Add tons of ugly ifdefs to Ed L. Cashin's mutex patch to ++ retain backwards compatibility. ++ ++2006-01-24 Ed L. Cashin <ecashin@coraid.com> ++ ++ * Support for the new mutex infrastructure ++ (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) ++ ++2005-10-15 Markus Klotzbuecher <mk@localhost.localdomain> ++ ++ * Bugfix for a serious memory leak in mini_fo_follow_link. ++ ++2005-09-21 Markus Klotzbuecher <mk@mary> ++ ++ * new release 0.6.1 ++ ++ * fix of a compiler warning due to changes in 2.6.13 ++ ++2005-09-21 Klaus Wenninger <klaus.wenninger@siemens.com> ++ ++ * 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 <eric@lammerts.org> ++ ++ * fix for an oops when overwriting a binary thats beeing ++ executed. ++ ++2005-06-09 <mk@mary> ++ ++ * 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 <mk@mary> ++ ++ * Added overlay script that allows to easily mount mini_fo ontop ++ of a given base directory ++ ++2005-05-10 <mk@mary> ++ ++ * inode.c: xattr functions return -EOPNOSUPP instead of ++ -ENOSUPP, what confuses "ls -l" ++ ++ * Changed license from LGPL to GPL. ++ ++2005-05-08 root <mk@mary> ++ ++ * Makefile: clean it up and added make install and make ++ uninstall. ++ ++2005-05-06 <mk@mary> ++ ++ * 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 <mk@mary> ++ ++ * 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 <mk@mary> ++ ++ * 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 <mk@mary> ++ ++ * 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 <mk@mary> ++ ++ * 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 <mk@mary> ++ ++ * aux.c: get_neg_sto_dentry(): allow to be called for dentries ++ in state UNMODIFIED, NON_EXISTANT _and_ DELETED. ++ ++2005-04-19 <mk@mary> ++ ++ * 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 <mk@mary> ++ ++ * Bugfixes ++ ++2005-04-13 root <mk@mary> ++ ++ * Added file state.c for the state transition ++ functions. Doesn't work very well yet, though... ++ ++2005-04-12 <mk@mary> ++ ++ * Porting to 2.6 started, which is easier than expected, also ++ due to Olivier previous work. ++ ++2005-04-08 <mk@mary> ++ ++ * 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 <mk@chasey> ++ ++ * Merged devel branch back to main trunk and updated the ++ RELEASE notes. This will be 0-6-0-pre1. ++ ++2005-03-31 Markus Klotzbuecher <mk@chasey> ++ ++ * 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 <mk@chasey> ++ ++ * Many, many directory renaming bugfixes and a lot of other ++ cleanup. Dir renaming seems to work relatively stable. ++ ++2005-03-22 Markus Klotzbuecher <mk@chasey> ++ ++ * 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 <mk@chasey> ++ ++ * Finished implementing meta.c stuff required for directory ++ renaming. ++ ++2005-03-17 Markus Klotzbuecher <mk@chasey> ++ ++ * 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 <mk@chasey> ++ ++ * Implemented new META subsystem, removed old WOL stuff in favor ++ if it. ++ ++ * After some basic testing everything seems ok... ++ ++2005-03-11 Markus Klotzbuecher <mk@chasey> ++ ++ * 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 <mk@chasey> ++ ++ * 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 <mk@chasey> ++ ++ * 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 <mk@mary> ++ ++ * Implemented directory deleting (inode.c) ++ ++ * main.c: made mini_fo_parse_options a little more robust. ++ ++2004-12-22 <mk@mary> ++ ++ * 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 <gleb@nbase.co.il> ++ ++ * Fix: owner and group where not correctly copied from base to ++ storage. ++ ++ ++2004-10-05 Gleb Natapov <gleb@nbase.co.il> ++ ++ * Implementation of fsync, fasync and lock mini_fo functions. ++ ++ ++2004-09-29 Bob Lee <bob@pantasys.com> ++ ++ * Fix of a serious pointer bug ++ ++ ++2004-09-28 Gleb Natapov <gleb@nbase.co.il> ++ ++ * Implementation of mini_fo_mknod and mini_fo_rename, support ++ for device files. ++ +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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, ++}; +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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 */ ++ }; +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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 <linux/version.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) ++#include <linux/autoconf.h> ++#else ++#include <linux/config.h> ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#ifdef CONFIG_MODVERSIONS ++# define MODVERSIONS ++# include <linux/modversions.h> ++#endif /* CONFIG_MODVERSIONS */ ++#endif /* KERNEL_VERSION < 2.6.0 */ ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/mm.h> ++#include <linux/string.h> ++#include <linux/stat.h> ++#include <linux/errno.h> ++#include <linux/wait.h> ++#include <linux/limits.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#include <linux/locks.h> ++#else ++#include <linux/buffer_head.h> ++#include <linux/pagemap.h> ++#include <linux/namei.h> ++#include <linux/module.h> ++#include <linux/mount.h> ++#include <linux/page-flags.h> ++#include <linux/writeback.h> ++#include <linux/statfs.h> ++#endif ++#include <linux/smp.h> ++#include <linux/smp_lock.h> ++#include <linux/file.h> ++#include <linux/slab.h> ++#include <linux/vmalloc.h> ++#include <linux/poll.h> ++#include <linux/list.h> ++#include <linux/init.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) ++#include <linux/xattr.h> ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#include <linux/security.h> ++#endif ++ ++#include <linux/swap.h> ++ ++#include <asm/system.h> ++/* #include <asm/segment.h> */ ++#include <asm/mman.h> ++#include <linux/seq_file.h> ++ ++/* ++ * 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_ */ +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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) */ ++ }; +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++#include <linux/module.h> ++ ++/* This definition must only appear after we include <linux/module.h> */ ++#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 <ezk@cs.sunysb.edu>"); ++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) +--- /dev/null ++++ b/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 <mk@creamnet.de> ++# ++# 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 ++ +--- /dev/null ++++ b/fs/mini_fo/meta.c +@@ -0,0 +1,1000 @@ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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; ++} ++ +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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_ */ +--- /dev/null ++++ b/fs/mini_fo/mini_fo-merge +@@ -0,0 +1,180 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher <mk@creamnet.de> ++# 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 <<EOF ++ ++USAGE: $0 -b <base dir> -s <storage dir> ++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 <base dir> ++ the directory of the base file system. ++ ++ -s <storage dir> ++ 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<<EOF ++############################################################################### ++# mini_fo-merge ++# ++# base dir: $BASE ++# storage dir: $STO ++# meta filename: $META_NAME ++# dry run: $DRYRUN ++# verbose: $VERBOSE ++# tmp files: $TMP ++############################################################################### ++ ++EOF ++ ++rm $TMP/$SKIP_DEL_LIST ++ ++# first process all renamed dirs ++echo "Merging renamed directories..." ++pushd $STO &> /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!" +--- /dev/null ++++ b/fs/mini_fo/mini_fo-overlay +@@ -0,0 +1,130 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher <mk@creamnet.de> ++# 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 <<EOF ++ ++Usage: $0 [-s suffix] [-d sto_dir_dir] [-m mount point] base_dir ++Version 0.1 ++ ++This script overlays the given base directory using the mini_fo file ++system. If only the base directory base_dir is given, $0 ++will use a storage directory called "sto-<base_dir_name>" in $STO_DIR, ++and mount point "mini_fo-<base_dir_dir>" in $MNT_DIR. ++ ++Options: ++ -s <suffix> ++ 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 <sto_dir_dir> ++ change the directory in which the storage directory will be ++ created (default is currently "$STO_DIR". ++ ++ -m <mount point> ++ 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 +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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; ++} +--- /dev/null ++++ b/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=<base directory>,sto=<storage directory>\ ++ <base directory> <mount point> ++ ++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 <mk@creamnet.de> ++ ++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. ++ ++ +--- /dev/null ++++ b/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. +--- /dev/null ++++ b/fs/mini_fo/state.c +@@ -0,0 +1,620 @@ ++/* ++ * Copyright (C) 2005 Markus Klotzbuecher <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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; ++} ++ +--- /dev/null ++++ b/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 <mk@creamnet.de> ++ * ++ * 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 <config.h> ++#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.28/210-mini_fo_2.6.25_fixes.patch b/target/linux/generic-2.6/patches-2.6.28/210-mini_fo_2.6.25_fixes.patch new file mode 100644 index 0000000..d71e3b6 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/210-mini_fo_2.6.25_fixes.patch @@ -0,0 +1,143 @@ +--- a/fs/mini_fo/main.c ++++ b/fs/mini_fo/main.c +@@ -79,6 +79,7 @@ mini_fo_tri_interpose(dentry_t *hidden_d + * of the new inode's fields + */ + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + /* + * original: inode = iget(sb, hidden_inode->i_ino); + */ +@@ -87,6 +88,13 @@ mini_fo_tri_interpose(dentry_t *hidden_d + err = -EACCES; /* should be impossible??? */ + goto out; + } ++#else ++ inode = mini_fo_iget(sb, iunique(sb, 25)); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out; ++ } ++#endif + + /* + * interpose the inode if not already interposed +@@ -184,9 +192,9 @@ mini_fo_parse_options(super_block_t *sb, + hidden_root = ERR_PTR(err); + goto out; + } +- hidden_root = nd.dentry; +- stopd(sb)->base_dir_dentry = nd.dentry; +- stopd(sb)->hidden_mnt = nd.mnt; ++ hidden_root = nd_get_dentry(&nd); ++ stopd(sb)->base_dir_dentry = nd_get_dentry(&nd); ++ stopd(sb)->hidden_mnt = nd_get_mnt(&nd); + + } else if(!strncmp("sto=", options, 4)) { + /* parse the storage dir */ +@@ -204,9 +212,9 @@ mini_fo_parse_options(super_block_t *sb, + hidden_root2 = ERR_PTR(err); + goto out; + } +- hidden_root2 = nd2.dentry; +- stopd(sb)->storage_dir_dentry = nd2.dentry; +- stopd(sb)->hidden_mnt2 = nd2.mnt; ++ hidden_root2 = nd_get_dentry(&nd2); ++ stopd(sb)->storage_dir_dentry = nd_get_dentry(&nd2); ++ stopd(sb)->hidden_mnt2 = nd_get_mnt(&nd2); + stohs2(sb) = hidden_root2->d_sb; + + /* validate storage dir, this is done in +--- a/fs/mini_fo/mini_fo.h ++++ b/fs/mini_fo/mini_fo.h +@@ -302,6 +302,10 @@ extern int mini_fo_tri_interpose(dentry_ + 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,25) ++extern struct inode *mini_fo_iget(struct super_block *sb, unsigned long ino); ++#endif ++ + #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); + +@@ -501,6 +505,29 @@ static inline void double_unlock(struct + #endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ + #endif /* __KERNEL__ */ + ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) ++static inline dentry_t *nd_get_dentry(struct nameidata *nd) ++{ ++ return (nd->path.dentry); ++} ++ ++static inline struct vfsmount *nd_get_mnt(struct nameidata *nd) ++{ ++ return (nd->path.mnt); ++} ++#else ++static inline dentry_t *nd_get_dentry(struct nameidata *nd) ++{ ++ return (nd->dentry); ++} ++ ++static inline struct vfsmount *nd_get_mnt(struct nameidata *nd) ++{ ++ return (nd->mnt); ++} ++#endif ++ + /* + * Definitions for user and kernel code + */ +--- a/fs/mini_fo/super.c ++++ b/fs/mini_fo/super.c +@@ -262,10 +262,31 @@ mini_fo_umount_begin(super_block_t *sb) + } + #endif + ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) ++struct inode * ++mini_fo_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ mini_fo_read_inode(inode); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) */ + + struct super_operations mini_fo_sops = + { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + read_inode: mini_fo_read_inode, ++#endif + #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) + write_inode: mini_fo_write_inode, + #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ +--- a/fs/mini_fo/aux.c ++++ b/fs/mini_fo/aux.c +@@ -164,11 +164,11 @@ dentry_t *bpath_walk(super_block_t *sb, + err = vfs_path_lookup(mnt->mnt_root, mnt, bpath+1, 0, &nd); + + /* validate */ +- if (err || !nd.dentry || !nd.dentry->d_inode) { ++ if (err || !nd_get_dentry(&nd) || !nd_get_dentry(&nd)->d_inode) { + printk(KERN_CRIT "mini_fo: bpath_walk: path_walk failed.\n"); + return NULL; + } +- return nd.dentry; ++ return nd_get_dentry(&nd); + } + + diff --git a/target/linux/generic-2.6/patches-2.6.28/211-mini_fo_2.6.25_dentry_open_war.patch b/target/linux/generic-2.6/patches-2.6.28/211-mini_fo_2.6.25_dentry_open_war.patch new file mode 100644 index 0000000..48a1942 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/211-mini_fo_2.6.25_dentry_open_war.patch @@ -0,0 +1,66 @@ +--- a/fs/mini_fo/meta.c ++++ b/fs/mini_fo/meta.c +@@ -442,6 +442,11 @@ int meta_write_d_entry(dentry_t *dentry, + S_IRUSR | S_IWUSR); + #endif + } ++ ++ /* $%& err, is this correct? */ ++ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ mntget(meta_mnt); ++ + /* open META-file for writing */ + meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); + if(!meta_file || IS_ERR(meta_file)) { +@@ -535,6 +540,11 @@ int meta_write_r_entry(dentry_t *dentry, + meta_dentry, S_IRUSR | S_IWUSR); + #endif + } ++ ++ /* $%& err, is this correct? */ ++ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ mntget(meta_mnt); ++ + /* open META-file for writing */ + meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); + if(!meta_file || IS_ERR(meta_file)) { +@@ -671,14 +681,16 @@ int meta_sync_d_list(dentry_t *dentry, i + } + } + ++ /* $%& err, is this correct? */ ++ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ mntget(meta_mnt); ++ + /* 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); +- */ ++ mntput(meta_mnt); + dput(meta_dentry); + err = -1; + goto out; +@@ -811,14 +823,16 @@ int meta_sync_r_list(dentry_t *dentry, i + } + } + ++ /* $%& err, is this correct? */ ++ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ mntget(meta_mnt); ++ + /* 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); +- */ ++ mntput(meta_mnt); + dput(meta_dentry); + err = -1; + goto out; diff --git a/target/linux/generic-2.6/patches-2.6.28/212-mini_fo_2.6.26_fixes.patch b/target/linux/generic-2.6/patches-2.6.28/212-mini_fo_2.6.26_fixes.patch new file mode 100644 index 0000000..8bd9ba3 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/212-mini_fo_2.6.26_fixes.patch @@ -0,0 +1,37 @@ +--- a/fs/mini_fo/super.c ++++ b/fs/mini_fo/super.c +@@ -84,6 +84,7 @@ mini_fo_write_inode(inode_t *inode, int + #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ + + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + STATIC void + mini_fo_put_inode(inode_t *inode) + { +@@ -99,6 +100,7 @@ mini_fo_put_inode(inode_t *inode) + if (atomic_read(&inode->i_count) == 1) + inode->i_nlink = 0; + } ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) */ + + + #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) +@@ -238,7 +240,7 @@ mini_fo_clear_inode(inode_t *inode) + * dies. + */ + STATIC void +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) + mini_fo_umount_begin(struct vfsmount *mnt, int flags) + { + struct vfsmount *hidden_mnt; +@@ -290,7 +292,9 @@ struct super_operations mini_fo_sops = + #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) + write_inode: mini_fo_write_inode, + #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + put_inode: mini_fo_put_inode, ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) */ + #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) + delete_inode: mini_fo_delete_inode, + #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ diff --git a/target/linux/generic-2.6/patches-2.6.28/213-mini_fo_2.6.27_fixes.patch b/target/linux/generic-2.6/patches-2.6.28/213-mini_fo_2.6.27_fixes.patch new file mode 100644 index 0000000..f92c18b --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/213-mini_fo_2.6.27_fixes.patch @@ -0,0 +1,41 @@ +--- a/fs/mini_fo/inode.c ++++ b/fs/mini_fo/inode.c +@@ -439,7 +439,7 @@ mini_fo_symlink(inode_t *dir, dentry_t * + int err=0; + dentry_t *hidden_sto_dentry; + dentry_t *hidden_sto_dir_dentry; +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) + umode_t mode; + #endif + +@@ -466,7 +466,7 @@ mini_fo_symlink(inode_t *dir, dentry_t * + down(&hidden_sto_dir_dentry->d_inode->i_sem); + #endif + +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) + mode = S_IALLUGO; + err = vfs_symlink(hidden_sto_dir_dentry->d_inode, + hidden_sto_dentry, symname, mode); +@@ -1128,7 +1128,7 @@ void mini_fo_put_link(struct dentry *den + #endif + + STATIC int +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) + mini_fo_permission(inode_t *inode, int mask, struct nameidata *nd) + #else + mini_fo_permission(inode_t *inode, int mask) +@@ -1150,8 +1150,9 @@ mini_fo_permission(inode_t *inode, int m + * if (err) + * goto out; + */ +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++ err = inode_permission(hidden_inode, mask); ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + err = permission(hidden_inode, mask, nd); + #else + err = permission(hidden_inode, mask); diff --git a/target/linux/generic-2.6/patches-2.6.28/219-kobject_uevent.patch b/target/linux/generic-2.6/patches-2.6.28/219-kobject_uevent.patch new file mode 100644 index 0000000..279665e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/219-kobject_uevent.patch @@ -0,0 +1,42 @@ +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -29,7 +29,8 @@ u64 uevent_seqnum; + char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; + static DEFINE_SPINLOCK(sequence_lock); + #if defined(CONFIG_NET) +-static struct sock *uevent_sock; ++struct sock *uevent_sock = NULL; ++EXPORT_SYMBOL_GPL(uevent_sock); + #endif + + /* the strings here must match the enum in include/linux/kobject.h */ +@@ -42,6 +43,18 @@ static const char *kobject_actions[] = { + [KOBJ_OFFLINE] = "offline", + }; + ++u64 uevent_next_seqnum(void) ++{ ++ u64 seq; ++ ++ spin_lock(&sequence_lock); ++ seq = ++uevent_seqnum; ++ spin_unlock(&sequence_lock); ++ ++ return seq; ++} ++EXPORT_SYMBOL_GPL(uevent_next_seqnum); ++ + /** + * kobject_action_type - translate action string to numeric type + * +@@ -194,9 +207,7 @@ int kobject_uevent_env(struct kobject *k + kobj->state_remove_uevent_sent = 1; + + /* we will send an event, so request a new sequence number */ +- spin_lock(&sequence_lock); +- seq = ++uevent_seqnum; +- spin_unlock(&sequence_lock); ++ seq = uevent_next_seqnum(); + retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq); + if (retval) + goto exit; diff --git a/target/linux/generic-2.6/patches-2.6.28/220-sound_kconfig.patch b/target/linux/generic-2.6/patches-2.6.28/220-sound_kconfig.patch new file mode 100644 index 0000000..f374009 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/220-sound_kconfig.patch @@ -0,0 +1,11 @@ +--- a/sound/core/Kconfig ++++ b/sound/core/Kconfig +@@ -7,7 +7,7 @@ config SND_PCM + select SND_TIMER + + config SND_HWDEP +- tristate ++ tristate "Sound hardware support" + + config SND_RAWMIDI + tristate diff --git a/target/linux/generic-2.6/patches-2.6.28/400-ledtrig_morse.patch b/target/linux/generic-2.6/patches-2.6.28/400-ledtrig_morse.patch new file mode 100644 index 0000000..8d3dd8e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/400-ledtrig_morse.patch @@ -0,0 +1,18 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -217,4 +217,8 @@ config LEDS_TRIGGER_DEFAULT_ON + This allows LEDs to be initialised in the ON state. + If unsure, say Y. + ++config LEDS_TRIGGER_MORSE ++ tristate "LED Morse Trigger" ++ depends on LEDS_TRIGGERS ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -30,3 +30,4 @@ obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += l + obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o + obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o ++obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o diff --git a/target/linux/generic-2.6/patches-2.6.28/401-led_alix.patch b/target/linux/generic-2.6/patches-2.6.28/401-led_alix.patch new file mode 100644 index 0000000..1601ac0 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/401-led_alix.patch @@ -0,0 +1,25 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -63,6 +63,12 @@ config LEDS_WRAP + help + This option enables support for the PCEngines WRAP programmable LEDs. + ++config LEDS_ALIX ++ tristate "LED Support for the ALIX 2/3 boards" ++ depends on LEDS_CLASS ++ help ++ This option enables support for the three LEDs on the PCEngines ALIX 2/3 boards. ++ + config LEDS_H1940 + tristate "LED Support for iPAQ H1940 device" + depends on LEDS_CLASS && ARCH_H1940 +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c2 + obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o + obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o + obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o ++obj-$(CONFIG_LEDS_ALIX) += leds-alix.o + obj-$(CONFIG_LEDS_H1940) += leds-h1940.o + obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o + obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o diff --git a/target/linux/generic-2.6/patches-2.6.28/402-ledtrig_netdev.patch b/target/linux/generic-2.6/patches-2.6.28/402-ledtrig_netdev.patch new file mode 100644 index 0000000..06ea4ac --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/402-ledtrig_netdev.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -227,4 +227,11 @@ config LEDS_TRIGGER_MORSE + tristate "LED Morse Trigger" + depends on LEDS_TRIGGERS + ++config LEDS_TRIGGER_NETDEV ++ tristate "LED Netdev Trigger" ++ depends on LEDS_TRIGGERS ++ help ++ This allows LEDs to be controlled by network device activity. ++ If unsure, say Y. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -32,3 +32,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += + obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o ++obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o diff --git a/target/linux/generic-2.6/patches-2.6.28/410-gpio_buttons.patch b/target/linux/generic-2.6/patches-2.6.28/410-gpio_buttons.patch new file mode 100644 index 0000000..198557a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/410-gpio_buttons.patch @@ -0,0 +1,30 @@ +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -220,4 +220,20 @@ config HP_SDC_RTC + Say Y here if you want to support the built-in real time clock + of the HP SDC controller. + ++config INPUT_GPIO_BUTTONS ++ tristate "Polled GPIO buttons interface" ++ depends on GENERIC_GPIO ++ select INPUT_POLLDEV ++ help ++ This driver implements support for buttons connected ++ to GPIO pins of various CPUs (and some other chips). ++ ++ Say Y here if your device has buttons connected ++ directly to such GPIO pins. Your board-specific ++ setup logic must also provide a platform device, ++ with configuration data saying which GPIOs are used. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called gpio-buttons. ++ + endif +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -21,3 +21,4 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc. + obj-$(CONFIG_INPUT_UINPUT) += uinput.o + obj-$(CONFIG_INPUT_APANEL) += apanel.o + obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o ++obj-$(CONFIG_INPUT_GPIO_BUTTONS) += gpio_buttons.o diff --git a/target/linux/generic-2.6/patches-2.6.28/420-gpiodev.patch b/target/linux/generic-2.6/patches-2.6.28/420-gpiodev.patch new file mode 100644 index 0000000..c7b665a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/420-gpiodev.patch @@ -0,0 +1,26 @@ +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -988,6 +988,13 @@ config CS5535_GPIO + + If compiled as a module, it will be called cs5535_gpio. + ++config GPIO_DEVICE ++ tristate "GPIO device support" ++ depends on GENERIC_GPIO ++ help ++ Say Y to enable Linux GPIO device support. This allows control of ++ GPIO pins using a character device ++ + config GPIO_VR41XX + tristate "NEC VR4100 series General-purpose I/O Unit support" + depends on CPU_VR41XX +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -92,6 +92,7 @@ obj-$(CONFIG_SCx200_GPIO) += scx200_gpio + obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o + obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o + obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o ++obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o + obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o + obj-$(CONFIG_GPIO_TB0219) += tb0219.o + obj-$(CONFIG_TELCLOCK) += tlclk.o diff --git a/target/linux/generic-2.6/patches-2.6.28/450-i2c_at24_add_kernel_interface_read_write.patch b/target/linux/generic-2.6/patches-2.6.28/450-i2c_at24_add_kernel_interface_read_write.patch new file mode 100644 index 0000000..e360db0 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/450-i2c_at24_add_kernel_interface_read_write.patch @@ -0,0 +1,134 @@ +Subject: [PATCH 2/3] I2C: at24: add kernel interface for reading/writing EEPROM +Date: Monday 25 August 2008 +From: Kevin Hilman <khilman@deeprootsystems.com> +To: davinci-linux-open-source@linux.davincidsp.com + +This patch adds an interface by which other kernel code can read/write +detected EEPROM. + +The platform code registers a 'setup' callback with the +at24_platform_data. When the at24 driver detects an EEPROM, it fills +out the read and write functions of at24_iface and calls the setup +callback. The platform code can then use the read/write functions in +the at24_iface struct for reading and writing the EEPROM. + +Original idea, review and updates by David Brownell <david-b@pacbell.net> + +Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com> +--- + drivers/i2c/chips/at24.c | 42 +++++++++++++++++++++++++++++++++++------- + include/linux/i2c/at24.h | 10 ++++++++++ + 2 files changed, 45 insertions(+), 7 deletions(-) + +--- a/drivers/i2c/chips/at24.c ++++ b/drivers/i2c/chips/at24.c +@@ -53,6 +53,7 @@ + + struct at24_data { + struct at24_platform_data chip; ++ struct at24_iface iface; + bool use_smbus; + + /* +@@ -264,13 +265,6 @@ static ssize_t at24_bin_read(struct kobj + + + /* +- * REVISIT: export at24_bin{read,write}() to let other kernel code use +- * eeprom data. For example, it might hold a board's Ethernet address, or +- * board-specific calibration data generated on the manufacturing floor. +- */ +- +- +-/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. +@@ -386,6 +380,30 @@ static ssize_t at24_bin_write(struct kob + + /*-------------------------------------------------------------------------*/ + ++/* ++ * This lets other kernel code access the eeprom data. For example, it ++ * might hold a board's Ethernet address, or board-specific calibration ++ * data generated on the manufacturing floor. ++ */ ++ ++static ssize_t at24_iface_read(struct at24_iface *iface, char *buf, ++ off_t offset, size_t count) ++{ ++ struct at24_data *at24 = container_of(iface, struct at24_data, iface); ++ ++ return at24_eeprom_read(at24, buf, offset, count); ++} ++ ++static ssize_t at24_iface_write(struct at24_iface *iface, char *buf, ++ off_t offset, size_t count) ++{ ++ struct at24_data *at24 = container_of(iface, struct at24_data, iface); ++ ++ return at24_eeprom_write(at24, buf, offset, count); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ + static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) + { + struct at24_platform_data chip; +@@ -413,6 +431,9 @@ static int at24_probe(struct i2c_client + * is recommended anyhow. + */ + chip.page_size = 1; ++ ++ chip.setup = NULL; ++ chip.context = NULL; + } + + if (!is_power_of_2(chip.byte_len)) +@@ -449,6 +470,9 @@ static int at24_probe(struct i2c_client + goto err_out; + } + ++ at24->iface.read = at24_iface_read; ++ at24->iface.write = at24_iface_write; ++ + mutex_init(&at24->lock); + at24->use_smbus = use_smbus; + at24->chip = chip; +@@ -520,6 +544,10 @@ static int at24_probe(struct i2c_client + at24->write_max, + use_smbus ? ", use_smbus" : ""); + ++ /* export data to kernel code */ ++ if (chip.setup) ++ chip.setup(&at24->iface, chip.context); ++ + return 0; + + err_clients: +--- a/include/linux/i2c/at24.h ++++ b/include/linux/i2c/at24.h +@@ -15,6 +15,13 @@ + * is bigger than what the chip actually supports! + */ + ++struct at24_iface { ++ ssize_t (*read)(struct at24_iface *, char *buf, off_t offset, ++ size_t count); ++ ssize_t (*write)(struct at24_iface *, char *buf, off_t offset, ++ size_t count); ++}; ++ + struct at24_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ +@@ -23,6 +30,9 @@ struct at24_platform_data { + #define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */ + #define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */ + #define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */ ++ ++ int (*setup)(struct at24_iface *, void *context); ++ void *context; + }; + + #endif /* _LINUX_AT24_H */ diff --git a/target/linux/generic-2.6/patches-2.6.28/510-yaffs_support.patch b/target/linux/generic-2.6/patches-2.6.28/510-yaffs_support.patch new file mode 100644 index 0000000..b564d3a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/510-yaffs_support.patch @@ -0,0 +1,17 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -182,6 +182,7 @@ config FILE_LOCKING + + source "fs/xfs/Kconfig" + source "fs/gfs2/Kconfig" ++source "fs/yaffs2/Kconfig" + + config OCFS2_FS + tristate "OCFS2 file system support" +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -124,3 +124,4 @@ obj-$(CONFIG_HPPFS) += hppfs/ + obj-$(CONFIG_DEBUG_FS) += debugfs/ + obj-$(CONFIG_OCFS2_FS) += ocfs2/ + obj-$(CONFIG_GFS2_FS) += gfs2/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ diff --git a/target/linux/generic-2.6/patches-2.6.28/512-yaffs_2.6.25_fix.patch b/target/linux/generic-2.6/patches-2.6.28/512-yaffs_2.6.25_fix.patch new file mode 100644 index 0000000..c127336 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/512-yaffs_2.6.25_fix.patch @@ -0,0 +1,92 @@ +--- a/fs/yaffs2/yaffs_fs.c ++++ b/fs/yaffs2/yaffs_fs.c +@@ -181,7 +181,13 @@ static int yaffs_statfs(struct super_blo + #else + static int yaffs_statfs(struct super_block *sb, struct statfs *buf); + #endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino); ++#else + static void yaffs_read_inode(struct inode *inode); ++#endif ++ + + static void yaffs_put_inode(struct inode *inode); + static void yaffs_delete_inode(struct inode *); +@@ -284,7 +290,9 @@ static struct file_operations yaffs_dir_ + + static struct super_operations yaffs_super_ops = { + .statfs = yaffs_statfs, ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) + .read_inode = yaffs_read_inode, ++#endif + .put_inode = yaffs_put_inode, + .put_super = yaffs_put_super, + .delete_inode = yaffs_delete_inode, +@@ -844,11 +852,17 @@ struct inode *yaffs_get_inode(struct sup + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_get_inode for object %d\n", obj->objectId)); + ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) ++ inode = yaffs_iget(sb, obj->objectId); ++ if (IS_ERR(inode)) ++ return NULL; ++#else + inode = iget(sb, obj->objectId); + + /* NB Side effect: iget calls back to yaffs_read_inode(). */ + /* iget also increments the inode's i_count */ + /* NB You can't be holding grossLock or deadlock will happen! */ ++#endif + + return inode; + } +@@ -1427,6 +1441,39 @@ static int yaffs_sync_fs(struct super_bl + } + + ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ yaffs_Object *obj; ++ yaffs_Device *dev = yaffs_SuperToDevice(sb); ++ struct inode *inode; ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_iget for %lu\n", ino)); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_GrossLock(dev); ++ ++ obj = yaffs_FindObjectByNumber(dev, inode->i_ino); ++ ++ yaffs_FillInodeFromObject(inode, obj); ++ ++ yaffs_GrossUnlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++#else + static void yaffs_read_inode(struct inode *inode) + { + /* NB This is called as a side effect of other functions, but +@@ -1448,6 +1495,7 @@ static void yaffs_read_inode(struct inod + + yaffs_GrossUnlock(dev); + } ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) */ + + static LIST_HEAD(yaffs_dev_list); + diff --git a/target/linux/generic-2.6/patches-2.6.28/513-yaffs_2.6.26_fix.patch b/target/linux/generic-2.6/patches-2.6.28/513-yaffs_2.6.26_fix.patch new file mode 100644 index 0000000..1159454 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/513-yaffs_2.6.26_fix.patch @@ -0,0 +1,69 @@ +--- a/fs/yaffs2/yaffs_fs.c ++++ b/fs/yaffs2/yaffs_fs.c +@@ -76,6 +76,12 @@ extern const char *yaffs_guts_c_version; + + #endif + ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) ++#define YPROC_ROOT &proc_root ++#else ++#define YPROC_ROOT NULL ++#endif ++ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + #define WRITE_SIZE_STR "writesize" + #define WRITE_SIZE(mtd) (mtd)->writesize +@@ -189,7 +195,9 @@ static void yaffs_read_inode(struct inod + #endif + + ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) + static void yaffs_put_inode(struct inode *inode); ++#endif + static void yaffs_delete_inode(struct inode *); + static void yaffs_clear_inode(struct inode *); + +@@ -293,7 +301,9 @@ static struct super_operations yaffs_sup + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) + .read_inode = yaffs_read_inode, + #endif ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) + .put_inode = yaffs_put_inode, ++#endif + .put_super = yaffs_put_super, + .delete_inode = yaffs_delete_inode, + .clear_inode = yaffs_clear_inode, +@@ -437,6 +447,7 @@ static struct dentry *yaffs_lookup(struc + + } + ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) + /* For now put inode is just for debugging + * Put inode is called when the inode **structure** is put. + */ +@@ -447,6 +458,7 @@ static void yaffs_put_inode(struct inode + atomic_read(&inode->i_count))); + + } ++#endif + + /* clear is called to tell the fs to release any per-inode data it holds */ + static void yaffs_clear_inode(struct inode *inode) +@@ -2279,7 +2291,7 @@ static int __init init_yaffs_fs(void) + /* Install the proc_fs entry */ + my_proc_entry = create_proc_entry("yaffs", + S_IRUGO | S_IFREG, +- &proc_root); ++ YPROC_ROOT); + + if (my_proc_entry) { + my_proc_entry->write_proc = yaffs_proc_write; +@@ -2325,7 +2337,7 @@ static void __exit exit_yaffs_fs(void) + T(YAFFS_TRACE_ALWAYS, ("yaffs " __DATE__ " " __TIME__ + " removing. \n")); + +- remove_proc_entry("yaffs", &proc_root); ++ remove_proc_entry("yaffs", YPROC_ROOT); + + fsinst = fs_to_install; + diff --git a/target/linux/generic-2.6/patches-2.6.28/600-phy_extension.patch b/target/linux/generic-2.6/patches-2.6.28/600-phy_extension.patch new file mode 100644 index 0000000..ed81bf3 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/600-phy_extension.patch @@ -0,0 +1,83 @@ +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -299,6 +299,50 @@ int phy_ethtool_gset(struct phy_device * + } + EXPORT_SYMBOL(phy_ethtool_gset); + ++int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr) ++{ ++ u32 cmd; ++ int tmp; ++ struct ethtool_cmd ecmd = { ETHTOOL_GSET }; ++ struct ethtool_value edata = { ETHTOOL_GLINK }; ++ ++ if (get_user(cmd, (u32 *) useraddr)) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case ETHTOOL_GSET: ++ phy_ethtool_gset(phydev, &ecmd); ++ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) ++ return -EFAULT; ++ return 0; ++ ++ case ETHTOOL_SSET: ++ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) ++ return -EFAULT; ++ return phy_ethtool_sset(phydev, &ecmd); ++ ++ case ETHTOOL_NWAY_RST: ++ /* if autoneg is off, it's an error */ ++ tmp = phy_read(phydev, MII_BMCR); ++ if (tmp & BMCR_ANENABLE) { ++ tmp |= (BMCR_ANRESTART); ++ phy_write(phydev, MII_BMCR, tmp); ++ return 0; ++ } ++ return -EINVAL; ++ ++ case ETHTOOL_GLINK: ++ edata.data = (phy_read(phydev, ++ MII_BMSR) & BMSR_LSTATUS) ? 1 : 0; ++ if (copy_to_user(useraddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ return -EOPNOTSUPP; ++} ++EXPORT_SYMBOL(phy_ethtool_ioctl); ++ + /** + * phy_mii_ioctl - generic PHY MII ioctl interface + * @phydev: the phy_device struct +@@ -355,8 +399,8 @@ int phy_mii_ioctl(struct phy_device *phy + } + + phy_write(phydev, mii_data->reg_num, val); +- +- if (mii_data->reg_num == MII_BMCR ++ ++ if (mii_data->reg_num == MII_BMCR + && val & BMCR_RESET + && phydev->drv->config_init) { + phy_scan_fixups(phydev); +@@ -476,7 +520,7 @@ static void phy_force_reduction(struct p + int idx; + + idx = phy_find_setting(phydev->speed, phydev->duplex); +- ++ + idx++; + + idx = phy_find_valid(idx, phydev->supported); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -476,6 +476,7 @@ void phy_start_machine(struct phy_device + void phy_stop_machine(struct phy_device *phydev); + int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); + int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); ++int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr); + int phy_mii_ioctl(struct phy_device *phydev, + struct mii_ioctl_data *mii_data, int cmd); + int phy_start_interrupts(struct phy_device *phydev); diff --git a/target/linux/generic-2.6/patches-2.6.28/610-phy_detect.patch b/target/linux/generic-2.6/patches-2.6.28/610-phy_detect.patch new file mode 100644 index 0000000..9790ab6 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/610-phy_detect.patch @@ -0,0 +1,26 @@ +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -273,6 +273,9 @@ static int mdio_bus_match(struct device + struct phy_device *phydev = to_phy_device(dev); + struct phy_driver *phydrv = to_phy_driver(drv); + ++ if (phydrv->detect) ++ return (phydrv->detect(phydev->bus, phydev->addr)); ++ + return ((phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->phy_id & phydrv->phy_id_mask)); + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -356,6 +356,11 @@ struct phy_driver { + u32 features; + u32 flags; + ++ /* Called during discovery to test if the ++ * device can attach to the bus, even if ++ * phy id and mask do not match */ ++ bool (*detect)(struct mii_bus *bus, int addr); ++ + /* + * Called to initialize the PHY, + * including after a reset diff --git a/target/linux/generic-2.6/patches-2.6.28/620-phy_adm6996.patch b/target/linux/generic-2.6/patches-2.6.28/620-phy_adm6996.patch new file mode 100644 index 0000000..7c98a0f --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/620-phy_adm6996.patch @@ -0,0 +1,24 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -66,6 +66,11 @@ config REALTEK_PHY + ---help--- + Supports the Realtek 821x PHY. + ++config ADM6996_PHY ++ tristate "Driver for ADM6996 switches" ++ ---help--- ++ Currently supports the ADM6996F switch ++ + config FIXED_PHY + bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" + depends on PHYLIB=y +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_SMSC_PHY) += smsc.o + obj-$(CONFIG_VITESSE_PHY) += vitesse.o + obj-$(CONFIG_BROADCOM_PHY) += broadcom.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o ++obj-$(CONFIG_ADM6996_PHY) += adm6996.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o + obj-$(CONFIG_FIXED_PHY) += fixed.o + obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o diff --git a/target/linux/generic-2.6/patches-2.6.28/630-phy_packets.patch b/target/linux/generic-2.6/patches-2.6.28/630-phy_packets.patch new file mode 100644 index 0000000..cc93307 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/630-phy_packets.patch @@ -0,0 +1,63 @@ +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -143,6 +143,18 @@ int phy_scan_fixups(struct phy_device *p + } + EXPORT_SYMBOL(phy_scan_fixups); + ++static int generic_receive_skb(struct sk_buff *skb) ++{ ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ return netif_receive_skb(skb); ++} ++ ++static int generic_rx(struct sk_buff *skb) ++{ ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ return netif_rx(skb); ++} ++ + struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) + { + struct phy_device *dev; +@@ -168,6 +180,8 @@ struct phy_device* phy_device_create(str + dev->bus = bus; + + dev->state = PHY_DOWN; ++ dev->netif_receive_skb = &generic_receive_skb; ++ dev->netif_rx = &generic_rx; + + mutex_init(&dev->lock); + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -326,6 +326,20 @@ struct phy_device { + void (*adjust_link)(struct net_device *dev); + + void (*adjust_state)(struct net_device *dev); ++ ++ /* ++ * By default these point to the original functions ++ * with the same name. adding them to the phy_device ++ * allows the phy driver to override them for packet ++ * mangling if the ethernet driver supports it ++ * This is required to support some really horrible ++ * switches such as the Marvell 88E6060 ++ */ ++ int (*netif_receive_skb)(struct sk_buff *skb); ++ int (*netif_rx)(struct sk_buff *skb); ++ ++ /* alignment offset for packets */ ++ int pkt_align; + }; + #define to_phy_device(d) container_of(d, struct phy_device, dev) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -627,6 +627,7 @@ struct net_device + void *ax25_ptr; /* AX.25 specific data */ + struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data, + assign before registering */ ++ void *phy_ptr; /* PHY device specific data */ + + /* + * Cache line mostly used on receive path (including eth_type_trans()) diff --git a/target/linux/generic-2.6/patches-2.6.28/640-mvswitch.patch b/target/linux/generic-2.6/patches-2.6.28/640-mvswitch.patch new file mode 100644 index 0000000..eeaa672 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/640-mvswitch.patch @@ -0,0 +1,25 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -71,6 +71,12 @@ config ADM6996_PHY + ---help--- + Currently supports the ADM6996F switch + ++config MVSWITCH_PHY ++ tristate "Driver for Marvell switches" ++ select VLAN_8021Q ++ ---help--- ++ Currently supports the Marvell 88E6060 switch. ++ + config FIXED_PHY + bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" + depends on PHYLIB=y +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -13,6 +13,7 @@ obj-$(CONFIG_VITESSE_PHY) += vitesse.o + obj-$(CONFIG_BROADCOM_PHY) += broadcom.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o + obj-$(CONFIG_ADM6996_PHY) += adm6996.o ++obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o + obj-$(CONFIG_FIXED_PHY) += fixed.o + obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o diff --git a/target/linux/generic-2.6/patches-2.6.28/650-swconfig.patch b/target/linux/generic-2.6/patches-2.6.28/650-swconfig.patch new file mode 100644 index 0000000..6825037 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/650-swconfig.patch @@ -0,0 +1,25 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -13,6 +13,12 @@ menuconfig PHYLIB + + if PHYLIB + ++config SWCONFIG ++ tristate "Switch configuration API" ++ ---help--- ++ Switch configuration API using netlink. This allows ++ you to configure the VLAN features of certain switches. ++ + comment "MII PHY device drivers" + + config MARVELL_PHY +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -3,6 +3,7 @@ + libphy-objs := phy.o phy_device.o mdio_bus.o + + obj-$(CONFIG_PHYLIB) += libphy.o ++obj-$(CONFIG_SWCONFIG) += swconfig.o + obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_DAVICOM_PHY) += davicom.o + obj-$(CONFIG_CICADA_PHY) += cicada.o diff --git a/target/linux/generic-2.6/patches-2.6.28/801-usb_serial_endpoint_size.patch b/target/linux/generic-2.6/patches-2.6.28/801-usb_serial_endpoint_size.patch new file mode 100644 index 0000000..827c350 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/801-usb_serial_endpoint_size.patch @@ -0,0 +1,25 @@ +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -59,6 +59,7 @@ static struct usb_driver usb_serial_driv + drivers depend on it. + */ + ++static ushort maxSize = 0; + static int debug; + /* initially all NULL */ + static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; +@@ -839,7 +840,7 @@ int usb_serial_probe(struct usb_interfac + dev_err(&interface->dev, "No free urbs available\n"); + goto probe_error; + } +- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); ++ buffer_size = (endpoint->wMaxPacketSize > maxSize) ? endpoint->wMaxPacketSize : maxSize; + port->bulk_in_size = buffer_size; + port->bulk_in_endpointAddress = endpoint->bEndpointAddress; + port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); +@@ -1268,3 +1269,5 @@ MODULE_LICENSE("GPL"); + + module_param(debug, bool, S_IRUGO | S_IWUSR); + MODULE_PARM_DESC(debug, "Debug enabled or not"); ++module_param(maxSize, ushort,0); ++MODULE_PARM_DESC(maxSize,"User specified USB endpoint size"); diff --git a/target/linux/generic-2.6/patches-2.6.28/840-unable_to_open_console.patch b/target/linux/generic-2.6/patches-2.6.28/840-unable_to_open_console.patch new file mode 100644 index 0000000..b109b0e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/840-unable_to_open_console.patch @@ -0,0 +1,11 @@ +--- a/init/main.c ++++ b/init/main.c +@@ -801,7 +801,7 @@ static int noinline init_post(void) + numa_default_policy(); + + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) +- printk(KERN_WARNING "Warning: unable to open an initial console.\n"); ++ printk(KERN_WARNING "Please be patient, while OpenWrt loads ...\n"); + + (void) sys_dup(0); + (void) sys_dup(0); diff --git a/target/linux/generic-2.6/patches-2.6.28/900-headers_type_and_time.patch b/target/linux/generic-2.6/patches-2.6.28/900-headers_type_and_time.patch new file mode 100644 index 0000000..4127cd4 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/900-headers_type_and_time.patch @@ -0,0 +1,46 @@ +--- a/include/linux/time.h ++++ b/include/linux/time.h +@@ -1,6 +1,10 @@ + #ifndef _LINUX_TIME_H + #define _LINUX_TIME_H + ++#ifndef __KERNEL__ ++#include <time.h> ++#else ++ + #include <linux/types.h> + + #ifdef __KERNEL__ +@@ -239,4 +243,6 @@ struct itimerval { + */ + #define TIMER_ABSTIME 0x01 + ++#endif /* __KERNEL__ DEBIAN */ ++ + #endif +--- a/include/linux/types.h ++++ b/include/linux/types.h +@@ -1,6 +1,14 @@ + #ifndef _LINUX_TYPES_H + #define _LINUX_TYPES_H + ++/* Debian: Use userland types instead. */ ++#ifndef __KERNEL__ ++# include <sys/types.h> ++/* For other kernel headers. */ ++# include <linux/posix_types.h> ++# include <asm/types.h> ++#else ++ + #ifdef __KERNEL__ + + #define DECLARE_BITMAP(name,bits) \ +@@ -161,6 +169,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.28/902-darwin_scripts_include.patch b/target/linux/generic-2.6/patches-2.6.28/902-darwin_scripts_include.patch new file mode 100644 index 0000000..c5aadd1 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/902-darwin_scripts_include.patch @@ -0,0 +1,102 @@ +--- a/scripts/genksyms/parse.c_shipped ++++ b/scripts/genksyms/parse.c_shipped +@@ -160,7 +160,9 @@ + + + #include <assert.h> ++#ifndef __APPLE__ + #include <malloc.h> ++#endif + #include "genksyms.h" + + static int is_typedef; +--- a/scripts/genksyms/parse.y ++++ b/scripts/genksyms/parse.y +@@ -24,7 +24,9 @@ + %{ + + #include <assert.h> ++#ifndef __APPLE__ + #include <malloc.h> ++#endif + #include "genksyms.h" + + static int is_typedef; +--- a/scripts/kallsyms.c ++++ b/scripts/kallsyms.c +@@ -22,6 +22,35 @@ + #include <stdlib.h> + #include <string.h> + #include <ctype.h> ++#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 128 + +--- a/scripts/kconfig/Makefile ++++ b/scripts/kconfig/Makefile +@@ -97,6 +97,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 + +--- a/scripts/mod/mk_elfconfig.c ++++ b/scripts/mod/mk_elfconfig.c +@@ -1,7 +1,11 @@ + #include <stdio.h> + #include <stdlib.h> + #include <string.h> ++#ifndef __APPLE__ + #include <elf.h> ++#else ++#include "../../../../../tools/sstrip/include/elf.h" ++#endif + + int + main(int argc, char **argv) +--- a/scripts/mod/modpost.h ++++ b/scripts/mod/modpost.h +@@ -7,7 +7,11 @@ + #include <sys/mman.h> + #include <fcntl.h> + #include <unistd.h> ++#ifndef __APPLE__ + #include <elf.h> ++#else ++#include "../../../../../tools/sstrip/include/elf.h" ++#endif + + #include "elfconfig.h" + diff --git a/target/linux/generic-2.6/patches-2.6.28/903-hostap_txpower.patch b/target/linux/generic-2.6/patches-2.6.28/903-hostap_txpower.patch new file mode 100644 index 0000000..6b4fc9f --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/903-hostap_txpower.patch @@ -0,0 +1,154 @@ +--- a/drivers/net/wireless/hostap/hostap_ap.c ++++ b/drivers/net/wireless/hostap/hostap_ap.c +@@ -2397,13 +2397,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; +@@ -2469,13 +2469,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(info, current_ev, end_buf, +--- a/drivers/net/wireless/hostap/hostap_config.h ++++ b/drivers/net/wireless/hostap/hostap_config.h +@@ -45,4 +45,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 */ +--- a/drivers/net/wireless/hostap/hostap.h ++++ b/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 */ +--- a/drivers/net/wireless/hostap/hostap_hw.c ++++ b/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; + } + +--- a/drivers/net/wireless/hostap/hostap_info.c ++++ b/drivers/net/wireless/hostap/hostap_info.c +@@ -434,6 +434,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); +--- a/drivers/net/wireless/hostap/hostap_ioctl.c ++++ b/drivers/net/wireless/hostap/hostap_ioctl.c +@@ -1502,23 +1502,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; + } +@@ -4083,3 +4080,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.28/903-stddef_include.patch b/target/linux/generic-2.6/patches-2.6.28/903-stddef_include.patch new file mode 100644 index 0000000..7fe248d --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/903-stddef_include.patch @@ -0,0 +1,17 @@ +--- a/include/linux/stddef.h ++++ b/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.28/905-i386_build.patch b/target/linux/generic-2.6/patches-2.6.28/905-i386_build.patch new file mode 100644 index 0000000..c701fda --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/905-i386_build.patch @@ -0,0 +1,10 @@ +--- a/arch/x86/boot/tools/build.c ++++ b/arch/x86/boot/tools/build.c +@@ -29,7 +29,6 @@ + #include <stdarg.h> + #include <sys/types.h> + #include <sys/stat.h> +-#include <sys/sysmacros.h> + #include <unistd.h> + #include <fcntl.h> + #include <sys/mman.h> diff --git a/target/linux/generic-2.6/patches-2.6.28/921-gpio_spi_driver.patch b/target/linux/generic-2.6/patches-2.6.28/921-gpio_spi_driver.patch new file mode 100644 index 0000000..d55eda6 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/921-gpio_spi_driver.patch @@ -0,0 +1,376 @@ +--- /dev/null ++++ b/include/linux/spi/spi_gpio.h +@@ -0,0 +1,73 @@ ++/* ++ * spi_gpio interface to platform code ++ * ++ * Copyright (c) 2008 Piotr Skamruk ++ * Copyright (c) 2008 Michael Buesch ++ * ++ * 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. ++ */ ++#ifndef _LINUX_SPI_SPI_GPIO ++#define _LINUX_SPI_SPI_GPIO ++ ++#include <linux/types.h> ++#include <linux/spi/spi.h> ++ ++ ++/** ++ * struct spi_gpio_platform_data - Data definitions for a SPI-GPIO device. ++ * ++ * This structure holds information about a GPIO-based SPI device. ++ * ++ * @pin_clk: The GPIO pin number of the CLOCK pin. ++ * ++ * @pin_miso: The GPIO pin number of the MISO pin. ++ * ++ * @pin_mosi: The GPIO pin number of the MOSI pin. ++ * ++ * @pin_cs: The GPIO pin number of the CHIPSELECT pin. ++ * ++ * @cs_activelow: If true, the chip is selected when the CS line is low. ++ * ++ * @no_spi_delay: If true, no delay is done in the lowlevel bitbanging. ++ * Note that doing no delay is not standards compliant, ++ * but it might be needed to speed up transfers on some ++ * slow embedded machines. ++ * ++ * @boardinfo_setup: This callback is called after the ++ * SPI master device was registered, but before the ++ * device is registered. ++ * @boardinfo_setup_data: Data argument passed to boardinfo_setup(). ++ */ ++struct spi_gpio_platform_data { ++ unsigned int pin_clk; ++ unsigned int pin_miso; ++ unsigned int pin_mosi; ++ unsigned int pin_cs; ++ bool cs_activelow; ++ bool no_spi_delay; ++ int (*boardinfo_setup)(struct spi_board_info *bi, ++ struct spi_master *master, ++ void *data); ++ void *boardinfo_setup_data; ++}; ++ ++/** ++ * SPI_GPIO_PLATDEV_NAME - The platform device name string. ++ * ++ * The name string that has to be used for platform_device_alloc ++ * when allocating a spi-gpio device. ++ */ ++#define SPI_GPIO_PLATDEV_NAME "spi-gpio" ++ ++/** ++ * spi_gpio_next_id - Get another platform device ID number. ++ * ++ * This returns the next platform device ID number that has to be used ++ * for platform_device_alloc. The ID is opaque and should not be used for ++ * anything else. ++ */ ++int spi_gpio_next_id(void); ++ ++#endif /* _LINUX_SPI_SPI_GPIO */ +--- /dev/null ++++ b/drivers/spi/spi_gpio.c +@@ -0,0 +1,251 @@ ++/* ++ * Bitbanging SPI bus driver using GPIO API ++ * ++ * Copyright (c) 2008 Piotr Skamruk ++ * Copyright (c) 2008 Michael Buesch ++ * ++ * based on spi_s3c2410_gpio.c ++ * Copyright (c) 2006 Ben Dooks ++ * Copyright (c) 2006 Simtec Electronics ++ * and on i2c-gpio.c ++ * Copyright (C) 2007 Atmel Corporation ++ * ++ * 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 <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/spi_bitbang.h> ++#include <linux/spi/spi_gpio.h> ++#include <linux/gpio.h> ++#include <asm/atomic.h> ++ ++ ++struct spi_gpio { ++ struct spi_bitbang bitbang; ++ struct spi_gpio_platform_data *info; ++ struct platform_device *pdev; ++ struct spi_board_info bi; ++}; ++ ++ ++static inline struct spi_gpio *spidev_to_sg(struct spi_device *dev) ++{ ++ return dev->controller_data; ++} ++ ++static inline void setsck(struct spi_device *dev, int val) ++{ ++ struct spi_gpio *sp = spidev_to_sg(dev); ++ gpio_set_value(sp->info->pin_clk, val ? 1 : 0); ++} ++ ++static inline void setmosi(struct spi_device *dev, int val) ++{ ++ struct spi_gpio *sp = spidev_to_sg(dev); ++ gpio_set_value(sp->info->pin_mosi, val ? 1 : 0); ++} ++ ++static inline u32 getmiso(struct spi_device *dev) ++{ ++ struct spi_gpio *sp = spidev_to_sg(dev); ++ return gpio_get_value(sp->info->pin_miso) ? 1 : 0; ++} ++ ++static inline void do_spidelay(struct spi_device *dev, unsigned nsecs) ++{ ++ struct spi_gpio *sp = spidev_to_sg(dev); ++ ++ if (!sp->info->no_spi_delay) ++ ndelay(nsecs); ++} ++ ++#define spidelay(nsecs) do { \ ++ /* Steal the spi_device pointer from our caller. \ ++ * The bitbang-API should probably get fixed here... */ \ ++ do_spidelay(spi, nsecs); \ ++ } while (0) ++ ++#define EXPAND_BITBANG_TXRX ++#include <linux/spi/spi_bitbang.h> ++ ++static u32 spi_gpio_txrx_mode0(struct spi_device *spi, ++ unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); ++} ++ ++static u32 spi_gpio_txrx_mode1(struct spi_device *spi, ++ unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); ++} ++ ++static u32 spi_gpio_txrx_mode2(struct spi_device *spi, ++ unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); ++} ++ ++static u32 spi_gpio_txrx_mode3(struct spi_device *spi, ++ unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); ++} ++ ++static void spi_gpio_chipselect(struct spi_device *dev, int on) ++{ ++ struct spi_gpio *sp = spidev_to_sg(dev); ++ ++ if (sp->info->cs_activelow) ++ on = !on; ++ gpio_set_value(sp->info->pin_cs, on ? 1 : 0); ++} ++ ++static int spi_gpio_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct spi_gpio_platform_data *pdata; ++ struct spi_gpio *sp; ++ struct spi_device *spidev; ++ int err; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) ++ return -ENXIO; ++ ++ err = -ENOMEM; ++ master = spi_alloc_master(&pdev->dev, sizeof(struct spi_gpio)); ++ if (!master) ++ goto err_alloc_master; ++ ++ sp = spi_master_get_devdata(master); ++ platform_set_drvdata(pdev, sp); ++ sp->info = pdata; ++ ++ err = gpio_request(pdata->pin_clk, "spi_clock"); ++ if (err) ++ goto err_request_clk; ++ err = gpio_request(pdata->pin_mosi, "spi_mosi"); ++ if (err) ++ goto err_request_mosi; ++ err = gpio_request(pdata->pin_miso, "spi_miso"); ++ if (err) ++ goto err_request_miso; ++ err = gpio_request(pdata->pin_cs, "spi_cs"); ++ if (err) ++ goto err_request_cs; ++ ++ sp->bitbang.master = spi_master_get(master); ++ sp->bitbang.master->bus_num = -1; ++ sp->bitbang.master->num_chipselect = 1; ++ sp->bitbang.chipselect = spi_gpio_chipselect; ++ sp->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_mode0; ++ sp->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_mode1; ++ sp->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_mode2; ++ sp->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_mode3; ++ ++ gpio_direction_output(pdata->pin_clk, 0); ++ gpio_direction_output(pdata->pin_mosi, 0); ++ gpio_direction_output(pdata->pin_cs, ++ pdata->cs_activelow ? 1 : 0); ++ gpio_direction_input(pdata->pin_miso); ++ ++ err = spi_bitbang_start(&sp->bitbang); ++ if (err) ++ goto err_no_bitbang; ++ err = pdata->boardinfo_setup(&sp->bi, master, ++ pdata->boardinfo_setup_data); ++ if (err) ++ goto err_bi_setup; ++ sp->bi.controller_data = sp; ++ spidev = spi_new_device(master, &sp->bi); ++ if (!spidev) ++ goto err_new_dev; ++ ++ return 0; ++ ++err_new_dev: ++err_bi_setup: ++ spi_bitbang_stop(&sp->bitbang); ++err_no_bitbang: ++ spi_master_put(sp->bitbang.master); ++ gpio_free(pdata->pin_cs); ++err_request_cs: ++ gpio_free(pdata->pin_miso); ++err_request_miso: ++ gpio_free(pdata->pin_mosi); ++err_request_mosi: ++ gpio_free(pdata->pin_clk); ++err_request_clk: ++ kfree(master); ++ ++err_alloc_master: ++ return err; ++} ++ ++static int __devexit spi_gpio_remove(struct platform_device *pdev) ++{ ++ struct spi_gpio *sp; ++ struct spi_gpio_platform_data *pdata; ++ ++ pdata = pdev->dev.platform_data; ++ sp = platform_get_drvdata(pdev); ++ ++ gpio_free(pdata->pin_clk); ++ gpio_free(pdata->pin_mosi); ++ gpio_free(pdata->pin_miso); ++ gpio_free(pdata->pin_cs); ++ spi_bitbang_stop(&sp->bitbang); ++ spi_master_put(sp->bitbang.master); ++ ++ return 0; ++} ++ ++static struct platform_driver spi_gpio_driver = { ++ .driver = { ++ .name = SPI_GPIO_PLATDEV_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = spi_gpio_probe, ++ .remove = __devexit_p(spi_gpio_remove), ++}; ++ ++int spi_gpio_next_id(void) ++{ ++ static atomic_t counter = ATOMIC_INIT(-1); ++ ++ return atomic_inc_return(&counter); ++} ++EXPORT_SYMBOL(spi_gpio_next_id); ++ ++static int __init spi_gpio_init(void) ++{ ++ int err; ++ ++ err = platform_driver_register(&spi_gpio_driver); ++ if (err) ++ printk(KERN_ERR "spi-gpio: register failed: %d\n", err); ++ ++ return err; ++} ++module_init(spi_gpio_init); ++ ++static void __exit spi_gpio_exit(void) ++{ ++ platform_driver_unregister(&spi_gpio_driver); ++} ++module_exit(spi_gpio_exit); ++ ++MODULE_AUTHOR("Piot Skamruk <piotr.skamruk at gmail.com>"); ++MODULE_AUTHOR("Michael Buesch"); ++MODULE_DESCRIPTION("Platform independent GPIO bitbanging SPI driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -100,6 +100,19 @@ config SPI_BUTTERFLY + inexpensive battery powered microcontroller evaluation board. + This same cable can be used to flash new firmware. + ++config SPI_GPIO ++ tristate "GPIO API based bitbanging SPI controller" ++ depends on SPI_MASTER && GENERIC_GPIO ++ select SPI_BITBANG ++ help ++ This is a platform driver that can be used for bitbanging ++ an SPI bus over GPIO pins. ++ Select this if you have any SPI device that is connected via ++ GPIO pins. ++ The module will be called spi_gpio. ++ ++ If unsure, say N. ++ + config SPI_IMX + tristate "Freescale iMX SPI controller" + depends on ARCH_IMX && EXPERIMENTAL +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx. + obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o + obj-$(CONFIG_SPI_AU1550) += au1550_spi.o + obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o ++obj-$(CONFIG_SPI_GPIO) += spi_gpio.o + obj-$(CONFIG_SPI_IMX) += spi_imx.o + obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o + obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -4017,6 +4017,11 @@ M: jbglaw@lug-owl.de + L: linux-kernel@vger.kernel.org + S: Maintained + ++SPI GPIO MASTER DRIVER ++P: Michael Buesch ++M: mb@bu3sch.de ++S: Maintained ++ + STABLE BRANCH + P: Greg Kroah-Hartman + M: greg@kroah.com diff --git a/target/linux/generic-2.6/patches-2.6.28/922-gpiommc.patch b/target/linux/generic-2.6/patches-2.6.28/922-gpiommc.patch new file mode 100644 index 0000000..cc16a10 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/922-gpiommc.patch @@ -0,0 +1,838 @@ +--- /dev/null ++++ b/drivers/mmc/host/gpiommc.c +@@ -0,0 +1,608 @@ ++/* ++ * Driver an MMC/SD card on a bitbanging GPIO SPI bus. ++ * This module hooks up the mmc_spi and spi_gpio modules and also ++ * provides a configfs interface. ++ * ++ * Copyright 2008 Michael Buesch <mb@bu3sch.de> ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include <linux/mmc/gpiommc.h> ++#include <linux/platform_device.h> ++#include <linux/list.h> ++#include <linux/mutex.h> ++#include <linux/spi/spi_gpio.h> ++#include <linux/configfs.h> ++#include <linux/gpio.h> ++#include <asm/atomic.h> ++ ++ ++#define PFX "gpio-mmc: " ++ ++ ++struct gpiommc_device { ++ struct platform_device *pdev; ++ struct platform_device *spi_pdev; ++ struct spi_board_info boardinfo; ++}; ++ ++ ++MODULE_DESCRIPTION("GPIO based MMC driver"); ++MODULE_AUTHOR("Michael Buesch"); ++MODULE_LICENSE("GPL"); ++ ++ ++static int gpiommc_boardinfo_setup(struct spi_board_info *bi, ++ struct spi_master *master, ++ void *data) ++{ ++ struct gpiommc_device *d = data; ++ struct gpiommc_platform_data *pdata = d->pdev->dev.platform_data; ++ ++ /* Bind the SPI master to the MMC-SPI host driver. */ ++ strlcpy(bi->modalias, "mmc_spi", sizeof(bi->modalias)); ++ ++ bi->max_speed_hz = pdata->max_bus_speed; ++ bi->bus_num = master->bus_num; ++ bi->mode = pdata->mode; ++ ++ return 0; ++} ++ ++static int gpiommc_probe(struct platform_device *pdev) ++{ ++ struct gpiommc_platform_data *mmc_pdata = pdev->dev.platform_data; ++ struct spi_gpio_platform_data spi_pdata; ++ struct gpiommc_device *d; ++ int err; ++ ++ err = -ENXIO; ++ if (!mmc_pdata) ++ goto error; ++ ++#ifdef CONFIG_MMC_SPI_MODULE ++ err = request_module("mmc_spi"); ++ if (err) { ++ printk(KERN_WARNING PFX ++ "Failed to request mmc_spi module.\n"); ++ } ++#endif /* CONFIG_MMC_SPI_MODULE */ ++ ++ /* Allocate the GPIO-MMC device */ ++ err = -ENOMEM; ++ d = kzalloc(sizeof(*d), GFP_KERNEL); ++ if (!d) ++ goto error; ++ d->pdev = pdev; ++ ++ /* Create the SPI-GPIO device */ ++ d->spi_pdev = platform_device_alloc(SPI_GPIO_PLATDEV_NAME, ++ spi_gpio_next_id()); ++ if (!d->spi_pdev) ++ goto err_free_d; ++ ++ memset(&spi_pdata, 0, sizeof(spi_pdata)); ++ spi_pdata.pin_clk = mmc_pdata->pins.gpio_clk; ++ spi_pdata.pin_miso = mmc_pdata->pins.gpio_do; ++ spi_pdata.pin_mosi = mmc_pdata->pins.gpio_di; ++ spi_pdata.pin_cs = mmc_pdata->pins.gpio_cs; ++ spi_pdata.cs_activelow = mmc_pdata->pins.cs_activelow; ++ spi_pdata.no_spi_delay = mmc_pdata->no_spi_delay; ++ spi_pdata.boardinfo_setup = gpiommc_boardinfo_setup; ++ spi_pdata.boardinfo_setup_data = d; ++ ++ err = platform_device_add_data(d->spi_pdev, &spi_pdata, ++ sizeof(spi_pdata)); ++ if (err) ++ goto err_free_pdev; ++ err = platform_device_add(d->spi_pdev); ++ if (err) ++ goto err_free_pdata; ++ platform_set_drvdata(pdev, d); ++ ++ printk(KERN_INFO PFX "MMC-Card \"%s\" " ++ "attached to GPIO pins di=%u, do=%u, clk=%u, cs=%u\n", ++ mmc_pdata->name, mmc_pdata->pins.gpio_di, ++ mmc_pdata->pins.gpio_do, ++ mmc_pdata->pins.gpio_clk, ++ mmc_pdata->pins.gpio_cs); ++ ++ return 0; ++ ++err_free_pdata: ++ kfree(d->spi_pdev->dev.platform_data); ++ d->spi_pdev->dev.platform_data = NULL; ++err_free_pdev: ++ platform_device_put(d->spi_pdev); ++err_free_d: ++ kfree(d); ++error: ++ return err; ++} ++ ++static int gpiommc_remove(struct platform_device *pdev) ++{ ++ struct gpiommc_device *d = platform_get_drvdata(pdev); ++ struct gpiommc_platform_data *pdata = d->pdev->dev.platform_data; ++ ++ platform_device_unregister(d->spi_pdev); ++ printk(KERN_INFO PFX "GPIO based MMC-Card \"%s\" removed\n", ++ pdata->name); ++ platform_device_put(d->spi_pdev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_GPIOMMC_CONFIGFS ++ ++/* A device that was created through configfs */ ++struct gpiommc_configfs_device { ++ struct config_item item; ++ /* The platform device, after registration. */ ++ struct platform_device *pdev; ++ /* The configuration */ ++ struct gpiommc_platform_data pdata; ++}; ++ ++#define GPIO_INVALID -1 ++ ++static inline bool gpiommc_is_registered(struct gpiommc_configfs_device *dev) ++{ ++ return (dev->pdev != NULL); ++} ++ ++static inline struct gpiommc_configfs_device *ci_to_gpiommc(struct config_item *item) ++{ ++ return item ? container_of(item, struct gpiommc_configfs_device, item) : NULL; ++} ++ ++static struct configfs_attribute gpiommc_attr_DI = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "gpio_data_in", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_DO = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "gpio_data_out", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_CLK = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "gpio_clock", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_CS = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "gpio_chipselect", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_CS_activelow = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "gpio_chipselect_activelow", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_spimode = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "spi_mode", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_spidelay = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "spi_delay", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_max_bus_speed = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "max_bus_speed", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute gpiommc_attr_register = { ++ .ca_owner = THIS_MODULE, ++ .ca_name = "register", ++ .ca_mode = S_IRUGO | S_IWUSR, ++}; ++ ++static struct configfs_attribute *gpiommc_config_attrs[] = { ++ &gpiommc_attr_DI, ++ &gpiommc_attr_DO, ++ &gpiommc_attr_CLK, ++ &gpiommc_attr_CS, ++ &gpiommc_attr_CS_activelow, ++ &gpiommc_attr_spimode, ++ &gpiommc_attr_spidelay, ++ &gpiommc_attr_max_bus_speed, ++ &gpiommc_attr_register, ++ NULL, ++}; ++ ++static ssize_t gpiommc_config_attr_show(struct config_item *item, ++ struct configfs_attribute *attr, ++ char *page) ++{ ++ struct gpiommc_configfs_device *dev = ci_to_gpiommc(item); ++ ssize_t count = 0; ++ unsigned int gpio; ++ int err = 0; ++ ++ if (attr == &gpiommc_attr_DI) { ++ gpio = dev->pdata.pins.gpio_di; ++ if (gpio == GPIO_INVALID) ++ count = snprintf(page, PAGE_SIZE, "not configured\n"); ++ else ++ count = snprintf(page, PAGE_SIZE, "%u\n", gpio); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_DO) { ++ gpio = dev->pdata.pins.gpio_do; ++ if (gpio == GPIO_INVALID) ++ count = snprintf(page, PAGE_SIZE, "not configured\n"); ++ else ++ count = snprintf(page, PAGE_SIZE, "%u\n", gpio); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_CLK) { ++ gpio = dev->pdata.pins.gpio_clk; ++ if (gpio == GPIO_INVALID) ++ count = snprintf(page, PAGE_SIZE, "not configured\n"); ++ else ++ count = snprintf(page, PAGE_SIZE, "%u\n", gpio); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_CS) { ++ gpio = dev->pdata.pins.gpio_cs; ++ if (gpio == GPIO_INVALID) ++ count = snprintf(page, PAGE_SIZE, "not configured\n"); ++ else ++ count = snprintf(page, PAGE_SIZE, "%u\n", gpio); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_CS_activelow) { ++ count = snprintf(page, PAGE_SIZE, "%u\n", ++ dev->pdata.pins.cs_activelow); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_spimode) { ++ count = snprintf(page, PAGE_SIZE, "%u\n", ++ dev->pdata.mode); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_spidelay) { ++ count = snprintf(page, PAGE_SIZE, "%u\n", ++ !dev->pdata.no_spi_delay); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_max_bus_speed) { ++ count = snprintf(page, PAGE_SIZE, "%u\n", ++ dev->pdata.max_bus_speed); ++ goto out; ++ } ++ if (attr == &gpiommc_attr_register) { ++ count = snprintf(page, PAGE_SIZE, "%u\n", ++ gpiommc_is_registered(dev)); ++ goto out; ++ } ++ WARN_ON(1); ++ err = -ENOSYS; ++out: ++ return err ? err : count; ++} ++ ++static int gpiommc_do_register(struct gpiommc_configfs_device *dev, ++ const char *name) ++{ ++ int err; ++ ++ if (gpiommc_is_registered(dev)) ++ return 0; ++ ++ if (!gpio_is_valid(dev->pdata.pins.gpio_di) || ++ !gpio_is_valid(dev->pdata.pins.gpio_do) || ++ !gpio_is_valid(dev->pdata.pins.gpio_clk) || ++ !gpio_is_valid(dev->pdata.pins.gpio_cs)) { ++ printk(KERN_ERR PFX ++ "configfs: Invalid GPIO pin number(s)\n"); ++ return -EINVAL; ++ } ++ ++ strlcpy(dev->pdata.name, name, ++ sizeof(dev->pdata.name)); ++ ++ dev->pdev = platform_device_alloc(GPIOMMC_PLATDEV_NAME, ++ gpiommc_next_id()); ++ if (!dev->pdev) ++ return -ENOMEM; ++ err = platform_device_add_data(dev->pdev, &dev->pdata, ++ sizeof(dev->pdata)); ++ if (err) { ++ platform_device_put(dev->pdev); ++ return err; ++ } ++ err = platform_device_add(dev->pdev); ++ if (err) { ++ platform_device_put(dev->pdev); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void gpiommc_do_unregister(struct gpiommc_configfs_device *dev) ++{ ++ if (!gpiommc_is_registered(dev)) ++ return; ++ ++ platform_device_unregister(dev->pdev); ++ dev->pdev = NULL; ++} ++ ++static ssize_t gpiommc_config_attr_store(struct config_item *item, ++ struct configfs_attribute *attr, ++ const char *page, size_t count) ++{ ++ struct gpiommc_configfs_device *dev = ci_to_gpiommc(item); ++ int err = -EINVAL; ++ unsigned long data; ++ ++ if (attr == &gpiommc_attr_register) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (data == 1) ++ err = gpiommc_do_register(dev, item->ci_name); ++ if (data == 0) { ++ gpiommc_do_unregister(dev); ++ err = 0; ++ } ++ goto out; ++ } ++ ++ if (gpiommc_is_registered(dev)) { ++ /* The rest of the config parameters can only be set ++ * as long as the device is not registered, yet. */ ++ err = -EBUSY; ++ goto out; ++ } ++ ++ if (attr == &gpiommc_attr_DI) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (!gpio_is_valid(data)) ++ goto out; ++ dev->pdata.pins.gpio_di = data; ++ err = 0; ++ goto out; ++ } ++ if (attr == &gpiommc_attr_DO) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (!gpio_is_valid(data)) ++ goto out; ++ dev->pdata.pins.gpio_do = data; ++ err = 0; ++ goto out; ++ } ++ if (attr == &gpiommc_attr_CLK) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (!gpio_is_valid(data)) ++ goto out; ++ dev->pdata.pins.gpio_clk = data; ++ err = 0; ++ goto out; ++ } ++ if (attr == &gpiommc_attr_CS) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (!gpio_is_valid(data)) ++ goto out; ++ dev->pdata.pins.gpio_cs = data; ++ err = 0; ++ goto out; ++ } ++ if (attr == &gpiommc_attr_CS_activelow) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (data != 0 && data != 1) ++ goto out; ++ dev->pdata.pins.cs_activelow = data; ++ err = 0; ++ goto out; ++ } ++ if (attr == &gpiommc_attr_spimode) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ switch (data) { ++ case 0: ++ dev->pdata.mode = SPI_MODE_0; ++ break; ++ case 1: ++ dev->pdata.mode = SPI_MODE_1; ++ break; ++ case 2: ++ dev->pdata.mode = SPI_MODE_2; ++ break; ++ case 3: ++ dev->pdata.mode = SPI_MODE_3; ++ break; ++ default: ++ goto out; ++ } ++ err = 0; ++ goto out; ++ } ++ if (attr == &gpiommc_attr_spidelay) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (data != 0 && data != 1) ++ goto out; ++ dev->pdata.no_spi_delay = !data; ++ err = 0; ++ goto out; ++ } ++ if (attr == &gpiommc_attr_max_bus_speed) { ++ err = strict_strtoul(page, 10, &data); ++ if (err) ++ goto out; ++ err = -EINVAL; ++ if (data > UINT_MAX) ++ goto out; ++ dev->pdata.max_bus_speed = data; ++ err = 0; ++ goto out; ++ } ++ WARN_ON(1); ++ err = -ENOSYS; ++out: ++ return err ? err : count; ++} ++ ++static void gpiommc_config_item_release(struct config_item *item) ++{ ++ struct gpiommc_configfs_device *dev = ci_to_gpiommc(item); ++ ++ kfree(dev); ++} ++ ++static struct configfs_item_operations gpiommc_config_item_ops = { ++ .release = gpiommc_config_item_release, ++ .show_attribute = gpiommc_config_attr_show, ++ .store_attribute = gpiommc_config_attr_store, ++}; ++ ++static struct config_item_type gpiommc_dev_ci_type = { ++ .ct_item_ops = &gpiommc_config_item_ops, ++ .ct_attrs = gpiommc_config_attrs, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct config_item *gpiommc_make_item(struct config_group *group, ++ const char *name) ++{ ++ struct gpiommc_configfs_device *dev; ++ ++ if (strlen(name) > GPIOMMC_MAX_NAMELEN) { ++ printk(KERN_ERR PFX "configfs: device name too long\n"); ++ return NULL; ++ } ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return NULL; ++ ++ config_item_init_type_name(&dev->item, name, ++ &gpiommc_dev_ci_type); ++ ++ /* Assign default configuration */ ++ dev->pdata.pins.gpio_di = GPIO_INVALID; ++ dev->pdata.pins.gpio_do = GPIO_INVALID; ++ dev->pdata.pins.gpio_clk = GPIO_INVALID; ++ dev->pdata.pins.gpio_cs = GPIO_INVALID; ++ dev->pdata.pins.cs_activelow = 1; ++ dev->pdata.mode = SPI_MODE_0; ++ dev->pdata.no_spi_delay = 0; ++ dev->pdata.max_bus_speed = 5000000; /* 5 MHz */ ++ ++ return &(dev->item); ++} ++ ++static void gpiommc_drop_item(struct config_group *group, ++ struct config_item *item) ++{ ++ struct gpiommc_configfs_device *dev = ci_to_gpiommc(item); ++ ++ gpiommc_do_unregister(dev); ++ kfree(dev); ++} ++ ++static struct configfs_group_operations gpiommc_ct_group_ops = { ++ .make_item = gpiommc_make_item, ++ .drop_item = gpiommc_drop_item, ++}; ++ ++static struct config_item_type gpiommc_ci_type = { ++ .ct_group_ops = &gpiommc_ct_group_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct configfs_subsystem gpiommc_subsys = { ++ .su_group = { ++ .cg_item = { ++ .ci_namebuf = GPIOMMC_PLATDEV_NAME, ++ .ci_type = &gpiommc_ci_type, ++ }, ++ }, ++ .su_mutex = __MUTEX_INITIALIZER(gpiommc_subsys.su_mutex), ++}; ++ ++#endif /* CONFIG_GPIOMMC_CONFIGFS */ ++ ++static struct platform_driver gpiommc_plat_driver = { ++ .probe = gpiommc_probe, ++ .remove = gpiommc_remove, ++ .driver = { ++ .name = GPIOMMC_PLATDEV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++int gpiommc_next_id(void) ++{ ++ static atomic_t counter = ATOMIC_INIT(-1); ++ ++ return atomic_inc_return(&counter); ++} ++EXPORT_SYMBOL(gpiommc_next_id); ++ ++static int __init gpiommc_modinit(void) ++{ ++ int err; ++ ++ err = platform_driver_register(&gpiommc_plat_driver); ++ if (err) ++ return err; ++ ++#ifdef CONFIG_GPIOMMC_CONFIGFS ++ config_group_init(&gpiommc_subsys.su_group); ++ err = configfs_register_subsystem(&gpiommc_subsys); ++ if (err) { ++ platform_driver_unregister(&gpiommc_plat_driver); ++ return err; ++ } ++#endif /* CONFIG_GPIOMMC_CONFIGFS */ ++ ++ return 0; ++} ++module_init(gpiommc_modinit); ++ ++static void __exit gpiommc_modexit(void) ++{ ++#ifdef CONFIG_GPIOMMC_CONFIGFS ++ configfs_unregister_subsystem(&gpiommc_subsys); ++#endif ++ platform_driver_unregister(&gpiommc_plat_driver); ++} ++module_exit(gpiommc_modexit); +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -192,3 +192,28 @@ config MMC_TMIO + help + This provides support for the SD/MMC cell found in TC6393XB, + T7L66XB and also ipaq ASIC3 ++ ++config GPIOMMC ++ tristate "MMC/SD over GPIO-based SPI" ++ depends on MMC && MMC_SPI && SPI_GPIO ++ help ++ This driver hooks up the mmc_spi and spi_gpio modules so that ++ MMC/SD cards can be used on a GPIO based bus by bitbanging ++ the SPI protocol in software. ++ ++ This driver provides a configfs interface to dynamically create ++ and destroy GPIO-based MMC/SD card devices. It also provides ++ a platform device interface API. ++ See Documentation/gpiommc.txt for details. ++ ++ The module will be called gpiommc. ++ ++ If unsure, say N. ++ ++config GPIOMMC_CONFIGFS ++ bool ++ depends on GPIOMMC && CONFIGFS_FS ++ default y ++ help ++ This option automatically enables configfs support for gpiommc ++ if configfs is available. +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -22,4 +22,5 @@ obj-$(CONFIG_MMC_SPI) += mmc_spi.o + obj-$(CONFIG_MMC_S3C) += s3cmci.o + obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o + obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o ++obj-$(CONFIG_GPIOMMC) += gpiommc.o + +--- /dev/null ++++ b/include/linux/mmc/gpiommc.h +@@ -0,0 +1,71 @@ ++/* ++ * Device driver for MMC/SD cards driven over a GPIO bus. ++ * ++ * Copyright (c) 2008 Michael Buesch ++ * ++ * Licensed under the GNU/GPL version 2. ++ */ ++#ifndef LINUX_GPIOMMC_H_ ++#define LINUX_GPIOMMC_H_ ++ ++#include <linux/types.h> ++ ++ ++#define GPIOMMC_MAX_NAMELEN 15 ++#define GPIOMMC_MAX_NAMELEN_STR __stringify(GPIOMMC_MAX_NAMELEN) ++ ++/** ++ * struct gpiommc_pins - Hardware pin assignments ++ * ++ * @gpio_di: The GPIO number of the DATA IN pin ++ * @gpio_do: The GPIO number of the DATA OUT pin ++ * @gpio_clk: The GPIO number of the CLOCK pin ++ * @gpio_cs: The GPIO number of the CHIPSELECT pin ++ * @cs_activelow: If true, the chip is considered selected if @gpio_cs is low. ++ */ ++struct gpiommc_pins { ++ unsigned int gpio_di; ++ unsigned int gpio_do; ++ unsigned int gpio_clk; ++ unsigned int gpio_cs; ++ bool cs_activelow; ++}; ++ ++/** ++ * struct gpiommc_platform_data - Platform data for a MMC-over-SPI-GPIO device. ++ * ++ * @name: The unique name string of the device. ++ * @pins: The hardware pin assignments. ++ * @mode: The hardware mode. This is either SPI_MODE_0, ++ * SPI_MODE_1, SPI_MODE_2 or SPI_MODE_3. See the SPI documentation. ++ * @no_spi_delay: Do not use delays in the lowlevel SPI bitbanging code. ++ * This is not standards compliant, but may be required for some ++ * embedded machines to gain reasonable speed. ++ * @max_bus_speed: The maximum speed of the SPI bus, in Hertz. ++ */ ++struct gpiommc_platform_data { ++ char name[GPIOMMC_MAX_NAMELEN + 1]; ++ struct gpiommc_pins pins; ++ u8 mode; ++ bool no_spi_delay; ++ unsigned int max_bus_speed; ++}; ++ ++/** ++ * GPIOMMC_PLATDEV_NAME - The platform device name string. ++ * ++ * The name string that has to be used for platform_device_alloc ++ * when allocating a gpiommc device. ++ */ ++#define GPIOMMC_PLATDEV_NAME "gpiommc" ++ ++/** ++ * gpiommc_next_id - Get another platform device ID number. ++ * ++ * This returns the next platform device ID number that has to be used ++ * for platform_device_alloc. The ID is opaque and should not be used for ++ * anything else. ++ */ ++int gpiommc_next_id(void); ++ ++#endif /* LINUX_GPIOMMC_H_ */ +--- /dev/null ++++ b/Documentation/gpiommc.txt +@@ -0,0 +1,97 @@ ++GPIOMMC - Driver for an MMC/SD card on a bitbanging GPIO SPI bus ++================================================================ ++ ++The gpiommc module hooks up the mmc_spi and spi_gpio modules for running an ++MMC or SD card on GPIO pins. ++ ++Two interfaces for registering a new MMC/SD card device are provided: ++A static platform-device based mechanism and a dynamic configfs based interface. ++ ++ ++Registering devices via platform-device ++======================================= ++ ++The platform-device interface is used for registering MMC/SD devices that are ++part of the hardware platform. This is most useful only for embedded machines ++with MMC/SD devices statically connected to the platform GPIO bus. ++ ++The data structures are declared in <linux/mmc/gpiommc.h>. ++ ++To register a new device, define an instance of struct gpiommc_platform_data. ++This structure holds any information about how the device is hooked up to the ++GPIO pins and what hardware modes the device supports. See the docbook-style ++documentation in the header file for more information on the struct fields. ++ ++Then allocate a new instance of a platform device by doing: ++ ++ pdev = platform_device_alloc(GPIOMMC_PLATDEV_NAME, gpiommc_next_id()); ++ ++This will allocate the platform device data structures and hook it up to the ++gpiommc driver. ++Then add the gpiommc_platform_data to the platform device. ++ ++ err = platform_device_add_data(pdev, pdata, sizeof(struct gpiommc_platform_data)); ++ ++You may free the local instance of struct gpiommc_platform_data now. (So the ++struct may be allocated on the stack, too). ++Now simply register the platform device. ++ ++ err = platform_device_add(pdev); ++ ++Done. The gpiommc probe routine will be invoked now and you should see a kernel ++log message for the added device. ++ ++ ++Registering devices via configfs ++================================ ++ ++MMC/SD cards connected via GPIO often are a pretty dynamic thing, as for example ++selfmade hacks for soldering an MMC/SD card to standard GPIO pins on embedded ++hardware are a common situation. ++So we provide a dynamic interface to conveniently handle adding and removing ++devices from userspace, without the need to recompile the kernel. ++ ++The "gpiommc" subdirectory at the configfs mountpoint is used for handling ++the dynamic configuration. ++ ++To create a new device, it must first be allocated with mkdir. ++The following command will allocate a device named "my_mmc": ++ mkdir /config/gpiommc/my_mmc ++ ++There are several configuration files available in the new ++/config/gpiommc/my_mmc/ directory: ++ ++gpio_data_in = The SPI data-IN GPIO pin number. ++gpio_data_out = The SPI data-OUT GPIO pin number. ++gpio_clock = The SPI Clock GPIO pin number. ++gpio_chipselect = The SPI Chipselect GPIO pin number. ++gpio_chipselect_activelow = Boolean. If 0, Chipselect is active-HIGH. ++ If 1, Chipselect is active-LOW. ++spi_mode = The SPI data mode. Can be 0-3. ++spi_delay = Enable all delays in the lowlevel bitbanging. ++max_bus_speed = The maximum SPI bus speed. In Hertz. ++ ++register = Not a configuration parameter. ++ Used to register the configured card ++ with the kernel. ++ ++The device must first get configured and then registered by writing "1" to ++the "register" file. ++The configuration parameters "gpio_data_in", "gpio_data_out", "gpio_clock" ++and "gpio_chipselect" are essential and _must_ be configured before writing ++"1" to the "register" file. The registration will fail, otherwise. ++ ++The default values for the other parameters are: ++gpio_chipselect_activelow = 1 (CS active-LOW) ++spi_mode = 0 (SPI_MODE_0) ++spi_delay = 1 (enabled) ++max_bus_speed = 5000000 (5 Mhz) ++ ++Configuration values can not be changed after registration. To unregister ++the device, write a "0" to the "register" file. The configuration can be ++changed again after unregistering. ++ ++To completely remove the device, simply rmdir the directory ++(/config/gpiommc/my_mmc in this example). ++There's no need to first unregister the device before removing it. That will ++be done automatically. +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1910,6 +1910,11 @@ W: http://moinejf.free.fr + L: video4linux-list@redhat.com + S: Maintained + ++GPIOMMC DRIVER ++P: Michael Buesch ++M: mb@bu3sch.de ++S: Maintained ++ + HARDWARE MONITORING + L: lm-sensors@lm-sensors.org + W: http://www.lm-sensors.org/ diff --git a/target/linux/generic-2.6/patches-2.6.28/923-gpiommc-configfs-locking.patch b/target/linux/generic-2.6/patches-2.6.28/923-gpiommc-configfs-locking.patch new file mode 100644 index 0000000..2e4e820 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/923-gpiommc-configfs-locking.patch @@ -0,0 +1,58 @@ +The gpiommc configfs context structure needs locking, as configfs +does not lock access between files. + +--- a/drivers/mmc/host/gpiommc.c ++++ b/drivers/mmc/host/gpiommc.c +@@ -143,6 +143,8 @@ struct gpiommc_configfs_device { + struct platform_device *pdev; + /* The configuration */ + struct gpiommc_platform_data pdata; ++ /* Mutex to protect this structure */ ++ struct mutex mutex; + }; + + #define GPIO_INVALID -1 +@@ -233,6 +235,8 @@ static ssize_t gpiommc_config_attr_show( + unsigned int gpio; + int err = 0; + ++ mutex_lock(&dev->mutex); ++ + if (attr == &gpiommc_attr_DI) { + gpio = dev->pdata.pins.gpio_di; + if (gpio == GPIO_INVALID) +@@ -293,6 +297,8 @@ static ssize_t gpiommc_config_attr_show( + WARN_ON(1); + err = -ENOSYS; + out: ++ mutex_unlock(&dev->mutex); ++ + return err ? err : count; + } + +@@ -352,6 +358,8 @@ static ssize_t gpiommc_config_attr_store + int err = -EINVAL; + unsigned long data; + ++ mutex_lock(&dev->mutex); ++ + if (attr == &gpiommc_attr_register) { + err = strict_strtoul(page, 10, &data); + if (err) +@@ -477,6 +485,8 @@ static ssize_t gpiommc_config_attr_store + WARN_ON(1); + err = -ENOSYS; + out: ++ mutex_unlock(&dev->mutex); ++ + return err ? err : count; + } + +@@ -513,6 +523,7 @@ static struct config_item *gpiommc_make_ + if (!dev) + return NULL; + ++ mutex_init(&dev->mutex); + config_item_init_type_name(&dev->item, name, + &gpiommc_dev_ci_type); + diff --git a/target/linux/generic-2.6/patches-2.6.28/950-revert_xt_sctp_simplify.patch b/target/linux/generic-2.6/patches-2.6.28/950-revert_xt_sctp_simplify.patch new file mode 100644 index 0000000..462147b --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/950-revert_xt_sctp_simplify.patch @@ -0,0 +1,103 @@ +--- a/include/linux/netfilter/xt_sctp.h ++++ b/include/linux/netfilter/xt_sctp.h +@@ -37,54 +37,68 @@ struct xt_sctp_info { + + #define SCTP_CHUNKMAP_SET(chunkmap, type) \ + do { \ +- (chunkmap)[type / bytes(u_int32_t)] |= \ ++ chunkmap[type / bytes(u_int32_t)] |= \ + 1 << (type % bytes(u_int32_t)); \ + } while (0) + + #define SCTP_CHUNKMAP_CLEAR(chunkmap, type) \ + do { \ +- (chunkmap)[type / bytes(u_int32_t)] &= \ ++ chunkmap[type / bytes(u_int32_t)] &= \ + ~(1 << (type % bytes(u_int32_t))); \ + } while (0) + + #define SCTP_CHUNKMAP_IS_SET(chunkmap, type) \ + ({ \ +- ((chunkmap)[type / bytes (u_int32_t)] & \ ++ (chunkmap[type / bytes (u_int32_t)] & \ + (1 << (type % bytes (u_int32_t)))) ? 1: 0; \ + }) + +-#define SCTP_CHUNKMAP_RESET(chunkmap) \ +- memset((chunkmap), 0, sizeof(chunkmap)) ++#define SCTP_CHUNKMAP_RESET(chunkmap) \ ++ do { \ ++ int i; \ ++ for (i = 0; i < ARRAY_SIZE(chunkmap); i++) \ ++ chunkmap[i] = 0; \ ++ } while (0) + +-#define SCTP_CHUNKMAP_SET_ALL(chunkmap) \ +- memset((chunkmap), ~0U, sizeof(chunkmap)) ++#define SCTP_CHUNKMAP_SET_ALL(chunkmap) \ ++ do { \ ++ int i; \ ++ for (i = 0; i < ARRAY_SIZE(chunkmap); i++) \ ++ chunkmap[i] = ~0; \ ++ } while (0) + +-#define SCTP_CHUNKMAP_COPY(destmap, srcmap) \ +- memcpy((destmap), (srcmap), sizeof(srcmap)) ++#define SCTP_CHUNKMAP_COPY(destmap, srcmap) \ ++ do { \ ++ int i; \ ++ for (i = 0; i < ARRAY_SIZE(srcmap); i++) \ ++ destmap[i] = srcmap[i]; \ ++ } while (0) ++ ++#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap) \ ++({ \ ++ int i; \ ++ int flag = 1; \ ++ for (i = 0; i < ARRAY_SIZE(chunkmap); i++) { \ ++ if (chunkmap[i]) { \ ++ flag = 0; \ ++ break; \ ++ } \ ++ } \ ++ flag; \ ++}) + +-#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap) \ +- __sctp_chunkmap_is_clear((chunkmap), ARRAY_SIZE(chunkmap)) +-static inline bool +-__sctp_chunkmap_is_clear(const u_int32_t *chunkmap, unsigned int n) +-{ +- unsigned int i; +- for (i = 0; i < n; ++i) +- if (chunkmap[i]) +- return false; +- return true; +-} +- +-#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap) \ +- __sctp_chunkmap_is_all_set((chunkmap), ARRAY_SIZE(chunkmap)) +-static inline bool +-__sctp_chunkmap_is_all_set(const u_int32_t *chunkmap, unsigned int n) +-{ +- unsigned int i; +- for (i = 0; i < n; ++i) +- if (chunkmap[i] != ~0U) +- return false; +- return true; +-} ++#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap) \ ++({ \ ++ int i; \ ++ int flag = 1; \ ++ for (i = 0; i < ARRAY_SIZE(chunkmap); i++) { \ ++ if (chunkmap[i] != ~0) { \ ++ flag = 0; \ ++ break; \ ++ } \ ++ } \ ++ flag; \ ++}) + + #endif /* _XT_SCTP_H_ */ + diff --git a/target/linux/generic-2.6/patches-2.6.28/951-revert_gcc4_4_fixes.patch b/target/linux/generic-2.6/patches-2.6.28/951-revert_gcc4_4_fixes.patch new file mode 100644 index 0000000..ade26dc --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/951-revert_gcc4_4_fixes.patch @@ -0,0 +1,524 @@ +--- a/arch/powerpc/boot/crtsavres.S ++++ /dev/null +@@ -1,233 +0,0 @@ +-/* +- * Special support for eabi and SVR4 +- * +- * Copyright (C) 1995, 1996, 1998, 2000, 2001 Free Software Foundation, Inc. +- * Copyright 2008 Freescale Semiconductor, Inc. +- * Written By Michael Meissner +- * +- * Based on gcc/config/rs6000/crtsavres.asm from gcc +- * +- * This file 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. +- * +- * In addition to the permissions in the GNU General Public License, the +- * Free Software Foundation gives you unlimited permission to link the +- * compiled version of this file with other programs, and to distribute +- * those programs without any restriction coming from the use of this +- * file. (The General Public License restrictions do apply in other +- * respects; for example, they cover modification of the file, and +- * distribution when not linked into another program.) +- * +- * This file 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; see the file COPYING. If not, write to +- * the Free Software Foundation, 51 Franklin Street, Fifth Floor, +- * Boston, MA 02110-1301, USA. +- * +- * As a special exception, if you link this library with files +- * compiled with GCC to produce an executable, this does not cause +- * the resulting executable to be covered by the GNU General Public License. +- * This exception does not however invalidate any other reasons why +- * the executable file might be covered by the GNU General Public License. +- */ +- +- .file "crtsavres.S" +- .section ".text" +- +-/* On PowerPC64 Linux, these functions are provided by the linker. */ +-#ifndef __powerpc64__ +- +-#define _GLOBAL(name) \ +- .type name,@function; \ +- .globl name; \ +-name: +- +-/* Routines for saving integer registers, called by the compiler. */ +-/* Called with r11 pointing to the stack header word of the caller of the */ +-/* function, just beyond the end of the integer save area. */ +- +-_GLOBAL(_savegpr_14) +-_GLOBAL(_save32gpr_14) +- stw 14,-72(11) /* save gp registers */ +-_GLOBAL(_savegpr_15) +-_GLOBAL(_save32gpr_15) +- stw 15,-68(11) +-_GLOBAL(_savegpr_16) +-_GLOBAL(_save32gpr_16) +- stw 16,-64(11) +-_GLOBAL(_savegpr_17) +-_GLOBAL(_save32gpr_17) +- stw 17,-60(11) +-_GLOBAL(_savegpr_18) +-_GLOBAL(_save32gpr_18) +- stw 18,-56(11) +-_GLOBAL(_savegpr_19) +-_GLOBAL(_save32gpr_19) +- stw 19,-52(11) +-_GLOBAL(_savegpr_20) +-_GLOBAL(_save32gpr_20) +- stw 20,-48(11) +-_GLOBAL(_savegpr_21) +-_GLOBAL(_save32gpr_21) +- stw 21,-44(11) +-_GLOBAL(_savegpr_22) +-_GLOBAL(_save32gpr_22) +- stw 22,-40(11) +-_GLOBAL(_savegpr_23) +-_GLOBAL(_save32gpr_23) +- stw 23,-36(11) +-_GLOBAL(_savegpr_24) +-_GLOBAL(_save32gpr_24) +- stw 24,-32(11) +-_GLOBAL(_savegpr_25) +-_GLOBAL(_save32gpr_25) +- stw 25,-28(11) +-_GLOBAL(_savegpr_26) +-_GLOBAL(_save32gpr_26) +- stw 26,-24(11) +-_GLOBAL(_savegpr_27) +-_GLOBAL(_save32gpr_27) +- stw 27,-20(11) +-_GLOBAL(_savegpr_28) +-_GLOBAL(_save32gpr_28) +- stw 28,-16(11) +-_GLOBAL(_savegpr_29) +-_GLOBAL(_save32gpr_29) +- stw 29,-12(11) +-_GLOBAL(_savegpr_30) +-_GLOBAL(_save32gpr_30) +- stw 30,-8(11) +-_GLOBAL(_savegpr_31) +-_GLOBAL(_save32gpr_31) +- stw 31,-4(11) +- blr +- +-/* Routines for restoring integer registers, called by the compiler. */ +-/* Called with r11 pointing to the stack header word of the caller of the */ +-/* function, just beyond the end of the integer restore area. */ +- +-_GLOBAL(_restgpr_14) +-_GLOBAL(_rest32gpr_14) +- lwz 14,-72(11) /* restore gp registers */ +-_GLOBAL(_restgpr_15) +-_GLOBAL(_rest32gpr_15) +- lwz 15,-68(11) +-_GLOBAL(_restgpr_16) +-_GLOBAL(_rest32gpr_16) +- lwz 16,-64(11) +-_GLOBAL(_restgpr_17) +-_GLOBAL(_rest32gpr_17) +- lwz 17,-60(11) +-_GLOBAL(_restgpr_18) +-_GLOBAL(_rest32gpr_18) +- lwz 18,-56(11) +-_GLOBAL(_restgpr_19) +-_GLOBAL(_rest32gpr_19) +- lwz 19,-52(11) +-_GLOBAL(_restgpr_20) +-_GLOBAL(_rest32gpr_20) +- lwz 20,-48(11) +-_GLOBAL(_restgpr_21) +-_GLOBAL(_rest32gpr_21) +- lwz 21,-44(11) +-_GLOBAL(_restgpr_22) +-_GLOBAL(_rest32gpr_22) +- lwz 22,-40(11) +-_GLOBAL(_restgpr_23) +-_GLOBAL(_rest32gpr_23) +- lwz 23,-36(11) +-_GLOBAL(_restgpr_24) +-_GLOBAL(_rest32gpr_24) +- lwz 24,-32(11) +-_GLOBAL(_restgpr_25) +-_GLOBAL(_rest32gpr_25) +- lwz 25,-28(11) +-_GLOBAL(_restgpr_26) +-_GLOBAL(_rest32gpr_26) +- lwz 26,-24(11) +-_GLOBAL(_restgpr_27) +-_GLOBAL(_rest32gpr_27) +- lwz 27,-20(11) +-_GLOBAL(_restgpr_28) +-_GLOBAL(_rest32gpr_28) +- lwz 28,-16(11) +-_GLOBAL(_restgpr_29) +-_GLOBAL(_rest32gpr_29) +- lwz 29,-12(11) +-_GLOBAL(_restgpr_30) +-_GLOBAL(_rest32gpr_30) +- lwz 30,-8(11) +-_GLOBAL(_restgpr_31) +-_GLOBAL(_rest32gpr_31) +- lwz 31,-4(11) +- blr +- +-/* Routines for restoring integer registers, called by the compiler. */ +-/* Called with r11 pointing to the stack header word of the caller of the */ +-/* function, just beyond the end of the integer restore area. */ +- +-_GLOBAL(_restgpr_14_x) +-_GLOBAL(_rest32gpr_14_x) +- lwz 14,-72(11) /* restore gp registers */ +-_GLOBAL(_restgpr_15_x) +-_GLOBAL(_rest32gpr_15_x) +- lwz 15,-68(11) +-_GLOBAL(_restgpr_16_x) +-_GLOBAL(_rest32gpr_16_x) +- lwz 16,-64(11) +-_GLOBAL(_restgpr_17_x) +-_GLOBAL(_rest32gpr_17_x) +- lwz 17,-60(11) +-_GLOBAL(_restgpr_18_x) +-_GLOBAL(_rest32gpr_18_x) +- lwz 18,-56(11) +-_GLOBAL(_restgpr_19_x) +-_GLOBAL(_rest32gpr_19_x) +- lwz 19,-52(11) +-_GLOBAL(_restgpr_20_x) +-_GLOBAL(_rest32gpr_20_x) +- lwz 20,-48(11) +-_GLOBAL(_restgpr_21_x) +-_GLOBAL(_rest32gpr_21_x) +- lwz 21,-44(11) +-_GLOBAL(_restgpr_22_x) +-_GLOBAL(_rest32gpr_22_x) +- lwz 22,-40(11) +-_GLOBAL(_restgpr_23_x) +-_GLOBAL(_rest32gpr_23_x) +- lwz 23,-36(11) +-_GLOBAL(_restgpr_24_x) +-_GLOBAL(_rest32gpr_24_x) +- lwz 24,-32(11) +-_GLOBAL(_restgpr_25_x) +-_GLOBAL(_rest32gpr_25_x) +- lwz 25,-28(11) +-_GLOBAL(_restgpr_26_x) +-_GLOBAL(_rest32gpr_26_x) +- lwz 26,-24(11) +-_GLOBAL(_restgpr_27_x) +-_GLOBAL(_rest32gpr_27_x) +- lwz 27,-20(11) +-_GLOBAL(_restgpr_28_x) +-_GLOBAL(_rest32gpr_28_x) +- lwz 28,-16(11) +-_GLOBAL(_restgpr_29_x) +-_GLOBAL(_rest32gpr_29_x) +- lwz 29,-12(11) +-_GLOBAL(_restgpr_30_x) +-_GLOBAL(_rest32gpr_30_x) +- lwz 30,-8(11) +-_GLOBAL(_restgpr_31_x) +-_GLOBAL(_rest32gpr_31_x) +- lwz 0,4(11) +- lwz 31,-4(11) +- mtlr 0 +- mr 1,11 +- blr +-#endif +--- a/arch/powerpc/boot/Makefile ++++ b/arch/powerpc/boot/Makefile +@@ -53,7 +53,7 @@ $(addprefix $(obj)/,$(zlib) cuboot-c2k.o + $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader)) + + src-libfdt := fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c +-src-wlib := string.S crt0.S crtsavres.S stdio.c main.c \ ++src-wlib := string.S crt0.S stdio.c main.c \ + $(addprefix libfdt/,$(src-libfdt)) libfdt-wrapper.c \ + ns16550.c serial.c simple_alloc.c div64.S util.S \ + gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \ +--- a/arch/powerpc/kernel/prom_init_check.sh ++++ b/arch/powerpc/kernel/prom_init_check.sh +@@ -48,20 +48,6 @@ do + fi + done + +- # ignore register save/restore funcitons +- if [ "${UNDEF:0:9}" = "_restgpr_" ]; then +- OK=1 +- fi +- if [ "${UNDEF:0:11}" = "_rest32gpr_" ]; then +- OK=1 +- fi +- if [ "${UNDEF:0:9}" = "_savegpr_" ]; then +- OK=1 +- fi +- if [ "${UNDEF:0:11}" = "_save32gpr_" ]; then +- OK=1 +- fi +- + if [ $OK -eq 0 ]; then + ERROR=1 + echo "Error: External symbol '$UNDEF' referenced" \ +--- a/arch/powerpc/lib/crtsavres.S ++++ /dev/null +@@ -1,229 +0,0 @@ +-/* +- * Special support for eabi and SVR4 +- * +- * Copyright (C) 1995, 1996, 1998, 2000, 2001 Free Software Foundation, Inc. +- * Copyright 2008 Freescale Semiconductor, Inc. +- * Written By Michael Meissner +- * +- * Based on gcc/config/rs6000/crtsavres.asm from gcc +- * +- * This file 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. +- * +- * In addition to the permissions in the GNU General Public License, the +- * Free Software Foundation gives you unlimited permission to link the +- * compiled version of this file with other programs, and to distribute +- * those programs without any restriction coming from the use of this +- * file. (The General Public License restrictions do apply in other +- * respects; for example, they cover modification of the file, and +- * distribution when not linked into another program.) +- * +- * This file 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; see the file COPYING. If not, write to +- * the Free Software Foundation, 51 Franklin Street, Fifth Floor, +- * Boston, MA 02110-1301, USA. +- * +- * As a special exception, if you link this library with files +- * compiled with GCC to produce an executable, this does not cause +- * the resulting executable to be covered by the GNU General Public License. +- * This exception does not however invalidate any other reasons why +- * the executable file might be covered by the GNU General Public License. +- */ +- +-#include <asm/ppc_asm.h> +- +- .file "crtsavres.S" +- .section ".text" +- +-#ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE +- +-/* Routines for saving integer registers, called by the compiler. */ +-/* Called with r11 pointing to the stack header word of the caller of the */ +-/* function, just beyond the end of the integer save area. */ +- +-_GLOBAL(_savegpr_14) +-_GLOBAL(_save32gpr_14) +- stw 14,-72(11) /* save gp registers */ +-_GLOBAL(_savegpr_15) +-_GLOBAL(_save32gpr_15) +- stw 15,-68(11) +-_GLOBAL(_savegpr_16) +-_GLOBAL(_save32gpr_16) +- stw 16,-64(11) +-_GLOBAL(_savegpr_17) +-_GLOBAL(_save32gpr_17) +- stw 17,-60(11) +-_GLOBAL(_savegpr_18) +-_GLOBAL(_save32gpr_18) +- stw 18,-56(11) +-_GLOBAL(_savegpr_19) +-_GLOBAL(_save32gpr_19) +- stw 19,-52(11) +-_GLOBAL(_savegpr_20) +-_GLOBAL(_save32gpr_20) +- stw 20,-48(11) +-_GLOBAL(_savegpr_21) +-_GLOBAL(_save32gpr_21) +- stw 21,-44(11) +-_GLOBAL(_savegpr_22) +-_GLOBAL(_save32gpr_22) +- stw 22,-40(11) +-_GLOBAL(_savegpr_23) +-_GLOBAL(_save32gpr_23) +- stw 23,-36(11) +-_GLOBAL(_savegpr_24) +-_GLOBAL(_save32gpr_24) +- stw 24,-32(11) +-_GLOBAL(_savegpr_25) +-_GLOBAL(_save32gpr_25) +- stw 25,-28(11) +-_GLOBAL(_savegpr_26) +-_GLOBAL(_save32gpr_26) +- stw 26,-24(11) +-_GLOBAL(_savegpr_27) +-_GLOBAL(_save32gpr_27) +- stw 27,-20(11) +-_GLOBAL(_savegpr_28) +-_GLOBAL(_save32gpr_28) +- stw 28,-16(11) +-_GLOBAL(_savegpr_29) +-_GLOBAL(_save32gpr_29) +- stw 29,-12(11) +-_GLOBAL(_savegpr_30) +-_GLOBAL(_save32gpr_30) +- stw 30,-8(11) +-_GLOBAL(_savegpr_31) +-_GLOBAL(_save32gpr_31) +- stw 31,-4(11) +- blr +- +-/* Routines for restoring integer registers, called by the compiler. */ +-/* Called with r11 pointing to the stack header word of the caller of the */ +-/* function, just beyond the end of the integer restore area. */ +- +-_GLOBAL(_restgpr_14) +-_GLOBAL(_rest32gpr_14) +- lwz 14,-72(11) /* restore gp registers */ +-_GLOBAL(_restgpr_15) +-_GLOBAL(_rest32gpr_15) +- lwz 15,-68(11) +-_GLOBAL(_restgpr_16) +-_GLOBAL(_rest32gpr_16) +- lwz 16,-64(11) +-_GLOBAL(_restgpr_17) +-_GLOBAL(_rest32gpr_17) +- lwz 17,-60(11) +-_GLOBAL(_restgpr_18) +-_GLOBAL(_rest32gpr_18) +- lwz 18,-56(11) +-_GLOBAL(_restgpr_19) +-_GLOBAL(_rest32gpr_19) +- lwz 19,-52(11) +-_GLOBAL(_restgpr_20) +-_GLOBAL(_rest32gpr_20) +- lwz 20,-48(11) +-_GLOBAL(_restgpr_21) +-_GLOBAL(_rest32gpr_21) +- lwz 21,-44(11) +-_GLOBAL(_restgpr_22) +-_GLOBAL(_rest32gpr_22) +- lwz 22,-40(11) +-_GLOBAL(_restgpr_23) +-_GLOBAL(_rest32gpr_23) +- lwz 23,-36(11) +-_GLOBAL(_restgpr_24) +-_GLOBAL(_rest32gpr_24) +- lwz 24,-32(11) +-_GLOBAL(_restgpr_25) +-_GLOBAL(_rest32gpr_25) +- lwz 25,-28(11) +-_GLOBAL(_restgpr_26) +-_GLOBAL(_rest32gpr_26) +- lwz 26,-24(11) +-_GLOBAL(_restgpr_27) +-_GLOBAL(_rest32gpr_27) +- lwz 27,-20(11) +-_GLOBAL(_restgpr_28) +-_GLOBAL(_rest32gpr_28) +- lwz 28,-16(11) +-_GLOBAL(_restgpr_29) +-_GLOBAL(_rest32gpr_29) +- lwz 29,-12(11) +-_GLOBAL(_restgpr_30) +-_GLOBAL(_rest32gpr_30) +- lwz 30,-8(11) +-_GLOBAL(_restgpr_31) +-_GLOBAL(_rest32gpr_31) +- lwz 31,-4(11) +- blr +- +-/* Routines for restoring integer registers, called by the compiler. */ +-/* Called with r11 pointing to the stack header word of the caller of the */ +-/* function, just beyond the end of the integer restore area. */ +- +-_GLOBAL(_restgpr_14_x) +-_GLOBAL(_rest32gpr_14_x) +- lwz 14,-72(11) /* restore gp registers */ +-_GLOBAL(_restgpr_15_x) +-_GLOBAL(_rest32gpr_15_x) +- lwz 15,-68(11) +-_GLOBAL(_restgpr_16_x) +-_GLOBAL(_rest32gpr_16_x) +- lwz 16,-64(11) +-_GLOBAL(_restgpr_17_x) +-_GLOBAL(_rest32gpr_17_x) +- lwz 17,-60(11) +-_GLOBAL(_restgpr_18_x) +-_GLOBAL(_rest32gpr_18_x) +- lwz 18,-56(11) +-_GLOBAL(_restgpr_19_x) +-_GLOBAL(_rest32gpr_19_x) +- lwz 19,-52(11) +-_GLOBAL(_restgpr_20_x) +-_GLOBAL(_rest32gpr_20_x) +- lwz 20,-48(11) +-_GLOBAL(_restgpr_21_x) +-_GLOBAL(_rest32gpr_21_x) +- lwz 21,-44(11) +-_GLOBAL(_restgpr_22_x) +-_GLOBAL(_rest32gpr_22_x) +- lwz 22,-40(11) +-_GLOBAL(_restgpr_23_x) +-_GLOBAL(_rest32gpr_23_x) +- lwz 23,-36(11) +-_GLOBAL(_restgpr_24_x) +-_GLOBAL(_rest32gpr_24_x) +- lwz 24,-32(11) +-_GLOBAL(_restgpr_25_x) +-_GLOBAL(_rest32gpr_25_x) +- lwz 25,-28(11) +-_GLOBAL(_restgpr_26_x) +-_GLOBAL(_rest32gpr_26_x) +- lwz 26,-24(11) +-_GLOBAL(_restgpr_27_x) +-_GLOBAL(_rest32gpr_27_x) +- lwz 27,-20(11) +-_GLOBAL(_restgpr_28_x) +-_GLOBAL(_rest32gpr_28_x) +- lwz 28,-16(11) +-_GLOBAL(_restgpr_29_x) +-_GLOBAL(_rest32gpr_29_x) +- lwz 29,-12(11) +-_GLOBAL(_restgpr_30_x) +-_GLOBAL(_rest32gpr_30_x) +- lwz 30,-8(11) +-_GLOBAL(_restgpr_31_x) +-_GLOBAL(_rest32gpr_31_x) +- lwz 0,4(11) +- lwz 31,-4(11) +- mtlr 0 +- mr 1,11 +- blr +-#endif +--- a/arch/powerpc/lib/Makefile ++++ b/arch/powerpc/lib/Makefile +@@ -8,7 +8,7 @@ endif + + obj-y := string.o alloc.o \ + checksum_$(CONFIG_WORD_SIZE).o +-obj-$(CONFIG_PPC32) += div64.o copy_32.o crtsavres.o ++obj-$(CONFIG_PPC32) += div64.o copy_32.o + obj-$(CONFIG_HAS_IOMEM) += devres.o + + obj-$(CONFIG_PPC64) += copypage_64.o copyuser_64.o \ +--- a/arch/powerpc/Makefile ++++ b/arch/powerpc/Makefile +@@ -92,8 +92,6 @@ endif + else + KBUILD_CFLAGS += $(call cc-option,-mtune=power4) + endif +-else +-LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o + endif + + ifeq ($(CONFIG_TUNE_CELL),y) diff --git a/target/linux/generic-2.6/patches-2.6.28/970-ocf_kbuild_integration.patch b/target/linux/generic-2.6/patches-2.6.28/970-ocf_kbuild_integration.patch new file mode 100644 index 0000000..9fe19ac --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/970-ocf_kbuild_integration.patch @@ -0,0 +1,25 @@ +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -707,6 +707,8 @@ config CRYPTO_ANSI_CPRNG + for cryptographic modules. Uses the Algorithm specified in + ANSI X9.31 A.2.4 + ++source "crypto/ocf/Kconfig" ++ + source "drivers/crypto/Kconfig" + + endif # if CRYPTO +--- a/crypto/Makefile ++++ b/crypto/Makefile +@@ -79,6 +79,11 @@ obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_ + obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o + + # ++# OCF ++# ++obj-$(CONFIG_OCF_OCF) += ocf/ ++ ++# + # generic algorithms and the async_tx api + # + obj-$(CONFIG_XOR_BLOCKS) += xor.o diff --git a/target/linux/generic-2.6/patches-2.6.28/971-ocf_20080917.patch b/target/linux/generic-2.6/patches-2.6.28/971-ocf_20080917.patch new file mode 100644 index 0000000..162be24 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/971-ocf_20080917.patch @@ -0,0 +1,160 @@ +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -129,6 +129,9 @@ + * unsigned int value); + * void add_interrupt_randomness(int irq); + * ++ * void random_input_words(__u32 *buf, size_t wordcount, int ent_count) ++ * int random_input_wait(void); ++ * + * add_input_randomness() uses the input layer interrupt timing, as well as + * the event type information from the hardware. + * +@@ -140,6 +143,13 @@ + * a better measure, since the timing of the disk interrupts are more + * unpredictable. + * ++ * random_input_words() just provides a raw block of entropy to the input ++ * pool, such as from a hardware entropy generator. ++ * ++ * random_input_wait() suspends the caller until such time as the ++ * entropy pool falls below the write threshold, and returns a count of how ++ * much entropy (in bits) is needed to sustain the pool. ++ * + * All of these routines try to estimate how many bits of randomness a + * particular randomness source. They do this by keeping track of the + * first and second order deltas of the event timings. +@@ -689,6 +699,61 @@ void add_disk_randomness(struct gendisk + } + #endif + ++/* ++ * random_input_words - add bulk entropy to pool ++ * ++ * @buf: buffer to add ++ * @wordcount: number of __u32 words to add ++ * @ent_count: total amount of entropy (in bits) to credit ++ * ++ * this provides bulk input of entropy to the input pool ++ * ++ */ ++void random_input_words(__u32 *buf, size_t wordcount, int ent_count) ++{ ++ mix_pool_bytes(&input_pool, buf, wordcount*4); ++ ++ credit_entropy_bits(&input_pool, ent_count); ++ ++ DEBUG_ENT("crediting %d bits => %d\n", ++ ent_count, input_pool.entropy_count); ++ /* ++ * Wake up waiting processes if we have enough ++ * entropy. ++ */ ++ if (input_pool.entropy_count >= random_read_wakeup_thresh) ++ wake_up_interruptible(&random_read_wait); ++} ++EXPORT_SYMBOL(random_input_words); ++ ++/* ++ * random_input_wait - wait until random needs entropy ++ * ++ * this function sleeps until the /dev/random subsystem actually ++ * needs more entropy, and then return the amount of entropy ++ * that it would be nice to have added to the system. ++ */ ++int random_input_wait(void) ++{ ++ int count; ++ ++ wait_event_interruptible(random_write_wait, ++ input_pool.entropy_count < random_write_wakeup_thresh); ++ ++ count = random_write_wakeup_thresh - input_pool.entropy_count; ++ ++ /* likely we got woken up due to a signal */ ++ if (count <= 0) count = random_read_wakeup_thresh; ++ ++ DEBUG_ENT("requesting %d bits from input_wait()er %d<%d\n", ++ count, ++ input_pool.entropy_count, random_write_wakeup_thresh); ++ ++ return count; ++} ++EXPORT_SYMBOL(random_input_wait); ++ ++ + #define EXTRACT_SIZE 10 + + /********************************************************************* +--- a/fs/fcntl.c ++++ b/fs/fcntl.c +@@ -139,6 +139,7 @@ asmlinkage long sys_dup(unsigned int fil + } + return ret; + } ++EXPORT_SYMBOL(sys_dup); + + #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_NOATIME) + +--- a/include/linux/miscdevice.h ++++ b/include/linux/miscdevice.h +@@ -12,6 +12,7 @@ + #define APOLLO_MOUSE_MINOR 7 + #define PC110PAD_MINOR 9 + /*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ ++#define CRYPTODEV_MINOR 70 /* /dev/crypto */ + #define WATCHDOG_MINOR 130 /* Watchdog timer */ + #define TEMP_MINOR 131 /* Temperature Sensor */ + #define RTC_MINOR 135 +--- a/include/linux/random.h ++++ b/include/linux/random.h +@@ -8,6 +8,7 @@ + #define _LINUX_RANDOM_H + + #include <linux/ioctl.h> ++#include <linux/types.h> /* for __u32 in user space */ + + /* ioctl()'s for the random number generator */ + +@@ -32,6 +33,30 @@ + /* Clear the entropy pool and associated counters. (Superuser only.) */ + #define RNDCLEARPOOL _IO( 'R', 0x06 ) + ++#ifdef CONFIG_FIPS_RNG ++ ++/* Size of seed value - equal to AES blocksize */ ++#define AES_BLOCK_SIZE_BYTES 16 ++#define SEED_SIZE_BYTES AES_BLOCK_SIZE_BYTES ++/* Size of AES key */ ++#define KEY_SIZE_BYTES 16 ++ ++/* ioctl() structure used by FIPS 140-2 Tests */ ++struct rand_fips_test { ++ unsigned char key[KEY_SIZE_BYTES]; /* Input */ ++ unsigned char datetime[SEED_SIZE_BYTES]; /* Input */ ++ unsigned char seed[SEED_SIZE_BYTES]; /* Input */ ++ unsigned char result[SEED_SIZE_BYTES]; /* Output */ ++}; ++ ++/* FIPS 140-2 RNG Variable Seed Test. (Superuser only.) */ ++#define RNDFIPSVST _IOWR('R', 0x10, struct rand_fips_test) ++ ++/* FIPS 140-2 RNG Monte Carlo Test. (Superuser only.) */ ++#define RNDFIPSMCT _IOWR('R', 0x11, struct rand_fips_test) ++ ++#endif /* #ifdef CONFIG_FIPS_RNG */ ++ + struct rand_pool_info { + int entropy_count; + int buf_size; +@@ -48,6 +73,10 @@ extern void add_input_randomness(unsigne + unsigned int value); + extern void add_interrupt_randomness(int irq); + ++extern void random_input_words(__u32 *buf, size_t wordcount, int ent_count); ++extern int random_input_wait(void); ++#define HAS_RANDOM_INPUT_WAIT 1 ++ + extern void get_random_bytes(void *buf, int nbytes); + void generate_random_uuid(unsigned char uuid_out[16]); + diff --git a/target/linux/generic-2.6/patches-2.6.28/972-ocf_compile_fix.patch b/target/linux/generic-2.6/patches-2.6.28/972-ocf_compile_fix.patch new file mode 100644 index 0000000..a3fa226 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/972-ocf_compile_fix.patch @@ -0,0 +1,11 @@ +--- a/crypto/ocf/cryptosoft.c ++++ b/crypto/ocf/cryptosoft.c +@@ -47,7 +47,7 @@ + #include <linux/mm.h> + #include <linux/skbuff.h> + #include <linux/random.h> +-#include <asm/scatterlist.h> ++#include <linux/scatterlist.h> + + #include <cryptodev.h> + #include <uio.h> diff --git a/target/linux/generic-2.6/patches-2.6.28/973-ocf_2.6.27_fix.patch b/target/linux/generic-2.6/patches-2.6.28/973-ocf_2.6.27_fix.patch new file mode 100644 index 0000000..6ffce01 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/973-ocf_2.6.27_fix.patch @@ -0,0 +1,197 @@ +--- a/crypto/ocf/random.c ++++ b/crypto/ocf/random.c +@@ -49,6 +49,7 @@ + #include <linux/unistd.h> + #include <linux/poll.h> + #include <linux/random.h> ++#include <linux/kthread.h> + #include <cryptodev.h> + + #ifdef CONFIG_OCF_FIPS +@@ -81,7 +82,7 @@ struct random_op { + + static int random_proc(void *arg); + +-static pid_t randomproc = (pid_t) -1; ++static struct task_struct *random_task; + static spinlock_t random_lock; + + /* +@@ -141,13 +142,18 @@ crypto_rregister( + spin_lock_irqsave(&random_lock, flags); + list_add_tail(&rops->random_list, &random_ops); + if (!started) { +- randomproc = kernel_thread(random_proc, NULL, CLONE_FS|CLONE_FILES); +- if (randomproc < 0) { +- ret = randomproc; ++ struct task_struct *t; ++ ++ t = kthread_create(random_proc, NULL, "ocf-random"); ++ if (IS_ERR(t)) { + printk("crypto: crypto_rregister cannot start random thread; " + "error %d", ret); +- } else ++ ret = PTR_ERR(t); ++ } else { ++ random_task = t; ++ wake_up_process(t); + started = 1; ++ } + } + spin_unlock_irqrestore(&random_lock, flags); + +@@ -172,7 +178,7 @@ crypto_runregister_all(u_int32_t driveri + + spin_lock_irqsave(&random_lock, flags); + if (list_empty(&random_ops) && started) +- kill_proc(randomproc, SIGKILL, 1); ++ send_sig(SIGKILL, random_task, 1); + spin_unlock_irqrestore(&random_lock, flags); + return(0); + } +@@ -308,7 +314,7 @@ random_proc(void *arg) + + bad_alloc: + spin_lock_irq(&random_lock); +- randomproc = (pid_t) -1; ++ random_task = NULL; + started = 0; + spin_unlock_irq(&random_lock); + +--- a/crypto/ocf/crypto.c ++++ b/crypto/ocf/crypto.c +@@ -74,6 +74,7 @@ __FBSDID("$FreeBSD: src/sys/opencrypto/c + #include <linux/sched.h> + #include <linux/spinlock.h> + #include <linux/version.h> ++#include <linux/kthread.h> + #include <cryptodev.h> + + /* +@@ -255,10 +256,10 @@ module_param(crypto_devallowsoft, int, 0 + MODULE_PARM_DESC(crypto_devallowsoft, + "Enable/disable use of software crypto support"); + +-static pid_t cryptoproc = (pid_t) -1; ++static struct task_struct *crypto_task; + static struct completion cryptoproc_exited; + static DECLARE_WAIT_QUEUE_HEAD(cryptoproc_wait); +-static pid_t cryptoretproc = (pid_t) -1; ++static struct task_struct *cryptoret_task; + static struct completion cryptoretproc_exited; + static DECLARE_WAIT_QUEUE_HEAD(cryptoretproc_wait); + +@@ -1401,7 +1402,7 @@ crypto_proc(void *arg) + wait_event_interruptible(cryptoproc_wait, + !(list_empty(&crp_q) || crypto_all_qblocked) || + !(list_empty(&crp_kq) || crypto_all_kqblocked) || +- cryptoproc == (pid_t) -1); ++ crypto_task == NULL); + crp_sleep = 0; + if (signal_pending (current)) { + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +@@ -1414,7 +1415,7 @@ crypto_proc(void *arg) + } + CRYPTO_Q_LOCK(); + dprintk("%s - awake\n", __FUNCTION__); +- if (cryptoproc == (pid_t) -1) ++ if (crypto_task == NULL) + break; + cryptostats.cs_intrs++; + } +@@ -1470,7 +1471,7 @@ crypto_ret_proc(void *arg) + dprintk("%s - sleeping\n", __FUNCTION__); + CRYPTO_RETQ_UNLOCK(); + wait_event_interruptible(cryptoretproc_wait, +- cryptoretproc == (pid_t) -1 || ++ cryptoret_task == NULL || + !list_empty(&crp_ret_q) || + !list_empty(&crp_ret_kq)); + if (signal_pending (current)) { +@@ -1484,7 +1485,7 @@ crypto_ret_proc(void *arg) + } + CRYPTO_RETQ_LOCK(); + dprintk("%s - awake\n", __FUNCTION__); +- if (cryptoretproc == (pid_t) -1) { ++ if (cryptoret_task == NULL) { + dprintk("%s - EXITING!\n", __FUNCTION__); + break; + } +@@ -1597,6 +1598,7 @@ DB_SHOW_COMMAND(kcrypto, db_show_kcrypto + static int + crypto_init(void) + { ++ struct task_struct *t; + int error; + + dprintk("%s(0x%x)\n", __FUNCTION__, (int) crypto_init); +@@ -1643,23 +1645,27 @@ crypto_init(void) + init_completion(&cryptoproc_exited); + init_completion(&cryptoretproc_exited); + +- cryptoproc = 0; /* to avoid race condition where proc runs first */ +- cryptoproc = kernel_thread(crypto_proc, NULL, CLONE_FS|CLONE_FILES); +- if (cryptoproc < 0) { +- error = cryptoproc; ++ crypto_task = NULL; /* to avoid race condition where proc runs first */ ++ t = kthread_create(crypto_proc, NULL, "ocf-crypto"); ++ if (IS_ERR(t)) { ++ error = PTR_ERR(t); + printk("crypto: crypto_init cannot start crypto thread; error %d", + error); + goto bad; + } ++ wake_up_process(t); ++ crypto_task = t; + +- cryptoretproc = 0; /* to avoid race condition where proc runs first */ +- cryptoretproc = kernel_thread(crypto_ret_proc, NULL, CLONE_FS|CLONE_FILES); +- if (cryptoretproc < 0) { +- error = cryptoretproc; ++ cryptoret_task = NULL; /* to avoid race condition where proc runs first */ ++ t = kthread_create(crypto_ret_proc, NULL, "ocf-cryptoret"); ++ if (IS_ERR(t)) { ++ error = PTR_ERR(t); + printk("crypto: crypto_init cannot start cryptoret thread; error %d", + error); + goto bad; + } ++ wake_up_process(t); ++ cryptoret_task = t; + + return 0; + bad: +@@ -1671,7 +1677,7 @@ bad: + static void + crypto_exit(void) + { +- pid_t p; ++ struct task_struct *t; + unsigned long d_flags; + + dprintk("%s()\n", __FUNCTION__); +@@ -1681,18 +1687,18 @@ crypto_exit(void) + */ + + CRYPTO_DRIVER_LOCK(); +- p = cryptoproc; +- cryptoproc = (pid_t) -1; +- kill_proc(p, SIGTERM, 1); ++ t = crypto_task; ++ crypto_task = NULL; ++ send_sig(SIGTERM, t, 1); + wake_up_interruptible(&cryptoproc_wait); + CRYPTO_DRIVER_UNLOCK(); + + wait_for_completion(&cryptoproc_exited); + + CRYPTO_DRIVER_LOCK(); +- p = cryptoretproc; +- cryptoretproc = (pid_t) -1; +- kill_proc(p, SIGTERM, 1); ++ t = cryptoret_task; ++ cryptoret_task = NULL; ++ send_sig(SIGTERM, t, 1); + wake_up_interruptible(&cryptoretproc_wait); + CRYPTO_DRIVER_UNLOCK(); + diff --git a/target/linux/generic-2.6/patches-2.6.28/974-ssb_b43_default_on.patch b/target/linux/generic-2.6/patches-2.6.28/974-ssb_b43_default_on.patch new file mode 100644 index 0000000..98dde2a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.28/974-ssb_b43_default_on.patch @@ -0,0 +1,11 @@ +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -49,7 +49,7 @@ config SSB_PCIHOST + config SSB_B43_PCI_BRIDGE + bool + depends on SSB_PCIHOST +- default n ++ default y + + config SSB_PCMCIAHOST_POSSIBLE + bool |