linux-2.6.17/0000755000000000000000000000000010651772634011457 5ustar rootrootlinux-2.6.17/.config.1.r720000644000000000000000000005155210611030460013461 0ustar rootroot# # Automatically generated make config: don't edit # Linux kernel version: 2.6.17 # Wed Apr 4 11:18:56 2007 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_VECTORS_BASE=0xffff0000 # # Code maturity level options # CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 # # General setup # CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y # CONFIG_SWAP is not set CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y # CONFIG_BSD_PROCESS_ACCT_V3 is not set CONFIG_SYSCTL=y # CONFIG_AUDIT is not set # CONFIG_IKCONFIG is not set # CONFIG_RELAY is not set CONFIG_INITRAMFS_SOURCE="" CONFIG_UID16=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EMBEDDED=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set CONFIG_FUTEX=y CONFIG_EPOLL=y # CONFIG_SHMEM is not set CONFIG_SLAB=y CONFIG_TINY_SHMEM=y CONFIG_BASE_SMALL=1 # CONFIG_SLOB is not set # # Loadable module support # CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set # CONFIG_KMOD is not set # # Block layer # # CONFIG_BLK_DEV_IO_TRACE is not set # # IO Schedulers # CONFIG_IOSCHED_NOOP=y # CONFIG_IOSCHED_AS is not set CONFIG_IOSCHED_DEADLINE=y # CONFIG_IOSCHED_CFQ is not set # CONFIG_DEFAULT_AS is not set CONFIG_DEFAULT_DEADLINE=y # CONFIG_DEFAULT_CFQ is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="deadline" # # System Type # # CONFIG_ARCH_CLPS7500 is not set # CONFIG_ARCH_CLPS711X is not set # CONFIG_ARCH_CO285 is not set # CONFIG_ARCH_EBSA110 is not set # CONFIG_ARCH_EP93XX is not set # CONFIG_ARCH_FOOTBRIDGE is not set # CONFIG_ARCH_INTEGRATOR is not set # CONFIG_ARCH_IOP3XX is not set # CONFIG_ARCH_IXP4XX is not set # CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_IXP23XX is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_RPC is not set # CONFIG_ARCH_SA1100 is not set # CONFIG_ARCH_S3C2410 is not set # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_OMAP is not set # CONFIG_ARCH_VERSATILE is not set CONFIG_ARCH_FV13XX=y # CONFIG_ARCH_GENESIS is not set # CONFIG_ARCH_REALVIEW is not set # CONFIG_ARCH_IMX is not set # CONFIG_ARCH_H720X is not set # CONFIG_ARCH_AAEC2000 is not set # CONFIG_ARCH_AT91RM9200 is not set # # Processor Type # CONFIG_CPU_32=y # CONFIG_CPU_ARM926T is not set CONFIG_CPU_ARM926EJS=y CONFIG_CPU_32v5=y CONFIG_CPU_ABRT_EV5TJ=y CONFIG_CPU_CACHE_VIVT=y CONFIG_CPU_COPY_V4WB=y CONFIG_CPU_TLB_V4WBI=y # # Processor Features # # CONFIG_ARM_THUMB is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set # CONFIG_CPU_DCACHE_WRITETHROUGH is not set # CONFIG_CPU_CACHE_ROUND_ROBIN is not set # # Bus support # # CONFIG_ARM_AMBA is not set CONFIG_PCI=y # # PCCARD (PCMCIA/CardBus) support # # CONFIG_PCCARD is not set # # Kernel Features # # CONFIG_PREEMPT is not set # CONFIG_NO_IDLE_HZ is not set CONFIG_HZ=100 # CONFIG_AEABI is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y # CONFIG_DISCONTIGMEM_MANUAL is not set # CONFIG_SPARSEMEM_MANUAL is not set CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set CONFIG_SPLIT_PTLOCK_CPUS=4096 CONFIG_ALIGNMENT_TRAP=y # # Boot options # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_CMDLINE="" # CONFIG_XIP_KERNEL is not set # # Floating point emulation # # # At least one emulation must be selected # CONFIG_FPE_NWFPE=y # CONFIG_FPE_NWFPE_XP is not set # CONFIG_FPE_FASTFPE is not set # CONFIG_VFP is not set # # Userspace binary formats # CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_AOUT is not set # CONFIG_BINFMT_MISC is not set # CONFIG_ARTHUR is not set # # Power management options # # CONFIG_PM is not set # CONFIG_APM is not set # # Networking # CONFIG_NET=y # # Networking options # # CONFIG_NETDEBUG is not set CONFIG_PACKET=y CONFIG_PACKET_MMAP=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_ASK_IP_FIB_HASH=y # CONFIG_IP_FIB_TRIE is not set CONFIG_IP_FIB_HASH=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_FWMARK=y # CONFIG_IP_ROUTE_MULTIPATH is not set # CONFIG_IP_ROUTE_VERBOSE is not set # CONFIG_IP_PNP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set # CONFIG_INET_XFRM_TUNNEL is not set # CONFIG_INET_TUNNEL is not set CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_BIC=y # # IP: Virtual Server Configuration # # CONFIG_IP_VS is not set # CONFIG_IPV6 is not set # CONFIG_INET6_XFRM_TUNNEL is not set # CONFIG_INET6_TUNNEL is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set # CONFIG_BRIDGE_NETFILTER is not set # # Core Netfilter Configuration # # CONFIG_NETFILTER_NETLINK is not set CONFIG_NETFILTER_XTABLES=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y # CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set CONFIG_NETFILTER_XT_TARGET_MARK=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y # CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DCCP=y CONFIG_NETFILTER_XT_MATCH_ESP=y CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y CONFIG_NETFILTER_XT_MATCH_REALM=y CONFIG_NETFILTER_XT_MATCH_SCTP=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TCPMSS=y # # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=y CONFIG_IP_NF_CT_ACCT=y CONFIG_IP_NF_CONNTRACK_MARK=y CONFIG_IP_NF_CONNTRACK_EVENTS=y CONFIG_IP_NF_CT_PROTO_SCTP=m CONFIG_IP_NF_FTP=m CONFIG_IP_NF_IRC=m CONFIG_IP_NF_NETBIOS_NS=m CONFIG_IP_NF_TFTP=m CONFIG_IP_NF_AMANDA=m CONFIG_IP_NF_PPTP=m CONFIG_IP_NF_L2TP=y CONFIG_IP_NF_IPSEC=y CONFIG_IP_NF_ESP=y CONFIG_IP_NF_H323=m CONFIG_IP_NF_QUEUE=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_IPRANGE=y # CONFIG_IP_NF_MATCH_LAYER7 is not set CONFIG_IP_NF_MATCH_TOS=y CONFIG_IP_NF_MATCH_RECENT=y CONFIG_IP_NF_MATCH_ECN=y CONFIG_IP_NF_MATCH_DSCP=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_MATCH_OWNER=y CONFIG_IP_NF_MATCH_ADDRTYPE=y CONFIG_IP_NF_MATCH_HASHLIMIT=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y CONFIG_IP_NF_TARGET_LOG=y CONFIG_IP_NF_TARGET_ULOG=y CONFIG_IP_NF_TARGET_TCPMSS=y CONFIG_IP_NF_NAT=y CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=y CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_TARGET_NETMAP=y CONFIG_IP_NF_TARGET_SAME=y CONFIG_IP_NF_NAT_SNMP_BASIC=y CONFIG_IP_NF_NAT_IRC=m CONFIG_IP_NF_NAT_FTP=m CONFIG_IP_NF_NAT_TFTP=m CONFIG_IP_NF_NAT_AMANDA=m CONFIG_IP_NF_NAT_PPTP=m CONFIG_IP_NF_NAT_ESP=y CONFIG_IP_NF_NAT_H323=m CONFIG_IP_NF_MANGLE=y CONFIG_IP_NF_TARGET_TOS=y CONFIG_IP_NF_TARGET_ECN=y CONFIG_IP_NF_TARGET_DSCP=y CONFIG_IP_NF_TARGET_TTL=y CONFIG_IP_NF_TARGET_CLUSTERIP=y CONFIG_IP_NF_RAW=y CONFIG_IP_NF_ARPTABLES=y # CONFIG_IP_NF_ARPFILTER is not set # CONFIG_IP_NF_ARP_MANGLE is not set CONFIG_IP_NF_NAT_MMS=m CONFIG_IP_NF_MMS=m CONFIG_IP_NF_NAT_QUAKE3=m CONFIG_IP_NF_QUAKE3=m CONFIG_IP_NF_MATCH_RPC=m CONFIG_IP_NF_RSH=m CONFIG_IP_NF_NAT_RTSP=m CONFIG_IP_NF_RTSP=m CONFIG_IP_NF_NAT_TALK=m CONFIG_IP_NF_TALK=m # # Bridge: Netfilter Configuration # # CONFIG_BRIDGE_NF_EBTABLES is not set # # DCCP Configuration (EXPERIMENTAL) # # CONFIG_IP_DCCP is not set # # SCTP Configuration (EXPERIMENTAL) # # CONFIG_IP_SCTP is not set # # TIPC Configuration (EXPERIMENTAL) # # CONFIG_TIPC is not set # CONFIG_ATM is not set CONFIG_BRIDGE=y # CONFIG_VLAN_8021Q is not set # CONFIG_DECNET is not set CONFIG_LLC=y # CONFIG_LLC2 is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set # # QoS and/or fair queueing # CONFIG_NET_SCHED=y # CONFIG_NET_SCH_CLK_JIFFIES is not set CONFIG_NET_SCH_CLK_GETTIMEOFDAY=y # CONFIG_NET_SCH_CLK_CPU is not set # # Queueing/Scheduling # # CONFIG_NET_SCH_CBQ is not set CONFIG_NET_SCH_HTB=y # CONFIG_NET_SCH_HFSC is not set # CONFIG_NET_SCH_PRIO is not set # CONFIG_NET_SCH_RED is not set CONFIG_NET_SCH_SFQ=y # CONFIG_NET_SCH_TEQL is not set # CONFIG_NET_SCH_TBF is not set # CONFIG_NET_SCH_GRED is not set CONFIG_NET_SCH_DSMARK=y # CONFIG_NET_SCH_NETEM is not set CONFIG_NET_SCH_INGRESS=y # # Classification # CONFIG_NET_CLS=y CONFIG_NET_CLS_BASIC=y CONFIG_NET_CLS_TCINDEX=y # CONFIG_NET_CLS_ROUTE4 is not set CONFIG_NET_CLS_ROUTE=y CONFIG_NET_CLS_FW=y CONFIG_NET_CLS_U32=y CONFIG_CLS_U32_PERF=y CONFIG_CLS_U32_MARK=y CONFIG_NET_CLS_RSVP=y # CONFIG_NET_CLS_RSVP6 is not set CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_STACK=32 # CONFIG_NET_EMATCH_CMP is not set # CONFIG_NET_EMATCH_NBYTE is not set CONFIG_NET_EMATCH_U32=y # CONFIG_NET_EMATCH_META is not set # CONFIG_NET_EMATCH_TEXT is not set # CONFIG_NET_CLS_ACT is not set # CONFIG_NET_CLS_POLICE is not set CONFIG_NET_CLS_IND=y CONFIG_NET_ESTIMATOR=y # # Network testing # # CONFIG_NET_PKTGEN is not set # CONFIG_HAMRADIO is not set # CONFIG_IRDA is not set # CONFIG_BT is not set # CONFIG_IEEE80211 is not set CONFIG_WIRELESS_EXT=y # # Device Drivers # # # Generic Driver Options # CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=m # # Connector - unified userspace <-> kernelspace linker # # CONFIG_CONNECTOR is not set # # Memory Technology Devices (MTD) # CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set # CONFIG_MTD_CONCAT is not set CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set CONFIG_MTD_CMDLINE_PARTS=y # CONFIG_MTD_AFS_PARTS is not set # # User Modules And Translation Layers # CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y # CONFIG_FTL is not set # CONFIG_NFTL is not set # CONFIG_INFTL is not set # CONFIG_RFD_FTL is not set # # RAM/ROM/Flash chip drivers # CONFIG_MTD_CFI=y # CONFIG_MTD_JEDECPROBE is not set CONFIG_MTD_GEN_PROBE=y CONFIG_MTD_CFI_ADV_OPTIONS=y CONFIG_MTD_CFI_NOSWAP=y # CONFIG_MTD_CFI_BE_BYTE_SWAP is not set # CONFIG_MTD_CFI_LE_BYTE_SWAP is not set CONFIG_MTD_CFI_GEOMETRY=y CONFIG_MTD_MAP_BANK_WIDTH_1=y # CONFIG_MTD_MAP_BANK_WIDTH_2 is not set # CONFIG_MTD_MAP_BANK_WIDTH_4 is not set # CONFIG_MTD_MAP_BANK_WIDTH_8 is not set # CONFIG_MTD_MAP_BANK_WIDTH_16 is not set # CONFIG_MTD_MAP_BANK_WIDTH_32 is not set CONFIG_MTD_CFI_I1=y # CONFIG_MTD_CFI_I2 is not set # CONFIG_MTD_CFI_I4 is not set # CONFIG_MTD_CFI_I8 is not set # CONFIG_MTD_OTP is not set CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y # CONFIG_MTD_CFI_STAA is not set CONFIG_MTD_CFI_UTIL=y # CONFIG_MTD_RAM is not set # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set # CONFIG_MTD_OBSOLETE_CHIPS is not set # # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set CONFIG_MTD_PHYSMAP=y CONFIG_MTD_PHYSMAP_START=0x1F000000 CONFIG_MTD_PHYSMAP_LEN=0x800000 CONFIG_MTD_PHYSMAP_BANKWIDTH=1 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_PLATRAM is not set # # Self-contained MTD device drivers # # CONFIG_MTD_PMC551 is not set # CONFIG_MTD_SLRAM is not set # CONFIG_MTD_PHRAM is not set # CONFIG_MTD_MTDRAM is not set # CONFIG_MTD_BLOCK2MTD is not set # # Disk-On-Chip Device Drivers # # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set # # NAND Flash Device Drivers # # CONFIG_MTD_NAND is not set # # OneNAND Flash Device Drivers # # CONFIG_MTD_ONENAND is not set # # Parallel port support # # CONFIG_PARPORT is not set # # Plug and Play support # # # Block devices # # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set # CONFIG_BLK_DEV_DAC960 is not set # CONFIG_BLK_DEV_UMEM is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set # CONFIG_CDROM_PKTCDVD is not set # CONFIG_ATA_OVER_ETH is not set # # SCSI device support # # CONFIG_RAID_ATTRS is not set # CONFIG_SCSI is not set # # Multi-device support (RAID and LVM) # # CONFIG_MD is not set # # Fusion MPT device support # # CONFIG_FUSION is not set # # IEEE 1394 (FireWire) support # # CONFIG_IEEE1394 is not set # # I2O device support # # CONFIG_I2O is not set # # Network device support # CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set # CONFIG_BONDING is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set # # ARCnet devices # # CONFIG_ARCNET is not set # # PHY device support # # CONFIG_PHYLIB is not set # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y CONFIG_MII=y # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set # CONFIG_CASSINI is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_SMC91X is not set # CONFIG_DM9000 is not set # # Tulip family network device support # # CONFIG_NET_TULIP is not set # CONFIG_HP100 is not set CONFIG_NET_PCI=y # CONFIG_PCNET32 is not set # CONFIG_AMD8111_ETH is not set # CONFIG_ADAPTEC_STARFIRE is not set # CONFIG_B44 is not set # CONFIG_FORCEDETH is not set # CONFIG_DGRS is not set # CONFIG_EEPRO100 is not set # CONFIG_E100 is not set # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set # CONFIG_8139CP is not set # CONFIG_8139TOO is not set # CONFIG_SIS900 is not set # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set # # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set # CONFIG_DL2K is not set # CONFIG_E1000 is not set # CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_R8169 is not set # CONFIG_SIS190 is not set # CONFIG_SKGE is not set # CONFIG_SKY2 is not set # CONFIG_SK98LIN is not set # CONFIG_VIA_VELOCITY is not set # CONFIG_TIGON3 is not set # CONFIG_BNX2 is not set # # Ethernet (10000 Mbit) # # CONFIG_CHELSIO_T1 is not set # CONFIG_IXGB is not set # CONFIG_S2IO is not set # # Token Ring devices # # CONFIG_TR is not set # # Wireless LAN (non-hamradio) # CONFIG_NET_RADIO=y # CONFIG_NET_WIRELESS_RTNETLINK is not set # # Obsolete Wireless cards support (pre-802.11) # # CONFIG_STRIP is not set # # Wireless 802.11b ISA/PCI cards support # # CONFIG_IPW2100 is not set # CONFIG_IPW2200 is not set # CONFIG_HERMES is not set # CONFIG_ATMEL is not set # # Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support # # CONFIG_PRISM54 is not set # CONFIG_HOSTAP is not set CONFIG_NET_WIRELESS=y # # Wan interfaces # # CONFIG_WAN is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set CONFIG_PPP=y CONFIG_PPP_MULTILINK=y CONFIG_PPP_FILTER=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y CONFIG_PPP_DEFLATE=y CONFIG_PPP_BSDCOMP=y CONFIG_PPP_MPPE=y CONFIG_PPPOE=y # CONFIG_SLIP is not set # CONFIG_SHAPER is not set # CONFIG_NETCONSOLE is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set # # ISDN subsystem # # CONFIG_ISDN is not set # # Input device support # # CONFIG_INPUT is not set # # Hardware I/O ports # # CONFIG_SERIO is not set # CONFIG_GAMEPORT is not set # # Character devices # # CONFIG_VT is not set CONFIG_SERIAL_NONSTANDARD=y # CONFIG_COMPUTONE is not set # CONFIG_ROCKETPORT is not set # CONFIG_CYCLADES is not set # CONFIG_DIGIEPCA is not set # CONFIG_MOXA_INTELLIO is not set # CONFIG_MOXA_SMARTIO is not set # CONFIG_ISI is not set # CONFIG_SYNCLINKMP is not set # CONFIG_SYNCLINK_GT is not set CONFIG_N_HDLC=y # CONFIG_RISCOM8 is not set # CONFIG_SPECIALIX is not set # CONFIG_SX is not set # CONFIG_RIO is not set # CONFIG_STALDRV is not set # # Serial drivers # CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y # CONFIG_SERIAL_8250_PCI is not set CONFIG_SERIAL_8250_NR_UARTS=2 CONFIG_SERIAL_8250_RUNTIME_UARTS=2 # CONFIG_SERIAL_8250_EXTENDED is not set # # Non-8250 serial port support # CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_JSM is not set CONFIG_UNIX98_PTYS=y # CONFIG_LEGACY_PTYS is not set # # IPMI # # CONFIG_IPMI_HANDLER is not set # # Watchdog Cards # CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set # # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set CONFIG_5VT_WATCHDOG=y # # PCI-based Watchdog Cards # # CONFIG_PCIPCWATCHDOG is not set # CONFIG_WDTPCI is not set # CONFIG_NVRAM is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set # # Ftape, the floppy tape device driver # # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set # # TPM devices # # CONFIG_TCG_TPM is not set # CONFIG_TELCLOCK is not set # # I2C support # # CONFIG_I2C is not set # # SPI support # # CONFIG_SPI is not set # CONFIG_SPI_MASTER is not set # # Dallas's 1-wire bus # # CONFIG_W1 is not set # # Hardware Monitoring support # # CONFIG_HWMON is not set # CONFIG_HWMON_VID is not set # # Misc devices # # # LED devices # # CONFIG_NEW_LEDS is not set # # LED drivers # # # LED Triggers # # # Multimedia devices # # CONFIG_VIDEO_DEV is not set CONFIG_VIDEO_V4L2=y # # Digital Video Broadcasting Devices # # CONFIG_DVB is not set # # Graphics support # # CONFIG_FB is not set # # Sound # # CONFIG_SOUND is not set # # USB support # CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB_ARCH_HAS_OHCI=y CONFIG_USB_ARCH_HAS_EHCI=y # CONFIG_USB is not set # # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' # # # USB Gadget Support # # CONFIG_USB_GADGET is not set # # MMC/SD Card support # # CONFIG_MMC is not set # # Real Time Clock # # CONFIG_RTC_CLASS is not set # # File systems # # CONFIG_EXT2_FS is not set # CONFIG_EXT3_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set # CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_INOTIFY is not set # CONFIG_QUOTA is not set # CONFIG_DNOTIFY is not set # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set # CONFIG_FUSE_FS is not set # # CD-ROM/DVD Filesystems # # CONFIG_ISO9660_FS is not set # CONFIG_UDF_FS is not set # # DOS/FAT/NT Filesystems # # CONFIG_MSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_NTFS_FS is not set # # Pseudo filesystems # CONFIG_PROC_FS=y CONFIG_SYSFS=y # CONFIG_TMPFS is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems # # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set # CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y CONFIG_SQUASHFS_EMBEDDED=y CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 # CONFIG_SQUASHFS_VMALLOC is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set # # Network File Systems # # CONFIG_NFS_FS is not set # CONFIG_NFSD is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set # CONFIG_9P_FS is not set # # Partition Types # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y # # Native Language Support # # CONFIG_NLS is not set # # Profiling support # # CONFIG_PROFILING is not set # # Kernel hacking # # CONFIG_PRINTK_TIME is not set # CONFIG_MAGIC_SYSRQ is not set # CONFIG_DEBUG_KERNEL is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_DEBUG_BUGVERBOSE is not set # CONFIG_DEBUG_FS is not set CONFIG_FRAME_POINTER=y # CONFIG_UNWIND_INFO is not set # CONFIG_DEBUG_USER is not set # # Security options # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set # # Cryptographic options # CONFIG_CRYPTO=y # CONFIG_CRYPTO_HMAC is not set # CONFIG_CRYPTO_NULL is not set # CONFIG_CRYPTO_MD4 is not set # CONFIG_CRYPTO_MD5 is not set CONFIG_CRYPTO_SHA1=y # CONFIG_CRYPTO_SHA256 is not set # CONFIG_CRYPTO_SHA512 is not set # CONFIG_CRYPTO_WP512 is not set # CONFIG_CRYPTO_TGR192 is not set # CONFIG_CRYPTO_DES is not set # CONFIG_CRYPTO_BLOWFISH is not set # CONFIG_CRYPTO_TWOFISH is not set # CONFIG_CRYPTO_SERPENT is not set # CONFIG_CRYPTO_AES is not set # CONFIG_CRYPTO_CAST5 is not set # CONFIG_CRYPTO_CAST6 is not set # CONFIG_CRYPTO_TEA is not set CONFIG_CRYPTO_ARC4=y # CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_ANUBIS is not set # CONFIG_CRYPTO_DEFLATE is not set # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_CRC32C is not set # CONFIG_CRYPTO_TEST is not set # # OCF Configuration # # CONFIG_OCF_OCF is not set # # Hardware crypto devices # # # Library routines # CONFIG_CRC_CCITT=y # CONFIG_CRC16 is not set CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y CONFIG_TEXTSEARCH=y CONFIG_TEXTSEARCH_KMP=y CONFIG_TEXTSEARCH_BM=y CONFIG_TEXTSEARCH_FSM=y linux-2.6.17/COPYING0000644000000000000000000004440510564537530012516 0ustar rootroot NOTE! This copyright does *not* cover user programs that use kernel services by normal system calls - this is merely considered normal use of the kernel, and does *not* fall under the heading of "derived work". Also note that the GPL below is copyrighted by the Free Software Foundation, but the instance of code that it refers to (the Linux kernel) is copyrighted by me and others who actually wrote it. Also note that the only valid version of the GPL as far as the kernel is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. Linus Torvalds ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. linux-2.6.17/.config.r190000644000000000000000000005155210574211001013323 0ustar rootroot# # Automatically generated make config: don't edit # Linux kernel version: 2.6.17 # Thu Feb 15 11:55:41 2007 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_VECTORS_BASE=0xffff0000 # # Code maturity level options # CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 # # General setup # CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y # CONFIG_SWAP is not set CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y # CONFIG_BSD_PROCESS_ACCT_V3 is not set CONFIG_SYSCTL=y # CONFIG_AUDIT is not set # CONFIG_IKCONFIG is not set # CONFIG_RELAY is not set CONFIG_INITRAMFS_SOURCE="" CONFIG_UID16=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EMBEDDED=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set CONFIG_FUTEX=y CONFIG_EPOLL=y # CONFIG_SHMEM is not set CONFIG_SLAB=y CONFIG_TINY_SHMEM=y CONFIG_BASE_SMALL=1 # CONFIG_SLOB is not set # # Loadable module support # CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set # CONFIG_KMOD is not set # # Block layer # # CONFIG_BLK_DEV_IO_TRACE is not set # # IO Schedulers # CONFIG_IOSCHED_NOOP=y # CONFIG_IOSCHED_AS is not set CONFIG_IOSCHED_DEADLINE=y # CONFIG_IOSCHED_CFQ is not set # CONFIG_DEFAULT_AS is not set CONFIG_DEFAULT_DEADLINE=y # CONFIG_DEFAULT_CFQ is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="deadline" # # System Type # # CONFIG_ARCH_CLPS7500 is not set # CONFIG_ARCH_CLPS711X is not set # CONFIG_ARCH_CO285 is not set # CONFIG_ARCH_EBSA110 is not set # CONFIG_ARCH_EP93XX is not set # CONFIG_ARCH_FOOTBRIDGE is not set # CONFIG_ARCH_INTEGRATOR is not set # CONFIG_ARCH_IOP3XX is not set # CONFIG_ARCH_IXP4XX is not set # CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_IXP23XX is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_RPC is not set # CONFIG_ARCH_SA1100 is not set # CONFIG_ARCH_S3C2410 is not set # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_OMAP is not set # CONFIG_ARCH_VERSATILE is not set CONFIG_ARCH_FV13XX=y # CONFIG_ARCH_GENESIS is not set # CONFIG_ARCH_REALVIEW is not set # CONFIG_ARCH_IMX is not set # CONFIG_ARCH_H720X is not set # CONFIG_ARCH_AAEC2000 is not set # CONFIG_ARCH_AT91RM9200 is not set # # Processor Type # CONFIG_CPU_32=y # CONFIG_CPU_ARM926T is not set CONFIG_CPU_ARM926EJS=y CONFIG_CPU_32v5=y CONFIG_CPU_ABRT_EV5TJ=y CONFIG_CPU_CACHE_VIVT=y CONFIG_CPU_COPY_V4WB=y CONFIG_CPU_TLB_V4WBI=y # # Processor Features # # CONFIG_ARM_THUMB is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set # CONFIG_CPU_DCACHE_WRITETHROUGH is not set # CONFIG_CPU_CACHE_ROUND_ROBIN is not set # # Bus support # # CONFIG_ARM_AMBA is not set CONFIG_PCI=y # # PCCARD (PCMCIA/CardBus) support # # CONFIG_PCCARD is not set # # Kernel Features # # CONFIG_PREEMPT is not set # CONFIG_NO_IDLE_HZ is not set CONFIG_HZ=100 # CONFIG_AEABI is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y # CONFIG_DISCONTIGMEM_MANUAL is not set # CONFIG_SPARSEMEM_MANUAL is not set CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set CONFIG_SPLIT_PTLOCK_CPUS=4096 CONFIG_ALIGNMENT_TRAP=y # # Boot options # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_CMDLINE="" # CONFIG_XIP_KERNEL is not set # # Floating point emulation # # # At least one emulation must be selected # CONFIG_FPE_NWFPE=y # CONFIG_FPE_NWFPE_XP is not set # CONFIG_FPE_FASTFPE is not set # CONFIG_VFP is not set # # Userspace binary formats # CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_AOUT is not set # CONFIG_BINFMT_MISC is not set # CONFIG_ARTHUR is not set # # Power management options # # CONFIG_PM is not set # CONFIG_APM is not set # # Networking # CONFIG_NET=y # # Networking options # # CONFIG_NETDEBUG is not set CONFIG_PACKET=y CONFIG_PACKET_MMAP=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_ASK_IP_FIB_HASH=y # CONFIG_IP_FIB_TRIE is not set CONFIG_IP_FIB_HASH=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_FWMARK=y # CONFIG_IP_ROUTE_MULTIPATH is not set # CONFIG_IP_ROUTE_VERBOSE is not set # CONFIG_IP_PNP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set # CONFIG_INET_XFRM_TUNNEL is not set # CONFIG_INET_TUNNEL is not set CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_BIC=y # # IP: Virtual Server Configuration # # CONFIG_IP_VS is not set # CONFIG_IPV6 is not set # CONFIG_INET6_XFRM_TUNNEL is not set # CONFIG_INET6_TUNNEL is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set # CONFIG_BRIDGE_NETFILTER is not set # # Core Netfilter Configuration # # CONFIG_NETFILTER_NETLINK is not set CONFIG_NETFILTER_XTABLES=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y # CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set CONFIG_NETFILTER_XT_TARGET_MARK=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y # CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DCCP=y CONFIG_NETFILTER_XT_MATCH_ESP=y CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y CONFIG_NETFILTER_XT_MATCH_REALM=y CONFIG_NETFILTER_XT_MATCH_SCTP=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TCPMSS=y # # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=y CONFIG_IP_NF_CT_ACCT=y CONFIG_IP_NF_CONNTRACK_MARK=y CONFIG_IP_NF_CONNTRACK_EVENTS=y CONFIG_IP_NF_CT_PROTO_SCTP=m CONFIG_IP_NF_FTP=m CONFIG_IP_NF_IRC=m CONFIG_IP_NF_NETBIOS_NS=m CONFIG_IP_NF_TFTP=m CONFIG_IP_NF_AMANDA=m CONFIG_IP_NF_PPTP=m CONFIG_IP_NF_L2TP=y CONFIG_IP_NF_IPSEC=y CONFIG_IP_NF_ESP=y CONFIG_IP_NF_H323=m CONFIG_IP_NF_QUEUE=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_IPRANGE=y # CONFIG_IP_NF_MATCH_LAYER7 is not set CONFIG_IP_NF_MATCH_TOS=y CONFIG_IP_NF_MATCH_RECENT=y CONFIG_IP_NF_MATCH_ECN=y CONFIG_IP_NF_MATCH_DSCP=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_MATCH_OWNER=y CONFIG_IP_NF_MATCH_ADDRTYPE=y CONFIG_IP_NF_MATCH_HASHLIMIT=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y CONFIG_IP_NF_TARGET_LOG=y CONFIG_IP_NF_TARGET_ULOG=y CONFIG_IP_NF_TARGET_TCPMSS=y CONFIG_IP_NF_NAT=y CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=y CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_TARGET_NETMAP=y CONFIG_IP_NF_TARGET_SAME=y CONFIG_IP_NF_NAT_SNMP_BASIC=y CONFIG_IP_NF_NAT_IRC=m CONFIG_IP_NF_NAT_FTP=m CONFIG_IP_NF_NAT_TFTP=m CONFIG_IP_NF_NAT_AMANDA=m CONFIG_IP_NF_NAT_PPTP=m CONFIG_IP_NF_NAT_ESP=y CONFIG_IP_NF_NAT_H323=m CONFIG_IP_NF_MANGLE=y CONFIG_IP_NF_TARGET_TOS=y CONFIG_IP_NF_TARGET_ECN=y CONFIG_IP_NF_TARGET_DSCP=y CONFIG_IP_NF_TARGET_TTL=y CONFIG_IP_NF_TARGET_CLUSTERIP=y CONFIG_IP_NF_RAW=y CONFIG_IP_NF_ARPTABLES=y # CONFIG_IP_NF_ARPFILTER is not set # CONFIG_IP_NF_ARP_MANGLE is not set CONFIG_IP_NF_NAT_MMS=m CONFIG_IP_NF_MMS=m CONFIG_IP_NF_NAT_QUAKE3=m CONFIG_IP_NF_QUAKE3=m CONFIG_IP_NF_MATCH_RPC=m CONFIG_IP_NF_RSH=m CONFIG_IP_NF_NAT_RTSP=m CONFIG_IP_NF_RTSP=m CONFIG_IP_NF_NAT_TALK=m CONFIG_IP_NF_TALK=m # # Bridge: Netfilter Configuration # # CONFIG_BRIDGE_NF_EBTABLES is not set # # DCCP Configuration (EXPERIMENTAL) # # CONFIG_IP_DCCP is not set # # SCTP Configuration (EXPERIMENTAL) # # CONFIG_IP_SCTP is not set # # TIPC Configuration (EXPERIMENTAL) # # CONFIG_TIPC is not set # CONFIG_ATM is not set CONFIG_BRIDGE=y # CONFIG_VLAN_8021Q is not set # CONFIG_DECNET is not set CONFIG_LLC=y # CONFIG_LLC2 is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set # # QoS and/or fair queueing # CONFIG_NET_SCHED=y # CONFIG_NET_SCH_CLK_JIFFIES is not set CONFIG_NET_SCH_CLK_GETTIMEOFDAY=y # CONFIG_NET_SCH_CLK_CPU is not set # # Queueing/Scheduling # # CONFIG_NET_SCH_CBQ is not set CONFIG_NET_SCH_HTB=y # CONFIG_NET_SCH_HFSC is not set # CONFIG_NET_SCH_PRIO is not set # CONFIG_NET_SCH_RED is not set CONFIG_NET_SCH_SFQ=y # CONFIG_NET_SCH_TEQL is not set # CONFIG_NET_SCH_TBF is not set # CONFIG_NET_SCH_GRED is not set CONFIG_NET_SCH_DSMARK=y # CONFIG_NET_SCH_NETEM is not set CONFIG_NET_SCH_INGRESS=y # # Classification # CONFIG_NET_CLS=y CONFIG_NET_CLS_BASIC=y CONFIG_NET_CLS_TCINDEX=y # CONFIG_NET_CLS_ROUTE4 is not set CONFIG_NET_CLS_ROUTE=y CONFIG_NET_CLS_FW=y CONFIG_NET_CLS_U32=y CONFIG_CLS_U32_PERF=y CONFIG_CLS_U32_MARK=y CONFIG_NET_CLS_RSVP=y # CONFIG_NET_CLS_RSVP6 is not set CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_STACK=32 # CONFIG_NET_EMATCH_CMP is not set # CONFIG_NET_EMATCH_NBYTE is not set CONFIG_NET_EMATCH_U32=y # CONFIG_NET_EMATCH_META is not set # CONFIG_NET_EMATCH_TEXT is not set # CONFIG_NET_CLS_ACT is not set # CONFIG_NET_CLS_POLICE is not set CONFIG_NET_CLS_IND=y CONFIG_NET_ESTIMATOR=y # # Network testing # # CONFIG_NET_PKTGEN is not set # CONFIG_HAMRADIO is not set # CONFIG_IRDA is not set # CONFIG_BT is not set # CONFIG_IEEE80211 is not set CONFIG_WIRELESS_EXT=y # # Device Drivers # # # Generic Driver Options # CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=m # # Connector - unified userspace <-> kernelspace linker # # CONFIG_CONNECTOR is not set # # Memory Technology Devices (MTD) # CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set # CONFIG_MTD_CONCAT is not set CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set CONFIG_MTD_CMDLINE_PARTS=y # CONFIG_MTD_AFS_PARTS is not set # # User Modules And Translation Layers # CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y # CONFIG_FTL is not set # CONFIG_NFTL is not set # CONFIG_INFTL is not set # CONFIG_RFD_FTL is not set # # RAM/ROM/Flash chip drivers # CONFIG_MTD_CFI=y # CONFIG_MTD_JEDECPROBE is not set CONFIG_MTD_GEN_PROBE=y CONFIG_MTD_CFI_ADV_OPTIONS=y CONFIG_MTD_CFI_NOSWAP=y # CONFIG_MTD_CFI_BE_BYTE_SWAP is not set # CONFIG_MTD_CFI_LE_BYTE_SWAP is not set CONFIG_MTD_CFI_GEOMETRY=y CONFIG_MTD_MAP_BANK_WIDTH_1=y # CONFIG_MTD_MAP_BANK_WIDTH_2 is not set # CONFIG_MTD_MAP_BANK_WIDTH_4 is not set # CONFIG_MTD_MAP_BANK_WIDTH_8 is not set # CONFIG_MTD_MAP_BANK_WIDTH_16 is not set # CONFIG_MTD_MAP_BANK_WIDTH_32 is not set CONFIG_MTD_CFI_I1=y # CONFIG_MTD_CFI_I2 is not set # CONFIG_MTD_CFI_I4 is not set # CONFIG_MTD_CFI_I8 is not set # CONFIG_MTD_OTP is not set CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y # CONFIG_MTD_CFI_STAA is not set CONFIG_MTD_CFI_UTIL=y # CONFIG_MTD_RAM is not set # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set # CONFIG_MTD_OBSOLETE_CHIPS is not set # # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set CONFIG_MTD_PHYSMAP=y CONFIG_MTD_PHYSMAP_START=0x1F000000 CONFIG_MTD_PHYSMAP_LEN=0x800000 CONFIG_MTD_PHYSMAP_BANKWIDTH=1 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_PLATRAM is not set # # Self-contained MTD device drivers # # CONFIG_MTD_PMC551 is not set # CONFIG_MTD_SLRAM is not set # CONFIG_MTD_PHRAM is not set # CONFIG_MTD_MTDRAM is not set # CONFIG_MTD_BLOCK2MTD is not set # # Disk-On-Chip Device Drivers # # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set # # NAND Flash Device Drivers # # CONFIG_MTD_NAND is not set # # OneNAND Flash Device Drivers # # CONFIG_MTD_ONENAND is not set # # Parallel port support # # CONFIG_PARPORT is not set # # Plug and Play support # # # Block devices # # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set # CONFIG_BLK_DEV_DAC960 is not set # CONFIG_BLK_DEV_UMEM is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set # CONFIG_CDROM_PKTCDVD is not set # CONFIG_ATA_OVER_ETH is not set # # SCSI device support # # CONFIG_RAID_ATTRS is not set # CONFIG_SCSI is not set # # Multi-device support (RAID and LVM) # # CONFIG_MD is not set # # Fusion MPT device support # # CONFIG_FUSION is not set # # IEEE 1394 (FireWire) support # # CONFIG_IEEE1394 is not set # # I2O device support # # CONFIG_I2O is not set # # Network device support # CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set # CONFIG_BONDING is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set # # ARCnet devices # # CONFIG_ARCNET is not set # # PHY device support # # CONFIG_PHYLIB is not set # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y CONFIG_MII=y # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set # CONFIG_CASSINI is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_SMC91X is not set # CONFIG_DM9000 is not set # # Tulip family network device support # # CONFIG_NET_TULIP is not set # CONFIG_HP100 is not set CONFIG_NET_PCI=y # CONFIG_PCNET32 is not set # CONFIG_AMD8111_ETH is not set # CONFIG_ADAPTEC_STARFIRE is not set # CONFIG_B44 is not set # CONFIG_FORCEDETH is not set # CONFIG_DGRS is not set # CONFIG_EEPRO100 is not set # CONFIG_E100 is not set # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set # CONFIG_8139CP is not set # CONFIG_8139TOO is not set # CONFIG_SIS900 is not set # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set # # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set # CONFIG_DL2K is not set # CONFIG_E1000 is not set # CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_R8169 is not set # CONFIG_SIS190 is not set # CONFIG_SKGE is not set # CONFIG_SKY2 is not set # CONFIG_SK98LIN is not set # CONFIG_VIA_VELOCITY is not set # CONFIG_TIGON3 is not set # CONFIG_BNX2 is not set # # Ethernet (10000 Mbit) # # CONFIG_CHELSIO_T1 is not set # CONFIG_IXGB is not set # CONFIG_S2IO is not set # # Token Ring devices # # CONFIG_TR is not set # # Wireless LAN (non-hamradio) # CONFIG_NET_RADIO=y # CONFIG_NET_WIRELESS_RTNETLINK is not set # # Obsolete Wireless cards support (pre-802.11) # # CONFIG_STRIP is not set # # Wireless 802.11b ISA/PCI cards support # # CONFIG_IPW2100 is not set # CONFIG_IPW2200 is not set # CONFIG_HERMES is not set # CONFIG_ATMEL is not set # # Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support # # CONFIG_PRISM54 is not set # CONFIG_HOSTAP is not set CONFIG_NET_WIRELESS=y # # Wan interfaces # # CONFIG_WAN is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set CONFIG_PPP=y CONFIG_PPP_MULTILINK=y CONFIG_PPP_FILTER=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y CONFIG_PPP_DEFLATE=y CONFIG_PPP_BSDCOMP=y CONFIG_PPP_MPPE=y CONFIG_PPPOE=y # CONFIG_SLIP is not set # CONFIG_SHAPER is not set # CONFIG_NETCONSOLE is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set # # ISDN subsystem # # CONFIG_ISDN is not set # # Input device support # # CONFIG_INPUT is not set # # Hardware I/O ports # # CONFIG_SERIO is not set # CONFIG_GAMEPORT is not set # # Character devices # # CONFIG_VT is not set CONFIG_SERIAL_NONSTANDARD=y # CONFIG_COMPUTONE is not set # CONFIG_ROCKETPORT is not set # CONFIG_CYCLADES is not set # CONFIG_DIGIEPCA is not set # CONFIG_MOXA_INTELLIO is not set # CONFIG_MOXA_SMARTIO is not set # CONFIG_ISI is not set # CONFIG_SYNCLINKMP is not set # CONFIG_SYNCLINK_GT is not set CONFIG_N_HDLC=y # CONFIG_RISCOM8 is not set # CONFIG_SPECIALIX is not set # CONFIG_SX is not set # CONFIG_RIO is not set # CONFIG_STALDRV is not set # # Serial drivers # CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y # CONFIG_SERIAL_8250_PCI is not set CONFIG_SERIAL_8250_NR_UARTS=2 CONFIG_SERIAL_8250_RUNTIME_UARTS=2 # CONFIG_SERIAL_8250_EXTENDED is not set # # Non-8250 serial port support # CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_JSM is not set CONFIG_UNIX98_PTYS=y # CONFIG_LEGACY_PTYS is not set # # IPMI # # CONFIG_IPMI_HANDLER is not set # # Watchdog Cards # CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set # # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set CONFIG_5VT_WATCHDOG=y # # PCI-based Watchdog Cards # # CONFIG_PCIPCWATCHDOG is not set # CONFIG_WDTPCI is not set # CONFIG_NVRAM is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set # # Ftape, the floppy tape device driver # # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set # # TPM devices # # CONFIG_TCG_TPM is not set # CONFIG_TELCLOCK is not set # # I2C support # # CONFIG_I2C is not set # # SPI support # # CONFIG_SPI is not set # CONFIG_SPI_MASTER is not set # # Dallas's 1-wire bus # # CONFIG_W1 is not set # # Hardware Monitoring support # # CONFIG_HWMON is not set # CONFIG_HWMON_VID is not set # # Misc devices # # # LED devices # # CONFIG_NEW_LEDS is not set # # LED drivers # # # LED Triggers # # # Multimedia devices # # CONFIG_VIDEO_DEV is not set CONFIG_VIDEO_V4L2=y # # Digital Video Broadcasting Devices # # CONFIG_DVB is not set # # Graphics support # # CONFIG_FB is not set # # Sound # # CONFIG_SOUND is not set # # USB support # CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB_ARCH_HAS_OHCI=y CONFIG_USB_ARCH_HAS_EHCI=y # CONFIG_USB is not set # # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' # # # USB Gadget Support # # CONFIG_USB_GADGET is not set # # MMC/SD Card support # # CONFIG_MMC is not set # # Real Time Clock # # CONFIG_RTC_CLASS is not set # # File systems # # CONFIG_EXT2_FS is not set # CONFIG_EXT3_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set # CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_INOTIFY is not set # CONFIG_QUOTA is not set # CONFIG_DNOTIFY is not set # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set # CONFIG_FUSE_FS is not set # # CD-ROM/DVD Filesystems # # CONFIG_ISO9660_FS is not set # CONFIG_UDF_FS is not set # # DOS/FAT/NT Filesystems # # CONFIG_MSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_NTFS_FS is not set # # Pseudo filesystems # CONFIG_PROC_FS=y CONFIG_SYSFS=y # CONFIG_TMPFS is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems # # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set # CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y CONFIG_SQUASHFS_EMBEDDED=y CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 # CONFIG_SQUASHFS_VMALLOC is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set # # Network File Systems # # CONFIG_NFS_FS is not set # CONFIG_NFSD is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set # CONFIG_9P_FS is not set # # Partition Types # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y # # Native Language Support # # CONFIG_NLS is not set # # Profiling support # # CONFIG_PROFILING is not set # # Kernel hacking # # CONFIG_PRINTK_TIME is not set # CONFIG_MAGIC_SYSRQ is not set # CONFIG_DEBUG_KERNEL is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_DEBUG_BUGVERBOSE is not set # CONFIG_DEBUG_FS is not set CONFIG_FRAME_POINTER=y # CONFIG_UNWIND_INFO is not set # CONFIG_DEBUG_USER is not set # # Security options # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set # # Cryptographic options # CONFIG_CRYPTO=y # CONFIG_CRYPTO_HMAC is not set # CONFIG_CRYPTO_NULL is not set # CONFIG_CRYPTO_MD4 is not set # CONFIG_CRYPTO_MD5 is not set CONFIG_CRYPTO_SHA1=y # CONFIG_CRYPTO_SHA256 is not set # CONFIG_CRYPTO_SHA512 is not set # CONFIG_CRYPTO_WP512 is not set # CONFIG_CRYPTO_TGR192 is not set # CONFIG_CRYPTO_DES is not set # CONFIG_CRYPTO_BLOWFISH is not set # CONFIG_CRYPTO_TWOFISH is not set # CONFIG_CRYPTO_SERPENT is not set # CONFIG_CRYPTO_AES is not set # CONFIG_CRYPTO_CAST5 is not set # CONFIG_CRYPTO_CAST6 is not set # CONFIG_CRYPTO_TEA is not set CONFIG_CRYPTO_ARC4=y # CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_ANUBIS is not set # CONFIG_CRYPTO_DEFLATE is not set # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_CRC32C is not set # CONFIG_CRYPTO_TEST is not set # # OCF Configuration # # CONFIG_OCF_OCF is not set # # Hardware crypto devices # # # Library routines # CONFIG_CRC_CCITT=y # CONFIG_CRC16 is not set CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y CONFIG_TEXTSEARCH=y CONFIG_TEXTSEARCH_KMP=y CONFIG_TEXTSEARCH_BM=y CONFIG_TEXTSEARCH_FSM=y linux-2.6.17/fs/0000755000000000000000000000000010651606551012061 5ustar rootrootlinux-2.6.17/fs/udf/0000755000000000000000000000000010574207645012644 5ustar rootrootlinux-2.6.17/fs/udf/ecma_167.h0000644000000000000000000005342510564537530014326 0ustar rootroot/* * ecma_167.h * * This file is based on ECMA-167 3rd edition (June 1997) * http://www.ecma.ch * * Copyright (c) 2001-2002 Ben Fennema * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef _ECMA_167_H #define _ECMA_167_H 1 /* Character set specification (ECMA 167r3 1/7.2.1) */ typedef struct { uint8_t charSetType; uint8_t charSetInfo[63]; } __attribute__ ((packed)) charspec; /* Character Set Type (ECMA 167r3 1/7.2.1.1) */ #define CHARSPEC_TYPE_CS0 0x00 /* (1/7.2.2) */ #define CHARSPEC_TYPE_CS1 0x01 /* (1/7.2.3) */ #define CHARSPEC_TYPE_CS2 0x02 /* (1/7.2.4) */ #define CHARSPEC_TYPE_CS3 0x03 /* (1/7.2.5) */ #define CHARSPEC_TYPE_CS4 0x04 /* (1/7.2.6) */ #define CHARSPEC_TYPE_CS5 0x05 /* (1/7.2.7) */ #define CHARSPEC_TYPE_CS6 0x06 /* (1/7.2.8) */ #define CHARSPEC_TYPE_CS7 0x07 /* (1/7.2.9) */ #define CHARSPEC_TYPE_CS8 0x08 /* (1/7.2.10) */ typedef uint8_t dstring; /* Timestamp (ECMA 167r3 1/7.3) */ typedef struct { __le16 typeAndTimezone; __le16 year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; uint8_t centiseconds; uint8_t hundredsOfMicroseconds; uint8_t microseconds; } __attribute__ ((packed)) timestamp; typedef struct { uint16_t typeAndTimezone; int16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; uint8_t centiseconds; uint8_t hundredsOfMicroseconds; uint8_t microseconds; } __attribute__ ((packed)) kernel_timestamp; /* Type and Time Zone (ECMA 167r3 1/7.3.1) */ #define TIMESTAMP_TYPE_MASK 0xF000 #define TIMESTAMP_TYPE_CUT 0x0000 #define TIMESTAMP_TYPE_LOCAL 0x1000 #define TIMESTAMP_TYPE_AGREEMENT 0x2000 #define TIMESTAMP_TIMEZONE_MASK 0x0FFF /* Entity identifier (ECMA 167r3 1/7.4) */ typedef struct { uint8_t flags; uint8_t ident[23]; uint8_t identSuffix[8]; } __attribute__ ((packed)) regid; /* Flags (ECMA 167r3 1/7.4.1) */ #define ENTITYID_FLAGS_DIRTY 0x00 #define ENTITYID_FLAGS_PROTECTED 0x01 /* Volume Structure Descriptor (ECMA 167r3 2/9.1) */ #define VSD_STD_ID_LEN 5 struct volStructDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t structData[2041]; } __attribute__ ((packed)); /* Standard Identifier (EMCA 167r2 2/9.1.2) */ #define VSD_STD_ID_NSR02 "NSR02" /* (3/9.1) */ /* Standard Identifier (ECMA 167r3 2/9.1.2) */ #define VSD_STD_ID_BEA01 "BEA01" /* (2/9.2) */ #define VSD_STD_ID_BOOT2 "BOOT2" /* (2/9.4) */ #define VSD_STD_ID_CD001 "CD001" /* (ECMA-119) */ #define VSD_STD_ID_CDW02 "CDW02" /* (ECMA-168) */ #define VSD_STD_ID_NSR03 "NSR03" /* (3/9.1) */ #define VSD_STD_ID_TEA01 "TEA01" /* (2/9.3) */ /* Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */ struct beginningExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t structData[2041]; } __attribute__ ((packed)); /* Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */ struct terminatingExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t structData[2041]; } __attribute__ ((packed)); /* Boot Descriptor (ECMA 167r3 2/9.4) */ struct bootDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t reserved1; regid archType; regid bootIdent; __le32 bootExtLocation; __le32 bootExtLength; __le64 loadAddress; __le64 startAddress; timestamp descCreationDateAndTime; __le16 flags; uint8_t reserved2[32]; uint8_t bootUse[1906]; } __attribute__ ((packed)); /* Flags (ECMA 167r3 2/9.4.12) */ #define BOOT_FLAGS_ERASE 0x01 /* Extent Descriptor (ECMA 167r3 3/7.1) */ typedef struct { __le32 extLength; __le32 extLocation; } __attribute__ ((packed)) extent_ad; typedef struct { uint32_t extLength; uint32_t extLocation; } kernel_extent_ad; /* Descriptor Tag (ECMA 167r3 3/7.2) */ typedef struct { __le16 tagIdent; __le16 descVersion; uint8_t tagChecksum; uint8_t reserved; __le16 tagSerialNum; __le16 descCRC; __le16 descCRCLength; __le32 tagLocation; } __attribute__ ((packed)) tag; /* Tag Identifier (ECMA 167r3 3/7.2.1) */ #define TAG_IDENT_PVD 0x0001 #define TAG_IDENT_AVDP 0x0002 #define TAG_IDENT_VDP 0x0003 #define TAG_IDENT_IUVD 0x0004 #define TAG_IDENT_PD 0x0005 #define TAG_IDENT_LVD 0x0006 #define TAG_IDENT_USD 0x0007 #define TAG_IDENT_TD 0x0008 #define TAG_IDENT_LVID 0x0009 /* NSR Descriptor (ECMA 167r3 3/9.1) */ struct NSRDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t reserved; uint8_t structData[2040]; } __attribute__ ((packed)); /* Primary Volume Descriptor (ECMA 167r3 3/10.1) */ struct primaryVolDesc { tag descTag; __le32 volDescSeqNum; __le32 primaryVolDescNum; dstring volIdent[32]; __le16 volSeqNum; __le16 maxVolSeqNum; __le16 interchangeLvl; __le16 maxInterchangeLvl; __le32 charSetList; __le32 maxCharSetList; dstring volSetIdent[128]; charspec descCharSet; charspec explanatoryCharSet; extent_ad volAbstract; extent_ad volCopyright; regid appIdent; timestamp recordingDateAndTime; regid impIdent; uint8_t impUse[64]; __le32 predecessorVolDescSeqLocation; __le16 flags; uint8_t reserved[22]; } __attribute__ ((packed)); /* Flags (ECMA 167r3 3/10.1.21) */ #define PVD_FLAGS_VSID_COMMON 0x0001 /* Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */ struct anchorVolDescPtr { tag descTag; extent_ad mainVolDescSeqExt; extent_ad reserveVolDescSeqExt; uint8_t reserved[480]; } __attribute__ ((packed)); /* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */ struct volDescPtr { tag descTag; __le32 volDescSeqNum; extent_ad nextVolDescSeqExt; uint8_t reserved[484]; } __attribute__ ((packed)); /* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */ struct impUseVolDesc { tag descTag; __le32 volDescSeqNum; regid impIdent; uint8_t impUse[460]; } __attribute__ ((packed)); /* Partition Descriptor (ECMA 167r3 3/10.5) */ struct partitionDesc { tag descTag; __le32 volDescSeqNum; __le16 partitionFlags; __le16 partitionNumber; regid partitionContents; uint8_t partitionContentsUse[128]; __le32 accessType; __le32 partitionStartingLocation; __le32 partitionLength; regid impIdent; uint8_t impUse[128]; uint8_t reserved[156]; } __attribute__ ((packed)); /* Partition Flags (ECMA 167r3 3/10.5.3) */ #define PD_PARTITION_FLAGS_ALLOC 0x0001 /* Partition Contents (ECMA 167r2 3/10.5.3) */ #define PD_PARTITION_CONTENTS_NSR02 "+NSR02" /* Partition Contents (ECMA 167r3 3/10.5.5) */ #define PD_PARTITION_CONTENTS_FDC01 "+FDC01" #define PD_PARTITION_CONTENTS_CD001 "+CD001" #define PD_PARTITION_CONTENTS_CDW02 "+CDW02" #define PD_PARTITION_CONTENTS_NSR03 "+NSR03" /* Access Type (ECMA 167r3 3/10.5.7) */ #define PD_ACCESS_TYPE_NONE 0x00000000 #define PD_ACCESS_TYPE_READ_ONLY 0x00000001 #define PD_ACCESS_TYPE_WRITE_ONCE 0x00000002 #define PD_ACCESS_TYPE_REWRITABLE 0x00000003 #define PD_ACCESS_TYPE_OVERWRITABLE 0x00000004 /* Logical Volume Descriptor (ECMA 167r3 3/10.6) */ struct logicalVolDesc { tag descTag; __le32 volDescSeqNum; charspec descCharSet; dstring logicalVolIdent[128]; __le32 logicalBlockSize; regid domainIdent; uint8_t logicalVolContentsUse[16]; __le32 mapTableLength; __le32 numPartitionMaps; regid impIdent; uint8_t impUse[128]; extent_ad integritySeqExt; uint8_t partitionMaps[0]; } __attribute__ ((packed)); /* Generic Partition Map (ECMA 167r3 3/10.7.1) */ struct genericPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t partitionMapping[0]; } __attribute__ ((packed)); /* Partition Map Type (ECMA 167r3 3/10.7.1.1) */ #define GP_PARTITION_MAP_TYPE_UNDEF 0x00 #define GP_PARTIITON_MAP_TYPE_1 0x01 #define GP_PARTITION_MAP_TYPE_2 0x02 /* Type 1 Partition Map (ECMA 167r3 3/10.7.2) */ struct genericPartitionMap1 { uint8_t partitionMapType; uint8_t partitionMapLength; __le16 volSeqNum; __le16 partitionNum; } __attribute__ ((packed)); /* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */ struct genericPartitionMap2 { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t partitionIdent[62]; } __attribute__ ((packed)); /* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */ struct unallocSpaceDesc { tag descTag; __le32 volDescSeqNum; __le32 numAllocDescs; extent_ad allocDescs[0]; } __attribute__ ((packed)); /* Terminating Descriptor (ECMA 167r3 3/10.9) */ struct terminatingDesc { tag descTag; uint8_t reserved[496]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */ struct logicalVolIntegrityDesc { tag descTag; timestamp recordingDateAndTime; __le32 integrityType; extent_ad nextIntegrityExt; uint8_t logicalVolContentsUse[32]; __le32 numOfPartitions; __le32 lengthOfImpUse; __le32 freeSpaceTable[0]; __le32 sizeTable[0]; uint8_t impUse[0]; } __attribute__ ((packed)); /* Integrity Type (ECMA 167r3 3/10.10.3) */ #define LVID_INTEGRITY_TYPE_OPEN 0x00000000 #define LVID_INTEGRITY_TYPE_CLOSE 0x00000001 /* Recorded Address (ECMA 167r3 4/7.1) */ typedef struct { __le32 logicalBlockNum; __le16 partitionReferenceNum; } __attribute__ ((packed)) lb_addr; /* ... and its in-core analog */ typedef struct { uint32_t logicalBlockNum; uint16_t partitionReferenceNum; } kernel_lb_addr; /* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ typedef struct { __le32 extLength; __le32 extPosition; } __attribute__ ((packed)) short_ad; /* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ typedef struct { __le32 extLength; lb_addr extLocation; uint8_t impUse[6]; } __attribute__ ((packed)) long_ad; typedef struct { uint32_t extLength; kernel_lb_addr extLocation; uint8_t impUse[6]; } kernel_long_ad; /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ typedef struct { __le32 extLength; __le32 recordedLength; __le32 informationLength; lb_addr extLocation; } __attribute__ ((packed)) ext_ad; typedef struct { uint32_t extLength; uint32_t recordedLength; uint32_t informationLength; kernel_lb_addr extLocation; } kernel_ext_ad; /* Descriptor Tag (ECMA 167r3 4/7.2 - See 3/7.2) */ /* Tag Identifier (ECMA 167r3 4/7.2.1) */ #define TAG_IDENT_FSD 0x0100 #define TAG_IDENT_FID 0x0101 #define TAG_IDENT_AED 0x0102 #define TAG_IDENT_IE 0x0103 #define TAG_IDENT_TE 0x0104 #define TAG_IDENT_FE 0x0105 #define TAG_IDENT_EAHD 0x0106 #define TAG_IDENT_USE 0x0107 #define TAG_IDENT_SBD 0x0108 #define TAG_IDENT_PIE 0x0109 #define TAG_IDENT_EFE 0x010A /* File Set Descriptor (ECMA 167r3 4/14.1) */ struct fileSetDesc { tag descTag; timestamp recordingDateAndTime; __le16 interchangeLvl; __le16 maxInterchangeLvl; __le32 charSetList; __le32 maxCharSetList; __le32 fileSetNum; __le32 fileSetDescNum; charspec logicalVolIdentCharSet; dstring logicalVolIdent[128]; charspec fileSetCharSet; dstring fileSetIdent[32]; dstring copyrightFileIdent[32]; dstring abstractFileIdent[32]; long_ad rootDirectoryICB; regid domainIdent; long_ad nextExt; long_ad streamDirectoryICB; uint8_t reserved[32]; } __attribute__ ((packed)); /* Partition Header Descriptor (ECMA 167r3 4/14.3) */ struct partitionHeaderDesc { short_ad unallocSpaceTable; short_ad unallocSpaceBitmap; short_ad partitionIntegrityTable; short_ad freedSpaceTable; short_ad freedSpaceBitmap; uint8_t reserved[88]; } __attribute__ ((packed)); /* File Identifier Descriptor (ECMA 167r3 4/14.4) */ struct fileIdentDesc { tag descTag; __le16 fileVersionNum; uint8_t fileCharacteristics; uint8_t lengthFileIdent; long_ad icb; __le16 lengthOfImpUse; uint8_t impUse[0]; uint8_t fileIdent[0]; uint8_t padding[0]; } __attribute__ ((packed)); /* File Characteristics (ECMA 167r3 4/14.4.3) */ #define FID_FILE_CHAR_HIDDEN 0x01 #define FID_FILE_CHAR_DIRECTORY 0x02 #define FID_FILE_CHAR_DELETED 0x04 #define FID_FILE_CHAR_PARENT 0x08 #define FID_FILE_CHAR_METADATA 0x10 /* Allocation Ext Descriptor (ECMA 167r3 4/14.5) */ struct allocExtDesc { tag descTag; __le32 previousAllocExtLocation; __le32 lengthAllocDescs; } __attribute__ ((packed)); /* ICB Tag (ECMA 167r3 4/14.6) */ typedef struct { __le32 priorRecordedNumDirectEntries; __le16 strategyType; __le16 strategyParameter; __le16 numEntries; uint8_t reserved; uint8_t fileType; lb_addr parentICBLocation; __le16 flags; } __attribute__ ((packed)) icbtag; /* Strategy Type (ECMA 167r3 4/14.6.2) */ #define ICBTAG_STRATEGY_TYPE_UNDEF 0x0000 #define ICBTAG_STRATEGY_TYPE_1 0x0001 #define ICBTAG_STRATEGY_TYPE_2 0x0002 #define ICBTAG_STRATEGY_TYPE_3 0x0003 #define ICBTAG_STRATEGY_TYPE_4 0x0004 /* File Type (ECMA 167r3 4/14.6.6) */ #define ICBTAG_FILE_TYPE_UNDEF 0x00 #define ICBTAG_FILE_TYPE_USE 0x01 #define ICBTAG_FILE_TYPE_PIE 0x02 #define ICBTAG_FILE_TYPE_IE 0x03 #define ICBTAG_FILE_TYPE_DIRECTORY 0x04 #define ICBTAG_FILE_TYPE_REGULAR 0x05 #define ICBTAG_FILE_TYPE_BLOCK 0x06 #define ICBTAG_FILE_TYPE_CHAR 0x07 #define ICBTAG_FILE_TYPE_EA 0x08 #define ICBTAG_FILE_TYPE_FIFO 0x09 #define ICBTAG_FILE_TYPE_SOCKET 0x0A #define ICBTAG_FILE_TYPE_TE 0x0B #define ICBTAG_FILE_TYPE_SYMLINK 0x0C #define ICBTAG_FILE_TYPE_STREAMDIR 0x0D /* Flags (ECMA 167r3 4/14.6.8) */ #define ICBTAG_FLAG_AD_MASK 0x0007 #define ICBTAG_FLAG_AD_SHORT 0x0000 #define ICBTAG_FLAG_AD_LONG 0x0001 #define ICBTAG_FLAG_AD_EXTENDED 0x0002 #define ICBTAG_FLAG_AD_IN_ICB 0x0003 #define ICBTAG_FLAG_SORTED 0x0008 #define ICBTAG_FLAG_NONRELOCATABLE 0x0010 #define ICBTAG_FLAG_ARCHIVE 0x0020 #define ICBTAG_FLAG_SETUID 0x0040 #define ICBTAG_FLAG_SETGID 0x0080 #define ICBTAG_FLAG_STICKY 0x0100 #define ICBTAG_FLAG_CONTIGUOUS 0x0200 #define ICBTAG_FLAG_SYSTEM 0x0400 #define ICBTAG_FLAG_TRANSFORMED 0x0800 #define ICBTAG_FLAG_MULTIVERSIONS 0x1000 #define ICBTAG_FLAG_STREAM 0x2000 /* Indirect Entry (ECMA 167r3 4/14.7) */ struct indirectEntry { tag descTag; icbtag icbTag; long_ad indirectICB; } __attribute__ ((packed)); /* Terminal Entry (ECMA 167r3 4/14.8) */ struct terminalEntry { tag descTag; icbtag icbTag; } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.9) */ struct fileEntry { tag descTag; icbtag icbTag; __le32 uid; __le32 gid; __le32 permissions; __le16 fileLinkCount; uint8_t recordFormat; uint8_t recordDisplayAttr; __le32 recordLength; __le64 informationLength; __le64 logicalBlocksRecorded; timestamp accessTime; timestamp modificationTime; timestamp attrTime; __le32 checkpoint; long_ad extendedAttrICB; regid impIdent; __le64 uniqueID; __le32 lengthExtendedAttr; __le32 lengthAllocDescs; uint8_t extendedAttr[0]; uint8_t allocDescs[0]; } __attribute__ ((packed)); /* Permissions (ECMA 167r3 4/14.9.5) */ #define FE_PERM_O_EXEC 0x00000001U #define FE_PERM_O_WRITE 0x00000002U #define FE_PERM_O_READ 0x00000004U #define FE_PERM_O_CHATTR 0x00000008U #define FE_PERM_O_DELETE 0x00000010U #define FE_PERM_G_EXEC 0x00000020U #define FE_PERM_G_WRITE 0x00000040U #define FE_PERM_G_READ 0x00000080U #define FE_PERM_G_CHATTR 0x00000100U #define FE_PERM_G_DELETE 0x00000200U #define FE_PERM_U_EXEC 0x00000400U #define FE_PERM_U_WRITE 0x00000800U #define FE_PERM_U_READ 0x00001000U #define FE_PERM_U_CHATTR 0x00002000U #define FE_PERM_U_DELETE 0x00004000U /* Record Format (ECMA 167r3 4/14.9.7) */ #define FE_RECORD_FMT_UNDEF 0x00 #define FE_RECORD_FMT_FIXED_PAD 0x01 #define FE_RECORD_FMT_FIXED 0x02 #define FE_RECORD_FMT_VARIABLE8 0x03 #define FE_RECORD_FMT_VARIABLE16 0x04 #define FE_RECORD_FMT_VARIABLE16_MSB 0x05 #define FE_RECORD_FMT_VARIABLE32 0x06 #define FE_RECORD_FMT_PRINT 0x07 #define FE_RECORD_FMT_LF 0x08 #define FE_RECORD_FMT_CR 0x09 #define FE_RECORD_FMT_CRLF 0x0A #define FE_RECORD_FMT_LFCR 0x0B /* Record Display Attributes (ECMA 167r3 4/14.9.8) */ #define FE_RECORD_DISPLAY_ATTR_UNDEF 0x00 #define FE_RECORD_DISPLAY_ATTR_1 0x01 #define FE_RECORD_DISPLAY_ATTR_2 0x02 #define FE_RECORD_DISPLAY_ATTR_3 0x03 /* Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */ struct extendedAttrHeaderDesc { tag descTag; __le32 impAttrLocation; __le32 appAttrLocation; } __attribute__ ((packed)); /* Generic Format (ECMA 167r3 4/14.10.2) */ struct genericFormat { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; uint8_t attrData[0]; } __attribute__ ((packed)); /* Character Set Information (ECMA 167r3 4/14.10.3) */ struct charSetInfo { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 escapeSeqLength; uint8_t charSetType; uint8_t escapeSeq[0]; } __attribute__ ((packed)); /* Alternate Permissions (ECMA 167r3 4/14.10.4) */ struct altPerms { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le16 ownerIdent; __le16 groupIdent; __le16 permission; } __attribute__ ((packed)); /* File Times Extended Attribute (ECMA 167r3 4/14.10.5) */ struct fileTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 dataLength; __le32 fileTimeExistence; uint8_t fileTimes; } __attribute__ ((packed)); /* FileTimeExistence (ECMA 167r3 4/14.10.5.6) */ #define FTE_CREATION 0x00000001 #define FTE_DELETION 0x00000004 #define FTE_EFFECTIVE 0x00000008 #define FTE_BACKUP 0x00000002 /* Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */ struct infoTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 dataLength; __le32 infoTimeExistence; uint8_t infoTimes[0]; } __attribute__ ((packed)); /* Device Specification (ECMA 167r3 4/14.10.7) */ struct deviceSpec { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 impUseLength; __le32 majorDeviceIdent; __le32 minorDeviceIdent; uint8_t impUse[0]; } __attribute__ ((packed)); /* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */ struct impUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 impUseLength; regid impIdent; uint8_t impUse[0]; } __attribute__ ((packed)); /* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */ struct appUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 appUseLength; regid appIdent; uint8_t appUse[0]; } __attribute__ ((packed)); #define EXTATTR_CHAR_SET 1 #define EXTATTR_ALT_PERMS 3 #define EXTATTR_FILE_TIMES 5 #define EXTATTR_INFO_TIMES 6 #define EXTATTR_DEV_SPEC 12 #define EXTATTR_IMP_USE 2048 #define EXTATTR_APP_USE 65536 /* Unallocated Space Entry (ECMA 167r3 4/14.11) */ struct unallocSpaceEntry { tag descTag; icbtag icbTag; __le32 lengthAllocDescs; uint8_t allocDescs[0]; } __attribute__ ((packed)); /* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */ struct spaceBitmapDesc { tag descTag; __le32 numOfBits; __le32 numOfBytes; uint8_t bitmap[0]; } __attribute__ ((packed)); /* Partition Integrity Entry (ECMA 167r3 4/14.13) */ struct partitionIntegrityEntry { tag descTag; icbtag icbTag; timestamp recordingDateAndTime; uint8_t integrityType; uint8_t reserved[175]; regid impIdent; uint8_t impUse[256]; } __attribute__ ((packed)); /* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ /* Extent Length (ECMA 167r3 4/14.14.1.1) */ #define EXT_RECORDED_ALLOCATED 0x00000000 #define EXT_NOT_RECORDED_ALLOCATED 0x40000000 #define EXT_NOT_RECORDED_NOT_ALLOCATED 0x80000000 #define EXT_NEXT_EXTENT_ALLOCDECS 0xC0000000 /* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ /* Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */ struct logicalVolHeaderDesc { __le64 uniqueID; uint8_t reserved[24]; } __attribute__ ((packed)); /* Path Component (ECMA 167r3 4/14.16.1) */ struct pathComponent { uint8_t componentType; uint8_t lengthComponentIdent; __le16 componentFileVersionNum; dstring componentIdent[0]; } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.17) */ struct extendedFileEntry { tag descTag; icbtag icbTag; __le32 uid; __le32 gid; __le32 permissions; __le16 fileLinkCount; uint8_t recordFormat; uint8_t recordDisplayAttr; __le32 recordLength; __le64 informationLength; __le64 objectSize; __le64 logicalBlocksRecorded; timestamp accessTime; timestamp modificationTime; timestamp createTime; timestamp attrTime; __le32 checkpoint; __le32 reserved; long_ad extendedAttrICB; long_ad streamDirectoryICB; regid impIdent; __le64 uniqueID; __le32 lengthExtendedAttr; __le32 lengthAllocDescs; uint8_t extendedAttr[0]; uint8_t allocDescs[0]; } __attribute__ ((packed)); #endif /* _ECMA_167_H */ linux-2.6.17/fs/udf/udfdecl.h0000644000000000000000000001500110564537530014416 0ustar rootroot#ifndef __UDF_DECL_H #define __UDF_DECL_H #include #include "ecma_167.h" #include "osta_udf.h" #include #include #include #include #include #include #include "udfend.h" #define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) ) #define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) ) #define UDF_EXTENT_LENGTH_MASK 0x3FFFFFFF #define UDF_EXTENT_FLAG_MASK 0xC0000000 #define UDF_NAME_PAD 4 #define UDF_NAME_LEN 256 #define UDF_PATH_LEN 1023 #define udf_file_entry_alloc_offset(inode)\ (UDF_I_USE(inode) ?\ sizeof(struct unallocSpaceEntry) :\ ((UDF_I_EFE(inode) ?\ sizeof(struct extendedFileEntry) :\ sizeof(struct fileEntry)) + UDF_I_LENEATTR(inode))) #define udf_ext0_offset(inode)\ (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ?\ udf_file_entry_alloc_offset(inode) : 0) #define udf_get_lb_pblock(sb,loc,offset) udf_get_pblock((sb), (loc).logicalBlockNum, (loc).partitionReferenceNum, (offset)) struct dentry; struct inode; struct task_struct; struct buffer_head; struct super_block; extern struct inode_operations udf_dir_inode_operations; extern const struct file_operations udf_dir_operations; extern struct inode_operations udf_file_inode_operations; extern const struct file_operations udf_file_operations; extern struct address_space_operations udf_aops; extern struct address_space_operations udf_adinicb_aops; extern struct address_space_operations udf_symlink_aops; struct udf_fileident_bh { struct buffer_head *sbh; struct buffer_head *ebh; int soffset; int eoffset; }; struct udf_vds_record { uint32_t block; uint32_t volDescSeqNum; }; struct generic_desc { tag descTag; __le32 volDescSeqNum; }; struct ustr { uint8_t u_cmpID; uint8_t u_name[UDF_NAME_LEN-2]; uint8_t u_len; }; /* super.c */ extern void udf_error(struct super_block *, const char *, const char *, ...); extern void udf_warning(struct super_block *, const char *, const char *, ...); /* namei.c */ extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, struct fileIdentDesc *, struct udf_fileident_bh *, uint8_t *, uint8_t *); /* file.c */ extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* inode.c */ extern struct inode *udf_iget(struct super_block *, kernel_lb_addr); extern int udf_sync_inode(struct inode *); extern void udf_expand_file_adinicb(struct inode *, int, int *); extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *); extern struct buffer_head * udf_bread(struct inode *, int, int, int *); extern void udf_truncate(struct inode *); extern void udf_read_inode(struct inode *); extern void udf_delete_inode(struct inode *); extern void udf_clear_inode(struct inode *); extern int udf_write_inode(struct inode *, int); extern long udf_block_map(struct inode *, long); extern int8_t inode_bmap(struct inode *, int, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **); extern int8_t udf_add_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr, uint32_t, struct buffer_head **, int); extern int8_t udf_write_aext(struct inode *, kernel_lb_addr, int *, kernel_lb_addr, uint32_t, struct buffer_head *, int); extern int8_t udf_delete_aext(struct inode *, kernel_lb_addr, int, kernel_lb_addr, uint32_t, struct buffer_head *); extern int8_t udf_next_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int); extern int8_t udf_current_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int); /* misc.c */ extern struct buffer_head *udf_tgetblk(struct super_block *, int); extern struct buffer_head *udf_tread(struct super_block *, int); extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t); extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t); extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, uint32_t, uint16_t *); extern struct buffer_head *udf_read_ptagged(struct super_block *, kernel_lb_addr, uint32_t, uint16_t *); extern void udf_release_data(struct buffer_head *); extern void udf_update_tag(char *, int); extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int); /* lowlevel.c */ extern unsigned int udf_get_last_session(struct super_block *); extern unsigned long udf_get_last_block(struct super_block *); /* partition.c */ extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, uint32_t); extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, uint32_t); extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, uint32_t); extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, uint32_t); extern int udf_relocate_blocks(struct super_block *, long, long *); /* unicode.c */ extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int); extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, int); extern int udf_build_ustr(struct ustr *, dstring *, int); extern int udf_CS0toUTF8(struct ustr *, struct ustr *); /* ialloc.c */ extern void udf_free_inode(struct inode *); extern struct inode * udf_new_inode (struct inode *, int, int *); /* truncate.c */ extern void udf_discard_prealloc(struct inode *); extern void udf_truncate_extents(struct inode *); /* balloc.c */ extern void udf_free_blocks(struct super_block *, struct inode *, kernel_lb_addr, uint32_t, uint32_t); extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, uint32_t, uint32_t); extern int udf_new_block(struct super_block *, struct inode *, uint16_t, uint32_t, int *); /* fsync.c */ extern int udf_fsync_file(struct file *, struct dentry *, int); /* directory.c */ extern struct fileIdentDesc * udf_fileident_read(struct inode *, loff_t *, struct udf_fileident_bh *, struct fileIdentDesc *, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **); extern struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset); extern long_ad * udf_get_filelongad(uint8_t *, int, int *, int); extern short_ad * udf_get_fileshortad(uint8_t *, int, int *, int); /* crc.c */ extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t); /* udftime.c */ extern time_t *udf_stamp_to_time(time_t *, long *, kernel_timestamp); extern kernel_timestamp *udf_time_to_stamp(kernel_timestamp *, struct timespec); #endif /* __UDF_DECL_H */ linux-2.6.17/fs/udf/misc.c0000644000000000000000000001731110564537530013744 0ustar rootroot/* * misc.c * * PURPOSE * Miscellaneous routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 04/19/99 blf partial support for reading/writing specific EA's */ #include "udfdecl.h" #include #include #include #include #include "udf_i.h" #include "udf_sb.h" struct buffer_head * udf_tgetblk(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_getblk(sb, udf_fixed_to_variable(block)); else return sb_getblk(sb, block); } struct buffer_head * udf_tread(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_bread(sb, udf_fixed_to_variable(block)); else return sb_bread(sb, block); } struct genericFormat * udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, uint8_t loc) { uint8_t *ea = NULL, *ad = NULL; int offset; uint16_t crclen; int i; ea = UDF_I_DATA(inode); if (UDF_I_LENEATTR(inode)) ad = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); else { ad = ea; size += sizeof(struct extendedAttrHeaderDesc); } offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) - UDF_I_LENALLOC(inode); /* TODO - Check for FreeEASpace */ if (loc & 0x01 && offset >= size) { struct extendedAttrHeaderDesc *eahd; eahd = (struct extendedAttrHeaderDesc *)ea; if (UDF_I_LENALLOC(inode)) { memmove(&ad[size], ad, UDF_I_LENALLOC(inode)); } if (UDF_I_LENEATTR(inode)) { /* check checksum/crc */ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { return NULL; } } else { size -= sizeof(struct extendedAttrHeaderDesc); UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc); eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD); if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) eahd->descTag.descVersion = cpu_to_le16(3); else eahd->descTag.descVersion = cpu_to_le16(2); eahd->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF); eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF); } offset = UDF_I_LENEATTR(inode); if (type < 2048) { if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t aal = le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); offset -= aal; eahd->appAttrLocation = cpu_to_le32(aal + size); } if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t ial = le32_to_cpu(eahd->impAttrLocation); memmove(&ea[offset - ial + size], &ea[ial], offset - ial); offset -= ial; eahd->impAttrLocation = cpu_to_le32(ial + size); } } else if (type < 65536) { if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t aal = le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); offset -= aal; eahd->appAttrLocation = cpu_to_le32(aal + size); } } /* rewrite CRC + checksum of eahd */ crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(tag); eahd->descTag.descCRCLength = cpu_to_le16(crclen); eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + sizeof(tag), crclen, 0)); eahd->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) eahd->descTag.tagChecksum += ((uint8_t *)&(eahd->descTag))[i]; UDF_I_LENEATTR(inode) += size; return (struct genericFormat *)&ea[offset]; } if (loc & 0x02) { } return NULL; } struct genericFormat * udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) { struct genericFormat *gaf; uint8_t *ea = NULL; uint32_t offset; ea = UDF_I_DATA(inode); if (UDF_I_LENEATTR(inode)) { struct extendedAttrHeaderDesc *eahd; eahd = (struct extendedAttrHeaderDesc *)ea; /* check checksum/crc */ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { return NULL; } if (type < 2048) offset = sizeof(struct extendedAttrHeaderDesc); else if (type < 65536) offset = le32_to_cpu(eahd->impAttrLocation); else offset = le32_to_cpu(eahd->appAttrLocation); while (offset < UDF_I_LENEATTR(inode)) { gaf = (struct genericFormat *)&ea[offset]; if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) return gaf; else offset += le32_to_cpu(gaf->attrLength); } } return NULL; } /* * udf_read_tagged * * PURPOSE * Read the first block of a tagged descriptor. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ struct buffer_head * udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident) { tag *tag_p; struct buffer_head *bh = NULL; register uint8_t checksum; register int i; /* Read the block */ if (block == 0xFFFFFFFF) return NULL; bh = udf_tread(sb, block + UDF_SB_SESSION(sb)); if (!bh) { udf_debug("block=%d, location=%d: read failed\n", block + UDF_SB_SESSION(sb), location); return NULL; } tag_p = (tag *)(bh->b_data); *ident = le16_to_cpu(tag_p->tagIdent); if ( location != le32_to_cpu(tag_p->tagLocation) ) { udf_debug("location mismatch block %u, tag %u != %u\n", block + UDF_SB_SESSION(sb), le32_to_cpu(tag_p->tagLocation), location); goto error_out; } /* Verify the tag checksum */ checksum = 0U; for (i = 0; i < 4; i++) checksum += (uint8_t)(bh->b_data[i]); for (i = 5; i < 16; i++) checksum += (uint8_t)(bh->b_data[i]); if (checksum != tag_p->tagChecksum) { printk(KERN_ERR "udf: tag checksum failed block %d\n", block); goto error_out; } /* Verify the tag version */ if (le16_to_cpu(tag_p->descVersion) != 0x0002U && le16_to_cpu(tag_p->descVersion) != 0x0003U) { udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n", le16_to_cpu(tag_p->descVersion), block); goto error_out; } /* Verify the descriptor CRC */ if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize || le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag), le16_to_cpu(tag_p->descCRCLength), 0)) { return bh; } udf_debug("Crc failure block %d: crc = %d, crclen = %d\n", block + UDF_SB_SESSION(sb), le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength)); error_out: brelse(bh); return NULL; } struct buffer_head * udf_read_ptagged(struct super_block *sb, kernel_lb_addr loc, uint32_t offset, uint16_t *ident) { return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset), loc.logicalBlockNum + offset, ident); } void udf_release_data(struct buffer_head *bh) { if (bh) brelse(bh); } void udf_update_tag(char *data, int length) { tag *tptr = (tag *)data; int i; length -= sizeof(tag); tptr->tagChecksum = 0; tptr->descCRCLength = cpu_to_le16(length); tptr->descCRC = cpu_to_le16(udf_crc(data + sizeof(tag), length, 0)); for (i=0; i<16; i++) if (i != 4) tptr->tagChecksum += (uint8_t)(data[i]); } void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum, uint32_t loc, int length) { tag *tptr = (tag *)data; tptr->tagIdent = cpu_to_le16(ident); tptr->descVersion = cpu_to_le16(version); tptr->tagSerialNum = cpu_to_le16(snum); tptr->tagLocation = cpu_to_le32(loc); udf_update_tag(data, length); } linux-2.6.17/fs/udf/dir.c0000644000000000000000000001431410564537530013567 0ustar rootroot/* * dir.c * * PURPOSE * Directory handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2004 Ben Fennema * * HISTORY * * 10/05/98 dgb Split directory operations into its own file * Implemented directory reads via do_udf_readdir * 10/06/98 Made directory operations work! * 11/17/98 Rewrote directory to support ICBTAG_FLAG_AD_LONG * 11/25/98 blf Rewrote directory handling (readdir+lookup) to support reading * across blocks. * 12/12/98 Split out the lookup code to namei.c. bulk of directory * code now in directory.c:udf_fileident_read. */ #include "udfdecl.h" #include #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" /* Prototypes for file operations */ static int udf_readdir(struct file *, void *, filldir_t); static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *); /* readdir and lookup functions */ const struct file_operations udf_dir_operations = { .read = generic_read_dir, .readdir = udf_readdir, .ioctl = udf_ioctl, .fsync = udf_fsync_file, }; /* * udf_readdir * * PURPOSE * Read a directory entry. * * DESCRIPTION * Optional - sys_getdents() will return -ENOTDIR if this routine is not * available. * * Refer to sys_getdents() in fs/readdir.c * sys_getdents() -> . * * PRE-CONDITIONS * filp Pointer to directory file. * buf Pointer to directory entry buffer. * filldir Pointer to filldir function. * * POST-CONDITIONS * >=0 on success. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *dir = filp->f_dentry->d_inode; int result; lock_kernel(); if ( filp->f_pos == 0 ) { if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0) { unlock_kernel(); return 0; } filp->f_pos ++; } result = do_udf_readdir(dir, filp, filldir, dirent); unlock_kernel(); return result; } static int do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent) { struct udf_fileident_bh fibh; struct fileIdentDesc *fi=NULL; struct fileIdentDesc cfi; int block, iblock; loff_t nf_pos = filp->f_pos - 1; int flen; char fname[UDF_NAME_LEN]; char *nameptr; uint16_t liu; uint8_t lfi; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; struct buffer_head * bh = NULL, * tmp, * bha[16]; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; int i, num; unsigned int dt_type; if (nf_pos >= size) return 0; if (nf_pos == 0) nf_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh.sbh = fibh.ebh = NULL; else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); return -EIO; } if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) { i = 16 >> (dir->i_sb->s_blocksize_bits - 9); if (i+offset > (elen >> dir->i_sb->s_blocksize_bits)) i = (elen >> dir->i_sb->s_blocksize_bits)-offset; for (num=0; i>0; i--) { block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i); tmp = udf_tgetblk(dir->i_sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } if (num) { ll_rw_block(READA, num, bha); for (i=0; if_pos = nf_pos + 1; fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } liu = le16_to_cpu(cfi.lengthOfImpUse); lfi = cfi.lengthFileIdent; if (fibh.sbh == fibh.ebh) nameptr = fi->fileIdent + liu; else { int poffset; /* Unpaded ending offset */ poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi; if (poffset >= lfi) nameptr = (char *)(fibh.ebh->b_data + poffset - lfi); else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset); } } if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) continue; } if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) continue; } if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT ) { iblock = parent_ino(filp->f_dentry); flen = 2; memcpy(fname, "..", flen); dt_type = DT_DIR; } else { kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation); iblock = udf_get_lb_pblock(dir->i_sb, tloc, 0); flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); dt_type = DT_UNKNOWN; } if (flen) { if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } } } /* end while */ filp->f_pos = nf_pos + 1; if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } linux-2.6.17/fs/udf/partition.c0000644000000000000000000001406110564537530015021 0ustar rootroot/* * partition.c * * PURPOSE * Partition handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema * * HISTORY * * 12/06/98 blf Created file. * */ #include "udfdecl.h" #include "udf_sb.h" #include "udf_i.h" #include #include #include #include #include inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { if (partition >= UDF_SB_NUMPARTS(sb)) { udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n", block, partition, offset); return 0xFFFFFFFF; } if (UDF_SB_PARTFUNC(sb, partition)) return UDF_SB_PARTFUNC(sb, partition)(sb, block, partition, offset); else return UDF_SB_PARTROOT(sb, partition) + block + offset; } uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { struct buffer_head *bh = NULL; uint32_t newblock; uint32_t index; uint32_t loc; index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(uint32_t); if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) { udf_debug("Trying to access block beyond end of VAT (%d max %d)\n", block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries); return 0xFFFFFFFF; } if (block >= index) { block -= index; newblock = 1 + (block / (sb->s_blocksize / sizeof(uint32_t))); index = block % (sb->s_blocksize / sizeof(uint32_t)); } else { newblock = 0; index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(uint32_t) + block; } loc = udf_block_map(UDF_SB_VAT(sb), newblock); if (!(bh = sb_bread(sb, loc))) { udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n", sb, block, partition, loc, index); return 0xFFFFFFFF; } loc = le32_to_cpu(((__le32 *)bh->b_data)[index]); udf_release_data(bh); if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) { udf_debug("recursive call to udf_get_pblock!\n"); return 0xFFFFFFFF; } return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset); } inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { return udf_get_pblock_virt15(sb, block, partition, offset); } uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { int i; struct sparingTable *st = NULL; uint32_t packet = (block + offset) & ~(UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1); for (i=0; i<4; i++) { if (UDF_SB_TYPESPAR(sb,partition).s_spar_map[i] != NULL) { st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,partition).s_spar_map[i]->b_data; break; } } if (st) { for (i=0; ireallocationTableLen); i++) { if (le32_to_cpu(st->mapEntry[i].origLocation) >= 0xFFFFFFF0) break; else if (le32_to_cpu(st->mapEntry[i].origLocation) == packet) { return le32_to_cpu(st->mapEntry[i].mappedLocation) + ((block + offset) & (UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1)); } else if (le32_to_cpu(st->mapEntry[i].origLocation) > packet) break; } } return UDF_SB_PARTROOT(sb,partition) + block + offset; } int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) { struct udf_sparing_data *sdata; struct sparingTable *st = NULL; struct sparingEntry mapEntry; uint32_t packet; int i, j, k, l; for (i=0; i UDF_SB_PARTROOT(sb,i) && old_block < UDF_SB_PARTROOT(sb,i) + UDF_SB_PARTLEN(sb,i)) { sdata = &UDF_SB_TYPESPAR(sb,i); packet = (old_block - UDF_SB_PARTROOT(sb,i)) & ~(sdata->s_packet_len - 1); for (j=0; j<4; j++) { if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; break; } } if (!st) return 1; for (k=0; kreallocationTableLen); k++) { if (le32_to_cpu(st->mapEntry[k].origLocation) == 0xFFFFFFFF) { for (; j<4; j++) { if (sdata->s_spar_map[j]) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; st->mapEntry[k].origLocation = cpu_to_le32(packet); udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); mark_buffer_dirty(sdata->s_spar_map[j]); } } *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; } else if (le32_to_cpu(st->mapEntry[k].origLocation) == packet) { *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; } else if (le32_to_cpu(st->mapEntry[k].origLocation) > packet) break; } for (l=k; lreallocationTableLen); l++) { if (le32_to_cpu(st->mapEntry[l].origLocation) == 0xFFFFFFFF) { for (; j<4; j++) { if (sdata->s_spar_map[j]) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; mapEntry = st->mapEntry[l]; mapEntry.origLocation = cpu_to_le32(packet); memmove(&st->mapEntry[k+1], &st->mapEntry[k], (l-k)*sizeof(struct sparingEntry)); st->mapEntry[k] = mapEntry; udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); mark_buffer_dirty(sdata->s_spar_map[j]); } } *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; } } return 1; } } if (i == UDF_SB_NUMPARTS(sb)) { /* outside of partitions */ /* for now, fail =) */ return 1; } return 0; } linux-2.6.17/fs/udf/super.c0000644000000000000000000014532610564537530014157 0ustar rootroot/* * super.c * * PURPOSE * Super block routines for the OSTA-UDF(tm) filesystem. * * DESCRIPTION * OSTA-UDF(tm) = Optical Storage Technology Association * Universal Disk Format. * * This code is based on version 2.00 of the UDF specification, * and revision 3 of the ECMA 167 standard [equivalent to ISO 13346]. * http://www.osta.org/ * http://www.ecma.ch/ * http://www.iso.org/ * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 2000 Stelias Computing Inc * * HISTORY * * 09/24/98 dgb changed to allow compiling outside of kernel, and * added some debugging. * 10/01/98 dgb updated to allow (some) possibility of compiling w/2.0.34 * 10/16/98 attempting some multi-session support * 10/17/98 added freespace count for "df" * 11/11/98 gr added novrs option * 11/26/98 dgb added fileset,anchor mount options * 12/06/98 blf really hosed things royally. vat/sparing support. sequenced vol descs * rewrote option handling based on isofs * 12/20/98 find the free space bitmap (if it exists) */ #include "udfdecl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "udf_sb.h" #include "udf_i.h" #include #include #define VDS_POS_PRIMARY_VOL_DESC 0 #define VDS_POS_UNALLOC_SPACE_DESC 1 #define VDS_POS_LOGICAL_VOL_DESC 2 #define VDS_POS_PARTITION_DESC 3 #define VDS_POS_IMP_USE_VOL_DESC 4 #define VDS_POS_VOL_DESC_PTR 5 #define VDS_POS_TERMINATING_DESC 6 #define VDS_POS_LENGTH 7 static char error_buf[1024]; /* These are the "meat" - everything else is stuffing */ static int udf_fill_super(struct super_block *, void *, int); static void udf_put_super(struct super_block *); static void udf_write_super(struct super_block *); static int udf_remount_fs(struct super_block *, int *, char *); static int udf_check_valid(struct super_block *, int, int); static int udf_vrs(struct super_block *sb, int silent); static int udf_load_partition(struct super_block *, kernel_lb_addr *); static int udf_load_logicalvol(struct super_block *, struct buffer_head *, kernel_lb_addr *); static void udf_load_logicalvolint(struct super_block *, kernel_extent_ad); static void udf_find_anchor(struct super_block *); static int udf_find_fileset(struct super_block *, kernel_lb_addr *, kernel_lb_addr *); static void udf_load_pvoldesc(struct super_block *, struct buffer_head *); static void udf_load_fileset(struct super_block *, struct buffer_head *, kernel_lb_addr *); static void udf_load_partdesc(struct super_block *, struct buffer_head *); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); static int udf_statfs(struct super_block *, struct kstatfs *); /* UDF filesystem type */ static struct super_block *udf_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super); } static struct file_system_type udf_fstype = { .owner = THIS_MODULE, .name = "udf", .get_sb = udf_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static kmem_cache_t * udf_inode_cachep; static struct inode *udf_alloc_inode(struct super_block *sb) { struct udf_inode_info *ei; ei = (struct udf_inode_info *)kmem_cache_alloc(udf_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } static void udf_destroy_inode(struct inode *inode) { kmem_cache_free(udf_inode_cachep, UDF_I(inode)); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct udf_inode_info *ei = (struct udf_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { ei->i_ext.i_data = NULL; inode_init_once(&ei->vfs_inode); } } static int init_inodecache(void) { udf_inode_cachep = kmem_cache_create("udf_inode_cache", sizeof(struct udf_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), init_once, NULL); if (udf_inode_cachep == NULL) return -ENOMEM; return 0; } static void destroy_inodecache(void) { if (kmem_cache_destroy(udf_inode_cachep)) printk(KERN_INFO "udf_inode_cache: not all structures were freed\n"); } /* Superblock operations */ static struct super_operations udf_sb_ops = { .alloc_inode = udf_alloc_inode, .destroy_inode = udf_destroy_inode, .write_inode = udf_write_inode, .delete_inode = udf_delete_inode, .clear_inode = udf_clear_inode, .put_super = udf_put_super, .write_super = udf_write_super, .statfs = udf_statfs, .remount_fs = udf_remount_fs, }; struct udf_options { unsigned char novrs; unsigned int blocksize; unsigned int session; unsigned int lastblock; unsigned int anchor; unsigned int volume; unsigned short partition; unsigned int fileset; unsigned int rootdir; unsigned int flags; mode_t umask; gid_t gid; uid_t uid; struct nls_table *nls_map; }; static int __init init_udf_fs(void) { int err; err = init_inodecache(); if (err) goto out1; err = register_filesystem(&udf_fstype); if (err) goto out; return 0; out: destroy_inodecache(); out1: return err; } static void __exit exit_udf_fs(void) { unregister_filesystem(&udf_fstype); destroy_inodecache(); } module_init(init_udf_fs) module_exit(exit_udf_fs) /* * udf_parse_options * * PURPOSE * Parse mount options. * * DESCRIPTION * The following mount options are supported: * * gid= Set the default group. * umask= Set the default umask. * uid= Set the default user. * bs= Set the block size. * unhide Show otherwise hidden files. * undelete Show deleted files in lists. * adinicb Embed data in the inode (default) * noadinicb Don't embed data in the inode * shortad Use short ad's * longad Use long ad's (default) * nostrict Unset strict conformance * iocharset= Set the NLS character set * * The remaining are for debugging and disaster recovery: * * novrs Skip volume sequence recognition * * The following expect a offset from 0. * * session= Set the CDROM session (default= last session) * anchor= Override standard anchor location. (default= 256) * volume= Override the VolumeDesc location. (unused) * partition= Override the PartitionDesc location. (unused) * lastblock= Set the last block of the filesystem/ * * The following expect a offset from the partition root. * * fileset= Override the fileset block location. (unused) * rootdir= Override the root directory location. (unused) * WARNING: overriding the rootdir to a non-directory may * yield highly unpredictable results. * * PRE-CONDITIONS * options Pointer to mount options string. * uopts Pointer to mount options variable. * * POST-CONDITIONS * 1 Mount options parsed okay. * 0 Error parsing mount options. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ enum { Opt_novrs, Opt_nostrict, Opt_bs, Opt_unhide, Opt_undelete, Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad, Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock, Opt_anchor, Opt_volume, Opt_partition, Opt_fileset, Opt_rootdir, Opt_utf8, Opt_iocharset, Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore }; static match_table_t tokens = { {Opt_novrs, "novrs"}, {Opt_nostrict, "nostrict"}, {Opt_bs, "bs=%u"}, {Opt_unhide, "unhide"}, {Opt_undelete, "undelete"}, {Opt_noadinicb, "noadinicb"}, {Opt_adinicb, "adinicb"}, {Opt_shortad, "shortad"}, {Opt_longad, "longad"}, {Opt_uforget, "uid=forget"}, {Opt_uignore, "uid=ignore"}, {Opt_gforget, "gid=forget"}, {Opt_gignore, "gid=ignore"}, {Opt_gid, "gid=%u"}, {Opt_uid, "uid=%u"}, {Opt_umask, "umask=%o"}, {Opt_session, "session=%u"}, {Opt_lastblock, "lastblock=%u"}, {Opt_anchor, "anchor=%u"}, {Opt_volume, "volume=%u"}, {Opt_partition, "partition=%u"}, {Opt_fileset, "fileset=%u"}, {Opt_rootdir, "rootdir=%u"}, {Opt_utf8, "utf8"}, {Opt_iocharset, "iocharset=%s"}, {Opt_err, NULL} }; static int udf_parse_options(char *options, struct udf_options *uopt) { char *p; int option; uopt->novrs = 0; uopt->blocksize = 2048; uopt->partition = 0xFFFF; uopt->session = 0xFFFFFFFF; uopt->lastblock = 0; uopt->anchor = 0; uopt->volume = 0xFFFFFFFF; uopt->rootdir = 0xFFFFFFFF; uopt->fileset = 0xFFFFFFFF; uopt->nls_map = NULL; if (!options) return 1; while ((p = strsep(&options, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { case Opt_novrs: uopt->novrs = 1; case Opt_bs: if (match_int(&args[0], &option)) return 0; uopt->blocksize = option; break; case Opt_unhide: uopt->flags |= (1 << UDF_FLAG_UNHIDE); break; case Opt_undelete: uopt->flags |= (1 << UDF_FLAG_UNDELETE); break; case Opt_noadinicb: uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); break; case Opt_adinicb: uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); break; case Opt_shortad: uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); break; case Opt_longad: uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); break; case Opt_gid: if (match_int(args, &option)) return 0; uopt->gid = option; break; case Opt_uid: if (match_int(args, &option)) return 0; uopt->uid = option; break; case Opt_umask: if (match_octal(args, &option)) return 0; uopt->umask = option; break; case Opt_nostrict: uopt->flags &= ~(1 << UDF_FLAG_STRICT); break; case Opt_session: if (match_int(args, &option)) return 0; uopt->session = option; break; case Opt_lastblock: if (match_int(args, &option)) return 0; uopt->lastblock = option; break; case Opt_anchor: if (match_int(args, &option)) return 0; uopt->anchor = option; break; case Opt_volume: if (match_int(args, &option)) return 0; uopt->volume = option; break; case Opt_partition: if (match_int(args, &option)) return 0; uopt->partition = option; break; case Opt_fileset: if (match_int(args, &option)) return 0; uopt->fileset = option; break; case Opt_rootdir: if (match_int(args, &option)) return 0; uopt->rootdir = option; break; case Opt_utf8: uopt->flags |= (1 << UDF_FLAG_UTF8); break; #ifdef CONFIG_UDF_NLS case Opt_iocharset: uopt->nls_map = load_nls(args[0].from); uopt->flags |= (1 << UDF_FLAG_NLS_MAP); break; #endif case Opt_uignore: uopt->flags |= (1 << UDF_FLAG_UID_IGNORE); break; case Opt_uforget: uopt->flags |= (1 << UDF_FLAG_UID_FORGET); break; case Opt_gignore: uopt->flags |= (1 << UDF_FLAG_GID_IGNORE); break; case Opt_gforget: uopt->flags |= (1 << UDF_FLAG_GID_FORGET); break; default: printk(KERN_ERR "udf: bad mount option \"%s\" " "or missing value\n", p); return 0; } } return 1; } void udf_write_super(struct super_block *sb) { lock_kernel(); if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); sb->s_dirt = 0; unlock_kernel(); } static int udf_remount_fs(struct super_block *sb, int *flags, char *options) { struct udf_options uopt; uopt.flags = UDF_SB(sb)->s_flags ; uopt.uid = UDF_SB(sb)->s_uid ; uopt.gid = UDF_SB(sb)->s_gid ; uopt.umask = UDF_SB(sb)->s_umask ; if ( !udf_parse_options(options, &uopt) ) return -EINVAL; UDF_SB(sb)->s_flags = uopt.flags; UDF_SB(sb)->s_uid = uopt.uid; UDF_SB(sb)->s_gid = uopt.gid; UDF_SB(sb)->s_umask = uopt.umask; if (UDF_SB_LVIDBH(sb)) { int write_rev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); if (write_rev > UDF_MAX_WRITE_VERSION) *flags |= MS_RDONLY; } if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) udf_close_lvid(sb); else udf_open_lvid(sb); return 0; } /* * udf_set_blocksize * * PURPOSE * Set the block size to be used in all transfers. * * DESCRIPTION * To allow room for a DMA transfer, it is best to guess big when unsure. * This routine picks 2048 bytes as the blocksize when guessing. This * should be adequate until devices with larger block sizes become common. * * Note that the Linux kernel can currently only deal with blocksizes of * 512, 1024, 2048, 4096, and 8192 bytes. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * * POST-CONDITIONS * sb->s_blocksize Blocksize. * sb->s_blocksize_bits log2 of blocksize. * 0 Blocksize is valid. * 1 Blocksize is invalid. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_set_blocksize(struct super_block *sb, int bsize) { if (!sb_min_blocksize(sb, bsize)) { udf_debug("Bad block size (%d)\n", bsize); printk(KERN_ERR "udf: bad block size (%d)\n", bsize); return 0; } return sb->s_blocksize; } static int udf_vrs(struct super_block *sb, int silent) { struct volStructDesc *vsd = NULL; int sector = 32768; int sectorsize; struct buffer_head *bh = NULL; int iso9660=0; int nsr02=0; int nsr03=0; /* Block size must be a multiple of 512 */ if (sb->s_blocksize & 511) return 0; if (sb->s_blocksize < sizeof(struct volStructDesc)) sectorsize = sizeof(struct volStructDesc); else sectorsize = sb->s_blocksize; sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits); udf_debug("Starting at sector %u (%ld byte sectors)\n", (sector >> sb->s_blocksize_bits), sb->s_blocksize); /* Process the sequence (if applicable) */ for (;!nsr02 && !nsr03; sector += sectorsize) { /* Read a block */ bh = udf_tread(sb, sector >> sb->s_blocksize_bits); if (!bh) break; /* Look for ISO descriptors */ vsd = (struct volStructDesc *)(bh->b_data + (sector & (sb->s_blocksize - 1))); if (vsd->stdIdent[0] == 0) { udf_release_data(bh); break; } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN)) { iso9660 = sector; switch (vsd->structType) { case 0: udf_debug("ISO9660 Boot Record found\n"); break; case 1: udf_debug("ISO9660 Primary Volume Descriptor found\n"); break; case 2: udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); break; case 3: udf_debug("ISO9660 Volume Partition Descriptor found\n"); break; case 255: udf_debug("ISO9660 Volume Descriptor Set Terminator found\n"); break; default: udf_debug("ISO9660 VRS (%u) found\n", vsd->structType); break; } } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, VSD_STD_ID_LEN)) { } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, VSD_STD_ID_LEN)) { udf_release_data(bh); break; } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, VSD_STD_ID_LEN)) { nsr02 = sector; } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, VSD_STD_ID_LEN)) { nsr03 = sector; } udf_release_data(bh); } if (nsr03) return nsr03; else if (nsr02) return nsr02; else if (sector - (UDF_SB_SESSION(sb) << sb->s_blocksize_bits) == 32768) return -1; else return 0; } /* * udf_find_anchor * * PURPOSE * Find an anchor volume descriptor. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * lastblock Last block on media. * * POST-CONDITIONS * 1 if not found, 0 if ok * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static void udf_find_anchor(struct super_block *sb) { int lastblock = UDF_SB_LASTBLOCK(sb); struct buffer_head *bh = NULL; uint16_t ident; uint32_t location; int i; if (lastblock) { int varlastblock = udf_variable_to_fixed(lastblock); int last[] = { lastblock, lastblock - 2, lastblock - 150, lastblock - 152, varlastblock, varlastblock - 2, varlastblock - 150, varlastblock - 152 }; lastblock = 0; /* Search for an anchor volume descriptor pointer */ /* according to spec, anchor is in either: * block 256 * lastblock-256 * lastblock * however, if the disc isn't closed, it could be 512 */ for (i = 0; !lastblock && i < ARRAY_SIZE(last); i++) { if (last[i] < 0 || !(bh = sb_bread(sb, last[i]))) { ident = location = 0; } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); } if (ident == TAG_IDENT_AVDP) { if (location == last[i] - UDF_SB_SESSION(sb)) { lastblock = UDF_SB_ANCHOR(sb)[0] = last[i] - UDF_SB_SESSION(sb); UDF_SB_ANCHOR(sb)[1] = last[i] - 256 - UDF_SB_SESSION(sb); } else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb)) { UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb); UDF_SB_ANCHOR(sb)[1] = lastblock - 256 - UDF_SB_SESSION(sb); } else udf_debug("Anchor found at block %d, location mismatch %d.\n", last[i], location); } else if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE) { lastblock = last[i]; UDF_SB_ANCHOR(sb)[3] = 512; } else { if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256))) { ident = location = 0; } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); } if (ident == TAG_IDENT_AVDP && location == last[i] - 256 - UDF_SB_SESSION(sb)) { lastblock = last[i]; UDF_SB_ANCHOR(sb)[1] = last[i] - 256; } else { if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb)))) { ident = location = 0; } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); } if (ident == TAG_IDENT_AVDP && location == udf_variable_to_fixed(last[i]) - 256) { UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); lastblock = udf_variable_to_fixed(last[i]); UDF_SB_ANCHOR(sb)[1] = lastblock - 256; } } } } } if (!lastblock) { /* We havn't found the lastblock. check 312 */ if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb)))) { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); if (ident == TAG_IDENT_AVDP && location == 256) UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); } } for (i = 0; i < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); i++) { if (UDF_SB_ANCHOR(sb)[i]) { if (!(bh = udf_read_tagged(sb, UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) { UDF_SB_ANCHOR(sb)[i] = 0; } else { udf_release_data(bh); if ((ident != TAG_IDENT_AVDP) && (i || (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE))) { UDF_SB_ANCHOR(sb)[i] = 0; } } } } UDF_SB_LASTBLOCK(sb) = lastblock; } static int udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, kernel_lb_addr *root) { struct buffer_head *bh = NULL; long lastblock; uint16_t ident; if (fileset->logicalBlockNum != 0xFFFFFFFF || fileset->partitionReferenceNum != 0xFFFF) { bh = udf_read_ptagged(sb, *fileset, 0, &ident); if (!bh) return 1; else if (ident != TAG_IDENT_FSD) { udf_release_data(bh); return 1; } } if (!bh) /* Search backwards through the partitions */ { kernel_lb_addr newfileset; return 1; for (newfileset.partitionReferenceNum=UDF_SB_NUMPARTS(sb)-1; (newfileset.partitionReferenceNum != 0xFFFF && fileset->logicalBlockNum == 0xFFFFFFFF && fileset->partitionReferenceNum == 0xFFFF); newfileset.partitionReferenceNum--) { lastblock = UDF_SB_PARTLEN(sb, newfileset.partitionReferenceNum); newfileset.logicalBlockNum = 0; do { bh = udf_read_ptagged(sb, newfileset, 0, &ident); if (!bh) { newfileset.logicalBlockNum ++; continue; } switch (ident) { case TAG_IDENT_SBD: { struct spaceBitmapDesc *sp; sp = (struct spaceBitmapDesc *)bh->b_data; newfileset.logicalBlockNum += 1 + ((le32_to_cpu(sp->numOfBytes) + sizeof(struct spaceBitmapDesc) - 1) >> sb->s_blocksize_bits); udf_release_data(bh); break; } case TAG_IDENT_FSD: { *fileset = newfileset; break; } default: { newfileset.logicalBlockNum ++; udf_release_data(bh); bh = NULL; break; } } } while (newfileset.logicalBlockNum < lastblock && fileset->logicalBlockNum == 0xFFFFFFFF && fileset->partitionReferenceNum == 0xFFFF); } } if ((fileset->logicalBlockNum != 0xFFFFFFFF || fileset->partitionReferenceNum != 0xFFFF) && bh) { udf_debug("Fileset at block=%d, partition=%d\n", fileset->logicalBlockNum, fileset->partitionReferenceNum); UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum; udf_load_fileset(sb, bh, root); udf_release_data(bh); return 0; } return 1; } static void udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) { struct primaryVolDesc *pvoldesc; time_t recording; long recording_usec; struct ustr instr; struct ustr outstr; pvoldesc = (struct primaryVolDesc *)bh->b_data; if ( udf_stamp_to_time(&recording, &recording_usec, lets_to_cpu(pvoldesc->recordingDateAndTime)) ) { kernel_timestamp ts; ts = lets_to_cpu(pvoldesc->recordingDateAndTime); udf_debug("recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x)\n", recording, recording_usec, ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); UDF_SB_RECORDTIME(sb).tv_sec = recording; UDF_SB_RECORDTIME(sb).tv_nsec = recording_usec * 1000; } if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) ) { if (udf_CS0toUTF8(&outstr, &instr)) { strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, outstr.u_len > 31 ? 31 : outstr.u_len); udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb)); } } if ( !udf_build_ustr(&instr, pvoldesc->volSetIdent, 128) ) { if (udf_CS0toUTF8(&outstr, &instr)) udf_debug("volSetIdent[] = '%s'\n", outstr.u_name); } } static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh, kernel_lb_addr *root) { struct fileSetDesc *fset; fset = (struct fileSetDesc *)bh->b_data; *root = lelb_to_cpu(fset->rootDirectoryICB.extLocation); UDF_SB_SERIALNUM(sb) = le16_to_cpu(fset->descTag.tagSerialNum); udf_debug("Rootdir at block=%d, partition=%d\n", root->logicalBlockNum, root->partitionReferenceNum); } static void udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) { struct partitionDesc *p; int i; p = (struct partitionDesc *)bh->b_data; for (i=0; ipartitionNumber)); if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber)) { UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */ UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation); if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_READ_ONLY) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_READ_ONLY; if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_WRITE_ONCE) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_WRITE_ONCE; if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_REWRITABLE) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_REWRITABLE; if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_OVERWRITABLE) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_OVERWRITABLE; if (!strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) || !strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03)) { struct partitionHeaderDesc *phd; phd = (struct partitionHeaderDesc *)(p->partitionContentsUse); if (phd->unallocSpaceTable.extLength) { kernel_lb_addr loc = { le32_to_cpu(phd->unallocSpaceTable.extPosition), i }; UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table = udf_iget(sb, loc); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_TABLE; udf_debug("unallocSpaceTable (part %d) @ %ld\n", i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino); } if (phd->unallocSpaceBitmap.extLength) { UDF_SB_ALLOC_BITMAP(sb, i, s_uspace); if (UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap != NULL) { UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extLength = le32_to_cpu(phd->unallocSpaceBitmap.extLength); UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition = le32_to_cpu(phd->unallocSpaceBitmap.extPosition); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP; udf_debug("unallocSpaceBitmap (part %d) @ %d\n", i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition); } } if (phd->partitionIntegrityTable.extLength) udf_debug("partitionIntegrityTable (part %d)\n", i); if (phd->freedSpaceTable.extLength) { kernel_lb_addr loc = { le32_to_cpu(phd->freedSpaceTable.extPosition), i }; UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table = udf_iget(sb, loc); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_TABLE; udf_debug("freedSpaceTable (part %d) @ %ld\n", i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table->i_ino); } if (phd->freedSpaceBitmap.extLength) { UDF_SB_ALLOC_BITMAP(sb, i, s_fspace); if (UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap != NULL) { UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extLength = le32_to_cpu(phd->freedSpaceBitmap.extLength); UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition = le32_to_cpu(phd->freedSpaceBitmap.extPosition); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP; udf_debug("freedSpaceBitmap (part %d) @ %d\n", i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition); } } } break; } } if (i == UDF_SB_NUMPARTS(sb)) { udf_debug("Partition (%d) not found in partition map\n", le16_to_cpu(p->partitionNumber)); } else { udf_debug("Partition (%d:%d type %x) starts at physical %d, block length %d\n", le16_to_cpu(p->partitionNumber), i, UDF_SB_PARTTYPE(sb,i), UDF_SB_PARTROOT(sb,i), UDF_SB_PARTLEN(sb,i)); } } static int udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_addr *fileset) { struct logicalVolDesc *lvd; int i, j, offset; uint8_t type; lvd = (struct logicalVolDesc *)bh->b_data; UDF_SB_ALLOC_PARTMAPS(sb, le32_to_cpu(lvd->numPartitionMaps)); for (i=0,offset=0; imapTableLength); i++,offset+=((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength) { type = ((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapType; if (type == 1) { struct genericPartitionMap1 *gpm1 = (struct genericPartitionMap1 *)&(lvd->partitionMaps[offset]); UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15; UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum); UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum); UDF_SB_PARTFUNC(sb,i) = NULL; } else if (type == 2) { struct udfPartitionMap2 *upm2 = (struct udfPartitionMap2 *)&(lvd->partitionMaps[offset]); if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) { if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15; UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15; } else if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20; UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20; } } else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) { uint32_t loc; uint16_t ident; struct sparingTable *st; struct sparablePartitionMap *spm = (struct sparablePartitionMap *)&(lvd->partitionMaps[offset]); UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; UDF_SB_TYPESPAR(sb,i).s_packet_len = le16_to_cpu(spm->packetLength); for (j=0; jnumSparingTables; j++) { loc = le32_to_cpu(spm->locSparingTable[j]); UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = udf_read_tagged(sb, loc, loc, &ident); if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) { st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,i).s_spar_map[j]->b_data; if (ident != 0 || strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) { udf_release_data(UDF_SB_TYPESPAR(sb,i).s_spar_map[j]); UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = NULL; } } } UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15; } else { udf_debug("Unknown ident: %s\n", upm2->partIdent.ident); continue; } UDF_SB_PARTVSN(sb,i) = le16_to_cpu(upm2->volSeqNum); UDF_SB_PARTNUM(sb,i) = le16_to_cpu(upm2->partitionNum); } udf_debug("Partition (%d:%d) type %d on volume %d\n", i, UDF_SB_PARTNUM(sb,i), type, UDF_SB_PARTVSN(sb,i)); } if (fileset) { long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]); *fileset = lelb_to_cpu(la->extLocation); udf_debug("FileSet found in LogicalVolDesc at block=%d, partition=%d\n", fileset->logicalBlockNum, fileset->partitionReferenceNum); } if (lvd->integritySeqExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt)); return 0; } /* * udf_load_logicalvolint * */ static void udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc) { struct buffer_head *bh = NULL; uint16_t ident; while (loc.extLength > 0 && (bh = udf_read_tagged(sb, loc.extLocation, loc.extLocation, &ident)) && ident == TAG_IDENT_LVID) { UDF_SB_LVIDBH(sb) = bh; if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt)); if (UDF_SB_LVIDBH(sb) != bh) udf_release_data(bh); loc.extLength -= sb->s_blocksize; loc.extLocation ++; } if (UDF_SB_LVIDBH(sb) != bh) udf_release_data(bh); } /* * udf_process_sequence * * PURPOSE * Process a main/reserve volume descriptor sequence. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * block First block of first extent of the sequence. * lastblock Lastblock of first extent of the sequence. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_lb_addr *fileset) { struct buffer_head *bh = NULL; struct udf_vds_record vds[VDS_POS_LENGTH]; struct generic_desc *gd; struct volDescPtr *vdp; int done=0; int i,j; uint32_t vdsn; uint16_t ident; long next_s = 0, next_e = 0; memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); /* Read the main descriptor sequence */ for (;(!done && block <= lastblock); block++) { bh = udf_read_tagged(sb, block, block, &ident); if (!bh) break; /* Process each descriptor (ISO 13346 3/8.3-8.4) */ gd = (struct generic_desc *)bh->b_data; vdsn = le32_to_cpu(gd->volDescSeqNum); switch (ident) { case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */ if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum) { vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn; vds[VDS_POS_PRIMARY_VOL_DESC].block = block; } break; case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */ if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum) { vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn; vds[VDS_POS_VOL_DESC_PTR].block = block; vdp = (struct volDescPtr *)bh->b_data; next_s = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation); next_e = le32_to_cpu(vdp->nextVolDescSeqExt.extLength); next_e = next_e >> sb->s_blocksize_bits; next_e += next_s; } break; case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum) { vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn; vds[VDS_POS_IMP_USE_VOL_DESC].block = block; } break; case TAG_IDENT_PD: /* ISO 13346 3/10.5 */ if (!vds[VDS_POS_PARTITION_DESC].block) vds[VDS_POS_PARTITION_DESC].block = block; break; case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */ if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum) { vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn; vds[VDS_POS_LOGICAL_VOL_DESC].block = block; } break; case TAG_IDENT_USD: /* ISO 13346 3/10.8 */ if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum) { vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn; vds[VDS_POS_UNALLOC_SPACE_DESC].block = block; } break; case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ vds[VDS_POS_TERMINATING_DESC].block = block; if (next_e) { block = next_s; lastblock = next_e; next_s = next_e = 0; } else done = 1; break; } udf_release_data(bh); } for (i=0; ib_data; if (ident == TAG_IDENT_PD) udf_load_partdesc(sb, bh2); udf_release_data(bh2); } } udf_release_data(bh); } } return 0; } /* * udf_check_valid() */ static int udf_check_valid(struct super_block *sb, int novrs, int silent) { long block; if (novrs) { udf_debug("Validity check skipped because of novrs option\n"); return 0; } /* Check that it is NSR02 compliant */ /* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */ else if ((block = udf_vrs(sb, silent)) == -1) { udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n"); if (!UDF_SB_LASTBLOCK(sb)) UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); return 0; } else return !block; } static int udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) { struct anchorVolDescPtr *anchor; uint16_t ident; struct buffer_head *bh; long main_s, main_e, reserve_s, reserve_e; int i, j; if (!sb) return 1; for (i = 0; i < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); i++) { if (UDF_SB_ANCHOR(sb)[i] && (bh = udf_read_tagged(sb, UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) { anchor = (struct anchorVolDescPtr *)bh->b_data; /* Locate the main sequence */ main_s = le32_to_cpu( anchor->mainVolDescSeqExt.extLocation ); main_e = le32_to_cpu( anchor->mainVolDescSeqExt.extLength ); main_e = main_e >> sb->s_blocksize_bits; main_e += main_s; /* Locate the reserve sequence */ reserve_s = le32_to_cpu(anchor->reserveVolDescSeqExt.extLocation); reserve_e = le32_to_cpu(anchor->reserveVolDescSeqExt.extLength); reserve_e = reserve_e >> sb->s_blocksize_bits; reserve_e += reserve_s; udf_release_data(bh); /* Process the main & reserve sequences */ /* responsible for finding the PartitionDesc(s) */ if (!(udf_process_sequence(sb, main_s, main_e, fileset) && udf_process_sequence(sb, reserve_s, reserve_e, fileset))) { break; } } } if (i == ARRAY_SIZE(UDF_SB_ANCHOR(sb))) { udf_debug("No Anchor block found\n"); return 1; } else udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]); for (i=0; ii_size - 36) >> 2; } else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20) { struct buffer_head *bh = NULL; uint32_t pos; pos = udf_block_map(UDF_SB_VAT(sb), 0); bh = sb_bread(sb, pos); UDF_SB_TYPEVIRT(sb,i).s_start_offset = le16_to_cpu(((struct virtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) + udf_ext0_offset(UDF_SB_VAT(sb)); UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - UDF_SB_TYPEVIRT(sb,i).s_start_offset) >> 2; udf_release_data(bh); } UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0); UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum); } } } return 0; } static void udf_open_lvid(struct super_block *sb) { if (UDF_SB_LVIDBH(sb)) { int i; kernel_timestamp cpu_time; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN; UDF_SB_LVID(sb)->descTag.descCRC = cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); UDF_SB_LVID(sb)->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) UDF_SB_LVID(sb)->descTag.tagChecksum += ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } } static void udf_close_lvid(struct super_block *sb) { if (UDF_SB_LVIDBH(sb) && UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN) { int i; kernel_timestamp cpu_time; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); if (UDF_MAX_WRITE_VERSION > le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev)) UDF_SB_LVIDIU(sb)->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION); if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev)) UDF_SB_LVIDIU(sb)->minUDFReadRev = cpu_to_le16(UDF_SB_UDFREV(sb)); if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev)) UDF_SB_LVIDIU(sb)->minUDFWriteRev = cpu_to_le16(UDF_SB_UDFREV(sb)); UDF_SB_LVID(sb)->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE); UDF_SB_LVID(sb)->descTag.descCRC = cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); UDF_SB_LVID(sb)->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) UDF_SB_LVID(sb)->descTag.tagChecksum += ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } } /* * udf_read_super * * PURPOSE * Complete the specified super block. * * PRE-CONDITIONS * sb Pointer to superblock to complete - never NULL. * sb->s_dev Device to read suberblock from. * options Pointer to mount options. * silent Silent flag. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_fill_super(struct super_block *sb, void *options, int silent) { int i; struct inode *inode=NULL; struct udf_options uopt; kernel_lb_addr rootdir, fileset; struct udf_sb_info *sbi; uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT); uopt.uid = -1; uopt.gid = -1; uopt.umask = 0; sbi = kmalloc(sizeof(struct udf_sb_info), GFP_KERNEL); if (!sbi) return -ENOMEM; sb->s_fs_info = sbi; memset(UDF_SB(sb), 0x00, sizeof(struct udf_sb_info)); mutex_init(&sbi->s_alloc_mutex); if (!udf_parse_options((char *)options, &uopt)) goto error_out; if (uopt.flags & (1 << UDF_FLAG_UTF8) && uopt.flags & (1 << UDF_FLAG_NLS_MAP)) { udf_error(sb, "udf_read_super", "utf8 cannot be combined with iocharset\n"); goto error_out; } #ifdef CONFIG_UDF_NLS if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map) { uopt.nls_map = load_nls_default(); if (!uopt.nls_map) uopt.flags &= ~(1 << UDF_FLAG_NLS_MAP); else udf_debug("Using default NLS map\n"); } #endif if (!(uopt.flags & (1 << UDF_FLAG_NLS_MAP))) uopt.flags |= (1 << UDF_FLAG_UTF8); fileset.logicalBlockNum = 0xFFFFFFFF; fileset.partitionReferenceNum = 0xFFFF; UDF_SB(sb)->s_flags = uopt.flags; UDF_SB(sb)->s_uid = uopt.uid; UDF_SB(sb)->s_gid = uopt.gid; UDF_SB(sb)->s_umask = uopt.umask; UDF_SB(sb)->s_nls_map = uopt.nls_map; /* Set the block size for all transfers */ if (!udf_set_blocksize(sb, uopt.blocksize)) goto error_out; if ( uopt.session == 0xFFFFFFFF ) UDF_SB_SESSION(sb) = udf_get_last_session(sb); else UDF_SB_SESSION(sb) = uopt.session; udf_debug("Multi-session=%d\n", UDF_SB_SESSION(sb)); UDF_SB_LASTBLOCK(sb) = uopt.lastblock; UDF_SB_ANCHOR(sb)[0] = UDF_SB_ANCHOR(sb)[1] = 0; UDF_SB_ANCHOR(sb)[2] = uopt.anchor; UDF_SB_ANCHOR(sb)[3] = 256; if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */ { printk("UDF-fs: No VRS found\n"); goto error_out; } udf_find_anchor(sb); /* Fill in the rest of the superblock */ sb->s_op = &udf_sb_ops; sb->dq_op = NULL; sb->s_dirt = 0; sb->s_magic = UDF_SUPER_MAGIC; sb->s_time_gran = 1000; if (udf_load_partition(sb, &fileset)) { printk("UDF-fs: No partition found (1)\n"); goto error_out; } udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb)); if ( UDF_SB_LVIDBH(sb) ) { uint16_t minUDFReadRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev); uint16_t minUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); /* uint16_t maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */ if (minUDFReadRev > UDF_MAX_READ_VERSION) { printk("UDF-fs: minUDFReadRev=%x (max is %x)\n", le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev), UDF_MAX_READ_VERSION); goto error_out; } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) { sb->s_flags |= MS_RDONLY; } UDF_SB_UDFREV(sb) = minUDFWriteRev; if (minUDFReadRev >= UDF_VERS_USE_EXTENDED_FE) UDF_SET_FLAG(sb, UDF_FLAG_USE_EXTENDED_FE); if (minUDFReadRev >= UDF_VERS_USE_STREAMS) UDF_SET_FLAG(sb, UDF_FLAG_USE_STREAMS); } if ( !UDF_SB_NUMPARTS(sb) ) { printk("UDF-fs: No partition found (2)\n"); goto error_out; } if ( udf_find_fileset(sb, &fileset, &rootdir) ) { printk("UDF-fs: No fileset found\n"); goto error_out; } if (!silent) { kernel_timestamp ts; udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb)); udf_info("UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", UDFFS_VERSION, UDFFS_DATE, UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); } if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); /* Assign the root inode */ /* assign inodes by physical block number */ /* perhaps it's not extensible enough, but for now ... */ inode = udf_iget(sb, rootdir); if (!inode) { printk("UDF-fs: Error in udf_iget, block=%d, partition=%d\n", rootdir.logicalBlockNum, rootdir.partitionReferenceNum); goto error_out; } /* Allocate a dentry for the root inode */ sb->s_root = d_alloc_root(inode); if (!sb->s_root) { printk("UDF-fs: Couldn't allocate root dentry\n"); iput(inode); goto error_out; } sb->s_maxbytes = 1<<30; return 0; error_out: if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); if (UDF_SB_NUMPARTS(sb)) { if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) { for (i=0; i<4; i++) udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); } } #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) unload_nls(UDF_SB(sb)->s_nls_map); #endif if (!(sb->s_flags & MS_RDONLY)) udf_close_lvid(sb); udf_release_data(UDF_SB_LVIDBH(sb)); UDF_SB_FREE(sb); kfree(sbi); sb->s_fs_info = NULL; return -EINVAL; } void udf_error(struct super_block *sb, const char *function, const char *fmt, ...) { va_list args; if (!(sb->s_flags & MS_RDONLY)) { /* mark sb error */ sb->s_dirt = 1; } va_start(args, fmt); vsprintf(error_buf, fmt, args); va_end(args); printk (KERN_CRIT "UDF-fs error (device %s): %s: %s\n", sb->s_id, function, error_buf); } void udf_warning(struct super_block *sb, const char *function, const char *fmt, ...) { va_list args; va_start (args, fmt); vsprintf(error_buf, fmt, args); va_end(args); printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n", sb->s_id, function, error_buf); } /* * udf_put_super * * PURPOSE * Prepare for destruction of the superblock. * * DESCRIPTION * Called before the filesystem is unmounted. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static void udf_put_super(struct super_block *sb) { int i; if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); if (UDF_SB_NUMPARTS(sb)) { if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) { for (i=0; i<4; i++) udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); } } #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) unload_nls(UDF_SB(sb)->s_nls_map); #endif if (!(sb->s_flags & MS_RDONLY)) udf_close_lvid(sb); udf_release_data(UDF_SB_LVIDBH(sb)); UDF_SB_FREE(sb); kfree(sb->s_fs_info); sb->s_fs_info = NULL; } /* * udf_stat_fs * * PURPOSE * Return info about the filesystem. * * DESCRIPTION * Called by sys_statfs() * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_statfs(struct super_block *sb, struct kstatfs *buf) { buf->f_type = UDF_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)); buf->f_bfree = udf_count_free(sb); buf->f_bavail = buf->f_bfree; buf->f_files = (UDF_SB_LVIDBH(sb) ? (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree; buf->f_ffree = buf->f_bfree; /* __kernel_fsid_t f_fsid */ buf->f_namelen = UDF_NAME_LEN-2; return 0; } static unsigned char udf_bitmap_lookup[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; static unsigned int udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) { struct buffer_head *bh = NULL; unsigned int accum = 0; int index; int block = 0, newblock; kernel_lb_addr loc; uint32_t bytes; uint8_t value; uint8_t *ptr; uint16_t ident; struct spaceBitmapDesc *bm; lock_kernel(); loc.logicalBlockNum = bitmap->s_extPosition; loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_read_ptagged(sb, loc, 0, &ident); if (!bh) { printk(KERN_ERR "udf: udf_count_free failed\n"); goto out; } else if (ident != TAG_IDENT_SBD) { udf_release_data(bh); printk(KERN_ERR "udf: udf_count_free failed\n"); goto out; } bm = (struct spaceBitmapDesc *)bh->b_data; bytes = le32_to_cpu(bm->numOfBytes); index = sizeof(struct spaceBitmapDesc); /* offset in first block only */ ptr = (uint8_t *)bh->b_data; while ( bytes > 0 ) { while ((bytes > 0) && (index < sb->s_blocksize)) { value = ptr[index]; accum += udf_bitmap_lookup[ value & 0x0f ]; accum += udf_bitmap_lookup[ value >> 4 ]; index++; bytes--; } if ( bytes ) { udf_release_data(bh); newblock = udf_get_lb_pblock(sb, loc, ++block); bh = udf_tread(sb, newblock); if (!bh) { udf_debug("read failed\n"); goto out; } index = 0; ptr = (uint8_t *)bh->b_data; } } udf_release_data(bh); out: unlock_kernel(); return accum; } static unsigned int udf_count_free_table(struct super_block *sb, struct inode * table) { unsigned int accum = 0; uint32_t extoffset, elen; kernel_lb_addr bloc, eloc; int8_t etype; struct buffer_head *bh = NULL; lock_kernel(); bloc = UDF_I_LOCATION(table); extoffset = sizeof(struct unallocSpaceEntry); while ((etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { accum += (elen >> table->i_sb->s_blocksize_bits); } udf_release_data(bh); unlock_kernel(); return accum; } static unsigned int udf_count_free(struct super_block *sb) { unsigned int accum = 0; if (UDF_SB_LVIDBH(sb)) { if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) { accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]); if (accum == 0xFFFFFFFF) accum = 0; } } if (accum) return accum; if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) { accum += udf_count_free_bitmap(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap); } if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) { accum += udf_count_free_bitmap(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap); } if (accum) return accum; if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) { accum += udf_count_free_table(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); } if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) { accum += udf_count_free_table(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); } return accum; } linux-2.6.17/fs/udf/osta_udf.h0000644000000000000000000002020010564537530014611 0ustar rootroot/* * osta_udf.h * * This file is based on OSTA UDF(tm) 2.50 (April 30, 2003) * http://www.osta.org * * Copyright (c) 2001-2004 Ben Fennema * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "ecma_167.h" #ifndef _OSTA_UDF_H #define _OSTA_UDF_H 1 /* OSTA CS0 Charspec (UDF 2.50 2.1.2) */ #define UDF_CHAR_SET_TYPE 0 #define UDF_CHAR_SET_INFO "OSTA Compressed Unicode" /* Entity Identifier (UDF 2.50 2.1.5) */ /* Identifiers (UDF 2.50 2.1.5.2) */ #define UDF_ID_DEVELOPER "*Linux UDFFS" #define UDF_ID_COMPLIANT "*OSTA UDF Compliant" #define UDF_ID_LV_INFO "*UDF LV Info" #define UDF_ID_FREE_EA "*UDF FreeEASpace" #define UDF_ID_FREE_APP_EA "*UDF FreeAppEASpace" #define UDF_ID_DVD_CGMS "*UDF DVD CGMS Info" #define UDF_ID_OS2_EA "*UDF OS/2 EA" #define UDF_ID_OS2_EA_LENGTH "*UDF OS/2 EALength" #define UDF_ID_MAC_VOLUME "*UDF Mac VolumeInfo" #define UDF_ID_MAC_FINDER "*UDF Mac FinderInfo" #define UDF_ID_MAC_UNIQUE "*UDF Mac UniqueIDTable" #define UDF_ID_MAC_RESOURCE "*UDF Mac ResourceFork" #define UDF_ID_VIRTUAL "*UDF Virtual Partition" #define UDF_ID_SPARABLE "*UDF Sparable Partition" #define UDF_ID_ALLOC "*UDF Virtual Alloc Tbl" #define UDF_ID_SPARING "*UDF Sparing Table" #define UDF_ID_METADATA "*UDF Metadata Partition" /* Identifier Suffix (UDF 2.50 2.1.5.3) */ #define IS_DF_HARD_WRITE_PROTECT 0x01 #define IS_DF_SOFT_WRITE_PROTECT 0x02 struct UDFIdentSuffix { __le16 UDFRevision; uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[4]; } __attribute__ ((packed)); struct impIdentSuffix { uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[6]; } __attribute__ ((packed)); struct appIdentSuffix { uint8_t impUse[8]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (UDF 2.50 2.2.6) */ /* Implementation Use (UDF 2.50 2.2.6.4) */ struct logicalVolIntegrityDescImpUse { regid impIdent; __le32 numFiles; __le32 numDirs; __le16 minUDFReadRev; __le16 minUDFWriteRev; __le16 maxUDFWriteRev; uint8_t impUse[0]; } __attribute__ ((packed)); /* Implementation Use Volume Descriptor (UDF 2.50 2.2.7) */ /* Implementation Use (UDF 2.50 2.2.7.2) */ struct impUseVolDescImpUse { charspec LVICharset; dstring logicalVolIdent[128]; dstring LVInfo1[36]; dstring LVInfo2[36]; dstring LVInfo3[36]; regid impIdent; uint8_t impUse[128]; } __attribute__ ((packed)); struct udfPartitionMap2 { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; } __attribute__ ((packed)); /* Virtual Partition Map (UDF 2.50 2.2.8) */ struct virtualPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; uint8_t reserved2[24]; } __attribute__ ((packed)); /* Sparable Partition Map (UDF 2.50 2.2.9) */ struct sparablePartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; __le16 packetLength; uint8_t numSparingTables; uint8_t reserved2[1]; __le32 sizeSparingTable; __le32 locSparingTable[4]; } __attribute__ ((packed)); /* Metadata Partition Map (UDF 2.4.0 2.2.10) */ struct metadataPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; __le32 metadataFileLoc; __le32 metadataMirrorFileLoc; __le32 metadataBitmapFileLoc; __le32 allocUnitSize; __le16 alignUnitSize; uint8_t flags; uint8_t reserved2[5]; } __attribute__ ((packed)); /* Virtual Allocation Table (UDF 1.5 2.2.10) */ struct virtualAllocationTable15 { __le32 VirtualSector[0]; regid vatIdent; __le32 previousVATICBLoc; } __attribute__ ((packed)); #define ICBTAG_FILE_TYPE_VAT15 0x00U /* Virtual Allocation Table (UDF 2.50 2.2.11) */ struct virtualAllocationTable20 { __le16 lengthHeader; __le16 lengthImpUse; dstring logicalVolIdent[128]; __le32 previousVATICBLoc; __le32 numFiles; __le32 numDirs; __le16 minReadRevision; __le16 minWriteRevision; __le16 maxWriteRevision; __le16 reserved; uint8_t impUse[0]; __le32 vatEntry[0]; } __attribute__ ((packed)); #define ICBTAG_FILE_TYPE_VAT20 0xF8U /* Sparing Table (UDF 2.50 2.2.12) */ struct sparingEntry { __le32 origLocation; __le32 mappedLocation; } __attribute__ ((packed)); struct sparingTable { tag descTag; regid sparingIdent; __le16 reallocationTableLen; __le16 reserved; __le32 sequenceNum; struct sparingEntry mapEntry[0]; } __attribute__ ((packed)); /* Metadata File (and Metadata Mirror File) (UDF 2.50 2.2.13.1) */ #define ICBTAG_FILE_TYPE_MAIN 0xFA #define ICBTAG_FILE_TYPE_MIRROR 0xFB #define ICBTAG_FILE_TYPE_BITMAP 0xFC /* struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */ struct allocDescImpUse { __le16 flags; uint8_t impUse[4]; } __attribute__ ((packed)); #define AD_IU_EXT_ERASED 0x0001 /* Real-Time Files (UDF 2.50 6.11) */ #define ICBTAG_FILE_TYPE_REALTIME 0xF9U /* Implementation Use Extended Attribute (UDF 2.50 3.3.4.5) */ /* FreeEASpace (UDF 2.50 3.3.4.5.1.1) */ struct freeEaSpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); /* DVD Copyright Management Information (UDF 2.50 3.3.4.5.1.2) */ struct DVDCopyrightImpUse { __le16 headerChecksum; uint8_t CGMSInfo; uint8_t dataType; uint8_t protectionSystemInfo[4]; } __attribute__ ((packed)); /* Application Use Extended Attribute (UDF 2.50 3.3.4.6) */ /* FreeAppEASpace (UDF 2.50 3.3.4.6.1) */ struct freeAppEASpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); /* UDF Defined System Stream (UDF 2.50 3.3.7) */ #define UDF_ID_UNIQUE_ID "*UDF Unique ID Mapping Data" #define UDF_ID_NON_ALLOC "*UDF Non-Allocatable Space" #define UDF_ID_POWER_CAL "*UDF Power Cal Table" #define UDF_ID_BACKUP "*UDF Backup" /* Operating System Identifiers (UDF 2.50 6.3) */ #define UDF_OS_CLASS_UNDEF 0x00U #define UDF_OS_CLASS_DOS 0x01U #define UDF_OS_CLASS_OS2 0x02U #define UDF_OS_CLASS_MAC 0x03U #define UDF_OS_CLASS_UNIX 0x04U #define UDF_OS_CLASS_WIN9X 0x05U #define UDF_OS_CLASS_WINNT 0x06U #define UDF_OS_CLASS_OS400 0x07U #define UDF_OS_CLASS_BEOS 0x08U #define UDF_OS_CLASS_WINCE 0x09U #define UDF_OS_ID_UNDEF 0x00U #define UDF_OS_ID_DOS 0x00U #define UDF_OS_ID_OS2 0x00U #define UDF_OS_ID_MAC 0x00U #define UDF_OS_ID_MAX_OSX 0x01U #define UDF_OS_ID_UNIX 0x00U #define UDF_OS_ID_AIX 0x01U #define UDF_OS_ID_SOLARIS 0x02U #define UDF_OS_ID_HPUX 0x03U #define UDF_OS_ID_IRIX 0x04U #define UDF_OS_ID_LINUX 0x05U #define UDF_OS_ID_MKLINUX 0x06U #define UDF_OS_ID_FREEBSD 0x07U #define UDF_OS_ID_WIN9X 0x00U #define UDF_OS_ID_WINNT 0x00U #define UDF_OS_ID_OS400 0x00U #define UDF_OS_ID_BEOS 0x00U #define UDF_OS_ID_WINCE 0x00U #endif /* _OSTA_UDF_H */ linux-2.6.17/fs/udf/crc.c0000644000000000000000000001230010564537530013551 0ustar rootroot/* * crc.c * * PURPOSE * Routines to generate, calculate, and test a 16-bit CRC. * * DESCRIPTION * The CRC code was devised by Don P. Mitchell of AT&T Bell Laboratories * and Ned W. Rhodes of Software Systems Group. It has been published in * "Design and Validation of Computer Protocols", Prentice Hall, * Englewood Cliffs, NJ, 1991, Chapter 3, ISBN 0-13-539925-4. * * Copyright is held by AT&T. * * AT&T gives permission for the free use of the CRC source code. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. */ #include "udfdecl.h" static uint16_t crc_table[256] = { 0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50a5U, 0x60c6U, 0x70e7U, 0x8108U, 0x9129U, 0xa14aU, 0xb16bU, 0xc18cU, 0xd1adU, 0xe1ceU, 0xf1efU, 0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52b5U, 0x4294U, 0x72f7U, 0x62d6U, 0x9339U, 0x8318U, 0xb37bU, 0xa35aU, 0xd3bdU, 0xc39cU, 0xf3ffU, 0xe3deU, 0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64e6U, 0x74c7U, 0x44a4U, 0x5485U, 0xa56aU, 0xb54bU, 0x8528U, 0x9509U, 0xe5eeU, 0xf5cfU, 0xc5acU, 0xd58dU, 0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76d7U, 0x66f6U, 0x5695U, 0x46b4U, 0xb75bU, 0xa77aU, 0x9719U, 0x8738U, 0xf7dfU, 0xe7feU, 0xd79dU, 0xc7bcU, 0x48c4U, 0x58e5U, 0x6886U, 0x78a7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U, 0xc9ccU, 0xd9edU, 0xe98eU, 0xf9afU, 0x8948U, 0x9969U, 0xa90aU, 0xb92bU, 0x5af5U, 0x4ad4U, 0x7ab7U, 0x6a96U, 0x1a71U, 0x0a50U, 0x3a33U, 0x2a12U, 0xdbfdU, 0xcbdcU, 0xfbbfU, 0xeb9eU, 0x9b79U, 0x8b58U, 0xbb3bU, 0xab1aU, 0x6ca6U, 0x7c87U, 0x4ce4U, 0x5cc5U, 0x2c22U, 0x3c03U, 0x0c60U, 0x1c41U, 0xedaeU, 0xfd8fU, 0xcdecU, 0xddcdU, 0xad2aU, 0xbd0bU, 0x8d68U, 0x9d49U, 0x7e97U, 0x6eb6U, 0x5ed5U, 0x4ef4U, 0x3e13U, 0x2e32U, 0x1e51U, 0x0e70U, 0xff9fU, 0xefbeU, 0xdfddU, 0xcffcU, 0xbf1bU, 0xaf3aU, 0x9f59U, 0x8f78U, 0x9188U, 0x81a9U, 0xb1caU, 0xa1ebU, 0xd10cU, 0xc12dU, 0xf14eU, 0xe16fU, 0x1080U, 0x00a1U, 0x30c2U, 0x20e3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U, 0x83b9U, 0x9398U, 0xa3fbU, 0xb3daU, 0xc33dU, 0xd31cU, 0xe37fU, 0xf35eU, 0x02b1U, 0x1290U, 0x22f3U, 0x32d2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U, 0xb5eaU, 0xa5cbU, 0x95a8U, 0x8589U, 0xf56eU, 0xe54fU, 0xd52cU, 0xc50dU, 0x34e2U, 0x24c3U, 0x14a0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U, 0xa7dbU, 0xb7faU, 0x8799U, 0x97b8U, 0xe75fU, 0xf77eU, 0xc71dU, 0xd73cU, 0x26d3U, 0x36f2U, 0x0691U, 0x16b0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U, 0xd94cU, 0xc96dU, 0xf90eU, 0xe92fU, 0x99c8U, 0x89e9U, 0xb98aU, 0xa9abU, 0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18c0U, 0x08e1U, 0x3882U, 0x28a3U, 0xcb7dU, 0xdb5cU, 0xeb3fU, 0xfb1eU, 0x8bf9U, 0x9bd8U, 0xabbbU, 0xbb9aU, 0x4a75U, 0x5a54U, 0x6a37U, 0x7a16U, 0x0af1U, 0x1ad0U, 0x2ab3U, 0x3a92U, 0xfd2eU, 0xed0fU, 0xdd6cU, 0xcd4dU, 0xbdaaU, 0xad8bU, 0x9de8U, 0x8dc9U, 0x7c26U, 0x6c07U, 0x5c64U, 0x4c45U, 0x3ca2U, 0x2c83U, 0x1ce0U, 0x0cc1U, 0xef1fU, 0xff3eU, 0xcf5dU, 0xdf7cU, 0xaf9bU, 0xbfbaU, 0x8fd9U, 0x9ff8U, 0x6e17U, 0x7e36U, 0x4e55U, 0x5e74U, 0x2e93U, 0x3eb2U, 0x0ed1U, 0x1ef0U }; /* * udf_crc * * PURPOSE * Calculate a 16-bit CRC checksum using ITU-T V.41 polynomial. * * DESCRIPTION * The OSTA-UDF(tm) 1.50 standard states that using CRCs is mandatory. * The polynomial used is: x^16 + x^12 + x^15 + 1 * * PRE-CONDITIONS * data Pointer to the data block. * size Size of the data block. * * POST-CONDITIONS * CRC of the data block. * * HISTORY * July 21, 1997 - Andrew E. Mileski * Adapted from OSTA-UDF(tm) 1.50 standard. */ uint16_t udf_crc(uint8_t *data, uint32_t size, uint16_t crc) { while (size--) crc = crc_table[(crc >> 8 ^ *(data++)) & 0xffU] ^ (crc << 8); return crc; } /****************************************************************************/ #if defined(TEST) /* * PURPOSE * Test udf_crc() * * HISTORY * July 21, 1997 - Andrew E. Mileski * Adapted from OSTA-UDF(tm) 1.50 standard. */ unsigned char bytes[] = { 0x70U, 0x6AU, 0x77U }; int main(void) { unsigned short x; x = udf_crc16(bytes, sizeof bytes); printf("udf_crc16: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U); return 0; } #endif /* defined(TEST) */ /****************************************************************************/ #if defined(GENERATE) /* * PURPOSE * Generate a table for fast 16-bit CRC calculations (any polynomial). * * DESCRIPTION * The ITU-T V.41 polynomial is 010041. * * HISTORY * July 21, 1997 - Andrew E. Mileski * Adapted from OSTA-UDF(tm) 1.50 standard. */ #include int main(int argc, char **argv) { unsigned long crc, poly; int n, i; /* Get the polynomial */ sscanf(argv[1], "%lo", &poly); if (poly & 0xffff0000U){ fprintf(stderr, "polynomial is too large\en"); exit(1); } printf("/* CRC 0%o */\n", poly); /* Create a table */ printf("static unsigned short crc_table[256] = {\n"); for (n = 0; n < 256; n++){ if (n % 8 == 0) printf("\t"); crc = n << 8; for (i = 0; i < 8; i++){ if(crc & 0x8000U) crc = (crc << 1) ^ poly; else crc <<= 1; crc &= 0xFFFFU; } if (n == 255) printf("0x%04xU ", crc); else printf("0x%04xU, ", crc); if(n % 8 == 7) printf("\n"); } printf("};\n"); return 0; } #endif /* defined(GENERATE) */ linux-2.6.17/fs/udf/truncate.c0000644000000000000000000002002010564537530014625 0ustar rootroot/* * truncate.c * * PURPOSE * Truncate handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2004 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY * * 02/24/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include #include "udf_i.h" #include "udf_sb.h" static void extent_trunc(struct inode * inode, kernel_lb_addr bloc, int extoffset, kernel_lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen) { kernel_lb_addr neloc = { 0, 0 }; int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; if (nelen) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block); etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30); } else neloc = eloc; nelen = (etype << 30) | nelen; } if (elen != nelen) { udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0); if (last_block - first_block > 0) { if (etype == (EXT_RECORDED_ALLOCATED >> 30)) mark_inode_dirty(inode); if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block); } } } void udf_discard_prealloc(struct inode * inode) { kernel_lb_addr bloc, eloc; uint32_t extoffset = 0, elen, nelen; uint64_t lbcount = 0; int8_t etype = -1, netype; struct buffer_head *bh = NULL; int adsize; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || inode->i_size == UDF_I_LENEXTENTS(inode)) { return; } if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else adsize = 0; bloc = UDF_I_LOCATION(inode); while ((netype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { etype = netype; lbcount += elen; if (lbcount > inode->i_size && lbcount - inode->i_size < inode->i_sb->s_blocksize) { nelen = elen - (lbcount - inode->i_size); extent_trunc(inode, bloc, extoffset-adsize, eloc, etype, elen, bh, nelen); lbcount = inode->i_size; } } if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { extoffset -= adsize; lbcount -= elen; extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0); if (!bh) { UDF_I_LENALLOC(inode) = extoffset - udf_file_entry_alloc_offset(inode); mark_inode_dirty(inode); } else { struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); aed->lengthAllocDescs = cpu_to_le32(extoffset - sizeof(struct allocExtDesc)); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(bh->b_data, extoffset); else udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(bh, inode); } } UDF_I_LENEXTENTS(inode) = lbcount; udf_release_data(bh); } void udf_truncate_extents(struct inode * inode) { kernel_lb_addr bloc, eloc, neloc = { 0, 0 }; uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc; int8_t etype; int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits; struct buffer_head *bh = NULL; int adsize; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else adsize = 0; etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh); offset += (inode->i_size & (inode->i_sb->s_blocksize - 1)); if (etype != -1) { extoffset -= adsize; extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset); extoffset += adsize; if (offset) lenalloc = extoffset; else lenalloc = extoffset - adsize; if (!bh) lenalloc -= udf_file_entry_alloc_offset(inode); else lenalloc -= sizeof(struct allocExtDesc); while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1) { if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0); extoffset = 0; if (lelen) { if (!bh) BUG(); else memset(bh->b_data, 0x00, sizeof(struct allocExtDesc)); udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen); } else { if (!bh) { UDF_I_LENALLOC(inode) = lenalloc; mark_inode_dirty(inode); } else { struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); aed->lengthAllocDescs = cpu_to_le32(lenalloc); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(bh->b_data, lenalloc + sizeof(struct allocExtDesc)); else udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(bh, inode); } } udf_release_data(bh); extoffset = sizeof(struct allocExtDesc); bloc = eloc; bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, bloc, 0)); if (elen) lelen = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; else lelen = 1; } else { extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0); extoffset += adsize; } } if (lelen) { if (!bh) BUG(); else memset(bh->b_data, 0x00, sizeof(struct allocExtDesc)); udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen); } else { if (!bh) { UDF_I_LENALLOC(inode) = lenalloc; mark_inode_dirty(inode); } else { struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); aed->lengthAllocDescs = cpu_to_le32(lenalloc); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(bh->b_data, lenalloc + sizeof(struct allocExtDesc)); else udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(bh, inode); } } } else if (inode->i_size) { if (offset) { /* * OK, there is not extent covering inode->i_size and * no extent above inode->i_size => truncate is * extending the file by 'offset'. */ if ((!bh && extoffset == udf_file_entry_alloc_offset(inode)) || (bh && extoffset == sizeof(struct allocExtDesc))) { /* File has no extents at all! */ memset(&eloc, 0x00, sizeof(kernel_lb_addr)); elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset; udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); } else { extoffset -= adsize; etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1); if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { extoffset -= adsize; elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset); udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0); } else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { kernel_lb_addr neloc = { 0, 0 }; extoffset -= adsize; nelen = EXT_NOT_RECORDED_NOT_ALLOCATED | ((elen + offset + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1); udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1); } else { if (elen & (inode->i_sb->s_blocksize - 1)) { extoffset -= adsize; elen = EXT_RECORDED_ALLOCATED | ((elen + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1); } memset(&eloc, 0x00, sizeof(kernel_lb_addr)); elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset; udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); } } } } UDF_I_LENEXTENTS(inode) = inode->i_size; udf_release_data(bh); } linux-2.6.17/fs/udf/.svn/0000755000000000000000000000000010613366603013522 5ustar rootrootlinux-2.6.17/fs/udf/.svn/format0000644000000000000000000000000210574207644014732 0ustar rootroot8 linux-2.6.17/fs/udf/.svn/entries0000444000000000000000000000574010613366571015126 0ustar rootroot8 dir 88 svn+ssh://192.168.2.6/home/svn-root/repos/FVT/linux-2.6.17/fs/udf svn+ssh://192.168.2.6/home/svn-root/repos/FVT 2007-02-14T07:50:48.156956Z 1 root svn:special svn:externals svn:needs-lock 4bdaaa2a-274d-416c-a84c-08d2865ac13c Makefile file 2007-02-14T07:50:48.000000Z f3622d92294cba6ea7bc4e0a9262c989 2007-02-14T07:50:48.156956Z 1 root balloc.c file 2007-02-14T07:50:48.000000Z 07ddcfd2b9fc2733840b547920dd8a4c 2007-02-14T07:50:48.156956Z 1 root crc.c file 2007-02-14T07:50:48.000000Z 5d207c204c7572b7370d233a6dc34092 2007-02-14T07:50:48.156956Z 1 root dir.c file 2007-02-14T07:50:48.000000Z 5883c565eeba666705bcd439695057a9 2007-02-14T07:50:48.156956Z 1 root directory.c file 2007-02-14T07:50:48.000000Z 5a80aecc51bf54f290bcc34556a3a4a6 2007-02-14T07:50:48.156956Z 1 root ecma_167.h file 2007-02-14T07:50:48.000000Z 4d87ee899eb83c319885cc0d26c01697 2007-02-14T07:50:48.156956Z 1 root file.c file 2007-02-14T07:50:48.000000Z e9c99cdfaee94c8c10a28176a1ab5ce4 2007-02-14T07:50:48.156956Z 1 root fsync.c file 2007-02-14T07:50:48.000000Z c46a2d0e8c2a58142228e090b96ff427 2007-02-14T07:50:48.156956Z 1 root ialloc.c file 2007-02-14T07:50:48.000000Z 4e74f00ab21eb716f4596d41e7bee742 2007-02-14T07:50:48.156956Z 1 root inode.c file 2007-02-14T07:50:48.000000Z 7ec19de3776874d4e3871d1a1a8658ad 2007-02-14T07:50:48.156956Z 1 root lowlevel.c file 2007-02-14T07:50:48.000000Z f827d60d7aba9a4855ec7adab344070e 2007-02-14T07:50:48.156956Z 1 root misc.c file 2007-02-14T07:50:48.000000Z ae2d1a29216ef4af9d7118df1ea6cc20 2007-02-14T07:50:48.156956Z 1 root namei.c file 2007-02-14T07:50:48.000000Z 545b9948d257a201738db9d7c4e831c3 2007-02-14T07:50:48.156956Z 1 root osta_udf.h file 2007-02-14T07:50:48.000000Z a20c83fa89c9641290683c3497c3bfec 2007-02-14T07:50:48.156956Z 1 root partition.c file 2007-02-14T07:50:48.000000Z 64e5d24193bb404e0259fc9daacc37fa 2007-02-14T07:50:48.156956Z 1 root super.c file 2007-02-14T07:50:48.000000Z 40056dd9489dfe04323d890714980118 2007-02-14T07:50:48.156956Z 1 root symlink.c file 2007-02-14T07:50:48.000000Z 7b6cc2310c4b5241470da2c433ccccca 2007-02-14T07:50:48.156956Z 1 root truncate.c file 2007-02-14T07:50:48.000000Z 70bac88f3391ed839852511cce602308 2007-02-14T07:50:48.156956Z 1 root udf_i.h file 2007-02-14T07:50:48.000000Z 237373eef965d8346a12a23ecadc0072 2007-02-14T07:50:48.156956Z 1 root udf_sb.h file 2007-02-14T07:50:48.000000Z 5b592064f95e08c198ec25a3cdebeffa 2007-02-14T07:50:48.156956Z 1 root udfdecl.h file 2007-02-14T07:50:48.000000Z 48c113578f36dbbb972dd081a1c394fb 2007-02-14T07:50:48.156956Z 1 root udfend.h file 2007-02-14T07:50:48.000000Z 8624ecdf2ac857904a70b8bb331a88a7 2007-02-14T07:50:48.156956Z 1 root udftime.c file 2007-02-14T07:50:48.000000Z 05ff99b7a6564d316076bab61e9697b1 2007-02-14T07:50:48.156956Z 1 root unicode.c file 2007-02-14T07:50:48.000000Z f7d8d6fdca0e8c199b3fcddc08acb61b 2007-02-14T07:50:48.156956Z 1 root linux-2.6.17/fs/udf/.svn/tmp/0000755000000000000000000000000010613366571014326 5ustar rootrootlinux-2.6.17/fs/udf/.svn/tmp/props/0000755000000000000000000000000010574207644015472 5ustar rootrootlinux-2.6.17/fs/udf/.svn/tmp/text-base/0000755000000000000000000000000010574207645016224 5ustar rootrootlinux-2.6.17/fs/udf/.svn/tmp/prop-base/0000755000000000000000000000000010574207644016217 5ustar rootrootlinux-2.6.17/fs/udf/.svn/props/0000755000000000000000000000000010574207644014672 5ustar rootrootlinux-2.6.17/fs/udf/.svn/text-base/0000755000000000000000000000000010574207645015424 5ustar rootrootlinux-2.6.17/fs/udf/.svn/text-base/osta_udf.h.svn-base0000444000000000000000000002020010574207644021105 0ustar rootroot/* * osta_udf.h * * This file is based on OSTA UDF(tm) 2.50 (April 30, 2003) * http://www.osta.org * * Copyright (c) 2001-2004 Ben Fennema * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "ecma_167.h" #ifndef _OSTA_UDF_H #define _OSTA_UDF_H 1 /* OSTA CS0 Charspec (UDF 2.50 2.1.2) */ #define UDF_CHAR_SET_TYPE 0 #define UDF_CHAR_SET_INFO "OSTA Compressed Unicode" /* Entity Identifier (UDF 2.50 2.1.5) */ /* Identifiers (UDF 2.50 2.1.5.2) */ #define UDF_ID_DEVELOPER "*Linux UDFFS" #define UDF_ID_COMPLIANT "*OSTA UDF Compliant" #define UDF_ID_LV_INFO "*UDF LV Info" #define UDF_ID_FREE_EA "*UDF FreeEASpace" #define UDF_ID_FREE_APP_EA "*UDF FreeAppEASpace" #define UDF_ID_DVD_CGMS "*UDF DVD CGMS Info" #define UDF_ID_OS2_EA "*UDF OS/2 EA" #define UDF_ID_OS2_EA_LENGTH "*UDF OS/2 EALength" #define UDF_ID_MAC_VOLUME "*UDF Mac VolumeInfo" #define UDF_ID_MAC_FINDER "*UDF Mac FinderInfo" #define UDF_ID_MAC_UNIQUE "*UDF Mac UniqueIDTable" #define UDF_ID_MAC_RESOURCE "*UDF Mac ResourceFork" #define UDF_ID_VIRTUAL "*UDF Virtual Partition" #define UDF_ID_SPARABLE "*UDF Sparable Partition" #define UDF_ID_ALLOC "*UDF Virtual Alloc Tbl" #define UDF_ID_SPARING "*UDF Sparing Table" #define UDF_ID_METADATA "*UDF Metadata Partition" /* Identifier Suffix (UDF 2.50 2.1.5.3) */ #define IS_DF_HARD_WRITE_PROTECT 0x01 #define IS_DF_SOFT_WRITE_PROTECT 0x02 struct UDFIdentSuffix { __le16 UDFRevision; uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[4]; } __attribute__ ((packed)); struct impIdentSuffix { uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[6]; } __attribute__ ((packed)); struct appIdentSuffix { uint8_t impUse[8]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (UDF 2.50 2.2.6) */ /* Implementation Use (UDF 2.50 2.2.6.4) */ struct logicalVolIntegrityDescImpUse { regid impIdent; __le32 numFiles; __le32 numDirs; __le16 minUDFReadRev; __le16 minUDFWriteRev; __le16 maxUDFWriteRev; uint8_t impUse[0]; } __attribute__ ((packed)); /* Implementation Use Volume Descriptor (UDF 2.50 2.2.7) */ /* Implementation Use (UDF 2.50 2.2.7.2) */ struct impUseVolDescImpUse { charspec LVICharset; dstring logicalVolIdent[128]; dstring LVInfo1[36]; dstring LVInfo2[36]; dstring LVInfo3[36]; regid impIdent; uint8_t impUse[128]; } __attribute__ ((packed)); struct udfPartitionMap2 { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; } __attribute__ ((packed)); /* Virtual Partition Map (UDF 2.50 2.2.8) */ struct virtualPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; uint8_t reserved2[24]; } __attribute__ ((packed)); /* Sparable Partition Map (UDF 2.50 2.2.9) */ struct sparablePartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; __le16 packetLength; uint8_t numSparingTables; uint8_t reserved2[1]; __le32 sizeSparingTable; __le32 locSparingTable[4]; } __attribute__ ((packed)); /* Metadata Partition Map (UDF 2.4.0 2.2.10) */ struct metadataPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; regid partIdent; __le16 volSeqNum; __le16 partitionNum; __le32 metadataFileLoc; __le32 metadataMirrorFileLoc; __le32 metadataBitmapFileLoc; __le32 allocUnitSize; __le16 alignUnitSize; uint8_t flags; uint8_t reserved2[5]; } __attribute__ ((packed)); /* Virtual Allocation Table (UDF 1.5 2.2.10) */ struct virtualAllocationTable15 { __le32 VirtualSector[0]; regid vatIdent; __le32 previousVATICBLoc; } __attribute__ ((packed)); #define ICBTAG_FILE_TYPE_VAT15 0x00U /* Virtual Allocation Table (UDF 2.50 2.2.11) */ struct virtualAllocationTable20 { __le16 lengthHeader; __le16 lengthImpUse; dstring logicalVolIdent[128]; __le32 previousVATICBLoc; __le32 numFiles; __le32 numDirs; __le16 minReadRevision; __le16 minWriteRevision; __le16 maxWriteRevision; __le16 reserved; uint8_t impUse[0]; __le32 vatEntry[0]; } __attribute__ ((packed)); #define ICBTAG_FILE_TYPE_VAT20 0xF8U /* Sparing Table (UDF 2.50 2.2.12) */ struct sparingEntry { __le32 origLocation; __le32 mappedLocation; } __attribute__ ((packed)); struct sparingTable { tag descTag; regid sparingIdent; __le16 reallocationTableLen; __le16 reserved; __le32 sequenceNum; struct sparingEntry mapEntry[0]; } __attribute__ ((packed)); /* Metadata File (and Metadata Mirror File) (UDF 2.50 2.2.13.1) */ #define ICBTAG_FILE_TYPE_MAIN 0xFA #define ICBTAG_FILE_TYPE_MIRROR 0xFB #define ICBTAG_FILE_TYPE_BITMAP 0xFC /* struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */ struct allocDescImpUse { __le16 flags; uint8_t impUse[4]; } __attribute__ ((packed)); #define AD_IU_EXT_ERASED 0x0001 /* Real-Time Files (UDF 2.50 6.11) */ #define ICBTAG_FILE_TYPE_REALTIME 0xF9U /* Implementation Use Extended Attribute (UDF 2.50 3.3.4.5) */ /* FreeEASpace (UDF 2.50 3.3.4.5.1.1) */ struct freeEaSpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); /* DVD Copyright Management Information (UDF 2.50 3.3.4.5.1.2) */ struct DVDCopyrightImpUse { __le16 headerChecksum; uint8_t CGMSInfo; uint8_t dataType; uint8_t protectionSystemInfo[4]; } __attribute__ ((packed)); /* Application Use Extended Attribute (UDF 2.50 3.3.4.6) */ /* FreeAppEASpace (UDF 2.50 3.3.4.6.1) */ struct freeAppEASpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); /* UDF Defined System Stream (UDF 2.50 3.3.7) */ #define UDF_ID_UNIQUE_ID "*UDF Unique ID Mapping Data" #define UDF_ID_NON_ALLOC "*UDF Non-Allocatable Space" #define UDF_ID_POWER_CAL "*UDF Power Cal Table" #define UDF_ID_BACKUP "*UDF Backup" /* Operating System Identifiers (UDF 2.50 6.3) */ #define UDF_OS_CLASS_UNDEF 0x00U #define UDF_OS_CLASS_DOS 0x01U #define UDF_OS_CLASS_OS2 0x02U #define UDF_OS_CLASS_MAC 0x03U #define UDF_OS_CLASS_UNIX 0x04U #define UDF_OS_CLASS_WIN9X 0x05U #define UDF_OS_CLASS_WINNT 0x06U #define UDF_OS_CLASS_OS400 0x07U #define UDF_OS_CLASS_BEOS 0x08U #define UDF_OS_CLASS_WINCE 0x09U #define UDF_OS_ID_UNDEF 0x00U #define UDF_OS_ID_DOS 0x00U #define UDF_OS_ID_OS2 0x00U #define UDF_OS_ID_MAC 0x00U #define UDF_OS_ID_MAX_OSX 0x01U #define UDF_OS_ID_UNIX 0x00U #define UDF_OS_ID_AIX 0x01U #define UDF_OS_ID_SOLARIS 0x02U #define UDF_OS_ID_HPUX 0x03U #define UDF_OS_ID_IRIX 0x04U #define UDF_OS_ID_LINUX 0x05U #define UDF_OS_ID_MKLINUX 0x06U #define UDF_OS_ID_FREEBSD 0x07U #define UDF_OS_ID_WIN9X 0x00U #define UDF_OS_ID_WINNT 0x00U #define UDF_OS_ID_OS400 0x00U #define UDF_OS_ID_BEOS 0x00U #define UDF_OS_ID_WINCE 0x00U #endif /* _OSTA_UDF_H */ linux-2.6.17/fs/udf/.svn/text-base/fsync.c.svn-base0000444000000000000000000000217410574207644020430 0ustar rootroot/* * fsync.c * * PURPOSE * Fsync handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2001 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 05/22/99 blf Created. */ #include "udfdecl.h" #include #include static int udf_fsync_inode(struct inode *, int); /* * File may be NULL when we are called. Perhaps we shouldn't * even pass file to fsync ? */ int udf_fsync_file(struct file * file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; return udf_fsync_inode(inode, datasync); } static int udf_fsync_inode(struct inode *inode, int datasync) { int err; err = sync_mapping_buffers(inode->i_mapping); if (!(inode->i_state & I_DIRTY)) return err; if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) return err; err |= udf_sync_inode (inode); return err ? -EIO : 0; } linux-2.6.17/fs/udf/.svn/text-base/Makefile.svn-base0000444000000000000000000000040610574207644020576 0ustar rootroot# # Makefile for the linux udf-filesystem routines. # obj-$(CONFIG_UDF_FS) += udf.o udf-objs := balloc.o dir.o file.o ialloc.o inode.o lowlevel.o namei.o \ partition.o super.o truncate.o symlink.o fsync.o \ crc.o directory.o misc.o udftime.o unicode.o linux-2.6.17/fs/udf/.svn/text-base/ecma_167.h.svn-base0000444000000000000000000005342510574207644020622 0ustar rootroot/* * ecma_167.h * * This file is based on ECMA-167 3rd edition (June 1997) * http://www.ecma.ch * * Copyright (c) 2001-2002 Ben Fennema * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef _ECMA_167_H #define _ECMA_167_H 1 /* Character set specification (ECMA 167r3 1/7.2.1) */ typedef struct { uint8_t charSetType; uint8_t charSetInfo[63]; } __attribute__ ((packed)) charspec; /* Character Set Type (ECMA 167r3 1/7.2.1.1) */ #define CHARSPEC_TYPE_CS0 0x00 /* (1/7.2.2) */ #define CHARSPEC_TYPE_CS1 0x01 /* (1/7.2.3) */ #define CHARSPEC_TYPE_CS2 0x02 /* (1/7.2.4) */ #define CHARSPEC_TYPE_CS3 0x03 /* (1/7.2.5) */ #define CHARSPEC_TYPE_CS4 0x04 /* (1/7.2.6) */ #define CHARSPEC_TYPE_CS5 0x05 /* (1/7.2.7) */ #define CHARSPEC_TYPE_CS6 0x06 /* (1/7.2.8) */ #define CHARSPEC_TYPE_CS7 0x07 /* (1/7.2.9) */ #define CHARSPEC_TYPE_CS8 0x08 /* (1/7.2.10) */ typedef uint8_t dstring; /* Timestamp (ECMA 167r3 1/7.3) */ typedef struct { __le16 typeAndTimezone; __le16 year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; uint8_t centiseconds; uint8_t hundredsOfMicroseconds; uint8_t microseconds; } __attribute__ ((packed)) timestamp; typedef struct { uint16_t typeAndTimezone; int16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; uint8_t centiseconds; uint8_t hundredsOfMicroseconds; uint8_t microseconds; } __attribute__ ((packed)) kernel_timestamp; /* Type and Time Zone (ECMA 167r3 1/7.3.1) */ #define TIMESTAMP_TYPE_MASK 0xF000 #define TIMESTAMP_TYPE_CUT 0x0000 #define TIMESTAMP_TYPE_LOCAL 0x1000 #define TIMESTAMP_TYPE_AGREEMENT 0x2000 #define TIMESTAMP_TIMEZONE_MASK 0x0FFF /* Entity identifier (ECMA 167r3 1/7.4) */ typedef struct { uint8_t flags; uint8_t ident[23]; uint8_t identSuffix[8]; } __attribute__ ((packed)) regid; /* Flags (ECMA 167r3 1/7.4.1) */ #define ENTITYID_FLAGS_DIRTY 0x00 #define ENTITYID_FLAGS_PROTECTED 0x01 /* Volume Structure Descriptor (ECMA 167r3 2/9.1) */ #define VSD_STD_ID_LEN 5 struct volStructDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t structData[2041]; } __attribute__ ((packed)); /* Standard Identifier (EMCA 167r2 2/9.1.2) */ #define VSD_STD_ID_NSR02 "NSR02" /* (3/9.1) */ /* Standard Identifier (ECMA 167r3 2/9.1.2) */ #define VSD_STD_ID_BEA01 "BEA01" /* (2/9.2) */ #define VSD_STD_ID_BOOT2 "BOOT2" /* (2/9.4) */ #define VSD_STD_ID_CD001 "CD001" /* (ECMA-119) */ #define VSD_STD_ID_CDW02 "CDW02" /* (ECMA-168) */ #define VSD_STD_ID_NSR03 "NSR03" /* (3/9.1) */ #define VSD_STD_ID_TEA01 "TEA01" /* (2/9.3) */ /* Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */ struct beginningExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t structData[2041]; } __attribute__ ((packed)); /* Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */ struct terminatingExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t structData[2041]; } __attribute__ ((packed)); /* Boot Descriptor (ECMA 167r3 2/9.4) */ struct bootDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t reserved1; regid archType; regid bootIdent; __le32 bootExtLocation; __le32 bootExtLength; __le64 loadAddress; __le64 startAddress; timestamp descCreationDateAndTime; __le16 flags; uint8_t reserved2[32]; uint8_t bootUse[1906]; } __attribute__ ((packed)); /* Flags (ECMA 167r3 2/9.4.12) */ #define BOOT_FLAGS_ERASE 0x01 /* Extent Descriptor (ECMA 167r3 3/7.1) */ typedef struct { __le32 extLength; __le32 extLocation; } __attribute__ ((packed)) extent_ad; typedef struct { uint32_t extLength; uint32_t extLocation; } kernel_extent_ad; /* Descriptor Tag (ECMA 167r3 3/7.2) */ typedef struct { __le16 tagIdent; __le16 descVersion; uint8_t tagChecksum; uint8_t reserved; __le16 tagSerialNum; __le16 descCRC; __le16 descCRCLength; __le32 tagLocation; } __attribute__ ((packed)) tag; /* Tag Identifier (ECMA 167r3 3/7.2.1) */ #define TAG_IDENT_PVD 0x0001 #define TAG_IDENT_AVDP 0x0002 #define TAG_IDENT_VDP 0x0003 #define TAG_IDENT_IUVD 0x0004 #define TAG_IDENT_PD 0x0005 #define TAG_IDENT_LVD 0x0006 #define TAG_IDENT_USD 0x0007 #define TAG_IDENT_TD 0x0008 #define TAG_IDENT_LVID 0x0009 /* NSR Descriptor (ECMA 167r3 3/9.1) */ struct NSRDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t reserved; uint8_t structData[2040]; } __attribute__ ((packed)); /* Primary Volume Descriptor (ECMA 167r3 3/10.1) */ struct primaryVolDesc { tag descTag; __le32 volDescSeqNum; __le32 primaryVolDescNum; dstring volIdent[32]; __le16 volSeqNum; __le16 maxVolSeqNum; __le16 interchangeLvl; __le16 maxInterchangeLvl; __le32 charSetList; __le32 maxCharSetList; dstring volSetIdent[128]; charspec descCharSet; charspec explanatoryCharSet; extent_ad volAbstract; extent_ad volCopyright; regid appIdent; timestamp recordingDateAndTime; regid impIdent; uint8_t impUse[64]; __le32 predecessorVolDescSeqLocation; __le16 flags; uint8_t reserved[22]; } __attribute__ ((packed)); /* Flags (ECMA 167r3 3/10.1.21) */ #define PVD_FLAGS_VSID_COMMON 0x0001 /* Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */ struct anchorVolDescPtr { tag descTag; extent_ad mainVolDescSeqExt; extent_ad reserveVolDescSeqExt; uint8_t reserved[480]; } __attribute__ ((packed)); /* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */ struct volDescPtr { tag descTag; __le32 volDescSeqNum; extent_ad nextVolDescSeqExt; uint8_t reserved[484]; } __attribute__ ((packed)); /* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */ struct impUseVolDesc { tag descTag; __le32 volDescSeqNum; regid impIdent; uint8_t impUse[460]; } __attribute__ ((packed)); /* Partition Descriptor (ECMA 167r3 3/10.5) */ struct partitionDesc { tag descTag; __le32 volDescSeqNum; __le16 partitionFlags; __le16 partitionNumber; regid partitionContents; uint8_t partitionContentsUse[128]; __le32 accessType; __le32 partitionStartingLocation; __le32 partitionLength; regid impIdent; uint8_t impUse[128]; uint8_t reserved[156]; } __attribute__ ((packed)); /* Partition Flags (ECMA 167r3 3/10.5.3) */ #define PD_PARTITION_FLAGS_ALLOC 0x0001 /* Partition Contents (ECMA 167r2 3/10.5.3) */ #define PD_PARTITION_CONTENTS_NSR02 "+NSR02" /* Partition Contents (ECMA 167r3 3/10.5.5) */ #define PD_PARTITION_CONTENTS_FDC01 "+FDC01" #define PD_PARTITION_CONTENTS_CD001 "+CD001" #define PD_PARTITION_CONTENTS_CDW02 "+CDW02" #define PD_PARTITION_CONTENTS_NSR03 "+NSR03" /* Access Type (ECMA 167r3 3/10.5.7) */ #define PD_ACCESS_TYPE_NONE 0x00000000 #define PD_ACCESS_TYPE_READ_ONLY 0x00000001 #define PD_ACCESS_TYPE_WRITE_ONCE 0x00000002 #define PD_ACCESS_TYPE_REWRITABLE 0x00000003 #define PD_ACCESS_TYPE_OVERWRITABLE 0x00000004 /* Logical Volume Descriptor (ECMA 167r3 3/10.6) */ struct logicalVolDesc { tag descTag; __le32 volDescSeqNum; charspec descCharSet; dstring logicalVolIdent[128]; __le32 logicalBlockSize; regid domainIdent; uint8_t logicalVolContentsUse[16]; __le32 mapTableLength; __le32 numPartitionMaps; regid impIdent; uint8_t impUse[128]; extent_ad integritySeqExt; uint8_t partitionMaps[0]; } __attribute__ ((packed)); /* Generic Partition Map (ECMA 167r3 3/10.7.1) */ struct genericPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t partitionMapping[0]; } __attribute__ ((packed)); /* Partition Map Type (ECMA 167r3 3/10.7.1.1) */ #define GP_PARTITION_MAP_TYPE_UNDEF 0x00 #define GP_PARTIITON_MAP_TYPE_1 0x01 #define GP_PARTITION_MAP_TYPE_2 0x02 /* Type 1 Partition Map (ECMA 167r3 3/10.7.2) */ struct genericPartitionMap1 { uint8_t partitionMapType; uint8_t partitionMapLength; __le16 volSeqNum; __le16 partitionNum; } __attribute__ ((packed)); /* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */ struct genericPartitionMap2 { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t partitionIdent[62]; } __attribute__ ((packed)); /* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */ struct unallocSpaceDesc { tag descTag; __le32 volDescSeqNum; __le32 numAllocDescs; extent_ad allocDescs[0]; } __attribute__ ((packed)); /* Terminating Descriptor (ECMA 167r3 3/10.9) */ struct terminatingDesc { tag descTag; uint8_t reserved[496]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */ struct logicalVolIntegrityDesc { tag descTag; timestamp recordingDateAndTime; __le32 integrityType; extent_ad nextIntegrityExt; uint8_t logicalVolContentsUse[32]; __le32 numOfPartitions; __le32 lengthOfImpUse; __le32 freeSpaceTable[0]; __le32 sizeTable[0]; uint8_t impUse[0]; } __attribute__ ((packed)); /* Integrity Type (ECMA 167r3 3/10.10.3) */ #define LVID_INTEGRITY_TYPE_OPEN 0x00000000 #define LVID_INTEGRITY_TYPE_CLOSE 0x00000001 /* Recorded Address (ECMA 167r3 4/7.1) */ typedef struct { __le32 logicalBlockNum; __le16 partitionReferenceNum; } __attribute__ ((packed)) lb_addr; /* ... and its in-core analog */ typedef struct { uint32_t logicalBlockNum; uint16_t partitionReferenceNum; } kernel_lb_addr; /* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ typedef struct { __le32 extLength; __le32 extPosition; } __attribute__ ((packed)) short_ad; /* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ typedef struct { __le32 extLength; lb_addr extLocation; uint8_t impUse[6]; } __attribute__ ((packed)) long_ad; typedef struct { uint32_t extLength; kernel_lb_addr extLocation; uint8_t impUse[6]; } kernel_long_ad; /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ typedef struct { __le32 extLength; __le32 recordedLength; __le32 informationLength; lb_addr extLocation; } __attribute__ ((packed)) ext_ad; typedef struct { uint32_t extLength; uint32_t recordedLength; uint32_t informationLength; kernel_lb_addr extLocation; } kernel_ext_ad; /* Descriptor Tag (ECMA 167r3 4/7.2 - See 3/7.2) */ /* Tag Identifier (ECMA 167r3 4/7.2.1) */ #define TAG_IDENT_FSD 0x0100 #define TAG_IDENT_FID 0x0101 #define TAG_IDENT_AED 0x0102 #define TAG_IDENT_IE 0x0103 #define TAG_IDENT_TE 0x0104 #define TAG_IDENT_FE 0x0105 #define TAG_IDENT_EAHD 0x0106 #define TAG_IDENT_USE 0x0107 #define TAG_IDENT_SBD 0x0108 #define TAG_IDENT_PIE 0x0109 #define TAG_IDENT_EFE 0x010A /* File Set Descriptor (ECMA 167r3 4/14.1) */ struct fileSetDesc { tag descTag; timestamp recordingDateAndTime; __le16 interchangeLvl; __le16 maxInterchangeLvl; __le32 charSetList; __le32 maxCharSetList; __le32 fileSetNum; __le32 fileSetDescNum; charspec logicalVolIdentCharSet; dstring logicalVolIdent[128]; charspec fileSetCharSet; dstring fileSetIdent[32]; dstring copyrightFileIdent[32]; dstring abstractFileIdent[32]; long_ad rootDirectoryICB; regid domainIdent; long_ad nextExt; long_ad streamDirectoryICB; uint8_t reserved[32]; } __attribute__ ((packed)); /* Partition Header Descriptor (ECMA 167r3 4/14.3) */ struct partitionHeaderDesc { short_ad unallocSpaceTable; short_ad unallocSpaceBitmap; short_ad partitionIntegrityTable; short_ad freedSpaceTable; short_ad freedSpaceBitmap; uint8_t reserved[88]; } __attribute__ ((packed)); /* File Identifier Descriptor (ECMA 167r3 4/14.4) */ struct fileIdentDesc { tag descTag; __le16 fileVersionNum; uint8_t fileCharacteristics; uint8_t lengthFileIdent; long_ad icb; __le16 lengthOfImpUse; uint8_t impUse[0]; uint8_t fileIdent[0]; uint8_t padding[0]; } __attribute__ ((packed)); /* File Characteristics (ECMA 167r3 4/14.4.3) */ #define FID_FILE_CHAR_HIDDEN 0x01 #define FID_FILE_CHAR_DIRECTORY 0x02 #define FID_FILE_CHAR_DELETED 0x04 #define FID_FILE_CHAR_PARENT 0x08 #define FID_FILE_CHAR_METADATA 0x10 /* Allocation Ext Descriptor (ECMA 167r3 4/14.5) */ struct allocExtDesc { tag descTag; __le32 previousAllocExtLocation; __le32 lengthAllocDescs; } __attribute__ ((packed)); /* ICB Tag (ECMA 167r3 4/14.6) */ typedef struct { __le32 priorRecordedNumDirectEntries; __le16 strategyType; __le16 strategyParameter; __le16 numEntries; uint8_t reserved; uint8_t fileType; lb_addr parentICBLocation; __le16 flags; } __attribute__ ((packed)) icbtag; /* Strategy Type (ECMA 167r3 4/14.6.2) */ #define ICBTAG_STRATEGY_TYPE_UNDEF 0x0000 #define ICBTAG_STRATEGY_TYPE_1 0x0001 #define ICBTAG_STRATEGY_TYPE_2 0x0002 #define ICBTAG_STRATEGY_TYPE_3 0x0003 #define ICBTAG_STRATEGY_TYPE_4 0x0004 /* File Type (ECMA 167r3 4/14.6.6) */ #define ICBTAG_FILE_TYPE_UNDEF 0x00 #define ICBTAG_FILE_TYPE_USE 0x01 #define ICBTAG_FILE_TYPE_PIE 0x02 #define ICBTAG_FILE_TYPE_IE 0x03 #define ICBTAG_FILE_TYPE_DIRECTORY 0x04 #define ICBTAG_FILE_TYPE_REGULAR 0x05 #define ICBTAG_FILE_TYPE_BLOCK 0x06 #define ICBTAG_FILE_TYPE_CHAR 0x07 #define ICBTAG_FILE_TYPE_EA 0x08 #define ICBTAG_FILE_TYPE_FIFO 0x09 #define ICBTAG_FILE_TYPE_SOCKET 0x0A #define ICBTAG_FILE_TYPE_TE 0x0B #define ICBTAG_FILE_TYPE_SYMLINK 0x0C #define ICBTAG_FILE_TYPE_STREAMDIR 0x0D /* Flags (ECMA 167r3 4/14.6.8) */ #define ICBTAG_FLAG_AD_MASK 0x0007 #define ICBTAG_FLAG_AD_SHORT 0x0000 #define ICBTAG_FLAG_AD_LONG 0x0001 #define ICBTAG_FLAG_AD_EXTENDED 0x0002 #define ICBTAG_FLAG_AD_IN_ICB 0x0003 #define ICBTAG_FLAG_SORTED 0x0008 #define ICBTAG_FLAG_NONRELOCATABLE 0x0010 #define ICBTAG_FLAG_ARCHIVE 0x0020 #define ICBTAG_FLAG_SETUID 0x0040 #define ICBTAG_FLAG_SETGID 0x0080 #define ICBTAG_FLAG_STICKY 0x0100 #define ICBTAG_FLAG_CONTIGUOUS 0x0200 #define ICBTAG_FLAG_SYSTEM 0x0400 #define ICBTAG_FLAG_TRANSFORMED 0x0800 #define ICBTAG_FLAG_MULTIVERSIONS 0x1000 #define ICBTAG_FLAG_STREAM 0x2000 /* Indirect Entry (ECMA 167r3 4/14.7) */ struct indirectEntry { tag descTag; icbtag icbTag; long_ad indirectICB; } __attribute__ ((packed)); /* Terminal Entry (ECMA 167r3 4/14.8) */ struct terminalEntry { tag descTag; icbtag icbTag; } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.9) */ struct fileEntry { tag descTag; icbtag icbTag; __le32 uid; __le32 gid; __le32 permissions; __le16 fileLinkCount; uint8_t recordFormat; uint8_t recordDisplayAttr; __le32 recordLength; __le64 informationLength; __le64 logicalBlocksRecorded; timestamp accessTime; timestamp modificationTime; timestamp attrTime; __le32 checkpoint; long_ad extendedAttrICB; regid impIdent; __le64 uniqueID; __le32 lengthExtendedAttr; __le32 lengthAllocDescs; uint8_t extendedAttr[0]; uint8_t allocDescs[0]; } __attribute__ ((packed)); /* Permissions (ECMA 167r3 4/14.9.5) */ #define FE_PERM_O_EXEC 0x00000001U #define FE_PERM_O_WRITE 0x00000002U #define FE_PERM_O_READ 0x00000004U #define FE_PERM_O_CHATTR 0x00000008U #define FE_PERM_O_DELETE 0x00000010U #define FE_PERM_G_EXEC 0x00000020U #define FE_PERM_G_WRITE 0x00000040U #define FE_PERM_G_READ 0x00000080U #define FE_PERM_G_CHATTR 0x00000100U #define FE_PERM_G_DELETE 0x00000200U #define FE_PERM_U_EXEC 0x00000400U #define FE_PERM_U_WRITE 0x00000800U #define FE_PERM_U_READ 0x00001000U #define FE_PERM_U_CHATTR 0x00002000U #define FE_PERM_U_DELETE 0x00004000U /* Record Format (ECMA 167r3 4/14.9.7) */ #define FE_RECORD_FMT_UNDEF 0x00 #define FE_RECORD_FMT_FIXED_PAD 0x01 #define FE_RECORD_FMT_FIXED 0x02 #define FE_RECORD_FMT_VARIABLE8 0x03 #define FE_RECORD_FMT_VARIABLE16 0x04 #define FE_RECORD_FMT_VARIABLE16_MSB 0x05 #define FE_RECORD_FMT_VARIABLE32 0x06 #define FE_RECORD_FMT_PRINT 0x07 #define FE_RECORD_FMT_LF 0x08 #define FE_RECORD_FMT_CR 0x09 #define FE_RECORD_FMT_CRLF 0x0A #define FE_RECORD_FMT_LFCR 0x0B /* Record Display Attributes (ECMA 167r3 4/14.9.8) */ #define FE_RECORD_DISPLAY_ATTR_UNDEF 0x00 #define FE_RECORD_DISPLAY_ATTR_1 0x01 #define FE_RECORD_DISPLAY_ATTR_2 0x02 #define FE_RECORD_DISPLAY_ATTR_3 0x03 /* Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */ struct extendedAttrHeaderDesc { tag descTag; __le32 impAttrLocation; __le32 appAttrLocation; } __attribute__ ((packed)); /* Generic Format (ECMA 167r3 4/14.10.2) */ struct genericFormat { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; uint8_t attrData[0]; } __attribute__ ((packed)); /* Character Set Information (ECMA 167r3 4/14.10.3) */ struct charSetInfo { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 escapeSeqLength; uint8_t charSetType; uint8_t escapeSeq[0]; } __attribute__ ((packed)); /* Alternate Permissions (ECMA 167r3 4/14.10.4) */ struct altPerms { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le16 ownerIdent; __le16 groupIdent; __le16 permission; } __attribute__ ((packed)); /* File Times Extended Attribute (ECMA 167r3 4/14.10.5) */ struct fileTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 dataLength; __le32 fileTimeExistence; uint8_t fileTimes; } __attribute__ ((packed)); /* FileTimeExistence (ECMA 167r3 4/14.10.5.6) */ #define FTE_CREATION 0x00000001 #define FTE_DELETION 0x00000004 #define FTE_EFFECTIVE 0x00000008 #define FTE_BACKUP 0x00000002 /* Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */ struct infoTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 dataLength; __le32 infoTimeExistence; uint8_t infoTimes[0]; } __attribute__ ((packed)); /* Device Specification (ECMA 167r3 4/14.10.7) */ struct deviceSpec { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 impUseLength; __le32 majorDeviceIdent; __le32 minorDeviceIdent; uint8_t impUse[0]; } __attribute__ ((packed)); /* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */ struct impUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 impUseLength; regid impIdent; uint8_t impUse[0]; } __attribute__ ((packed)); /* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */ struct appUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 appUseLength; regid appIdent; uint8_t appUse[0]; } __attribute__ ((packed)); #define EXTATTR_CHAR_SET 1 #define EXTATTR_ALT_PERMS 3 #define EXTATTR_FILE_TIMES 5 #define EXTATTR_INFO_TIMES 6 #define EXTATTR_DEV_SPEC 12 #define EXTATTR_IMP_USE 2048 #define EXTATTR_APP_USE 65536 /* Unallocated Space Entry (ECMA 167r3 4/14.11) */ struct unallocSpaceEntry { tag descTag; icbtag icbTag; __le32 lengthAllocDescs; uint8_t allocDescs[0]; } __attribute__ ((packed)); /* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */ struct spaceBitmapDesc { tag descTag; __le32 numOfBits; __le32 numOfBytes; uint8_t bitmap[0]; } __attribute__ ((packed)); /* Partition Integrity Entry (ECMA 167r3 4/14.13) */ struct partitionIntegrityEntry { tag descTag; icbtag icbTag; timestamp recordingDateAndTime; uint8_t integrityType; uint8_t reserved[175]; regid impIdent; uint8_t impUse[256]; } __attribute__ ((packed)); /* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ /* Extent Length (ECMA 167r3 4/14.14.1.1) */ #define EXT_RECORDED_ALLOCATED 0x00000000 #define EXT_NOT_RECORDED_ALLOCATED 0x40000000 #define EXT_NOT_RECORDED_NOT_ALLOCATED 0x80000000 #define EXT_NEXT_EXTENT_ALLOCDECS 0xC0000000 /* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ /* Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */ struct logicalVolHeaderDesc { __le64 uniqueID; uint8_t reserved[24]; } __attribute__ ((packed)); /* Path Component (ECMA 167r3 4/14.16.1) */ struct pathComponent { uint8_t componentType; uint8_t lengthComponentIdent; __le16 componentFileVersionNum; dstring componentIdent[0]; } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.17) */ struct extendedFileEntry { tag descTag; icbtag icbTag; __le32 uid; __le32 gid; __le32 permissions; __le16 fileLinkCount; uint8_t recordFormat; uint8_t recordDisplayAttr; __le32 recordLength; __le64 informationLength; __le64 objectSize; __le64 logicalBlocksRecorded; timestamp accessTime; timestamp modificationTime; timestamp createTime; timestamp attrTime; __le32 checkpoint; __le32 reserved; long_ad extendedAttrICB; long_ad streamDirectoryICB; regid impIdent; __le64 uniqueID; __le32 lengthExtendedAttr; __le32 lengthAllocDescs; uint8_t extendedAttr[0]; uint8_t allocDescs[0]; } __attribute__ ((packed)); #endif /* _ECMA_167_H */ linux-2.6.17/fs/udf/.svn/text-base/udftime.c.svn-base0000444000000000000000000001312410574207644020740 0ustar rootroot/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Paul Eggert (eggert@twinsun.com). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * dgb 10/02/98: ripped this from glibc source to help convert timestamps to unix time * 10/04/98: added new table-based lookup after seeing how ugly the gnu code is * blf 09/27/99: ripped out all the old code and inserted new table from * John Brockmeyer (without leap second corrections) * rewrote udf_stamp_to_time and fixed timezone accounting in udf_time_to_stamp. */ /* * We don't take into account leap seconds. This may be correct or incorrect. * For more NIST information (especially dealing with leap seconds), see: * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm */ #include #include #include "udfdecl.h" #define EPOCH_YEAR 1970 #ifndef __isleap /* Nonzero if YEAR is a leap year (every 4 years, except every 100th isn't, and every 400th is). */ #define __isleap(year) \ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) #endif /* How many days come before each month (0-12). */ static const unsigned short int __mon_yday[2][13] = { /* Normal years. */ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, /* Leap years. */ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; #define MAX_YEAR_SECONDS 69 #define SPD 0x15180 /*3600*24*/ #define SPY(y,l,s) (SPD * (365*y+l)+s) static time_t year_seconds[MAX_YEAR_SECONDS]= { /*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), /*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), /*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), /*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), /*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), /*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), /*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), /*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), /*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), /*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), /*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), /*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), /*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), /*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), /*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), /*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), /*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), /*2038*/ SPY(68,17,0) }; extern struct timezone sys_tz; #define SECS_PER_HOUR (60 * 60) #define SECS_PER_DAY (SECS_PER_HOUR * 24) time_t * udf_stamp_to_time(time_t *dest, long *dest_usec, kernel_timestamp src) { int yday; uint8_t type = src.typeAndTimezone >> 12; int16_t offset; if (type == 1) { offset = src.typeAndTimezone << 4; /* sign extent offset */ offset = (offset >> 4); if (offset == -2047) /* unspecified offset */ offset = 0; } else offset = 0; if ((src.year < EPOCH_YEAR) || (src.year >= EPOCH_YEAR+MAX_YEAR_SECONDS)) { *dest = -1; *dest_usec = -1; return NULL; } *dest = year_seconds[src.year - EPOCH_YEAR]; *dest -= offset * 60; yday = ((__mon_yday[__isleap (src.year)] [src.month-1]) + (src.day-1)); *dest += ( ( (yday* 24) + src.hour ) * 60 + src.minute ) * 60 + src.second; *dest_usec = src.centiseconds * 10000 + src.hundredsOfMicroseconds * 100 + src.microseconds; return dest; } kernel_timestamp * udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) { long int days, rem, y; const unsigned short int *ip; int16_t offset; offset = -sys_tz.tz_minuteswest; if (!dest) return NULL; dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF); ts.tv_sec += offset * 60; days = ts.tv_sec / SECS_PER_DAY; rem = ts.tv_sec % SECS_PER_DAY; dest->hour = rem / SECS_PER_HOUR; rem %= SECS_PER_HOUR; dest->minute = rem / 60; dest->second = rem % 60; y = 1970; #define DIV(a,b) ((a) / (b) - ((a) % (b) < 0)) #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) while (days < 0 || days >= (__isleap(y) ? 366 : 365)) { long int yg = y + days / 365 - (days % 365 < 0); /* Adjust DAYS and Y to match the guessed year. */ days -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) - LEAPS_THRU_END_OF (y - 1)); y = yg; } dest->year = y; ip = __mon_yday[__isleap(y)]; for (y = 11; days < (long int) ip[y]; --y) continue; days -= ip[y]; dest->month = y + 1; dest->day = days + 1; dest->centiseconds = ts.tv_nsec / 10000000; dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000) / 100; dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 - dest->hundredsOfMicroseconds * 100); return dest; } /* EOF */ linux-2.6.17/fs/udf/.svn/text-base/dir.c.svn-base0000444000000000000000000001431410574207644020063 0ustar rootroot/* * dir.c * * PURPOSE * Directory handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2004 Ben Fennema * * HISTORY * * 10/05/98 dgb Split directory operations into its own file * Implemented directory reads via do_udf_readdir * 10/06/98 Made directory operations work! * 11/17/98 Rewrote directory to support ICBTAG_FLAG_AD_LONG * 11/25/98 blf Rewrote directory handling (readdir+lookup) to support reading * across blocks. * 12/12/98 Split out the lookup code to namei.c. bulk of directory * code now in directory.c:udf_fileident_read. */ #include "udfdecl.h" #include #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" /* Prototypes for file operations */ static int udf_readdir(struct file *, void *, filldir_t); static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *); /* readdir and lookup functions */ const struct file_operations udf_dir_operations = { .read = generic_read_dir, .readdir = udf_readdir, .ioctl = udf_ioctl, .fsync = udf_fsync_file, }; /* * udf_readdir * * PURPOSE * Read a directory entry. * * DESCRIPTION * Optional - sys_getdents() will return -ENOTDIR if this routine is not * available. * * Refer to sys_getdents() in fs/readdir.c * sys_getdents() -> . * * PRE-CONDITIONS * filp Pointer to directory file. * buf Pointer to directory entry buffer. * filldir Pointer to filldir function. * * POST-CONDITIONS * >=0 on success. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *dir = filp->f_dentry->d_inode; int result; lock_kernel(); if ( filp->f_pos == 0 ) { if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0) { unlock_kernel(); return 0; } filp->f_pos ++; } result = do_udf_readdir(dir, filp, filldir, dirent); unlock_kernel(); return result; } static int do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent) { struct udf_fileident_bh fibh; struct fileIdentDesc *fi=NULL; struct fileIdentDesc cfi; int block, iblock; loff_t nf_pos = filp->f_pos - 1; int flen; char fname[UDF_NAME_LEN]; char *nameptr; uint16_t liu; uint8_t lfi; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; struct buffer_head * bh = NULL, * tmp, * bha[16]; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; int i, num; unsigned int dt_type; if (nf_pos >= size) return 0; if (nf_pos == 0) nf_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh.sbh = fibh.ebh = NULL; else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); return -EIO; } if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) { i = 16 >> (dir->i_sb->s_blocksize_bits - 9); if (i+offset > (elen >> dir->i_sb->s_blocksize_bits)) i = (elen >> dir->i_sb->s_blocksize_bits)-offset; for (num=0; i>0; i--) { block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i); tmp = udf_tgetblk(dir->i_sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } if (num) { ll_rw_block(READA, num, bha); for (i=0; if_pos = nf_pos + 1; fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } liu = le16_to_cpu(cfi.lengthOfImpUse); lfi = cfi.lengthFileIdent; if (fibh.sbh == fibh.ebh) nameptr = fi->fileIdent + liu; else { int poffset; /* Unpaded ending offset */ poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi; if (poffset >= lfi) nameptr = (char *)(fibh.ebh->b_data + poffset - lfi); else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset); } } if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) continue; } if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) continue; } if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT ) { iblock = parent_ino(filp->f_dentry); flen = 2; memcpy(fname, "..", flen); dt_type = DT_DIR; } else { kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation); iblock = udf_get_lb_pblock(dir->i_sb, tloc, 0); flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); dt_type = DT_UNKNOWN; } if (flen) { if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } } } /* end while */ filp->f_pos = nf_pos + 1; if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } linux-2.6.17/fs/udf/.svn/text-base/lowlevel.c.svn-base0000444000000000000000000000305310574207644021134 0ustar rootroot/* * lowlevel.c * * PURPOSE * Low Level Device Routines for the UDF filesystem * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2001 Ben Fennema * * HISTORY * * 03/26/99 blf Created. */ #include "udfdecl.h" #include #include #include #include #include "udf_sb.h" unsigned int udf_get_last_session(struct super_block *sb) { struct cdrom_multisession ms_info; unsigned int vol_desc_start; struct block_device *bdev = sb->s_bdev; int i; vol_desc_start=0; ms_info.addr_format=CDROM_LBA; i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); #define WE_OBEY_THE_WRITTEN_STANDARDS 1 if (i == 0) { udf_debug("XA disk: %s, vol_desc_start=%d\n", (ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba); #if WE_OBEY_THE_WRITTEN_STANDARDS if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ #endif vol_desc_start = ms_info.addr.lba; } else { udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i); } return vol_desc_start; } unsigned long udf_get_last_block(struct super_block *sb) { struct block_device *bdev = sb->s_bdev; unsigned long lblock = 0; if (ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock)) lblock = bdev->bd_inode->i_size >> sb->s_blocksize_bits; if (lblock) return lblock - 1; else return 0; } linux-2.6.17/fs/udf/.svn/text-base/udfdecl.h.svn-base0000444000000000000000000001500110574207644020712 0ustar rootroot#ifndef __UDF_DECL_H #define __UDF_DECL_H #include #include "ecma_167.h" #include "osta_udf.h" #include #include #include #include #include #include #include "udfend.h" #define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) ) #define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) ) #define UDF_EXTENT_LENGTH_MASK 0x3FFFFFFF #define UDF_EXTENT_FLAG_MASK 0xC0000000 #define UDF_NAME_PAD 4 #define UDF_NAME_LEN 256 #define UDF_PATH_LEN 1023 #define udf_file_entry_alloc_offset(inode)\ (UDF_I_USE(inode) ?\ sizeof(struct unallocSpaceEntry) :\ ((UDF_I_EFE(inode) ?\ sizeof(struct extendedFileEntry) :\ sizeof(struct fileEntry)) + UDF_I_LENEATTR(inode))) #define udf_ext0_offset(inode)\ (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ?\ udf_file_entry_alloc_offset(inode) : 0) #define udf_get_lb_pblock(sb,loc,offset) udf_get_pblock((sb), (loc).logicalBlockNum, (loc).partitionReferenceNum, (offset)) struct dentry; struct inode; struct task_struct; struct buffer_head; struct super_block; extern struct inode_operations udf_dir_inode_operations; extern const struct file_operations udf_dir_operations; extern struct inode_operations udf_file_inode_operations; extern const struct file_operations udf_file_operations; extern struct address_space_operations udf_aops; extern struct address_space_operations udf_adinicb_aops; extern struct address_space_operations udf_symlink_aops; struct udf_fileident_bh { struct buffer_head *sbh; struct buffer_head *ebh; int soffset; int eoffset; }; struct udf_vds_record { uint32_t block; uint32_t volDescSeqNum; }; struct generic_desc { tag descTag; __le32 volDescSeqNum; }; struct ustr { uint8_t u_cmpID; uint8_t u_name[UDF_NAME_LEN-2]; uint8_t u_len; }; /* super.c */ extern void udf_error(struct super_block *, const char *, const char *, ...); extern void udf_warning(struct super_block *, const char *, const char *, ...); /* namei.c */ extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, struct fileIdentDesc *, struct udf_fileident_bh *, uint8_t *, uint8_t *); /* file.c */ extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* inode.c */ extern struct inode *udf_iget(struct super_block *, kernel_lb_addr); extern int udf_sync_inode(struct inode *); extern void udf_expand_file_adinicb(struct inode *, int, int *); extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *); extern struct buffer_head * udf_bread(struct inode *, int, int, int *); extern void udf_truncate(struct inode *); extern void udf_read_inode(struct inode *); extern void udf_delete_inode(struct inode *); extern void udf_clear_inode(struct inode *); extern int udf_write_inode(struct inode *, int); extern long udf_block_map(struct inode *, long); extern int8_t inode_bmap(struct inode *, int, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **); extern int8_t udf_add_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr, uint32_t, struct buffer_head **, int); extern int8_t udf_write_aext(struct inode *, kernel_lb_addr, int *, kernel_lb_addr, uint32_t, struct buffer_head *, int); extern int8_t udf_delete_aext(struct inode *, kernel_lb_addr, int, kernel_lb_addr, uint32_t, struct buffer_head *); extern int8_t udf_next_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int); extern int8_t udf_current_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int); /* misc.c */ extern struct buffer_head *udf_tgetblk(struct super_block *, int); extern struct buffer_head *udf_tread(struct super_block *, int); extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t); extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t); extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, uint32_t, uint16_t *); extern struct buffer_head *udf_read_ptagged(struct super_block *, kernel_lb_addr, uint32_t, uint16_t *); extern void udf_release_data(struct buffer_head *); extern void udf_update_tag(char *, int); extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int); /* lowlevel.c */ extern unsigned int udf_get_last_session(struct super_block *); extern unsigned long udf_get_last_block(struct super_block *); /* partition.c */ extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, uint32_t); extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, uint32_t); extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, uint32_t); extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, uint32_t); extern int udf_relocate_blocks(struct super_block *, long, long *); /* unicode.c */ extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int); extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, int); extern int udf_build_ustr(struct ustr *, dstring *, int); extern int udf_CS0toUTF8(struct ustr *, struct ustr *); /* ialloc.c */ extern void udf_free_inode(struct inode *); extern struct inode * udf_new_inode (struct inode *, int, int *); /* truncate.c */ extern void udf_discard_prealloc(struct inode *); extern void udf_truncate_extents(struct inode *); /* balloc.c */ extern void udf_free_blocks(struct super_block *, struct inode *, kernel_lb_addr, uint32_t, uint32_t); extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, uint32_t, uint32_t); extern int udf_new_block(struct super_block *, struct inode *, uint16_t, uint32_t, int *); /* fsync.c */ extern int udf_fsync_file(struct file *, struct dentry *, int); /* directory.c */ extern struct fileIdentDesc * udf_fileident_read(struct inode *, loff_t *, struct udf_fileident_bh *, struct fileIdentDesc *, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **); extern struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset); extern long_ad * udf_get_filelongad(uint8_t *, int, int *, int); extern short_ad * udf_get_fileshortad(uint8_t *, int, int *, int); /* crc.c */ extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t); /* udftime.c */ extern time_t *udf_stamp_to_time(time_t *, long *, kernel_timestamp); extern kernel_timestamp *udf_time_to_stamp(kernel_timestamp *, struct timespec); #endif /* __UDF_DECL_H */ linux-2.6.17/fs/udf/.svn/text-base/udf_sb.h.svn-base0000444000000000000000000001240310574207644020551 0ustar rootroot#ifndef __LINUX_UDF_SB_H #define __LINUX_UDF_SB_H /* Since UDF 2.01 is ISO 13346 based... */ #define UDF_SUPER_MAGIC 0x15013346 #define UDF_MAX_READ_VERSION 0x0201 #define UDF_MAX_WRITE_VERSION 0x0201 #define UDF_FLAG_USE_EXTENDED_FE 0 #define UDF_VERS_USE_EXTENDED_FE 0x0200 #define UDF_FLAG_USE_STREAMS 1 #define UDF_VERS_USE_STREAMS 0x0200 #define UDF_FLAG_USE_SHORT_AD 2 #define UDF_FLAG_USE_AD_IN_ICB 3 #define UDF_FLAG_USE_FILE_CTIME_EA 4 #define UDF_FLAG_STRICT 5 #define UDF_FLAG_UNDELETE 6 #define UDF_FLAG_UNHIDE 7 #define UDF_FLAG_VARCONV 8 #define UDF_FLAG_NLS_MAP 9 #define UDF_FLAG_UTF8 10 #define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */ #define UDF_FLAG_UID_IGNORE 12 /* use sb uid instead of on disk uid */ #define UDF_FLAG_GID_FORGET 13 #define UDF_FLAG_GID_IGNORE 14 #define UDF_PART_FLAG_UNALLOC_BITMAP 0x0001 #define UDF_PART_FLAG_UNALLOC_TABLE 0x0002 #define UDF_PART_FLAG_FREED_BITMAP 0x0004 #define UDF_PART_FLAG_FREED_TABLE 0x0008 #define UDF_PART_FLAG_READ_ONLY 0x0010 #define UDF_PART_FLAG_WRITE_ONCE 0x0020 #define UDF_PART_FLAG_REWRITABLE 0x0040 #define UDF_PART_FLAG_OVERWRITABLE 0x0080 static inline struct udf_sb_info *UDF_SB(struct super_block *sb) { return sb->s_fs_info; } #define UDF_SB_FREE(X)\ {\ if (UDF_SB(X))\ {\ kfree(UDF_SB_PARTMAPS(X));\ UDF_SB_PARTMAPS(X) = NULL;\ }\ } #define UDF_SB_ALLOC_PARTMAPS(X,Y)\ {\ UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\ if (UDF_SB_PARTMAPS(X) != NULL)\ {\ UDF_SB_NUMPARTS(X) = Y;\ memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * Y);\ }\ else\ {\ UDF_SB_NUMPARTS(X) = 0;\ udf_error(X, __FUNCTION__, "Unable to allocate space for %d partition maps", Y);\ }\ } #define UDF_SB_ALLOC_BITMAP(X,Y,Z)\ {\ int nr_groups = ((UDF_SB_PARTLEN((X),(Y)) + (sizeof(struct spaceBitmapDesc) << 3) +\ ((X)->s_blocksize * 8) - 1) / ((X)->s_blocksize * 8));\ int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ if (size <= PAGE_SIZE)\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(size, GFP_KERNEL);\ else\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = vmalloc(size);\ if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL)\ {\ memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00, size);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap =\ (struct buffer_head **)(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap + 1);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups = nr_groups;\ }\ else\ {\ udf_error(X, __FUNCTION__, "Unable to allocate space for bitmap and %d buffer_head pointers", nr_groups);\ }\ } #define UDF_SB_FREE_BITMAP(X,Y,Z)\ {\ int i;\ int nr_groups = UDF_SB_BITMAP_NR_GROUPS(X,Y,Z);\ int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ for (i=0; is_flags & ( 1 << (Y) ) ) #define UDF_SET_FLAG(X,Y) ( UDF_SB(X)->s_flags |= ( 1 << (Y) ) ) #define UDF_CLEAR_FLAG(X,Y) ( UDF_SB(X)->s_flags &= ~( 1 << (Y) ) ) #define UDF_UPDATE_UDFREV(X,Y) ( ((Y) > UDF_SB_UDFREV(X)) ? UDF_SB_UDFREV(X) = (Y) : UDF_SB_UDFREV(X) ) #define UDF_SB_PARTMAPS(X) ( UDF_SB(X)->s_partmaps ) #define UDF_SB_PARTTYPE(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_type ) #define UDF_SB_PARTROOT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_root ) #define UDF_SB_PARTLEN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_len ) #define UDF_SB_PARTVSN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_volumeseqnum ) #define UDF_SB_PARTNUM(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_num ) #define UDF_SB_TYPESPAR(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_sparing ) #define UDF_SB_TYPEVIRT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_virtual ) #define UDF_SB_PARTFUNC(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func ) #define UDF_SB_PARTFLAGS(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags ) #define UDF_SB_BITMAP(X,Y,Z,I) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap[I] ) #define UDF_SB_BITMAP_NR_GROUPS(X,Y,Z) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups ) #define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident ) #define UDF_SB_NUMPARTS(X) ( UDF_SB(X)->s_partitions ) #define UDF_SB_PARTITION(X) ( UDF_SB(X)->s_partition ) #define UDF_SB_SESSION(X) ( UDF_SB(X)->s_session ) #define UDF_SB_ANCHOR(X) ( UDF_SB(X)->s_anchor ) #define UDF_SB_LASTBLOCK(X) ( UDF_SB(X)->s_lastblock ) #define UDF_SB_LVIDBH(X) ( UDF_SB(X)->s_lvidbh ) #define UDF_SB_LVID(X) ( (struct logicalVolIntegrityDesc *)UDF_SB_LVIDBH(X)->b_data ) #define UDF_SB_LVIDIU(X) ( (struct logicalVolIntegrityDescImpUse *)&(UDF_SB_LVID(X)->impUse[le32_to_cpu(UDF_SB_LVID(X)->numOfPartitions) * 2 * sizeof(uint32_t)/sizeof(uint8_t)]) ) #define UDF_SB_UMASK(X) ( UDF_SB(X)->s_umask ) #define UDF_SB_GID(X) ( UDF_SB(X)->s_gid ) #define UDF_SB_UID(X) ( UDF_SB(X)->s_uid ) #define UDF_SB_RECORDTIME(X) ( UDF_SB(X)->s_recordtime ) #define UDF_SB_SERIALNUM(X) ( UDF_SB(X)->s_serialnum ) #define UDF_SB_UDFREV(X) ( UDF_SB(X)->s_udfrev ) #define UDF_SB_FLAGS(X) ( UDF_SB(X)->s_flags ) #define UDF_SB_VAT(X) ( UDF_SB(X)->s_vat ) #endif /* __LINUX_UDF_SB_H */ linux-2.6.17/fs/udf/.svn/text-base/ialloc.c.svn-base0000444000000000000000000001045310574207644020550 0ustar rootroot/* * ialloc.c * * PURPOSE * Inode allocation handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema * * HISTORY * * 02/24/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" void udf_free_inode(struct inode * inode) { struct super_block *sb = inode->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); /* * Note: we must free any quota before locking the superblock, * as writing the quota to disk may need the lock as well. */ DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); clear_inode(inode); mutex_lock(&sbi->s_alloc_mutex); if (sbi->s_lvidbh) { if (S_ISDIR(inode->i_mode)) UDF_SB_LVIDIU(sb)->numDirs = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) - 1); else UDF_SB_LVIDIU(sb)->numFiles = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1); mark_buffer_dirty(sbi->s_lvidbh); } mutex_unlock(&sbi->s_alloc_mutex); udf_free_blocks(sb, NULL, UDF_I_LOCATION(inode), 0, 1); } struct inode * udf_new_inode (struct inode *dir, int mode, int * err) { struct super_block *sb = dir->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); struct inode * inode; int block; uint32_t start = UDF_I_LOCATION(dir).logicalBlockNum; inode = new_inode(sb); if (!inode) { *err = -ENOMEM; return NULL; } *err = -ENOSPC; block = udf_new_block(dir->i_sb, NULL, UDF_I_LOCATION(dir).partitionReferenceNum, start, err); if (*err) { iput(inode); return NULL; } mutex_lock(&sbi->s_alloc_mutex); UDF_I_UNIQUE(inode) = 0; UDF_I_LENEXTENTS(inode) = 0; UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; UDF_I_NEXT_ALLOC_GOAL(inode) = 0; UDF_I_STRAT4096(inode) = 0; if (UDF_SB_LVIDBH(sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(sb)->logicalVolContentsUse); if (S_ISDIR(mode)) UDF_SB_LVIDIU(sb)->numDirs = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) + 1); else UDF_SB_LVIDIU(sb)->numFiles = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + 1); UDF_I_UNIQUE(inode) = uniqueID = le64_to_cpu(lvhd->uniqueID); if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } inode->i_mode = mode; inode->i_uid = current->fsuid; if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; } else inode->i_gid = current->fsgid; UDF_I_LOCATION(inode).logicalBlockNum = block; UDF_I_LOCATION(inode).partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum; inode->i_ino = udf_get_lb_pblock(sb, UDF_I_LOCATION(inode), 0); inode->i_blksize = PAGE_SIZE; inode->i_blocks = 0; UDF_I_LENEATTR(inode) = 0; UDF_I_LENALLOC(inode) = 0; UDF_I_USE(inode) = 0; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) { UDF_I_EFE(inode) = 1; UDF_UPDATE_UDFREV(inode->i_sb, UDF_VERS_USE_EXTENDED_FE); UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); } else { UDF_I_EFE(inode) = 0; UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct fileEntry)); } if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; inode->i_mtime = inode->i_atime = inode->i_ctime = UDF_I_CRTIME(inode) = current_fs_time(inode->i_sb); insert_inode_hash(inode); mark_inode_dirty(inode); mutex_unlock(&sbi->s_alloc_mutex); if (DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; iput(inode); *err = -EDQUOT; return NULL; } *err = 0; return inode; } linux-2.6.17/fs/udf/.svn/text-base/crc.c.svn-base0000444000000000000000000001230010574207644020045 0ustar rootroot/* * crc.c * * PURPOSE * Routines to generate, calculate, and test a 16-bit CRC. * * DESCRIPTION * The CRC code was devised by Don P. Mitchell of AT&T Bell Laboratories * and Ned W. Rhodes of Software Systems Group. It has been published in * "Design and Validation of Computer Protocols", Prentice Hall, * Englewood Cliffs, NJ, 1991, Chapter 3, ISBN 0-13-539925-4. * * Copyright is held by AT&T. * * AT&T gives permission for the free use of the CRC source code. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. */ #include "udfdecl.h" static uint16_t crc_table[256] = { 0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50a5U, 0x60c6U, 0x70e7U, 0x8108U, 0x9129U, 0xa14aU, 0xb16bU, 0xc18cU, 0xd1adU, 0xe1ceU, 0xf1efU, 0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52b5U, 0x4294U, 0x72f7U, 0x62d6U, 0x9339U, 0x8318U, 0xb37bU, 0xa35aU, 0xd3bdU, 0xc39cU, 0xf3ffU, 0xe3deU, 0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64e6U, 0x74c7U, 0x44a4U, 0x5485U, 0xa56aU, 0xb54bU, 0x8528U, 0x9509U, 0xe5eeU, 0xf5cfU, 0xc5acU, 0xd58dU, 0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76d7U, 0x66f6U, 0x5695U, 0x46b4U, 0xb75bU, 0xa77aU, 0x9719U, 0x8738U, 0xf7dfU, 0xe7feU, 0xd79dU, 0xc7bcU, 0x48c4U, 0x58e5U, 0x6886U, 0x78a7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U, 0xc9ccU, 0xd9edU, 0xe98eU, 0xf9afU, 0x8948U, 0x9969U, 0xa90aU, 0xb92bU, 0x5af5U, 0x4ad4U, 0x7ab7U, 0x6a96U, 0x1a71U, 0x0a50U, 0x3a33U, 0x2a12U, 0xdbfdU, 0xcbdcU, 0xfbbfU, 0xeb9eU, 0x9b79U, 0x8b58U, 0xbb3bU, 0xab1aU, 0x6ca6U, 0x7c87U, 0x4ce4U, 0x5cc5U, 0x2c22U, 0x3c03U, 0x0c60U, 0x1c41U, 0xedaeU, 0xfd8fU, 0xcdecU, 0xddcdU, 0xad2aU, 0xbd0bU, 0x8d68U, 0x9d49U, 0x7e97U, 0x6eb6U, 0x5ed5U, 0x4ef4U, 0x3e13U, 0x2e32U, 0x1e51U, 0x0e70U, 0xff9fU, 0xefbeU, 0xdfddU, 0xcffcU, 0xbf1bU, 0xaf3aU, 0x9f59U, 0x8f78U, 0x9188U, 0x81a9U, 0xb1caU, 0xa1ebU, 0xd10cU, 0xc12dU, 0xf14eU, 0xe16fU, 0x1080U, 0x00a1U, 0x30c2U, 0x20e3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U, 0x83b9U, 0x9398U, 0xa3fbU, 0xb3daU, 0xc33dU, 0xd31cU, 0xe37fU, 0xf35eU, 0x02b1U, 0x1290U, 0x22f3U, 0x32d2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U, 0xb5eaU, 0xa5cbU, 0x95a8U, 0x8589U, 0xf56eU, 0xe54fU, 0xd52cU, 0xc50dU, 0x34e2U, 0x24c3U, 0x14a0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U, 0xa7dbU, 0xb7faU, 0x8799U, 0x97b8U, 0xe75fU, 0xf77eU, 0xc71dU, 0xd73cU, 0x26d3U, 0x36f2U, 0x0691U, 0x16b0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U, 0xd94cU, 0xc96dU, 0xf90eU, 0xe92fU, 0x99c8U, 0x89e9U, 0xb98aU, 0xa9abU, 0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18c0U, 0x08e1U, 0x3882U, 0x28a3U, 0xcb7dU, 0xdb5cU, 0xeb3fU, 0xfb1eU, 0x8bf9U, 0x9bd8U, 0xabbbU, 0xbb9aU, 0x4a75U, 0x5a54U, 0x6a37U, 0x7a16U, 0x0af1U, 0x1ad0U, 0x2ab3U, 0x3a92U, 0xfd2eU, 0xed0fU, 0xdd6cU, 0xcd4dU, 0xbdaaU, 0xad8bU, 0x9de8U, 0x8dc9U, 0x7c26U, 0x6c07U, 0x5c64U, 0x4c45U, 0x3ca2U, 0x2c83U, 0x1ce0U, 0x0cc1U, 0xef1fU, 0xff3eU, 0xcf5dU, 0xdf7cU, 0xaf9bU, 0xbfbaU, 0x8fd9U, 0x9ff8U, 0x6e17U, 0x7e36U, 0x4e55U, 0x5e74U, 0x2e93U, 0x3eb2U, 0x0ed1U, 0x1ef0U }; /* * udf_crc * * PURPOSE * Calculate a 16-bit CRC checksum using ITU-T V.41 polynomial. * * DESCRIPTION * The OSTA-UDF(tm) 1.50 standard states that using CRCs is mandatory. * The polynomial used is: x^16 + x^12 + x^15 + 1 * * PRE-CONDITIONS * data Pointer to the data block. * size Size of the data block. * * POST-CONDITIONS * CRC of the data block. * * HISTORY * July 21, 1997 - Andrew E. Mileski * Adapted from OSTA-UDF(tm) 1.50 standard. */ uint16_t udf_crc(uint8_t *data, uint32_t size, uint16_t crc) { while (size--) crc = crc_table[(crc >> 8 ^ *(data++)) & 0xffU] ^ (crc << 8); return crc; } /****************************************************************************/ #if defined(TEST) /* * PURPOSE * Test udf_crc() * * HISTORY * July 21, 1997 - Andrew E. Mileski * Adapted from OSTA-UDF(tm) 1.50 standard. */ unsigned char bytes[] = { 0x70U, 0x6AU, 0x77U }; int main(void) { unsigned short x; x = udf_crc16(bytes, sizeof bytes); printf("udf_crc16: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U); return 0; } #endif /* defined(TEST) */ /****************************************************************************/ #if defined(GENERATE) /* * PURPOSE * Generate a table for fast 16-bit CRC calculations (any polynomial). * * DESCRIPTION * The ITU-T V.41 polynomial is 010041. * * HISTORY * July 21, 1997 - Andrew E. Mileski * Adapted from OSTA-UDF(tm) 1.50 standard. */ #include int main(int argc, char **argv) { unsigned long crc, poly; int n, i; /* Get the polynomial */ sscanf(argv[1], "%lo", &poly); if (poly & 0xffff0000U){ fprintf(stderr, "polynomial is too large\en"); exit(1); } printf("/* CRC 0%o */\n", poly); /* Create a table */ printf("static unsigned short crc_table[256] = {\n"); for (n = 0; n < 256; n++){ if (n % 8 == 0) printf("\t"); crc = n << 8; for (i = 0; i < 8; i++){ if(crc & 0x8000U) crc = (crc << 1) ^ poly; else crc <<= 1; crc &= 0xFFFFU; } if (n == 255) printf("0x%04xU ", crc); else printf("0x%04xU, ", crc); if(n % 8 == 7) printf("\n"); } printf("};\n"); return 0; } #endif /* defined(GENERATE) */ linux-2.6.17/fs/udf/.svn/text-base/file.c.svn-base0000444000000000000000000001441410574207644020225 0ustar rootroot/* * file.c * * PURPOSE * File handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-1999 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 10/02/98 dgb Attempt to integrate into udf.o * 10/07/98 Switched to using generic_readpage, etc., like isofs * And it works! * 12/06/98 blf Added udf_file_read. uses generic_file_read for all cases but * ICBTAG_FLAG_AD_IN_ICB. * 04/06/99 64 bit file handling on 32 bit systems taken from ext2 file.c * 05/12/99 Preliminary file write support */ #include "udfdecl.h" #include #include #include #include #include /* memset */ #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" static int udf_adinicb_readpage(struct file *file, struct page * page) { struct inode *inode = page->mapping->host; char *kaddr; BUG_ON(!PageLocked(page)); kaddr = kmap(page); memset(kaddr, 0, PAGE_CACHE_SIZE); memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), inode->i_size); flush_dcache_page(page); SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; } static int udf_adinicb_writepage(struct page *page, struct writeback_control *wbc) { struct inode *inode = page->mapping->host; char *kaddr; BUG_ON(!PageLocked(page)); kaddr = kmap(page); memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), kaddr, inode->i_size); mark_inode_dirty(inode); SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; } static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { kmap(page); return 0; } static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { struct inode *inode = page->mapping->host; char *kaddr = page_address(page); memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, kaddr + offset, to - offset); mark_inode_dirty(inode); SetPageUptodate(page); kunmap(page); /* only one page here */ if (to > inode->i_size) inode->i_size = to; return 0; } struct address_space_operations udf_adinicb_aops = { .readpage = udf_adinicb_readpage, .writepage = udf_adinicb_writepage, .sync_page = block_sync_page, .prepare_write = udf_adinicb_prepare_write, .commit_write = udf_adinicb_commit_write, }; static ssize_t udf_file_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos) { ssize_t retval; struct inode *inode = file->f_dentry->d_inode; int err, pos; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { if (file->f_flags & O_APPEND) pos = inode->i_size; else pos = *ppos; if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + pos + count)) { udf_expand_file_adinicb(inode, pos + count, &err); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { udf_debug("udf_expand_adinicb: err=%d\n", err); return err; } } else { if (pos + count > inode->i_size) UDF_I_LENALLOC(inode) = pos + count; else UDF_I_LENALLOC(inode) = inode->i_size; } } retval = generic_file_write(file, buf, count, ppos); if (retval > 0) mark_inode_dirty(inode); return retval; } /* * udf_ioctl * * PURPOSE * Issue an ioctl. * * DESCRIPTION * Optional - sys_ioctl() will return -ENOTTY if this routine is not * available, and the ioctl cannot be handled without filesystem help. * * sys_ioctl() handles these ioctls that apply only to regular files: * FIBMAP [requires udf_block_map()], FIGETBSZ, FIONREAD * These ioctls are also handled by sys_ioctl(): * FIOCLEX, FIONCLEX, FIONBIO, FIOASYNC * All other ioctls are passed to the filesystem. * * Refer to sys_ioctl() in fs/ioctl.c * sys_ioctl() -> . * * PRE-CONDITIONS * inode Pointer to inode that ioctl was issued on. * filp Pointer to file that ioctl was issued on. * cmd The ioctl command. * arg The ioctl argument [can be interpreted as a * user-space pointer if desired]. * * POST-CONDITIONS * Success (>=0) or an error code (<=0) that * sys_ioctl() will return. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int result = -EINVAL; if ( file_permission(filp, MAY_READ) != 0 ) { udf_debug("no permission to access inode %lu\n", inode->i_ino); return -EPERM; } if ( !arg ) { udf_debug("invalid argument to udf_ioctl\n"); return -EINVAL; } switch (cmd) { case UDF_GETVOLIDENT: return copy_to_user((char __user *)arg, UDF_SB_VOLIDENT(inode->i_sb), 32) ? -EFAULT : 0; case UDF_RELOCATE_BLOCKS: { long old, new; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (get_user(old, (long __user *)arg)) return -EFAULT; if ((result = udf_relocate_blocks(inode->i_sb, old, &new)) == 0) result = put_user(new, (long __user *)arg); return result; } case UDF_GETEASIZE: result = put_user(UDF_I_LENEATTR(inode), (int __user *)arg); break; case UDF_GETEABLOCK: result = copy_to_user((char __user *)arg, UDF_I_DATA(inode), UDF_I_LENEATTR(inode)) ? -EFAULT : 0; break; } return result; } /* * udf_release_file * * PURPOSE * Called when all references to the file are closed * * DESCRIPTION * Discard prealloced blocks * * HISTORY * */ static int udf_release_file(struct inode * inode, struct file * filp) { if (filp->f_mode & FMODE_WRITE) { lock_kernel(); udf_discard_prealloc(inode); unlock_kernel(); } return 0; } const struct file_operations udf_file_operations = { .read = generic_file_read, .ioctl = udf_ioctl, .open = generic_file_open, .mmap = generic_file_mmap, .write = udf_file_write, .release = udf_release_file, .fsync = udf_fsync_file, .sendfile = generic_file_sendfile, }; struct inode_operations udf_file_inode_operations = { .truncate = udf_truncate, }; linux-2.6.17/fs/udf/.svn/text-base/namei.c.svn-base0000444000000000000000000010154310574207644020377 0ustar rootroot/* * namei.c * * PURPOSE * Inode name handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 12/12/98 blf Created. Split out the lookup code from dir.c * 04/19/99 blf link, mknod, symlink support */ #include "udfdecl.h" #include "udf_i.h" #include "udf_sb.h" #include #include #include #include #include #include #include static inline int udf_match(int len1, const char *name1, int len2, const char *name2) { if (len1 != len2) return 0; return !memcmp(name1, name2, len1); } int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, uint8_t *impuse, uint8_t *fileident) { uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag); uint16_t crc; uint8_t checksum = 0; int i; int offset; uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse); uint8_t lfi = cfi->lengthFileIdent; int padlen = fibh->eoffset - fibh->soffset - liu - lfi - sizeof(struct fileIdentDesc); int adinicb = 0; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) adinicb = 1; offset = fibh->soffset + sizeof(struct fileIdentDesc); if (impuse) { if (adinicb || (offset + liu < 0)) memcpy((uint8_t *)sfi->impUse, impuse, liu); else if (offset >= 0) memcpy(fibh->ebh->b_data + offset, impuse, liu); else { memcpy((uint8_t *)sfi->impUse, impuse, -offset); memcpy(fibh->ebh->b_data, impuse - offset, liu + offset); } } offset += liu; if (fileident) { if (adinicb || (offset + lfi < 0)) memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi); else if (offset >= 0) memcpy(fibh->ebh->b_data + offset, fileident, lfi); else { memcpy((uint8_t *)sfi->fileIdent + liu, fileident, -offset); memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset); } } offset += lfi; if (adinicb || (offset + padlen < 0)) memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen); else if (offset >= 0) memset(fibh->ebh->b_data + offset, 0x00, padlen); else { memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset); memset(fibh->ebh->b_data, 0x00, padlen + offset); } crc = udf_crc((uint8_t *)cfi + sizeof(tag), sizeof(struct fileIdentDesc) - sizeof(tag), 0); if (fibh->sbh == fibh->ebh) crc = udf_crc((uint8_t *)sfi->impUse, crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) crc = udf_crc(fibh->ebh->b_data + sizeof(struct fileIdentDesc) + fibh->soffset, crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); else { crc = udf_crc((uint8_t *)sfi->impUse, -fibh->soffset - sizeof(struct fileIdentDesc), crc); crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); } cfi->descTag.descCRC = cpu_to_le16(crc); cfi->descTag.descCRCLength = cpu_to_le16(crclen); for (i=0; i<16; i++) if (i != 4) checksum += ((uint8_t *)&cfi->descTag)[i]; cfi->descTag.tagChecksum = checksum; if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) memcpy((uint8_t *)sfi, (uint8_t *)cfi, sizeof(struct fileIdentDesc)); else { memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset); memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset, sizeof(struct fileIdentDesc) + fibh->soffset); } if (adinicb) mark_inode_dirty(inode); else { if (fibh->sbh != fibh->ebh) mark_buffer_dirty_inode(fibh->ebh, inode); mark_buffer_dirty_inode(fibh->sbh, inode); } return 0; } static struct fileIdentDesc * udf_find_entry(struct inode *dir, struct dentry *dentry, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi) { struct fileIdentDesc *fi=NULL; loff_t f_pos; int block, flen; char fname[UDF_NAME_LEN]; char *nameptr; uint8_t lfi; uint16_t liu; loff_t size; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; size = (udf_ext0_offset(dir) + dir->i_size) >> 2; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh->sbh = fibh->ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); return NULL; } } else { udf_release_data(bh); return NULL; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); return NULL; } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; if (fibh->sbh == fibh->ebh) { nameptr = fi->fileIdent + liu; } else { int poffset; /* Unpaded ending offset */ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; if (poffset >= lfi) nameptr = (uint8_t *)(fibh->ebh->b_data + poffset - lfi); else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); } } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) continue; } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) continue; } if (!lfi) continue; if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi))) { if (udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) { udf_release_data(bh); return fi; } } } if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); return NULL; } /* * udf_lookup * * PURPOSE * Look-up the inode for a given name. * * DESCRIPTION * Required - lookup_dentry() will return -ENOTDIR if this routine is not * available for a directory. The filesystem is useless if this routine is * not available for at least the filesystem's root directory. * * This routine is passed an incomplete dentry - it must be completed by * calling d_add(dentry, inode). If the name does not exist, then the * specified inode must be set to null. An error should only be returned * when the lookup fails for a reason other than the name not existing. * Note that the directory inode semaphore is held during the call. * * Refer to lookup_dentry() in fs/namei.c * lookup_dentry() -> lookup() -> real_lookup() -> . * * PRE-CONDITIONS * dir Pointer to inode of parent directory. * dentry Pointer to dentry to complete. * nd Pointer to lookup nameidata * * POST-CONDITIONS * Zero on success. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static struct dentry * udf_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct inode *inode = NULL; struct fileIdentDesc cfi; struct udf_fileident_bh fibh; if (dentry->d_name.len > UDF_NAME_LEN-2) return ERR_PTR(-ENAMETOOLONG); lock_kernel(); #ifdef UDF_RECOVERY /* temporary shorthand for specifying files by inode number */ if (!strncmp(dentry->d_name.name, ".B=", 3) ) { kernel_lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; inode = udf_iget(dir->i_sb, lb); if (!inode) { unlock_kernel(); return ERR_PTR(-EACCES); } } else #endif /* UDF_RECOVERY */ if (udf_find_entry(dir, dentry, &fibh, &cfi)) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation)); if ( !inode ) { unlock_kernel(); return ERR_PTR(-EACCES); } } unlock_kernel(); d_add(dentry, inode); return NULL; } static struct fileIdentDesc * udf_add_entry(struct inode *dir, struct dentry *dentry, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi, int *err) { struct super_block *sb; struct fileIdentDesc *fi=NULL; char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; int namelen; loff_t f_pos; int flen; char *nameptr; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int nfidlen; uint8_t lfi; uint16_t liu; int block; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; sb = dir->i_sb; if (dentry) { if (!dentry->d_name.len) { *err = -EINVAL; return NULL; } if ( !(namelen = udf_put_filename(sb, dentry->d_name.name, name, dentry->d_name.len))) { *err = -ENAMETOOLONG; return NULL; } } else namelen = 0; nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh->sbh = fibh->ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); *err = -EIO; return NULL; } block = UDF_I_LOCATION(dir).logicalBlockNum; } else { block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0); fibh->sbh = fibh->ebh = NULL; fibh->soffset = fibh->eoffset = sb->s_blocksize; goto add; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); *err = -EIO; return NULL; } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; if (fibh->sbh == fibh->ebh) nameptr = fi->fileIdent + liu; else { int poffset; /* Unpaded ending offset */ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; if (poffset >= lfi) nameptr = (char *)(fibh->ebh->b_data + poffset - lfi); else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); } } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) { if (((sizeof(struct fileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen) { udf_release_data(bh); cfi->descTag.tagSerialNum = cpu_to_le16(1); cfi->fileVersionNum = cpu_to_le16(1); cfi->fileCharacteristics = 0; cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) return fi; else { *err = -EIO; return NULL; } } } if (!lfi || !dentry) continue; if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)) && udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); *err = -EEXIST; return NULL; } } add: f_pos += nfidlen; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB && sb->s_blocksize - fibh->eoffset < nfidlen) { udf_release_data(bh); bh = NULL; fibh->soffset -= udf_ext0_offset(dir); fibh->eoffset -= udf_ext0_offset(dir); f_pos -= (udf_ext0_offset(dir) >> 2); if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); if (!(fibh->sbh = fibh->ebh = udf_expand_dir_adinicb(dir, &block, err))) return NULL; bloc = UDF_I_LOCATION(dir); eloc.logicalBlockNum = block; eloc.partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum; elen = dir->i_sb->s_blocksize; extoffset = udf_file_entry_alloc_offset(dir); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset += sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset += sizeof(long_ad); } if (sb->s_blocksize - fibh->eoffset >= nfidlen) { fibh->soffset = fibh->eoffset; fibh->eoffset += nfidlen; if (fibh->sbh != fibh->ebh) { udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; } if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { block = UDF_I_LOCATION(dir).logicalBlockNum; fi = (struct fileIdentDesc *)(UDF_I_DATA(dir) + fibh->soffset - udf_ext0_offset(dir) + UDF_I_LENEATTR(dir)); } else { block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); fi = (struct fileIdentDesc *)(fibh->sbh->b_data + fibh->soffset); } } else { fibh->soffset = fibh->eoffset - sb->s_blocksize; fibh->eoffset += nfidlen - sb->s_blocksize; if (fibh->sbh != fibh->ebh) { udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; } block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); if (!(fibh->ebh = udf_bread(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), 1, err))) { udf_release_data(bh); udf_release_data(fibh->sbh); return NULL; } if (!(fibh->soffset)) { if (udf_next_aext(dir, &bloc, &extoffset, &eloc, &elen, &bh, 1) == (EXT_RECORDED_ALLOCATED >> 30)) { block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); } else block ++; udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; fi = (struct fileIdentDesc *)(fibh->sbh->b_data); } else { fi = (struct fileIdentDesc *) (fibh->sbh->b_data + sb->s_blocksize + fibh->soffset); } } memset(cfi, 0, sizeof(struct fileIdentDesc)); if (UDF_SB_UDFREV(sb) >= 0x0200) udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, sizeof(tag)); else udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, sizeof(tag)); cfi->fileVersionNum = cpu_to_le16(1); cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) { udf_release_data(bh); dir->i_size += nfidlen; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) UDF_I_LENALLOC(dir) += nfidlen; mark_inode_dirty(dir); return fi; } else { udf_release_data(bh); if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); *err = -EIO; return NULL; } } static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi) { cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) memset(&(cfi->icb), 0x00, sizeof(long_ad)); return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL); } static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { struct udf_fileident_bh fibh; struct inode *inode; struct fileIdentDesc cfi, *fi; int err; lock_kernel(); inode = udf_new_inode(dir, mode, &err); if (!inode) { unlock_kernel(); return err; } if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) inode->i_data.a_ops = &udf_adinicb_aops; else inode->i_data.a_ops = &udf_aops; inode->i_op = &udf_file_inode_operations; inode->i_fop = &udf_file_operations; inode->i_mode = mode; mark_inode_dirty(inode); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { inode->i_nlink --; mark_inode_dirty(inode); iput(inode); unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); unlock_kernel(); d_instantiate(dentry, inode); return 0; } static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) { struct inode * inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; if (!old_valid_dev(rdev)) return -EINVAL; lock_kernel(); err = -EIO; inode = udf_new_inode(dir, mode, &err); if (!inode) goto out; inode->i_uid = current->fsuid; init_special_inode(inode, mode, rdev); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { inode->i_nlink --; mark_inode_dirty(inode); iput(inode); unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } mark_inode_dirty(inode); if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); d_instantiate(dentry, inode); err = 0; out: unlock_kernel(); return err; } static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; lock_kernel(); err = -EMLINK; if (dir->i_nlink >= (256<i_nlink))-1) goto out; err = -EIO; inode = udf_new_inode(dir, S_IFDIR, &err); if (!inode) goto out; inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err))) { inode->i_nlink--; mark_inode_dirty(inode); iput(inode); goto out; } inode->i_nlink = 2; cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(dir)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(dir) & 0x00000000FFFFFFFFUL); cfi.fileCharacteristics = FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT; udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL); udf_release_data(fibh.sbh); inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); goto out; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY; udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); dir->i_nlink++; mark_inode_dirty(dir); d_instantiate(dentry, inode); if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); err = 0; out: unlock_kernel(); return err; } static int empty_dir(struct inode *dir) { struct fileIdentDesc *fi, cfi; struct udf_fileident_bh fibh; loff_t f_pos; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int block; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; f_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh.sbh = fibh.ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); return 0; } } else { udf_release_data(bh); return 0; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } if (cfi.lengthFileIdent && (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 1; } static int udf_rmdir(struct inode * dir, struct dentry * dentry) { int retval; struct inode * inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi, cfi; kernel_lb_addr tloc; retval = -ENOENT; lock_kernel(); fi = udf_find_entry(dir, dentry, &fibh, &cfi); if (!fi) goto out; retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino) goto end_rmdir; retval = -ENOTEMPTY; if (!empty_dir(inode)) goto end_rmdir; retval = udf_delete_entry(dir, fi, &fibh, &cfi); if (retval) goto end_rmdir; if (inode->i_nlink != 2) udf_warning(inode->i_sb, "udf_rmdir", "empty directory has nlink != 2 (%d)", inode->i_nlink); inode->i_nlink = 0; inode->i_size = 0; mark_inode_dirty(inode); dir->i_nlink --; inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb); mark_inode_dirty(dir); end_rmdir: if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); out: unlock_kernel(); return retval; } static int udf_unlink(struct inode * dir, struct dentry * dentry) { int retval; struct inode * inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi; struct fileIdentDesc cfi; kernel_lb_addr tloc; retval = -ENOENT; lock_kernel(); fi = udf_find_entry(dir, dentry, &fibh, &cfi); if (!fi) goto out; retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino) goto end_unlink; if (!inode->i_nlink) { udf_debug("Deleting nonexistent file (%lu), %d\n", inode->i_ino, inode->i_nlink); inode->i_nlink = 1; } retval = udf_delete_entry(dir, fi, &fibh, &cfi); if (retval) goto end_unlink; dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb); mark_inode_dirty(dir); inode->i_nlink--; mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; end_unlink: if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); out: unlock_kernel(); return retval; } static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname) { struct inode * inode; struct pathComponent *pc; char *compstart; struct udf_fileident_bh fibh; struct buffer_head *bh = NULL; int eoffset, elen = 0; struct fileIdentDesc *fi; struct fileIdentDesc cfi; char *ea; int err; int block; char name[UDF_NAME_LEN]; int namelen; lock_kernel(); if (!(inode = udf_new_inode(dir, S_IFLNK, &err))) goto out; inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; if (UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB) { struct buffer_head *bh = NULL; kernel_lb_addr bloc, eloc; uint32_t elen, extoffset; block = udf_new_block(inode->i_sb, inode, UDF_I_LOCATION(inode).partitionReferenceNum, UDF_I_LOCATION(inode).logicalBlockNum, &err); if (!block) goto out_no_entry; bloc = UDF_I_LOCATION(inode); eloc.logicalBlockNum = block; eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; elen = inode->i_sb->s_blocksize; UDF_I_LENEXTENTS(inode) = elen; extoffset = udf_file_entry_alloc_offset(inode); udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 0); udf_release_data(bh); block = udf_get_pblock(inode->i_sb, block, UDF_I_LOCATION(inode).partitionReferenceNum, 0); bh = udf_tread(inode->i_sb, block); lock_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(bh); unlock_buffer(bh); mark_buffer_dirty_inode(bh, inode); ea = bh->b_data + udf_ext0_offset(inode); } else ea = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode); pc = (struct pathComponent *)ea; if (*symname == '/') { do { symname++; } while (*symname == '/'); pc->componentType = 1; pc->lengthComponentIdent = 0; pc->componentFileVersionNum = 0; pc += sizeof(struct pathComponent); elen += sizeof(struct pathComponent); } err = -ENAMETOOLONG; while (*symname) { if (elen + sizeof(struct pathComponent) > eoffset) goto out_no_entry; pc = (struct pathComponent *)(ea + elen); compstart = (char *)symname; do { symname++; } while (*symname && *symname != '/'); pc->componentType = 5; pc->lengthComponentIdent = 0; pc->componentFileVersionNum = 0; if (compstart[0] == '.') { if ((symname-compstart) == 1) pc->componentType = 4; else if ((symname-compstart) == 2 && compstart[1] == '.') pc->componentType = 3; } if (pc->componentType == 5) { if ( !(namelen = udf_put_filename(inode->i_sb, compstart, name, symname-compstart))) goto out_no_entry; if (elen + sizeof(struct pathComponent) + namelen > eoffset) goto out_no_entry; else pc->lengthComponentIdent = namelen; memcpy(pc->componentIdent, name, namelen); } elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; if (*symname) { do { symname++; } while (*symname == '/'); } } udf_release_data(bh); inode->i_size = elen; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) UDF_I_LENALLOC(inode) = inode->i_size; mark_inode_dirty(inode); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) goto out_no_entry; cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); if (UDF_SB_LVIDBH(inode->i_sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); uniqueID = le64_to_cpu(lvhd->uniqueID); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL); if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); d_instantiate(dentry, inode); err = 0; out: unlock_kernel(); return err; out_no_entry: inode->i_nlink--; mark_inode_dirty(inode); iput(inode); goto out; } static int udf_link(struct dentry * old_dentry, struct inode * dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; lock_kernel(); if (inode->i_nlink >= (256<i_nlink))-1) { unlock_kernel(); return -EMLINK; } if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); if (UDF_SB_LVIDBH(inode->i_sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); uniqueID = le64_to_cpu(lvhd->uniqueID); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL); if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); inode->i_nlink ++; inode->i_ctime = current_fs_time(inode->i_sb); mark_inode_dirty(inode); atomic_inc(&inode->i_count); d_instantiate(dentry, inode); unlock_kernel(); return 0; } /* Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry) { struct inode * old_inode = old_dentry->d_inode; struct inode * new_inode = new_dentry->d_inode; struct udf_fileident_bh ofibh, nfibh; struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL, ocfi, ncfi; struct buffer_head *dir_bh = NULL; int retval = -ENOENT; kernel_lb_addr tloc; lock_kernel(); if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi))) { if (ofibh.sbh != ofibh.ebh) udf_release_data(ofibh.ebh); udf_release_data(ofibh.sbh); } tloc = lelb_to_cpu(ocfi.icb.extLocation); if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0) != old_inode->i_ino) goto end_rename; nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi); if (nfi) { if (!new_inode) { if (nfibh.sbh != nfibh.ebh) udf_release_data(nfibh.ebh); udf_release_data(nfibh.sbh); nfi = NULL; } } if (S_ISDIR(old_inode->i_mode)) { uint32_t offset = udf_ext0_offset(old_inode); if (new_inode) { retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; } retval = -EIO; if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) { dir_fi = udf_get_fileident(UDF_I_DATA(old_inode) - (UDF_I_EFE(old_inode) ? sizeof(struct extendedFileEntry) : sizeof(struct fileEntry)), old_inode->i_sb->s_blocksize, &offset); } else { dir_bh = udf_bread(old_inode, 0, 0, &retval); if (!dir_bh) goto end_rename; dir_fi = udf_get_fileident(dir_bh->b_data, old_inode->i_sb->s_blocksize, &offset); } if (!dir_fi) goto end_rename; tloc = lelb_to_cpu(dir_fi->icb.extLocation); if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) != old_dir->i_ino) goto end_rename; retval = -EMLINK; if (!new_inode && new_dir->i_nlink >= (256<i_nlink))-1) goto end_rename; } if (!nfi) { nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval); if (!nfi) goto end_rename; } /* * Like most other Unix systems, set the ctime for inodes on a * rename. */ old_inode->i_ctime = current_fs_time(old_inode->i_sb); mark_inode_dirty(old_inode); /* * ok, that's it */ ncfi.fileVersionNum = ocfi.fileVersionNum; ncfi.fileCharacteristics = ocfi.fileCharacteristics; memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad)); udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL); /* The old fid may have moved - find it again */ ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = current_fs_time(new_inode->i_sb); mark_inode_dirty(new_inode); } old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb); mark_inode_dirty(old_dir); if (dir_fi) { dir_fi->icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(new_dir)); udf_update_tag((char *)dir_fi, (sizeof(struct fileIdentDesc) + le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3); if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(old_inode); } else mark_buffer_dirty_inode(dir_bh, old_inode); old_dir->i_nlink --; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink --; mark_inode_dirty(new_inode); } else { new_dir->i_nlink ++; mark_inode_dirty(new_dir); } } if (ofi) { if (ofibh.sbh != ofibh.ebh) udf_release_data(ofibh.ebh); udf_release_data(ofibh.sbh); } retval = 0; end_rename: udf_release_data(dir_bh); if (nfi) { if (nfibh.sbh != nfibh.ebh) udf_release_data(nfibh.ebh); udf_release_data(nfibh.sbh); } unlock_kernel(); return retval; } struct inode_operations udf_dir_inode_operations = { .lookup = udf_lookup, .create = udf_create, .link = udf_link, .unlink = udf_unlink, .symlink = udf_symlink, .mkdir = udf_mkdir, .rmdir = udf_rmdir, .mknod = udf_mknod, .rename = udf_rename, }; linux-2.6.17/fs/udf/.svn/text-base/udf_i.h.svn-base0000444000000000000000000000177310574207644020405 0ustar rootroot#ifndef __LINUX_UDF_I_H #define __LINUX_UDF_I_H #include static inline struct udf_inode_info *UDF_I(struct inode *inode) { return list_entry(inode, struct udf_inode_info, vfs_inode); } #define UDF_I_LOCATION(X) ( UDF_I(X)->i_location ) #define UDF_I_LENEATTR(X) ( UDF_I(X)->i_lenEAttr ) #define UDF_I_LENALLOC(X) ( UDF_I(X)->i_lenAlloc ) #define UDF_I_LENEXTENTS(X) ( UDF_I(X)->i_lenExtents ) #define UDF_I_UNIQUE(X) ( UDF_I(X)->i_unique ) #define UDF_I_ALLOCTYPE(X) ( UDF_I(X)->i_alloc_type ) #define UDF_I_EFE(X) ( UDF_I(X)->i_efe ) #define UDF_I_USE(X) ( UDF_I(X)->i_use ) #define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat4096 ) #define UDF_I_NEXT_ALLOC_BLOCK(X) ( UDF_I(X)->i_next_alloc_block ) #define UDF_I_NEXT_ALLOC_GOAL(X) ( UDF_I(X)->i_next_alloc_goal ) #define UDF_I_CRTIME(X) ( UDF_I(X)->i_crtime ) #define UDF_I_SAD(X) ( UDF_I(X)->i_ext.i_sad ) #define UDF_I_LAD(X) ( UDF_I(X)->i_ext.i_lad ) #define UDF_I_DATA(X) ( UDF_I(X)->i_ext.i_data ) #endif /* !defined(_LINUX_UDF_I_H) */ linux-2.6.17/fs/udf/.svn/text-base/super.c.svn-base0000444000000000000000000014532610574207644020453 0ustar rootroot/* * super.c * * PURPOSE * Super block routines for the OSTA-UDF(tm) filesystem. * * DESCRIPTION * OSTA-UDF(tm) = Optical Storage Technology Association * Universal Disk Format. * * This code is based on version 2.00 of the UDF specification, * and revision 3 of the ECMA 167 standard [equivalent to ISO 13346]. * http://www.osta.org/ * http://www.ecma.ch/ * http://www.iso.org/ * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 2000 Stelias Computing Inc * * HISTORY * * 09/24/98 dgb changed to allow compiling outside of kernel, and * added some debugging. * 10/01/98 dgb updated to allow (some) possibility of compiling w/2.0.34 * 10/16/98 attempting some multi-session support * 10/17/98 added freespace count for "df" * 11/11/98 gr added novrs option * 11/26/98 dgb added fileset,anchor mount options * 12/06/98 blf really hosed things royally. vat/sparing support. sequenced vol descs * rewrote option handling based on isofs * 12/20/98 find the free space bitmap (if it exists) */ #include "udfdecl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "udf_sb.h" #include "udf_i.h" #include #include #define VDS_POS_PRIMARY_VOL_DESC 0 #define VDS_POS_UNALLOC_SPACE_DESC 1 #define VDS_POS_LOGICAL_VOL_DESC 2 #define VDS_POS_PARTITION_DESC 3 #define VDS_POS_IMP_USE_VOL_DESC 4 #define VDS_POS_VOL_DESC_PTR 5 #define VDS_POS_TERMINATING_DESC 6 #define VDS_POS_LENGTH 7 static char error_buf[1024]; /* These are the "meat" - everything else is stuffing */ static int udf_fill_super(struct super_block *, void *, int); static void udf_put_super(struct super_block *); static void udf_write_super(struct super_block *); static int udf_remount_fs(struct super_block *, int *, char *); static int udf_check_valid(struct super_block *, int, int); static int udf_vrs(struct super_block *sb, int silent); static int udf_load_partition(struct super_block *, kernel_lb_addr *); static int udf_load_logicalvol(struct super_block *, struct buffer_head *, kernel_lb_addr *); static void udf_load_logicalvolint(struct super_block *, kernel_extent_ad); static void udf_find_anchor(struct super_block *); static int udf_find_fileset(struct super_block *, kernel_lb_addr *, kernel_lb_addr *); static void udf_load_pvoldesc(struct super_block *, struct buffer_head *); static void udf_load_fileset(struct super_block *, struct buffer_head *, kernel_lb_addr *); static void udf_load_partdesc(struct super_block *, struct buffer_head *); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); static int udf_statfs(struct super_block *, struct kstatfs *); /* UDF filesystem type */ static struct super_block *udf_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super); } static struct file_system_type udf_fstype = { .owner = THIS_MODULE, .name = "udf", .get_sb = udf_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static kmem_cache_t * udf_inode_cachep; static struct inode *udf_alloc_inode(struct super_block *sb) { struct udf_inode_info *ei; ei = (struct udf_inode_info *)kmem_cache_alloc(udf_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } static void udf_destroy_inode(struct inode *inode) { kmem_cache_free(udf_inode_cachep, UDF_I(inode)); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct udf_inode_info *ei = (struct udf_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { ei->i_ext.i_data = NULL; inode_init_once(&ei->vfs_inode); } } static int init_inodecache(void) { udf_inode_cachep = kmem_cache_create("udf_inode_cache", sizeof(struct udf_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), init_once, NULL); if (udf_inode_cachep == NULL) return -ENOMEM; return 0; } static void destroy_inodecache(void) { if (kmem_cache_destroy(udf_inode_cachep)) printk(KERN_INFO "udf_inode_cache: not all structures were freed\n"); } /* Superblock operations */ static struct super_operations udf_sb_ops = { .alloc_inode = udf_alloc_inode, .destroy_inode = udf_destroy_inode, .write_inode = udf_write_inode, .delete_inode = udf_delete_inode, .clear_inode = udf_clear_inode, .put_super = udf_put_super, .write_super = udf_write_super, .statfs = udf_statfs, .remount_fs = udf_remount_fs, }; struct udf_options { unsigned char novrs; unsigned int blocksize; unsigned int session; unsigned int lastblock; unsigned int anchor; unsigned int volume; unsigned short partition; unsigned int fileset; unsigned int rootdir; unsigned int flags; mode_t umask; gid_t gid; uid_t uid; struct nls_table *nls_map; }; static int __init init_udf_fs(void) { int err; err = init_inodecache(); if (err) goto out1; err = register_filesystem(&udf_fstype); if (err) goto out; return 0; out: destroy_inodecache(); out1: return err; } static void __exit exit_udf_fs(void) { unregister_filesystem(&udf_fstype); destroy_inodecache(); } module_init(init_udf_fs) module_exit(exit_udf_fs) /* * udf_parse_options * * PURPOSE * Parse mount options. * * DESCRIPTION * The following mount options are supported: * * gid= Set the default group. * umask= Set the default umask. * uid= Set the default user. * bs= Set the block size. * unhide Show otherwise hidden files. * undelete Show deleted files in lists. * adinicb Embed data in the inode (default) * noadinicb Don't embed data in the inode * shortad Use short ad's * longad Use long ad's (default) * nostrict Unset strict conformance * iocharset= Set the NLS character set * * The remaining are for debugging and disaster recovery: * * novrs Skip volume sequence recognition * * The following expect a offset from 0. * * session= Set the CDROM session (default= last session) * anchor= Override standard anchor location. (default= 256) * volume= Override the VolumeDesc location. (unused) * partition= Override the PartitionDesc location. (unused) * lastblock= Set the last block of the filesystem/ * * The following expect a offset from the partition root. * * fileset= Override the fileset block location. (unused) * rootdir= Override the root directory location. (unused) * WARNING: overriding the rootdir to a non-directory may * yield highly unpredictable results. * * PRE-CONDITIONS * options Pointer to mount options string. * uopts Pointer to mount options variable. * * POST-CONDITIONS * 1 Mount options parsed okay. * 0 Error parsing mount options. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ enum { Opt_novrs, Opt_nostrict, Opt_bs, Opt_unhide, Opt_undelete, Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad, Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock, Opt_anchor, Opt_volume, Opt_partition, Opt_fileset, Opt_rootdir, Opt_utf8, Opt_iocharset, Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore }; static match_table_t tokens = { {Opt_novrs, "novrs"}, {Opt_nostrict, "nostrict"}, {Opt_bs, "bs=%u"}, {Opt_unhide, "unhide"}, {Opt_undelete, "undelete"}, {Opt_noadinicb, "noadinicb"}, {Opt_adinicb, "adinicb"}, {Opt_shortad, "shortad"}, {Opt_longad, "longad"}, {Opt_uforget, "uid=forget"}, {Opt_uignore, "uid=ignore"}, {Opt_gforget, "gid=forget"}, {Opt_gignore, "gid=ignore"}, {Opt_gid, "gid=%u"}, {Opt_uid, "uid=%u"}, {Opt_umask, "umask=%o"}, {Opt_session, "session=%u"}, {Opt_lastblock, "lastblock=%u"}, {Opt_anchor, "anchor=%u"}, {Opt_volume, "volume=%u"}, {Opt_partition, "partition=%u"}, {Opt_fileset, "fileset=%u"}, {Opt_rootdir, "rootdir=%u"}, {Opt_utf8, "utf8"}, {Opt_iocharset, "iocharset=%s"}, {Opt_err, NULL} }; static int udf_parse_options(char *options, struct udf_options *uopt) { char *p; int option; uopt->novrs = 0; uopt->blocksize = 2048; uopt->partition = 0xFFFF; uopt->session = 0xFFFFFFFF; uopt->lastblock = 0; uopt->anchor = 0; uopt->volume = 0xFFFFFFFF; uopt->rootdir = 0xFFFFFFFF; uopt->fileset = 0xFFFFFFFF; uopt->nls_map = NULL; if (!options) return 1; while ((p = strsep(&options, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { case Opt_novrs: uopt->novrs = 1; case Opt_bs: if (match_int(&args[0], &option)) return 0; uopt->blocksize = option; break; case Opt_unhide: uopt->flags |= (1 << UDF_FLAG_UNHIDE); break; case Opt_undelete: uopt->flags |= (1 << UDF_FLAG_UNDELETE); break; case Opt_noadinicb: uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); break; case Opt_adinicb: uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); break; case Opt_shortad: uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); break; case Opt_longad: uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); break; case Opt_gid: if (match_int(args, &option)) return 0; uopt->gid = option; break; case Opt_uid: if (match_int(args, &option)) return 0; uopt->uid = option; break; case Opt_umask: if (match_octal(args, &option)) return 0; uopt->umask = option; break; case Opt_nostrict: uopt->flags &= ~(1 << UDF_FLAG_STRICT); break; case Opt_session: if (match_int(args, &option)) return 0; uopt->session = option; break; case Opt_lastblock: if (match_int(args, &option)) return 0; uopt->lastblock = option; break; case Opt_anchor: if (match_int(args, &option)) return 0; uopt->anchor = option; break; case Opt_volume: if (match_int(args, &option)) return 0; uopt->volume = option; break; case Opt_partition: if (match_int(args, &option)) return 0; uopt->partition = option; break; case Opt_fileset: if (match_int(args, &option)) return 0; uopt->fileset = option; break; case Opt_rootdir: if (match_int(args, &option)) return 0; uopt->rootdir = option; break; case Opt_utf8: uopt->flags |= (1 << UDF_FLAG_UTF8); break; #ifdef CONFIG_UDF_NLS case Opt_iocharset: uopt->nls_map = load_nls(args[0].from); uopt->flags |= (1 << UDF_FLAG_NLS_MAP); break; #endif case Opt_uignore: uopt->flags |= (1 << UDF_FLAG_UID_IGNORE); break; case Opt_uforget: uopt->flags |= (1 << UDF_FLAG_UID_FORGET); break; case Opt_gignore: uopt->flags |= (1 << UDF_FLAG_GID_IGNORE); break; case Opt_gforget: uopt->flags |= (1 << UDF_FLAG_GID_FORGET); break; default: printk(KERN_ERR "udf: bad mount option \"%s\" " "or missing value\n", p); return 0; } } return 1; } void udf_write_super(struct super_block *sb) { lock_kernel(); if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); sb->s_dirt = 0; unlock_kernel(); } static int udf_remount_fs(struct super_block *sb, int *flags, char *options) { struct udf_options uopt; uopt.flags = UDF_SB(sb)->s_flags ; uopt.uid = UDF_SB(sb)->s_uid ; uopt.gid = UDF_SB(sb)->s_gid ; uopt.umask = UDF_SB(sb)->s_umask ; if ( !udf_parse_options(options, &uopt) ) return -EINVAL; UDF_SB(sb)->s_flags = uopt.flags; UDF_SB(sb)->s_uid = uopt.uid; UDF_SB(sb)->s_gid = uopt.gid; UDF_SB(sb)->s_umask = uopt.umask; if (UDF_SB_LVIDBH(sb)) { int write_rev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); if (write_rev > UDF_MAX_WRITE_VERSION) *flags |= MS_RDONLY; } if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) udf_close_lvid(sb); else udf_open_lvid(sb); return 0; } /* * udf_set_blocksize * * PURPOSE * Set the block size to be used in all transfers. * * DESCRIPTION * To allow room for a DMA transfer, it is best to guess big when unsure. * This routine picks 2048 bytes as the blocksize when guessing. This * should be adequate until devices with larger block sizes become common. * * Note that the Linux kernel can currently only deal with blocksizes of * 512, 1024, 2048, 4096, and 8192 bytes. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * * POST-CONDITIONS * sb->s_blocksize Blocksize. * sb->s_blocksize_bits log2 of blocksize. * 0 Blocksize is valid. * 1 Blocksize is invalid. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_set_blocksize(struct super_block *sb, int bsize) { if (!sb_min_blocksize(sb, bsize)) { udf_debug("Bad block size (%d)\n", bsize); printk(KERN_ERR "udf: bad block size (%d)\n", bsize); return 0; } return sb->s_blocksize; } static int udf_vrs(struct super_block *sb, int silent) { struct volStructDesc *vsd = NULL; int sector = 32768; int sectorsize; struct buffer_head *bh = NULL; int iso9660=0; int nsr02=0; int nsr03=0; /* Block size must be a multiple of 512 */ if (sb->s_blocksize & 511) return 0; if (sb->s_blocksize < sizeof(struct volStructDesc)) sectorsize = sizeof(struct volStructDesc); else sectorsize = sb->s_blocksize; sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits); udf_debug("Starting at sector %u (%ld byte sectors)\n", (sector >> sb->s_blocksize_bits), sb->s_blocksize); /* Process the sequence (if applicable) */ for (;!nsr02 && !nsr03; sector += sectorsize) { /* Read a block */ bh = udf_tread(sb, sector >> sb->s_blocksize_bits); if (!bh) break; /* Look for ISO descriptors */ vsd = (struct volStructDesc *)(bh->b_data + (sector & (sb->s_blocksize - 1))); if (vsd->stdIdent[0] == 0) { udf_release_data(bh); break; } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN)) { iso9660 = sector; switch (vsd->structType) { case 0: udf_debug("ISO9660 Boot Record found\n"); break; case 1: udf_debug("ISO9660 Primary Volume Descriptor found\n"); break; case 2: udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); break; case 3: udf_debug("ISO9660 Volume Partition Descriptor found\n"); break; case 255: udf_debug("ISO9660 Volume Descriptor Set Terminator found\n"); break; default: udf_debug("ISO9660 VRS (%u) found\n", vsd->structType); break; } } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, VSD_STD_ID_LEN)) { } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, VSD_STD_ID_LEN)) { udf_release_data(bh); break; } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, VSD_STD_ID_LEN)) { nsr02 = sector; } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, VSD_STD_ID_LEN)) { nsr03 = sector; } udf_release_data(bh); } if (nsr03) return nsr03; else if (nsr02) return nsr02; else if (sector - (UDF_SB_SESSION(sb) << sb->s_blocksize_bits) == 32768) return -1; else return 0; } /* * udf_find_anchor * * PURPOSE * Find an anchor volume descriptor. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * lastblock Last block on media. * * POST-CONDITIONS * 1 if not found, 0 if ok * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static void udf_find_anchor(struct super_block *sb) { int lastblock = UDF_SB_LASTBLOCK(sb); struct buffer_head *bh = NULL; uint16_t ident; uint32_t location; int i; if (lastblock) { int varlastblock = udf_variable_to_fixed(lastblock); int last[] = { lastblock, lastblock - 2, lastblock - 150, lastblock - 152, varlastblock, varlastblock - 2, varlastblock - 150, varlastblock - 152 }; lastblock = 0; /* Search for an anchor volume descriptor pointer */ /* according to spec, anchor is in either: * block 256 * lastblock-256 * lastblock * however, if the disc isn't closed, it could be 512 */ for (i = 0; !lastblock && i < ARRAY_SIZE(last); i++) { if (last[i] < 0 || !(bh = sb_bread(sb, last[i]))) { ident = location = 0; } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); } if (ident == TAG_IDENT_AVDP) { if (location == last[i] - UDF_SB_SESSION(sb)) { lastblock = UDF_SB_ANCHOR(sb)[0] = last[i] - UDF_SB_SESSION(sb); UDF_SB_ANCHOR(sb)[1] = last[i] - 256 - UDF_SB_SESSION(sb); } else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb)) { UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb); UDF_SB_ANCHOR(sb)[1] = lastblock - 256 - UDF_SB_SESSION(sb); } else udf_debug("Anchor found at block %d, location mismatch %d.\n", last[i], location); } else if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE) { lastblock = last[i]; UDF_SB_ANCHOR(sb)[3] = 512; } else { if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256))) { ident = location = 0; } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); } if (ident == TAG_IDENT_AVDP && location == last[i] - 256 - UDF_SB_SESSION(sb)) { lastblock = last[i]; UDF_SB_ANCHOR(sb)[1] = last[i] - 256; } else { if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb)))) { ident = location = 0; } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); } if (ident == TAG_IDENT_AVDP && location == udf_variable_to_fixed(last[i]) - 256) { UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); lastblock = udf_variable_to_fixed(last[i]); UDF_SB_ANCHOR(sb)[1] = lastblock - 256; } } } } } if (!lastblock) { /* We havn't found the lastblock. check 312 */ if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb)))) { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); if (ident == TAG_IDENT_AVDP && location == 256) UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); } } for (i = 0; i < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); i++) { if (UDF_SB_ANCHOR(sb)[i]) { if (!(bh = udf_read_tagged(sb, UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) { UDF_SB_ANCHOR(sb)[i] = 0; } else { udf_release_data(bh); if ((ident != TAG_IDENT_AVDP) && (i || (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE))) { UDF_SB_ANCHOR(sb)[i] = 0; } } } } UDF_SB_LASTBLOCK(sb) = lastblock; } static int udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, kernel_lb_addr *root) { struct buffer_head *bh = NULL; long lastblock; uint16_t ident; if (fileset->logicalBlockNum != 0xFFFFFFFF || fileset->partitionReferenceNum != 0xFFFF) { bh = udf_read_ptagged(sb, *fileset, 0, &ident); if (!bh) return 1; else if (ident != TAG_IDENT_FSD) { udf_release_data(bh); return 1; } } if (!bh) /* Search backwards through the partitions */ { kernel_lb_addr newfileset; return 1; for (newfileset.partitionReferenceNum=UDF_SB_NUMPARTS(sb)-1; (newfileset.partitionReferenceNum != 0xFFFF && fileset->logicalBlockNum == 0xFFFFFFFF && fileset->partitionReferenceNum == 0xFFFF); newfileset.partitionReferenceNum--) { lastblock = UDF_SB_PARTLEN(sb, newfileset.partitionReferenceNum); newfileset.logicalBlockNum = 0; do { bh = udf_read_ptagged(sb, newfileset, 0, &ident); if (!bh) { newfileset.logicalBlockNum ++; continue; } switch (ident) { case TAG_IDENT_SBD: { struct spaceBitmapDesc *sp; sp = (struct spaceBitmapDesc *)bh->b_data; newfileset.logicalBlockNum += 1 + ((le32_to_cpu(sp->numOfBytes) + sizeof(struct spaceBitmapDesc) - 1) >> sb->s_blocksize_bits); udf_release_data(bh); break; } case TAG_IDENT_FSD: { *fileset = newfileset; break; } default: { newfileset.logicalBlockNum ++; udf_release_data(bh); bh = NULL; break; } } } while (newfileset.logicalBlockNum < lastblock && fileset->logicalBlockNum == 0xFFFFFFFF && fileset->partitionReferenceNum == 0xFFFF); } } if ((fileset->logicalBlockNum != 0xFFFFFFFF || fileset->partitionReferenceNum != 0xFFFF) && bh) { udf_debug("Fileset at block=%d, partition=%d\n", fileset->logicalBlockNum, fileset->partitionReferenceNum); UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum; udf_load_fileset(sb, bh, root); udf_release_data(bh); return 0; } return 1; } static void udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) { struct primaryVolDesc *pvoldesc; time_t recording; long recording_usec; struct ustr instr; struct ustr outstr; pvoldesc = (struct primaryVolDesc *)bh->b_data; if ( udf_stamp_to_time(&recording, &recording_usec, lets_to_cpu(pvoldesc->recordingDateAndTime)) ) { kernel_timestamp ts; ts = lets_to_cpu(pvoldesc->recordingDateAndTime); udf_debug("recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x)\n", recording, recording_usec, ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); UDF_SB_RECORDTIME(sb).tv_sec = recording; UDF_SB_RECORDTIME(sb).tv_nsec = recording_usec * 1000; } if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) ) { if (udf_CS0toUTF8(&outstr, &instr)) { strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, outstr.u_len > 31 ? 31 : outstr.u_len); udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb)); } } if ( !udf_build_ustr(&instr, pvoldesc->volSetIdent, 128) ) { if (udf_CS0toUTF8(&outstr, &instr)) udf_debug("volSetIdent[] = '%s'\n", outstr.u_name); } } static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh, kernel_lb_addr *root) { struct fileSetDesc *fset; fset = (struct fileSetDesc *)bh->b_data; *root = lelb_to_cpu(fset->rootDirectoryICB.extLocation); UDF_SB_SERIALNUM(sb) = le16_to_cpu(fset->descTag.tagSerialNum); udf_debug("Rootdir at block=%d, partition=%d\n", root->logicalBlockNum, root->partitionReferenceNum); } static void udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) { struct partitionDesc *p; int i; p = (struct partitionDesc *)bh->b_data; for (i=0; ipartitionNumber)); if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber)) { UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */ UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation); if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_READ_ONLY) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_READ_ONLY; if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_WRITE_ONCE) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_WRITE_ONCE; if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_REWRITABLE) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_REWRITABLE; if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_OVERWRITABLE) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_OVERWRITABLE; if (!strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) || !strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03)) { struct partitionHeaderDesc *phd; phd = (struct partitionHeaderDesc *)(p->partitionContentsUse); if (phd->unallocSpaceTable.extLength) { kernel_lb_addr loc = { le32_to_cpu(phd->unallocSpaceTable.extPosition), i }; UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table = udf_iget(sb, loc); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_TABLE; udf_debug("unallocSpaceTable (part %d) @ %ld\n", i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino); } if (phd->unallocSpaceBitmap.extLength) { UDF_SB_ALLOC_BITMAP(sb, i, s_uspace); if (UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap != NULL) { UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extLength = le32_to_cpu(phd->unallocSpaceBitmap.extLength); UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition = le32_to_cpu(phd->unallocSpaceBitmap.extPosition); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP; udf_debug("unallocSpaceBitmap (part %d) @ %d\n", i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition); } } if (phd->partitionIntegrityTable.extLength) udf_debug("partitionIntegrityTable (part %d)\n", i); if (phd->freedSpaceTable.extLength) { kernel_lb_addr loc = { le32_to_cpu(phd->freedSpaceTable.extPosition), i }; UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table = udf_iget(sb, loc); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_TABLE; udf_debug("freedSpaceTable (part %d) @ %ld\n", i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table->i_ino); } if (phd->freedSpaceBitmap.extLength) { UDF_SB_ALLOC_BITMAP(sb, i, s_fspace); if (UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap != NULL) { UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extLength = le32_to_cpu(phd->freedSpaceBitmap.extLength); UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition = le32_to_cpu(phd->freedSpaceBitmap.extPosition); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP; udf_debug("freedSpaceBitmap (part %d) @ %d\n", i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition); } } } break; } } if (i == UDF_SB_NUMPARTS(sb)) { udf_debug("Partition (%d) not found in partition map\n", le16_to_cpu(p->partitionNumber)); } else { udf_debug("Partition (%d:%d type %x) starts at physical %d, block length %d\n", le16_to_cpu(p->partitionNumber), i, UDF_SB_PARTTYPE(sb,i), UDF_SB_PARTROOT(sb,i), UDF_SB_PARTLEN(sb,i)); } } static int udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_addr *fileset) { struct logicalVolDesc *lvd; int i, j, offset; uint8_t type; lvd = (struct logicalVolDesc *)bh->b_data; UDF_SB_ALLOC_PARTMAPS(sb, le32_to_cpu(lvd->numPartitionMaps)); for (i=0,offset=0; imapTableLength); i++,offset+=((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength) { type = ((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapType; if (type == 1) { struct genericPartitionMap1 *gpm1 = (struct genericPartitionMap1 *)&(lvd->partitionMaps[offset]); UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15; UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum); UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum); UDF_SB_PARTFUNC(sb,i) = NULL; } else if (type == 2) { struct udfPartitionMap2 *upm2 = (struct udfPartitionMap2 *)&(lvd->partitionMaps[offset]); if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) { if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15; UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15; } else if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20; UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20; } } else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) { uint32_t loc; uint16_t ident; struct sparingTable *st; struct sparablePartitionMap *spm = (struct sparablePartitionMap *)&(lvd->partitionMaps[offset]); UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; UDF_SB_TYPESPAR(sb,i).s_packet_len = le16_to_cpu(spm->packetLength); for (j=0; jnumSparingTables; j++) { loc = le32_to_cpu(spm->locSparingTable[j]); UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = udf_read_tagged(sb, loc, loc, &ident); if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) { st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,i).s_spar_map[j]->b_data; if (ident != 0 || strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) { udf_release_data(UDF_SB_TYPESPAR(sb,i).s_spar_map[j]); UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = NULL; } } } UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15; } else { udf_debug("Unknown ident: %s\n", upm2->partIdent.ident); continue; } UDF_SB_PARTVSN(sb,i) = le16_to_cpu(upm2->volSeqNum); UDF_SB_PARTNUM(sb,i) = le16_to_cpu(upm2->partitionNum); } udf_debug("Partition (%d:%d) type %d on volume %d\n", i, UDF_SB_PARTNUM(sb,i), type, UDF_SB_PARTVSN(sb,i)); } if (fileset) { long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]); *fileset = lelb_to_cpu(la->extLocation); udf_debug("FileSet found in LogicalVolDesc at block=%d, partition=%d\n", fileset->logicalBlockNum, fileset->partitionReferenceNum); } if (lvd->integritySeqExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt)); return 0; } /* * udf_load_logicalvolint * */ static void udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc) { struct buffer_head *bh = NULL; uint16_t ident; while (loc.extLength > 0 && (bh = udf_read_tagged(sb, loc.extLocation, loc.extLocation, &ident)) && ident == TAG_IDENT_LVID) { UDF_SB_LVIDBH(sb) = bh; if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt)); if (UDF_SB_LVIDBH(sb) != bh) udf_release_data(bh); loc.extLength -= sb->s_blocksize; loc.extLocation ++; } if (UDF_SB_LVIDBH(sb) != bh) udf_release_data(bh); } /* * udf_process_sequence * * PURPOSE * Process a main/reserve volume descriptor sequence. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * block First block of first extent of the sequence. * lastblock Lastblock of first extent of the sequence. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_lb_addr *fileset) { struct buffer_head *bh = NULL; struct udf_vds_record vds[VDS_POS_LENGTH]; struct generic_desc *gd; struct volDescPtr *vdp; int done=0; int i,j; uint32_t vdsn; uint16_t ident; long next_s = 0, next_e = 0; memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); /* Read the main descriptor sequence */ for (;(!done && block <= lastblock); block++) { bh = udf_read_tagged(sb, block, block, &ident); if (!bh) break; /* Process each descriptor (ISO 13346 3/8.3-8.4) */ gd = (struct generic_desc *)bh->b_data; vdsn = le32_to_cpu(gd->volDescSeqNum); switch (ident) { case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */ if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum) { vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn; vds[VDS_POS_PRIMARY_VOL_DESC].block = block; } break; case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */ if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum) { vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn; vds[VDS_POS_VOL_DESC_PTR].block = block; vdp = (struct volDescPtr *)bh->b_data; next_s = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation); next_e = le32_to_cpu(vdp->nextVolDescSeqExt.extLength); next_e = next_e >> sb->s_blocksize_bits; next_e += next_s; } break; case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum) { vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn; vds[VDS_POS_IMP_USE_VOL_DESC].block = block; } break; case TAG_IDENT_PD: /* ISO 13346 3/10.5 */ if (!vds[VDS_POS_PARTITION_DESC].block) vds[VDS_POS_PARTITION_DESC].block = block; break; case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */ if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum) { vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn; vds[VDS_POS_LOGICAL_VOL_DESC].block = block; } break; case TAG_IDENT_USD: /* ISO 13346 3/10.8 */ if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum) { vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn; vds[VDS_POS_UNALLOC_SPACE_DESC].block = block; } break; case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ vds[VDS_POS_TERMINATING_DESC].block = block; if (next_e) { block = next_s; lastblock = next_e; next_s = next_e = 0; } else done = 1; break; } udf_release_data(bh); } for (i=0; ib_data; if (ident == TAG_IDENT_PD) udf_load_partdesc(sb, bh2); udf_release_data(bh2); } } udf_release_data(bh); } } return 0; } /* * udf_check_valid() */ static int udf_check_valid(struct super_block *sb, int novrs, int silent) { long block; if (novrs) { udf_debug("Validity check skipped because of novrs option\n"); return 0; } /* Check that it is NSR02 compliant */ /* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */ else if ((block = udf_vrs(sb, silent)) == -1) { udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n"); if (!UDF_SB_LASTBLOCK(sb)) UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); return 0; } else return !block; } static int udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) { struct anchorVolDescPtr *anchor; uint16_t ident; struct buffer_head *bh; long main_s, main_e, reserve_s, reserve_e; int i, j; if (!sb) return 1; for (i = 0; i < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); i++) { if (UDF_SB_ANCHOR(sb)[i] && (bh = udf_read_tagged(sb, UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) { anchor = (struct anchorVolDescPtr *)bh->b_data; /* Locate the main sequence */ main_s = le32_to_cpu( anchor->mainVolDescSeqExt.extLocation ); main_e = le32_to_cpu( anchor->mainVolDescSeqExt.extLength ); main_e = main_e >> sb->s_blocksize_bits; main_e += main_s; /* Locate the reserve sequence */ reserve_s = le32_to_cpu(anchor->reserveVolDescSeqExt.extLocation); reserve_e = le32_to_cpu(anchor->reserveVolDescSeqExt.extLength); reserve_e = reserve_e >> sb->s_blocksize_bits; reserve_e += reserve_s; udf_release_data(bh); /* Process the main & reserve sequences */ /* responsible for finding the PartitionDesc(s) */ if (!(udf_process_sequence(sb, main_s, main_e, fileset) && udf_process_sequence(sb, reserve_s, reserve_e, fileset))) { break; } } } if (i == ARRAY_SIZE(UDF_SB_ANCHOR(sb))) { udf_debug("No Anchor block found\n"); return 1; } else udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]); for (i=0; ii_size - 36) >> 2; } else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20) { struct buffer_head *bh = NULL; uint32_t pos; pos = udf_block_map(UDF_SB_VAT(sb), 0); bh = sb_bread(sb, pos); UDF_SB_TYPEVIRT(sb,i).s_start_offset = le16_to_cpu(((struct virtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) + udf_ext0_offset(UDF_SB_VAT(sb)); UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - UDF_SB_TYPEVIRT(sb,i).s_start_offset) >> 2; udf_release_data(bh); } UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0); UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum); } } } return 0; } static void udf_open_lvid(struct super_block *sb) { if (UDF_SB_LVIDBH(sb)) { int i; kernel_timestamp cpu_time; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN; UDF_SB_LVID(sb)->descTag.descCRC = cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); UDF_SB_LVID(sb)->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) UDF_SB_LVID(sb)->descTag.tagChecksum += ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } } static void udf_close_lvid(struct super_block *sb) { if (UDF_SB_LVIDBH(sb) && UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN) { int i; kernel_timestamp cpu_time; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); if (UDF_MAX_WRITE_VERSION > le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev)) UDF_SB_LVIDIU(sb)->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION); if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev)) UDF_SB_LVIDIU(sb)->minUDFReadRev = cpu_to_le16(UDF_SB_UDFREV(sb)); if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev)) UDF_SB_LVIDIU(sb)->minUDFWriteRev = cpu_to_le16(UDF_SB_UDFREV(sb)); UDF_SB_LVID(sb)->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE); UDF_SB_LVID(sb)->descTag.descCRC = cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); UDF_SB_LVID(sb)->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) UDF_SB_LVID(sb)->descTag.tagChecksum += ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } } /* * udf_read_super * * PURPOSE * Complete the specified super block. * * PRE-CONDITIONS * sb Pointer to superblock to complete - never NULL. * sb->s_dev Device to read suberblock from. * options Pointer to mount options. * silent Silent flag. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_fill_super(struct super_block *sb, void *options, int silent) { int i; struct inode *inode=NULL; struct udf_options uopt; kernel_lb_addr rootdir, fileset; struct udf_sb_info *sbi; uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT); uopt.uid = -1; uopt.gid = -1; uopt.umask = 0; sbi = kmalloc(sizeof(struct udf_sb_info), GFP_KERNEL); if (!sbi) return -ENOMEM; sb->s_fs_info = sbi; memset(UDF_SB(sb), 0x00, sizeof(struct udf_sb_info)); mutex_init(&sbi->s_alloc_mutex); if (!udf_parse_options((char *)options, &uopt)) goto error_out; if (uopt.flags & (1 << UDF_FLAG_UTF8) && uopt.flags & (1 << UDF_FLAG_NLS_MAP)) { udf_error(sb, "udf_read_super", "utf8 cannot be combined with iocharset\n"); goto error_out; } #ifdef CONFIG_UDF_NLS if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map) { uopt.nls_map = load_nls_default(); if (!uopt.nls_map) uopt.flags &= ~(1 << UDF_FLAG_NLS_MAP); else udf_debug("Using default NLS map\n"); } #endif if (!(uopt.flags & (1 << UDF_FLAG_NLS_MAP))) uopt.flags |= (1 << UDF_FLAG_UTF8); fileset.logicalBlockNum = 0xFFFFFFFF; fileset.partitionReferenceNum = 0xFFFF; UDF_SB(sb)->s_flags = uopt.flags; UDF_SB(sb)->s_uid = uopt.uid; UDF_SB(sb)->s_gid = uopt.gid; UDF_SB(sb)->s_umask = uopt.umask; UDF_SB(sb)->s_nls_map = uopt.nls_map; /* Set the block size for all transfers */ if (!udf_set_blocksize(sb, uopt.blocksize)) goto error_out; if ( uopt.session == 0xFFFFFFFF ) UDF_SB_SESSION(sb) = udf_get_last_session(sb); else UDF_SB_SESSION(sb) = uopt.session; udf_debug("Multi-session=%d\n", UDF_SB_SESSION(sb)); UDF_SB_LASTBLOCK(sb) = uopt.lastblock; UDF_SB_ANCHOR(sb)[0] = UDF_SB_ANCHOR(sb)[1] = 0; UDF_SB_ANCHOR(sb)[2] = uopt.anchor; UDF_SB_ANCHOR(sb)[3] = 256; if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */ { printk("UDF-fs: No VRS found\n"); goto error_out; } udf_find_anchor(sb); /* Fill in the rest of the superblock */ sb->s_op = &udf_sb_ops; sb->dq_op = NULL; sb->s_dirt = 0; sb->s_magic = UDF_SUPER_MAGIC; sb->s_time_gran = 1000; if (udf_load_partition(sb, &fileset)) { printk("UDF-fs: No partition found (1)\n"); goto error_out; } udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb)); if ( UDF_SB_LVIDBH(sb) ) { uint16_t minUDFReadRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev); uint16_t minUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); /* uint16_t maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */ if (minUDFReadRev > UDF_MAX_READ_VERSION) { printk("UDF-fs: minUDFReadRev=%x (max is %x)\n", le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev), UDF_MAX_READ_VERSION); goto error_out; } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) { sb->s_flags |= MS_RDONLY; } UDF_SB_UDFREV(sb) = minUDFWriteRev; if (minUDFReadRev >= UDF_VERS_USE_EXTENDED_FE) UDF_SET_FLAG(sb, UDF_FLAG_USE_EXTENDED_FE); if (minUDFReadRev >= UDF_VERS_USE_STREAMS) UDF_SET_FLAG(sb, UDF_FLAG_USE_STREAMS); } if ( !UDF_SB_NUMPARTS(sb) ) { printk("UDF-fs: No partition found (2)\n"); goto error_out; } if ( udf_find_fileset(sb, &fileset, &rootdir) ) { printk("UDF-fs: No fileset found\n"); goto error_out; } if (!silent) { kernel_timestamp ts; udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb)); udf_info("UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", UDFFS_VERSION, UDFFS_DATE, UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); } if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); /* Assign the root inode */ /* assign inodes by physical block number */ /* perhaps it's not extensible enough, but for now ... */ inode = udf_iget(sb, rootdir); if (!inode) { printk("UDF-fs: Error in udf_iget, block=%d, partition=%d\n", rootdir.logicalBlockNum, rootdir.partitionReferenceNum); goto error_out; } /* Allocate a dentry for the root inode */ sb->s_root = d_alloc_root(inode); if (!sb->s_root) { printk("UDF-fs: Couldn't allocate root dentry\n"); iput(inode); goto error_out; } sb->s_maxbytes = 1<<30; return 0; error_out: if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); if (UDF_SB_NUMPARTS(sb)) { if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) { for (i=0; i<4; i++) udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); } } #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) unload_nls(UDF_SB(sb)->s_nls_map); #endif if (!(sb->s_flags & MS_RDONLY)) udf_close_lvid(sb); udf_release_data(UDF_SB_LVIDBH(sb)); UDF_SB_FREE(sb); kfree(sbi); sb->s_fs_info = NULL; return -EINVAL; } void udf_error(struct super_block *sb, const char *function, const char *fmt, ...) { va_list args; if (!(sb->s_flags & MS_RDONLY)) { /* mark sb error */ sb->s_dirt = 1; } va_start(args, fmt); vsprintf(error_buf, fmt, args); va_end(args); printk (KERN_CRIT "UDF-fs error (device %s): %s: %s\n", sb->s_id, function, error_buf); } void udf_warning(struct super_block *sb, const char *function, const char *fmt, ...) { va_list args; va_start (args, fmt); vsprintf(error_buf, fmt, args); va_end(args); printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n", sb->s_id, function, error_buf); } /* * udf_put_super * * PURPOSE * Prepare for destruction of the superblock. * * DESCRIPTION * Called before the filesystem is unmounted. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static void udf_put_super(struct super_block *sb) { int i; if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); if (UDF_SB_NUMPARTS(sb)) { if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) { for (i=0; i<4; i++) udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); } } #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) unload_nls(UDF_SB(sb)->s_nls_map); #endif if (!(sb->s_flags & MS_RDONLY)) udf_close_lvid(sb); udf_release_data(UDF_SB_LVIDBH(sb)); UDF_SB_FREE(sb); kfree(sb->s_fs_info); sb->s_fs_info = NULL; } /* * udf_stat_fs * * PURPOSE * Return info about the filesystem. * * DESCRIPTION * Called by sys_statfs() * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_statfs(struct super_block *sb, struct kstatfs *buf) { buf->f_type = UDF_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)); buf->f_bfree = udf_count_free(sb); buf->f_bavail = buf->f_bfree; buf->f_files = (UDF_SB_LVIDBH(sb) ? (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree; buf->f_ffree = buf->f_bfree; /* __kernel_fsid_t f_fsid */ buf->f_namelen = UDF_NAME_LEN-2; return 0; } static unsigned char udf_bitmap_lookup[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; static unsigned int udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) { struct buffer_head *bh = NULL; unsigned int accum = 0; int index; int block = 0, newblock; kernel_lb_addr loc; uint32_t bytes; uint8_t value; uint8_t *ptr; uint16_t ident; struct spaceBitmapDesc *bm; lock_kernel(); loc.logicalBlockNum = bitmap->s_extPosition; loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_read_ptagged(sb, loc, 0, &ident); if (!bh) { printk(KERN_ERR "udf: udf_count_free failed\n"); goto out; } else if (ident != TAG_IDENT_SBD) { udf_release_data(bh); printk(KERN_ERR "udf: udf_count_free failed\n"); goto out; } bm = (struct spaceBitmapDesc *)bh->b_data; bytes = le32_to_cpu(bm->numOfBytes); index = sizeof(struct spaceBitmapDesc); /* offset in first block only */ ptr = (uint8_t *)bh->b_data; while ( bytes > 0 ) { while ((bytes > 0) && (index < sb->s_blocksize)) { value = ptr[index]; accum += udf_bitmap_lookup[ value & 0x0f ]; accum += udf_bitmap_lookup[ value >> 4 ]; index++; bytes--; } if ( bytes ) { udf_release_data(bh); newblock = udf_get_lb_pblock(sb, loc, ++block); bh = udf_tread(sb, newblock); if (!bh) { udf_debug("read failed\n"); goto out; } index = 0; ptr = (uint8_t *)bh->b_data; } } udf_release_data(bh); out: unlock_kernel(); return accum; } static unsigned int udf_count_free_table(struct super_block *sb, struct inode * table) { unsigned int accum = 0; uint32_t extoffset, elen; kernel_lb_addr bloc, eloc; int8_t etype; struct buffer_head *bh = NULL; lock_kernel(); bloc = UDF_I_LOCATION(table); extoffset = sizeof(struct unallocSpaceEntry); while ((etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { accum += (elen >> table->i_sb->s_blocksize_bits); } udf_release_data(bh); unlock_kernel(); return accum; } static unsigned int udf_count_free(struct super_block *sb) { unsigned int accum = 0; if (UDF_SB_LVIDBH(sb)) { if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) { accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]); if (accum == 0xFFFFFFFF) accum = 0; } } if (accum) return accum; if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) { accum += udf_count_free_bitmap(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap); } if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) { accum += udf_count_free_bitmap(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap); } if (accum) return accum; if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) { accum += udf_count_free_table(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); } if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) { accum += udf_count_free_table(sb, UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); } return accum; } linux-2.6.17/fs/udf/.svn/text-base/partition.c.svn-base0000444000000000000000000001406110574207644021315 0ustar rootroot/* * partition.c * * PURPOSE * Partition handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema * * HISTORY * * 12/06/98 blf Created file. * */ #include "udfdecl.h" #include "udf_sb.h" #include "udf_i.h" #include #include #include #include #include inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { if (partition >= UDF_SB_NUMPARTS(sb)) { udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n", block, partition, offset); return 0xFFFFFFFF; } if (UDF_SB_PARTFUNC(sb, partition)) return UDF_SB_PARTFUNC(sb, partition)(sb, block, partition, offset); else return UDF_SB_PARTROOT(sb, partition) + block + offset; } uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { struct buffer_head *bh = NULL; uint32_t newblock; uint32_t index; uint32_t loc; index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(uint32_t); if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) { udf_debug("Trying to access block beyond end of VAT (%d max %d)\n", block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries); return 0xFFFFFFFF; } if (block >= index) { block -= index; newblock = 1 + (block / (sb->s_blocksize / sizeof(uint32_t))); index = block % (sb->s_blocksize / sizeof(uint32_t)); } else { newblock = 0; index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(uint32_t) + block; } loc = udf_block_map(UDF_SB_VAT(sb), newblock); if (!(bh = sb_bread(sb, loc))) { udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n", sb, block, partition, loc, index); return 0xFFFFFFFF; } loc = le32_to_cpu(((__le32 *)bh->b_data)[index]); udf_release_data(bh); if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) { udf_debug("recursive call to udf_get_pblock!\n"); return 0xFFFFFFFF; } return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset); } inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { return udf_get_pblock_virt15(sb, block, partition, offset); } uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) { int i; struct sparingTable *st = NULL; uint32_t packet = (block + offset) & ~(UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1); for (i=0; i<4; i++) { if (UDF_SB_TYPESPAR(sb,partition).s_spar_map[i] != NULL) { st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,partition).s_spar_map[i]->b_data; break; } } if (st) { for (i=0; ireallocationTableLen); i++) { if (le32_to_cpu(st->mapEntry[i].origLocation) >= 0xFFFFFFF0) break; else if (le32_to_cpu(st->mapEntry[i].origLocation) == packet) { return le32_to_cpu(st->mapEntry[i].mappedLocation) + ((block + offset) & (UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1)); } else if (le32_to_cpu(st->mapEntry[i].origLocation) > packet) break; } } return UDF_SB_PARTROOT(sb,partition) + block + offset; } int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) { struct udf_sparing_data *sdata; struct sparingTable *st = NULL; struct sparingEntry mapEntry; uint32_t packet; int i, j, k, l; for (i=0; i UDF_SB_PARTROOT(sb,i) && old_block < UDF_SB_PARTROOT(sb,i) + UDF_SB_PARTLEN(sb,i)) { sdata = &UDF_SB_TYPESPAR(sb,i); packet = (old_block - UDF_SB_PARTROOT(sb,i)) & ~(sdata->s_packet_len - 1); for (j=0; j<4; j++) { if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; break; } } if (!st) return 1; for (k=0; kreallocationTableLen); k++) { if (le32_to_cpu(st->mapEntry[k].origLocation) == 0xFFFFFFFF) { for (; j<4; j++) { if (sdata->s_spar_map[j]) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; st->mapEntry[k].origLocation = cpu_to_le32(packet); udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); mark_buffer_dirty(sdata->s_spar_map[j]); } } *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; } else if (le32_to_cpu(st->mapEntry[k].origLocation) == packet) { *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; } else if (le32_to_cpu(st->mapEntry[k].origLocation) > packet) break; } for (l=k; lreallocationTableLen); l++) { if (le32_to_cpu(st->mapEntry[l].origLocation) == 0xFFFFFFFF) { for (; j<4; j++) { if (sdata->s_spar_map[j]) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; mapEntry = st->mapEntry[l]; mapEntry.origLocation = cpu_to_le32(packet); memmove(&st->mapEntry[k+1], &st->mapEntry[k], (l-k)*sizeof(struct sparingEntry)); st->mapEntry[k] = mapEntry; udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); mark_buffer_dirty(sdata->s_spar_map[j]); } } *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; } } return 1; } } if (i == UDF_SB_NUMPARTS(sb)) { /* outside of partitions */ /* for now, fail =) */ return 1; } return 0; } linux-2.6.17/fs/udf/.svn/text-base/inode.c.svn-base0000444000000000000000000015356010574207644020412 0ustar rootroot/* * inode.c * * PURPOSE * Inode handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 10/04/98 dgb Added rudimentary directory functions * 10/07/98 Fully working udf_block_map! It works! * 11/25/98 bmap altered to better support extents * 12/06/98 blf partition support in udf_iget, udf_block_map and udf_read_inode * 12/12/98 rewrote udf_block_map to handle next extents and descs across * block boundaries (which is not actually allowed) * 12/20/98 added support for strategy 4096 * 03/07/99 rewrote udf_block_map (again) * New funcs, inode_bmap, udf_next_aext * 04/19/99 Support for writing device EA's for major/minor # */ #include "udfdecl.h" #include #include #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" MODULE_AUTHOR("Ben Fennema"); MODULE_DESCRIPTION("Universal Disk Format Filesystem"); MODULE_LICENSE("GPL"); #define EXTENT_MERGE_SIZE 5 static mode_t udf_convert_permissions(struct fileEntry *); static int udf_update_inode(struct inode *, int); static void udf_fill_inode(struct inode *, struct buffer_head *); static struct buffer_head *inode_getblk(struct inode *, long, int *, long *, int *); static int8_t udf_insert_aext(struct inode *, kernel_lb_addr, int, kernel_lb_addr, uint32_t, struct buffer_head *); static void udf_split_extents(struct inode *, int *, int, int, kernel_long_ad [EXTENT_MERGE_SIZE], int *); static void udf_prealloc_extents(struct inode *, int, int, kernel_long_ad [EXTENT_MERGE_SIZE], int *); static void udf_merge_extents(struct inode *, kernel_long_ad [EXTENT_MERGE_SIZE], int *); static void udf_update_extents(struct inode *, kernel_long_ad [EXTENT_MERGE_SIZE], int, int, kernel_lb_addr, uint32_t, struct buffer_head **); static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); /* * udf_delete_inode * * PURPOSE * Clean-up before the specified inode is destroyed. * * DESCRIPTION * This routine is called when the kernel destroys an inode structure * ie. when iput() finds i_count == 0. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. * * Called at the last iput() if i_nlink is zero. */ void udf_delete_inode(struct inode * inode) { truncate_inode_pages(&inode->i_data, 0); if (is_bad_inode(inode)) goto no_delete; inode->i_size = 0; udf_truncate(inode); lock_kernel(); udf_update_inode(inode, IS_SYNC(inode)); udf_free_inode(inode); unlock_kernel(); return; no_delete: clear_inode(inode); } void udf_clear_inode(struct inode *inode) { if (!(inode->i_sb->s_flags & MS_RDONLY)) { lock_kernel(); udf_discard_prealloc(inode); unlock_kernel(); } kfree(UDF_I_DATA(inode)); UDF_I_DATA(inode) = NULL; } static int udf_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page, udf_get_block, wbc); } static int udf_readpage(struct file *file, struct page *page) { return block_read_full_page(page, udf_get_block); } static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { return block_prepare_write(page, from, to, udf_get_block); } static sector_t udf_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,udf_get_block); } struct address_space_operations udf_aops = { .readpage = udf_readpage, .writepage = udf_writepage, .sync_page = block_sync_page, .prepare_write = udf_prepare_write, .commit_write = generic_commit_write, .bmap = udf_bmap, }; void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) { struct page *page; char *kaddr; struct writeback_control udf_wbc = { .sync_mode = WB_SYNC_NONE, .nr_to_write = 1, }; /* from now on we have normal address_space methods */ inode->i_data.a_ops = &udf_aops; if (!UDF_I_LENALLOC(inode)) { if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; mark_inode_dirty(inode); return; } page = grab_cache_page(inode->i_mapping, 0); BUG_ON(!PageLocked(page)); if (!PageUptodate(page)) { kaddr = kmap(page); memset(kaddr + UDF_I_LENALLOC(inode), 0x00, PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode)); memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), UDF_I_LENALLOC(inode)); flush_dcache_page(page); SetPageUptodate(page); kunmap(page); } memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0x00, UDF_I_LENALLOC(inode)); UDF_I_LENALLOC(inode) = 0; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; inode->i_data.a_ops->writepage(page, &udf_wbc); page_cache_release(page); mark_inode_dirty(inode); } struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err) { int newblock; struct buffer_head *sbh = NULL, *dbh = NULL; kernel_lb_addr bloc, eloc; uint32_t elen, extoffset; uint8_t alloctype; struct udf_fileident_bh sfibh, dfibh; loff_t f_pos = udf_ext0_offset(inode) >> 2; int size = (udf_ext0_offset(inode) + inode->i_size) >> 2; struct fileIdentDesc cfi, *sfi, *dfi; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) alloctype = ICBTAG_FLAG_AD_SHORT; else alloctype = ICBTAG_FLAG_AD_LONG; if (!inode->i_size) { UDF_I_ALLOCTYPE(inode) = alloctype; mark_inode_dirty(inode); return NULL; } /* alloc block, and copy data to it */ *block = udf_new_block(inode->i_sb, inode, UDF_I_LOCATION(inode).partitionReferenceNum, UDF_I_LOCATION(inode).logicalBlockNum, err); if (!(*block)) return NULL; newblock = udf_get_pblock(inode->i_sb, *block, UDF_I_LOCATION(inode).partitionReferenceNum, 0); if (!newblock) return NULL; dbh = udf_tgetblk(inode->i_sb, newblock); if (!dbh) return NULL; lock_buffer(dbh); memset(dbh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(dbh); unlock_buffer(dbh); mark_buffer_dirty_inode(dbh, inode); sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2; sbh = sfibh.sbh = sfibh.ebh = NULL; dfibh.soffset = dfibh.eoffset = 0; dfibh.sbh = dfibh.ebh = dbh; while ( (f_pos < size) ) { UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL, NULL, NULL); if (!sfi) { udf_release_data(dbh); return NULL; } UDF_I_ALLOCTYPE(inode) = alloctype; sfi->descTag.tagLocation = cpu_to_le32(*block); dfibh.soffset = dfibh.eoffset; dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset); if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse, sfi->fileIdent + le16_to_cpu(sfi->lengthOfImpUse))) { UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; udf_release_data(dbh); return NULL; } } mark_buffer_dirty_inode(dbh, inode); memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0, UDF_I_LENALLOC(inode)); UDF_I_LENALLOC(inode) = 0; bloc = UDF_I_LOCATION(inode); eloc.logicalBlockNum = *block; eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; elen = inode->i_size; UDF_I_LENEXTENTS(inode) = elen; extoffset = udf_file_entry_alloc_offset(inode); udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &sbh, 0); /* UniqueID stuff */ udf_release_data(sbh); mark_inode_dirty(inode); return dbh; } static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) { int err, new; struct buffer_head *bh; unsigned long phys; if (!create) { phys = udf_block_map(inode, block); if (phys) map_bh(bh_result, inode->i_sb, phys); return 0; } err = -EIO; new = 0; bh = NULL; lock_kernel(); if (block < 0) goto abort_negative; if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1) { UDF_I_NEXT_ALLOC_BLOCK(inode) ++; UDF_I_NEXT_ALLOC_GOAL(inode) ++; } err = 0; bh = inode_getblk(inode, block, &err, &phys, &new); BUG_ON(bh); if (err) goto abort; BUG_ON(!phys); if (new) set_buffer_new(bh_result); map_bh(bh_result, inode->i_sb, phys); abort: unlock_kernel(); return err; abort_negative: udf_warning(inode->i_sb, "udf_get_block", "block < 0"); goto abort; } static struct buffer_head * udf_getblk(struct inode *inode, long block, int create, int *err) { struct buffer_head dummy; dummy.b_state = 0; dummy.b_blocknr = -1000; *err = udf_get_block(inode, block, &dummy, create); if (!*err && buffer_mapped(&dummy)) { struct buffer_head *bh; bh = sb_getblk(inode->i_sb, dummy.b_blocknr); if (buffer_new(&dummy)) { lock_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(bh); unlock_buffer(bh); mark_buffer_dirty_inode(bh, inode); } return bh; } return NULL; } static struct buffer_head * inode_getblk(struct inode * inode, long block, int *err, long *phys, int *new) { struct buffer_head *pbh = NULL, *cbh = NULL, *nbh = NULL, *result = NULL; kernel_long_ad laarr[EXTENT_MERGE_SIZE]; uint32_t pextoffset = 0, cextoffset = 0, nextoffset = 0; int count = 0, startnum = 0, endnum = 0; uint32_t elen = 0; kernel_lb_addr eloc, pbloc, cbloc, nbloc; int c = 1; uint64_t lbcount = 0, b_off = 0; uint32_t newblocknum, newblock, offset = 0; int8_t etype; int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum; char lastblock = 0; pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset(inode); b_off = (uint64_t)block << inode->i_sb->s_blocksize_bits; pbloc = cbloc = nbloc = UDF_I_LOCATION(inode); /* find the extent which contains the block we are looking for. alternate between laarr[0] and laarr[1] for locations of the current extent, and the previous extent */ do { if (pbh != cbh) { udf_release_data(pbh); atomic_inc(&cbh->b_count); pbh = cbh; } if (cbh != nbh) { udf_release_data(cbh); atomic_inc(&nbh->b_count); cbh = nbh; } lbcount += elen; pbloc = cbloc; cbloc = nbloc; pextoffset = cextoffset; cextoffset = nextoffset; if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) == -1) break; c = !c; laarr[c].extLength = (etype << 30) | elen; laarr[c].extLocation = eloc; if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) pgoal = eloc.logicalBlockNum + ((elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); count ++; } while (lbcount + elen <= b_off); b_off -= lbcount; offset = b_off >> inode->i_sb->s_blocksize_bits; /* if the extent is allocated and recorded, return the block if the extent is not a multiple of the blocksize, round up */ if (etype == (EXT_RECORDED_ALLOCATED >> 30)) { if (elen & (inode->i_sb->s_blocksize - 1)) { elen = EXT_RECORDED_ALLOCATED | ((elen + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); etype = udf_write_aext(inode, nbloc, &cextoffset, eloc, elen, nbh, 1); } udf_release_data(pbh); udf_release_data(cbh); udf_release_data(nbh); newblock = udf_get_lb_pblock(inode->i_sb, eloc, offset); *phys = newblock; return NULL; } if (etype == -1) { endnum = startnum = ((count > 1) ? 1 : count); if (laarr[c].extLength & (inode->i_sb->s_blocksize - 1)) { laarr[c].extLength = (laarr[c].extLength & UDF_EXTENT_FLAG_MASK) | (((laarr[c].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); UDF_I_LENEXTENTS(inode) = (UDF_I_LENEXTENTS(inode) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1); } c = !c; laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | ((offset + 1) << inode->i_sb->s_blocksize_bits); memset(&laarr[c].extLocation, 0x00, sizeof(kernel_lb_addr)); count ++; endnum ++; lastblock = 1; } else endnum = startnum = ((count > 2) ? 2 : count); /* if the current extent is in position 0, swap it with the previous */ if (!c && count != 1) { laarr[2] = laarr[0]; laarr[0] = laarr[1]; laarr[1] = laarr[2]; c = 1; } /* if the current block is located in a extent, read the next extent */ if (etype != -1) { if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 0)) != -1) { laarr[c+1].extLength = (etype << 30) | elen; laarr[c+1].extLocation = eloc; count ++; startnum ++; endnum ++; } else lastblock = 1; } udf_release_data(cbh); udf_release_data(nbh); /* if the current extent is not recorded but allocated, get the block in the extent corresponding to the requested block */ if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) newblocknum = laarr[c].extLocation.logicalBlockNum + offset; else /* otherwise, allocate a new block */ { if (UDF_I_NEXT_ALLOC_BLOCK(inode) == block) goal = UDF_I_NEXT_ALLOC_GOAL(inode); if (!goal) { if (!(goal = pgoal)) goal = UDF_I_LOCATION(inode).logicalBlockNum + 1; } if (!(newblocknum = udf_new_block(inode->i_sb, inode, UDF_I_LOCATION(inode).partitionReferenceNum, goal, err))) { udf_release_data(pbh); *err = -ENOSPC; return NULL; } UDF_I_LENEXTENTS(inode) += inode->i_sb->s_blocksize; } /* if the extent the requsted block is located in contains multiple blocks, split the extent into at most three extents. blocks prior to requested block, requested block, and blocks after requested block */ udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum); #ifdef UDF_PREALLOCATE /* preallocate blocks */ udf_prealloc_extents(inode, c, lastblock, laarr, &endnum); #endif /* merge any continuous blocks in laarr */ udf_merge_extents(inode, laarr, &endnum); /* write back the new extents, inserting new extents if the new number of extents is greater than the old number, and deleting extents if the new number of extents is less than the old number */ udf_update_extents(inode, laarr, startnum, endnum, pbloc, pextoffset, &pbh); udf_release_data(pbh); if (!(newblock = udf_get_pblock(inode->i_sb, newblocknum, UDF_I_LOCATION(inode).partitionReferenceNum, 0))) { return NULL; } *phys = newblock; *err = 0; *new = 1; UDF_I_NEXT_ALLOC_BLOCK(inode) = block; UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum; inode->i_ctime = current_fs_time(inode->i_sb); if (IS_SYNC(inode)) udf_sync_inode(inode); else mark_inode_dirty(inode); return result; } static void udf_split_extents(struct inode *inode, int *c, int offset, int newblocknum, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) { if ((laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30) || (laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { int curr = *c; int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; int8_t etype = (laarr[curr].extLength >> 30); if (blen == 1) ; else if (!offset || blen == offset + 1) { laarr[curr+2] = laarr[curr+1]; laarr[curr+1] = laarr[curr]; } else { laarr[curr+3] = laarr[curr+1]; laarr[curr+2] = laarr[curr+1] = laarr[curr]; } if (offset) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, laarr[curr].extLocation, 0, offset); laarr[curr].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | (offset << inode->i_sb->s_blocksize_bits); laarr[curr].extLocation.logicalBlockNum = 0; laarr[curr].extLocation.partitionReferenceNum = 0; } else laarr[curr].extLength = (etype << 30) | (offset << inode->i_sb->s_blocksize_bits); curr ++; (*c) ++; (*endnum) ++; } laarr[curr].extLocation.logicalBlockNum = newblocknum; if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) laarr[curr].extLocation.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; laarr[curr].extLength = EXT_RECORDED_ALLOCATED | inode->i_sb->s_blocksize; curr ++; if (blen != offset + 1) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) laarr[curr].extLocation.logicalBlockNum += (offset + 1); laarr[curr].extLength = (etype << 30) | ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits); curr ++; (*endnum) ++; } } } static void udf_prealloc_extents(struct inode *inode, int c, int lastblock, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) { int start, length = 0, currlength = 0, i; if (*endnum >= (c+1)) { if (!lastblock) return; else start = c; } else { if ((laarr[c+1].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { start = c+1; length = currlength = (((laarr[c+1].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); } else start = c; } for (i=start+1; i<=*endnum; i++) { if (i == *endnum) { if (lastblock) length += UDF_DEFAULT_PREALLOC_BLOCKS; } else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) length += (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); else break; } if (length) { int next = laarr[start].extLocation.logicalBlockNum + (((laarr[start].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); int numalloc = udf_prealloc_blocks(inode->i_sb, inode, laarr[start].extLocation.partitionReferenceNum, next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length : UDF_DEFAULT_PREALLOC_BLOCKS) - currlength); if (numalloc) { if (start == (c+1)) laarr[start].extLength += (numalloc << inode->i_sb->s_blocksize_bits); else { memmove(&laarr[c+2], &laarr[c+1], sizeof(long_ad) * (*endnum - (c+1))); (*endnum) ++; laarr[c+1].extLocation.logicalBlockNum = next; laarr[c+1].extLocation.partitionReferenceNum = laarr[c].extLocation.partitionReferenceNum; laarr[c+1].extLength = EXT_NOT_RECORDED_ALLOCATED | (numalloc << inode->i_sb->s_blocksize_bits); start = c+1; } for (i=start+1; numalloc && i<*endnum; i++) { int elen = ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; if (elen > numalloc) { laarr[i].extLength -= (numalloc << inode->i_sb->s_blocksize_bits); numalloc = 0; } else { numalloc -= elen; if (*endnum > (i+1)) memmove(&laarr[i], &laarr[i+1], sizeof(long_ad) * (*endnum - (i+1))); i --; (*endnum) --; } } UDF_I_LENEXTENTS(inode) += numalloc << inode->i_sb->s_blocksize_bits; } } } static void udf_merge_extents(struct inode *inode, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) { int i; for (i=0; i<(*endnum-1); i++) { if ((laarr[i].extLength >> 30) == (laarr[i+1].extLength >> 30)) { if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) || ((laarr[i+1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) == (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits))) { if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { laarr[i+1].extLength = (laarr[i+1].extLength - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; laarr[i+1].extLocation.logicalBlockNum = laarr[i].extLocation.logicalBlockNum + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) >> inode->i_sb->s_blocksize_bits); } else { laarr[i].extLength = laarr[i+1].extLength + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); if (*endnum > (i+2)) memmove(&laarr[i+1], &laarr[i+2], sizeof(long_ad) * (*endnum - (i+2))); i --; (*endnum) --; } } } else if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) && ((laarr[i+1].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) { udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); laarr[i].extLocation.logicalBlockNum = 0; laarr[i].extLocation.partitionReferenceNum = 0; if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { laarr[i+1].extLength = (laarr[i+1].extLength - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; } else { laarr[i].extLength = laarr[i+1].extLength + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); if (*endnum > (i+2)) memmove(&laarr[i+1], &laarr[i+2], sizeof(long_ad) * (*endnum - (i+2))); i --; (*endnum) --; } } else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); laarr[i].extLocation.logicalBlockNum = 0; laarr[i].extLocation.partitionReferenceNum = 0; laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) | EXT_NOT_RECORDED_NOT_ALLOCATED; } } } static void udf_update_extents(struct inode *inode, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum, kernel_lb_addr pbloc, uint32_t pextoffset, struct buffer_head **pbh) { int start = 0, i; kernel_lb_addr tmploc; uint32_t tmplen; if (startnum > endnum) { for (i=0; i<(startnum-endnum); i++) { udf_delete_aext(inode, pbloc, pextoffset, laarr[i].extLocation, laarr[i].extLength, *pbh); } } else if (startnum < endnum) { for (i=0; i<(endnum-startnum); i++) { udf_insert_aext(inode, pbloc, pextoffset, laarr[i].extLocation, laarr[i].extLength, *pbh); udf_next_aext(inode, &pbloc, &pextoffset, &laarr[i].extLocation, &laarr[i].extLength, pbh, 1); start ++; } } for (i=start; ii_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; lock_kernel(); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + inode->i_size)) { udf_expand_file_adinicb(inode, inode->i_size, &err); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { inode->i_size = UDF_I_LENALLOC(inode); unlock_kernel(); return; } else udf_truncate_extents(inode); } else { offset = inode->i_size & (inode->i_sb->s_blocksize - 1); memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, 0x00, inode->i_sb->s_blocksize - offset - udf_file_entry_alloc_offset(inode)); UDF_I_LENALLOC(inode) = inode->i_size; } } else { block_truncate_page(inode->i_mapping, inode->i_size, udf_get_block); udf_truncate_extents(inode); } inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb); if (IS_SYNC(inode)) udf_sync_inode (inode); else mark_inode_dirty(inode); unlock_kernel(); } static void __udf_read_inode(struct inode *inode) { struct buffer_head *bh = NULL; struct fileEntry *fe; uint16_t ident; /* * Set defaults, but the inode is still incomplete! * Note: get_new_inode() sets the following on a new inode: * i_sb = sb * i_no = ino * i_flags = sb->s_flags * i_state = 0 * clean_inode(): zero fills and sets * i_count = 1 * i_nlink = 1 * i_op = NULL; */ inode->i_blksize = PAGE_SIZE; bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident); if (!bh) { printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed !bh\n", inode->i_ino); make_bad_inode(inode); return; } if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE && ident != TAG_IDENT_USE) { printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed ident=%d\n", inode->i_ino, ident); udf_release_data(bh); make_bad_inode(inode); return; } fe = (struct fileEntry *)bh->b_data; if (le16_to_cpu(fe->icbTag.strategyType) == 4096) { struct buffer_head *ibh = NULL, *nbh = NULL; struct indirectEntry *ie; ibh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 1, &ident); if (ident == TAG_IDENT_IE) { if (ibh) { kernel_lb_addr loc; ie = (struct indirectEntry *)ibh->b_data; loc = lelb_to_cpu(ie->indirectICB.extLocation); if (ie->indirectICB.extLength && (nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident))) { if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE) { memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(kernel_lb_addr)); udf_release_data(bh); udf_release_data(ibh); udf_release_data(nbh); __udf_read_inode(inode); return; } else { udf_release_data(nbh); udf_release_data(ibh); } } else udf_release_data(ibh); } } else udf_release_data(ibh); } else if (le16_to_cpu(fe->icbTag.strategyType) != 4) { printk(KERN_ERR "udf: unsupported strategy type: %d\n", le16_to_cpu(fe->icbTag.strategyType)); udf_release_data(bh); make_bad_inode(inode); return; } udf_fill_inode(inode, bh); udf_release_data(bh); } static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) { struct fileEntry *fe; struct extendedFileEntry *efe; time_t convtime; long convtime_usec; int offset; fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; if (le16_to_cpu(fe->icbTag.strategyType) == 4) UDF_I_STRAT4096(inode) = 0; else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */ UDF_I_STRAT4096(inode) = 1; UDF_I_ALLOCTYPE(inode) = le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK; UDF_I_UNIQUE(inode) = 0; UDF_I_LENEATTR(inode) = 0; UDF_I_LENEXTENTS(inode) = 0; UDF_I_LENALLOC(inode) = 0; UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; UDF_I_NEXT_ALLOC_GOAL(inode) = 0; if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE) { UDF_I_EFE(inode) = 1; UDF_I_USE(inode) = 0; UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct extendedFileEntry), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); } else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE) { UDF_I_EFE(inode) = 0; UDF_I_USE(inode) = 0; UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct fileEntry), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); } else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) { UDF_I_EFE(inode) = 0; UDF_I_USE(inode) = 1; UDF_I_LENALLOC(inode) = le32_to_cpu( ((struct unallocSpaceEntry *)bh->b_data)->lengthAllocDescs); UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry), GFP_KERNEL); memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct unallocSpaceEntry), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); return; } inode->i_uid = le32_to_cpu(fe->uid); if (inode->i_uid == -1 || UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_IGNORE)) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; inode->i_gid = le32_to_cpu(fe->gid); if (inode->i_gid == -1 || UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_IGNORE)) inode->i_gid = UDF_SB(inode->i_sb)->s_gid; inode->i_nlink = le16_to_cpu(fe->fileLinkCount); if (!inode->i_nlink) inode->i_nlink = 1; inode->i_size = le64_to_cpu(fe->informationLength); UDF_I_LENEXTENTS(inode) = inode->i_size; inode->i_mode = udf_convert_permissions(fe); inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask; if (UDF_I_EFE(inode) == 0) { inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) << (inode->i_sb->s_blocksize_bits - 9); if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->accessTime)) ) { inode->i_atime.tv_sec = convtime; inode->i_atime.tv_nsec = convtime_usec * 1000; } else { inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->modificationTime)) ) { inode->i_mtime.tv_sec = convtime; inode->i_mtime.tv_nsec = convtime_usec * 1000; } else { inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->attrTime)) ) { inode->i_ctime.tv_sec = convtime; inode->i_ctime.tv_nsec = convtime_usec * 1000; } else { inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } UDF_I_UNIQUE(inode) = le64_to_cpu(fe->uniqueID); UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr); UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs); offset = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode); } else { inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << (inode->i_sb->s_blocksize_bits - 9); if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->accessTime)) ) { inode->i_atime.tv_sec = convtime; inode->i_atime.tv_nsec = convtime_usec * 1000; } else { inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->modificationTime)) ) { inode->i_mtime.tv_sec = convtime; inode->i_mtime.tv_nsec = convtime_usec * 1000; } else { inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->createTime)) ) { UDF_I_CRTIME(inode).tv_sec = convtime; UDF_I_CRTIME(inode).tv_nsec = convtime_usec * 1000; } else { UDF_I_CRTIME(inode) = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->attrTime)) ) { inode->i_ctime.tv_sec = convtime; inode->i_ctime.tv_nsec = convtime_usec * 1000; } else { inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } UDF_I_UNIQUE(inode) = le64_to_cpu(efe->uniqueID); UDF_I_LENEATTR(inode) = le32_to_cpu(efe->lengthExtendedAttr); UDF_I_LENALLOC(inode) = le32_to_cpu(efe->lengthAllocDescs); offset = sizeof(struct extendedFileEntry) + UDF_I_LENEATTR(inode); } switch (fe->icbTag.fileType) { case ICBTAG_FILE_TYPE_DIRECTORY: { inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; inode->i_mode |= S_IFDIR; inode->i_nlink ++; break; } case ICBTAG_FILE_TYPE_REALTIME: case ICBTAG_FILE_TYPE_REGULAR: case ICBTAG_FILE_TYPE_UNDEF: { if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) inode->i_data.a_ops = &udf_adinicb_aops; else inode->i_data.a_ops = &udf_aops; inode->i_op = &udf_file_inode_operations; inode->i_fop = &udf_file_operations; inode->i_mode |= S_IFREG; break; } case ICBTAG_FILE_TYPE_BLOCK: { inode->i_mode |= S_IFBLK; break; } case ICBTAG_FILE_TYPE_CHAR: { inode->i_mode |= S_IFCHR; break; } case ICBTAG_FILE_TYPE_FIFO: { init_special_inode(inode, inode->i_mode | S_IFIFO, 0); break; } case ICBTAG_FILE_TYPE_SOCKET: { init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); break; } case ICBTAG_FILE_TYPE_SYMLINK: { inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; inode->i_mode = S_IFLNK|S_IRWXUGO; break; } default: { printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n", inode->i_ino, fe->icbTag.fileType); make_bad_inode(inode); return; } } if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { struct deviceSpec *dsea = (struct deviceSpec *) udf_get_extendedattr(inode, 12, 1); if (dsea) { init_special_inode(inode, inode->i_mode, MKDEV( le32_to_cpu(dsea->majorDeviceIdent), le32_to_cpu(dsea->minorDeviceIdent))); /* Developer ID ??? */ } else { make_bad_inode(inode); } } } static mode_t udf_convert_permissions(struct fileEntry *fe) { mode_t mode; uint32_t permissions; uint32_t flags; permissions = le32_to_cpu(fe->permissions); flags = le16_to_cpu(fe->icbTag.flags); mode = (( permissions ) & S_IRWXO) | (( permissions >> 2 ) & S_IRWXG) | (( permissions >> 4 ) & S_IRWXU) | (( flags & ICBTAG_FLAG_SETUID) ? S_ISUID : 0) | (( flags & ICBTAG_FLAG_SETGID) ? S_ISGID : 0) | (( flags & ICBTAG_FLAG_STICKY) ? S_ISVTX : 0); return mode; } /* * udf_write_inode * * PURPOSE * Write out the specified inode. * * DESCRIPTION * This routine is called whenever an inode is synced. * Currently this routine is just a placeholder. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_write_inode(struct inode * inode, int sync) { int ret; lock_kernel(); ret = udf_update_inode(inode, sync); unlock_kernel(); return ret; } int udf_sync_inode(struct inode * inode) { return udf_update_inode(inode, 1); } static int udf_update_inode(struct inode *inode, int do_sync) { struct buffer_head *bh = NULL; struct fileEntry *fe; struct extendedFileEntry *efe; uint32_t udfperms; uint16_t icbflags; uint16_t crclen; int i; kernel_timestamp cpu_time; int err = 0; bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0)); if (!bh) { udf_debug("bread failure\n"); return -EIO; } memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) { struct unallocSpaceEntry *use = (struct unallocSpaceEntry *)bh->b_data; use->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); memcpy(bh->b_data + sizeof(struct unallocSpaceEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); crclen = sizeof(struct unallocSpaceEntry) + UDF_I_LENALLOC(inode) - sizeof(tag); use->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); use->descTag.descCRCLength = cpu_to_le16(crclen); use->descTag.descCRC = cpu_to_le16(udf_crc((char *)use + sizeof(tag), crclen, 0)); use->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) use->descTag.tagChecksum += ((uint8_t *)&(use->descTag))[i]; mark_buffer_dirty(bh); udf_release_data(bh); return err; } if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_FORGET)) fe->uid = cpu_to_le32(-1); else fe->uid = cpu_to_le32(inode->i_uid); if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_FORGET)) fe->gid = cpu_to_le32(-1); else fe->gid = cpu_to_le32(inode->i_gid); udfperms = ((inode->i_mode & S_IRWXO) ) | ((inode->i_mode & S_IRWXG) << 2) | ((inode->i_mode & S_IRWXU) << 4); udfperms |= (le32_to_cpu(fe->permissions) & (FE_PERM_O_DELETE | FE_PERM_O_CHATTR | FE_PERM_G_DELETE | FE_PERM_G_CHATTR | FE_PERM_U_DELETE | FE_PERM_U_CHATTR)); fe->permissions = cpu_to_le32(udfperms); if (S_ISDIR(inode->i_mode)) fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1); else fe->fileLinkCount = cpu_to_le16(inode->i_nlink); fe->informationLength = cpu_to_le64(inode->i_size); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { regid *eid; struct deviceSpec *dsea = (struct deviceSpec *) udf_get_extendedattr(inode, 12, 1); if (!dsea) { dsea = (struct deviceSpec *) udf_add_extendedattr(inode, sizeof(struct deviceSpec) + sizeof(regid), 12, 0x3); dsea->attrType = cpu_to_le32(12); dsea->attrSubtype = 1; dsea->attrLength = cpu_to_le32(sizeof(struct deviceSpec) + sizeof(regid)); dsea->impUseLength = cpu_to_le32(sizeof(regid)); } eid = (regid *)dsea->impUse; memset(eid, 0, sizeof(regid)); strcpy(eid->ident, UDF_ID_DEVELOPER); eid->identSuffix[0] = UDF_OS_CLASS_UNIX; eid->identSuffix[1] = UDF_OS_ID_LINUX; dsea->majorDeviceIdent = cpu_to_le32(imajor(inode)); dsea->minorDeviceIdent = cpu_to_le32(iminor(inode)); } if (UDF_I_EFE(inode) == 0) { memcpy(bh->b_data + sizeof(struct fileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); fe->logicalBlocksRecorded = cpu_to_le64( (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); if (udf_time_to_stamp(&cpu_time, inode->i_atime)) fe->accessTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) fe->modificationTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) fe->attrTime = cpu_to_lets(cpu_time); memset(&(fe->impIdent), 0, sizeof(regid)); strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER); fe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; fe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; fe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); fe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); fe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); fe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_FE); crclen = sizeof(struct fileEntry); } else { memcpy(bh->b_data + sizeof(struct extendedFileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); efe->objectSize = cpu_to_le64(inode->i_size); efe->logicalBlocksRecorded = cpu_to_le64( (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); if (UDF_I_CRTIME(inode).tv_sec > inode->i_atime.tv_sec || (UDF_I_CRTIME(inode).tv_sec == inode->i_atime.tv_sec && UDF_I_CRTIME(inode).tv_nsec > inode->i_atime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_atime; } if (UDF_I_CRTIME(inode).tv_sec > inode->i_mtime.tv_sec || (UDF_I_CRTIME(inode).tv_sec == inode->i_mtime.tv_sec && UDF_I_CRTIME(inode).tv_nsec > inode->i_mtime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_mtime; } if (UDF_I_CRTIME(inode).tv_sec > inode->i_ctime.tv_sec || (UDF_I_CRTIME(inode).tv_sec == inode->i_ctime.tv_sec && UDF_I_CRTIME(inode).tv_nsec > inode->i_ctime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_ctime; } if (udf_time_to_stamp(&cpu_time, inode->i_atime)) efe->accessTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) efe->modificationTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, UDF_I_CRTIME(inode))) efe->createTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) efe->attrTime = cpu_to_lets(cpu_time); memset(&(efe->impIdent), 0, sizeof(regid)); strcpy(efe->impIdent.ident, UDF_ID_DEVELOPER); efe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; efe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; efe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); efe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); efe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); efe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EFE); crclen = sizeof(struct extendedFileEntry); } if (UDF_I_STRAT4096(inode)) { fe->icbTag.strategyType = cpu_to_le16(4096); fe->icbTag.strategyParameter = cpu_to_le16(1); fe->icbTag.numEntries = cpu_to_le16(2); } else { fe->icbTag.strategyType = cpu_to_le16(4); fe->icbTag.numEntries = cpu_to_le16(1); } if (S_ISDIR(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_DIRECTORY; else if (S_ISREG(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_REGULAR; else if (S_ISLNK(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_SYMLINK; else if (S_ISBLK(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_BLOCK; else if (S_ISCHR(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_CHAR; else if (S_ISFIFO(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_FIFO; else if (S_ISSOCK(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_SOCKET; icbflags = UDF_I_ALLOCTYPE(inode) | ((inode->i_mode & S_ISUID) ? ICBTAG_FLAG_SETUID : 0) | ((inode->i_mode & S_ISGID) ? ICBTAG_FLAG_SETGID : 0) | ((inode->i_mode & S_ISVTX) ? ICBTAG_FLAG_STICKY : 0) | (le16_to_cpu(fe->icbTag.flags) & ~(ICBTAG_FLAG_AD_MASK | ICBTAG_FLAG_SETUID | ICBTAG_FLAG_SETGID | ICBTAG_FLAG_STICKY)); fe->icbTag.flags = cpu_to_le16(icbflags); if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) fe->descTag.descVersion = cpu_to_le16(3); else fe->descTag.descVersion = cpu_to_le16(2); fe->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); fe->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); crclen += UDF_I_LENEATTR(inode) + UDF_I_LENALLOC(inode) - sizeof(tag); fe->descTag.descCRCLength = cpu_to_le16(crclen); fe->descTag.descCRC = cpu_to_le16(udf_crc((char *)fe + sizeof(tag), crclen, 0)); fe->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) fe->descTag.tagChecksum += ((uint8_t *)&(fe->descTag))[i]; /* write the data blocks */ mark_buffer_dirty(bh); if (do_sync) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { printk("IO error syncing udf inode [%s:%08lx]\n", inode->i_sb->s_id, inode->i_ino); err = -EIO; } } udf_release_data(bh); return err; } struct inode * udf_iget(struct super_block *sb, kernel_lb_addr ino) { unsigned long block = udf_get_lb_pblock(sb, ino, 0); struct inode *inode = iget_locked(sb, block); if (!inode) return NULL; if (inode->i_state & I_NEW) { memcpy(&UDF_I_LOCATION(inode), &ino, sizeof(kernel_lb_addr)); __udf_read_inode(inode); unlock_new_inode(inode); } if (is_bad_inode(inode)) goto out_iput; if (ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum)) { udf_debug("block=%d, partition=%d out of range\n", ino.logicalBlockNum, ino.partitionReferenceNum); make_bad_inode(inode); goto out_iput; } return inode; out_iput: iput(inode); return NULL; } int8_t udf_add_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, kernel_lb_addr eloc, uint32_t elen, struct buffer_head **bh, int inc) { int adsize; short_ad *sad = NULL; long_ad *lad = NULL; struct allocExtDesc *aed; int8_t etype; uint8_t *ptr; if (!*bh) ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); else ptr = (*bh)->b_data + *extoffset; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else return -1; if (*extoffset + (2 * adsize) > inode->i_sb->s_blocksize) { char *sptr, *dptr; struct buffer_head *nbh; int err, loffset; kernel_lb_addr obloc = *bloc; if (!(bloc->logicalBlockNum = udf_new_block(inode->i_sb, NULL, obloc.partitionReferenceNum, obloc.logicalBlockNum, &err))) { return -1; } if (!(nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb, *bloc, 0)))) { return -1; } lock_buffer(nbh); memset(nbh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(nbh); unlock_buffer(nbh); mark_buffer_dirty_inode(nbh, inode); aed = (struct allocExtDesc *)(nbh->b_data); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum); if (*extoffset + adsize > inode->i_sb->s_blocksize) { loffset = *extoffset; aed->lengthAllocDescs = cpu_to_le32(adsize); sptr = ptr - adsize; dptr = nbh->b_data + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); *extoffset = sizeof(struct allocExtDesc) + adsize; } else { loffset = *extoffset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); sptr = ptr; *extoffset = sizeof(struct allocExtDesc); if (*bh) { aed = (struct allocExtDesc *)(*bh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); } else { UDF_I_LENALLOC(inode) += adsize; mark_inode_dirty(inode); } } if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1, bloc->logicalBlockNum, sizeof(tag)); else udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1, bloc->logicalBlockNum, sizeof(tag)); switch (UDF_I_ALLOCTYPE(inode)) { case ICBTAG_FLAG_AD_SHORT: { sad = (short_ad *)sptr; sad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | inode->i_sb->s_blocksize); sad->extPosition = cpu_to_le32(bloc->logicalBlockNum); break; } case ICBTAG_FLAG_AD_LONG: { lad = (long_ad *)sptr; lad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | inode->i_sb->s_blocksize); lad->extLocation = cpu_to_lelb(*bloc); memset(lad->impUse, 0x00, sizeof(lad->impUse)); break; } } if (*bh) { if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((*bh)->b_data, loffset); else udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(*bh, inode); udf_release_data(*bh); } else mark_inode_dirty(inode); *bh = nbh; } etype = udf_write_aext(inode, *bloc, extoffset, eloc, elen, *bh, inc); if (!*bh) { UDF_I_LENALLOC(inode) += adsize; mark_inode_dirty(inode); } else { aed = (struct allocExtDesc *)(*bh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize)); else udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(*bh, inode); } return etype; } int8_t udf_write_aext(struct inode *inode, kernel_lb_addr bloc, int *extoffset, kernel_lb_addr eloc, uint32_t elen, struct buffer_head *bh, int inc) { int adsize; uint8_t *ptr; if (!bh) ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); else { ptr = bh->b_data + *extoffset; atomic_inc(&bh->b_count); } switch (UDF_I_ALLOCTYPE(inode)) { case ICBTAG_FLAG_AD_SHORT: { short_ad *sad = (short_ad *)ptr; sad->extLength = cpu_to_le32(elen); sad->extPosition = cpu_to_le32(eloc.logicalBlockNum); adsize = sizeof(short_ad); break; } case ICBTAG_FLAG_AD_LONG: { long_ad *lad = (long_ad *)ptr; lad->extLength = cpu_to_le32(elen); lad->extLocation = cpu_to_lelb(eloc); memset(lad->impUse, 0x00, sizeof(lad->impUse)); adsize = sizeof(long_ad); break; } default: return -1; } if (bh) { if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) { struct allocExtDesc *aed = (struct allocExtDesc *)(bh)->b_data; udf_update_tag((bh)->b_data, le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct allocExtDesc)); } mark_buffer_dirty_inode(bh, inode); udf_release_data(bh); } else mark_inode_dirty(inode); if (inc) *extoffset += adsize; return (elen >> 30); } int8_t udf_next_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc) { int8_t etype; while ((etype = udf_current_aext(inode, bloc, extoffset, eloc, elen, bh, inc)) == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { *bloc = *eloc; *extoffset = sizeof(struct allocExtDesc); udf_release_data(*bh); if (!(*bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, *bloc, 0)))) { udf_debug("reading block %d failed!\n", udf_get_lb_pblock(inode->i_sb, *bloc, 0)); return -1; } } return etype; } int8_t udf_current_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc) { int alen; int8_t etype; uint8_t *ptr; if (!*bh) { if (!(*extoffset)) *extoffset = udf_file_entry_alloc_offset(inode); ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); alen = udf_file_entry_alloc_offset(inode) + UDF_I_LENALLOC(inode); } else { if (!(*extoffset)) *extoffset = sizeof(struct allocExtDesc); ptr = (*bh)->b_data + *extoffset; alen = sizeof(struct allocExtDesc) + le32_to_cpu(((struct allocExtDesc *)(*bh)->b_data)->lengthAllocDescs); } switch (UDF_I_ALLOCTYPE(inode)) { case ICBTAG_FLAG_AD_SHORT: { short_ad *sad; if (!(sad = udf_get_fileshortad(ptr, alen, extoffset, inc))) return -1; etype = le32_to_cpu(sad->extLength) >> 30; eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; break; } case ICBTAG_FLAG_AD_LONG: { long_ad *lad; if (!(lad = udf_get_filelongad(ptr, alen, extoffset, inc))) return -1; etype = le32_to_cpu(lad->extLength) >> 30; *eloc = lelb_to_cpu(lad->extLocation); *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; break; } default: { udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); return -1; } } return etype; } static int8_t udf_insert_aext(struct inode *inode, kernel_lb_addr bloc, int extoffset, kernel_lb_addr neloc, uint32_t nelen, struct buffer_head *bh) { kernel_lb_addr oeloc; uint32_t oelen; int8_t etype; if (bh) atomic_inc(&bh->b_count); while ((etype = udf_next_aext(inode, &bloc, &extoffset, &oeloc, &oelen, &bh, 0)) != -1) { udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1); neloc = oeloc; nelen = (etype << 30) | oelen; } udf_add_aext(inode, &bloc, &extoffset, neloc, nelen, &bh, 1); udf_release_data(bh); return (nelen >> 30); } int8_t udf_delete_aext(struct inode *inode, kernel_lb_addr nbloc, int nextoffset, kernel_lb_addr eloc, uint32_t elen, struct buffer_head *nbh) { struct buffer_head *obh; kernel_lb_addr obloc; int oextoffset, adsize; int8_t etype; struct allocExtDesc *aed; if (nbh) { atomic_inc(&nbh->b_count); atomic_inc(&nbh->b_count); } if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else adsize = 0; obh = nbh; obloc = nbloc; oextoffset = nextoffset; if (udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1) == -1) return -1; while ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) { udf_write_aext(inode, obloc, &oextoffset, eloc, (etype << 30) | elen, obh, 1); if (obh != nbh) { obloc = nbloc; udf_release_data(obh); atomic_inc(&nbh->b_count); obh = nbh; oextoffset = nextoffset - adsize; } } memset(&eloc, 0x00, sizeof(kernel_lb_addr)); elen = 0; if (nbh != obh) { udf_free_blocks(inode->i_sb, inode, nbloc, 0, 1); udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); if (!obh) { UDF_I_LENALLOC(inode) -= (adsize * 2); mark_inode_dirty(inode); } else { aed = (struct allocExtDesc *)(obh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize)); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((obh)->b_data, oextoffset - (2*adsize)); else udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(obh, inode); } } else { udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); if (!obh) { UDF_I_LENALLOC(inode) -= adsize; mark_inode_dirty(inode); } else { aed = (struct allocExtDesc *)(obh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((obh)->b_data, oextoffset - adsize); else udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(obh, inode); } } udf_release_data(nbh); udf_release_data(obh); return (elen >> 30); } int8_t inode_bmap(struct inode *inode, int block, kernel_lb_addr *bloc, uint32_t *extoffset, kernel_lb_addr *eloc, uint32_t *elen, uint32_t *offset, struct buffer_head **bh) { uint64_t lbcount = 0, bcount = (uint64_t)block << inode->i_sb->s_blocksize_bits; int8_t etype; if (block < 0) { printk(KERN_ERR "udf: inode_bmap: block < 0\n"); return -1; } *extoffset = 0; *elen = 0; *bloc = UDF_I_LOCATION(inode); do { if ((etype = udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, 1)) == -1) { *offset = bcount - lbcount; UDF_I_LENEXTENTS(inode) = lbcount; return -1; } lbcount += *elen; } while (lbcount <= bcount); *offset = bcount + *elen - lbcount; return etype; } long udf_block_map(struct inode *inode, long block) { kernel_lb_addr eloc, bloc; uint32_t offset, extoffset, elen; struct buffer_head *bh = NULL; int ret; lock_kernel(); if (inode_bmap(inode, block, &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) ret = udf_get_lb_pblock(inode->i_sb, eloc, offset >> inode->i_sb->s_blocksize_bits); else ret = 0; unlock_kernel(); udf_release_data(bh); if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV)) return udf_fixed_to_variable(ret); else return ret; } linux-2.6.17/fs/udf/.svn/text-base/udfend.h.svn-base0000444000000000000000000000355210574207644020561 0ustar rootroot#ifndef __UDF_ENDIAN_H #define __UDF_ENDIAN_H #include #include static inline kernel_lb_addr lelb_to_cpu(lb_addr in) { kernel_lb_addr out; out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum); out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum); return out; } static inline lb_addr cpu_to_lelb(kernel_lb_addr in) { lb_addr out; out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum); out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum); return out; } static inline kernel_timestamp lets_to_cpu(timestamp in) { kernel_timestamp out; memcpy(&out, &in, sizeof(timestamp)); out.typeAndTimezone = le16_to_cpu(in.typeAndTimezone); out.year = le16_to_cpu(in.year); return out; } static inline short_ad lesa_to_cpu(short_ad in) { short_ad out; out.extLength = le32_to_cpu(in.extLength); out.extPosition = le32_to_cpu(in.extPosition); return out; } static inline short_ad cpu_to_lesa(short_ad in) { short_ad out; out.extLength = cpu_to_le32(in.extLength); out.extPosition = cpu_to_le32(in.extPosition); return out; } static inline kernel_long_ad lela_to_cpu(long_ad in) { kernel_long_ad out; out.extLength = le32_to_cpu(in.extLength); out.extLocation = lelb_to_cpu(in.extLocation); return out; } static inline long_ad cpu_to_lela(kernel_long_ad in) { long_ad out; out.extLength = cpu_to_le32(in.extLength); out.extLocation = cpu_to_lelb(in.extLocation); return out; } static inline kernel_extent_ad leea_to_cpu(extent_ad in) { kernel_extent_ad out; out.extLength = le32_to_cpu(in.extLength); out.extLocation = le32_to_cpu(in.extLocation); return out; } static inline timestamp cpu_to_lets(kernel_timestamp in) { timestamp out; memcpy(&out, &in, sizeof(timestamp)); out.typeAndTimezone = cpu_to_le16(in.typeAndTimezone); out.year = cpu_to_le16(in.year); return out; } #endif /* __UDF_ENDIAN_H */ linux-2.6.17/fs/udf/.svn/text-base/directory.c.svn-base0000444000000000000000000001667110574207644021321 0ustar rootroot/* * directory.c * * PURPOSE * Directory related functions * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. */ #include "udfdecl.h" #include "udf_i.h" #include #include #include #if 0 static uint8_t * udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size, kernel_lb_addr fe_loc, int *pos, int *offset, struct buffer_head **bh, int *error) { int loffset = *offset; int block; uint8_t *ad; int remainder; *error = 0; ad = (uint8_t *)(*bh)->b_data + *offset; *offset += ad_size; if (!ad) { udf_release_data(*bh); *error = 1; return NULL; } if (*offset == dir->i_sb->s_blocksize) { udf_release_data(*bh); block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); if (!block) return NULL; if (!(*bh = udf_tread(dir->i_sb, block))) return NULL; } else if (*offset > dir->i_sb->s_blocksize) { ad = tmpad; remainder = dir->i_sb->s_blocksize - loffset; memcpy((uint8_t *)ad, (*bh)->b_data + loffset, remainder); udf_release_data(*bh); block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); if (!block) return NULL; if (!((*bh) = udf_tread(dir->i_sb, block))) return NULL; memcpy((uint8_t *)ad + remainder, (*bh)->b_data, ad_size - remainder); *offset = ad_size - remainder; } return ad; } #endif struct fileIdentDesc * udf_fileident_read(struct inode *dir, loff_t *nf_pos, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi, kernel_lb_addr *bloc, uint32_t *extoffset, kernel_lb_addr *eloc, uint32_t *elen, uint32_t *offset, struct buffer_head **bh) { struct fileIdentDesc *fi; int i, num, block; struct buffer_head * tmp, * bha[16]; fibh->soffset = fibh->eoffset; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { fi = udf_get_fileident(UDF_I_DATA(dir) - (UDF_I_EFE(dir) ? sizeof(struct extendedFileEntry) : sizeof(struct fileEntry)), dir->i_sb->s_blocksize, &(fibh->eoffset)); if (!fi) return NULL; *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); return fi; } if (fibh->eoffset == dir->i_sb->s_blocksize) { int lextoffset = *extoffset; if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) != (EXT_RECORDED_ALLOCATED >> 30)) { return NULL; } block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); (*offset) ++; if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) *offset = 0; else *extoffset = lextoffset; udf_release_data(fibh->sbh); if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) return NULL; fibh->soffset = fibh->eoffset = 0; if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) { i = 16 >> (dir->i_sb->s_blocksize_bits - 9); if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits)) i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset; for (num=0; i>0; i--) { block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i); tmp = udf_tgetblk(dir->i_sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } if (num) { ll_rw_block(READA, num, bha); for (i=0; isbh != fibh->ebh) { udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; } fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize, &(fibh->eoffset)); if (!fi) return NULL; *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); if (fibh->eoffset <= dir->i_sb->s_blocksize) { memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); } else if (fibh->eoffset > dir->i_sb->s_blocksize) { int lextoffset = *extoffset; if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) != (EXT_RECORDED_ALLOCATED >> 30)) { return NULL; } block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); (*offset) ++; if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) *offset = 0; else *extoffset = lextoffset; fibh->soffset -= dir->i_sb->s_blocksize; fibh->eoffset -= dir->i_sb->s_blocksize; if (!(fibh->ebh = udf_tread(dir->i_sb, block))) return NULL; if (sizeof(struct fileIdentDesc) > - fibh->soffset) { int fi_len; memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset); memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data, sizeof(struct fileIdentDesc) + fibh->soffset); fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent + le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3; *nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2); fibh->eoffset = fibh->soffset + fi_len; } else { memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); } } return fi; } struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset) { struct fileIdentDesc *fi; int lengthThisIdent; uint8_t * ptr; int padlen; if ( (!buffer) || (!offset) ) { udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset); return NULL; } ptr = buffer; if ( (*offset > 0) && (*offset < bufsize) ) { ptr += *offset; } fi=(struct fileIdentDesc *)ptr; if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID) { udf_debug("0x%x != TAG_IDENT_FID\n", le16_to_cpu(fi->descTag.tagIdent)); udf_debug("offset: %u sizeof: %lu bufsize: %u\n", *offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize); return NULL; } if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize ) { lengthThisIdent = sizeof(struct fileIdentDesc); } else lengthThisIdent = sizeof(struct fileIdentDesc) + fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse); /* we need to figure padding, too! */ padlen = lengthThisIdent % UDF_NAME_PAD; if (padlen) lengthThisIdent += (UDF_NAME_PAD - padlen); *offset = *offset + lengthThisIdent; return fi; } #if 0 static extent_ad * udf_get_fileextent(void * buffer, int bufsize, int * offset) { extent_ad * ext; struct fileEntry *fe; uint8_t * ptr; if ( (!buffer) || (!offset) ) { printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n"); return NULL; } fe = (struct fileEntry *)buffer; if ( le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE ) { udf_debug("0x%x != TAG_IDENT_FE\n", le16_to_cpu(fe->descTag.tagIdent)); return NULL; } ptr=(uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr); if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) ) { ptr += *offset; } ext = (extent_ad *)ptr; *offset = *offset + sizeof(extent_ad); return ext; } #endif short_ad * udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc) { short_ad *sa; if ( (!ptr) || (!offset) ) { printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n"); return NULL; } if ( (*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset) ) return NULL; else if ((sa = (short_ad *)ptr)->extLength == 0) return NULL; if (inc) *offset += sizeof(short_ad); return sa; } long_ad * udf_get_filelongad(uint8_t *ptr, int maxoffset, int * offset, int inc) { long_ad *la; if ( (!ptr) || (!offset) ) { printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n"); return NULL; } if ( (*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset) ) return NULL; else if ((la = (long_ad *)ptr)->extLength == 0) return NULL; if (inc) *offset += sizeof(long_ad); return la; } linux-2.6.17/fs/udf/.svn/text-base/symlink.c.svn-base0000444000000000000000000000450710574207644020776 0ustar rootroot/* * symlink.c * * PURPOSE * Symlink handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY * * 04/16/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include #include #include #include #include #include #include #include #include "udf_i.h" static void udf_pc_to_char(struct super_block *sb, char *from, int fromlen, char *to) { struct pathComponent *pc; int elen = 0; char *p = to; while (elen < fromlen) { pc = (struct pathComponent *)(from + elen); switch (pc->componentType) { case 1: if (pc->lengthComponentIdent == 0) { p = to; *p++ = '/'; } break; case 3: memcpy(p, "../", 3); p += 3; break; case 4: memcpy(p, "./", 2); p += 2; /* that would be . - just ignore */ break; case 5: p += udf_get_filename(sb, pc->componentIdent, p, pc->lengthComponentIdent); *p++ = '/'; break; } elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; } if (p > to+1) p[-1] = '\0'; else p[0] = '\0'; } static int udf_symlink_filler(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; struct buffer_head *bh = NULL; char *symlink; int err = -EIO; char *p = kmap(page); lock_kernel(); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) symlink = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); else { bh = sb_bread(inode->i_sb, udf_block_map(inode, 0)); if (!bh) goto out; symlink = bh->b_data; } udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p); udf_release_data(bh); unlock_kernel(); SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; out: unlock_kernel(); SetPageError(page); kunmap(page); unlock_page(page); return err; } /* * symlinks can't do much... */ struct address_space_operations udf_symlink_aops = { .readpage = udf_symlink_filler, }; linux-2.6.17/fs/udf/.svn/text-base/balloc.c.svn-base0000444000000000000000000006031210574207644020540 0ustar rootroot/* * balloc.c * * PURPOSE * Block allocation handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2001 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY * * 02/24/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include "udf_i.h" #include "udf_sb.h" #define udf_clear_bit(nr,addr) ext2_clear_bit(nr,addr) #define udf_set_bit(nr,addr) ext2_set_bit(nr,addr) #define udf_test_bit(nr, addr) ext2_test_bit(nr, addr) #define udf_find_first_one_bit(addr, size) find_first_one_bit(addr, size) #define udf_find_next_one_bit(addr, size, offset) find_next_one_bit(addr, size, offset) #define leBPL_to_cpup(x) leNUM_to_cpup(BITS_PER_LONG, x) #define leNUM_to_cpup(x,y) xleNUM_to_cpup(x,y) #define xleNUM_to_cpup(x,y) (le ## x ## _to_cpup(y)) #define uintBPL_t uint(BITS_PER_LONG) #define uint(x) xuint(x) #define xuint(x) __le ## x static inline int find_next_one_bit (void * addr, int size, int offset) { uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG); int result = offset & ~(BITS_PER_LONG-1); unsigned long tmp; if (offset >= size) return size; size -= result; offset &= (BITS_PER_LONG-1); if (offset) { tmp = leBPL_to_cpup(p++); tmp &= ~0UL << offset; if (size < BITS_PER_LONG) goto found_first; if (tmp) goto found_middle; size -= BITS_PER_LONG; result += BITS_PER_LONG; } while (size & ~(BITS_PER_LONG-1)) { if ((tmp = leBPL_to_cpup(p++))) goto found_middle; result += BITS_PER_LONG; size -= BITS_PER_LONG; } if (!size) return result; tmp = leBPL_to_cpup(p); found_first: tmp &= ~0UL >> (BITS_PER_LONG-size); found_middle: return result + ffz(~tmp); } #define find_first_one_bit(addr, size)\ find_next_one_bit((addr), (size), 0) static int read_block_bitmap(struct super_block * sb, struct udf_bitmap *bitmap, unsigned int block, unsigned long bitmap_nr) { struct buffer_head *bh = NULL; int retval = 0; kernel_lb_addr loc; loc.logicalBlockNum = bitmap->s_extPosition; loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block)); if (!bh) { retval = -EIO; } bitmap->s_block_bitmap[bitmap_nr] = bh; return retval; } static int __load_block_bitmap(struct super_block * sb, struct udf_bitmap *bitmap, unsigned int block_group) { int retval = 0; int nr_groups = bitmap->s_nr_groups; if (block_group >= nr_groups) { udf_debug("block_group (%d) > nr_groups (%d)\n", block_group, nr_groups); } if (bitmap->s_block_bitmap[block_group]) return block_group; else { retval = read_block_bitmap(sb, bitmap, block_group, block_group); if (retval < 0) return retval; return block_group; } } static inline int load_block_bitmap(struct super_block * sb, struct udf_bitmap *bitmap, unsigned int block_group) { int slot; slot = __load_block_bitmap(sb, bitmap, block_group); if (slot < 0) return slot; if (!bitmap->s_block_bitmap[slot]) return -EIO; return slot; } static void udf_bitmap_free_blocks(struct super_block * sb, struct inode * inode, struct udf_bitmap *bitmap, kernel_lb_addr bloc, uint32_t offset, uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); struct buffer_head * bh = NULL; unsigned long block; unsigned long block_group; unsigned long bit; unsigned long i; int bitmap_nr; unsigned long overflow; mutex_lock(&sbi->s_alloc_mutex); if (bloc.logicalBlockNum < 0 || (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) { udf_debug("%d < %d || %d + %d > %d\n", bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); goto error_return; } block = bloc.logicalBlockNum + offset + (sizeof(struct spaceBitmapDesc) << 3); do_more: overflow = 0; block_group = block >> (sb->s_blocksize_bits + 3); bit = block % (sb->s_blocksize << 3); /* * Check to see if we are freeing blocks across a group boundary. */ if (bit + count > (sb->s_blocksize << 3)) { overflow = bit + count - (sb->s_blocksize << 3); count -= overflow; } bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; for (i=0; i < count; i++) { if (udf_set_bit(bit + i, bh->b_data)) { udf_debug("bit %ld already set\n", bit + i); udf_debug("byte=%2x\n", ((char *)bh->b_data)[(bit + i) >> 3]); } else { if (inode) DQUOT_FREE_BLOCK(inode, 1); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+1); } } } mark_buffer_dirty(bh); if (overflow) { block += count; count = overflow; goto do_more; } error_return: sb->s_dirt = 1; if (UDF_SB_LVIDBH(sb)) mark_buffer_dirty(UDF_SB_LVIDBH(sb)); mutex_unlock(&sbi->s_alloc_mutex); return; } static int udf_bitmap_prealloc_blocks(struct super_block * sb, struct inode * inode, struct udf_bitmap *bitmap, uint16_t partition, uint32_t first_block, uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; int bit, block, block_group, group_start; int nr_groups, bitmap_nr; struct buffer_head *bh; mutex_lock(&sbi->s_alloc_mutex); if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) goto out; if (first_block + block_count > UDF_SB_PARTLEN(sb, partition)) block_count = UDF_SB_PARTLEN(sb, partition) - first_block; repeat: nr_groups = (UDF_SB_PARTLEN(sb, partition) + (sizeof(struct spaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); block = first_block + (sizeof(struct spaceBitmapDesc) << 3); block_group = block >> (sb->s_blocksize_bits + 3); group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto out; bh = bitmap->s_block_bitmap[bitmap_nr]; bit = block % (sb->s_blocksize << 3); while (bit < (sb->s_blocksize << 3) && block_count > 0) { if (!udf_test_bit(bit, bh->b_data)) goto out; else if (DQUOT_PREALLOC_BLOCK(inode, 1)) goto out; else if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); DQUOT_FREE_BLOCK(inode, 1); goto out; } block_count --; alloc_count ++; bit ++; block ++; } mark_buffer_dirty(bh); if (block_count > 0) goto repeat; out: if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); return alloc_count; } static int udf_bitmap_new_block(struct super_block * sb, struct inode * inode, struct udf_bitmap *bitmap, uint16_t partition, uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); int newbit, bit=0, block, block_group, group_start; int end_goal, nr_groups, bitmap_nr, i; struct buffer_head *bh = NULL; char *ptr; int newblock = 0; *err = -ENOSPC; mutex_lock(&sbi->s_alloc_mutex); repeat: if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition)) goal = 0; nr_groups = bitmap->s_nr_groups; block = goal + (sizeof(struct spaceBitmapDesc) << 3); block_group = block >> (sb->s_blocksize_bits + 3); group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) { bit = block % (sb->s_blocksize << 3); if (udf_test_bit(bit, bh->b_data)) { goto got_block; } end_goal = (bit + 63) & ~63; bit = udf_find_next_one_bit(bh->b_data, end_goal, bit); if (bit < end_goal) goto got_block; ptr = memscan((char *)bh->b_data + (bit >> 3), 0xFF, sb->s_blocksize - ((bit + 7) >> 3)); newbit = (ptr - ((char *)bh->b_data)) << 3; if (newbit < sb->s_blocksize << 3) { bit = newbit; goto search_back; } newbit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, bit); if (newbit < sb->s_blocksize << 3) { bit = newbit; goto got_block; } } for (i=0; i<(nr_groups*2); i++) { block_group ++; if (block_group >= nr_groups) block_group = 0; group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; if (i < nr_groups) { ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) { bit = (ptr - ((char *)bh->b_data)) << 3; break; } } else { bit = udf_find_next_one_bit((char *)bh->b_data, sb->s_blocksize << 3, group_start << 3); if (bit < sb->s_blocksize << 3) break; } } if (i >= (nr_groups*2)) { mutex_unlock(&sbi->s_alloc_mutex); return newblock; } if (bit < sb->s_blocksize << 3) goto search_back; else bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, group_start << 3); if (bit >= sb->s_blocksize << 3) { mutex_unlock(&sbi->s_alloc_mutex); return 0; } search_back: for (i=0; i<7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--); got_block: /* * Check quota for allocation of this block. */ if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) { mutex_unlock(&sbi->s_alloc_mutex); *err = -EDQUOT; return 0; } newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) - (sizeof(struct spaceBitmapDesc) << 3); if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); goto repeat; } mark_buffer_dirty(bh); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); *err = 0; return newblock; error_return: *err = -EIO; mutex_unlock(&sbi->s_alloc_mutex); return 0; } static void udf_table_free_blocks(struct super_block * sb, struct inode * inode, struct inode * table, kernel_lb_addr bloc, uint32_t offset, uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); uint32_t start, end; uint32_t nextoffset, oextoffset, elen; kernel_lb_addr nbloc, obloc, eloc; struct buffer_head *obh, *nbh; int8_t etype; int i; mutex_lock(&sbi->s_alloc_mutex); if (bloc.logicalBlockNum < 0 || (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) { udf_debug("%d < %d || %d + %d > %d\n", bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); goto error_return; } /* We do this up front - There are some error conditions that could occure, but.. oh well */ if (inode) DQUOT_FREE_BLOCK(inode, count); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } start = bloc.logicalBlockNum + offset; end = bloc.logicalBlockNum + offset + count - 1; oextoffset = nextoffset = sizeof(struct unallocSpaceEntry); elen = 0; obloc = nbloc = UDF_I_LOCATION(table); obh = nbh = NULL; while (count && (etype = udf_next_aext(table, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) { if (((eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) == start)) { if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) { count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); start += ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); elen = (etype << 30) | (0x40000000 - sb->s_blocksize); } else { elen = (etype << 30) | (elen + (count << sb->s_blocksize_bits)); start += count; count = 0; } udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1); } else if (eloc.logicalBlockNum == (end + 1)) { if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) { count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); end -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); eloc.logicalBlockNum -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); elen = (etype << 30) | (0x40000000 - sb->s_blocksize); } else { eloc.logicalBlockNum = start; elen = (etype << 30) | (elen + (count << sb->s_blocksize_bits)); end -= count; count = 0; } udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1); } if (nbh != obh) { i = -1; obloc = nbloc; udf_release_data(obh); atomic_inc(&nbh->b_count); obh = nbh; oextoffset = 0; } else oextoffset = nextoffset; } if (count) { /* NOTE: we CANNOT use udf_add_aext here, as it can try to allocate a new block, and since we hold the super block lock already very bad things would happen :) We copy the behavior of udf_add_aext, but instead of trying to allocate a new block close to the existing one, we just steal a block from the extent we are trying to add. It would be nice if the blocks were close together, but it isn't required. */ int adsize; short_ad *sad = NULL; long_ad *lad = NULL; struct allocExtDesc *aed; eloc.logicalBlockNum = start; elen = EXT_RECORDED_ALLOCATED | (count << sb->s_blocksize_bits); if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else { udf_release_data(obh); udf_release_data(nbh); goto error_return; } if (nextoffset + (2 * adsize) > sb->s_blocksize) { char *sptr, *dptr; int loffset; udf_release_data(obh); obh = nbh; obloc = nbloc; oextoffset = nextoffset; /* Steal a block from the extent being free'd */ nbloc.logicalBlockNum = eloc.logicalBlockNum; eloc.logicalBlockNum ++; elen -= sb->s_blocksize; if (!(nbh = udf_tread(sb, udf_get_lb_pblock(sb, nbloc, 0)))) { udf_release_data(obh); goto error_return; } aed = (struct allocExtDesc *)(nbh->b_data); aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum); if (nextoffset + adsize > sb->s_blocksize) { loffset = nextoffset; aed->lengthAllocDescs = cpu_to_le32(adsize); sptr = UDF_I_DATA(inode) + nextoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode) - adsize; dptr = nbh->b_data + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); nextoffset = sizeof(struct allocExtDesc) + adsize; } else { loffset = nextoffset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); sptr = (obh)->b_data + nextoffset; nextoffset = sizeof(struct allocExtDesc); if (obh) { aed = (struct allocExtDesc *)(obh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); } else { UDF_I_LENALLOC(table) += adsize; mark_inode_dirty(table); } } if (UDF_SB_UDFREV(sb) >= 0x0200) udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1, nbloc.logicalBlockNum, sizeof(tag)); else udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1, nbloc.logicalBlockNum, sizeof(tag)); switch (UDF_I_ALLOCTYPE(table)) { case ICBTAG_FLAG_AD_SHORT: { sad = (short_ad *)sptr; sad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | sb->s_blocksize); sad->extPosition = cpu_to_le32(nbloc.logicalBlockNum); break; } case ICBTAG_FLAG_AD_LONG: { lad = (long_ad *)sptr; lad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | sb->s_blocksize); lad->extLocation = cpu_to_lelb(nbloc); break; } } if (obh) { udf_update_tag(obh->b_data, loffset); mark_buffer_dirty(obh); } else mark_inode_dirty(table); } if (elen) /* It's possible that stealing the block emptied the extent */ { udf_write_aext(table, nbloc, &nextoffset, eloc, elen, nbh, 1); if (!nbh) { UDF_I_LENALLOC(table) += adsize; mark_inode_dirty(table); } else { aed = (struct allocExtDesc *)nbh->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); udf_update_tag(nbh->b_data, nextoffset); mark_buffer_dirty(nbh); } } } udf_release_data(nbh); udf_release_data(obh); error_return: sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); return; } static int udf_table_prealloc_blocks(struct super_block * sb, struct inode * inode, struct inode *table, uint16_t partition, uint32_t first_block, uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; uint32_t extoffset, elen, adsize; kernel_lb_addr bloc, eloc; struct buffer_head *bh; int8_t etype = -1; if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) return 0; if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else return 0; mutex_lock(&sbi->s_alloc_mutex); extoffset = sizeof(struct unallocSpaceEntry); bloc = UDF_I_LOCATION(table); bh = NULL; eloc.logicalBlockNum = 0xFFFFFFFF; while (first_block != eloc.logicalBlockNum && (etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { udf_debug("eloc=%d, elen=%d, first_block=%d\n", eloc.logicalBlockNum, elen, first_block); ; /* empty loop body */ } if (first_block == eloc.logicalBlockNum) { extoffset -= adsize; alloc_count = (elen >> sb->s_blocksize_bits); if (inode && DQUOT_PREALLOC_BLOCK(inode, alloc_count > block_count ? block_count : alloc_count)) alloc_count = 0; else if (alloc_count > block_count) { alloc_count = block_count; eloc.logicalBlockNum += alloc_count; elen -= (alloc_count << sb->s_blocksize_bits); udf_write_aext(table, bloc, &extoffset, eloc, (etype << 30) | elen, bh, 1); } else udf_delete_aext(table, bloc, extoffset, eloc, (etype << 30) | elen, bh); } else alloc_count = 0; udf_release_data(bh); if (alloc_count && UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); sb->s_dirt = 1; } mutex_unlock(&sbi->s_alloc_mutex); return alloc_count; } static int udf_table_new_block(struct super_block * sb, struct inode * inode, struct inode *table, uint16_t partition, uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF; uint32_t newblock = 0, adsize; uint32_t extoffset, goal_extoffset, elen, goal_elen = 0; kernel_lb_addr bloc, goal_bloc, eloc, goal_eloc; struct buffer_head *bh, *goal_bh; int8_t etype; *err = -ENOSPC; if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else return newblock; mutex_lock(&sbi->s_alloc_mutex); if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition)) goal = 0; /* We search for the closest matching block to goal. If we find a exact hit, we stop. Otherwise we keep going till we run out of extents. We store the buffer_head, bloc, and extoffset of the current closest match and use that when we are done. */ extoffset = sizeof(struct unallocSpaceEntry); bloc = UDF_I_LOCATION(table); goal_bh = bh = NULL; while (spread && (etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { if (goal >= eloc.logicalBlockNum) { if (goal < eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) nspread = 0; else nspread = goal - eloc.logicalBlockNum - (elen >> sb->s_blocksize_bits); } else nspread = eloc.logicalBlockNum - goal; if (nspread < spread) { spread = nspread; if (goal_bh != bh) { udf_release_data(goal_bh); goal_bh = bh; atomic_inc(&goal_bh->b_count); } goal_bloc = bloc; goal_extoffset = extoffset - adsize; goal_eloc = eloc; goal_elen = (etype << 30) | elen; } } udf_release_data(bh); if (spread == 0xFFFFFFFF) { udf_release_data(goal_bh); mutex_unlock(&sbi->s_alloc_mutex); return 0; } /* Only allocate blocks from the beginning of the extent. That way, we only delete (empty) extents, never have to insert an extent because of splitting */ /* This works, but very poorly.... */ newblock = goal_eloc.logicalBlockNum; goal_eloc.logicalBlockNum ++; goal_elen -= sb->s_blocksize; if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) { udf_release_data(goal_bh); mutex_unlock(&sbi->s_alloc_mutex); *err = -EDQUOT; return 0; } if (goal_elen) udf_write_aext(table, goal_bloc, &goal_extoffset, goal_eloc, goal_elen, goal_bh, 1); else udf_delete_aext(table, goal_bloc, goal_extoffset, goal_eloc, goal_elen, goal_bh); udf_release_data(goal_bh); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); *err = 0; return newblock; } inline void udf_free_blocks(struct super_block * sb, struct inode * inode, kernel_lb_addr bloc, uint32_t offset, uint32_t count) { uint16_t partition = bloc.partitionReferenceNum; if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, bloc, offset, count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, bloc, offset, count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, bloc, offset, count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, bloc, offset, count); } else return; } inline int udf_prealloc_blocks(struct super_block * sb, struct inode * inode, uint16_t partition, uint32_t first_block, uint32_t block_count) { if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, partition, first_block, block_count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, partition, first_block, block_count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, partition, first_block, block_count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, partition, first_block, block_count); } else return 0; } inline int udf_new_block(struct super_block * sb, struct inode * inode, uint16_t partition, uint32_t goal, int *err) { if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, partition, goal, err); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, partition, goal, err); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, partition, goal, err); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, partition, goal, err); } else { *err = -EIO; return 0; } } linux-2.6.17/fs/udf/.svn/text-base/unicode.c.svn-base0000444000000000000000000002522410574207644020735 0ustar rootroot/* * unicode.c * * PURPOSE * Routines for converting between UTF-8 and OSTA Compressed Unicode. * Also handles filename mangling * * DESCRIPTION * OSTA Compressed Unicode is explained in the OSTA UDF specification. * http://www.osta.org/ * UTF-8 is explained in the IETF RFC XXXX. * ftp://ftp.internic.net/rfc/rfcxxxx.txt * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. */ #include "udfdecl.h" #include #include /* for memset */ #include #include #include "udf_sb.h" static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int); static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) { if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN-2) ) return 0; memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src, strlen); dest->u_cmpID = 0x08; dest->u_len = strlen; return strlen; } /* * udf_build_ustr */ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) { int usesize; if ( (!dest) || (!ptr) || (!size) ) return -1; memset(dest, 0, sizeof(struct ustr)); usesize= (size > UDF_NAME_LEN) ? UDF_NAME_LEN : size; dest->u_cmpID=ptr[0]; dest->u_len=ptr[size-1]; memcpy(dest->u_name, ptr+1, usesize-1); return 0; } /* * udf_build_ustr_exact */ static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) { if ( (!dest) || (!ptr) || (!exactsize) ) return -1; memset(dest, 0, sizeof(struct ustr)); dest->u_cmpID=ptr[0]; dest->u_len=exactsize-1; memcpy(dest->u_name, ptr+1, exactsize-1); return 0; } /* * udf_ocu_to_utf8 * * PURPOSE * Convert OSTA Compressed Unicode to the UTF-8 equivalent. * * DESCRIPTION * This routine is only called by udf_filldir(). * * PRE-CONDITIONS * utf Pointer to UTF-8 output buffer. * ocu Pointer to OSTA Compressed Unicode input buffer * of size UDF_NAME_LEN bytes. * both of type "struct ustr *" * * POST-CONDITIONS * Zero on success. * * HISTORY * November 12, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) { uint8_t *ocu; uint32_t c; uint8_t cmp_id, ocu_len; int i; ocu = ocu_i->u_name; ocu_len = ocu_i->u_len; cmp_id = ocu_i->u_cmpID; utf_o->u_len = 0; if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); utf_o->u_cmpID = 0; utf_o->u_len = 0; return 0; } if ((cmp_id != 8) && (cmp_id != 16)) { printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); return 0; } for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) { /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; /* Compress Unicode to UTF-8 */ if (c < 0x80U) utf_o->u_name[utf_o->u_len++] = (uint8_t)c; else if (c < 0x800U) { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xc0 | (c >> 6)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); } else { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xe0 | (c >> 12)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | ((c >> 6) & 0x3f)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); } } utf_o->u_cmpID=8; return utf_o->u_len; } /* * * udf_utf8_to_ocu * * PURPOSE * Convert UTF-8 to the OSTA Compressed Unicode equivalent. * * DESCRIPTION * This routine is only called by udf_lookup(). * * PRE-CONDITIONS * ocu Pointer to OSTA Compressed Unicode output * buffer of size UDF_NAME_LEN bytes. * utf Pointer to UTF-8 input buffer. * utf_len Length of UTF-8 input buffer in bytes. * * POST-CONDITIONS * Zero on success. * * HISTORY * November 12, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length) { unsigned c, i, max_val, utf_char; int utf_cnt, u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: u_len = 0U; utf_char = 0U; utf_cnt = 0U; for (i = 0U; i < utf->u_len; i++) { c = (uint8_t)utf->u_name[i]; /* Complete a multi-byte UTF-8 character */ if (utf_cnt) { utf_char = (utf_char << 6) | (c & 0x3fU); if (--utf_cnt) continue; } else { /* Check for a multi-byte UTF-8 character */ if (c & 0x80U) { /* Start a multi-byte UTF-8 character */ if ((c & 0xe0U) == 0xc0U) { utf_char = c & 0x1fU; utf_cnt = 1; } else if ((c & 0xf0U) == 0xe0U) { utf_char = c & 0x0fU; utf_cnt = 2; } else if ((c & 0xf8U) == 0xf0U) { utf_char = c & 0x07U; utf_cnt = 3; } else if ((c & 0xfcU) == 0xf8U) { utf_char = c & 0x03U; utf_cnt = 4; } else if ((c & 0xfeU) == 0xfcU) { utf_char = c & 0x01U; utf_cnt = 5; } else goto error_out; continue; } else /* Single byte UTF-8 character (most common) */ utf_char = c; } /* Choose no compression if necessary */ if (utf_char > max_val) { if ( 0xffU == max_val ) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } goto error_out; } if (max_val == 0xffffU) { ocu[++u_len] = (uint8_t)(utf_char >> 8); } ocu[++u_len] = (uint8_t)(utf_char & 0xffU); } if (utf_cnt) { error_out: ocu[++u_len] = '?'; printk(KERN_DEBUG "udf: bad UTF-8 character\n"); } ocu[length - 1] = (uint8_t)u_len + 1; return u_len + 1; } static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, struct ustr *ocu_i) { uint8_t *ocu; uint32_t c; uint8_t cmp_id, ocu_len; int i; ocu = ocu_i->u_name; ocu_len = ocu_i->u_len; cmp_id = ocu_i->u_cmpID; utf_o->u_len = 0; if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); utf_o->u_cmpID = 0; utf_o->u_len = 0; return 0; } if ((cmp_id != 8) && (cmp_id != 16)) { printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); return 0; } for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) { /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; utf_o->u_len += nls->uni2char(c, &utf_o->u_name[utf_o->u_len], UDF_NAME_LEN - utf_o->u_len); } utf_o->u_cmpID=8; return utf_o->u_len; } static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, int length) { unsigned len, i, max_val; uint16_t uni_char; int u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: u_len = 0U; for (i = 0U; i < uni->u_len; i++) { len = nls->char2uni(&uni->u_name[i], uni->u_len-i, &uni_char); if (len <= 0) continue; if (uni_char > max_val) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } if (max_val == 0xffffU) ocu[++u_len] = (uint8_t)(uni_char >> 8); ocu[++u_len] = (uint8_t)(uni_char & 0xffU); i += len - 1; } ocu[length - 1] = (uint8_t)u_len + 1; return u_len + 1; } int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, int flen) { struct ustr filename, unifilename; int len; if (udf_build_ustr_exact(&unifilename, sname, flen)) { return 0; } if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { if (!udf_CS0toUTF8(&filename, &unifilename) ) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); return 0; } } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, &filename, &unifilename) ) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); return 0; } } else return 0; if ((len = udf_translate_to_linux(dname, filename.u_name, filename.u_len, unifilename.u_name, unifilename.u_len))) { return len; } return 0; } int udf_put_filename(struct super_block *sb, const uint8_t *sname, uint8_t *dname, int flen) { struct ustr unifilename; int namelen; if ( !(udf_char_to_ustr(&unifilename, sname, flen)) ) { return 0; } if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { if ( !(namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN)) ) { return 0; } } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { if ( !(namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, &unifilename, UDF_NAME_LEN)) ) { return 0; } } else return 0; return namelen; } #define ILLEGAL_CHAR_MARK '_' #define EXT_MARK '.' #define CRC_MARK '#' #define EXT_SIZE 5 static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen, uint8_t *fidName, int fidNameLen) { int index, newIndex = 0, needsCRC = 0; int extIndex = 0, newExtIndex = 0, hasExt = 0; unsigned short valueCRC; uint8_t curr; const uint8_t hexChar[] = "0123456789ABCDEF"; if (udfName[0] == '.' && (udfLen == 1 || (udfLen == 2 && udfName[1] == '.'))) { needsCRC = 1; newIndex = udfLen; memcpy(newName, udfName, udfLen); } else { for (index = 0; index < udfLen; index++) { curr = udfName[index]; if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; while (index+1 < udfLen && (udfName[index+1] == '/' || udfName[index+1] == 0)) index++; } if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE) { if (udfLen == index + 1) hasExt = 0; else { hasExt = 1; extIndex = index; newExtIndex = newIndex; } } if (newIndex < 256) newName[newIndex++] = curr; else needsCRC = 1; } } if (needsCRC) { uint8_t ext[EXT_SIZE]; int localExtIndex = 0; if (hasExt) { int maxFilenameLen; for(index = 0; index maxFilenameLen) newIndex = maxFilenameLen; else newIndex = newExtIndex; } else if (newIndex > 250) newIndex = 250; newName[newIndex++] = CRC_MARK; valueCRC = udf_crc(fidName, fidNameLen, 0); newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12]; newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8]; newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4]; newName[newIndex++] = hexChar[(valueCRC & 0x000f)]; if (hasExt) { newName[newIndex++] = EXT_MARK; for (index = 0;index < localExtIndex ;index++ ) newName[newIndex++] = ext[index]; } } return newIndex; } linux-2.6.17/fs/udf/.svn/text-base/truncate.c.svn-base0000444000000000000000000002002010574207644021121 0ustar rootroot/* * truncate.c * * PURPOSE * Truncate handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2004 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY * * 02/24/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include #include "udf_i.h" #include "udf_sb.h" static void extent_trunc(struct inode * inode, kernel_lb_addr bloc, int extoffset, kernel_lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen) { kernel_lb_addr neloc = { 0, 0 }; int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; if (nelen) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block); etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30); } else neloc = eloc; nelen = (etype << 30) | nelen; } if (elen != nelen) { udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0); if (last_block - first_block > 0) { if (etype == (EXT_RECORDED_ALLOCATED >> 30)) mark_inode_dirty(inode); if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block); } } } void udf_discard_prealloc(struct inode * inode) { kernel_lb_addr bloc, eloc; uint32_t extoffset = 0, elen, nelen; uint64_t lbcount = 0; int8_t etype = -1, netype; struct buffer_head *bh = NULL; int adsize; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || inode->i_size == UDF_I_LENEXTENTS(inode)) { return; } if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else adsize = 0; bloc = UDF_I_LOCATION(inode); while ((netype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { etype = netype; lbcount += elen; if (lbcount > inode->i_size && lbcount - inode->i_size < inode->i_sb->s_blocksize) { nelen = elen - (lbcount - inode->i_size); extent_trunc(inode, bloc, extoffset-adsize, eloc, etype, elen, bh, nelen); lbcount = inode->i_size; } } if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { extoffset -= adsize; lbcount -= elen; extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0); if (!bh) { UDF_I_LENALLOC(inode) = extoffset - udf_file_entry_alloc_offset(inode); mark_inode_dirty(inode); } else { struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); aed->lengthAllocDescs = cpu_to_le32(extoffset - sizeof(struct allocExtDesc)); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(bh->b_data, extoffset); else udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(bh, inode); } } UDF_I_LENEXTENTS(inode) = lbcount; udf_release_data(bh); } void udf_truncate_extents(struct inode * inode) { kernel_lb_addr bloc, eloc, neloc = { 0, 0 }; uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc; int8_t etype; int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits; struct buffer_head *bh = NULL; int adsize; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else adsize = 0; etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh); offset += (inode->i_size & (inode->i_sb->s_blocksize - 1)); if (etype != -1) { extoffset -= adsize; extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset); extoffset += adsize; if (offset) lenalloc = extoffset; else lenalloc = extoffset - adsize; if (!bh) lenalloc -= udf_file_entry_alloc_offset(inode); else lenalloc -= sizeof(struct allocExtDesc); while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1) { if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0); extoffset = 0; if (lelen) { if (!bh) BUG(); else memset(bh->b_data, 0x00, sizeof(struct allocExtDesc)); udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen); } else { if (!bh) { UDF_I_LENALLOC(inode) = lenalloc; mark_inode_dirty(inode); } else { struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); aed->lengthAllocDescs = cpu_to_le32(lenalloc); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(bh->b_data, lenalloc + sizeof(struct allocExtDesc)); else udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(bh, inode); } } udf_release_data(bh); extoffset = sizeof(struct allocExtDesc); bloc = eloc; bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, bloc, 0)); if (elen) lelen = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; else lelen = 1; } else { extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0); extoffset += adsize; } } if (lelen) { if (!bh) BUG(); else memset(bh->b_data, 0x00, sizeof(struct allocExtDesc)); udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen); } else { if (!bh) { UDF_I_LENALLOC(inode) = lenalloc; mark_inode_dirty(inode); } else { struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); aed->lengthAllocDescs = cpu_to_le32(lenalloc); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(bh->b_data, lenalloc + sizeof(struct allocExtDesc)); else udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(bh, inode); } } } else if (inode->i_size) { if (offset) { /* * OK, there is not extent covering inode->i_size and * no extent above inode->i_size => truncate is * extending the file by 'offset'. */ if ((!bh && extoffset == udf_file_entry_alloc_offset(inode)) || (bh && extoffset == sizeof(struct allocExtDesc))) { /* File has no extents at all! */ memset(&eloc, 0x00, sizeof(kernel_lb_addr)); elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset; udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); } else { extoffset -= adsize; etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1); if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { extoffset -= adsize; elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset); udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0); } else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { kernel_lb_addr neloc = { 0, 0 }; extoffset -= adsize; nelen = EXT_NOT_RECORDED_NOT_ALLOCATED | ((elen + offset + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1); udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1); } else { if (elen & (inode->i_sb->s_blocksize - 1)) { extoffset -= adsize; elen = EXT_RECORDED_ALLOCATED | ((elen + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1); } memset(&eloc, 0x00, sizeof(kernel_lb_addr)); elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset; udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); } } } } UDF_I_LENEXTENTS(inode) = inode->i_size; udf_release_data(bh); } linux-2.6.17/fs/udf/.svn/text-base/misc.c.svn-base0000444000000000000000000001731110574207644020240 0ustar rootroot/* * misc.c * * PURPOSE * Miscellaneous routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 04/19/99 blf partial support for reading/writing specific EA's */ #include "udfdecl.h" #include #include #include #include #include "udf_i.h" #include "udf_sb.h" struct buffer_head * udf_tgetblk(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_getblk(sb, udf_fixed_to_variable(block)); else return sb_getblk(sb, block); } struct buffer_head * udf_tread(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_bread(sb, udf_fixed_to_variable(block)); else return sb_bread(sb, block); } struct genericFormat * udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, uint8_t loc) { uint8_t *ea = NULL, *ad = NULL; int offset; uint16_t crclen; int i; ea = UDF_I_DATA(inode); if (UDF_I_LENEATTR(inode)) ad = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); else { ad = ea; size += sizeof(struct extendedAttrHeaderDesc); } offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) - UDF_I_LENALLOC(inode); /* TODO - Check for FreeEASpace */ if (loc & 0x01 && offset >= size) { struct extendedAttrHeaderDesc *eahd; eahd = (struct extendedAttrHeaderDesc *)ea; if (UDF_I_LENALLOC(inode)) { memmove(&ad[size], ad, UDF_I_LENALLOC(inode)); } if (UDF_I_LENEATTR(inode)) { /* check checksum/crc */ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { return NULL; } } else { size -= sizeof(struct extendedAttrHeaderDesc); UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc); eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD); if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) eahd->descTag.descVersion = cpu_to_le16(3); else eahd->descTag.descVersion = cpu_to_le16(2); eahd->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF); eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF); } offset = UDF_I_LENEATTR(inode); if (type < 2048) { if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t aal = le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); offset -= aal; eahd->appAttrLocation = cpu_to_le32(aal + size); } if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t ial = le32_to_cpu(eahd->impAttrLocation); memmove(&ea[offset - ial + size], &ea[ial], offset - ial); offset -= ial; eahd->impAttrLocation = cpu_to_le32(ial + size); } } else if (type < 65536) { if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t aal = le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); offset -= aal; eahd->appAttrLocation = cpu_to_le32(aal + size); } } /* rewrite CRC + checksum of eahd */ crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(tag); eahd->descTag.descCRCLength = cpu_to_le16(crclen); eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + sizeof(tag), crclen, 0)); eahd->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) eahd->descTag.tagChecksum += ((uint8_t *)&(eahd->descTag))[i]; UDF_I_LENEATTR(inode) += size; return (struct genericFormat *)&ea[offset]; } if (loc & 0x02) { } return NULL; } struct genericFormat * udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) { struct genericFormat *gaf; uint8_t *ea = NULL; uint32_t offset; ea = UDF_I_DATA(inode); if (UDF_I_LENEATTR(inode)) { struct extendedAttrHeaderDesc *eahd; eahd = (struct extendedAttrHeaderDesc *)ea; /* check checksum/crc */ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { return NULL; } if (type < 2048) offset = sizeof(struct extendedAttrHeaderDesc); else if (type < 65536) offset = le32_to_cpu(eahd->impAttrLocation); else offset = le32_to_cpu(eahd->appAttrLocation); while (offset < UDF_I_LENEATTR(inode)) { gaf = (struct genericFormat *)&ea[offset]; if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) return gaf; else offset += le32_to_cpu(gaf->attrLength); } } return NULL; } /* * udf_read_tagged * * PURPOSE * Read the first block of a tagged descriptor. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ struct buffer_head * udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident) { tag *tag_p; struct buffer_head *bh = NULL; register uint8_t checksum; register int i; /* Read the block */ if (block == 0xFFFFFFFF) return NULL; bh = udf_tread(sb, block + UDF_SB_SESSION(sb)); if (!bh) { udf_debug("block=%d, location=%d: read failed\n", block + UDF_SB_SESSION(sb), location); return NULL; } tag_p = (tag *)(bh->b_data); *ident = le16_to_cpu(tag_p->tagIdent); if ( location != le32_to_cpu(tag_p->tagLocation) ) { udf_debug("location mismatch block %u, tag %u != %u\n", block + UDF_SB_SESSION(sb), le32_to_cpu(tag_p->tagLocation), location); goto error_out; } /* Verify the tag checksum */ checksum = 0U; for (i = 0; i < 4; i++) checksum += (uint8_t)(bh->b_data[i]); for (i = 5; i < 16; i++) checksum += (uint8_t)(bh->b_data[i]); if (checksum != tag_p->tagChecksum) { printk(KERN_ERR "udf: tag checksum failed block %d\n", block); goto error_out; } /* Verify the tag version */ if (le16_to_cpu(tag_p->descVersion) != 0x0002U && le16_to_cpu(tag_p->descVersion) != 0x0003U) { udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n", le16_to_cpu(tag_p->descVersion), block); goto error_out; } /* Verify the descriptor CRC */ if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize || le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag), le16_to_cpu(tag_p->descCRCLength), 0)) { return bh; } udf_debug("Crc failure block %d: crc = %d, crclen = %d\n", block + UDF_SB_SESSION(sb), le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength)); error_out: brelse(bh); return NULL; } struct buffer_head * udf_read_ptagged(struct super_block *sb, kernel_lb_addr loc, uint32_t offset, uint16_t *ident) { return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset), loc.logicalBlockNum + offset, ident); } void udf_release_data(struct buffer_head *bh) { if (bh) brelse(bh); } void udf_update_tag(char *data, int length) { tag *tptr = (tag *)data; int i; length -= sizeof(tag); tptr->tagChecksum = 0; tptr->descCRCLength = cpu_to_le16(length); tptr->descCRC = cpu_to_le16(udf_crc(data + sizeof(tag), length, 0)); for (i=0; i<16; i++) if (i != 4) tptr->tagChecksum += (uint8_t)(data[i]); } void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum, uint32_t loc, int length) { tag *tptr = (tag *)data; tptr->tagIdent = cpu_to_le16(ident); tptr->descVersion = cpu_to_le16(version); tptr->tagSerialNum = cpu_to_le16(snum); tptr->tagLocation = cpu_to_le32(loc); udf_update_tag(data, length); } linux-2.6.17/fs/udf/.svn/prop-base/0000755000000000000000000000000010574207644015417 5ustar rootrootlinux-2.6.17/fs/udf/udf_i.h0000644000000000000000000000177310564537530014111 0ustar rootroot#ifndef __LINUX_UDF_I_H #define __LINUX_UDF_I_H #include static inline struct udf_inode_info *UDF_I(struct inode *inode) { return list_entry(inode, struct udf_inode_info, vfs_inode); } #define UDF_I_LOCATION(X) ( UDF_I(X)->i_location ) #define UDF_I_LENEATTR(X) ( UDF_I(X)->i_lenEAttr ) #define UDF_I_LENALLOC(X) ( UDF_I(X)->i_lenAlloc ) #define UDF_I_LENEXTENTS(X) ( UDF_I(X)->i_lenExtents ) #define UDF_I_UNIQUE(X) ( UDF_I(X)->i_unique ) #define UDF_I_ALLOCTYPE(X) ( UDF_I(X)->i_alloc_type ) #define UDF_I_EFE(X) ( UDF_I(X)->i_efe ) #define UDF_I_USE(X) ( UDF_I(X)->i_use ) #define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat4096 ) #define UDF_I_NEXT_ALLOC_BLOCK(X) ( UDF_I(X)->i_next_alloc_block ) #define UDF_I_NEXT_ALLOC_GOAL(X) ( UDF_I(X)->i_next_alloc_goal ) #define UDF_I_CRTIME(X) ( UDF_I(X)->i_crtime ) #define UDF_I_SAD(X) ( UDF_I(X)->i_ext.i_sad ) #define UDF_I_LAD(X) ( UDF_I(X)->i_ext.i_lad ) #define UDF_I_DATA(X) ( UDF_I(X)->i_ext.i_data ) #endif /* !defined(_LINUX_UDF_I_H) */ linux-2.6.17/fs/udf/udftime.c0000644000000000000000000001312410564537530014444 0ustar rootroot/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Paul Eggert (eggert@twinsun.com). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * dgb 10/02/98: ripped this from glibc source to help convert timestamps to unix time * 10/04/98: added new table-based lookup after seeing how ugly the gnu code is * blf 09/27/99: ripped out all the old code and inserted new table from * John Brockmeyer (without leap second corrections) * rewrote udf_stamp_to_time and fixed timezone accounting in udf_time_to_stamp. */ /* * We don't take into account leap seconds. This may be correct or incorrect. * For more NIST information (especially dealing with leap seconds), see: * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm */ #include #include #include "udfdecl.h" #define EPOCH_YEAR 1970 #ifndef __isleap /* Nonzero if YEAR is a leap year (every 4 years, except every 100th isn't, and every 400th is). */ #define __isleap(year) \ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) #endif /* How many days come before each month (0-12). */ static const unsigned short int __mon_yday[2][13] = { /* Normal years. */ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, /* Leap years. */ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; #define MAX_YEAR_SECONDS 69 #define SPD 0x15180 /*3600*24*/ #define SPY(y,l,s) (SPD * (365*y+l)+s) static time_t year_seconds[MAX_YEAR_SECONDS]= { /*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), /*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), /*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), /*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), /*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), /*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), /*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), /*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), /*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), /*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), /*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), /*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), /*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), /*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), /*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), /*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), /*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), /*2038*/ SPY(68,17,0) }; extern struct timezone sys_tz; #define SECS_PER_HOUR (60 * 60) #define SECS_PER_DAY (SECS_PER_HOUR * 24) time_t * udf_stamp_to_time(time_t *dest, long *dest_usec, kernel_timestamp src) { int yday; uint8_t type = src.typeAndTimezone >> 12; int16_t offset; if (type == 1) { offset = src.typeAndTimezone << 4; /* sign extent offset */ offset = (offset >> 4); if (offset == -2047) /* unspecified offset */ offset = 0; } else offset = 0; if ((src.year < EPOCH_YEAR) || (src.year >= EPOCH_YEAR+MAX_YEAR_SECONDS)) { *dest = -1; *dest_usec = -1; return NULL; } *dest = year_seconds[src.year - EPOCH_YEAR]; *dest -= offset * 60; yday = ((__mon_yday[__isleap (src.year)] [src.month-1]) + (src.day-1)); *dest += ( ( (yday* 24) + src.hour ) * 60 + src.minute ) * 60 + src.second; *dest_usec = src.centiseconds * 10000 + src.hundredsOfMicroseconds * 100 + src.microseconds; return dest; } kernel_timestamp * udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) { long int days, rem, y; const unsigned short int *ip; int16_t offset; offset = -sys_tz.tz_minuteswest; if (!dest) return NULL; dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF); ts.tv_sec += offset * 60; days = ts.tv_sec / SECS_PER_DAY; rem = ts.tv_sec % SECS_PER_DAY; dest->hour = rem / SECS_PER_HOUR; rem %= SECS_PER_HOUR; dest->minute = rem / 60; dest->second = rem % 60; y = 1970; #define DIV(a,b) ((a) / (b) - ((a) % (b) < 0)) #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) while (days < 0 || days >= (__isleap(y) ? 366 : 365)) { long int yg = y + days / 365 - (days % 365 < 0); /* Adjust DAYS and Y to match the guessed year. */ days -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) - LEAPS_THRU_END_OF (y - 1)); y = yg; } dest->year = y; ip = __mon_yday[__isleap(y)]; for (y = 11; days < (long int) ip[y]; --y) continue; days -= ip[y]; dest->month = y + 1; dest->day = days + 1; dest->centiseconds = ts.tv_nsec / 10000000; dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000) / 100; dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 - dest->hundredsOfMicroseconds * 100); return dest; } /* EOF */ linux-2.6.17/fs/udf/directory.c0000644000000000000000000001667110564537530015025 0ustar rootroot/* * directory.c * * PURPOSE * Directory related functions * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. */ #include "udfdecl.h" #include "udf_i.h" #include #include #include #if 0 static uint8_t * udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size, kernel_lb_addr fe_loc, int *pos, int *offset, struct buffer_head **bh, int *error) { int loffset = *offset; int block; uint8_t *ad; int remainder; *error = 0; ad = (uint8_t *)(*bh)->b_data + *offset; *offset += ad_size; if (!ad) { udf_release_data(*bh); *error = 1; return NULL; } if (*offset == dir->i_sb->s_blocksize) { udf_release_data(*bh); block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); if (!block) return NULL; if (!(*bh = udf_tread(dir->i_sb, block))) return NULL; } else if (*offset > dir->i_sb->s_blocksize) { ad = tmpad; remainder = dir->i_sb->s_blocksize - loffset; memcpy((uint8_t *)ad, (*bh)->b_data + loffset, remainder); udf_release_data(*bh); block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); if (!block) return NULL; if (!((*bh) = udf_tread(dir->i_sb, block))) return NULL; memcpy((uint8_t *)ad + remainder, (*bh)->b_data, ad_size - remainder); *offset = ad_size - remainder; } return ad; } #endif struct fileIdentDesc * udf_fileident_read(struct inode *dir, loff_t *nf_pos, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi, kernel_lb_addr *bloc, uint32_t *extoffset, kernel_lb_addr *eloc, uint32_t *elen, uint32_t *offset, struct buffer_head **bh) { struct fileIdentDesc *fi; int i, num, block; struct buffer_head * tmp, * bha[16]; fibh->soffset = fibh->eoffset; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { fi = udf_get_fileident(UDF_I_DATA(dir) - (UDF_I_EFE(dir) ? sizeof(struct extendedFileEntry) : sizeof(struct fileEntry)), dir->i_sb->s_blocksize, &(fibh->eoffset)); if (!fi) return NULL; *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); return fi; } if (fibh->eoffset == dir->i_sb->s_blocksize) { int lextoffset = *extoffset; if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) != (EXT_RECORDED_ALLOCATED >> 30)) { return NULL; } block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); (*offset) ++; if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) *offset = 0; else *extoffset = lextoffset; udf_release_data(fibh->sbh); if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) return NULL; fibh->soffset = fibh->eoffset = 0; if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) { i = 16 >> (dir->i_sb->s_blocksize_bits - 9); if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits)) i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset; for (num=0; i>0; i--) { block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i); tmp = udf_tgetblk(dir->i_sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } if (num) { ll_rw_block(READA, num, bha); for (i=0; isbh != fibh->ebh) { udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; } fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize, &(fibh->eoffset)); if (!fi) return NULL; *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); if (fibh->eoffset <= dir->i_sb->s_blocksize) { memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); } else if (fibh->eoffset > dir->i_sb->s_blocksize) { int lextoffset = *extoffset; if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) != (EXT_RECORDED_ALLOCATED >> 30)) { return NULL; } block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); (*offset) ++; if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) *offset = 0; else *extoffset = lextoffset; fibh->soffset -= dir->i_sb->s_blocksize; fibh->eoffset -= dir->i_sb->s_blocksize; if (!(fibh->ebh = udf_tread(dir->i_sb, block))) return NULL; if (sizeof(struct fileIdentDesc) > - fibh->soffset) { int fi_len; memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset); memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data, sizeof(struct fileIdentDesc) + fibh->soffset); fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent + le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3; *nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2); fibh->eoffset = fibh->soffset + fi_len; } else { memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); } } return fi; } struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset) { struct fileIdentDesc *fi; int lengthThisIdent; uint8_t * ptr; int padlen; if ( (!buffer) || (!offset) ) { udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset); return NULL; } ptr = buffer; if ( (*offset > 0) && (*offset < bufsize) ) { ptr += *offset; } fi=(struct fileIdentDesc *)ptr; if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID) { udf_debug("0x%x != TAG_IDENT_FID\n", le16_to_cpu(fi->descTag.tagIdent)); udf_debug("offset: %u sizeof: %lu bufsize: %u\n", *offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize); return NULL; } if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize ) { lengthThisIdent = sizeof(struct fileIdentDesc); } else lengthThisIdent = sizeof(struct fileIdentDesc) + fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse); /* we need to figure padding, too! */ padlen = lengthThisIdent % UDF_NAME_PAD; if (padlen) lengthThisIdent += (UDF_NAME_PAD - padlen); *offset = *offset + lengthThisIdent; return fi; } #if 0 static extent_ad * udf_get_fileextent(void * buffer, int bufsize, int * offset) { extent_ad * ext; struct fileEntry *fe; uint8_t * ptr; if ( (!buffer) || (!offset) ) { printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n"); return NULL; } fe = (struct fileEntry *)buffer; if ( le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE ) { udf_debug("0x%x != TAG_IDENT_FE\n", le16_to_cpu(fe->descTag.tagIdent)); return NULL; } ptr=(uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr); if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) ) { ptr += *offset; } ext = (extent_ad *)ptr; *offset = *offset + sizeof(extent_ad); return ext; } #endif short_ad * udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc) { short_ad *sa; if ( (!ptr) || (!offset) ) { printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n"); return NULL; } if ( (*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset) ) return NULL; else if ((sa = (short_ad *)ptr)->extLength == 0) return NULL; if (inc) *offset += sizeof(short_ad); return sa; } long_ad * udf_get_filelongad(uint8_t *ptr, int maxoffset, int * offset, int inc) { long_ad *la; if ( (!ptr) || (!offset) ) { printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n"); return NULL; } if ( (*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset) ) return NULL; else if ((la = (long_ad *)ptr)->extLength == 0) return NULL; if (inc) *offset += sizeof(long_ad); return la; } linux-2.6.17/fs/udf/udf_sb.h0000644000000000000000000001240310564537530014255 0ustar rootroot#ifndef __LINUX_UDF_SB_H #define __LINUX_UDF_SB_H /* Since UDF 2.01 is ISO 13346 based... */ #define UDF_SUPER_MAGIC 0x15013346 #define UDF_MAX_READ_VERSION 0x0201 #define UDF_MAX_WRITE_VERSION 0x0201 #define UDF_FLAG_USE_EXTENDED_FE 0 #define UDF_VERS_USE_EXTENDED_FE 0x0200 #define UDF_FLAG_USE_STREAMS 1 #define UDF_VERS_USE_STREAMS 0x0200 #define UDF_FLAG_USE_SHORT_AD 2 #define UDF_FLAG_USE_AD_IN_ICB 3 #define UDF_FLAG_USE_FILE_CTIME_EA 4 #define UDF_FLAG_STRICT 5 #define UDF_FLAG_UNDELETE 6 #define UDF_FLAG_UNHIDE 7 #define UDF_FLAG_VARCONV 8 #define UDF_FLAG_NLS_MAP 9 #define UDF_FLAG_UTF8 10 #define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */ #define UDF_FLAG_UID_IGNORE 12 /* use sb uid instead of on disk uid */ #define UDF_FLAG_GID_FORGET 13 #define UDF_FLAG_GID_IGNORE 14 #define UDF_PART_FLAG_UNALLOC_BITMAP 0x0001 #define UDF_PART_FLAG_UNALLOC_TABLE 0x0002 #define UDF_PART_FLAG_FREED_BITMAP 0x0004 #define UDF_PART_FLAG_FREED_TABLE 0x0008 #define UDF_PART_FLAG_READ_ONLY 0x0010 #define UDF_PART_FLAG_WRITE_ONCE 0x0020 #define UDF_PART_FLAG_REWRITABLE 0x0040 #define UDF_PART_FLAG_OVERWRITABLE 0x0080 static inline struct udf_sb_info *UDF_SB(struct super_block *sb) { return sb->s_fs_info; } #define UDF_SB_FREE(X)\ {\ if (UDF_SB(X))\ {\ kfree(UDF_SB_PARTMAPS(X));\ UDF_SB_PARTMAPS(X) = NULL;\ }\ } #define UDF_SB_ALLOC_PARTMAPS(X,Y)\ {\ UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\ if (UDF_SB_PARTMAPS(X) != NULL)\ {\ UDF_SB_NUMPARTS(X) = Y;\ memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * Y);\ }\ else\ {\ UDF_SB_NUMPARTS(X) = 0;\ udf_error(X, __FUNCTION__, "Unable to allocate space for %d partition maps", Y);\ }\ } #define UDF_SB_ALLOC_BITMAP(X,Y,Z)\ {\ int nr_groups = ((UDF_SB_PARTLEN((X),(Y)) + (sizeof(struct spaceBitmapDesc) << 3) +\ ((X)->s_blocksize * 8) - 1) / ((X)->s_blocksize * 8));\ int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ if (size <= PAGE_SIZE)\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(size, GFP_KERNEL);\ else\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = vmalloc(size);\ if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL)\ {\ memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00, size);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap =\ (struct buffer_head **)(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap + 1);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups = nr_groups;\ }\ else\ {\ udf_error(X, __FUNCTION__, "Unable to allocate space for bitmap and %d buffer_head pointers", nr_groups);\ }\ } #define UDF_SB_FREE_BITMAP(X,Y,Z)\ {\ int i;\ int nr_groups = UDF_SB_BITMAP_NR_GROUPS(X,Y,Z);\ int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ for (i=0; is_flags & ( 1 << (Y) ) ) #define UDF_SET_FLAG(X,Y) ( UDF_SB(X)->s_flags |= ( 1 << (Y) ) ) #define UDF_CLEAR_FLAG(X,Y) ( UDF_SB(X)->s_flags &= ~( 1 << (Y) ) ) #define UDF_UPDATE_UDFREV(X,Y) ( ((Y) > UDF_SB_UDFREV(X)) ? UDF_SB_UDFREV(X) = (Y) : UDF_SB_UDFREV(X) ) #define UDF_SB_PARTMAPS(X) ( UDF_SB(X)->s_partmaps ) #define UDF_SB_PARTTYPE(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_type ) #define UDF_SB_PARTROOT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_root ) #define UDF_SB_PARTLEN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_len ) #define UDF_SB_PARTVSN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_volumeseqnum ) #define UDF_SB_PARTNUM(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_num ) #define UDF_SB_TYPESPAR(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_sparing ) #define UDF_SB_TYPEVIRT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_virtual ) #define UDF_SB_PARTFUNC(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func ) #define UDF_SB_PARTFLAGS(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags ) #define UDF_SB_BITMAP(X,Y,Z,I) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap[I] ) #define UDF_SB_BITMAP_NR_GROUPS(X,Y,Z) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups ) #define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident ) #define UDF_SB_NUMPARTS(X) ( UDF_SB(X)->s_partitions ) #define UDF_SB_PARTITION(X) ( UDF_SB(X)->s_partition ) #define UDF_SB_SESSION(X) ( UDF_SB(X)->s_session ) #define UDF_SB_ANCHOR(X) ( UDF_SB(X)->s_anchor ) #define UDF_SB_LASTBLOCK(X) ( UDF_SB(X)->s_lastblock ) #define UDF_SB_LVIDBH(X) ( UDF_SB(X)->s_lvidbh ) #define UDF_SB_LVID(X) ( (struct logicalVolIntegrityDesc *)UDF_SB_LVIDBH(X)->b_data ) #define UDF_SB_LVIDIU(X) ( (struct logicalVolIntegrityDescImpUse *)&(UDF_SB_LVID(X)->impUse[le32_to_cpu(UDF_SB_LVID(X)->numOfPartitions) * 2 * sizeof(uint32_t)/sizeof(uint8_t)]) ) #define UDF_SB_UMASK(X) ( UDF_SB(X)->s_umask ) #define UDF_SB_GID(X) ( UDF_SB(X)->s_gid ) #define UDF_SB_UID(X) ( UDF_SB(X)->s_uid ) #define UDF_SB_RECORDTIME(X) ( UDF_SB(X)->s_recordtime ) #define UDF_SB_SERIALNUM(X) ( UDF_SB(X)->s_serialnum ) #define UDF_SB_UDFREV(X) ( UDF_SB(X)->s_udfrev ) #define UDF_SB_FLAGS(X) ( UDF_SB(X)->s_flags ) #define UDF_SB_VAT(X) ( UDF_SB(X)->s_vat ) #endif /* __LINUX_UDF_SB_H */ linux-2.6.17/fs/udf/fsync.c0000644000000000000000000000217410564537530014134 0ustar rootroot/* * fsync.c * * PURPOSE * Fsync handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2001 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 05/22/99 blf Created. */ #include "udfdecl.h" #include #include static int udf_fsync_inode(struct inode *, int); /* * File may be NULL when we are called. Perhaps we shouldn't * even pass file to fsync ? */ int udf_fsync_file(struct file * file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; return udf_fsync_inode(inode, datasync); } static int udf_fsync_inode(struct inode *inode, int datasync) { int err; err = sync_mapping_buffers(inode->i_mapping); if (!(inode->i_state & I_DIRTY)) return err; if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) return err; err |= udf_sync_inode (inode); return err ? -EIO : 0; } linux-2.6.17/fs/udf/symlink.c0000644000000000000000000000450710564537530014502 0ustar rootroot/* * symlink.c * * PURPOSE * Symlink handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY * * 04/16/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include #include #include #include #include #include #include #include #include "udf_i.h" static void udf_pc_to_char(struct super_block *sb, char *from, int fromlen, char *to) { struct pathComponent *pc; int elen = 0; char *p = to; while (elen < fromlen) { pc = (struct pathComponent *)(from + elen); switch (pc->componentType) { case 1: if (pc->lengthComponentIdent == 0) { p = to; *p++ = '/'; } break; case 3: memcpy(p, "../", 3); p += 3; break; case 4: memcpy(p, "./", 2); p += 2; /* that would be . - just ignore */ break; case 5: p += udf_get_filename(sb, pc->componentIdent, p, pc->lengthComponentIdent); *p++ = '/'; break; } elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; } if (p > to+1) p[-1] = '\0'; else p[0] = '\0'; } static int udf_symlink_filler(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; struct buffer_head *bh = NULL; char *symlink; int err = -EIO; char *p = kmap(page); lock_kernel(); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) symlink = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); else { bh = sb_bread(inode->i_sb, udf_block_map(inode, 0)); if (!bh) goto out; symlink = bh->b_data; } udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p); udf_release_data(bh); unlock_kernel(); SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; out: unlock_kernel(); SetPageError(page); kunmap(page); unlock_page(page); return err; } /* * symlinks can't do much... */ struct address_space_operations udf_symlink_aops = { .readpage = udf_symlink_filler, }; linux-2.6.17/fs/udf/inode.c0000644000000000000000000015356010564537530014116 0ustar rootroot/* * inode.c * * PURPOSE * Inode handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 10/04/98 dgb Added rudimentary directory functions * 10/07/98 Fully working udf_block_map! It works! * 11/25/98 bmap altered to better support extents * 12/06/98 blf partition support in udf_iget, udf_block_map and udf_read_inode * 12/12/98 rewrote udf_block_map to handle next extents and descs across * block boundaries (which is not actually allowed) * 12/20/98 added support for strategy 4096 * 03/07/99 rewrote udf_block_map (again) * New funcs, inode_bmap, udf_next_aext * 04/19/99 Support for writing device EA's for major/minor # */ #include "udfdecl.h" #include #include #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" MODULE_AUTHOR("Ben Fennema"); MODULE_DESCRIPTION("Universal Disk Format Filesystem"); MODULE_LICENSE("GPL"); #define EXTENT_MERGE_SIZE 5 static mode_t udf_convert_permissions(struct fileEntry *); static int udf_update_inode(struct inode *, int); static void udf_fill_inode(struct inode *, struct buffer_head *); static struct buffer_head *inode_getblk(struct inode *, long, int *, long *, int *); static int8_t udf_insert_aext(struct inode *, kernel_lb_addr, int, kernel_lb_addr, uint32_t, struct buffer_head *); static void udf_split_extents(struct inode *, int *, int, int, kernel_long_ad [EXTENT_MERGE_SIZE], int *); static void udf_prealloc_extents(struct inode *, int, int, kernel_long_ad [EXTENT_MERGE_SIZE], int *); static void udf_merge_extents(struct inode *, kernel_long_ad [EXTENT_MERGE_SIZE], int *); static void udf_update_extents(struct inode *, kernel_long_ad [EXTENT_MERGE_SIZE], int, int, kernel_lb_addr, uint32_t, struct buffer_head **); static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); /* * udf_delete_inode * * PURPOSE * Clean-up before the specified inode is destroyed. * * DESCRIPTION * This routine is called when the kernel destroys an inode structure * ie. when iput() finds i_count == 0. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. * * Called at the last iput() if i_nlink is zero. */ void udf_delete_inode(struct inode * inode) { truncate_inode_pages(&inode->i_data, 0); if (is_bad_inode(inode)) goto no_delete; inode->i_size = 0; udf_truncate(inode); lock_kernel(); udf_update_inode(inode, IS_SYNC(inode)); udf_free_inode(inode); unlock_kernel(); return; no_delete: clear_inode(inode); } void udf_clear_inode(struct inode *inode) { if (!(inode->i_sb->s_flags & MS_RDONLY)) { lock_kernel(); udf_discard_prealloc(inode); unlock_kernel(); } kfree(UDF_I_DATA(inode)); UDF_I_DATA(inode) = NULL; } static int udf_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page, udf_get_block, wbc); } static int udf_readpage(struct file *file, struct page *page) { return block_read_full_page(page, udf_get_block); } static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { return block_prepare_write(page, from, to, udf_get_block); } static sector_t udf_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,udf_get_block); } struct address_space_operations udf_aops = { .readpage = udf_readpage, .writepage = udf_writepage, .sync_page = block_sync_page, .prepare_write = udf_prepare_write, .commit_write = generic_commit_write, .bmap = udf_bmap, }; void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) { struct page *page; char *kaddr; struct writeback_control udf_wbc = { .sync_mode = WB_SYNC_NONE, .nr_to_write = 1, }; /* from now on we have normal address_space methods */ inode->i_data.a_ops = &udf_aops; if (!UDF_I_LENALLOC(inode)) { if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; mark_inode_dirty(inode); return; } page = grab_cache_page(inode->i_mapping, 0); BUG_ON(!PageLocked(page)); if (!PageUptodate(page)) { kaddr = kmap(page); memset(kaddr + UDF_I_LENALLOC(inode), 0x00, PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode)); memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), UDF_I_LENALLOC(inode)); flush_dcache_page(page); SetPageUptodate(page); kunmap(page); } memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0x00, UDF_I_LENALLOC(inode)); UDF_I_LENALLOC(inode) = 0; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; inode->i_data.a_ops->writepage(page, &udf_wbc); page_cache_release(page); mark_inode_dirty(inode); } struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err) { int newblock; struct buffer_head *sbh = NULL, *dbh = NULL; kernel_lb_addr bloc, eloc; uint32_t elen, extoffset; uint8_t alloctype; struct udf_fileident_bh sfibh, dfibh; loff_t f_pos = udf_ext0_offset(inode) >> 2; int size = (udf_ext0_offset(inode) + inode->i_size) >> 2; struct fileIdentDesc cfi, *sfi, *dfi; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) alloctype = ICBTAG_FLAG_AD_SHORT; else alloctype = ICBTAG_FLAG_AD_LONG; if (!inode->i_size) { UDF_I_ALLOCTYPE(inode) = alloctype; mark_inode_dirty(inode); return NULL; } /* alloc block, and copy data to it */ *block = udf_new_block(inode->i_sb, inode, UDF_I_LOCATION(inode).partitionReferenceNum, UDF_I_LOCATION(inode).logicalBlockNum, err); if (!(*block)) return NULL; newblock = udf_get_pblock(inode->i_sb, *block, UDF_I_LOCATION(inode).partitionReferenceNum, 0); if (!newblock) return NULL; dbh = udf_tgetblk(inode->i_sb, newblock); if (!dbh) return NULL; lock_buffer(dbh); memset(dbh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(dbh); unlock_buffer(dbh); mark_buffer_dirty_inode(dbh, inode); sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2; sbh = sfibh.sbh = sfibh.ebh = NULL; dfibh.soffset = dfibh.eoffset = 0; dfibh.sbh = dfibh.ebh = dbh; while ( (f_pos < size) ) { UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL, NULL, NULL); if (!sfi) { udf_release_data(dbh); return NULL; } UDF_I_ALLOCTYPE(inode) = alloctype; sfi->descTag.tagLocation = cpu_to_le32(*block); dfibh.soffset = dfibh.eoffset; dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset); if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse, sfi->fileIdent + le16_to_cpu(sfi->lengthOfImpUse))) { UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; udf_release_data(dbh); return NULL; } } mark_buffer_dirty_inode(dbh, inode); memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0, UDF_I_LENALLOC(inode)); UDF_I_LENALLOC(inode) = 0; bloc = UDF_I_LOCATION(inode); eloc.logicalBlockNum = *block; eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; elen = inode->i_size; UDF_I_LENEXTENTS(inode) = elen; extoffset = udf_file_entry_alloc_offset(inode); udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &sbh, 0); /* UniqueID stuff */ udf_release_data(sbh); mark_inode_dirty(inode); return dbh; } static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) { int err, new; struct buffer_head *bh; unsigned long phys; if (!create) { phys = udf_block_map(inode, block); if (phys) map_bh(bh_result, inode->i_sb, phys); return 0; } err = -EIO; new = 0; bh = NULL; lock_kernel(); if (block < 0) goto abort_negative; if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1) { UDF_I_NEXT_ALLOC_BLOCK(inode) ++; UDF_I_NEXT_ALLOC_GOAL(inode) ++; } err = 0; bh = inode_getblk(inode, block, &err, &phys, &new); BUG_ON(bh); if (err) goto abort; BUG_ON(!phys); if (new) set_buffer_new(bh_result); map_bh(bh_result, inode->i_sb, phys); abort: unlock_kernel(); return err; abort_negative: udf_warning(inode->i_sb, "udf_get_block", "block < 0"); goto abort; } static struct buffer_head * udf_getblk(struct inode *inode, long block, int create, int *err) { struct buffer_head dummy; dummy.b_state = 0; dummy.b_blocknr = -1000; *err = udf_get_block(inode, block, &dummy, create); if (!*err && buffer_mapped(&dummy)) { struct buffer_head *bh; bh = sb_getblk(inode->i_sb, dummy.b_blocknr); if (buffer_new(&dummy)) { lock_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(bh); unlock_buffer(bh); mark_buffer_dirty_inode(bh, inode); } return bh; } return NULL; } static struct buffer_head * inode_getblk(struct inode * inode, long block, int *err, long *phys, int *new) { struct buffer_head *pbh = NULL, *cbh = NULL, *nbh = NULL, *result = NULL; kernel_long_ad laarr[EXTENT_MERGE_SIZE]; uint32_t pextoffset = 0, cextoffset = 0, nextoffset = 0; int count = 0, startnum = 0, endnum = 0; uint32_t elen = 0; kernel_lb_addr eloc, pbloc, cbloc, nbloc; int c = 1; uint64_t lbcount = 0, b_off = 0; uint32_t newblocknum, newblock, offset = 0; int8_t etype; int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum; char lastblock = 0; pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset(inode); b_off = (uint64_t)block << inode->i_sb->s_blocksize_bits; pbloc = cbloc = nbloc = UDF_I_LOCATION(inode); /* find the extent which contains the block we are looking for. alternate between laarr[0] and laarr[1] for locations of the current extent, and the previous extent */ do { if (pbh != cbh) { udf_release_data(pbh); atomic_inc(&cbh->b_count); pbh = cbh; } if (cbh != nbh) { udf_release_data(cbh); atomic_inc(&nbh->b_count); cbh = nbh; } lbcount += elen; pbloc = cbloc; cbloc = nbloc; pextoffset = cextoffset; cextoffset = nextoffset; if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) == -1) break; c = !c; laarr[c].extLength = (etype << 30) | elen; laarr[c].extLocation = eloc; if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) pgoal = eloc.logicalBlockNum + ((elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); count ++; } while (lbcount + elen <= b_off); b_off -= lbcount; offset = b_off >> inode->i_sb->s_blocksize_bits; /* if the extent is allocated and recorded, return the block if the extent is not a multiple of the blocksize, round up */ if (etype == (EXT_RECORDED_ALLOCATED >> 30)) { if (elen & (inode->i_sb->s_blocksize - 1)) { elen = EXT_RECORDED_ALLOCATED | ((elen + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); etype = udf_write_aext(inode, nbloc, &cextoffset, eloc, elen, nbh, 1); } udf_release_data(pbh); udf_release_data(cbh); udf_release_data(nbh); newblock = udf_get_lb_pblock(inode->i_sb, eloc, offset); *phys = newblock; return NULL; } if (etype == -1) { endnum = startnum = ((count > 1) ? 1 : count); if (laarr[c].extLength & (inode->i_sb->s_blocksize - 1)) { laarr[c].extLength = (laarr[c].extLength & UDF_EXTENT_FLAG_MASK) | (((laarr[c].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); UDF_I_LENEXTENTS(inode) = (UDF_I_LENEXTENTS(inode) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1); } c = !c; laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | ((offset + 1) << inode->i_sb->s_blocksize_bits); memset(&laarr[c].extLocation, 0x00, sizeof(kernel_lb_addr)); count ++; endnum ++; lastblock = 1; } else endnum = startnum = ((count > 2) ? 2 : count); /* if the current extent is in position 0, swap it with the previous */ if (!c && count != 1) { laarr[2] = laarr[0]; laarr[0] = laarr[1]; laarr[1] = laarr[2]; c = 1; } /* if the current block is located in a extent, read the next extent */ if (etype != -1) { if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 0)) != -1) { laarr[c+1].extLength = (etype << 30) | elen; laarr[c+1].extLocation = eloc; count ++; startnum ++; endnum ++; } else lastblock = 1; } udf_release_data(cbh); udf_release_data(nbh); /* if the current extent is not recorded but allocated, get the block in the extent corresponding to the requested block */ if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) newblocknum = laarr[c].extLocation.logicalBlockNum + offset; else /* otherwise, allocate a new block */ { if (UDF_I_NEXT_ALLOC_BLOCK(inode) == block) goal = UDF_I_NEXT_ALLOC_GOAL(inode); if (!goal) { if (!(goal = pgoal)) goal = UDF_I_LOCATION(inode).logicalBlockNum + 1; } if (!(newblocknum = udf_new_block(inode->i_sb, inode, UDF_I_LOCATION(inode).partitionReferenceNum, goal, err))) { udf_release_data(pbh); *err = -ENOSPC; return NULL; } UDF_I_LENEXTENTS(inode) += inode->i_sb->s_blocksize; } /* if the extent the requsted block is located in contains multiple blocks, split the extent into at most three extents. blocks prior to requested block, requested block, and blocks after requested block */ udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum); #ifdef UDF_PREALLOCATE /* preallocate blocks */ udf_prealloc_extents(inode, c, lastblock, laarr, &endnum); #endif /* merge any continuous blocks in laarr */ udf_merge_extents(inode, laarr, &endnum); /* write back the new extents, inserting new extents if the new number of extents is greater than the old number, and deleting extents if the new number of extents is less than the old number */ udf_update_extents(inode, laarr, startnum, endnum, pbloc, pextoffset, &pbh); udf_release_data(pbh); if (!(newblock = udf_get_pblock(inode->i_sb, newblocknum, UDF_I_LOCATION(inode).partitionReferenceNum, 0))) { return NULL; } *phys = newblock; *err = 0; *new = 1; UDF_I_NEXT_ALLOC_BLOCK(inode) = block; UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum; inode->i_ctime = current_fs_time(inode->i_sb); if (IS_SYNC(inode)) udf_sync_inode(inode); else mark_inode_dirty(inode); return result; } static void udf_split_extents(struct inode *inode, int *c, int offset, int newblocknum, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) { if ((laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30) || (laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { int curr = *c; int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; int8_t etype = (laarr[curr].extLength >> 30); if (blen == 1) ; else if (!offset || blen == offset + 1) { laarr[curr+2] = laarr[curr+1]; laarr[curr+1] = laarr[curr]; } else { laarr[curr+3] = laarr[curr+1]; laarr[curr+2] = laarr[curr+1] = laarr[curr]; } if (offset) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, laarr[curr].extLocation, 0, offset); laarr[curr].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | (offset << inode->i_sb->s_blocksize_bits); laarr[curr].extLocation.logicalBlockNum = 0; laarr[curr].extLocation.partitionReferenceNum = 0; } else laarr[curr].extLength = (etype << 30) | (offset << inode->i_sb->s_blocksize_bits); curr ++; (*c) ++; (*endnum) ++; } laarr[curr].extLocation.logicalBlockNum = newblocknum; if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) laarr[curr].extLocation.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; laarr[curr].extLength = EXT_RECORDED_ALLOCATED | inode->i_sb->s_blocksize; curr ++; if (blen != offset + 1) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) laarr[curr].extLocation.logicalBlockNum += (offset + 1); laarr[curr].extLength = (etype << 30) | ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits); curr ++; (*endnum) ++; } } } static void udf_prealloc_extents(struct inode *inode, int c, int lastblock, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) { int start, length = 0, currlength = 0, i; if (*endnum >= (c+1)) { if (!lastblock) return; else start = c; } else { if ((laarr[c+1].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { start = c+1; length = currlength = (((laarr[c+1].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); } else start = c; } for (i=start+1; i<=*endnum; i++) { if (i == *endnum) { if (lastblock) length += UDF_DEFAULT_PREALLOC_BLOCKS; } else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) length += (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); else break; } if (length) { int next = laarr[start].extLocation.logicalBlockNum + (((laarr[start].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); int numalloc = udf_prealloc_blocks(inode->i_sb, inode, laarr[start].extLocation.partitionReferenceNum, next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length : UDF_DEFAULT_PREALLOC_BLOCKS) - currlength); if (numalloc) { if (start == (c+1)) laarr[start].extLength += (numalloc << inode->i_sb->s_blocksize_bits); else { memmove(&laarr[c+2], &laarr[c+1], sizeof(long_ad) * (*endnum - (c+1))); (*endnum) ++; laarr[c+1].extLocation.logicalBlockNum = next; laarr[c+1].extLocation.partitionReferenceNum = laarr[c].extLocation.partitionReferenceNum; laarr[c+1].extLength = EXT_NOT_RECORDED_ALLOCATED | (numalloc << inode->i_sb->s_blocksize_bits); start = c+1; } for (i=start+1; numalloc && i<*endnum; i++) { int elen = ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; if (elen > numalloc) { laarr[i].extLength -= (numalloc << inode->i_sb->s_blocksize_bits); numalloc = 0; } else { numalloc -= elen; if (*endnum > (i+1)) memmove(&laarr[i], &laarr[i+1], sizeof(long_ad) * (*endnum - (i+1))); i --; (*endnum) --; } } UDF_I_LENEXTENTS(inode) += numalloc << inode->i_sb->s_blocksize_bits; } } } static void udf_merge_extents(struct inode *inode, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) { int i; for (i=0; i<(*endnum-1); i++) { if ((laarr[i].extLength >> 30) == (laarr[i+1].extLength >> 30)) { if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) || ((laarr[i+1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) == (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits))) { if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { laarr[i+1].extLength = (laarr[i+1].extLength - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; laarr[i+1].extLocation.logicalBlockNum = laarr[i].extLocation.logicalBlockNum + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) >> inode->i_sb->s_blocksize_bits); } else { laarr[i].extLength = laarr[i+1].extLength + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); if (*endnum > (i+2)) memmove(&laarr[i+1], &laarr[i+2], sizeof(long_ad) * (*endnum - (i+2))); i --; (*endnum) --; } } } else if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) && ((laarr[i+1].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) { udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); laarr[i].extLocation.logicalBlockNum = 0; laarr[i].extLocation.partitionReferenceNum = 0; if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { laarr[i+1].extLength = (laarr[i+1].extLength - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; } else { laarr[i].extLength = laarr[i+1].extLength + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); if (*endnum > (i+2)) memmove(&laarr[i+1], &laarr[i+2], sizeof(long_ad) * (*endnum - (i+2))); i --; (*endnum) --; } } else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); laarr[i].extLocation.logicalBlockNum = 0; laarr[i].extLocation.partitionReferenceNum = 0; laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) | EXT_NOT_RECORDED_NOT_ALLOCATED; } } } static void udf_update_extents(struct inode *inode, kernel_long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum, kernel_lb_addr pbloc, uint32_t pextoffset, struct buffer_head **pbh) { int start = 0, i; kernel_lb_addr tmploc; uint32_t tmplen; if (startnum > endnum) { for (i=0; i<(startnum-endnum); i++) { udf_delete_aext(inode, pbloc, pextoffset, laarr[i].extLocation, laarr[i].extLength, *pbh); } } else if (startnum < endnum) { for (i=0; i<(endnum-startnum); i++) { udf_insert_aext(inode, pbloc, pextoffset, laarr[i].extLocation, laarr[i].extLength, *pbh); udf_next_aext(inode, &pbloc, &pextoffset, &laarr[i].extLocation, &laarr[i].extLength, pbh, 1); start ++; } } for (i=start; ii_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; lock_kernel(); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + inode->i_size)) { udf_expand_file_adinicb(inode, inode->i_size, &err); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { inode->i_size = UDF_I_LENALLOC(inode); unlock_kernel(); return; } else udf_truncate_extents(inode); } else { offset = inode->i_size & (inode->i_sb->s_blocksize - 1); memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, 0x00, inode->i_sb->s_blocksize - offset - udf_file_entry_alloc_offset(inode)); UDF_I_LENALLOC(inode) = inode->i_size; } } else { block_truncate_page(inode->i_mapping, inode->i_size, udf_get_block); udf_truncate_extents(inode); } inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb); if (IS_SYNC(inode)) udf_sync_inode (inode); else mark_inode_dirty(inode); unlock_kernel(); } static void __udf_read_inode(struct inode *inode) { struct buffer_head *bh = NULL; struct fileEntry *fe; uint16_t ident; /* * Set defaults, but the inode is still incomplete! * Note: get_new_inode() sets the following on a new inode: * i_sb = sb * i_no = ino * i_flags = sb->s_flags * i_state = 0 * clean_inode(): zero fills and sets * i_count = 1 * i_nlink = 1 * i_op = NULL; */ inode->i_blksize = PAGE_SIZE; bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident); if (!bh) { printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed !bh\n", inode->i_ino); make_bad_inode(inode); return; } if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE && ident != TAG_IDENT_USE) { printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed ident=%d\n", inode->i_ino, ident); udf_release_data(bh); make_bad_inode(inode); return; } fe = (struct fileEntry *)bh->b_data; if (le16_to_cpu(fe->icbTag.strategyType) == 4096) { struct buffer_head *ibh = NULL, *nbh = NULL; struct indirectEntry *ie; ibh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 1, &ident); if (ident == TAG_IDENT_IE) { if (ibh) { kernel_lb_addr loc; ie = (struct indirectEntry *)ibh->b_data; loc = lelb_to_cpu(ie->indirectICB.extLocation); if (ie->indirectICB.extLength && (nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident))) { if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE) { memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(kernel_lb_addr)); udf_release_data(bh); udf_release_data(ibh); udf_release_data(nbh); __udf_read_inode(inode); return; } else { udf_release_data(nbh); udf_release_data(ibh); } } else udf_release_data(ibh); } } else udf_release_data(ibh); } else if (le16_to_cpu(fe->icbTag.strategyType) != 4) { printk(KERN_ERR "udf: unsupported strategy type: %d\n", le16_to_cpu(fe->icbTag.strategyType)); udf_release_data(bh); make_bad_inode(inode); return; } udf_fill_inode(inode, bh); udf_release_data(bh); } static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) { struct fileEntry *fe; struct extendedFileEntry *efe; time_t convtime; long convtime_usec; int offset; fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; if (le16_to_cpu(fe->icbTag.strategyType) == 4) UDF_I_STRAT4096(inode) = 0; else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */ UDF_I_STRAT4096(inode) = 1; UDF_I_ALLOCTYPE(inode) = le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK; UDF_I_UNIQUE(inode) = 0; UDF_I_LENEATTR(inode) = 0; UDF_I_LENEXTENTS(inode) = 0; UDF_I_LENALLOC(inode) = 0; UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; UDF_I_NEXT_ALLOC_GOAL(inode) = 0; if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE) { UDF_I_EFE(inode) = 1; UDF_I_USE(inode) = 0; UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct extendedFileEntry), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); } else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE) { UDF_I_EFE(inode) = 0; UDF_I_USE(inode) = 0; UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct fileEntry), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); } else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) { UDF_I_EFE(inode) = 0; UDF_I_USE(inode) = 1; UDF_I_LENALLOC(inode) = le32_to_cpu( ((struct unallocSpaceEntry *)bh->b_data)->lengthAllocDescs); UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry), GFP_KERNEL); memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct unallocSpaceEntry), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); return; } inode->i_uid = le32_to_cpu(fe->uid); if (inode->i_uid == -1 || UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_IGNORE)) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; inode->i_gid = le32_to_cpu(fe->gid); if (inode->i_gid == -1 || UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_IGNORE)) inode->i_gid = UDF_SB(inode->i_sb)->s_gid; inode->i_nlink = le16_to_cpu(fe->fileLinkCount); if (!inode->i_nlink) inode->i_nlink = 1; inode->i_size = le64_to_cpu(fe->informationLength); UDF_I_LENEXTENTS(inode) = inode->i_size; inode->i_mode = udf_convert_permissions(fe); inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask; if (UDF_I_EFE(inode) == 0) { inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) << (inode->i_sb->s_blocksize_bits - 9); if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->accessTime)) ) { inode->i_atime.tv_sec = convtime; inode->i_atime.tv_nsec = convtime_usec * 1000; } else { inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->modificationTime)) ) { inode->i_mtime.tv_sec = convtime; inode->i_mtime.tv_nsec = convtime_usec * 1000; } else { inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->attrTime)) ) { inode->i_ctime.tv_sec = convtime; inode->i_ctime.tv_nsec = convtime_usec * 1000; } else { inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } UDF_I_UNIQUE(inode) = le64_to_cpu(fe->uniqueID); UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr); UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs); offset = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode); } else { inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << (inode->i_sb->s_blocksize_bits - 9); if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->accessTime)) ) { inode->i_atime.tv_sec = convtime; inode->i_atime.tv_nsec = convtime_usec * 1000; } else { inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->modificationTime)) ) { inode->i_mtime.tv_sec = convtime; inode->i_mtime.tv_nsec = convtime_usec * 1000; } else { inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->createTime)) ) { UDF_I_CRTIME(inode).tv_sec = convtime; UDF_I_CRTIME(inode).tv_nsec = convtime_usec * 1000; } else { UDF_I_CRTIME(inode) = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->attrTime)) ) { inode->i_ctime.tv_sec = convtime; inode->i_ctime.tv_nsec = convtime_usec * 1000; } else { inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } UDF_I_UNIQUE(inode) = le64_to_cpu(efe->uniqueID); UDF_I_LENEATTR(inode) = le32_to_cpu(efe->lengthExtendedAttr); UDF_I_LENALLOC(inode) = le32_to_cpu(efe->lengthAllocDescs); offset = sizeof(struct extendedFileEntry) + UDF_I_LENEATTR(inode); } switch (fe->icbTag.fileType) { case ICBTAG_FILE_TYPE_DIRECTORY: { inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; inode->i_mode |= S_IFDIR; inode->i_nlink ++; break; } case ICBTAG_FILE_TYPE_REALTIME: case ICBTAG_FILE_TYPE_REGULAR: case ICBTAG_FILE_TYPE_UNDEF: { if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) inode->i_data.a_ops = &udf_adinicb_aops; else inode->i_data.a_ops = &udf_aops; inode->i_op = &udf_file_inode_operations; inode->i_fop = &udf_file_operations; inode->i_mode |= S_IFREG; break; } case ICBTAG_FILE_TYPE_BLOCK: { inode->i_mode |= S_IFBLK; break; } case ICBTAG_FILE_TYPE_CHAR: { inode->i_mode |= S_IFCHR; break; } case ICBTAG_FILE_TYPE_FIFO: { init_special_inode(inode, inode->i_mode | S_IFIFO, 0); break; } case ICBTAG_FILE_TYPE_SOCKET: { init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); break; } case ICBTAG_FILE_TYPE_SYMLINK: { inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; inode->i_mode = S_IFLNK|S_IRWXUGO; break; } default: { printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n", inode->i_ino, fe->icbTag.fileType); make_bad_inode(inode); return; } } if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { struct deviceSpec *dsea = (struct deviceSpec *) udf_get_extendedattr(inode, 12, 1); if (dsea) { init_special_inode(inode, inode->i_mode, MKDEV( le32_to_cpu(dsea->majorDeviceIdent), le32_to_cpu(dsea->minorDeviceIdent))); /* Developer ID ??? */ } else { make_bad_inode(inode); } } } static mode_t udf_convert_permissions(struct fileEntry *fe) { mode_t mode; uint32_t permissions; uint32_t flags; permissions = le32_to_cpu(fe->permissions); flags = le16_to_cpu(fe->icbTag.flags); mode = (( permissions ) & S_IRWXO) | (( permissions >> 2 ) & S_IRWXG) | (( permissions >> 4 ) & S_IRWXU) | (( flags & ICBTAG_FLAG_SETUID) ? S_ISUID : 0) | (( flags & ICBTAG_FLAG_SETGID) ? S_ISGID : 0) | (( flags & ICBTAG_FLAG_STICKY) ? S_ISVTX : 0); return mode; } /* * udf_write_inode * * PURPOSE * Write out the specified inode. * * DESCRIPTION * This routine is called whenever an inode is synced. * Currently this routine is just a placeholder. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_write_inode(struct inode * inode, int sync) { int ret; lock_kernel(); ret = udf_update_inode(inode, sync); unlock_kernel(); return ret; } int udf_sync_inode(struct inode * inode) { return udf_update_inode(inode, 1); } static int udf_update_inode(struct inode *inode, int do_sync) { struct buffer_head *bh = NULL; struct fileEntry *fe; struct extendedFileEntry *efe; uint32_t udfperms; uint16_t icbflags; uint16_t crclen; int i; kernel_timestamp cpu_time; int err = 0; bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0)); if (!bh) { udf_debug("bread failure\n"); return -EIO; } memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) { struct unallocSpaceEntry *use = (struct unallocSpaceEntry *)bh->b_data; use->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); memcpy(bh->b_data + sizeof(struct unallocSpaceEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); crclen = sizeof(struct unallocSpaceEntry) + UDF_I_LENALLOC(inode) - sizeof(tag); use->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); use->descTag.descCRCLength = cpu_to_le16(crclen); use->descTag.descCRC = cpu_to_le16(udf_crc((char *)use + sizeof(tag), crclen, 0)); use->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) use->descTag.tagChecksum += ((uint8_t *)&(use->descTag))[i]; mark_buffer_dirty(bh); udf_release_data(bh); return err; } if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_FORGET)) fe->uid = cpu_to_le32(-1); else fe->uid = cpu_to_le32(inode->i_uid); if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_FORGET)) fe->gid = cpu_to_le32(-1); else fe->gid = cpu_to_le32(inode->i_gid); udfperms = ((inode->i_mode & S_IRWXO) ) | ((inode->i_mode & S_IRWXG) << 2) | ((inode->i_mode & S_IRWXU) << 4); udfperms |= (le32_to_cpu(fe->permissions) & (FE_PERM_O_DELETE | FE_PERM_O_CHATTR | FE_PERM_G_DELETE | FE_PERM_G_CHATTR | FE_PERM_U_DELETE | FE_PERM_U_CHATTR)); fe->permissions = cpu_to_le32(udfperms); if (S_ISDIR(inode->i_mode)) fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1); else fe->fileLinkCount = cpu_to_le16(inode->i_nlink); fe->informationLength = cpu_to_le64(inode->i_size); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { regid *eid; struct deviceSpec *dsea = (struct deviceSpec *) udf_get_extendedattr(inode, 12, 1); if (!dsea) { dsea = (struct deviceSpec *) udf_add_extendedattr(inode, sizeof(struct deviceSpec) + sizeof(regid), 12, 0x3); dsea->attrType = cpu_to_le32(12); dsea->attrSubtype = 1; dsea->attrLength = cpu_to_le32(sizeof(struct deviceSpec) + sizeof(regid)); dsea->impUseLength = cpu_to_le32(sizeof(regid)); } eid = (regid *)dsea->impUse; memset(eid, 0, sizeof(regid)); strcpy(eid->ident, UDF_ID_DEVELOPER); eid->identSuffix[0] = UDF_OS_CLASS_UNIX; eid->identSuffix[1] = UDF_OS_ID_LINUX; dsea->majorDeviceIdent = cpu_to_le32(imajor(inode)); dsea->minorDeviceIdent = cpu_to_le32(iminor(inode)); } if (UDF_I_EFE(inode) == 0) { memcpy(bh->b_data + sizeof(struct fileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); fe->logicalBlocksRecorded = cpu_to_le64( (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); if (udf_time_to_stamp(&cpu_time, inode->i_atime)) fe->accessTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) fe->modificationTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) fe->attrTime = cpu_to_lets(cpu_time); memset(&(fe->impIdent), 0, sizeof(regid)); strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER); fe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; fe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; fe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); fe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); fe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); fe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_FE); crclen = sizeof(struct fileEntry); } else { memcpy(bh->b_data + sizeof(struct extendedFileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); efe->objectSize = cpu_to_le64(inode->i_size); efe->logicalBlocksRecorded = cpu_to_le64( (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); if (UDF_I_CRTIME(inode).tv_sec > inode->i_atime.tv_sec || (UDF_I_CRTIME(inode).tv_sec == inode->i_atime.tv_sec && UDF_I_CRTIME(inode).tv_nsec > inode->i_atime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_atime; } if (UDF_I_CRTIME(inode).tv_sec > inode->i_mtime.tv_sec || (UDF_I_CRTIME(inode).tv_sec == inode->i_mtime.tv_sec && UDF_I_CRTIME(inode).tv_nsec > inode->i_mtime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_mtime; } if (UDF_I_CRTIME(inode).tv_sec > inode->i_ctime.tv_sec || (UDF_I_CRTIME(inode).tv_sec == inode->i_ctime.tv_sec && UDF_I_CRTIME(inode).tv_nsec > inode->i_ctime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_ctime; } if (udf_time_to_stamp(&cpu_time, inode->i_atime)) efe->accessTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) efe->modificationTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, UDF_I_CRTIME(inode))) efe->createTime = cpu_to_lets(cpu_time); if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) efe->attrTime = cpu_to_lets(cpu_time); memset(&(efe->impIdent), 0, sizeof(regid)); strcpy(efe->impIdent.ident, UDF_ID_DEVELOPER); efe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; efe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; efe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); efe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); efe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); efe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EFE); crclen = sizeof(struct extendedFileEntry); } if (UDF_I_STRAT4096(inode)) { fe->icbTag.strategyType = cpu_to_le16(4096); fe->icbTag.strategyParameter = cpu_to_le16(1); fe->icbTag.numEntries = cpu_to_le16(2); } else { fe->icbTag.strategyType = cpu_to_le16(4); fe->icbTag.numEntries = cpu_to_le16(1); } if (S_ISDIR(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_DIRECTORY; else if (S_ISREG(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_REGULAR; else if (S_ISLNK(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_SYMLINK; else if (S_ISBLK(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_BLOCK; else if (S_ISCHR(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_CHAR; else if (S_ISFIFO(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_FIFO; else if (S_ISSOCK(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_SOCKET; icbflags = UDF_I_ALLOCTYPE(inode) | ((inode->i_mode & S_ISUID) ? ICBTAG_FLAG_SETUID : 0) | ((inode->i_mode & S_ISGID) ? ICBTAG_FLAG_SETGID : 0) | ((inode->i_mode & S_ISVTX) ? ICBTAG_FLAG_STICKY : 0) | (le16_to_cpu(fe->icbTag.flags) & ~(ICBTAG_FLAG_AD_MASK | ICBTAG_FLAG_SETUID | ICBTAG_FLAG_SETGID | ICBTAG_FLAG_STICKY)); fe->icbTag.flags = cpu_to_le16(icbflags); if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) fe->descTag.descVersion = cpu_to_le16(3); else fe->descTag.descVersion = cpu_to_le16(2); fe->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); fe->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); crclen += UDF_I_LENEATTR(inode) + UDF_I_LENALLOC(inode) - sizeof(tag); fe->descTag.descCRCLength = cpu_to_le16(crclen); fe->descTag.descCRC = cpu_to_le16(udf_crc((char *)fe + sizeof(tag), crclen, 0)); fe->descTag.tagChecksum = 0; for (i=0; i<16; i++) if (i != 4) fe->descTag.tagChecksum += ((uint8_t *)&(fe->descTag))[i]; /* write the data blocks */ mark_buffer_dirty(bh); if (do_sync) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { printk("IO error syncing udf inode [%s:%08lx]\n", inode->i_sb->s_id, inode->i_ino); err = -EIO; } } udf_release_data(bh); return err; } struct inode * udf_iget(struct super_block *sb, kernel_lb_addr ino) { unsigned long block = udf_get_lb_pblock(sb, ino, 0); struct inode *inode = iget_locked(sb, block); if (!inode) return NULL; if (inode->i_state & I_NEW) { memcpy(&UDF_I_LOCATION(inode), &ino, sizeof(kernel_lb_addr)); __udf_read_inode(inode); unlock_new_inode(inode); } if (is_bad_inode(inode)) goto out_iput; if (ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum)) { udf_debug("block=%d, partition=%d out of range\n", ino.logicalBlockNum, ino.partitionReferenceNum); make_bad_inode(inode); goto out_iput; } return inode; out_iput: iput(inode); return NULL; } int8_t udf_add_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, kernel_lb_addr eloc, uint32_t elen, struct buffer_head **bh, int inc) { int adsize; short_ad *sad = NULL; long_ad *lad = NULL; struct allocExtDesc *aed; int8_t etype; uint8_t *ptr; if (!*bh) ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); else ptr = (*bh)->b_data + *extoffset; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else return -1; if (*extoffset + (2 * adsize) > inode->i_sb->s_blocksize) { char *sptr, *dptr; struct buffer_head *nbh; int err, loffset; kernel_lb_addr obloc = *bloc; if (!(bloc->logicalBlockNum = udf_new_block(inode->i_sb, NULL, obloc.partitionReferenceNum, obloc.logicalBlockNum, &err))) { return -1; } if (!(nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb, *bloc, 0)))) { return -1; } lock_buffer(nbh); memset(nbh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(nbh); unlock_buffer(nbh); mark_buffer_dirty_inode(nbh, inode); aed = (struct allocExtDesc *)(nbh->b_data); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum); if (*extoffset + adsize > inode->i_sb->s_blocksize) { loffset = *extoffset; aed->lengthAllocDescs = cpu_to_le32(adsize); sptr = ptr - adsize; dptr = nbh->b_data + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); *extoffset = sizeof(struct allocExtDesc) + adsize; } else { loffset = *extoffset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); sptr = ptr; *extoffset = sizeof(struct allocExtDesc); if (*bh) { aed = (struct allocExtDesc *)(*bh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); } else { UDF_I_LENALLOC(inode) += adsize; mark_inode_dirty(inode); } } if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1, bloc->logicalBlockNum, sizeof(tag)); else udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1, bloc->logicalBlockNum, sizeof(tag)); switch (UDF_I_ALLOCTYPE(inode)) { case ICBTAG_FLAG_AD_SHORT: { sad = (short_ad *)sptr; sad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | inode->i_sb->s_blocksize); sad->extPosition = cpu_to_le32(bloc->logicalBlockNum); break; } case ICBTAG_FLAG_AD_LONG: { lad = (long_ad *)sptr; lad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | inode->i_sb->s_blocksize); lad->extLocation = cpu_to_lelb(*bloc); memset(lad->impUse, 0x00, sizeof(lad->impUse)); break; } } if (*bh) { if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((*bh)->b_data, loffset); else udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(*bh, inode); udf_release_data(*bh); } else mark_inode_dirty(inode); *bh = nbh; } etype = udf_write_aext(inode, *bloc, extoffset, eloc, elen, *bh, inc); if (!*bh) { UDF_I_LENALLOC(inode) += adsize; mark_inode_dirty(inode); } else { aed = (struct allocExtDesc *)(*bh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize)); else udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(*bh, inode); } return etype; } int8_t udf_write_aext(struct inode *inode, kernel_lb_addr bloc, int *extoffset, kernel_lb_addr eloc, uint32_t elen, struct buffer_head *bh, int inc) { int adsize; uint8_t *ptr; if (!bh) ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); else { ptr = bh->b_data + *extoffset; atomic_inc(&bh->b_count); } switch (UDF_I_ALLOCTYPE(inode)) { case ICBTAG_FLAG_AD_SHORT: { short_ad *sad = (short_ad *)ptr; sad->extLength = cpu_to_le32(elen); sad->extPosition = cpu_to_le32(eloc.logicalBlockNum); adsize = sizeof(short_ad); break; } case ICBTAG_FLAG_AD_LONG: { long_ad *lad = (long_ad *)ptr; lad->extLength = cpu_to_le32(elen); lad->extLocation = cpu_to_lelb(eloc); memset(lad->impUse, 0x00, sizeof(lad->impUse)); adsize = sizeof(long_ad); break; } default: return -1; } if (bh) { if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) { struct allocExtDesc *aed = (struct allocExtDesc *)(bh)->b_data; udf_update_tag((bh)->b_data, le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct allocExtDesc)); } mark_buffer_dirty_inode(bh, inode); udf_release_data(bh); } else mark_inode_dirty(inode); if (inc) *extoffset += adsize; return (elen >> 30); } int8_t udf_next_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc) { int8_t etype; while ((etype = udf_current_aext(inode, bloc, extoffset, eloc, elen, bh, inc)) == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { *bloc = *eloc; *extoffset = sizeof(struct allocExtDesc); udf_release_data(*bh); if (!(*bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, *bloc, 0)))) { udf_debug("reading block %d failed!\n", udf_get_lb_pblock(inode->i_sb, *bloc, 0)); return -1; } } return etype; } int8_t udf_current_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc) { int alen; int8_t etype; uint8_t *ptr; if (!*bh) { if (!(*extoffset)) *extoffset = udf_file_entry_alloc_offset(inode); ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); alen = udf_file_entry_alloc_offset(inode) + UDF_I_LENALLOC(inode); } else { if (!(*extoffset)) *extoffset = sizeof(struct allocExtDesc); ptr = (*bh)->b_data + *extoffset; alen = sizeof(struct allocExtDesc) + le32_to_cpu(((struct allocExtDesc *)(*bh)->b_data)->lengthAllocDescs); } switch (UDF_I_ALLOCTYPE(inode)) { case ICBTAG_FLAG_AD_SHORT: { short_ad *sad; if (!(sad = udf_get_fileshortad(ptr, alen, extoffset, inc))) return -1; etype = le32_to_cpu(sad->extLength) >> 30; eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; break; } case ICBTAG_FLAG_AD_LONG: { long_ad *lad; if (!(lad = udf_get_filelongad(ptr, alen, extoffset, inc))) return -1; etype = le32_to_cpu(lad->extLength) >> 30; *eloc = lelb_to_cpu(lad->extLocation); *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; break; } default: { udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); return -1; } } return etype; } static int8_t udf_insert_aext(struct inode *inode, kernel_lb_addr bloc, int extoffset, kernel_lb_addr neloc, uint32_t nelen, struct buffer_head *bh) { kernel_lb_addr oeloc; uint32_t oelen; int8_t etype; if (bh) atomic_inc(&bh->b_count); while ((etype = udf_next_aext(inode, &bloc, &extoffset, &oeloc, &oelen, &bh, 0)) != -1) { udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1); neloc = oeloc; nelen = (etype << 30) | oelen; } udf_add_aext(inode, &bloc, &extoffset, neloc, nelen, &bh, 1); udf_release_data(bh); return (nelen >> 30); } int8_t udf_delete_aext(struct inode *inode, kernel_lb_addr nbloc, int nextoffset, kernel_lb_addr eloc, uint32_t elen, struct buffer_head *nbh) { struct buffer_head *obh; kernel_lb_addr obloc; int oextoffset, adsize; int8_t etype; struct allocExtDesc *aed; if (nbh) { atomic_inc(&nbh->b_count); atomic_inc(&nbh->b_count); } if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else adsize = 0; obh = nbh; obloc = nbloc; oextoffset = nextoffset; if (udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1) == -1) return -1; while ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) { udf_write_aext(inode, obloc, &oextoffset, eloc, (etype << 30) | elen, obh, 1); if (obh != nbh) { obloc = nbloc; udf_release_data(obh); atomic_inc(&nbh->b_count); obh = nbh; oextoffset = nextoffset - adsize; } } memset(&eloc, 0x00, sizeof(kernel_lb_addr)); elen = 0; if (nbh != obh) { udf_free_blocks(inode->i_sb, inode, nbloc, 0, 1); udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); if (!obh) { UDF_I_LENALLOC(inode) -= (adsize * 2); mark_inode_dirty(inode); } else { aed = (struct allocExtDesc *)(obh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize)); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((obh)->b_data, oextoffset - (2*adsize)); else udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(obh, inode); } } else { udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); if (!obh) { UDF_I_LENALLOC(inode) -= adsize; mark_inode_dirty(inode); } else { aed = (struct allocExtDesc *)(obh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag((obh)->b_data, oextoffset - adsize); else udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(obh, inode); } } udf_release_data(nbh); udf_release_data(obh); return (elen >> 30); } int8_t inode_bmap(struct inode *inode, int block, kernel_lb_addr *bloc, uint32_t *extoffset, kernel_lb_addr *eloc, uint32_t *elen, uint32_t *offset, struct buffer_head **bh) { uint64_t lbcount = 0, bcount = (uint64_t)block << inode->i_sb->s_blocksize_bits; int8_t etype; if (block < 0) { printk(KERN_ERR "udf: inode_bmap: block < 0\n"); return -1; } *extoffset = 0; *elen = 0; *bloc = UDF_I_LOCATION(inode); do { if ((etype = udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, 1)) == -1) { *offset = bcount - lbcount; UDF_I_LENEXTENTS(inode) = lbcount; return -1; } lbcount += *elen; } while (lbcount <= bcount); *offset = bcount + *elen - lbcount; return etype; } long udf_block_map(struct inode *inode, long block) { kernel_lb_addr eloc, bloc; uint32_t offset, extoffset, elen; struct buffer_head *bh = NULL; int ret; lock_kernel(); if (inode_bmap(inode, block, &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) ret = udf_get_lb_pblock(inode->i_sb, eloc, offset >> inode->i_sb->s_blocksize_bits); else ret = 0; unlock_kernel(); udf_release_data(bh); if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV)) return udf_fixed_to_variable(ret); else return ret; } linux-2.6.17/fs/udf/udfend.h0000644000000000000000000000355210564537530014265 0ustar rootroot#ifndef __UDF_ENDIAN_H #define __UDF_ENDIAN_H #include #include static inline kernel_lb_addr lelb_to_cpu(lb_addr in) { kernel_lb_addr out; out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum); out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum); return out; } static inline lb_addr cpu_to_lelb(kernel_lb_addr in) { lb_addr out; out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum); out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum); return out; } static inline kernel_timestamp lets_to_cpu(timestamp in) { kernel_timestamp out; memcpy(&out, &in, sizeof(timestamp)); out.typeAndTimezone = le16_to_cpu(in.typeAndTimezone); out.year = le16_to_cpu(in.year); return out; } static inline short_ad lesa_to_cpu(short_ad in) { short_ad out; out.extLength = le32_to_cpu(in.extLength); out.extPosition = le32_to_cpu(in.extPosition); return out; } static inline short_ad cpu_to_lesa(short_ad in) { short_ad out; out.extLength = cpu_to_le32(in.extLength); out.extPosition = cpu_to_le32(in.extPosition); return out; } static inline kernel_long_ad lela_to_cpu(long_ad in) { kernel_long_ad out; out.extLength = le32_to_cpu(in.extLength); out.extLocation = lelb_to_cpu(in.extLocation); return out; } static inline long_ad cpu_to_lela(kernel_long_ad in) { long_ad out; out.extLength = cpu_to_le32(in.extLength); out.extLocation = cpu_to_lelb(in.extLocation); return out; } static inline kernel_extent_ad leea_to_cpu(extent_ad in) { kernel_extent_ad out; out.extLength = le32_to_cpu(in.extLength); out.extLocation = le32_to_cpu(in.extLocation); return out; } static inline timestamp cpu_to_lets(kernel_timestamp in) { timestamp out; memcpy(&out, &in, sizeof(timestamp)); out.typeAndTimezone = cpu_to_le16(in.typeAndTimezone); out.year = cpu_to_le16(in.year); return out; } #endif /* __UDF_ENDIAN_H */ linux-2.6.17/fs/udf/Makefile0000644000000000000000000000040610564537530014302 0ustar rootroot# # Makefile for the linux udf-filesystem routines. # obj-$(CONFIG_UDF_FS) += udf.o udf-objs := balloc.o dir.o file.o ialloc.o inode.o lowlevel.o namei.o \ partition.o super.o truncate.o symlink.o fsync.o \ crc.o directory.o misc.o udftime.o unicode.o linux-2.6.17/fs/udf/file.c0000644000000000000000000001441410564537530013731 0ustar rootroot/* * file.c * * PURPOSE * File handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-1999 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 10/02/98 dgb Attempt to integrate into udf.o * 10/07/98 Switched to using generic_readpage, etc., like isofs * And it works! * 12/06/98 blf Added udf_file_read. uses generic_file_read for all cases but * ICBTAG_FLAG_AD_IN_ICB. * 04/06/99 64 bit file handling on 32 bit systems taken from ext2 file.c * 05/12/99 Preliminary file write support */ #include "udfdecl.h" #include #include #include #include #include /* memset */ #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" static int udf_adinicb_readpage(struct file *file, struct page * page) { struct inode *inode = page->mapping->host; char *kaddr; BUG_ON(!PageLocked(page)); kaddr = kmap(page); memset(kaddr, 0, PAGE_CACHE_SIZE); memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), inode->i_size); flush_dcache_page(page); SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; } static int udf_adinicb_writepage(struct page *page, struct writeback_control *wbc) { struct inode *inode = page->mapping->host; char *kaddr; BUG_ON(!PageLocked(page)); kaddr = kmap(page); memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), kaddr, inode->i_size); mark_inode_dirty(inode); SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; } static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { kmap(page); return 0; } static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { struct inode *inode = page->mapping->host; char *kaddr = page_address(page); memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, kaddr + offset, to - offset); mark_inode_dirty(inode); SetPageUptodate(page); kunmap(page); /* only one page here */ if (to > inode->i_size) inode->i_size = to; return 0; } struct address_space_operations udf_adinicb_aops = { .readpage = udf_adinicb_readpage, .writepage = udf_adinicb_writepage, .sync_page = block_sync_page, .prepare_write = udf_adinicb_prepare_write, .commit_write = udf_adinicb_commit_write, }; static ssize_t udf_file_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos) { ssize_t retval; struct inode *inode = file->f_dentry->d_inode; int err, pos; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { if (file->f_flags & O_APPEND) pos = inode->i_size; else pos = *ppos; if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + pos + count)) { udf_expand_file_adinicb(inode, pos + count, &err); if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { udf_debug("udf_expand_adinicb: err=%d\n", err); return err; } } else { if (pos + count > inode->i_size) UDF_I_LENALLOC(inode) = pos + count; else UDF_I_LENALLOC(inode) = inode->i_size; } } retval = generic_file_write(file, buf, count, ppos); if (retval > 0) mark_inode_dirty(inode); return retval; } /* * udf_ioctl * * PURPOSE * Issue an ioctl. * * DESCRIPTION * Optional - sys_ioctl() will return -ENOTTY if this routine is not * available, and the ioctl cannot be handled without filesystem help. * * sys_ioctl() handles these ioctls that apply only to regular files: * FIBMAP [requires udf_block_map()], FIGETBSZ, FIONREAD * These ioctls are also handled by sys_ioctl(): * FIOCLEX, FIONCLEX, FIONBIO, FIOASYNC * All other ioctls are passed to the filesystem. * * Refer to sys_ioctl() in fs/ioctl.c * sys_ioctl() -> . * * PRE-CONDITIONS * inode Pointer to inode that ioctl was issued on. * filp Pointer to file that ioctl was issued on. * cmd The ioctl command. * arg The ioctl argument [can be interpreted as a * user-space pointer if desired]. * * POST-CONDITIONS * Success (>=0) or an error code (<=0) that * sys_ioctl() will return. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int result = -EINVAL; if ( file_permission(filp, MAY_READ) != 0 ) { udf_debug("no permission to access inode %lu\n", inode->i_ino); return -EPERM; } if ( !arg ) { udf_debug("invalid argument to udf_ioctl\n"); return -EINVAL; } switch (cmd) { case UDF_GETVOLIDENT: return copy_to_user((char __user *)arg, UDF_SB_VOLIDENT(inode->i_sb), 32) ? -EFAULT : 0; case UDF_RELOCATE_BLOCKS: { long old, new; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (get_user(old, (long __user *)arg)) return -EFAULT; if ((result = udf_relocate_blocks(inode->i_sb, old, &new)) == 0) result = put_user(new, (long __user *)arg); return result; } case UDF_GETEASIZE: result = put_user(UDF_I_LENEATTR(inode), (int __user *)arg); break; case UDF_GETEABLOCK: result = copy_to_user((char __user *)arg, UDF_I_DATA(inode), UDF_I_LENEATTR(inode)) ? -EFAULT : 0; break; } return result; } /* * udf_release_file * * PURPOSE * Called when all references to the file are closed * * DESCRIPTION * Discard prealloced blocks * * HISTORY * */ static int udf_release_file(struct inode * inode, struct file * filp) { if (filp->f_mode & FMODE_WRITE) { lock_kernel(); udf_discard_prealloc(inode); unlock_kernel(); } return 0; } const struct file_operations udf_file_operations = { .read = generic_file_read, .ioctl = udf_ioctl, .open = generic_file_open, .mmap = generic_file_mmap, .write = udf_file_write, .release = udf_release_file, .fsync = udf_fsync_file, .sendfile = generic_file_sendfile, }; struct inode_operations udf_file_inode_operations = { .truncate = udf_truncate, }; linux-2.6.17/fs/udf/lowlevel.c0000644000000000000000000000305310564537530014640 0ustar rootroot/* * lowlevel.c * * PURPOSE * Low Level Device Routines for the UDF filesystem * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2001 Ben Fennema * * HISTORY * * 03/26/99 blf Created. */ #include "udfdecl.h" #include #include #include #include #include "udf_sb.h" unsigned int udf_get_last_session(struct super_block *sb) { struct cdrom_multisession ms_info; unsigned int vol_desc_start; struct block_device *bdev = sb->s_bdev; int i; vol_desc_start=0; ms_info.addr_format=CDROM_LBA; i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); #define WE_OBEY_THE_WRITTEN_STANDARDS 1 if (i == 0) { udf_debug("XA disk: %s, vol_desc_start=%d\n", (ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba); #if WE_OBEY_THE_WRITTEN_STANDARDS if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ #endif vol_desc_start = ms_info.addr.lba; } else { udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i); } return vol_desc_start; } unsigned long udf_get_last_block(struct super_block *sb) { struct block_device *bdev = sb->s_bdev; unsigned long lblock = 0; if (ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock)) lblock = bdev->bd_inode->i_size >> sb->s_blocksize_bits; if (lblock) return lblock - 1; else return 0; } linux-2.6.17/fs/udf/ialloc.c0000644000000000000000000001045310564537530014254 0ustar rootroot/* * ialloc.c * * PURPOSE * Inode allocation handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema * * HISTORY * * 02/24/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include #include #include "udf_i.h" #include "udf_sb.h" void udf_free_inode(struct inode * inode) { struct super_block *sb = inode->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); /* * Note: we must free any quota before locking the superblock, * as writing the quota to disk may need the lock as well. */ DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); clear_inode(inode); mutex_lock(&sbi->s_alloc_mutex); if (sbi->s_lvidbh) { if (S_ISDIR(inode->i_mode)) UDF_SB_LVIDIU(sb)->numDirs = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) - 1); else UDF_SB_LVIDIU(sb)->numFiles = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1); mark_buffer_dirty(sbi->s_lvidbh); } mutex_unlock(&sbi->s_alloc_mutex); udf_free_blocks(sb, NULL, UDF_I_LOCATION(inode), 0, 1); } struct inode * udf_new_inode (struct inode *dir, int mode, int * err) { struct super_block *sb = dir->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); struct inode * inode; int block; uint32_t start = UDF_I_LOCATION(dir).logicalBlockNum; inode = new_inode(sb); if (!inode) { *err = -ENOMEM; return NULL; } *err = -ENOSPC; block = udf_new_block(dir->i_sb, NULL, UDF_I_LOCATION(dir).partitionReferenceNum, start, err); if (*err) { iput(inode); return NULL; } mutex_lock(&sbi->s_alloc_mutex); UDF_I_UNIQUE(inode) = 0; UDF_I_LENEXTENTS(inode) = 0; UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; UDF_I_NEXT_ALLOC_GOAL(inode) = 0; UDF_I_STRAT4096(inode) = 0; if (UDF_SB_LVIDBH(sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(sb)->logicalVolContentsUse); if (S_ISDIR(mode)) UDF_SB_LVIDIU(sb)->numDirs = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) + 1); else UDF_SB_LVIDIU(sb)->numFiles = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + 1); UDF_I_UNIQUE(inode) = uniqueID = le64_to_cpu(lvhd->uniqueID); if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } inode->i_mode = mode; inode->i_uid = current->fsuid; if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; } else inode->i_gid = current->fsgid; UDF_I_LOCATION(inode).logicalBlockNum = block; UDF_I_LOCATION(inode).partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum; inode->i_ino = udf_get_lb_pblock(sb, UDF_I_LOCATION(inode), 0); inode->i_blksize = PAGE_SIZE; inode->i_blocks = 0; UDF_I_LENEATTR(inode) = 0; UDF_I_LENALLOC(inode) = 0; UDF_I_USE(inode) = 0; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) { UDF_I_EFE(inode) = 1; UDF_UPDATE_UDFREV(inode->i_sb, UDF_VERS_USE_EXTENDED_FE); UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); } else { UDF_I_EFE(inode) = 0; UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct fileEntry)); } if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; inode->i_mtime = inode->i_atime = inode->i_ctime = UDF_I_CRTIME(inode) = current_fs_time(inode->i_sb); insert_inode_hash(inode); mark_inode_dirty(inode); mutex_unlock(&sbi->s_alloc_mutex); if (DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; iput(inode); *err = -EDQUOT; return NULL; } *err = 0; return inode; } linux-2.6.17/fs/udf/unicode.c0000644000000000000000000002522410564537530014441 0ustar rootroot/* * unicode.c * * PURPOSE * Routines for converting between UTF-8 and OSTA Compressed Unicode. * Also handles filename mangling * * DESCRIPTION * OSTA Compressed Unicode is explained in the OSTA UDF specification. * http://www.osta.org/ * UTF-8 is explained in the IETF RFC XXXX. * ftp://ftp.internic.net/rfc/rfcxxxx.txt * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. */ #include "udfdecl.h" #include #include /* for memset */ #include #include #include "udf_sb.h" static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int); static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) { if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN-2) ) return 0; memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src, strlen); dest->u_cmpID = 0x08; dest->u_len = strlen; return strlen; } /* * udf_build_ustr */ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) { int usesize; if ( (!dest) || (!ptr) || (!size) ) return -1; memset(dest, 0, sizeof(struct ustr)); usesize= (size > UDF_NAME_LEN) ? UDF_NAME_LEN : size; dest->u_cmpID=ptr[0]; dest->u_len=ptr[size-1]; memcpy(dest->u_name, ptr+1, usesize-1); return 0; } /* * udf_build_ustr_exact */ static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) { if ( (!dest) || (!ptr) || (!exactsize) ) return -1; memset(dest, 0, sizeof(struct ustr)); dest->u_cmpID=ptr[0]; dest->u_len=exactsize-1; memcpy(dest->u_name, ptr+1, exactsize-1); return 0; } /* * udf_ocu_to_utf8 * * PURPOSE * Convert OSTA Compressed Unicode to the UTF-8 equivalent. * * DESCRIPTION * This routine is only called by udf_filldir(). * * PRE-CONDITIONS * utf Pointer to UTF-8 output buffer. * ocu Pointer to OSTA Compressed Unicode input buffer * of size UDF_NAME_LEN bytes. * both of type "struct ustr *" * * POST-CONDITIONS * Zero on success. * * HISTORY * November 12, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) { uint8_t *ocu; uint32_t c; uint8_t cmp_id, ocu_len; int i; ocu = ocu_i->u_name; ocu_len = ocu_i->u_len; cmp_id = ocu_i->u_cmpID; utf_o->u_len = 0; if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); utf_o->u_cmpID = 0; utf_o->u_len = 0; return 0; } if ((cmp_id != 8) && (cmp_id != 16)) { printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); return 0; } for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) { /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; /* Compress Unicode to UTF-8 */ if (c < 0x80U) utf_o->u_name[utf_o->u_len++] = (uint8_t)c; else if (c < 0x800U) { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xc0 | (c >> 6)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); } else { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xe0 | (c >> 12)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | ((c >> 6) & 0x3f)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); } } utf_o->u_cmpID=8; return utf_o->u_len; } /* * * udf_utf8_to_ocu * * PURPOSE * Convert UTF-8 to the OSTA Compressed Unicode equivalent. * * DESCRIPTION * This routine is only called by udf_lookup(). * * PRE-CONDITIONS * ocu Pointer to OSTA Compressed Unicode output * buffer of size UDF_NAME_LEN bytes. * utf Pointer to UTF-8 input buffer. * utf_len Length of UTF-8 input buffer in bytes. * * POST-CONDITIONS * Zero on success. * * HISTORY * November 12, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length) { unsigned c, i, max_val, utf_char; int utf_cnt, u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: u_len = 0U; utf_char = 0U; utf_cnt = 0U; for (i = 0U; i < utf->u_len; i++) { c = (uint8_t)utf->u_name[i]; /* Complete a multi-byte UTF-8 character */ if (utf_cnt) { utf_char = (utf_char << 6) | (c & 0x3fU); if (--utf_cnt) continue; } else { /* Check for a multi-byte UTF-8 character */ if (c & 0x80U) { /* Start a multi-byte UTF-8 character */ if ((c & 0xe0U) == 0xc0U) { utf_char = c & 0x1fU; utf_cnt = 1; } else if ((c & 0xf0U) == 0xe0U) { utf_char = c & 0x0fU; utf_cnt = 2; } else if ((c & 0xf8U) == 0xf0U) { utf_char = c & 0x07U; utf_cnt = 3; } else if ((c & 0xfcU) == 0xf8U) { utf_char = c & 0x03U; utf_cnt = 4; } else if ((c & 0xfeU) == 0xfcU) { utf_char = c & 0x01U; utf_cnt = 5; } else goto error_out; continue; } else /* Single byte UTF-8 character (most common) */ utf_char = c; } /* Choose no compression if necessary */ if (utf_char > max_val) { if ( 0xffU == max_val ) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } goto error_out; } if (max_val == 0xffffU) { ocu[++u_len] = (uint8_t)(utf_char >> 8); } ocu[++u_len] = (uint8_t)(utf_char & 0xffU); } if (utf_cnt) { error_out: ocu[++u_len] = '?'; printk(KERN_DEBUG "udf: bad UTF-8 character\n"); } ocu[length - 1] = (uint8_t)u_len + 1; return u_len + 1; } static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, struct ustr *ocu_i) { uint8_t *ocu; uint32_t c; uint8_t cmp_id, ocu_len; int i; ocu = ocu_i->u_name; ocu_len = ocu_i->u_len; cmp_id = ocu_i->u_cmpID; utf_o->u_len = 0; if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); utf_o->u_cmpID = 0; utf_o->u_len = 0; return 0; } if ((cmp_id != 8) && (cmp_id != 16)) { printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); return 0; } for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) { /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; utf_o->u_len += nls->uni2char(c, &utf_o->u_name[utf_o->u_len], UDF_NAME_LEN - utf_o->u_len); } utf_o->u_cmpID=8; return utf_o->u_len; } static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, int length) { unsigned len, i, max_val; uint16_t uni_char; int u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: u_len = 0U; for (i = 0U; i < uni->u_len; i++) { len = nls->char2uni(&uni->u_name[i], uni->u_len-i, &uni_char); if (len <= 0) continue; if (uni_char > max_val) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } if (max_val == 0xffffU) ocu[++u_len] = (uint8_t)(uni_char >> 8); ocu[++u_len] = (uint8_t)(uni_char & 0xffU); i += len - 1; } ocu[length - 1] = (uint8_t)u_len + 1; return u_len + 1; } int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, int flen) { struct ustr filename, unifilename; int len; if (udf_build_ustr_exact(&unifilename, sname, flen)) { return 0; } if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { if (!udf_CS0toUTF8(&filename, &unifilename) ) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); return 0; } } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, &filename, &unifilename) ) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); return 0; } } else return 0; if ((len = udf_translate_to_linux(dname, filename.u_name, filename.u_len, unifilename.u_name, unifilename.u_len))) { return len; } return 0; } int udf_put_filename(struct super_block *sb, const uint8_t *sname, uint8_t *dname, int flen) { struct ustr unifilename; int namelen; if ( !(udf_char_to_ustr(&unifilename, sname, flen)) ) { return 0; } if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { if ( !(namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN)) ) { return 0; } } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { if ( !(namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, &unifilename, UDF_NAME_LEN)) ) { return 0; } } else return 0; return namelen; } #define ILLEGAL_CHAR_MARK '_' #define EXT_MARK '.' #define CRC_MARK '#' #define EXT_SIZE 5 static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen, uint8_t *fidName, int fidNameLen) { int index, newIndex = 0, needsCRC = 0; int extIndex = 0, newExtIndex = 0, hasExt = 0; unsigned short valueCRC; uint8_t curr; const uint8_t hexChar[] = "0123456789ABCDEF"; if (udfName[0] == '.' && (udfLen == 1 || (udfLen == 2 && udfName[1] == '.'))) { needsCRC = 1; newIndex = udfLen; memcpy(newName, udfName, udfLen); } else { for (index = 0; index < udfLen; index++) { curr = udfName[index]; if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; while (index+1 < udfLen && (udfName[index+1] == '/' || udfName[index+1] == 0)) index++; } if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE) { if (udfLen == index + 1) hasExt = 0; else { hasExt = 1; extIndex = index; newExtIndex = newIndex; } } if (newIndex < 256) newName[newIndex++] = curr; else needsCRC = 1; } } if (needsCRC) { uint8_t ext[EXT_SIZE]; int localExtIndex = 0; if (hasExt) { int maxFilenameLen; for(index = 0; index maxFilenameLen) newIndex = maxFilenameLen; else newIndex = newExtIndex; } else if (newIndex > 250) newIndex = 250; newName[newIndex++] = CRC_MARK; valueCRC = udf_crc(fidName, fidNameLen, 0); newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12]; newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8]; newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4]; newName[newIndex++] = hexChar[(valueCRC & 0x000f)]; if (hasExt) { newName[newIndex++] = EXT_MARK; for (index = 0;index < localExtIndex ;index++ ) newName[newIndex++] = ext[index]; } } return newIndex; } linux-2.6.17/fs/udf/balloc.c0000644000000000000000000006031210564537530014244 0ustar rootroot/* * balloc.c * * PURPOSE * Block allocation handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1999-2001 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY * * 02/24/99 blf Created. * */ #include "udfdecl.h" #include #include #include #include "udf_i.h" #include "udf_sb.h" #define udf_clear_bit(nr,addr) ext2_clear_bit(nr,addr) #define udf_set_bit(nr,addr) ext2_set_bit(nr,addr) #define udf_test_bit(nr, addr) ext2_test_bit(nr, addr) #define udf_find_first_one_bit(addr, size) find_first_one_bit(addr, size) #define udf_find_next_one_bit(addr, size, offset) find_next_one_bit(addr, size, offset) #define leBPL_to_cpup(x) leNUM_to_cpup(BITS_PER_LONG, x) #define leNUM_to_cpup(x,y) xleNUM_to_cpup(x,y) #define xleNUM_to_cpup(x,y) (le ## x ## _to_cpup(y)) #define uintBPL_t uint(BITS_PER_LONG) #define uint(x) xuint(x) #define xuint(x) __le ## x static inline int find_next_one_bit (void * addr, int size, int offset) { uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG); int result = offset & ~(BITS_PER_LONG-1); unsigned long tmp; if (offset >= size) return size; size -= result; offset &= (BITS_PER_LONG-1); if (offset) { tmp = leBPL_to_cpup(p++); tmp &= ~0UL << offset; if (size < BITS_PER_LONG) goto found_first; if (tmp) goto found_middle; size -= BITS_PER_LONG; result += BITS_PER_LONG; } while (size & ~(BITS_PER_LONG-1)) { if ((tmp = leBPL_to_cpup(p++))) goto found_middle; result += BITS_PER_LONG; size -= BITS_PER_LONG; } if (!size) return result; tmp = leBPL_to_cpup(p); found_first: tmp &= ~0UL >> (BITS_PER_LONG-size); found_middle: return result + ffz(~tmp); } #define find_first_one_bit(addr, size)\ find_next_one_bit((addr), (size), 0) static int read_block_bitmap(struct super_block * sb, struct udf_bitmap *bitmap, unsigned int block, unsigned long bitmap_nr) { struct buffer_head *bh = NULL; int retval = 0; kernel_lb_addr loc; loc.logicalBlockNum = bitmap->s_extPosition; loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block)); if (!bh) { retval = -EIO; } bitmap->s_block_bitmap[bitmap_nr] = bh; return retval; } static int __load_block_bitmap(struct super_block * sb, struct udf_bitmap *bitmap, unsigned int block_group) { int retval = 0; int nr_groups = bitmap->s_nr_groups; if (block_group >= nr_groups) { udf_debug("block_group (%d) > nr_groups (%d)\n", block_group, nr_groups); } if (bitmap->s_block_bitmap[block_group]) return block_group; else { retval = read_block_bitmap(sb, bitmap, block_group, block_group); if (retval < 0) return retval; return block_group; } } static inline int load_block_bitmap(struct super_block * sb, struct udf_bitmap *bitmap, unsigned int block_group) { int slot; slot = __load_block_bitmap(sb, bitmap, block_group); if (slot < 0) return slot; if (!bitmap->s_block_bitmap[slot]) return -EIO; return slot; } static void udf_bitmap_free_blocks(struct super_block * sb, struct inode * inode, struct udf_bitmap *bitmap, kernel_lb_addr bloc, uint32_t offset, uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); struct buffer_head * bh = NULL; unsigned long block; unsigned long block_group; unsigned long bit; unsigned long i; int bitmap_nr; unsigned long overflow; mutex_lock(&sbi->s_alloc_mutex); if (bloc.logicalBlockNum < 0 || (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) { udf_debug("%d < %d || %d + %d > %d\n", bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); goto error_return; } block = bloc.logicalBlockNum + offset + (sizeof(struct spaceBitmapDesc) << 3); do_more: overflow = 0; block_group = block >> (sb->s_blocksize_bits + 3); bit = block % (sb->s_blocksize << 3); /* * Check to see if we are freeing blocks across a group boundary. */ if (bit + count > (sb->s_blocksize << 3)) { overflow = bit + count - (sb->s_blocksize << 3); count -= overflow; } bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; for (i=0; i < count; i++) { if (udf_set_bit(bit + i, bh->b_data)) { udf_debug("bit %ld already set\n", bit + i); udf_debug("byte=%2x\n", ((char *)bh->b_data)[(bit + i) >> 3]); } else { if (inode) DQUOT_FREE_BLOCK(inode, 1); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+1); } } } mark_buffer_dirty(bh); if (overflow) { block += count; count = overflow; goto do_more; } error_return: sb->s_dirt = 1; if (UDF_SB_LVIDBH(sb)) mark_buffer_dirty(UDF_SB_LVIDBH(sb)); mutex_unlock(&sbi->s_alloc_mutex); return; } static int udf_bitmap_prealloc_blocks(struct super_block * sb, struct inode * inode, struct udf_bitmap *bitmap, uint16_t partition, uint32_t first_block, uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; int bit, block, block_group, group_start; int nr_groups, bitmap_nr; struct buffer_head *bh; mutex_lock(&sbi->s_alloc_mutex); if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) goto out; if (first_block + block_count > UDF_SB_PARTLEN(sb, partition)) block_count = UDF_SB_PARTLEN(sb, partition) - first_block; repeat: nr_groups = (UDF_SB_PARTLEN(sb, partition) + (sizeof(struct spaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); block = first_block + (sizeof(struct spaceBitmapDesc) << 3); block_group = block >> (sb->s_blocksize_bits + 3); group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto out; bh = bitmap->s_block_bitmap[bitmap_nr]; bit = block % (sb->s_blocksize << 3); while (bit < (sb->s_blocksize << 3) && block_count > 0) { if (!udf_test_bit(bit, bh->b_data)) goto out; else if (DQUOT_PREALLOC_BLOCK(inode, 1)) goto out; else if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); DQUOT_FREE_BLOCK(inode, 1); goto out; } block_count --; alloc_count ++; bit ++; block ++; } mark_buffer_dirty(bh); if (block_count > 0) goto repeat; out: if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); return alloc_count; } static int udf_bitmap_new_block(struct super_block * sb, struct inode * inode, struct udf_bitmap *bitmap, uint16_t partition, uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); int newbit, bit=0, block, block_group, group_start; int end_goal, nr_groups, bitmap_nr, i; struct buffer_head *bh = NULL; char *ptr; int newblock = 0; *err = -ENOSPC; mutex_lock(&sbi->s_alloc_mutex); repeat: if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition)) goal = 0; nr_groups = bitmap->s_nr_groups; block = goal + (sizeof(struct spaceBitmapDesc) << 3); block_group = block >> (sb->s_blocksize_bits + 3); group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) { bit = block % (sb->s_blocksize << 3); if (udf_test_bit(bit, bh->b_data)) { goto got_block; } end_goal = (bit + 63) & ~63; bit = udf_find_next_one_bit(bh->b_data, end_goal, bit); if (bit < end_goal) goto got_block; ptr = memscan((char *)bh->b_data + (bit >> 3), 0xFF, sb->s_blocksize - ((bit + 7) >> 3)); newbit = (ptr - ((char *)bh->b_data)) << 3; if (newbit < sb->s_blocksize << 3) { bit = newbit; goto search_back; } newbit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, bit); if (newbit < sb->s_blocksize << 3) { bit = newbit; goto got_block; } } for (i=0; i<(nr_groups*2); i++) { block_group ++; if (block_group >= nr_groups) block_group = 0; group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; if (i < nr_groups) { ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) { bit = (ptr - ((char *)bh->b_data)) << 3; break; } } else { bit = udf_find_next_one_bit((char *)bh->b_data, sb->s_blocksize << 3, group_start << 3); if (bit < sb->s_blocksize << 3) break; } } if (i >= (nr_groups*2)) { mutex_unlock(&sbi->s_alloc_mutex); return newblock; } if (bit < sb->s_blocksize << 3) goto search_back; else bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, group_start << 3); if (bit >= sb->s_blocksize << 3) { mutex_unlock(&sbi->s_alloc_mutex); return 0; } search_back: for (i=0; i<7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--); got_block: /* * Check quota for allocation of this block. */ if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) { mutex_unlock(&sbi->s_alloc_mutex); *err = -EDQUOT; return 0; } newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) - (sizeof(struct spaceBitmapDesc) << 3); if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); goto repeat; } mark_buffer_dirty(bh); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); *err = 0; return newblock; error_return: *err = -EIO; mutex_unlock(&sbi->s_alloc_mutex); return 0; } static void udf_table_free_blocks(struct super_block * sb, struct inode * inode, struct inode * table, kernel_lb_addr bloc, uint32_t offset, uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); uint32_t start, end; uint32_t nextoffset, oextoffset, elen; kernel_lb_addr nbloc, obloc, eloc; struct buffer_head *obh, *nbh; int8_t etype; int i; mutex_lock(&sbi->s_alloc_mutex); if (bloc.logicalBlockNum < 0 || (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) { udf_debug("%d < %d || %d + %d > %d\n", bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); goto error_return; } /* We do this up front - There are some error conditions that could occure, but.. oh well */ if (inode) DQUOT_FREE_BLOCK(inode, count); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } start = bloc.logicalBlockNum + offset; end = bloc.logicalBlockNum + offset + count - 1; oextoffset = nextoffset = sizeof(struct unallocSpaceEntry); elen = 0; obloc = nbloc = UDF_I_LOCATION(table); obh = nbh = NULL; while (count && (etype = udf_next_aext(table, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) { if (((eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) == start)) { if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) { count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); start += ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); elen = (etype << 30) | (0x40000000 - sb->s_blocksize); } else { elen = (etype << 30) | (elen + (count << sb->s_blocksize_bits)); start += count; count = 0; } udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1); } else if (eloc.logicalBlockNum == (end + 1)) { if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) { count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); end -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); eloc.logicalBlockNum -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); elen = (etype << 30) | (0x40000000 - sb->s_blocksize); } else { eloc.logicalBlockNum = start; elen = (etype << 30) | (elen + (count << sb->s_blocksize_bits)); end -= count; count = 0; } udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1); } if (nbh != obh) { i = -1; obloc = nbloc; udf_release_data(obh); atomic_inc(&nbh->b_count); obh = nbh; oextoffset = 0; } else oextoffset = nextoffset; } if (count) { /* NOTE: we CANNOT use udf_add_aext here, as it can try to allocate a new block, and since we hold the super block lock already very bad things would happen :) We copy the behavior of udf_add_aext, but instead of trying to allocate a new block close to the existing one, we just steal a block from the extent we are trying to add. It would be nice if the blocks were close together, but it isn't required. */ int adsize; short_ad *sad = NULL; long_ad *lad = NULL; struct allocExtDesc *aed; eloc.logicalBlockNum = start; elen = EXT_RECORDED_ALLOCATED | (count << sb->s_blocksize_bits); if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else { udf_release_data(obh); udf_release_data(nbh); goto error_return; } if (nextoffset + (2 * adsize) > sb->s_blocksize) { char *sptr, *dptr; int loffset; udf_release_data(obh); obh = nbh; obloc = nbloc; oextoffset = nextoffset; /* Steal a block from the extent being free'd */ nbloc.logicalBlockNum = eloc.logicalBlockNum; eloc.logicalBlockNum ++; elen -= sb->s_blocksize; if (!(nbh = udf_tread(sb, udf_get_lb_pblock(sb, nbloc, 0)))) { udf_release_data(obh); goto error_return; } aed = (struct allocExtDesc *)(nbh->b_data); aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum); if (nextoffset + adsize > sb->s_blocksize) { loffset = nextoffset; aed->lengthAllocDescs = cpu_to_le32(adsize); sptr = UDF_I_DATA(inode) + nextoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode) - adsize; dptr = nbh->b_data + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); nextoffset = sizeof(struct allocExtDesc) + adsize; } else { loffset = nextoffset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); sptr = (obh)->b_data + nextoffset; nextoffset = sizeof(struct allocExtDesc); if (obh) { aed = (struct allocExtDesc *)(obh)->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); } else { UDF_I_LENALLOC(table) += adsize; mark_inode_dirty(table); } } if (UDF_SB_UDFREV(sb) >= 0x0200) udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1, nbloc.logicalBlockNum, sizeof(tag)); else udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1, nbloc.logicalBlockNum, sizeof(tag)); switch (UDF_I_ALLOCTYPE(table)) { case ICBTAG_FLAG_AD_SHORT: { sad = (short_ad *)sptr; sad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | sb->s_blocksize); sad->extPosition = cpu_to_le32(nbloc.logicalBlockNum); break; } case ICBTAG_FLAG_AD_LONG: { lad = (long_ad *)sptr; lad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | sb->s_blocksize); lad->extLocation = cpu_to_lelb(nbloc); break; } } if (obh) { udf_update_tag(obh->b_data, loffset); mark_buffer_dirty(obh); } else mark_inode_dirty(table); } if (elen) /* It's possible that stealing the block emptied the extent */ { udf_write_aext(table, nbloc, &nextoffset, eloc, elen, nbh, 1); if (!nbh) { UDF_I_LENALLOC(table) += adsize; mark_inode_dirty(table); } else { aed = (struct allocExtDesc *)nbh->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); udf_update_tag(nbh->b_data, nextoffset); mark_buffer_dirty(nbh); } } } udf_release_data(nbh); udf_release_data(obh); error_return: sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); return; } static int udf_table_prealloc_blocks(struct super_block * sb, struct inode * inode, struct inode *table, uint16_t partition, uint32_t first_block, uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; uint32_t extoffset, elen, adsize; kernel_lb_addr bloc, eloc; struct buffer_head *bh; int8_t etype = -1; if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) return 0; if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else return 0; mutex_lock(&sbi->s_alloc_mutex); extoffset = sizeof(struct unallocSpaceEntry); bloc = UDF_I_LOCATION(table); bh = NULL; eloc.logicalBlockNum = 0xFFFFFFFF; while (first_block != eloc.logicalBlockNum && (etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { udf_debug("eloc=%d, elen=%d, first_block=%d\n", eloc.logicalBlockNum, elen, first_block); ; /* empty loop body */ } if (first_block == eloc.logicalBlockNum) { extoffset -= adsize; alloc_count = (elen >> sb->s_blocksize_bits); if (inode && DQUOT_PREALLOC_BLOCK(inode, alloc_count > block_count ? block_count : alloc_count)) alloc_count = 0; else if (alloc_count > block_count) { alloc_count = block_count; eloc.logicalBlockNum += alloc_count; elen -= (alloc_count << sb->s_blocksize_bits); udf_write_aext(table, bloc, &extoffset, eloc, (etype << 30) | elen, bh, 1); } else udf_delete_aext(table, bloc, extoffset, eloc, (etype << 30) | elen, bh); } else alloc_count = 0; udf_release_data(bh); if (alloc_count && UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); sb->s_dirt = 1; } mutex_unlock(&sbi->s_alloc_mutex); return alloc_count; } static int udf_table_new_block(struct super_block * sb, struct inode * inode, struct inode *table, uint16_t partition, uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF; uint32_t newblock = 0, adsize; uint32_t extoffset, goal_extoffset, elen, goal_elen = 0; kernel_lb_addr bloc, goal_bloc, eloc, goal_eloc; struct buffer_head *bh, *goal_bh; int8_t etype; *err = -ENOSPC; if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(short_ad); else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) adsize = sizeof(long_ad); else return newblock; mutex_lock(&sbi->s_alloc_mutex); if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition)) goal = 0; /* We search for the closest matching block to goal. If we find a exact hit, we stop. Otherwise we keep going till we run out of extents. We store the buffer_head, bloc, and extoffset of the current closest match and use that when we are done. */ extoffset = sizeof(struct unallocSpaceEntry); bloc = UDF_I_LOCATION(table); goal_bh = bh = NULL; while (spread && (etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) { if (goal >= eloc.logicalBlockNum) { if (goal < eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) nspread = 0; else nspread = goal - eloc.logicalBlockNum - (elen >> sb->s_blocksize_bits); } else nspread = eloc.logicalBlockNum - goal; if (nspread < spread) { spread = nspread; if (goal_bh != bh) { udf_release_data(goal_bh); goal_bh = bh; atomic_inc(&goal_bh->b_count); } goal_bloc = bloc; goal_extoffset = extoffset - adsize; goal_eloc = eloc; goal_elen = (etype << 30) | elen; } } udf_release_data(bh); if (spread == 0xFFFFFFFF) { udf_release_data(goal_bh); mutex_unlock(&sbi->s_alloc_mutex); return 0; } /* Only allocate blocks from the beginning of the extent. That way, we only delete (empty) extents, never have to insert an extent because of splitting */ /* This works, but very poorly.... */ newblock = goal_eloc.logicalBlockNum; goal_eloc.logicalBlockNum ++; goal_elen -= sb->s_blocksize; if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) { udf_release_data(goal_bh); mutex_unlock(&sbi->s_alloc_mutex); *err = -EDQUOT; return 0; } if (goal_elen) udf_write_aext(table, goal_bloc, &goal_extoffset, goal_eloc, goal_elen, goal_bh, 1); else udf_delete_aext(table, goal_bloc, goal_extoffset, goal_eloc, goal_elen, goal_bh); udf_release_data(goal_bh); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; mutex_unlock(&sbi->s_alloc_mutex); *err = 0; return newblock; } inline void udf_free_blocks(struct super_block * sb, struct inode * inode, kernel_lb_addr bloc, uint32_t offset, uint32_t count) { uint16_t partition = bloc.partitionReferenceNum; if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, bloc, offset, count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, bloc, offset, count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, bloc, offset, count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_free_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, bloc, offset, count); } else return; } inline int udf_prealloc_blocks(struct super_block * sb, struct inode * inode, uint16_t partition, uint32_t first_block, uint32_t block_count) { if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, partition, first_block, block_count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, partition, first_block, block_count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, partition, first_block, block_count); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_prealloc_blocks(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, partition, first_block, block_count); } else return 0; } inline int udf_new_block(struct super_block * sb, struct inode * inode, uint16_t partition, uint32_t goal, int *err) { if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, partition, goal, err); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, partition, goal, err); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, partition, goal, err); } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_new_block(sb, inode, UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, partition, goal, err); } else { *err = -EIO; return 0; } } linux-2.6.17/fs/udf/namei.c0000644000000000000000000010154310564537530014103 0ustar rootroot/* * namei.c * * PURPOSE * Inode name handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * 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 * Each contributing author retains all rights to their own work. * * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 12/12/98 blf Created. Split out the lookup code from dir.c * 04/19/99 blf link, mknod, symlink support */ #include "udfdecl.h" #include "udf_i.h" #include "udf_sb.h" #include #include #include #include #include #include #include static inline int udf_match(int len1, const char *name1, int len2, const char *name2) { if (len1 != len2) return 0; return !memcmp(name1, name2, len1); } int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, uint8_t *impuse, uint8_t *fileident) { uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag); uint16_t crc; uint8_t checksum = 0; int i; int offset; uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse); uint8_t lfi = cfi->lengthFileIdent; int padlen = fibh->eoffset - fibh->soffset - liu - lfi - sizeof(struct fileIdentDesc); int adinicb = 0; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) adinicb = 1; offset = fibh->soffset + sizeof(struct fileIdentDesc); if (impuse) { if (adinicb || (offset + liu < 0)) memcpy((uint8_t *)sfi->impUse, impuse, liu); else if (offset >= 0) memcpy(fibh->ebh->b_data + offset, impuse, liu); else { memcpy((uint8_t *)sfi->impUse, impuse, -offset); memcpy(fibh->ebh->b_data, impuse - offset, liu + offset); } } offset += liu; if (fileident) { if (adinicb || (offset + lfi < 0)) memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi); else if (offset >= 0) memcpy(fibh->ebh->b_data + offset, fileident, lfi); else { memcpy((uint8_t *)sfi->fileIdent + liu, fileident, -offset); memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset); } } offset += lfi; if (adinicb || (offset + padlen < 0)) memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen); else if (offset >= 0) memset(fibh->ebh->b_data + offset, 0x00, padlen); else { memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset); memset(fibh->ebh->b_data, 0x00, padlen + offset); } crc = udf_crc((uint8_t *)cfi + sizeof(tag), sizeof(struct fileIdentDesc) - sizeof(tag), 0); if (fibh->sbh == fibh->ebh) crc = udf_crc((uint8_t *)sfi->impUse, crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) crc = udf_crc(fibh->ebh->b_data + sizeof(struct fileIdentDesc) + fibh->soffset, crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); else { crc = udf_crc((uint8_t *)sfi->impUse, -fibh->soffset - sizeof(struct fileIdentDesc), crc); crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); } cfi->descTag.descCRC = cpu_to_le16(crc); cfi->descTag.descCRCLength = cpu_to_le16(crclen); for (i=0; i<16; i++) if (i != 4) checksum += ((uint8_t *)&cfi->descTag)[i]; cfi->descTag.tagChecksum = checksum; if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) memcpy((uint8_t *)sfi, (uint8_t *)cfi, sizeof(struct fileIdentDesc)); else { memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset); memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset, sizeof(struct fileIdentDesc) + fibh->soffset); } if (adinicb) mark_inode_dirty(inode); else { if (fibh->sbh != fibh->ebh) mark_buffer_dirty_inode(fibh->ebh, inode); mark_buffer_dirty_inode(fibh->sbh, inode); } return 0; } static struct fileIdentDesc * udf_find_entry(struct inode *dir, struct dentry *dentry, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi) { struct fileIdentDesc *fi=NULL; loff_t f_pos; int block, flen; char fname[UDF_NAME_LEN]; char *nameptr; uint8_t lfi; uint16_t liu; loff_t size; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; size = (udf_ext0_offset(dir) + dir->i_size) >> 2; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh->sbh = fibh->ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); return NULL; } } else { udf_release_data(bh); return NULL; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); return NULL; } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; if (fibh->sbh == fibh->ebh) { nameptr = fi->fileIdent + liu; } else { int poffset; /* Unpaded ending offset */ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; if (poffset >= lfi) nameptr = (uint8_t *)(fibh->ebh->b_data + poffset - lfi); else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); } } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) continue; } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) { if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) continue; } if (!lfi) continue; if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi))) { if (udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) { udf_release_data(bh); return fi; } } } if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); return NULL; } /* * udf_lookup * * PURPOSE * Look-up the inode for a given name. * * DESCRIPTION * Required - lookup_dentry() will return -ENOTDIR if this routine is not * available for a directory. The filesystem is useless if this routine is * not available for at least the filesystem's root directory. * * This routine is passed an incomplete dentry - it must be completed by * calling d_add(dentry, inode). If the name does not exist, then the * specified inode must be set to null. An error should only be returned * when the lookup fails for a reason other than the name not existing. * Note that the directory inode semaphore is held during the call. * * Refer to lookup_dentry() in fs/namei.c * lookup_dentry() -> lookup() -> real_lookup() -> . * * PRE-CONDITIONS * dir Pointer to inode of parent directory. * dentry Pointer to dentry to complete. * nd Pointer to lookup nameidata * * POST-CONDITIONS * Zero on success. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ static struct dentry * udf_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct inode *inode = NULL; struct fileIdentDesc cfi; struct udf_fileident_bh fibh; if (dentry->d_name.len > UDF_NAME_LEN-2) return ERR_PTR(-ENAMETOOLONG); lock_kernel(); #ifdef UDF_RECOVERY /* temporary shorthand for specifying files by inode number */ if (!strncmp(dentry->d_name.name, ".B=", 3) ) { kernel_lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; inode = udf_iget(dir->i_sb, lb); if (!inode) { unlock_kernel(); return ERR_PTR(-EACCES); } } else #endif /* UDF_RECOVERY */ if (udf_find_entry(dir, dentry, &fibh, &cfi)) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation)); if ( !inode ) { unlock_kernel(); return ERR_PTR(-EACCES); } } unlock_kernel(); d_add(dentry, inode); return NULL; } static struct fileIdentDesc * udf_add_entry(struct inode *dir, struct dentry *dentry, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi, int *err) { struct super_block *sb; struct fileIdentDesc *fi=NULL; char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; int namelen; loff_t f_pos; int flen; char *nameptr; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int nfidlen; uint8_t lfi; uint16_t liu; int block; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; sb = dir->i_sb; if (dentry) { if (!dentry->d_name.len) { *err = -EINVAL; return NULL; } if ( !(namelen = udf_put_filename(sb, dentry->d_name.name, name, dentry->d_name.len))) { *err = -ENAMETOOLONG; return NULL; } } else namelen = 0; nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh->sbh = fibh->ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); *err = -EIO; return NULL; } block = UDF_I_LOCATION(dir).logicalBlockNum; } else { block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0); fibh->sbh = fibh->ebh = NULL; fibh->soffset = fibh->eoffset = sb->s_blocksize; goto add; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); *err = -EIO; return NULL; } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; if (fibh->sbh == fibh->ebh) nameptr = fi->fileIdent + liu; else { int poffset; /* Unpaded ending offset */ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; if (poffset >= lfi) nameptr = (char *)(fibh->ebh->b_data + poffset - lfi); else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); } } if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) { if (((sizeof(struct fileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen) { udf_release_data(bh); cfi->descTag.tagSerialNum = cpu_to_le16(1); cfi->fileVersionNum = cpu_to_le16(1); cfi->fileCharacteristics = 0; cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) return fi; else { *err = -EIO; return NULL; } } } if (!lfi || !dentry) continue; if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)) && udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); udf_release_data(bh); *err = -EEXIST; return NULL; } } add: f_pos += nfidlen; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB && sb->s_blocksize - fibh->eoffset < nfidlen) { udf_release_data(bh); bh = NULL; fibh->soffset -= udf_ext0_offset(dir); fibh->eoffset -= udf_ext0_offset(dir); f_pos -= (udf_ext0_offset(dir) >> 2); if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); if (!(fibh->sbh = fibh->ebh = udf_expand_dir_adinicb(dir, &block, err))) return NULL; bloc = UDF_I_LOCATION(dir); eloc.logicalBlockNum = block; eloc.partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum; elen = dir->i_sb->s_blocksize; extoffset = udf_file_entry_alloc_offset(dir); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset += sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset += sizeof(long_ad); } if (sb->s_blocksize - fibh->eoffset >= nfidlen) { fibh->soffset = fibh->eoffset; fibh->eoffset += nfidlen; if (fibh->sbh != fibh->ebh) { udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; } if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { block = UDF_I_LOCATION(dir).logicalBlockNum; fi = (struct fileIdentDesc *)(UDF_I_DATA(dir) + fibh->soffset - udf_ext0_offset(dir) + UDF_I_LENEATTR(dir)); } else { block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); fi = (struct fileIdentDesc *)(fibh->sbh->b_data + fibh->soffset); } } else { fibh->soffset = fibh->eoffset - sb->s_blocksize; fibh->eoffset += nfidlen - sb->s_blocksize; if (fibh->sbh != fibh->ebh) { udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; } block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); if (!(fibh->ebh = udf_bread(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), 1, err))) { udf_release_data(bh); udf_release_data(fibh->sbh); return NULL; } if (!(fibh->soffset)) { if (udf_next_aext(dir, &bloc, &extoffset, &eloc, &elen, &bh, 1) == (EXT_RECORDED_ALLOCATED >> 30)) { block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); } else block ++; udf_release_data(fibh->sbh); fibh->sbh = fibh->ebh; fi = (struct fileIdentDesc *)(fibh->sbh->b_data); } else { fi = (struct fileIdentDesc *) (fibh->sbh->b_data + sb->s_blocksize + fibh->soffset); } } memset(cfi, 0, sizeof(struct fileIdentDesc)); if (UDF_SB_UDFREV(sb) >= 0x0200) udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, sizeof(tag)); else udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, sizeof(tag)); cfi->fileVersionNum = cpu_to_le16(1); cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) { udf_release_data(bh); dir->i_size += nfidlen; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) UDF_I_LENALLOC(dir) += nfidlen; mark_inode_dirty(dir); return fi; } else { udf_release_data(bh); if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); *err = -EIO; return NULL; } } static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi) { cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) memset(&(cfi->icb), 0x00, sizeof(long_ad)); return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL); } static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { struct udf_fileident_bh fibh; struct inode *inode; struct fileIdentDesc cfi, *fi; int err; lock_kernel(); inode = udf_new_inode(dir, mode, &err); if (!inode) { unlock_kernel(); return err; } if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) inode->i_data.a_ops = &udf_adinicb_aops; else inode->i_data.a_ops = &udf_aops; inode->i_op = &udf_file_inode_operations; inode->i_fop = &udf_file_operations; inode->i_mode = mode; mark_inode_dirty(inode); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { inode->i_nlink --; mark_inode_dirty(inode); iput(inode); unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); unlock_kernel(); d_instantiate(dentry, inode); return 0; } static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) { struct inode * inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; if (!old_valid_dev(rdev)) return -EINVAL; lock_kernel(); err = -EIO; inode = udf_new_inode(dir, mode, &err); if (!inode) goto out; inode->i_uid = current->fsuid; init_special_inode(inode, mode, rdev); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { inode->i_nlink --; mark_inode_dirty(inode); iput(inode); unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } mark_inode_dirty(inode); if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); d_instantiate(dentry, inode); err = 0; out: unlock_kernel(); return err; } static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; lock_kernel(); err = -EMLINK; if (dir->i_nlink >= (256<i_nlink))-1) goto out; err = -EIO; inode = udf_new_inode(dir, S_IFDIR, &err); if (!inode) goto out; inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err))) { inode->i_nlink--; mark_inode_dirty(inode); iput(inode); goto out; } inode->i_nlink = 2; cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(dir)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(dir) & 0x00000000FFFFFFFFUL); cfi.fileCharacteristics = FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT; udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL); udf_release_data(fibh.sbh); inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); goto out; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY; udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); dir->i_nlink++; mark_inode_dirty(dir); d_instantiate(dentry, inode); if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); err = 0; out: unlock_kernel(); return err; } static int empty_dir(struct inode *dir) { struct fileIdentDesc *fi, cfi; struct udf_fileident_bh fibh; loff_t f_pos; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int block; kernel_lb_addr bloc, eloc; uint32_t extoffset, elen, offset; struct buffer_head *bh = NULL; f_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) fibh.sbh = fibh.ebh = NULL; else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) { offset >>= dir->i_sb->s_blocksize_bits; block = udf_get_lb_pblock(dir->i_sb, eloc, offset); if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) extoffset -= sizeof(long_ad); } else offset = 0; if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { udf_release_data(bh); return 0; } } else { udf_release_data(bh); return 0; } while ( (f_pos < size) ) { fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); if (!fi) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } if (cfi.lengthFileIdent && (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 0; } } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); udf_release_data(bh); return 1; } static int udf_rmdir(struct inode * dir, struct dentry * dentry) { int retval; struct inode * inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi, cfi; kernel_lb_addr tloc; retval = -ENOENT; lock_kernel(); fi = udf_find_entry(dir, dentry, &fibh, &cfi); if (!fi) goto out; retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino) goto end_rmdir; retval = -ENOTEMPTY; if (!empty_dir(inode)) goto end_rmdir; retval = udf_delete_entry(dir, fi, &fibh, &cfi); if (retval) goto end_rmdir; if (inode->i_nlink != 2) udf_warning(inode->i_sb, "udf_rmdir", "empty directory has nlink != 2 (%d)", inode->i_nlink); inode->i_nlink = 0; inode->i_size = 0; mark_inode_dirty(inode); dir->i_nlink --; inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb); mark_inode_dirty(dir); end_rmdir: if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); out: unlock_kernel(); return retval; } static int udf_unlink(struct inode * dir, struct dentry * dentry) { int retval; struct inode * inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi; struct fileIdentDesc cfi; kernel_lb_addr tloc; retval = -ENOENT; lock_kernel(); fi = udf_find_entry(dir, dentry, &fibh, &cfi); if (!fi) goto out; retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino) goto end_unlink; if (!inode->i_nlink) { udf_debug("Deleting nonexistent file (%lu), %d\n", inode->i_ino, inode->i_nlink); inode->i_nlink = 1; } retval = udf_delete_entry(dir, fi, &fibh, &cfi); if (retval) goto end_unlink; dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb); mark_inode_dirty(dir); inode->i_nlink--; mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; end_unlink: if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); out: unlock_kernel(); return retval; } static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname) { struct inode * inode; struct pathComponent *pc; char *compstart; struct udf_fileident_bh fibh; struct buffer_head *bh = NULL; int eoffset, elen = 0; struct fileIdentDesc *fi; struct fileIdentDesc cfi; char *ea; int err; int block; char name[UDF_NAME_LEN]; int namelen; lock_kernel(); if (!(inode = udf_new_inode(dir, S_IFLNK, &err))) goto out; inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; if (UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB) { struct buffer_head *bh = NULL; kernel_lb_addr bloc, eloc; uint32_t elen, extoffset; block = udf_new_block(inode->i_sb, inode, UDF_I_LOCATION(inode).partitionReferenceNum, UDF_I_LOCATION(inode).logicalBlockNum, &err); if (!block) goto out_no_entry; bloc = UDF_I_LOCATION(inode); eloc.logicalBlockNum = block; eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; elen = inode->i_sb->s_blocksize; UDF_I_LENEXTENTS(inode) = elen; extoffset = udf_file_entry_alloc_offset(inode); udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 0); udf_release_data(bh); block = udf_get_pblock(inode->i_sb, block, UDF_I_LOCATION(inode).partitionReferenceNum, 0); bh = udf_tread(inode->i_sb, block); lock_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(bh); unlock_buffer(bh); mark_buffer_dirty_inode(bh, inode); ea = bh->b_data + udf_ext0_offset(inode); } else ea = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode); pc = (struct pathComponent *)ea; if (*symname == '/') { do { symname++; } while (*symname == '/'); pc->componentType = 1; pc->lengthComponentIdent = 0; pc->componentFileVersionNum = 0; pc += sizeof(struct pathComponent); elen += sizeof(struct pathComponent); } err = -ENAMETOOLONG; while (*symname) { if (elen + sizeof(struct pathComponent) > eoffset) goto out_no_entry; pc = (struct pathComponent *)(ea + elen); compstart = (char *)symname; do { symname++; } while (*symname && *symname != '/'); pc->componentType = 5; pc->lengthComponentIdent = 0; pc->componentFileVersionNum = 0; if (compstart[0] == '.') { if ((symname-compstart) == 1) pc->componentType = 4; else if ((symname-compstart) == 2 && compstart[1] == '.') pc->componentType = 3; } if (pc->componentType == 5) { if ( !(namelen = udf_put_filename(inode->i_sb, compstart, name, symname-compstart))) goto out_no_entry; if (elen + sizeof(struct pathComponent) + namelen > eoffset) goto out_no_entry; else pc->lengthComponentIdent = namelen; memcpy(pc->componentIdent, name, namelen); } elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; if (*symname) { do { symname++; } while (*symname == '/'); } } udf_release_data(bh); inode->i_size = elen; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) UDF_I_LENALLOC(inode) = inode->i_size; mark_inode_dirty(inode); if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) goto out_no_entry; cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); if (UDF_SB_LVIDBH(inode->i_sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); uniqueID = le64_to_cpu(lvhd->uniqueID); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL); if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); d_instantiate(dentry, inode); err = 0; out: unlock_kernel(); return err; out_no_entry: inode->i_nlink--; mark_inode_dirty(inode); iput(inode); goto out; } static int udf_link(struct dentry * old_dentry, struct inode * dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; lock_kernel(); if (inode->i_nlink >= (256<i_nlink))-1) { unlock_kernel(); return -EMLINK; } if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); if (UDF_SB_LVIDBH(inode->i_sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); uniqueID = le64_to_cpu(lvhd->uniqueID); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL); if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); inode->i_nlink ++; inode->i_ctime = current_fs_time(inode->i_sb); mark_inode_dirty(inode); atomic_inc(&inode->i_count); d_instantiate(dentry, inode); unlock_kernel(); return 0; } /* Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry) { struct inode * old_inode = old_dentry->d_inode; struct inode * new_inode = new_dentry->d_inode; struct udf_fileident_bh ofibh, nfibh; struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL, ocfi, ncfi; struct buffer_head *dir_bh = NULL; int retval = -ENOENT; kernel_lb_addr tloc; lock_kernel(); if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi))) { if (ofibh.sbh != ofibh.ebh) udf_release_data(ofibh.ebh); udf_release_data(ofibh.sbh); } tloc = lelb_to_cpu(ocfi.icb.extLocation); if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0) != old_inode->i_ino) goto end_rename; nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi); if (nfi) { if (!new_inode) { if (nfibh.sbh != nfibh.ebh) udf_release_data(nfibh.ebh); udf_release_data(nfibh.sbh); nfi = NULL; } } if (S_ISDIR(old_inode->i_mode)) { uint32_t offset = udf_ext0_offset(old_inode); if (new_inode) { retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; } retval = -EIO; if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) { dir_fi = udf_get_fileident(UDF_I_DATA(old_inode) - (UDF_I_EFE(old_inode) ? sizeof(struct extendedFileEntry) : sizeof(struct fileEntry)), old_inode->i_sb->s_blocksize, &offset); } else { dir_bh = udf_bread(old_inode, 0, 0, &retval); if (!dir_bh) goto end_rename; dir_fi = udf_get_fileident(dir_bh->b_data, old_inode->i_sb->s_blocksize, &offset); } if (!dir_fi) goto end_rename; tloc = lelb_to_cpu(dir_fi->icb.extLocation); if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) != old_dir->i_ino) goto end_rename; retval = -EMLINK; if (!new_inode && new_dir->i_nlink >= (256<i_nlink))-1) goto end_rename; } if (!nfi) { nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval); if (!nfi) goto end_rename; } /* * Like most other Unix systems, set the ctime for inodes on a * rename. */ old_inode->i_ctime = current_fs_time(old_inode->i_sb); mark_inode_dirty(old_inode); /* * ok, that's it */ ncfi.fileVersionNum = ocfi.fileVersionNum; ncfi.fileCharacteristics = ocfi.fileCharacteristics; memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad)); udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL); /* The old fid may have moved - find it again */ ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = current_fs_time(new_inode->i_sb); mark_inode_dirty(new_inode); } old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb); mark_inode_dirty(old_dir); if (dir_fi) { dir_fi->icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(new_dir)); udf_update_tag((char *)dir_fi, (sizeof(struct fileIdentDesc) + le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3); if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(old_inode); } else mark_buffer_dirty_inode(dir_bh, old_inode); old_dir->i_nlink --; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink --; mark_inode_dirty(new_inode); } else { new_dir->i_nlink ++; mark_inode_dirty(new_dir); } } if (ofi) { if (ofibh.sbh != ofibh.ebh) udf_release_data(ofibh.ebh); udf_release_data(ofibh.sbh); } retval = 0; end_rename: udf_release_data(dir_bh); if (nfi) { if (nfibh.sbh != nfibh.ebh) udf_release_data(nfibh.ebh); udf_release_data(nfibh.sbh); } unlock_kernel(); return retval; } struct inode_operations udf_dir_inode_operations = { .lookup = udf_lookup, .create = udf_create, .link = udf_link, .unlink = udf_unlink, .symlink = udf_symlink, .mkdir = udf_mkdir, .rmdir = udf_rmdir, .mknod = udf_mknod, .rename = udf_rename, }; linux-2.6.17/fs/pipe.c0000644000000000000000000005254010564537530013173 0ustar rootroot/* * linux/fs/pipe.c * * Copyright (C) 1991, 1992, 1999 Linus Torvalds */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * We use a start+len construction, which provides full use of the * allocated memory. * -- Florian Coosmann (FGC) * * Reads with count = 0 should always return 0. * -- Julian Bradfield 1999-06-07. * * FIFOs and Pipes now generate SIGIO for both readers and writers. * -- Jeremy Elson 2001-08-16 * * pipe_read & write cleanup * -- Manfred Spraul 2002-05-09 */ /* Drop the inode semaphore and wait for a pipe event, atomically */ void pipe_wait(struct pipe_inode_info *pipe) { DEFINE_WAIT(wait); /* * Pipes are system-local resources, so sleeping on them * is considered a noninteractive wait: */ prepare_to_wait(&pipe->wait, &wait, TASK_INTERRUPTIBLE | TASK_NONINTERACTIVE); if (pipe->inode) mutex_unlock(&pipe->inode->i_mutex); schedule(); finish_wait(&pipe->wait, &wait); if (pipe->inode) mutex_lock(&pipe->inode->i_mutex); } static int pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len, int atomic) { unsigned long copy; while (len > 0) { while (!iov->iov_len) iov++; copy = min_t(unsigned long, len, iov->iov_len); if (atomic) { if (__copy_from_user_inatomic(to, iov->iov_base, copy)) return -EFAULT; } else { if (copy_from_user(to, iov->iov_base, copy)) return -EFAULT; } to += copy; len -= copy; iov->iov_base += copy; iov->iov_len -= copy; } return 0; } static int pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len, int atomic) { unsigned long copy; while (len > 0) { while (!iov->iov_len) iov++; copy = min_t(unsigned long, len, iov->iov_len); if (atomic) { if (__copy_to_user_inatomic(iov->iov_base, from, copy)) return -EFAULT; } else { if (copy_to_user(iov->iov_base, from, copy)) return -EFAULT; } from += copy; len -= copy; iov->iov_base += copy; iov->iov_len -= copy; } return 0; } /* * Attempt to pre-fault in the user memory, so we can use atomic copies. * Returns the number of bytes not faulted in. */ static int iov_fault_in_pages_write(struct iovec *iov, unsigned long len) { while (!iov->iov_len) iov++; while (len > 0) { unsigned long this_len; this_len = min_t(unsigned long, len, iov->iov_len); if (fault_in_pages_writeable(iov->iov_base, this_len)) break; len -= this_len; iov++; } return len; } /* * Pre-fault in the user memory, so we can use atomic copies. */ static void iov_fault_in_pages_read(struct iovec *iov, unsigned long len) { while (!iov->iov_len) iov++; while (len > 0) { unsigned long this_len; this_len = min_t(unsigned long, len, iov->iov_len); fault_in_pages_readable(iov->iov_base, this_len); len -= this_len; iov++; } } static void anon_pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { struct page *page = buf->page; /* * If nobody else uses this page, and we don't already have a * temporary page, let's keep track of it as a one-deep * allocation cache. (Otherwise just release our reference to it) */ if (page_count(page) == 1 && !pipe->tmp_page) pipe->tmp_page = page; else page_cache_release(page); } void *generic_pipe_buf_map(struct pipe_inode_info *pipe, struct pipe_buffer *buf, int atomic) { if (atomic) { buf->flags |= PIPE_BUF_FLAG_ATOMIC; return kmap_atomic(buf->page, KM_USER0); } return kmap(buf->page); } void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, struct pipe_buffer *buf, void *map_data) { if (buf->flags & PIPE_BUF_FLAG_ATOMIC) { buf->flags &= ~PIPE_BUF_FLAG_ATOMIC; kunmap_atomic(map_data, KM_USER0); } else kunmap(buf->page); } int generic_pipe_buf_steal(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { struct page *page = buf->page; if (page_count(page) == 1) { lock_page(page); return 0; } return 1; } void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf) { page_cache_get(buf->page); } int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf) { return 0; } static struct pipe_buf_operations anon_pipe_buf_ops = { .can_merge = 1, .map = generic_pipe_buf_map, .unmap = generic_pipe_buf_unmap, .pin = generic_pipe_buf_pin, .release = anon_pipe_buf_release, .steal = generic_pipe_buf_steal, .get = generic_pipe_buf_get, }; static ssize_t pipe_readv(struct file *filp, const struct iovec *_iov, unsigned long nr_segs, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; struct pipe_inode_info *pipe; int do_wakeup; ssize_t ret; struct iovec *iov = (struct iovec *)_iov; size_t total_len; total_len = iov_length(iov, nr_segs); /* Null read succeeds. */ if (unlikely(total_len == 0)) return 0; do_wakeup = 0; ret = 0; mutex_lock(&inode->i_mutex); pipe = inode->i_pipe; for (;;) { int bufs = pipe->nrbufs; if (bufs) { int curbuf = pipe->curbuf; struct pipe_buffer *buf = pipe->bufs + curbuf; struct pipe_buf_operations *ops = buf->ops; void *addr; size_t chars = buf->len; int error, atomic; if (chars > total_len) chars = total_len; error = ops->pin(pipe, buf); if (error) { if (!ret) error = ret; break; } atomic = !iov_fault_in_pages_write(iov, chars); redo: addr = ops->map(pipe, buf, atomic); error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars, atomic); ops->unmap(pipe, buf, addr); if (unlikely(error)) { /* * Just retry with the slow path if we failed. */ if (atomic) { atomic = 0; goto redo; } if (!ret) ret = error; break; } ret += chars; buf->offset += chars; buf->len -= chars; if (!buf->len) { buf->ops = NULL; ops->release(pipe, buf); curbuf = (curbuf + 1) & (PIPE_BUFFERS-1); pipe->curbuf = curbuf; pipe->nrbufs = --bufs; do_wakeup = 1; } total_len -= chars; if (!total_len) break; /* common path: read succeeded */ } if (bufs) /* More to do? */ continue; if (!pipe->writers) break; if (!pipe->waiting_writers) { /* syscall merging: Usually we must not sleep * if O_NONBLOCK is set, or if we got some data. * But if a writer sleeps in kernel space, then * we can wait for that data without violating POSIX. */ if (ret) break; if (filp->f_flags & O_NONBLOCK) { ret = -EAGAIN; break; } } if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; break; } if (do_wakeup) { wake_up_interruptible_sync(&pipe->wait); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); } pipe_wait(pipe); } mutex_unlock(&inode->i_mutex); /* Signal writers asynchronously that there is more room. */ if (do_wakeup) { wake_up_interruptible(&pipe->wait); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); } if (ret > 0) file_accessed(filp); return ret; } static ssize_t pipe_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { struct iovec iov = { .iov_base = buf, .iov_len = count }; return pipe_readv(filp, &iov, 1, ppos); } static ssize_t pipe_writev(struct file *filp, const struct iovec *_iov, unsigned long nr_segs, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; struct pipe_inode_info *pipe; ssize_t ret; int do_wakeup; struct iovec *iov = (struct iovec *)_iov; size_t total_len; ssize_t chars; total_len = iov_length(iov, nr_segs); /* Null write succeeds. */ if (unlikely(total_len == 0)) return 0; do_wakeup = 0; ret = 0; mutex_lock(&inode->i_mutex); pipe = inode->i_pipe; if (!pipe->readers) { send_sig(SIGPIPE, current, 0); ret = -EPIPE; goto out; } /* We try to merge small writes */ chars = total_len & (PAGE_SIZE-1); /* size of the last buffer */ if (pipe->nrbufs && chars != 0) { int lastbuf = (pipe->curbuf + pipe->nrbufs - 1) & (PIPE_BUFFERS-1); struct pipe_buffer *buf = pipe->bufs + lastbuf; struct pipe_buf_operations *ops = buf->ops; int offset = buf->offset + buf->len; if (ops->can_merge && offset + chars <= PAGE_SIZE) { int error, atomic = 1; void *addr; error = ops->pin(pipe, buf); if (error) goto out; iov_fault_in_pages_read(iov, chars); redo1: addr = ops->map(pipe, buf, atomic); error = pipe_iov_copy_from_user(offset + addr, iov, chars, atomic); ops->unmap(pipe, buf, addr); ret = error; do_wakeup = 1; if (error) { if (atomic) { atomic = 0; goto redo1; } goto out; } buf->len += chars; total_len -= chars; ret = chars; if (!total_len) goto out; } } for (;;) { int bufs; if (!pipe->readers) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; break; } bufs = pipe->nrbufs; if (bufs < PIPE_BUFFERS) { int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1); struct pipe_buffer *buf = pipe->bufs + newbuf; struct page *page = pipe->tmp_page; char *src; int error, atomic = 1; if (!page) { page = alloc_page(GFP_HIGHUSER); if (unlikely(!page)) { ret = ret ? : -ENOMEM; break; } pipe->tmp_page = page; } /* Always wake up, even if the copy fails. Otherwise * we lock up (O_NONBLOCK-)readers that sleep due to * syscall merging. * FIXME! Is this really true? */ do_wakeup = 1; chars = PAGE_SIZE; if (chars > total_len) chars = total_len; iov_fault_in_pages_read(iov, chars); redo2: if (atomic) src = kmap_atomic(page, KM_USER0); else src = kmap(page); error = pipe_iov_copy_from_user(src, iov, chars, atomic); if (atomic) kunmap_atomic(src, KM_USER0); else kunmap(page); if (unlikely(error)) { if (atomic) { atomic = 0; goto redo2; } if (!ret) ret = error; break; } ret += chars; /* Insert it into the buffer array */ buf->page = page; buf->ops = &anon_pipe_buf_ops; buf->offset = 0; buf->len = chars; pipe->nrbufs = ++bufs; pipe->tmp_page = NULL; total_len -= chars; if (!total_len) break; } if (bufs < PIPE_BUFFERS) continue; if (filp->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; break; } if (do_wakeup) { wake_up_interruptible_sync(&pipe->wait); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); do_wakeup = 0; } pipe->waiting_writers++; pipe_wait(pipe); pipe->waiting_writers--; } out: mutex_unlock(&inode->i_mutex); if (do_wakeup) { wake_up_interruptible(&pipe->wait); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); } if (ret > 0) file_update_time(filp); return ret; } static ssize_t pipe_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count }; return pipe_writev(filp, &iov, 1, ppos); } static ssize_t bad_pipe_r(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { return -EBADF; } static ssize_t bad_pipe_w(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { return -EBADF; } static int pipe_ioctl(struct inode *pino, struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct pipe_inode_info *pipe; int count, buf, nrbufs; switch (cmd) { case FIONREAD: mutex_lock(&inode->i_mutex); pipe = inode->i_pipe; count = 0; buf = pipe->curbuf; nrbufs = pipe->nrbufs; while (--nrbufs >= 0) { count += pipe->bufs[buf].len; buf = (buf+1) & (PIPE_BUFFERS-1); } mutex_unlock(&inode->i_mutex); return put_user(count, (int __user *)arg); default: return -EINVAL; } } /* No kernel lock held - fine */ static unsigned int pipe_poll(struct file *filp, poll_table *wait) { unsigned int mask; struct inode *inode = filp->f_dentry->d_inode; struct pipe_inode_info *pipe = inode->i_pipe; int nrbufs; poll_wait(filp, &pipe->wait, wait); /* Reading only -- no need for acquiring the semaphore. */ nrbufs = pipe->nrbufs; mask = 0; if (filp->f_mode & FMODE_READ) { mask = (nrbufs > 0) ? POLLIN | POLLRDNORM : 0; if (!pipe->writers && filp->f_version != pipe->w_counter) mask |= POLLHUP; } if (filp->f_mode & FMODE_WRITE) { mask |= (nrbufs < PIPE_BUFFERS) ? POLLOUT | POLLWRNORM : 0; /* * Most Unices do not set POLLERR for FIFOs but on Linux they * behave exactly like pipes for poll(). */ if (!pipe->readers) mask |= POLLERR; } return mask; } static int pipe_release(struct inode *inode, int decr, int decw) { struct pipe_inode_info *pipe; mutex_lock(&inode->i_mutex); pipe = inode->i_pipe; pipe->readers -= decr; pipe->writers -= decw; if (!pipe->readers && !pipe->writers) { free_pipe_info(inode); } else { wake_up_interruptible(&pipe->wait); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); } mutex_unlock(&inode->i_mutex); return 0; } static int pipe_read_fasync(int fd, struct file *filp, int on) { struct inode *inode = filp->f_dentry->d_inode; int retval; mutex_lock(&inode->i_mutex); retval = fasync_helper(fd, filp, on, &inode->i_pipe->fasync_readers); mutex_unlock(&inode->i_mutex); if (retval < 0) return retval; return 0; } static int pipe_write_fasync(int fd, struct file *filp, int on) { struct inode *inode = filp->f_dentry->d_inode; int retval; mutex_lock(&inode->i_mutex); retval = fasync_helper(fd, filp, on, &inode->i_pipe->fasync_writers); mutex_unlock(&inode->i_mutex); if (retval < 0) return retval; return 0; } static int pipe_rdwr_fasync(int fd, struct file *filp, int on) { struct inode *inode = filp->f_dentry->d_inode; struct pipe_inode_info *pipe = inode->i_pipe; int retval; mutex_lock(&inode->i_mutex); retval = fasync_helper(fd, filp, on, &pipe->fasync_readers); if (retval >= 0) retval = fasync_helper(fd, filp, on, &pipe->fasync_writers); mutex_unlock(&inode->i_mutex); if (retval < 0) return retval; return 0; } static int pipe_read_release(struct inode *inode, struct file *filp) { pipe_read_fasync(-1, filp, 0); return pipe_release(inode, 1, 0); } static int pipe_write_release(struct inode *inode, struct file *filp) { pipe_write_fasync(-1, filp, 0); return pipe_release(inode, 0, 1); } static int pipe_rdwr_release(struct inode *inode, struct file *filp) { int decr, decw; pipe_rdwr_fasync(-1, filp, 0); decr = (filp->f_mode & FMODE_READ) != 0; decw = (filp->f_mode & FMODE_WRITE) != 0; return pipe_release(inode, decr, decw); } static int pipe_read_open(struct inode *inode, struct file *filp) { /* We could have perhaps used atomic_t, but this and friends below are the only places. So it doesn't seem worthwhile. */ mutex_lock(&inode->i_mutex); inode->i_pipe->readers++; mutex_unlock(&inode->i_mutex); return 0; } static int pipe_write_open(struct inode *inode, struct file *filp) { mutex_lock(&inode->i_mutex); inode->i_pipe->writers++; mutex_unlock(&inode->i_mutex); return 0; } static int pipe_rdwr_open(struct inode *inode, struct file *filp) { mutex_lock(&inode->i_mutex); if (filp->f_mode & FMODE_READ) inode->i_pipe->readers++; if (filp->f_mode & FMODE_WRITE) inode->i_pipe->writers++; mutex_unlock(&inode->i_mutex); return 0; } /* * The file_operations structs are not static because they * are also used in linux/fs/fifo.c to do operations on FIFOs. */ const struct file_operations read_fifo_fops = { .llseek = no_llseek, .read = pipe_read, .readv = pipe_readv, .write = bad_pipe_w, .poll = pipe_poll, .ioctl = pipe_ioctl, .open = pipe_read_open, .release = pipe_read_release, .fasync = pipe_read_fasync, }; const struct file_operations write_fifo_fops = { .llseek = no_llseek, .read = bad_pipe_r, .write = pipe_write, .writev = pipe_writev, .poll = pipe_poll, .ioctl = pipe_ioctl, .open = pipe_write_open, .release = pipe_write_release, .fasync = pipe_write_fasync, }; const struct file_operations rdwr_fifo_fops = { .llseek = no_llseek, .read = pipe_read, .readv = pipe_readv, .write = pipe_write, .writev = pipe_writev, .poll = pipe_poll, .ioctl = pipe_ioctl, .open = pipe_rdwr_open, .release = pipe_rdwr_release, .fasync = pipe_rdwr_fasync, }; static struct file_operations read_pipe_fops = { .llseek = no_llseek, .read = pipe_read, .readv = pipe_readv, .write = bad_pipe_w, .poll = pipe_poll, .ioctl = pipe_ioctl, .open = pipe_read_open, .release = pipe_read_release, .fasync = pipe_read_fasync, }; static struct file_operations write_pipe_fops = { .llseek = no_llseek, .read = bad_pipe_r, .write = pipe_write, .writev = pipe_writev, .poll = pipe_poll, .ioctl = pipe_ioctl, .open = pipe_write_open, .release = pipe_write_release, .fasync = pipe_write_fasync, }; static struct file_operations rdwr_pipe_fops = { .llseek = no_llseek, .read = pipe_read, .readv = pipe_readv, .write = pipe_write, .writev = pipe_writev, .poll = pipe_poll, .ioctl = pipe_ioctl, .open = pipe_rdwr_open, .release = pipe_rdwr_release, .fasync = pipe_rdwr_fasync, }; struct pipe_inode_info * alloc_pipe_info(struct inode *inode) { struct pipe_inode_info *pipe; pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); if (pipe) { init_waitqueue_head(&pipe->wait); pipe->r_counter = pipe->w_counter = 1; pipe->inode = inode; } return pipe; } void __free_pipe_info(struct pipe_inode_info *pipe) { int i; for (i = 0; i < PIPE_BUFFERS; i++) { struct pipe_buffer *buf = pipe->bufs + i; if (buf->ops) buf->ops->release(pipe, buf); } if (pipe->tmp_page) __free_page(pipe->tmp_page); kfree(pipe); } void free_pipe_info(struct inode *inode) { __free_pipe_info(inode->i_pipe); inode->i_pipe = NULL; } static struct vfsmount *pipe_mnt __read_mostly; static int pipefs_delete_dentry(struct dentry *dentry) { return 1; } static struct dentry_operations pipefs_dentry_operations = { .d_delete = pipefs_delete_dentry, }; static struct inode * get_pipe_inode(void) { struct inode *inode = new_inode(pipe_mnt->mnt_sb); struct pipe_inode_info *pipe; if (!inode) goto fail_inode; pipe = alloc_pipe_info(inode); if (!pipe) goto fail_iput; inode->i_pipe = pipe; pipe->readers = pipe->writers = 1; inode->i_fop = &rdwr_pipe_fops; /* * Mark the inode dirty from the very beginning, * that way it will never be moved to the dirty * list because "mark_inode_dirty()" will think * that it already _is_ on the dirty list. */ inode->i_state = I_DIRTY; inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_blksize = PAGE_SIZE; return inode; fail_iput: iput(inode); fail_inode: return NULL; } int do_pipe(int *fd) { struct qstr this; char name[32]; struct dentry *dentry; struct inode * inode; struct file *f1, *f2; int error; int i, j; error = -ENFILE; f1 = get_empty_filp(); if (!f1) goto no_files; f2 = get_empty_filp(); if (!f2) goto close_f1; inode = get_pipe_inode(); if (!inode) goto close_f12; error = get_unused_fd(); if (error < 0) goto close_f12_inode; i = error; error = get_unused_fd(); if (error < 0) goto close_f12_inode_i; j = error; error = -ENOMEM; sprintf(name, "[%lu]", inode->i_ino); this.name = name; this.len = strlen(name); this.hash = inode->i_ino; /* will go */ dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this); if (!dentry) goto close_f12_inode_i_j; dentry->d_op = &pipefs_dentry_operations; d_add(dentry, inode); f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt)); f1->f_dentry = f2->f_dentry = dget(dentry); f1->f_mapping = f2->f_mapping = inode->i_mapping; /* read file */ f1->f_pos = f2->f_pos = 0; f1->f_flags = O_RDONLY; f1->f_op = &read_pipe_fops; f1->f_mode = FMODE_READ; f1->f_version = 0; /* write file */ f2->f_flags = O_WRONLY; f2->f_op = &write_pipe_fops; f2->f_mode = FMODE_WRITE; f2->f_version = 0; fd_install(i, f1); fd_install(j, f2); fd[0] = i; fd[1] = j; return 0; close_f12_inode_i_j: put_unused_fd(j); close_f12_inode_i: put_unused_fd(i); close_f12_inode: free_pipe_info(inode); iput(inode); close_f12: put_filp(f2); close_f1: put_filp(f1); no_files: return error; } /* * pipefs should _never_ be mounted by userland - too much of security hassle, * no real gain from having the whole whorehouse mounted. So we don't need * any operations on the root directory. However, we need a non-trivial * d_name - pipe: will go nicely and kill the special-casing in procfs. */ static struct super_block * pipefs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_pseudo(fs_type, "pipe:", NULL, PIPEFS_MAGIC); } static struct file_system_type pipe_fs_type = { .name = "pipefs", .get_sb = pipefs_get_sb, .kill_sb = kill_anon_super, }; static int __init init_pipe_fs(void) { int err = register_filesystem(&pipe_fs_type); if (!err) { pipe_mnt = kern_mount(&pipe_fs_type); if (IS_ERR(pipe_mnt)) { err = PTR_ERR(pipe_mnt); unregister_filesystem(&pipe_fs_type); } } return err; } static void __exit exit_pipe_fs(void) { unregister_filesystem(&pipe_fs_type); mntput(pipe_mnt); } fs_initcall(init_pipe_fs); module_exit(exit_pipe_fs); linux-2.6.17/fs/jffs2/0000755000000000000000000000000010574207644013077 5ustar rootrootlinux-2.6.17/fs/jffs2/pushpull.h0000644000000000000000000000243610564537530015130 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $ * */ #ifndef __PUSHPULL_H__ #define __PUSHPULL_H__ #include struct pushpull { unsigned char *buf; unsigned int buflen; unsigned int ofs; unsigned int reserve; }; static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) { pp->buf = buf; pp->buflen = buflen; pp->ofs = ofs; pp->reserve = reserve; } static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) { if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { return -ENOSPC; } if (bit) { pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); } else { pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); } pp->ofs++; return 0; } static inline int pushedbits(struct pushpull *pp) { return pp->ofs; } static inline int pullbit(struct pushpull *pp) { int bit; bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1; pp->ofs++; return bit; } static inline int pulledbits(struct pushpull *pp) { return pp->ofs; } #endif /* __PUSHPULL_H__ */ linux-2.6.17/fs/jffs2/nodemgmt.c0000644000000000000000000006543510564537530015071 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: nodemgmt.c,v 1.127 2005/09/20 15:49:12 dedekind Exp $ * */ #include #include #include #include #include /* For cond_resched() */ #include "nodelist.h" #include "debug.h" /** * jffs2_reserve_space - request physical space to write nodes to flash * @c: superblock info * @minsize: Minimum acceptable size of allocation * @ofs: Returned value of node offset * @len: Returned value of allocation length * @prio: Allocation type - ALLOC_{NORMAL,DELETION} * * Requests a block of physical space on the flash. Returns zero for success * and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC * or other error if appropriate. * * If it returns zero, jffs2_reserve_space() also downs the per-filesystem * allocation semaphore, to prevent more than one allocation from being * active at any time. The semaphore is later released by jffs2_commit_allocation() * * jffs2_reserve_space() may trigger garbage collection in order to make room * for the requested allocation. */ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize); int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio, uint32_t sumsize) { int ret = -EAGAIN; int blocksneeded = c->resv_blocks_write; /* align it */ minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); down(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); spin_lock(&c->erase_completion_lock); /* this needs a little more thought (true :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { int ret; uint32_t dirty, avail; /* calculate real dirty size * dirty_size contains blocks on erase_pending_list * those blocks are counted in c->nr_erasing_blocks. * If one block is actually erased, it is not longer counted as dirty_space * but it is counted in c->nr_erasing_blocks, so we add it and subtract it * with c->nr_erasing_blocks * c->sector_size again. * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks * This helps us to force gc and pick eventually a clean block to spread the load. * We add unchecked_size here, as we hopefully will find some space to use. * This will affect the sum only once, as gc first finishes checking * of nodes. */ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; if (dirty < c->nospc_dirty_size) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", dirty, c->unchecked_size, c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } /* Calc possibly available space. Possibly available means that we * don't know, if unchecked size contains obsoleted nodes, which could give us some * more usable space. This will affect the sum only once, as gc first finishes checking * of nodes. + Return -ENOSPC, if the maximum possibly available space is less or equal than * blocksneeded * sector_size. * This blocks endless gc looping on a filesystem, which is nearly full, even if * the check above passes. */ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; if ( (avail / c->sector_size) <= blocksneeded) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", avail, blocksneeded * c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } up(&c->alloc_sem); D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); if (ret) return ret; cond_resched(); if (signal_pending(current)) return -EINTR; down(&c->alloc_sem); spin_lock(&c->erase_completion_lock); } ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); if (ret) up(&c->alloc_sem); return ret; } int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize) { int ret = -EAGAIN; minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize)); spin_lock(&c->erase_completion_lock); while(ret == -EAGAIN) { ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); return ret; } /* Classify nextblock (clean, dirty of verydirty) and force to select an other one */ static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { /* Check, if we have a dirty block now, or if it was dirty already */ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->dirty_size += jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->very_dirty_list); } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->dirty_list); } } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->clean_list); } c->nextblock = NULL; } /* Select a new jeb for nextblock */ static int jffs2_find_nextblock(struct jffs2_sb_info *c) { struct list_head *next; /* Take the next block off the 'free' list */ if (list_empty(&c->free_list)) { if (!c->nr_erasing_blocks && !list_empty(&c->erasable_list)) { struct jffs2_eraseblock *ejeb; ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); list_del(&ejeb->list); list_add_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } if (!c->nr_erasing_blocks && !list_empty(&c->erasable_pending_wbuf_list)) { D1(printk(KERN_DEBUG "jffs2_find_nextblock: Flushing write buffer\n")); /* c->nextblock is NULL, no update to c->nextblock allowed */ spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); /* Have another go. It'll be on the erasable_list now */ return -EAGAIN; } if (!c->nr_erasing_blocks) { /* Ouch. We're in GC, or we wouldn't have got here. And there's no space left. At all. */ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); return -ENOSPC; } spin_unlock(&c->erase_completion_lock); /* Don't wait for it; just erase one right now */ jffs2_erase_pending_blocks(c, 1); spin_lock(&c->erase_completion_lock); /* An erase may have failed, decreasing the amount of free space available. So we must restart from the beginning */ return -EAGAIN; } next = c->free_list.next; list_del(next); c->nextblock = list_entry(next, struct jffs2_eraseblock, list); c->nr_free_blocks--; jffs2_sum_reset_collected(c->summary); /* reset collected summary */ D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset)); return 0; } /* Called with alloc sem _and_ erase_completion_lock */ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize) { struct jffs2_eraseblock *jeb = c->nextblock; uint32_t reserved_size; /* for summary information at the end of the jeb */ int ret; restart: reserved_size = 0; if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) { /* NOSUM_SIZE means not to generate summary */ if (jeb) { reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); dbg_summary("minsize=%d , jeb->free=%d ," "summary->size=%d , sumsize=%d\n", minsize, jeb->free_size, c->summary->sum_size, sumsize); } /* Is there enough space for writing out the current node, or we have to write out summary information now, close this jeb and select new nextblock? */ if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) { /* Has summary been disabled for this jeb? */ if (jffs2_sum_is_disabled(c->summary)) { sumsize = JFFS2_SUMMARY_NOSUM_SIZE; goto restart; } /* Writing out the collected summary information */ dbg_summary("generating summary for 0x%08x.\n", jeb->offset); ret = jffs2_sum_write_sumnode(c); if (ret) return ret; if (jffs2_sum_is_disabled(c->summary)) { /* jffs2_write_sumnode() couldn't write out the summary information diabling summary for this jeb and free the collected information */ sumsize = JFFS2_SUMMARY_NOSUM_SIZE; goto restart; } jffs2_close_nextblock(c, jeb); jeb = NULL; /* keep always valid value in reserved_size */ reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); } } else { if (jeb && minsize > jeb->free_size) { /* Skip the end of this block and file it as having some dirty space */ /* If there's a pending write to it, flush now */ if (jffs2_wbuf_dirty(c)) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); jeb = c->nextblock; goto restart; } c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->wasted_size += jeb->free_size; jeb->free_size = 0; jffs2_close_nextblock(c, jeb); jeb = NULL; } } if (!jeb) { ret = jffs2_find_nextblock(c); if (ret) return ret; jeb = c->nextblock; if (jeb->free_size != c->sector_size - c->cleanmarker_size) { printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); goto restart; } } /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has enough space */ *ofs = jeb->offset + (c->sector_size - jeb->free_size); *len = jeb->free_size - reserved_size; if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && !jeb->first_node->next_in_ino) { /* Only node in it beforehand was a CLEANMARKER node (we think). So mark it obsolete now that there's going to be another node in the block. This will reduce used_size to zero but We've already set c->nextblock so that jffs2_mark_node_obsolete() won't try to refile it to the dirty_list. */ spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, jeb->first_node); spin_lock(&c->erase_completion_lock); } D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); return 0; } /** * jffs2_add_physical_node_ref - add a physical node reference to the list * @c: superblock info * @new: new node reference to add * @len: length of this physical node * @dirty: dirty flag for new node * * Should only be used to report nodes for which space has been allocated * by jffs2_reserve_space. * * Must be called with the alloc_sem held. */ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) { struct jffs2_eraseblock *jeb; uint32_t len; jeb = &c->blocks[new->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, new); D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); #if 1 /* we could get some obsolete nodes after nextblock was refiled in wbuf.c */ if ((c->nextblock || !ref_obsolete(new)) &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) { printk(KERN_WARNING "argh. node added in wrong place\n"); jffs2_free_raw_node_ref(new); return -EINVAL; } #endif spin_lock(&c->erase_completion_lock); if (!jeb->first_node) jeb->first_node = new; if (jeb->last_node) jeb->last_node->next_phys = new; jeb->last_node = new; jeb->free_size -= len; c->free_size -= len; if (ref_obsolete(new)) { jeb->dirty_size += len; c->dirty_size += len; } else { jeb->used_size += len; c->used_size += len; } if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) { /* If it lives on the dirty_list, jffs2_reserve_space will put it there */ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); if (jffs2_wbuf_dirty(c)) { /* Flush the last write in the block if it's outstanding */ spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); } list_add_tail(&jeb->list, &c->clean_list); c->nextblock = NULL; } jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); return 0; } void jffs2_complete_reservation(struct jffs2_sb_info *c) { D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n")); jffs2_garbage_collect_trigger(c); up(&c->alloc_sem); } static inline int on_list(struct list_head *obj, struct list_head *head) { struct list_head *this; list_for_each(this, head) { if (this == obj) { D1(printk("%p is on list at %p\n", obj, head)); return 1; } } return 0; } void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref) { struct jffs2_eraseblock *jeb; int blocknr; struct jffs2_unknown_node n; int ret, addedsize; size_t retlen; if(!ref) { printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n"); return; } if (ref_obsolete(ref)) { D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref))); return; } blocknr = ref->flash_offset / c->sector_size; if (blocknr >= c->nr_blocks) { printk(KERN_NOTICE "raw node at 0x%08x is off the end of device!\n", ref->flash_offset); BUG(); } jeb = &c->blocks[blocknr]; if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) && !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) { /* Hm. This may confuse static lock analysis. If any of the above three conditions is false, we're going to return from this function without actually obliterating any nodes or freeing any jffs2_raw_node_refs. So we don't need to stop erases from happening, or protect against people holding an obsolete jffs2_raw_node_ref without the erase_completion_lock. */ down(&c->erase_free_sem); } spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); jeb->unchecked_size -= ref_totlen(c, jeb, ref); c->unchecked_size -= ref_totlen(c, jeb, ref); } else { D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); jeb->used_size -= ref_totlen(c, jeb, ref); c->used_size -= ref_totlen(c, jeb, ref); } // Take care, that wasted size is taken into concern if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { D1(printk(KERN_DEBUG "Dirtying\n")); addedsize = ref_totlen(c, jeb, ref); jeb->dirty_size += ref_totlen(c, jeb, ref); c->dirty_size += ref_totlen(c, jeb, ref); /* Convert wasted space to dirty, if not a bad block */ if (jeb->wasted_size) { if (on_list(&jeb->list, &c->bad_used_list)) { D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n", jeb->offset)); addedsize = 0; /* To fool the refiling code later */ } else { D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n", jeb->wasted_size, jeb->offset)); addedsize += jeb->wasted_size; jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; } } } else { D1(printk(KERN_DEBUG "Wasting\n")); addedsize = 0; jeb->wasted_size += ref_totlen(c, jeb, ref); c->wasted_size += ref_totlen(c, jeb, ref); } ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; jffs2_dbg_acct_sanity_check_nolock(c, jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); if (c->flags & JFFS2_SB_FLAG_SCANNING) { /* Flash scanning is in progress. Don't muck about with the block lists because they're not ready yet, and don't actually obliterate nodes that look obsolete. If they weren't marked obsolete on the flash at the time they _became_ obsolete, there was probably a reason for that. */ spin_unlock(&c->erase_completion_lock); /* We didn't lock the erase_free_sem */ return; } if (jeb == c->nextblock) { D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); } else if (!jeb->used_size && !jeb->unchecked_size) { if (jeb == c->gcblock) { D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset)); c->gcblock = NULL; } else { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset)); list_del(&jeb->list); } if (jffs2_wbuf_dirty(c)) { D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); } else { if (jiffies & 127) { /* Most of the time, we just erase it immediately. Otherwise we spend ages scanning it on mount, etc. */ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); list_add_tail(&jeb->list, &c->erasable_list); } } D1(printk(KERN_DEBUG "Done OK\n")); } else if (jeb == c->gcblock) { D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset)); } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset)); list_del(&jeb->list); D1(printk(KERN_DEBUG "...and adding to dirty_list\n")); list_add_tail(&jeb->list, &c->dirty_list); } else if (VERYDIRTY(c, jeb->dirty_size) && !VERYDIRTY(c, jeb->dirty_size - addedsize)) { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); list_del(&jeb->list); D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); list_add_tail(&jeb->list, &c->very_dirty_list); } else { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); } spin_unlock(&c->erase_completion_lock); if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) || (c->flags & JFFS2_SB_FLAG_BUILDING)) { /* We didn't lock the erase_free_sem */ return; } /* The erase_free_sem is locked, and has been since before we marked the node obsolete and potentially put its eraseblock onto the erase_pending_list. Thus, we know that the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet by jffs2_free_all_node_refs() in erase.c. Which is nice. */ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); goto out_erase_sem; } if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); goto out_erase_sem; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); goto out_erase_sem; } /* XXX FIXME: This is ugly now */ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); goto out_erase_sem; } /* Nodes which have been marked obsolete no longer need to be associated with any inode. Remove them from the per-inode list. Note we can't do this for NAND at the moment because we need obsolete dirent nodes to stay on the lists, because of the horridness in jffs2_garbage_collect_deletion_dirent(). Also because we delete the inocache, and on NAND we need that to stay around until all the nodes are actually erased, in order to stop us from giving the same inode number to another newly created inode. */ if (ref->next_in_ino) { struct jffs2_inode_cache *ic; struct jffs2_raw_node_ref **p; spin_lock(&c->erase_completion_lock); ic = jffs2_raw_ref_to_ic(ref); for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino)) ; *p = ref->next_in_ino; ref->next_in_ino = NULL; if (ic->nodes == (void *)ic && ic->nlink == 0) jffs2_del_ino_cache(c, ic); spin_unlock(&c->erase_completion_lock); } /* Merge with the next node in the physical list, if there is one and if it's also obsolete and if it doesn't belong to any inode */ if (ref->next_phys && ref_obsolete(ref->next_phys) && !ref->next_phys->next_in_ino) { struct jffs2_raw_node_ref *n = ref->next_phys; spin_lock(&c->erase_completion_lock); ref->__totlen += n->__totlen; ref->next_phys = n->next_phys; if (jeb->last_node == n) jeb->last_node = ref; if (jeb->gc_node == n) { /* gc will be happy continuing gc on this node */ jeb->gc_node=ref; } spin_unlock(&c->erase_completion_lock); jffs2_free_raw_node_ref(n); } /* Also merge with the previous node in the list, if there is one and that one is obsolete */ if (ref != jeb->first_node ) { struct jffs2_raw_node_ref *p = jeb->first_node; spin_lock(&c->erase_completion_lock); while (p->next_phys != ref) p = p->next_phys; if (ref_obsolete(p) && !ref->next_in_ino) { p->__totlen += ref->__totlen; if (jeb->last_node == ref) { jeb->last_node = p; } if (jeb->gc_node == ref) { /* gc will be happy continuing gc on this node */ jeb->gc_node=p; } p->next_phys = ref->next_phys; jffs2_free_raw_node_ref(ref); } spin_unlock(&c->erase_completion_lock); } out_erase_sem: up(&c->erase_free_sem); } int jffs2_thread_should_wake(struct jffs2_sb_info *c) { int ret = 0; uint32_t dirty; if (c->unchecked_size) { D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", c->unchecked_size, c->checked_ino)); return 1; } /* dirty_size contains blocks on erase_pending_list * those blocks are counted in c->nr_erasing_blocks. * If one block is actually erased, it is not longer counted as dirty_space * but it is counted in c->nr_erasing_blocks, so we add it and subtract it * with c->nr_erasing_blocks * c->sector_size again. * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks * This helps us to force gc and pick eventually a clean block to spread the load. */ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && (dirty > c->nospc_dirty_size)) ret = 1; D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); return ret; } linux-2.6.17/fs/jffs2/summary.h0000644000000000000000000001302310564537530014743 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: summary.h,v 1.2 2005/09/26 11:37:21 havasi Exp $ * */ #ifndef JFFS2_SUMMARY_H #define JFFS2_SUMMARY_H #include #include #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->dirty_size += _x; \ jeb->free_size -= _x ; jeb->dirty_size += _x; \ }while(0) #define USED_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->used_size += _x; \ jeb->free_size -= _x ; jeb->used_size += _x; \ }while(0) #define WASTED_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->wasted_size += _x; \ jeb->free_size -= _x ; jeb->wasted_size += _x; \ }while(0) #define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->unchecked_size += _x; \ jeb->free_size -= _x ; jeb->unchecked_size += _x; \ }while(0) #define BLK_STATE_ALLFF 0 #define BLK_STATE_CLEAN 1 #define BLK_STATE_PARTDIRTY 2 #define BLK_STATE_CLEANMARKER 3 #define BLK_STATE_ALLDIRTY 4 #define BLK_STATE_BADBLOCK 5 #define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff #define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash)) #define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x)) /* Summary structures used on flash */ struct jffs2_sum_unknown_flash { jint16_t nodetype; /* node type */ }; struct jffs2_sum_inode_flash { jint16_t nodetype; /* node type */ jint32_t inode; /* inode number */ jint32_t version; /* inode version */ jint32_t offset; /* offset on jeb */ jint32_t totlen; /* record length */ } __attribute__((packed)); struct jffs2_sum_dirent_flash { jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ jint32_t totlen; /* record length */ jint32_t offset; /* offset on jeb */ jint32_t pino; /* parent inode */ jint32_t version; /* dirent version */ jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ uint8_t name[0]; /* dirent name */ } __attribute__((packed)); union jffs2_sum_flash { struct jffs2_sum_unknown_flash u; struct jffs2_sum_inode_flash i; struct jffs2_sum_dirent_flash d; }; /* Summary structures used in the memory */ struct jffs2_sum_unknown_mem { union jffs2_sum_mem *next; jint16_t nodetype; /* node type */ }; struct jffs2_sum_inode_mem { union jffs2_sum_mem *next; jint16_t nodetype; /* node type */ jint32_t inode; /* inode number */ jint32_t version; /* inode version */ jint32_t offset; /* offset on jeb */ jint32_t totlen; /* record length */ } __attribute__((packed)); struct jffs2_sum_dirent_mem { union jffs2_sum_mem *next; jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ jint32_t totlen; /* record length */ jint32_t offset; /* ofset on jeb */ jint32_t pino; /* parent inode */ jint32_t version; /* dirent version */ jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ uint8_t name[0]; /* dirent name */ } __attribute__((packed)); union jffs2_sum_mem { struct jffs2_sum_unknown_mem u; struct jffs2_sum_inode_mem i; struct jffs2_sum_dirent_mem d; }; /* Summary related information stored in superblock */ struct jffs2_summary { uint32_t sum_size; /* collected summary information for nextblock */ uint32_t sum_num; uint32_t sum_padded; union jffs2_sum_mem *sum_list_head; union jffs2_sum_mem *sum_list_tail; jint32_t *sum_buf; /* buffer for writing out summary */ }; /* Summary marker is stored at the end of every sumarized erase block */ struct jffs2_sum_marker { jint32_t offset; /* offset of the summary node in the jeb */ jint32_t magic; /* == JFFS2_SUM_MAGIC */ }; #define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker)) #ifdef CONFIG_JFFS2_SUMMARY /* SUMMARY SUPPORT ENABLED */ #define jffs2_sum_active() (1) int jffs2_sum_init(struct jffs2_sb_info *c); void jffs2_sum_exit(struct jffs2_sb_info *c); void jffs2_sum_disable_collecting(struct jffs2_summary *s); int jffs2_sum_is_disabled(struct jffs2_summary *s); void jffs2_sum_reset_collected(struct jffs2_summary *s); void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s); int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, uint32_t to); int jffs2_sum_write_sumnode(struct jffs2_sb_info *c); int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size); int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs); int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs); int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t ofs, uint32_t *pseudo_random); #else /* SUMMARY DISABLED */ #define jffs2_sum_active() (0) #define jffs2_sum_init(a) (0) #define jffs2_sum_exit(a) #define jffs2_sum_disable_collecting(a) #define jffs2_sum_is_disabled(a) (0) #define jffs2_sum_reset_collected(a) #define jffs2_sum_add_kvec(a,b,c,d) (0) #define jffs2_sum_move_collected(a,b) #define jffs2_sum_write_sumnode(a) (0) #define jffs2_sum_add_padding_mem(a,b) #define jffs2_sum_add_inode_mem(a,b,c) #define jffs2_sum_add_dirent_mem(a,b,c) #define jffs2_sum_scan_sumnode(a,b,c,d) (0) #endif /* CONFIG_JFFS2_SUMMARY */ #endif /* JFFS2_SUMMARY_H */ linux-2.6.17/fs/jffs2/compr_zlib.c0000644000000000000000000001353510564537530015411 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr_zlib.c,v 1.32 2005/11/07 11:14:38 gleixner Exp $ * */ #if !defined(__KERNEL__) && !defined(__ECOS) #error "The userspace support got too messy and was removed. Update your mkfs.jffs2" #endif #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" /* Plan: call deflate() with avail_in == *sourcelen, avail_out = *dstlen - 12 and flush == Z_FINISH. If it doesn't manage to finish, call it again with avail_in == 0 and avail_out set to the remaining 12 bytes for it to clean up. Q: Is 12 bytes sufficient? */ #define STREAM_END_SPACE 12 static DEFINE_MUTEX(deflate_mutex); static DEFINE_MUTEX(inflate_mutex); static z_stream inf_strm, def_strm; #ifdef __KERNEL__ /* Linux-only */ #include #include #include static int __init alloc_workspaces(void) { def_strm.workspace = vmalloc(zlib_deflate_workspacesize()); if (!def_strm.workspace) { printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); return -ENOMEM; } D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); inf_strm.workspace = vmalloc(zlib_inflate_workspacesize()); if (!inf_strm.workspace) { printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); vfree(def_strm.workspace); return -ENOMEM; } D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); return 0; } static void free_workspaces(void) { vfree(def_strm.workspace); vfree(inf_strm.workspace); } #else #define alloc_workspaces() (0) #define free_workspaces() do { } while(0) #endif /* __KERNEL__ */ static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { int ret; if (*dstlen <= STREAM_END_SPACE) return -1; mutex_lock(&deflate_mutex); if (Z_OK != zlib_deflateInit(&def_strm, 3)) { printk(KERN_WARNING "deflateInit failed\n"); mutex_unlock(&deflate_mutex); return -1; } def_strm.next_in = data_in; def_strm.total_in = 0; def_strm.next_out = cpage_out; def_strm.total_out = 0; while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) { def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE); def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out); D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n", def_strm.avail_in, def_strm.avail_out)); ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH); D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out)); if (ret != Z_OK) { D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret)); zlib_deflateEnd(&def_strm); mutex_unlock(&deflate_mutex); return -1; } } def_strm.avail_out += STREAM_END_SPACE; def_strm.avail_in = 0; ret = zlib_deflate(&def_strm, Z_FINISH); zlib_deflateEnd(&def_strm); if (ret != Z_STREAM_END) { D1(printk(KERN_DEBUG "final deflate returned %d\n", ret)); ret = -1; goto out; } if (def_strm.total_out >= def_strm.total_in) { D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n", def_strm.total_in, def_strm.total_out)); ret = -1; goto out; } D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", def_strm.total_in, def_strm.total_out)); *dstlen = def_strm.total_out; *sourcelen = def_strm.total_in; ret = 0; out: mutex_unlock(&deflate_mutex); return ret; } static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen, void *model) { int ret; int wbits = MAX_WBITS; mutex_lock(&inflate_mutex); inf_strm.next_in = data_in; inf_strm.avail_in = srclen; inf_strm.total_in = 0; inf_strm.next_out = cpage_out; inf_strm.avail_out = destlen; inf_strm.total_out = 0; /* If it's deflate, and it's got no preset dictionary, then we can tell zlib to skip the adler32 check. */ if (srclen > 2 && !(data_in[1] & PRESET_DICT) && ((data_in[0] & 0x0f) == Z_DEFLATED) && !(((data_in[0]<<8) + data_in[1]) % 31)) { D2(printk(KERN_DEBUG "inflate skipping adler32\n")); wbits = -((data_in[0] >> 4) + 8); inf_strm.next_in += 2; inf_strm.avail_in -= 2; } else { /* Let this remain D1 for now -- it should never happen */ D1(printk(KERN_DEBUG "inflate not skipping adler32\n")); } if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { printk(KERN_WARNING "inflateInit failed\n"); mutex_unlock(&inflate_mutex); return 1; } while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) ; if (ret != Z_STREAM_END) { printk(KERN_NOTICE "inflate returned %d\n", ret); } zlib_inflateEnd(&inf_strm); mutex_unlock(&inflate_mutex); return 0; } static struct jffs2_compressor jffs2_zlib_comp = { .priority = JFFS2_ZLIB_PRIORITY, .name = "zlib", .compr = JFFS2_COMPR_ZLIB, .compress = &jffs2_zlib_compress, .decompress = &jffs2_zlib_decompress, #ifdef JFFS2_ZLIB_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int __init jffs2_zlib_init(void) { int ret; ret = alloc_workspaces(); if (ret) return ret; ret = jffs2_register_compressor(&jffs2_zlib_comp); if (ret) free_workspaces(); return ret; } void jffs2_zlib_exit(void) { jffs2_unregister_compressor(&jffs2_zlib_comp); free_workspaces(); } linux-2.6.17/fs/jffs2/compr_rubin.c0000644000000000000000000001705710564537530015573 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by Arjan van de Ven * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $ * */ #include #include #include #include "compr_rubin.h" #include "histo_mips.h" #include "compr.h" static void init_rubin(struct rubin_state *rs, int div, int *bits) { int c; rs->q = 0; rs->p = (long) (2 * UPPER_BIT_RUBIN); rs->bit_number = (long) 0; rs->bit_divider = div; for (c=0; c<8; c++) rs->bits[c] = bits[c]; } static int encode(struct rubin_state *rs, long A, long B, int symbol) { long i0, i1; int ret; while ((rs->q >= UPPER_BIT_RUBIN) || ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) { rs->bit_number++; ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0); if (ret) return ret; rs->q &= LOWER_BITS_RUBIN; rs->q <<= 1; rs->p <<= 1; } i0 = A * rs->p / (A + B); if (i0 <= 0) { i0 = 1; } if (i0 >= rs->p) { i0 = rs->p - 1; } i1 = rs->p - i0; if (symbol == 0) rs->p = i0; else { rs->p = i1; rs->q += i0; } return 0; } static void end_rubin(struct rubin_state *rs) { int i; for (i = 0; i < RUBIN_REG_SIZE; i++) { pushbit(&rs->pp, (UPPER_BIT_RUBIN & rs->q) ? 1 : 0, 1); rs->q &= LOWER_BITS_RUBIN; rs->q <<= 1; } } static void init_decode(struct rubin_state *rs, int div, int *bits) { init_rubin(rs, div, bits); /* behalve lower */ rs->rec_q = 0; for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp))) ; } static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q) { register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN; unsigned long rec_q; int c, bits = 0; /* * First, work out how many bits we need from the input stream. * Note that we have already done the initial check on this * loop prior to calling this function. */ do { bits++; q &= lower_bits_rubin; q <<= 1; p <<= 1; } while ((q >= UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN)); rs->p = p; rs->q = q; rs->bit_number += bits; /* * Now get the bits. We really want this to be "get n bits". */ rec_q = rs->rec_q; do { c = pullbit(&rs->pp); rec_q &= lower_bits_rubin; rec_q <<= 1; rec_q += c; } while (--bits); rs->rec_q = rec_q; } static int decode(struct rubin_state *rs, long A, long B) { unsigned long p = rs->p, q = rs->q; long i0, threshold; int symbol; if (q >= UPPER_BIT_RUBIN || ((p + q) <= UPPER_BIT_RUBIN)) __do_decode(rs, p, q); i0 = A * rs->p / (A + B); if (i0 <= 0) { i0 = 1; } if (i0 >= rs->p) { i0 = rs->p - 1; } threshold = rs->q + i0; symbol = rs->rec_q >= threshold; if (rs->rec_q >= threshold) { rs->q += i0; i0 = rs->p - i0; } rs->p = i0; return symbol; } static int out_byte(struct rubin_state *rs, unsigned char byte) { int i, ret; struct rubin_state rs_copy; rs_copy = *rs; for (i=0;i<8;i++) { ret = encode(rs, rs->bit_divider-rs->bits[i],rs->bits[i],byte&1); if (ret) { /* Failed. Restore old state */ *rs = rs_copy; return ret; } byte=byte>>1; } return 0; } static int in_byte(struct rubin_state *rs) { int i, result = 0, bit_divider = rs->bit_divider; for (i = 0; i < 8; i++) result |= decode(rs, bit_divider - rs->bits[i], rs->bits[i]) << i; return result; } static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) { int outpos = 0; int pos=0; struct rubin_state rs; init_pushpull(&rs.pp, cpage_out, *dstlen * 8, 0, 32); init_rubin(&rs, bit_divider, bits); while (pos < (*sourcelen) && !out_byte(&rs, data_in[pos])) pos++; end_rubin(&rs); if (outpos > pos) { /* We failed */ return -1; } /* Tell the caller how much we managed to compress, * and how much space it took */ outpos = (pushedbits(&rs.pp)+7)/8; if (outpos >= pos) return -1; /* We didn't actually compress */ *sourcelen = pos; *dstlen = outpos; return 0; } #if 0 /* _compress returns the compressed size, -1 if bigger */ int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); } #endif static int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { int bits[8]; unsigned char histo[256]; int i; int ret; uint32_t mysrclen, mydstlen; mysrclen = *sourcelen; mydstlen = *dstlen - 8; if (*dstlen <= 12) return -1; memset(histo, 0, 256); for (i=0; i 255) bits[i] = 255; cpage_out[i] = bits[i]; } ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, &mydstlen); if (ret) return ret; /* Add back the 8 bytes we took for the probabilities */ mydstlen += 8; if (mysrclen <= mydstlen) { /* We compressed */ return -1; } *sourcelen = mysrclen; *dstlen = mydstlen; return 0; } static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, unsigned char *page_out, uint32_t srclen, uint32_t destlen) { int outpos = 0; struct rubin_state rs; init_pushpull(&rs.pp, cdata_in, srclen, 0, 0); init_decode(&rs, bit_divider, bits); while (outpos < destlen) { page_out[outpos++] = in_byte(&rs); } } static int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t sourcelen, uint32_t dstlen, void *model) { rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); return 0; } static int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t sourcelen, uint32_t dstlen, void *model) { int bits[8]; int c; for (c=0; c<8; c++) bits[c] = data_in[c]; rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); return 0; } static struct jffs2_compressor jffs2_rubinmips_comp = { .priority = JFFS2_RUBINMIPS_PRIORITY, .name = "rubinmips", .compr = JFFS2_COMPR_DYNRUBIN, .compress = NULL, /*&jffs2_rubinmips_compress,*/ .decompress = &jffs2_rubinmips_decompress, #ifdef JFFS2_RUBINMIPS_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int jffs2_rubinmips_init(void) { return jffs2_register_compressor(&jffs2_rubinmips_comp); } void jffs2_rubinmips_exit(void) { jffs2_unregister_compressor(&jffs2_rubinmips_comp); } static struct jffs2_compressor jffs2_dynrubin_comp = { .priority = JFFS2_DYNRUBIN_PRIORITY, .name = "dynrubin", .compr = JFFS2_COMPR_RUBINMIPS, .compress = jffs2_dynrubin_compress, .decompress = &jffs2_dynrubin_decompress, #ifdef JFFS2_DYNRUBIN_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int jffs2_dynrubin_init(void) { return jffs2_register_compressor(&jffs2_dynrubin_comp); } void jffs2_dynrubin_exit(void) { jffs2_unregister_compressor(&jffs2_dynrubin_comp); } linux-2.6.17/fs/jffs2/README.Locking0000644000000000000000000001442610564537530015352 0ustar rootroot $Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $ JFFS2 LOCKING DOCUMENTATION --------------------------- At least theoretically, JFFS2 does not require the Big Kernel Lock (BKL), which was always helpfully obtained for it by Linux 2.4 VFS code. It has its own locking, as described below. This document attempts to describe the existing locking rules for JFFS2. It is not expected to remain perfectly up to date, but ought to be fairly close. alloc_sem --------- The alloc_sem is a per-filesystem semaphore, used primarily to ensure contiguous allocation of space on the medium. It is automatically obtained during space allocations (jffs2_reserve_space()) and freed upon write completion (jffs2_complete_reservation()). Note that the garbage collector will obtain this right at the beginning of jffs2_garbage_collect_pass() and release it at the end, thereby preventing any other write activity on the file system during a garbage collect pass. When writing new nodes, the alloc_sem must be held until the new nodes have been properly linked into the data structures for the inode to which they belong. This is for the benefit of NAND flash - adding new nodes to an inode may obsolete old ones, and by holding the alloc_sem until this happens we ensure that any data in the write-buffer at the time this happens are part of the new node, not just something that was written afterwards. Hence, we can ensure the newly-obsoleted nodes don't actually get erased until the write-buffer has been flushed to the medium. With the introduction of NAND flash support and the write-buffer, the alloc_sem is also used to protect the wbuf-related members of the jffs2_sb_info structure. Atomically reading the wbuf_len member to see if the wbuf is currently holding any data is permitted, though. Ordering constraints: See f->sem. File Semaphore f->sem --------------------- This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. It protects the contents of the jffs2_inode_info private inode data, including the linked list of node fragments (but see the notes below on erase_completion_lock), etc. The reason that the i_sem itself isn't used for this purpose is to avoid deadlocks with garbage collection -- the VFS will lock the i_sem before calling a function which may need to allocate space. The allocation may trigger garbage-collection, which may need to move a node belonging to the inode which was locked in the first place by the VFS. If the garbage collection code were to attempt to lock the i_sem of the inode from which it's garbage-collecting a physical node, this lead to deadlock, unless we played games with unlocking the i_sem before calling the space allocation functions. Instead of playing such games, we just have an extra internal semaphore, which is obtained by the garbage collection code and also by the normal file system code _after_ allocation of space. Ordering constraints: 1. Never attempt to allocate space or lock alloc_sem with any f->sem held. 2. Never attempt to lock two file semaphores in one thread. No ordering rules have been made for doing so. erase_completion_lock spinlock ------------------------------ This is used to serialise access to the eraseblock lists, to the per-eraseblock lists of physical jffs2_raw_node_ref structures, and (NB) the per-inode list of physical nodes. The latter is a special case - see below. As the MTD API no longer permits erase-completion callback functions to be called from bottom-half (timer) context (on the basis that nobody ever actually implemented such a thing), it's now sufficient to use a simple spin_lock() rather than spin_lock_bh(). Note that the per-inode list of physical nodes (f->nodes) is a special case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in the list are protected by the file semaphore f->sem. But the erase code may remove _obsolete_ nodes from the list while holding only the erase_completion_lock. So you can walk the list only while holding the erase_completion_lock, and can drop the lock temporarily mid-walk as long as the pointer you're holding is to a _valid_ node, not an obsolete one. The erase_completion_lock is also used to protect the c->gc_task pointer when the garbage collection thread exits. The code to kill the GC thread locks it, sends the signal, then unlocks it - while the GC thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. inocache_lock spinlock ---------------------- This spinlock protects the hashed list (c->inocache_list) of the in-core jffs2_inode_cache objects (each inode in JFFS2 has the correspondent jffs2_inode_cache object). So, the inocache_lock has to be locked while walking the c->inocache_list hash buckets. This spinlock also covers allocation of new inode numbers, which is currently just '++->highest_ino++', but might one day get more complicated if we need to deal with wrapping after 4 milliard inode numbers are used. Note, the f->sem guarantees that the correspondent jffs2_inode_cache will not be removed. So, it is allowed to access it without locking the inocache_lock spinlock. Ordering constraints: If both erase_completion_lock and inocache_lock are needed, the c->erase_completion has to be acquired first. erase_free_sem -------------- This semaphore is only used by the erase code which frees obsolete node references and the jffs2_garbage_collect_deletion_dirent() function. The latter function on NAND flash must read _obsolete_ nodes to determine whether the 'deletion dirent' under consideration can be discarded or whether it is still required to show that an inode has been unlinked. Because reading from the flash may sleep, the erase_completion_lock cannot be held, so an alternative, more heavyweight lock was required to prevent the erase code from freeing the jffs2_raw_node_ref structures in question while the garbage collection code is looking at them. Suggestions for alternative solutions to this problem would be welcomed. wbuf_sem -------- This read/write semaphore protects against concurrent access to the write-behind buffer ('wbuf') used for flash chips where we must write in blocks. It protects both the contents of the wbuf and the metadata which indicates which flash region (if any) is currently covered by the buffer. Ordering constraints: Lock wbuf_sem last, after the alloc_sem or and f->sem. linux-2.6.17/fs/jffs2/write.c0000644000000000000000000005176110564537530014406 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: write.c,v 1.97 2005/11/07 11:14:42 gleixner Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) { struct jffs2_inode_cache *ic; ic = jffs2_alloc_inode_cache(); if (!ic) { return -ENOMEM; } memset(ic, 0, sizeof(*ic)); f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_PRESENT; jffs2_add_ino_cache(c, f->inocache); D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); ri->ino = cpu_to_je32(f->inocache->ino); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(PAD(sizeof(*ri))); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->mode = cpu_to_jemode(mode); f->highest_version = 1; ri->version = cpu_to_je32(f->highest_version); return 0; } /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, write it to the flash, link it into the existing inode/fragment list */ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dnode *fn; size_t retlen; struct kvec vecs[2]; int ret; int retried = 0; unsigned long cnt = 2; D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n"); BUG(); } ); vecs[0].iov_base = ri; vecs[0].iov_len = sizeof(*ri); vecs[1].iov_base = (unsigned char *)data; vecs[1].iov_len = datalen; jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen); } raw = jffs2_alloc_raw_node_ref(); if (!raw) return ERR_PTR(-ENOMEM); fn = jffs2_alloc_full_dnode(); if (!fn) { jffs2_free_raw_node_ref(raw); return ERR_PTR(-ENOMEM); } fn->ofs = je32_to_cpu(ri->offset); fn->size = je32_to_cpu(ri->dsize); fn->frags = 0; /* check number of valid vecs */ if (!datalen || !data) cnt = 1; retry: fn->raw = raw; raw->flash_offset = flash_ofs; raw->__totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) { BUG_ON(!retried); D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, " "highest version %d -> updating dnode\n", je32_to_cpu(ri->version), f->highest_version)); ri->version = cpu_to_je32(++f->highest_version); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); } ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:f->inocache->ino); if (ret || (retlen != sizeof(*ri) + datalen)) { printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*ri)+datalen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ if (retlen) { /* Doesn't belong to any inode */ raw->next_in_ino = NULL; /* Don't change raw->size to match retlen. We may have written the node header already, and only the data will seem corrupted, in which case the scan would skip over any node we write before the original intended end of this node */ raw->flash_offset |= REF_OBSOLETE; jffs2_add_physical_node_ref(c, raw); jffs2_mark_node_obsolete(c, raw); } else { printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); jffs2_free_raw_node_ref(raw); } if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; retried = 1; D1(printk(KERN_DEBUG "Retrying failed write.\n")); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, JFFS2_SUMMARY_INODE_SIZE); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE); down(&f->sem); } if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); jffs2_free_raw_node_ref(raw); } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dnode(fn); return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ /* If node covers at least a whole page, or if it starts at the beginning of a page and runs to the end of the file, or if it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. */ if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) || ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) && (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) { raw->flash_offset |= REF_PRISTINE; } else { raw->flash_offset |= REF_NORMAL; } jffs2_add_physical_node_ref(c, raw); /* Link into per-inode list */ spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); if (retried) { jffs2_dbg_acct_sanity_check(c,NULL); } return fn; } struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; size_t retlen; struct kvec vecs[2]; int retried = 0; int ret; D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), je32_to_cpu(rd->name_crc))); D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n"); BUG(); } ); vecs[0].iov_base = rd; vecs[0].iov_len = sizeof(*rd); vecs[1].iov_base = (unsigned char *)name; vecs[1].iov_len = namelen; jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); raw = jffs2_alloc_raw_node_ref(); if (!raw) return ERR_PTR(-ENOMEM); fd = jffs2_alloc_full_dirent(namelen+1); if (!fd) { jffs2_free_raw_node_ref(raw); return ERR_PTR(-ENOMEM); } fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(name, strlen(name)); fd->type = rd->type; memcpy(fd->name, name, namelen); fd->name[namelen]=0; retry: fd->raw = raw; raw->flash_offset = flash_ofs; raw->__totlen = PAD(sizeof(*rd)+namelen); raw->next_phys = NULL; if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) { BUG_ON(!retried); D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, " "highest version %d -> updating dirent\n", je32_to_cpu(rd->version), f->highest_version)); rd->version = cpu_to_je32(++f->highest_version); fd->version = je32_to_cpu(rd->version); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); } ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); if (ret || (retlen != sizeof(*rd) + namelen)) { printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*rd)+namelen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ if (retlen) { raw->next_in_ino = NULL; raw->flash_offset |= REF_OBSOLETE; jffs2_add_physical_node_ref(c, raw); jffs2_mark_node_obsolete(c, raw); } else { printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); jffs2_free_raw_node_ref(raw); } if (!retried && (raw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; retried = 1; D1(printk(KERN_DEBUG "Retrying failed write.\n")); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); down(&f->sem); } if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); jffs2_free_raw_node_ref(raw); } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dirent(fd); return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ raw->flash_offset |= REF_PRISTINE; jffs2_add_physical_node_ref(c, raw); spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; spin_unlock(&c->erase_completion_lock); if (retried) { jffs2_dbg_acct_sanity_check(c,NULL); } return fd; } /* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that we don't have to go digging in struct inode or its equivalent. It should set: mode, uid, gid, (starting)isize, atime, ctime, mtime */ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, unsigned char *buf, uint32_t offset, uint32_t writelen, uint32_t *retlen) { int ret = 0; uint32_t writtenlen = 0; D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n", f->inocache->ino, offset, writelen)); while(writelen) { struct jffs2_full_dnode *fn; unsigned char *comprbuf = NULL; uint16_t comprtype = JFFS2_COMPR_NONE; uint32_t phys_ofs, alloclen; uint32_t datalen, cdatalen; int retried = 0; retry: D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); break; } down(&f->sem); datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->ino = cpu_to_je32(f->inocache->ino); ri->version = cpu_to_je32(++f->highest_version); ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen)); ri->offset = cpu_to_je32(offset); ri->csize = cpu_to_je32(cdatalen); ri->dsize = cpu_to_je32(datalen); ri->compr = comprtype & 0xff; ri->usercompr = (comprtype >> 8 ) & 0xff; ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY); jffs2_free_comprbuf(comprbuf, buf); if (IS_ERR(fn)) { ret = PTR_ERR(fn); up(&f->sem); jffs2_complete_reservation(c); if (!retried) { /* Write error to be retried */ retried = 1; D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n")); goto retry; } break; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } if (ret) { /* Eep */ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret)); jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); up(&f->sem); jffs2_complete_reservation(c); break; } up(&f->sem); jffs2_complete_reservation(c); if (!datalen) { printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n"); ret = -EIO; break; } D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen)); writtenlen += datalen; offset += datalen; writelen -= datalen; buf += datalen; } *retlen = writtenlen; return ret; } int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen) { struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; uint32_t alloclen, phys_ofs; int ret; /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { up(&f->sem); return ret; } ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", jemode_to_cpu(ri->mode))); if (IS_ERR(fn)) { D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); return -ENOMEM; } down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_f->inocache->ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = ri->ino; rd->mctime = ri->ctime; rd->nsize = namelen; rd->type = DT_REG; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); up(&dir_f->sem); return PTR_ERR(fd); } /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); up(&dir_f->sem); return 0; } int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time) { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; uint32_t alloclen, phys_ofs; int ret; if (1 /* alternative branch needs testing */ || !jffs2_can_mark_obsolete(c)) { /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */ rd = jffs2_alloc_raw_dirent(); if (!rd) return -ENOMEM; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); return ret; } down(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_f->inocache->ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(0); rd->mctime = cpu_to_je32(time); rd->nsize = namelen; rd->type = DT_UNKNOWN; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { jffs2_complete_reservation(c); up(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); } else { struct jffs2_full_dirent **prev = &dir_f->dents; uint32_t nhash = full_name_hash(name, namelen); down(&dir_f->sem); while ((*prev) && (*prev)->nhash <= nhash) { if ((*prev)->nhash == nhash && !memcmp((*prev)->name, name, namelen) && !(*prev)->name[namelen]) { struct jffs2_full_dirent *this = *prev; D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n", this->ino, ref_offset(this->raw))); *prev = this->next; jffs2_mark_node_obsolete(c, (this->raw)); jffs2_free_full_dirent(this); break; } prev = &((*prev)->next); } up(&dir_f->sem); } /* dead_f is NULL if this was a rename not a real unlink */ /* Also catch the !f->inocache case, where there was a dirent pointing to an inode which didn't exist. */ if (dead_f && dead_f->inocache) { down(&dead_f->sem); if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { while (dead_f->dents) { /* There can be only deleted ones */ fd = dead_f->dents; dead_f->dents = fd->next; if (fd->ino) { printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", dead_f->inocache->ino, fd->name, fd->ino); } else { D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); } jffs2_mark_node_obsolete(c, fd->raw); jffs2_free_full_dirent(fd); } } dead_f->inocache->nlink--; /* NB: Caller must set inode nlink if appropriate */ up(&dead_f->sem); } jffs2_complete_reservation(c); return 0; } int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time) { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; uint32_t alloclen, phys_ofs; int ret; rd = jffs2_alloc_raw_dirent(); if (!rd) return -ENOMEM; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); return ret; } down(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_f->inocache->ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(ino); rd->mctime = cpu_to_je32(time); rd->nsize = namelen; rd->type = type; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { jffs2_complete_reservation(c); up(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); up(&dir_f->sem); return 0; } linux-2.6.17/fs/jffs2/compr_rubin.h0000644000000000000000000000101010564537530015556 0ustar rootroot/* Rubin encoder/decoder header */ /* work started at : aug 3, 1994 */ /* last modification : aug 15, 1994 */ /* $Id: compr_rubin.h,v 1.7 2005/11/07 11:14:38 gleixner Exp $ */ #include "pushpull.h" #define RUBIN_REG_SIZE 16 #define UPPER_BIT_RUBIN (((long) 1)<<(RUBIN_REG_SIZE-1)) #define LOWER_BITS_RUBIN ((((long) 1)<<(RUBIN_REG_SIZE-1))-1) struct rubin_state { unsigned long p; unsigned long q; unsigned long rec_q; long bit_number; struct pushpull pp; int bit_divider; int bits[8]; }; linux-2.6.17/fs/jffs2/readinode.c0000644000000000000000000007127710564537530015212 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: readinode.c,v 1.143 2005/11/07 11:14:41 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" /* * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in * order of increasing version. */ static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list) { struct rb_node **p = &list->rb_node; struct rb_node * parent = NULL; struct jffs2_tmp_dnode_info *this; while (*p) { parent = *p; this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); /* There may actually be a collision here, but it doesn't actually matter. As long as the two nodes with the same version are together, it's all fine. */ if (tn->version > this->version) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&tn->rb, parent, p); rb_insert_color(&tn->rb, list); } static void jffs2_free_tmp_dnode_info_list(struct rb_root *list) { struct rb_node *this; struct jffs2_tmp_dnode_info *tn; this = list->rb_node; /* Now at bottom of tree */ while (this) { if (this->rb_left) this = this->rb_left; else if (this->rb_right) this = this->rb_right; else { tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb); jffs2_free_full_dnode(tn->fn); jffs2_free_tmp_dnode_info(tn); this = this->rb_parent; if (!this) break; if (this->rb_left == &tn->rb) this->rb_left = NULL; else if (this->rb_right == &tn->rb) this->rb_right = NULL; else BUG(); } } list->rb_node = NULL; } static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) { struct jffs2_full_dirent *next; while (fd) { next = fd->next; jffs2_free_full_dirent(fd); fd = next; } } /* Returns first valid node after 'ref'. May return 'ref' */ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref) { while (ref && ref->next_in_ino) { if (!ref_obsolete(ref)) return ref; dbg_noderef("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)); ref = ref->next_in_ino; } return NULL; } /* * Helper function for jffs2_get_inode_nodes(). * It is called every time an directory entry node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_raw_dirent *rd, size_t read, struct jffs2_full_dirent **fdp, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_full_dirent *fd; /* The direntry nodes are checked during the flash scanning */ BUG_ON(ref_flags(ref) == REF_UNCHECKED); /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); /* Sanity check */ if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); return 1; } fd = jffs2_alloc_full_dirent(rd->nsize + 1); if (unlikely(!fd)) return -ENOMEM; fd->raw = ref; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->type = rd->type; /* Pick out the mctime of the latest dirent */ if(fd->version > *mctime_ver && je32_to_cpu(rd->mctime)) { *mctime_ver = fd->version; *latest_mctime = je32_to_cpu(rd->mctime); } /* * Copy as much of the name as possible from the raw * dirent we've already read from the flash. */ if (read > sizeof(*rd)) memcpy(&fd->name[0], &rd->name[0], min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) )); /* Do we need to copy any more of the name directly from the flash? */ if (rd->nsize + sizeof(*rd) > read) { /* FIXME: point() */ int err; int already = read - sizeof(*rd); err = jffs2_flash_read(c, (ref_offset(ref)) + read, rd->nsize - already, &read, &fd->name[already]); if (unlikely(read != rd->nsize - already) && likely(!err)) return -EIO; if (unlikely(err)) { JFFS2_ERROR("read remainder of name: error %d\n", err); jffs2_free_full_dirent(fd); return -EIO; } } fd->nhash = full_name_hash(fd->name, rd->nsize); fd->next = NULL; fd->name[rd->nsize] = '\0'; /* * Wheee. We now have a complete jffs2_full_dirent structure, with * the name in it and everything. Link it into the list */ jffs2_add_fd_to_list(c, fd, fdp); return 0; } /* * Helper function for jffs2_get_inode_nodes(). * It is called every time an inode node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_raw_inode *rd, struct rb_root *tnp, int rdlen, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_tmp_dnode_info *tn; uint32_t len, csize; int ret = 1; /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); tn = jffs2_alloc_tmp_dnode_info(); if (!tn) { JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn)); return -ENOMEM; } tn->partial_crc = 0; csize = je32_to_cpu(rd->csize); /* If we've never checked the CRCs on this node, check them now */ if (ref_flags(ref) == REF_UNCHECKED) { uint32_t crc; crc = crc32(0, rd, sizeof(*rd) - 8); if (unlikely(crc != je32_to_cpu(rd->node_crc))) { JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n", ref_offset(ref), je32_to_cpu(rd->node_crc), crc); goto free_out; } /* Sanity checks */ if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) || unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) { JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref)); jffs2_dbg_dump_node(c, ref_offset(ref)); goto free_out; } if (jffs2_is_writebuffered(c) && csize != 0) { /* At this point we are supposed to check the data CRC * of our unchecked node. But thus far, we do not * know whether the node is valid or obsolete. To * figure this out, we need to walk all the nodes of * the inode and build the inode fragtree. We don't * want to spend time checking data of nodes which may * later be found to be obsolete. So we put off the full * data CRC checking until we have read all the inode * nodes and have started building the fragtree. * * The fragtree is being built starting with nodes * having the highest version number, so we'll be able * to detect whether a node is valid (i.e., it is not * overlapped by a node with higher version) or not. * And we'll be able to check only those nodes, which * are not obsolete. * * Of course, this optimization only makes sense in case * of NAND flashes (or other flashes whith * !jffs2_can_mark_obsolete()), since on NOR flashes * nodes are marked obsolete physically. * * Since NAND flashes (or other flashes with * jffs2_is_writebuffered(c)) are anyway read by * fractions of c->wbuf_pagesize, and we have just read * the node header, it is likely that the starting part * of the node data is also read when we read the * header. So we don't mind to check the CRC of the * starting part of the data of the node now, and check * the second part later (in jffs2_check_node_data()). * Of course, we will not need to re-read and re-check * the NAND page which we have just read. This is why we * read the whole NAND page at jffs2_get_inode_nodes(), * while we needed only the node header. */ unsigned char *buf; /* 'buf' will point to the start of data */ buf = (unsigned char *)rd + sizeof(*rd); /* len will be the read data length */ len = min_t(uint32_t, rdlen - sizeof(*rd), csize); tn->partial_crc = crc32(0, buf, len); dbg_readinode("Calculates CRC (%#08x) for %d bytes, csize %d\n", tn->partial_crc, len, csize); /* If we actually calculated the whole data CRC * and it is wrong, drop the node. */ if (len >= csize && unlikely(tn->partial_crc != je32_to_cpu(rd->data_crc))) { JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", ref_offset(ref), tn->partial_crc, je32_to_cpu(rd->data_crc)); goto free_out; } } else if (csize == 0) { /* * We checked the header CRC. If the node has no data, adjust * the space accounting now. For other nodes this will be done * later either when the node is marked obsolete or when its * data is checked. */ struct jffs2_eraseblock *jeb; dbg_readinode("the node has no data.\n"); jeb = &c->blocks[ref->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, ref); spin_lock(&c->erase_completion_lock); jeb->used_size += len; jeb->unchecked_size -= len; c->used_size += len; c->unchecked_size -= len; ref->flash_offset = ref_offset(ref) | REF_NORMAL; spin_unlock(&c->erase_completion_lock); } } tn->fn = jffs2_alloc_full_dnode(); if (!tn->fn) { JFFS2_ERROR("alloc fn failed\n"); ret = -ENOMEM; goto free_out; } tn->version = je32_to_cpu(rd->version); tn->fn->ofs = je32_to_cpu(rd->offset); tn->data_crc = je32_to_cpu(rd->data_crc); tn->csize = csize; tn->fn->raw = ref; /* There was a bug where we wrote hole nodes out with csize/dsize swapped. Deal with it */ if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && csize) tn->fn->size = csize; else // normal case... tn->fn->size = je32_to_cpu(rd->dsize); dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); jffs2_add_tn_to_tree(tn, tnp); return 0; free_out: jffs2_free_tmp_dnode_info(tn); return ret; } /* * Helper function for jffs2_get_inode_nodes(). * It is called every time an unknown node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un) { /* We don't mark unknown nodes as REF_UNCHECKED */ BUG_ON(ref_flags(ref) == REF_UNCHECKED); un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) { /* Hmmm. This should have been caught at scan time. */ JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref)); jffs2_dbg_dump_node(c, ref_offset(ref)); return 1; } else { switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_INCOMPAT: JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); /* EEP */ BUG(); break; case JFFS2_FEATURE_ROCOMPAT: JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); break; case JFFS2_FEATURE_RWCOMPAT_DELETE: JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); return 1; } } return 0; } /* * Helper function for jffs2_get_inode_nodes(). * The function detects whether more data should be read and reads it if yes. * * Returns: 0 on succes; * negative error code on failure. */ static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, int right_size, int *rdlen, unsigned char *buf, unsigned char *bufstart) { int right_len, err, len; size_t retlen; uint32_t offs; if (jffs2_is_writebuffered(c)) { right_len = c->wbuf_pagesize - (bufstart - buf); if (right_size + (int)(bufstart - buf) > c->wbuf_pagesize) right_len += c->wbuf_pagesize; } else right_len = right_size; if (*rdlen == right_len) return 0; /* We need to read more data */ offs = ref_offset(ref) + *rdlen; if (jffs2_is_writebuffered(c)) { bufstart = buf + c->wbuf_pagesize; len = c->wbuf_pagesize; } else { bufstart = buf + *rdlen; len = right_size - *rdlen; } dbg_readinode("read more %d bytes\n", len); err = jffs2_flash_read(c, offs, len, &retlen, bufstart); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, offs, err); return err; } if (retlen < len) { JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", offs, retlen, len); return -EIO; } *rdlen = right_len; return 0; } /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated with this ino, returning the former in order of version */ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct rb_root *tnp, struct jffs2_full_dirent **fdp, uint32_t *highest_version, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_raw_node_ref *ref, *valid_ref; struct rb_root ret_tn = RB_ROOT; struct jffs2_full_dirent *ret_fd = NULL; unsigned char *buf = NULL; union jffs2_node_union *node; size_t retlen; int len, err; *mctime_ver = 0; dbg_readinode("ino #%u\n", f->inocache->ino); if (jffs2_is_writebuffered(c)) { /* * If we have the write buffer, we assume the minimal I/O unit * is c->wbuf_pagesize. We implement some optimizations which in * this case and we need a temporary buffer of size = * 2*c->wbuf_pagesize bytes (see comments in read_dnode()). * Basically, we want to read not only the node header, but the * whole wbuf (NAND page in case of NAND) or 2, if the node * header overlaps the border between the 2 wbufs. */ len = 2*c->wbuf_pagesize; } else { /* * When there is no write buffer, the size of the temporary * buffer is the size of the larges node header. */ len = sizeof(union jffs2_node_union); } /* FIXME: in case of NOR and available ->point() this * needs to be fixed. */ buf = kmalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; spin_lock(&c->erase_completion_lock); valid_ref = jffs2_first_valid_node(f->inocache->nodes); if (!valid_ref && f->inocache->ino != 1) JFFS2_WARNING("Eep. No valid nodes for ino #%u.\n", f->inocache->ino); while (valid_ref) { unsigned char *bufstart; /* We can hold a pointer to a non-obsolete node without the spinlock, but _obsolete_ nodes may disappear at any time, if the block they're in gets erased. So if we mark 'ref' obsolete while we're not holding the lock, it can go away immediately. For that reason, we find the next valid node first, before processing 'ref'. */ ref = valid_ref; valid_ref = jffs2_first_valid_node(ref->next_in_ino); spin_unlock(&c->erase_completion_lock); cond_resched(); /* * At this point we don't know the type of the node we're going * to read, so we do not know the size of its header. In order * to minimize the amount of flash IO we assume the node has * size = JFFS2_MIN_NODE_HEADER. */ if (jffs2_is_writebuffered(c)) { /* * We treat 'buf' as 2 adjacent wbufs. We want to * adjust bufstart such as it points to the * beginning of the node within this wbuf. */ bufstart = buf + (ref_offset(ref) % c->wbuf_pagesize); /* We will read either one wbuf or 2 wbufs. */ len = c->wbuf_pagesize - (bufstart - buf); if (JFFS2_MIN_NODE_HEADER + (int)(bufstart - buf) > c->wbuf_pagesize) { /* The header spans the border of the first wbuf */ len += c->wbuf_pagesize; } } else { bufstart = buf; len = JFFS2_MIN_NODE_HEADER; } dbg_readinode("read %d bytes at %#08x(%d).\n", len, ref_offset(ref), ref_flags(ref)); /* FIXME: point() */ err = jffs2_flash_read(c, ref_offset(ref), len, &retlen, bufstart); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, ref_offset(ref), err); goto free_out; } if (retlen < len) { JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ref_offset(ref), retlen, len); err = -EIO; goto free_out; } node = (union jffs2_node_union *)bufstart; switch (je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_DIRENT: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent)) { err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf, bufstart); if (unlikely(err)) goto free_out; } err = read_direntry(c, ref, &node->d, retlen, &ret_fd, latest_mctime, mctime_ver); if (err == 1) { jffs2_mark_node_obsolete(c, ref); break; } else if (unlikely(err)) goto free_out; if (je32_to_cpu(node->d.version) > *highest_version) *highest_version = je32_to_cpu(node->d.version); break; case JFFS2_NODETYPE_INODE: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode)) { err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf, bufstart); if (unlikely(err)) goto free_out; } err = read_dnode(c, ref, &node->i, &ret_tn, len, latest_mctime, mctime_ver); if (err == 1) { jffs2_mark_node_obsolete(c, ref); break; } else if (unlikely(err)) goto free_out; if (je32_to_cpu(node->i.version) > *highest_version) *highest_version = je32_to_cpu(node->i.version); break; default: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node)) { err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf, bufstart); if (unlikely(err)) goto free_out; } err = read_unknown(c, ref, &node->u); if (err == 1) { jffs2_mark_node_obsolete(c, ref); break; } else if (unlikely(err)) goto free_out; } spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); *tnp = ret_tn; *fdp = ret_fd; kfree(buf); dbg_readinode("nodes of inode #%u were read, the highest version is %u, latest_mctime %u, mctime_ver %u.\n", f->inocache->ino, *highest_version, *latest_mctime, *mctime_ver); return 0; free_out: jffs2_free_tmp_dnode_info_list(&ret_tn); jffs2_free_full_dirent_list(ret_fd); kfree(buf); return err; } static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *latest_node) { struct jffs2_tmp_dnode_info *tn; struct rb_root tn_list; struct rb_node *rb, *repl_rb; struct jffs2_full_dirent *fd_list = NULL; struct jffs2_full_dnode *fn, *first_fn = NULL; uint32_t crc; uint32_t latest_mctime = 0, mctime_ver; size_t retlen; int ret; dbg_readinode("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink); /* Grab all nodes relevant to this ino */ ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { JFFS2_ERROR("cannot read nodes for ino %u, returned error is %d\n", f->inocache->ino, ret); if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return ret; } f->dents = fd_list; rb = rb_first(&tn_list); while (rb) { cond_resched(); tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb); fn = tn->fn; ret = 1; dbg_readinode("consider node ver %u, phys offset " "%#08x(%d), range %u-%u.\n", tn->version, ref_offset(fn->raw), ref_flags(fn->raw), fn->ofs, fn->ofs + fn->size); if (fn->size) { ret = jffs2_add_older_frag_to_fragtree(c, f, tn); /* TODO: the error code isn't checked, check it */ jffs2_dbg_fragtree_paranoia_check_nolock(f); BUG_ON(ret < 0); if (!first_fn && ret == 0) first_fn = fn; } else if (!first_fn) { first_fn = fn; f->metadata = fn; ret = 0; /* Prevent freeing the metadata update node */ } else jffs2_mark_node_obsolete(c, fn->raw); BUG_ON(rb->rb_left); if (rb->rb_parent && rb->rb_parent->rb_left == rb) { /* We were then left-hand child of our parent. We need * to move our own right-hand child into our place. */ repl_rb = rb->rb_right; if (repl_rb) repl_rb->rb_parent = rb->rb_parent; } else repl_rb = NULL; rb = rb_next(rb); /* Remove the spent tn from the tree; don't bother rebalancing * but put our right-hand child in our own place. */ if (tn->rb.rb_parent) { if (tn->rb.rb_parent->rb_left == &tn->rb) tn->rb.rb_parent->rb_left = repl_rb; else if (tn->rb.rb_parent->rb_right == &tn->rb) tn->rb.rb_parent->rb_right = repl_rb; else BUG(); } else if (tn->rb.rb_right) tn->rb.rb_right->rb_parent = NULL; jffs2_free_tmp_dnode_info(tn); if (ret) { dbg_readinode("delete dnode %u-%u.\n", fn->ofs, fn->ofs + fn->size); jffs2_free_full_dnode(fn); } } jffs2_dbg_fragtree_paranoia_check_nolock(f); BUG_ON(first_fn && ref_obsolete(first_fn->raw)); fn = first_fn; if (unlikely(!first_fn)) { /* No data nodes for this inode. */ if (f->inocache->ino != 1) { JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino); if (!fd_list) { if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return -EIO; } JFFS2_NOTICE("but it has children so we fake some modes for it\n"); } latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); latest_node->version = cpu_to_je32(0); latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); latest_node->isize = cpu_to_je32(0); latest_node->gid = cpu_to_je16(0); latest_node->uid = cpu_to_je16(0); if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); if (ret || retlen != sizeof(*latest_node)) { JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ up(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } crc = crc32(0, latest_node, sizeof(*latest_node)-8); if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { case S_IFDIR: if (mctime_ver > je32_to_cpu(latest_node->version)) { /* The times in the latest_node are actually older than mctime in the latest dirent. Cheat. */ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); } break; case S_IFREG: /* If it was a regular file, truncate it to the latest node's isize */ jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize)); break; case S_IFLNK: /* Hack to work around broken isize in old symlink code. Remove this when dwmw2 comes to his senses and stops symlinks from being an entirely gratuitous special case. */ if (!je32_to_cpu(latest_node->isize)) latest_node->isize = latest_node->dsize; if (f->inocache->state != INO_STATE_CHECKING) { /* Symlink's inode data is the target path. Read it and * keep in RAM to facilitate quick follow symlink * operation. */ f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); up(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); if (ret || retlen != je32_to_cpu(latest_node->csize)) { if (retlen != je32_to_cpu(latest_node->csize)) ret = -EIO; kfree(f->target); f->target = NULL; up(&f->sem); jffs2_do_clear_inode(c, f); return -ret; } f->target[je32_to_cpu(latest_node->csize)] = '\0'; dbg_readinode("symlink's target '%s' cached\n", f->target); } /* fall through... */ case S_IFBLK: case S_IFCHR: /* Certain inode types should have only one data node, and it's kept as the metadata node */ if (f->metadata) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } if (!frag_first(&f->fragtree)) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* ASSERT: f->fraglist != NULL */ if (frag_next(frag_first(&f->fragtree))) { JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* OK. We're happy */ f->metadata = frag_first(&f->fragtree)->node; jffs2_free_node_frag(frag_first(&f->fragtree)); f->fragtree = RB_ROOT; break; } if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } /* Scan the list of all nodes present for this ino, build map of versions, etc. */ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t ino, struct jffs2_raw_inode *latest_node) { dbg_readinode("read inode #%u\n", ino); retry_inocache: spin_lock(&c->inocache_lock); f->inocache = jffs2_get_ino_cache(c, ino); if (f->inocache) { /* Check its state. We may need to wait before we can use it */ switch(f->inocache->state) { case INO_STATE_UNCHECKED: case INO_STATE_CHECKEDABSENT: f->inocache->state = INO_STATE_READING; break; case INO_STATE_CHECKING: case INO_STATE_GC: /* If it's in either of these states, we need to wait for whoever's got it to finish and put it back. */ dbg_readinode("waiting for ino #%u in state %d\n", ino, f->inocache->state); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); goto retry_inocache; case INO_STATE_READING: case INO_STATE_PRESENT: /* Eep. This should never happen. It can happen if Linux calls read_inode() again before clear_inode() has finished though. */ JFFS2_ERROR("Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state); /* Fail. That's probably better than allowing it to succeed */ f->inocache = NULL; break; default: BUG(); } } spin_unlock(&c->inocache_lock); if (!f->inocache && ino == 1) { /* Special case - no root inode on medium */ f->inocache = jffs2_alloc_inode_cache(); if (!f->inocache) { JFFS2_ERROR("cannot allocate inocache for root inode\n"); return -ENOMEM; } dbg_readinode("creating inocache for root inode\n"); memset(f->inocache, 0, sizeof(struct jffs2_inode_cache)); f->inocache->ino = f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_READING; jffs2_add_ino_cache(c, f->inocache); } if (!f->inocache) { JFFS2_ERROR("requestied to read an nonexistent ino %u\n", ino); return -ENOENT; } return jffs2_do_read_inode_internal(c, f, latest_node); } int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { struct jffs2_raw_inode n; struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); int ret; if (!f) return -ENOMEM; memset(f, 0, sizeof(*f)); init_MUTEX_LOCKED(&f->sem); f->inocache = ic; ret = jffs2_do_read_inode_internal(c, f, &n); if (!ret) { up(&f->sem); jffs2_do_clear_inode(c, f); } kfree (f); return ret; } void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { struct jffs2_full_dirent *fd, *fds; int deleted; down(&f->sem); deleted = f->inocache && !f->inocache->nlink; if (f->inocache && f->inocache->state != INO_STATE_CHECKING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING); if (f->metadata) { if (deleted) jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); } jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); if (f->target) { kfree(f->target); f->target = NULL; } fds = f->dents; while(fds) { fd = fds; fds = fd->next; jffs2_free_full_dirent(fd); } if (f->inocache && f->inocache->state != INO_STATE_CHECKING) { jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); if (f->inocache->nodes == (void *)f->inocache) jffs2_del_ino_cache(c, f->inocache); } up(&f->sem); } linux-2.6.17/fs/jffs2/debug.c0000644000000000000000000005301010564537530014327 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: debug.c,v 1.12 2005/11/07 11:14:39 gleixner Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" #include "debug.h" #ifdef JFFS2_DBG_SANITY_CHECKS void __jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { if (unlikely(jeb && jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size)) { JFFS2_ERROR("eeep, space accounting for block at 0x%08x is screwed.\n", jeb->offset); JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + wasted %#08x + unchecked %#08x != total %#08x.\n", jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); BUG(); } if (unlikely(c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size)) { JFFS2_ERROR("eeep, space accounting superblock info is screwed.\n"); JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + erasing %#08x + bad %#08x + wasted %#08x + unchecked %#08x != total %#08x.\n", c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); BUG(); } } void __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); jffs2_dbg_acct_sanity_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); } #endif /* JFFS2_DBG_SANITY_CHECKS */ #ifdef JFFS2_DBG_PARANOIA_CHECKS /* * Check the fragtree. */ void __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f) { down(&f->sem); __jffs2_dbg_fragtree_paranoia_check_nolock(f); up(&f->sem); } void __jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f) { struct jffs2_node_frag *frag; int bitched = 0; for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { struct jffs2_full_dnode *fn = frag->node; if (!fn || !fn->raw) continue; if (ref_flags(fn->raw) == REF_PRISTINE) { if (fn->frags > 1) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2.\n", ref_offset(fn->raw), fn->frags); bitched = 1; } /* A hole node which isn't multi-page should be garbage-collected and merged anyway, so we just check for the frag size here, rather than mucking around with actually reading the node and checking the compression type, which is the real way to tell a hole node. */ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2.\n", ref_offset(fn->raw)); bitched = 1; } if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2.\n", ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); bitched = 1; } } } if (bitched) { JFFS2_ERROR("fragtree is corrupted.\n"); __jffs2_dbg_dump_fragtree_nolock(f); BUG(); } } /* * Check if the flash contains all 0xFF before we start writing. */ void __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, uint32_t ofs, int len) { size_t retlen; int ret, i; unsigned char *buf; buf = kmalloc(len, GFP_KERNEL); if (!buf) return; ret = jffs2_flash_read(c, ofs, len, &retlen, buf); if (ret || (retlen != len)) { JFFS2_WARNING("read %d bytes failed or short. ret %d, retlen %zd.\n", len, ret, retlen); kfree(buf); return; } ret = 0; for (i = 0; i < len; i++) if (buf[i] != 0xff) ret = 1; if (ret) { JFFS2_ERROR("argh, about to write node to %#08x on flash, but there are data already there. The first corrupted byte is at %#08x offset.\n", ofs, ofs + i); __jffs2_dbg_dump_buffer(buf, len, ofs); kfree(buf); BUG(); } kfree(buf); } /* * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'. */ void __jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_acct_paranoia_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { uint32_t my_used_size = 0; uint32_t my_unchecked_size = 0; uint32_t my_dirty_size = 0; struct jffs2_raw_node_ref *ref2 = jeb->first_node; while (ref2) { uint32_t totlen = ref_totlen(c, jeb, ref2); if (ref2->flash_offset < jeb->offset || ref2->flash_offset > jeb->offset + c->sector_size) { JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n", ref_offset(ref2), jeb->offset); goto error; } if (ref_flags(ref2) == REF_UNCHECKED) my_unchecked_size += totlen; else if (!ref_obsolete(ref2)) my_used_size += totlen; else my_dirty_size += totlen; if ((!ref2->next_phys) != (ref2 == jeb->last_node)) { JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next_phys at %#08x (mem %p), last_node is at %#08x (mem %p).\n", ref_offset(ref2), ref2, ref_offset(ref2->next_phys), ref2->next_phys, ref_offset(jeb->last_node), jeb->last_node); goto error; } ref2 = ref2->next_phys; } if (my_used_size != jeb->used_size) { JFFS2_ERROR("Calculated used size %#08x != stored used size %#08x.\n", my_used_size, jeb->used_size); goto error; } if (my_unchecked_size != jeb->unchecked_size) { JFFS2_ERROR("Calculated unchecked size %#08x != stored unchecked size %#08x.\n", my_unchecked_size, jeb->unchecked_size); goto error; } #if 0 /* This should work when we implement ref->__totlen elemination */ if (my_dirty_size != jeb->dirty_size + jeb->wasted_size) { JFFS2_ERROR("Calculated dirty+wasted size %#08x != stored dirty + wasted size %#08x\n", my_dirty_size, jeb->dirty_size + jeb->wasted_size); goto error; } if (jeb->free_size == 0 && my_used_size + my_unchecked_size + my_dirty_size != c->sector_size) { JFFS2_ERROR("The sum of all nodes in block (%#x) != size of block (%#x)\n", my_used_size + my_unchecked_size + my_dirty_size, c->sector_size); goto error; } #endif return; error: __jffs2_dbg_dump_node_refs_nolock(c, jeb); __jffs2_dbg_dump_jeb_nolock(jeb); __jffs2_dbg_dump_block_lists_nolock(c); BUG(); } #endif /* JFFS2_DBG_PARANOIA_CHECKS */ #if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS) /* * Dump the node_refs of the 'jeb' JFFS2 eraseblock. */ void __jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_dump_node_refs_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *ref; int i = 0; printk(JFFS2_DBG_MSG_PREFIX " Dump node_refs of the eraseblock %#08x\n", jeb->offset); if (!jeb->first_node) { printk(JFFS2_DBG_MSG_PREFIX " no nodes in the eraseblock %#08x\n", jeb->offset); return; } printk(JFFS2_DBG); for (ref = jeb->first_node; ; ref = ref->next_phys) { printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); if (ref->next_phys) printk("->"); else break; if (++i == 4) { i = 0; printk("\n" JFFS2_DBG); } } printk("\n"); } /* * Dump an eraseblock's space accounting. */ void __jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_dump_jeb_nolock(jeb); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb) { if (!jeb) return; printk(JFFS2_DBG_MSG_PREFIX " dump space accounting for the eraseblock at %#08x:\n", jeb->offset); printk(JFFS2_DBG "used_size: %#08x\n", jeb->used_size); printk(JFFS2_DBG "dirty_size: %#08x\n", jeb->dirty_size); printk(JFFS2_DBG "wasted_size: %#08x\n", jeb->wasted_size); printk(JFFS2_DBG "unchecked_size: %#08x\n", jeb->unchecked_size); printk(JFFS2_DBG "free_size: %#08x\n", jeb->free_size); } void __jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) { printk(JFFS2_DBG_MSG_PREFIX " dump JFFS2 blocks lists:\n"); printk(JFFS2_DBG "flash_size: %#08x\n", c->flash_size); printk(JFFS2_DBG "used_size: %#08x\n", c->used_size); printk(JFFS2_DBG "dirty_size: %#08x\n", c->dirty_size); printk(JFFS2_DBG "wasted_size: %#08x\n", c->wasted_size); printk(JFFS2_DBG "unchecked_size: %#08x\n", c->unchecked_size); printk(JFFS2_DBG "free_size: %#08x\n", c->free_size); printk(JFFS2_DBG "erasing_size: %#08x\n", c->erasing_size); printk(JFFS2_DBG "bad_size: %#08x\n", c->bad_size); printk(JFFS2_DBG "sector_size: %#08x\n", c->sector_size); printk(JFFS2_DBG "jffs2_reserved_blocks size: %#08x\n", c->sector_size * c->resv_blocks_write); if (c->nextblock) printk(JFFS2_DBG "nextblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size); else printk(JFFS2_DBG "nextblock: NULL\n"); if (c->gcblock) printk(JFFS2_DBG "gcblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); else printk(JFFS2_DBG "gcblock: NULL\n"); if (list_empty(&c->clean_list)) { printk(JFFS2_DBG "clean_list: empty\n"); } else { struct list_head *this; int numblocks = 0; uint32_t dirty = 0; list_for_each(this, &c->clean_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); numblocks ++; dirty += jeb->wasted_size; if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "clean_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } printk (JFFS2_DBG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks); } if (list_empty(&c->very_dirty_list)) { printk(JFFS2_DBG "very_dirty_list: empty\n"); } else { struct list_head *this; int numblocks = 0; uint32_t dirty = 0; list_for_each(this, &c->very_dirty_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); numblocks ++; dirty += jeb->dirty_size; if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "very_dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } printk (JFFS2_DBG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", numblocks, dirty, dirty / numblocks); } if (list_empty(&c->dirty_list)) { printk(JFFS2_DBG "dirty_list: empty\n"); } else { struct list_head *this; int numblocks = 0; uint32_t dirty = 0; list_for_each(this, &c->dirty_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); numblocks ++; dirty += jeb->dirty_size; if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } printk (JFFS2_DBG "contains %d blocks with total dirty size %u, average dirty size: %u\n", numblocks, dirty, dirty / numblocks); } if (list_empty(&c->erasable_list)) { printk(JFFS2_DBG "erasable_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erasable_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erasable_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->erasing_list)) { printk(JFFS2_DBG "erasing_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erasing_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erasing_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->erase_pending_list)) { printk(JFFS2_DBG "erase_pending_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erase_pending_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erase_pending_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->erasable_pending_wbuf_list)) { printk(JFFS2_DBG "erasable_pending_wbuf_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erasable_pending_wbuf_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erasable_pending_wbuf_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->free_list)) { printk(JFFS2_DBG "free_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->free_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "free_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->bad_list)) { printk(JFFS2_DBG "bad_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->bad_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "bad_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->bad_used_list)) { printk(JFFS2_DBG "bad_used_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->bad_used_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "bad_used_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } } void __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f) { down(&f->sem); jffs2_dbg_dump_fragtree_nolock(f); up(&f->sem); } void __jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f) { struct jffs2_node_frag *this = frag_first(&f->fragtree); uint32_t lastofs = 0; int buggy = 0; printk(JFFS2_DBG_MSG_PREFIX " dump fragtree of ino #%u\n", f->inocache->ino); while(this) { if (this->node) printk(JFFS2_DBG "frag %#04x-%#04x: %#08x(%d) on flash (*%p), left (%p), right (%p), parent (%p)\n", this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw), this, frag_left(this), frag_right(this), frag_parent(this)); else printk(JFFS2_DBG "frag %#04x-%#04x: hole (*%p). left (%p), right (%p), parent (%p)\n", this->ofs, this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this)); if (this->ofs != lastofs) buggy = 1; lastofs = this->ofs + this->size; this = frag_next(this); } if (f->metadata) printk(JFFS2_DBG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); if (buggy) { JFFS2_ERROR("frag tree got a hole in it.\n"); BUG(); } } #define JFFS2_BUFDUMP_BYTES_PER_LINE 32 void __jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs) { int skip; int i; printk(JFFS2_DBG_MSG_PREFIX " dump from offset %#08x to offset %#08x (%x bytes).\n", offs, offs + len, len); i = skip = offs % JFFS2_BUFDUMP_BYTES_PER_LINE; offs = offs & ~(JFFS2_BUFDUMP_BYTES_PER_LINE - 1); if (skip != 0) printk(JFFS2_DBG "%#08x: ", offs); while (skip--) printk(" "); while (i < len) { if ((i % JFFS2_BUFDUMP_BYTES_PER_LINE) == 0 && i != len -1) { if (i != 0) printk("\n"); offs += JFFS2_BUFDUMP_BYTES_PER_LINE; printk(JFFS2_DBG "%0#8x: ", offs); } printk("%02x ", buf[i]); i += 1; } printk("\n"); } /* * Dump a JFFS2 node. */ void __jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs) { union jffs2_node_union node; int len = sizeof(union jffs2_node_union); size_t retlen; uint32_t crc; int ret; printk(JFFS2_DBG_MSG_PREFIX " dump node at offset %#08x.\n", ofs); ret = jffs2_flash_read(c, ofs, len, &retlen, (unsigned char *)&node); if (ret || (retlen != len)) { JFFS2_ERROR("read %d bytes failed or short. ret %d, retlen %zd.\n", len, ret, retlen); return; } printk(JFFS2_DBG "magic:\t%#04x\n", je16_to_cpu(node.u.magic)); printk(JFFS2_DBG "nodetype:\t%#04x\n", je16_to_cpu(node.u.nodetype)); printk(JFFS2_DBG "totlen:\t%#08x\n", je32_to_cpu(node.u.totlen)); printk(JFFS2_DBG "hdr_crc:\t%#08x\n", je32_to_cpu(node.u.hdr_crc)); crc = crc32(0, &node.u, sizeof(node.u) - 4); if (crc != je32_to_cpu(node.u.hdr_crc)) { JFFS2_ERROR("wrong common header CRC.\n"); return; } if (je16_to_cpu(node.u.magic) != JFFS2_MAGIC_BITMASK && je16_to_cpu(node.u.magic) != JFFS2_OLD_MAGIC_BITMASK) { JFFS2_ERROR("wrong node magic: %#04x instead of %#04x.\n", je16_to_cpu(node.u.magic), JFFS2_MAGIC_BITMASK); return; } switch(je16_to_cpu(node.u.nodetype)) { case JFFS2_NODETYPE_INODE: printk(JFFS2_DBG "the node is inode node\n"); printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.i.ino)); printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.i.version)); printk(JFFS2_DBG "mode:\t%#08x\n", node.i.mode.m); printk(JFFS2_DBG "uid:\t%#04x\n", je16_to_cpu(node.i.uid)); printk(JFFS2_DBG "gid:\t%#04x\n", je16_to_cpu(node.i.gid)); printk(JFFS2_DBG "isize:\t%#08x\n", je32_to_cpu(node.i.isize)); printk(JFFS2_DBG "atime:\t%#08x\n", je32_to_cpu(node.i.atime)); printk(JFFS2_DBG "mtime:\t%#08x\n", je32_to_cpu(node.i.mtime)); printk(JFFS2_DBG "ctime:\t%#08x\n", je32_to_cpu(node.i.ctime)); printk(JFFS2_DBG "offset:\t%#08x\n", je32_to_cpu(node.i.offset)); printk(JFFS2_DBG "csize:\t%#08x\n", je32_to_cpu(node.i.csize)); printk(JFFS2_DBG "dsize:\t%#08x\n", je32_to_cpu(node.i.dsize)); printk(JFFS2_DBG "compr:\t%#02x\n", node.i.compr); printk(JFFS2_DBG "usercompr:\t%#02x\n", node.i.usercompr); printk(JFFS2_DBG "flags:\t%#04x\n", je16_to_cpu(node.i.flags)); printk(JFFS2_DBG "data_crc:\t%#08x\n", je32_to_cpu(node.i.data_crc)); printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.i.node_crc)); crc = crc32(0, &node.i, sizeof(node.i) - 8); if (crc != je32_to_cpu(node.i.node_crc)) { JFFS2_ERROR("wrong node header CRC.\n"); return; } break; case JFFS2_NODETYPE_DIRENT: printk(JFFS2_DBG "the node is dirent node\n"); printk(JFFS2_DBG "pino:\t%#08x\n", je32_to_cpu(node.d.pino)); printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.d.version)); printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.d.ino)); printk(JFFS2_DBG "mctime:\t%#08x\n", je32_to_cpu(node.d.mctime)); printk(JFFS2_DBG "nsize:\t%#02x\n", node.d.nsize); printk(JFFS2_DBG "type:\t%#02x\n", node.d.type); printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.d.node_crc)); printk(JFFS2_DBG "name_crc:\t%#08x\n", je32_to_cpu(node.d.name_crc)); node.d.name[node.d.nsize] = '\0'; printk(JFFS2_DBG "name:\t\"%s\"\n", node.d.name); crc = crc32(0, &node.d, sizeof(node.d) - 8); if (crc != je32_to_cpu(node.d.node_crc)) { JFFS2_ERROR("wrong node header CRC.\n"); return; } break; default: printk(JFFS2_DBG "node type is unknown\n"); break; } } #endif /* JFFS2_DBG_DUMPS || JFFS2_DBG_PARANOIA_CHECKS */ linux-2.6.17/fs/jffs2/compr.c0000644000000000000000000004146210564537530014371 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * Created by Arjan van de Ven * * Copyright (C) 2004 Ferenc Havasi , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr.c,v 1.46 2005/11/07 11:14:38 gleixner Exp $ * */ #include "compr.h" static DEFINE_SPINLOCK(jffs2_compressor_list_lock); /* Available compressors are on this list */ static LIST_HEAD(jffs2_compressor_list); /* Actual compression mode */ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; /* Statistics for blocks stored without compression */ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; /* jffs2_compress: * @data: Pointer to uncompressed data * @cdata: Pointer to returned pointer to buffer for compressed data * @datalen: On entry, holds the amount of data available for compression. * On exit, expected to hold the amount of data actually compressed. * @cdatalen: On entry, holds the amount of space available for compressed * data. On exit, expected to hold the actual size of the compressed * data. * * Returns: Lower byte to be stored with data indicating compression type used. * Zero is used to show that the data could not be compressed - the * compressed version was actually larger than the original. * Upper byte will be used later. (soon) * * If the cdata buffer isn't large enough to hold all the uncompressed data, * jffs2_compress should compress as much as will fit, and should set * *datalen accordingly to show the amount of data which were compressed. */ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen) { int ret = JFFS2_COMPR_NONE; int compr_ret; struct jffs2_compressor *this, *best=NULL; unsigned char *output_buf = NULL, *tmp_buf; uint32_t orig_slen, orig_dlen; uint32_t best_slen=0, best_dlen=0; switch (jffs2_compression_mode) { case JFFS2_COMPR_MODE_NONE: break; case JFFS2_COMPR_MODE_PRIORITY: output_buf = kmalloc(*cdatalen,GFP_KERNEL); if (!output_buf) { printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); goto out; } orig_slen = *datalen; orig_dlen = *cdatalen; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { /* Skip decompress-only backwards-compatibility and disabled modules */ if ((!this->compress)||(this->disabled)) continue; this->usecount++; spin_unlock(&jffs2_compressor_list_lock); *datalen = orig_slen; *cdatalen = orig_dlen; compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); spin_lock(&jffs2_compressor_list_lock); this->usecount--; if (!compr_ret) { ret = this->compr; this->stat_compr_blocks++; this->stat_compr_orig_size += *datalen; this->stat_compr_new_size += *cdatalen; break; } } spin_unlock(&jffs2_compressor_list_lock); if (ret == JFFS2_COMPR_NONE) kfree(output_buf); break; case JFFS2_COMPR_MODE_SIZE: orig_slen = *datalen; orig_dlen = *cdatalen; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { /* Skip decompress-only backwards-compatibility and disabled modules */ if ((!this->compress)||(this->disabled)) continue; /* Allocating memory for output buffer if necessary */ if ((this->compr_buf_sizecompr_buf)) { spin_unlock(&jffs2_compressor_list_lock); kfree(this->compr_buf); spin_lock(&jffs2_compressor_list_lock); this->compr_buf_size=0; this->compr_buf=NULL; } if (!this->compr_buf) { spin_unlock(&jffs2_compressor_list_lock); tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); spin_lock(&jffs2_compressor_list_lock); if (!tmp_buf) { printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); continue; } else { this->compr_buf = tmp_buf; this->compr_buf_size = orig_dlen; } } this->usecount++; spin_unlock(&jffs2_compressor_list_lock); *datalen = orig_slen; *cdatalen = orig_dlen; compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); spin_lock(&jffs2_compressor_list_lock); this->usecount--; if (!compr_ret) { if ((!best_dlen)||(best_dlen>*cdatalen)) { best_dlen = *cdatalen; best_slen = *datalen; best = this; } } } if (best_dlen) { *cdatalen = best_dlen; *datalen = best_slen; output_buf = best->compr_buf; best->compr_buf = NULL; best->compr_buf_size = 0; best->stat_compr_blocks++; best->stat_compr_orig_size += best_slen; best->stat_compr_new_size += best_dlen; ret = best->compr; } spin_unlock(&jffs2_compressor_list_lock); break; default: printk(KERN_ERR "JFFS2: unknow compression mode.\n"); } out: if (ret == JFFS2_COMPR_NONE) { *cpage_out = data_in; *datalen = *cdatalen; none_stat_compr_blocks++; none_stat_compr_size += *datalen; } else { *cpage_out = output_buf; } return ret; } int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) { struct jffs2_compressor *this; int ret; /* Older code had a bug where it would write non-zero 'usercompr' fields. Deal with it. */ if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) comprtype &= 0xff; switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ memcpy(data_out, cdata_in, datalen); none_stat_decompr_blocks++; break; case JFFS2_COMPR_ZERO: memset(data_out, 0, datalen); break; default: spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (comprtype == this->compr) { this->usecount++; spin_unlock(&jffs2_compressor_list_lock); ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); spin_lock(&jffs2_compressor_list_lock); if (ret) { printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); } else { this->stat_decompr_blocks++; } this->usecount--; spin_unlock(&jffs2_compressor_list_lock); return ret; } } printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); spin_unlock(&jffs2_compressor_list_lock); return -EIO; } return 0; } int jffs2_register_compressor(struct jffs2_compressor *comp) { struct jffs2_compressor *this; if (!comp->name) { printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); return -1; } comp->compr_buf_size=0; comp->compr_buf=NULL; comp->usecount=0; comp->stat_compr_orig_size=0; comp->stat_compr_new_size=0; comp->stat_compr_blocks=0; comp->stat_decompr_blocks=0; D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (this->priority < comp->priority) { list_add(&comp->list, this->list.prev); goto out; } } list_add_tail(&comp->list, &jffs2_compressor_list); out: D2(list_for_each_entry(this, &jffs2_compressor_list, list) { printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); }) spin_unlock(&jffs2_compressor_list_lock); return 0; } int jffs2_unregister_compressor(struct jffs2_compressor *comp) { D2(struct jffs2_compressor *this;) D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); spin_lock(&jffs2_compressor_list_lock); if (comp->usecount) { spin_unlock(&jffs2_compressor_list_lock); printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); return -1; } list_del(&comp->list); D2(list_for_each_entry(this, &jffs2_compressor_list, list) { printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); }) spin_unlock(&jffs2_compressor_list_lock); return 0; } #ifdef CONFIG_JFFS2_PROC #define JFFS2_STAT_BUF_SIZE 16000 char *jffs2_list_compressors(void) { struct jffs2_compressor *this; char *buf, *act_buf; act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); list_for_each_entry(this, &jffs2_compressor_list, list) { act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); if ((this->disabled)||(!this->compress)) act_buf += sprintf(act_buf,"disabled"); else act_buf += sprintf(act_buf,"enabled"); act_buf += sprintf(act_buf,"\n"); } return buf; } char *jffs2_stats(void) { struct jffs2_compressor *this; char *buf, *act_buf; act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n"); act_buf += sprintf(act_buf,"%10s ","none"); act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, none_stat_compr_size, none_stat_decompr_blocks); spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { act_buf += sprintf(act_buf,"%10s ",this->name); if ((this->disabled)||(!this->compress)) act_buf += sprintf(act_buf,"- "); else act_buf += sprintf(act_buf,"+ "); act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, this->stat_compr_new_size, this->stat_compr_orig_size, this->stat_decompr_blocks); act_buf += sprintf(act_buf,"\n"); } spin_unlock(&jffs2_compressor_list_lock); return buf; } char *jffs2_get_compression_mode_name(void) { switch (jffs2_compression_mode) { case JFFS2_COMPR_MODE_NONE: return "none"; case JFFS2_COMPR_MODE_PRIORITY: return "priority"; case JFFS2_COMPR_MODE_SIZE: return "size"; } return "unkown"; } int jffs2_set_compression_mode_name(const char *name) { if (!strcmp("none",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; return 0; } if (!strcmp("priority",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; return 0; } if (!strcmp("size",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; return 0; } return 1; } static int jffs2_compressor_Xable(const char *name, int disabled) { struct jffs2_compressor *this; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (!strcmp(this->name, name)) { this->disabled = disabled; spin_unlock(&jffs2_compressor_list_lock); return 0; } } spin_unlock(&jffs2_compressor_list_lock); printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); return 1; } int jffs2_enable_compressor_name(const char *name) { return jffs2_compressor_Xable(name, 0); } int jffs2_disable_compressor_name(const char *name) { return jffs2_compressor_Xable(name, 1); } int jffs2_set_compressor_priority(const char *name, int priority) { struct jffs2_compressor *this,*comp; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (!strcmp(this->name, name)) { this->priority = priority; comp = this; goto reinsert; } } spin_unlock(&jffs2_compressor_list_lock); printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); return 1; reinsert: /* list is sorted in the order of priority, so if we change it we have to reinsert it into the good place */ list_del(&comp->list); list_for_each_entry(this, &jffs2_compressor_list, list) { if (this->priority < comp->priority) { list_add(&comp->list, this->list.prev); spin_unlock(&jffs2_compressor_list_lock); return 0; } } list_add_tail(&comp->list, &jffs2_compressor_list); spin_unlock(&jffs2_compressor_list_lock); return 0; } #endif void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) { if (orig != comprbuf) kfree(comprbuf); } int jffs2_compressors_init(void) { /* Registering compressors */ #ifdef CONFIG_JFFS2_ZLIB jffs2_zlib_init(); #endif #ifdef CONFIG_JFFS2_RTIME jffs2_rtime_init(); #endif #ifdef CONFIG_JFFS2_RUBIN jffs2_rubinmips_init(); jffs2_dynrubin_init(); #endif /* Setting default compression mode */ #ifdef CONFIG_JFFS2_CMODE_NONE jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) #else #ifdef CONFIG_JFFS2_CMODE_SIZE jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) #else D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) #endif #endif return 0; } int jffs2_compressors_exit(void) { /* Unregistering compressors */ #ifdef CONFIG_JFFS2_RUBIN jffs2_dynrubin_exit(); jffs2_rubinmips_exit(); #endif #ifdef CONFIG_JFFS2_RTIME jffs2_rtime_exit(); #endif #ifdef CONFIG_JFFS2_ZLIB jffs2_zlib_exit(); #endif return 0; } linux-2.6.17/fs/jffs2/TODO0000644000000000000000000000411110564537530013563 0ustar rootroot$Id: TODO,v 1.18 2005/09/22 11:24:56 dedekind Exp $ - support asynchronous operation -- add a per-fs 'reserved_space' count, let each outstanding write reserve the _maximum_ amount of physical space it could take. Let GC flush the outstanding writes because the reservations will necessarily be pessimistic. With this we could even do shared writable mmap, if we can have a fs hook for do_wp_page() to make the reservation. - disable compression in commit_write()? - fine-tune the allocation / GC thresholds - chattr support - turning on/off and tuning compression per-inode - checkpointing (do we need this? scan is quite fast) - make the scan code populate real inodes so read_inode just after mount doesn't have to read the flash twice for large files. Make this a per-inode option, changable with chattr, so you can decide which inodes should be in-core immediately after mount. - test, test, test - NAND flash support: - almost done :) - use bad block check instead of the hardwired byte check - Optimisations: - Split writes so they go to two separate blocks rather than just c->nextblock. By writing _new_ nodes to one block, and garbage-collected REF_PRISTINE nodes to a different one, we can separate clean nodes from those which are likely to become dirty, and end up with blocks which are each far closer to 100% or 0% clean, hence speeding up later GC progress dramatically. - Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in the full dirent, we only need to go to the flash in lookup() when we think we've got a match, and in readdir(). - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately? - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into jffs2_mark_node_obsolete(). Can all callers work it out? - Remove size from jffs2_raw_node_frag. dedekind: 1. __jffs2_flush_wbuf() has a strange 'pad' parameter. Eliminate. 2. get_sb()->build_fs()->scan() path... Why get_sb() removes scan()'s crap in case of failure? scan() does not clean everything. Fix. linux-2.6.17/fs/jffs2/dir.c0000644000000000000000000005301610564537530014025 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: dir.c,v 1.90 2005/11/07 11:14:39 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include #include "nodelist.h" static int jffs2_readdir (struct file *, void *, filldir_t); static int jffs2_create (struct inode *,struct dentry *,int, struct nameidata *); static struct dentry *jffs2_lookup (struct inode *,struct dentry *, struct nameidata *); static int jffs2_link (struct dentry *,struct inode *,struct dentry *); static int jffs2_unlink (struct inode *,struct dentry *); static int jffs2_symlink (struct inode *,struct dentry *,const char *); static int jffs2_mkdir (struct inode *,struct dentry *,int); static int jffs2_rmdir (struct inode *,struct dentry *); static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *); const struct file_operations jffs2_dir_operations = { .read = generic_read_dir, .readdir = jffs2_readdir, .ioctl = jffs2_ioctl, .fsync = jffs2_fsync }; struct inode_operations jffs2_dir_inode_operations = { .create = jffs2_create, .lookup = jffs2_lookup, .link = jffs2_link, .unlink = jffs2_unlink, .symlink = jffs2_symlink, .mkdir = jffs2_mkdir, .rmdir = jffs2_rmdir, .mknod = jffs2_mknod, .rename = jffs2_rename, .setattr = jffs2_setattr, }; /***********************************************************************/ /* We keep the dirent list sorted in increasing order of name hash, and we use the same hash function as the dentries. Makes this nice and simple */ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, struct nameidata *nd) { struct jffs2_inode_info *dir_f; struct jffs2_sb_info *c; struct jffs2_full_dirent *fd = NULL, *fd_list; uint32_t ino = 0; struct inode *inode = NULL; D1(printk(KERN_DEBUG "jffs2_lookup()\n")); dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); down(&dir_f->sem); /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) { if (fd_list->nhash == target->d_name.hash && (!fd || fd_list->version > fd->version) && strlen(fd_list->name) == target->d_name.len && !strncmp(fd_list->name, target->d_name.name, target->d_name.len)) { fd = fd_list; } } if (fd) ino = fd->ino; up(&dir_f->sem); if (ino) { inode = iget(dir_i->i_sb, ino); if (!inode) { printk(KERN_WARNING "iget() failed for ino #%u\n", ino); return (ERR_PTR(-EIO)); } } d_add(target, inode); return NULL; } /***********************************************************************/ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct jffs2_inode_info *f; struct jffs2_sb_info *c; struct inode *inode = filp->f_dentry->d_inode; struct jffs2_full_dirent *fd; unsigned long offset, curofs; D1(printk(KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", filp->f_dentry->d_inode->i_ino)); f = JFFS2_INODE_INFO(inode); c = JFFS2_SB_INFO(inode->i_sb); offset = filp->f_pos; if (offset == 0) { D1(printk(KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino)); if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) goto out; offset++; } if (offset == 1) { unsigned long pino = parent_ino(filp->f_dentry); D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino)); if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0) goto out; offset++; } curofs=1; down(&f->sem); for (fd = f->dents; fd; fd = fd->next) { curofs++; /* First loop: curofs = 2; offset = 2 */ if (curofs < offset) { D2(printk(KERN_DEBUG "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n", fd->name, fd->ino, fd->type, curofs, offset)); continue; } if (!fd->ino) { D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name)); offset++; continue; } D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type)); if (filldir(dirent, fd->name, strlen(fd->name), offset, fd->ino, fd->type) < 0) break; offset++; } up(&f->sem); out: filp->f_pos = offset; return 0; } /***********************************************************************/ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, struct nameidata *nd) { struct jffs2_raw_inode *ri; struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; int ret; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); D1(printk(KERN_DEBUG "jffs2_create()\n")); inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); jffs2_free_raw_inode(ri); return PTR_ERR(inode); } inode->i_op = &jffs2_file_inode_operations; inode->i_fop = &jffs2_file_operations; inode->i_mapping->a_ops = &jffs2_file_address_operations; inode->i_mapping->nrpages = 0; f = JFFS2_INODE_INFO(inode); dir_f = JFFS2_INODE_INFO(dir_i); ret = jffs2_do_create(c, dir_f, f, ri, dentry->d_name.name, dentry->d_name.len); if (ret) { make_bad_inode(inode); iput(inode); jffs2_free_raw_inode(ri); return ret; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); jffs2_free_raw_inode(ri); d_instantiate(dentry, inode); D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); return 0; } /***********************************************************************/ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry) { struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb); struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode); int ret; uint32_t now = get_seconds(); ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, dentry->d_name.len, dead_f, now); if (dead_f->inocache) dentry->d_inode->i_nlink = dead_f->inocache->nlink; if (!ret) dir_i->i_mtime = dir_i->i_ctime = ITIME(now); return ret; } /***********************************************************************/ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) { struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); int ret; uint8_t type; uint32_t now; /* Don't let people make hard links to bad inodes. */ if (!f->inocache) return -EIO; if (S_ISDIR(old_dentry->d_inode->i_mode)) return -EPERM; /* XXX: This is ugly */ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; now = get_seconds(); ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now); if (!ret) { down(&f->sem); old_dentry->d_inode->i_nlink = ++f->inocache->nlink; up(&f->sem); d_instantiate(dentry, old_dentry->d_inode); dir_i->i_mtime = dir_i->i_ctime = ITIME(now); atomic_inc(&old_dentry->d_inode->i_count); } return ret; } /***********************************************************************/ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char *target) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen, phys_ofs; int ret, targetlen = strlen(target); /* FIXME: If you care. We'd need to use frags for the target if it grows much more than this */ if (targetlen > 254) return -EINVAL; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_symlink_inode_operations; f = JFFS2_INODE_INFO(inode); inode->i_size = targetlen; ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->compr = JFFS2_COMPR_NONE; ri->data_crc = cpu_to_je32(crc32(0, target, targetlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); } /* We use f->target field to store the target path. */ f->target = kmalloc(targetlen + 1, GFP_KERNEL); if (!f->target) { printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } memcpy(f->target, target, targetlen + 1); D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target)); /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } dir_f = JFFS2_INODE_INFO(dir_i); down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = DT_LNK; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); return 0; } static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen, phys_ofs; int ret; mode |= S_IFDIR; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_dir_inode_operations; inode->i_fop = &jffs2_dir_operations; /* Directories get nlink 2 at start */ inode->i_nlink = 2; f = JFFS2_INODE_INFO(inode); ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } dir_f = JFFS2_INODE_INFO(dir_i); down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = DT_DIR; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); dir_i->i_nlink++; jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); return 0; } static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); struct jffs2_full_dirent *fd; int ret; for (fd = f->dents ; fd; fd = fd->next) { if (fd->ino) return -ENOTEMPTY; } ret = jffs2_unlink(dir_i, dentry); if (!ret) dir_i->i_nlink--; return ret; } static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; jint16_t dev; int devlen = 0; uint32_t alloclen, phys_ofs; int ret; if (!old_valid_dev(rdev)) return -EINVAL; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); if (S_ISBLK(mode) || S_ISCHR(mode)) { dev = cpu_to_je16(old_encode_dev(rdev)); devlen = sizeof(dev); } /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_file_inode_operations; init_special_inode(inode, inode->i_mode, rdev); f = JFFS2_INODE_INFO(inode); ri->dsize = ri->csize = cpu_to_je32(devlen); ri->totlen = cpu_to_je32(sizeof(*ri) + devlen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->compr = JFFS2_COMPR_NONE; ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } dir_f = JFFS2_INODE_INFO(dir_i); down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; /* XXX: This is ugly. */ rd->type = (mode & S_IFMT) >> 12; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); return 0; } static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, struct inode *new_dir_i, struct dentry *new_dentry) { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); struct jffs2_inode_info *victim_f = NULL; uint8_t type; uint32_t now; /* The VFS will check for us and prevent trying to rename a * file over a directory and vice versa, but if it's a directory, * the VFS can't check whether the victim is empty. The filesystem * needs to do that for itself. */ if (new_dentry->d_inode) { victim_f = JFFS2_INODE_INFO(new_dentry->d_inode); if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct jffs2_full_dirent *fd; down(&victim_f->sem); for (fd = victim_f->dents; fd; fd = fd->next) { if (fd->ino) { up(&victim_f->sem); return -ENOTEMPTY; } } up(&victim_f->sem); } } /* XXX: We probably ought to alloc enough space for both nodes at the same time. Writing the new link, then getting -ENOSPC, is quite bad :) */ /* Make a hard link */ /* XXX: This is ugly */ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; now = get_seconds(); ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), old_dentry->d_inode->i_ino, type, new_dentry->d_name.name, new_dentry->d_name.len, now); if (ret) return ret; if (victim_f) { /* There was a victim. Kill it off nicely */ new_dentry->d_inode->i_nlink--; /* Don't oops if the victim was a dirent pointing to an inode which didn't exist. */ if (victim_f->inocache) { down(&victim_f->sem); victim_f->inocache->nlink--; up(&victim_f->sem); } } /* If it was a directory we moved, and there was no victim, increase i_nlink on its new parent */ if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f) new_dir_i->i_nlink++; /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); /* We don't touch inode->i_nlink */ if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); down(&f->sem); old_dentry->d_inode->i_nlink++; if (f->inocache) f->inocache->nlink++; up(&f->sem); printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); /* Might as well let the VFS know */ d_instantiate(new_dentry, old_dentry->d_inode); atomic_inc(&old_dentry->d_inode->i_count); new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now); return ret; } if (S_ISDIR(old_dentry->d_inode->i_mode)) old_dir_i->i_nlink--; new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); return 0; } linux-2.6.17/fs/jffs2/super.c0000644000000000000000000002274110564537530014406 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: super.c,v 1.110 2005/11/07 11:14:42 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "compr.h" #include "nodelist.h" static void jffs2_put_super(struct super_block *); static kmem_cache_t *jffs2_inode_cachep; static struct inode *jffs2_alloc_inode(struct super_block *sb) { struct jffs2_inode_info *ei; ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } static void jffs2_destroy_inode(struct inode *inode) { kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); } static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { init_MUTEX(&ei->sem); inode_init_once(&ei->vfs_inode); } } static int jffs2_sync_fs(struct super_block *sb, int wait) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); return 0; } static struct super_operations jffs2_super_operations = { .alloc_inode = jffs2_alloc_inode, .destroy_inode =jffs2_destroy_inode, .read_inode = jffs2_read_inode, .put_super = jffs2_put_super, .write_super = jffs2_write_super, .statfs = jffs2_statfs, .remount_fs = jffs2_remount_fs, .clear_inode = jffs2_clear_inode, .dirty_inode = jffs2_dirty_inode, .sync_fs = jffs2_sync_fs, }; static int jffs2_sb_compare(struct super_block *sb, void *data) { struct jffs2_sb_info *p = data; struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); /* The superblocks are considered to be equivalent if the underlying MTD device is the same one */ if (c->mtd == p->mtd) { D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name)); return 1; } else { D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n", c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name)); return 0; } } static int jffs2_sb_set(struct super_block *sb, void *data) { struct jffs2_sb_info *p = data; /* For persistence of NFS exports etc. we use the same s_dev each time we mount the device, don't just use an anonymous device */ sb->s_fs_info = p; p->os_priv = sb; sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index); return 0; } static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct mtd_info *mtd) { struct super_block *sb; struct jffs2_sb_info *c; int ret; c = kmalloc(sizeof(*c), GFP_KERNEL); if (!c) return ERR_PTR(-ENOMEM); memset(c, 0, sizeof(*c)); c->mtd = mtd; sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); if (IS_ERR(sb)) goto out_put; if (sb->s_root) { /* New mountpoint for JFFS2 which is already mounted */ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", mtd->index, mtd->name)); goto out_put; } D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", mtd->index, mtd->name)); /* Initialize JFFS2 superblock locks, the further initialization will be * done later */ init_MUTEX(&c->alloc_sem); init_MUTEX(&c->erase_free_sem); init_waitqueue_head(&c->erase_wait); init_waitqueue_head(&c->inocache_wq); spin_lock_init(&c->erase_completion_lock); spin_lock_init(&c->inocache_lock); sb->s_op = &jffs2_super_operations; sb->s_flags = flags | MS_NOATIME; ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); if (ret) { /* Failure case... */ up_write(&sb->s_umount); deactivate_super(sb); return ERR_PTR(ret); } sb->s_flags |= MS_ACTIVE; return sb; out_put: kfree(c); put_mtd_device(mtd); return sb; } static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, int mtdnr) { struct mtd_info *mtd; mtd = get_mtd_device(NULL, mtdnr); if (!mtd) { D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); return ERR_PTR(-EINVAL); } return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); } static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { int err; struct nameidata nd; int mtdnr; if (!dev_name) return ERR_PTR(-EINVAL); D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); /* The preferred way of mounting in future; especially when CONFIG_BLK_DEV is implemented - we specify the underlying MTD device by number or by name, so that we don't require block device support to be present in the kernel. */ /* FIXME: How to do the root fs this way? */ if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { /* Probably mounting without the blkdev crap */ if (dev_name[3] == ':') { struct mtd_info *mtd; /* Mount by MTD device name */ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { mtd = get_mtd_device(NULL, mtdnr); if (mtd) { if (!strcmp(mtd->name, dev_name+4)) return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); put_mtd_device(mtd); } } printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); } else if (isdigit(dev_name[3])) { /* Mount by MTD device number name */ char *endptr; mtdnr = simple_strtoul(dev_name+3, &endptr, 0); if (!*endptr) { /* It was a valid number */ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); } } } /* Try the old way - the hack where we allowed users to mount /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", err, nd.dentry->d_inode)); if (err) return ERR_PTR(err); err = -EINVAL; if (!S_ISBLK(nd.dentry->d_inode->i_mode)) goto out; if (nd.mnt->mnt_flags & MNT_NODEV) { err = -EACCES; goto out; } if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { if (!(flags & MS_SILENT)) printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", dev_name); goto out; } mtdnr = iminor(nd.dentry->d_inode); path_release(&nd); return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); out: path_release(&nd); return ERR_PTR(err); } static void jffs2_put_super (struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); jffs2_sum_exit(c); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); if (jffs2_blocks_use_vmalloc(c)) vfree(c->blocks); else kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) c->mtd->sync(c->mtd); D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); } static void jffs2_kill_sb(struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); if (!(sb->s_flags & MS_RDONLY)) jffs2_stop_garbage_collect_thread(c); generic_shutdown_super(sb); put_mtd_device(c->mtd); kfree(c); } static struct file_system_type jffs2_fs_type = { .owner = THIS_MODULE, .name = "jffs2", .get_sb = jffs2_get_sb, .kill_sb = jffs2_kill_sb, }; static int __init init_jffs2_fs(void) { int ret; printk(KERN_INFO "JFFS2 version 2.2." #ifdef CONFIG_JFFS2_FS_WRITEBUFFER " (NAND)" #endif #ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY) " #endif " (C) 2001-2003 Red Hat, Inc.\n"); jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), jffs2_i_init_once, NULL); if (!jffs2_inode_cachep) { printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); return -ENOMEM; } ret = jffs2_compressors_init(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n"); goto out; } ret = jffs2_create_slab_caches(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); goto out_compressors; } ret = register_filesystem(&jffs2_fs_type); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); goto out_slab; } return 0; out_slab: jffs2_destroy_slab_caches(); out_compressors: jffs2_compressors_exit(); out: kmem_cache_destroy(jffs2_inode_cachep); return ret; } static void __exit exit_jffs2_fs(void) { unregister_filesystem(&jffs2_fs_type); jffs2_destroy_slab_caches(); jffs2_compressors_exit(); kmem_cache_destroy(jffs2_inode_cachep); } module_init(init_jffs2_fs); module_exit(exit_jffs2_fs); MODULE_DESCRIPTION("The Journalling Flash File System, v2"); MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for // the sake of this tag. It's Free Software. linux-2.6.17/fs/jffs2/malloc.c0000644000000000000000000001167210564537530014520 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: malloc.c,v 1.31 2005/11/07 11:14:40 gleixner Exp $ * */ #include #include #include #include #include "nodelist.h" /* These are initialised to NULL in the kernel startup code. If you're porting to other operating systems, beware */ static kmem_cache_t *full_dnode_slab; static kmem_cache_t *raw_dirent_slab; static kmem_cache_t *raw_inode_slab; static kmem_cache_t *tmp_dnode_info_slab; static kmem_cache_t *raw_node_ref_slab; static kmem_cache_t *node_frag_slab; static kmem_cache_t *inode_cache_slab; int __init jffs2_create_slab_caches(void) { full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, 0, NULL, NULL); if (!full_dnode_slab) goto err; raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, 0, NULL, NULL); if (!raw_dirent_slab) goto err; raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, 0, NULL, NULL); if (!raw_inode_slab) goto err; tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, 0, NULL, NULL); if (!tmp_dnode_info_slab) goto err; raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, 0, NULL, NULL); if (!raw_node_ref_slab) goto err; node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, 0, NULL, NULL); if (!node_frag_slab) goto err; inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, 0, NULL, NULL); if (inode_cache_slab) return 0; err: jffs2_destroy_slab_caches(); return -ENOMEM; } void jffs2_destroy_slab_caches(void) { if(full_dnode_slab) kmem_cache_destroy(full_dnode_slab); if(raw_dirent_slab) kmem_cache_destroy(raw_dirent_slab); if(raw_inode_slab) kmem_cache_destroy(raw_inode_slab); if(tmp_dnode_info_slab) kmem_cache_destroy(tmp_dnode_info_slab); if(raw_node_ref_slab) kmem_cache_destroy(raw_node_ref_slab); if(node_frag_slab) kmem_cache_destroy(node_frag_slab); if(inode_cache_slab) kmem_cache_destroy(inode_cache_slab); } struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) { struct jffs2_full_dirent *ret; ret = kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_full_dirent(struct jffs2_full_dirent *x) { dbg_memalloc("%p\n", x); kfree(x); } struct jffs2_full_dnode *jffs2_alloc_full_dnode(void) { struct jffs2_full_dnode *ret; ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_full_dnode(struct jffs2_full_dnode *x) { dbg_memalloc("%p\n", x); kmem_cache_free(full_dnode_slab, x); } struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void) { struct jffs2_raw_dirent *ret; ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x) { dbg_memalloc("%p\n", x); kmem_cache_free(raw_dirent_slab, x); } struct jffs2_raw_inode *jffs2_alloc_raw_inode(void) { struct jffs2_raw_inode *ret; ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_raw_inode(struct jffs2_raw_inode *x) { dbg_memalloc("%p\n", x); kmem_cache_free(raw_inode_slab, x); } struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void) { struct jffs2_tmp_dnode_info *ret; ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x) { dbg_memalloc("%p\n", x); kmem_cache_free(tmp_dnode_info_slab, x); } struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void) { struct jffs2_raw_node_ref *ret; ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x) { dbg_memalloc("%p\n", x); kmem_cache_free(raw_node_ref_slab, x); } struct jffs2_node_frag *jffs2_alloc_node_frag(void) { struct jffs2_node_frag *ret; ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_node_frag(struct jffs2_node_frag *x) { dbg_memalloc("%p\n", x); kmem_cache_free(node_frag_slab, x); } struct jffs2_inode_cache *jffs2_alloc_inode_cache(void) { struct jffs2_inode_cache *ret; ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_inode_cache(struct jffs2_inode_cache *x) { dbg_memalloc("%p\n", x); kmem_cache_free(inode_cache_slab, x); } linux-2.6.17/fs/jffs2/histo_mips.h0000644000000000000000000000015110564537530015422 0ustar rootroot#define BIT_DIVIDER_MIPS 1043 static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */ linux-2.6.17/fs/jffs2/writev.c0000644000000000000000000000354510564537530014571 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: writev.c,v 1.8 2005/09/09 15:11:58 havasi Exp $ * */ #include #include #include "nodelist.h" /* This ought to be in core MTD code. All registered MTD devices without writev should have this put in place. Bug the MTD maintainer */ static inline int mtd_fake_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { unsigned long i; size_t totlen = 0, thislen; int ret = 0; for (i=0; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); totlen += thislen; if (ret || thislen != vecs[i].iov_len) break; to += vecs[i].iov_len; } if (retlen) *retlen = totlen; return ret; } int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { if (!jffs2_is_writebuffered(c)) { if (jffs2_sum_active()) { int res; res = jffs2_sum_add_kvec(c, vecs, count, (uint32_t) to); if (res) { return res; } } } if (c->mtd->writev) return c->mtd->writev(c->mtd, vecs, count, to, retlen); else { return mtd_fake_writev(c->mtd, vecs, count, to, retlen); } } int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) { int ret; ret = c->mtd->write(c->mtd, ofs, len, retlen, buf); if (jffs2_sum_active()) { struct kvec vecs[1]; int res; vecs[0].iov_base = (unsigned char *) buf; vecs[0].iov_len = len; res = jffs2_sum_add_kvec(c, vecs, 1, (uint32_t) ofs); if (res) { return res; } } return ret; } linux-2.6.17/fs/jffs2/.svn/0000755000000000000000000000000010613366603013756 5ustar rootrootlinux-2.6.17/fs/jffs2/.svn/format0000644000000000000000000000000210574207642015164 0ustar rootroot8 linux-2.6.17/fs/jffs2/.svn/entries0000444000000000000000000001127510613366571015362 0ustar rootroot8 dir 88 svn+ssh://192.168.2.6/home/svn-root/repos/FVT/linux-2.6.17/fs/jffs2 svn+ssh://192.168.2.6/home/svn-root/repos/FVT 2007-02-14T07:50:48.156956Z 1 root svn:special svn:externals svn:needs-lock 4bdaaa2a-274d-416c-a84c-08d2865ac13c LICENCE file 2007-02-14T07:50:48.000000Z 5372cac7d2484b024b6dbb2e8e0af2b9 2007-02-14T07:50:48.156956Z 1 root Makefile file 2007-02-14T07:50:48.000000Z 2d4729cb21b134a828e2bc4a632f9b5e 2007-02-14T07:50:48.156956Z 1 root README.Locking file 2007-02-14T07:50:48.000000Z c1589c70fb8fa4705700271b209b3d6b 2007-02-14T07:50:48.156956Z 1 root TODO file 2007-02-14T07:50:48.000000Z 5fcdb5713a64a71f611b487d51e88fee 2007-02-14T07:50:48.156956Z 1 root background.c file 2007-02-14T07:50:48.000000Z 9b01ee5502a783923662b1cd59aa92e4 2007-02-14T07:50:48.156956Z 1 root build.c file 2007-02-14T07:50:48.000000Z bf3fee347d50b6579a55d5da2a685bd2 2007-02-14T07:50:48.156956Z 1 root compr.c file 2007-02-14T07:50:48.000000Z 520db5e466f088dedf1c7a9ba65b60ca 2007-02-14T07:50:48.156956Z 1 root compr.h file 2007-02-14T07:50:48.000000Z ecd4fb8567e6460422dba7c4c63e4087 2007-02-14T07:50:48.156956Z 1 root compr_rtime.c file 2007-02-14T07:50:48.000000Z b4e40cadea7a76d173407ddcdf93dc45 2007-02-14T07:50:48.156956Z 1 root compr_rubin.c file 2007-02-14T07:50:48.000000Z 3b141e6a9cdcb08827ac52a39495d8ac 2007-02-14T07:50:48.156956Z 1 root compr_rubin.h file 2007-02-14T07:50:48.000000Z 34e5370f717fd948cbd6cf546c6ff304 2007-02-14T07:50:48.156956Z 1 root compr_zlib.c file 2007-02-14T07:50:48.000000Z 278b7131615bfce3335f41e05c5795be 2007-02-14T07:50:48.156956Z 1 root comprtest.c file 2007-02-14T07:50:48.000000Z e4cda0118557eada5fa3eb20dd042ee5 2007-02-14T07:50:48.156956Z 1 root debug.c file 2007-02-14T07:50:48.000000Z 101bb8a1e85fedb4e809e9b533fa3700 2007-02-14T07:50:48.156956Z 1 root debug.h file 2007-02-14T07:50:48.000000Z bb33f1f989e2a0c568b514e0bccd3bd4 2007-02-14T07:50:48.156956Z 1 root dir.c file 2007-02-14T07:50:48.000000Z 602932dd846ff464615070c6daa964a7 2007-02-14T07:50:48.156956Z 1 root erase.c file 2007-02-14T07:50:48.000000Z 78527a4c5e96da60ce216bf46725adc0 2007-02-14T07:50:48.156956Z 1 root file.c file 2007-02-14T07:50:48.000000Z 0a803dea9e93f1e4a83ecbb84869d5ca 2007-02-14T07:50:48.156956Z 1 root fs.c file 2007-02-14T07:50:48.000000Z 5f7e936802615e5346fd84ddeac80974 2007-02-14T07:50:48.156956Z 1 root gc.c file 2007-02-14T07:50:48.000000Z f76076a26289d4ca13c4780c56558bbe 2007-02-14T07:50:48.156956Z 1 root histo.h file 2007-02-14T07:50:48.000000Z d63d22dd31206440449e17e22c8eee69 2007-02-14T07:50:48.156956Z 1 root histo_mips.h file 2007-02-14T07:50:48.000000Z 3181f5652c75a9909ef8e3ee17602fde 2007-02-14T07:50:48.156956Z 1 root ioctl.c file 2007-02-14T07:50:48.000000Z bdabc791fc81a9320e0a36f5b91b1e16 2007-02-14T07:50:48.156956Z 1 root malloc.c file 2007-02-14T07:50:48.000000Z 0f931dced7e7209f06820e361a6723a8 2007-02-14T07:50:48.156956Z 1 root nodelist.c file 2007-02-14T07:50:48.000000Z ca3d4b9958d1b30e9c2e7e3c5686da0d 2007-02-14T07:50:48.156956Z 1 root nodelist.h file 2007-02-14T07:50:48.000000Z 6211512007e0df8e367aafe75d1b1130 2007-02-14T07:50:48.156956Z 1 root nodemgmt.c file 2007-02-14T07:50:48.000000Z fc9e9b8c04ee86bef86b8e3fe830b997 2007-02-14T07:50:48.156956Z 1 root os-linux.h file 2007-02-14T07:50:48.000000Z 03a38dd9d2d209b22ddf0b73af99d2e5 2007-02-14T07:50:48.156956Z 1 root pushpull.h file 2007-02-14T07:50:48.000000Z e80ee9840224abdb121d01b443080fac 2007-02-14T07:50:48.156956Z 1 root read.c file 2007-02-14T07:50:48.000000Z 0de664fb9595ef9b0219ccf9446233e9 2007-02-14T07:50:48.156956Z 1 root readinode.c file 2007-02-14T07:50:48.000000Z 65da50f7c0b41ab75a3e0d894b870f6b 2007-02-14T07:50:48.156956Z 1 root scan.c file 2007-02-14T07:50:48.000000Z 80efc28a9b68a8bb5d30f064cd313af9 2007-02-14T07:50:48.156956Z 1 root summary.c file 2007-02-14T07:50:48.000000Z d3de57921283b5c16312dab407f95aa0 2007-02-14T07:50:48.156956Z 1 root summary.h file 2007-02-14T07:50:48.000000Z 0519ed2942749554d8c6cca01dff6d37 2007-02-14T07:50:48.156956Z 1 root super.c file 2007-02-14T07:50:48.000000Z fb870b4556b78ca0115601c50906fe1a 2007-02-14T07:50:48.156956Z 1 root symlink.c file 2007-02-14T07:50:48.000000Z 3244db03d449e1a60b40c39551751bfd 2007-02-14T07:50:48.156956Z 1 root wbuf.c file 2007-02-14T07:50:48.000000Z 6e1192143602e59060c66e35e4a8de4a 2007-02-14T07:50:48.156956Z 1 root write.c file 2007-02-14T07:50:48.000000Z 079a1cfe6de526edc97a615557eac573 2007-02-14T07:50:48.156956Z 1 root writev.c file 2007-02-14T07:50:48.000000Z 2e914b1eaae6d55a39c82fd24de07eae 2007-02-14T07:50:48.156956Z 1 root linux-2.6.17/fs/jffs2/.svn/tmp/0000755000000000000000000000000010613366571014562 5ustar rootrootlinux-2.6.17/fs/jffs2/.svn/tmp/props/0000755000000000000000000000000010574207642015724 5ustar rootrootlinux-2.6.17/fs/jffs2/.svn/tmp/text-base/0000755000000000000000000000000010574207644016457 5ustar rootrootlinux-2.6.17/fs/jffs2/.svn/tmp/prop-base/0000755000000000000000000000000010574207642016451 5ustar rootrootlinux-2.6.17/fs/jffs2/.svn/props/0000755000000000000000000000000010574207642015124 5ustar rootrootlinux-2.6.17/fs/jffs2/.svn/text-base/0000755000000000000000000000000010574207644015657 5ustar rootrootlinux-2.6.17/fs/jffs2/.svn/text-base/wbuf.c.svn-base0000444000000000000000000011027610574207642020506 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * Copyright (C) 2004 Thomas Gleixner * * Created by David Woodhouse * Modified debugged and enhanced by Thomas Gleixner * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: wbuf.c,v 1.100 2005/09/30 13:59:13 dedekind Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" /* For testing write failures */ #undef BREAKME #undef BREAKMEHEADER #ifdef BREAKME static unsigned char *brokenbuf; #endif #define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) ) #define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) ) /* max. erase failures before we mark a block bad */ #define MAX_ERASE_FAILURES 2 struct jffs2_inodirty { uint32_t ino; struct jffs2_inodirty *next; }; static struct jffs2_inodirty inodirty_nomem; static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inodirty *this = c->wbuf_inodes; /* If a malloc failed, consider _everything_ dirty */ if (this == &inodirty_nomem) return 1; /* If ino == 0, _any_ non-GC writes mean 'yes' */ if (this && !ino) return 1; /* Look to see if the inode in question is pending in the wbuf */ while (this) { if (this->ino == ino) return 1; this = this->next; } return 0; } static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c) { struct jffs2_inodirty *this; this = c->wbuf_inodes; if (this != &inodirty_nomem) { while (this) { struct jffs2_inodirty *next = this->next; kfree(this); this = next; } } c->wbuf_inodes = NULL; } static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inodirty *new; /* Mark the superblock dirty so that kupdated will flush... */ jffs2_erase_pending_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) return; new = kmalloc(sizeof(*new), GFP_KERNEL); if (!new) { D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n")); jffs2_clear_wbuf_ino_list(c); c->wbuf_inodes = &inodirty_nomem; return; } new->ino = ino; new->next = c->wbuf_inodes; c->wbuf_inodes = new; return; } static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) { struct list_head *this, *next; static int n; if (list_empty(&c->erasable_pending_wbuf_list)) return; list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset)); list_del(this); if ((jiffies + (n++)) & 127) { /* Most of the time, we just erase it immediately. Otherwise we spend ages scanning it on mount, etc. */ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); list_add_tail(&jeb->list, &c->erasable_list); } } } #define REFILE_NOTEMPTY 0 #define REFILE_ANYWAY 1 static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty) { D1(printk("About to refile bad block at %08x\n", jeb->offset)); /* File the existing block on the bad_used_list.... */ if (c->nextblock == jeb) c->nextblock = NULL; else /* Not sure this should ever happen... need more coffee */ list_del(&jeb->list); if (jeb->first_node) { D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset)); list_add(&jeb->list, &c->bad_used_list); } else { BUG_ON(allow_empty == REFILE_NOTEMPTY); /* It has to have had some nodes or we couldn't be here */ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } /* Adjust its size counts accordingly */ c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->wasted_size += jeb->free_size; jeb->free_size = 0; jffs2_dbg_dump_block_lists_nolock(c); jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); } /* Recover from failure to write wbuf. Recover the nodes up to the * wbuf, not the one which we were starting to try to write. */ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) { struct jffs2_eraseblock *jeb, *new_jeb; struct jffs2_raw_node_ref **first_raw, **raw; size_t retlen; int ret; unsigned char *buf; uint32_t start, end, ofs, len; spin_lock(&c->erase_completion_lock); jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; jffs2_block_refile(c, jeb, REFILE_NOTEMPTY); /* Find the first node to be recovered, by skipping over every node which ends before the wbuf starts, or which is obsolete. */ first_raw = &jeb->first_node; while (*first_raw && (ref_obsolete(*first_raw) || (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ref_offset(*first_raw), ref_flags(*first_raw), (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), c->wbuf_ofs)); first_raw = &(*first_raw)->next_phys; } if (!*first_raw) { /* All nodes were obsolete. Nothing to recover. */ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n")); spin_unlock(&c->erase_completion_lock); return; } start = ref_offset(*first_raw); end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); /* Find the last node to be recovered */ raw = first_raw; while ((*raw)) { if (!ref_obsolete(*raw)) end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); raw = &(*raw)->next_phys; } spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end)); buf = NULL; if (start < c->wbuf_ofs) { /* First affected node was already partially written. * Attempt to reread the old data into our buffer. */ buf = kmalloc(end - start, GFP_KERNEL); if (!buf) { printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n"); goto read_failed; } /* Do the read... */ if (jffs2_cleanmarker_oob(c)) ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); else ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { /* ECC recovered */ ret = 0; } if (ret || retlen != c->wbuf_ofs - start) { printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); kfree(buf); buf = NULL; read_failed: first_raw = &(*first_raw)->next_phys; /* If this was the only node to be recovered, give up */ if (!(*first_raw)) return; /* It wasn't. Go on and try to recover nodes complete in the wbuf */ start = ref_offset(*first_raw); } else { /* Read succeeded. Copy the remaining data from the wbuf */ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); } } /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. Either 'buf' contains the data, or we find it in the wbuf */ /* ... and get an allocation of space from a shiny new block instead */ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len, JFFS2_SUMMARY_NOSUM_SIZE); if (ret) { printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); kfree(buf); return; } if (end-start >= c->wbuf_pagesize) { /* Need to do another write immediately, but it's possible that this is just because the wbuf itself is completely full, and there's nothing earlier read back from the flash. Hence 'buf' isn't necessarily what we're writing from. */ unsigned char *rewrite_buf = buf?:c->wbuf; uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n", towrite, ofs)); #ifdef BREAKMEHEADER static int breakme; if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs); breakme = 0; c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, brokenbuf, NULL, c->oobinfo); ret = -EIO; } else #endif if (jffs2_cleanmarker_oob(c)) ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, rewrite_buf, NULL, c->oobinfo); else ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); kfree(buf); if (retlen) { struct jffs2_raw_node_ref *raw2; raw2 = jffs2_alloc_raw_node_ref(); if (!raw2) return; raw2->flash_offset = ofs | REF_OBSOLETE; raw2->__totlen = ref_totlen(c, jeb, *first_raw); raw2->next_phys = NULL; raw2->next_in_ino = NULL; jffs2_add_physical_node_ref(c, raw2); } return; } printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs); c->wbuf_len = (end - start) - towrite; c->wbuf_ofs = ofs + towrite; memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len); /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ kfree(buf); } else { /* OK, now we're left with the dregs in whichever buffer we're using */ if (buf) { memcpy(c->wbuf, buf, end-start); kfree(buf); } else { memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); } c->wbuf_ofs = ofs; c->wbuf_len = end - start; } /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ new_jeb = &c->blocks[ofs / c->sector_size]; spin_lock(&c->erase_completion_lock); if (new_jeb->first_node) { /* Odd, but possible with ST flash later maybe */ new_jeb->last_node->next_phys = *first_raw; } else { new_jeb->first_node = *first_raw; } raw = first_raw; while (*raw) { uint32_t rawlen = ref_totlen(c, jeb, *raw); D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); if (ref_obsolete(*raw)) { /* Shouldn't really happen much */ new_jeb->dirty_size += rawlen; new_jeb->free_size -= rawlen; c->dirty_size += rawlen; } else { new_jeb->used_size += rawlen; new_jeb->free_size -= rawlen; jeb->dirty_size += rawlen; jeb->used_size -= rawlen; c->dirty_size += rawlen; } c->free_size -= rawlen; (*raw)->flash_offset = ofs | ref_flags(*raw); ofs += rawlen; new_jeb->last_node = *raw; raw = &(*raw)->next_phys; } /* Fix up the original jeb now it's on the bad_list */ *first_raw = NULL; if (first_raw == &jeb->first_node) { jeb->last_node = NULL; D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } else jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys); jffs2_dbg_acct_sanity_check_nolock(c, jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); jffs2_dbg_acct_sanity_check_nolock(c, new_jeb); jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "wbuf recovery completed OK\n")); } /* Meaning of pad argument: 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. 1: Pad, do not adjust nextblock free_size 2: Pad, adjust nextblock free_size */ #define NOPAD 0 #define PAD_NOACCOUNT 1 #define PAD_ACCOUNTING 2 static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { int ret; size_t retlen; /* Nothing to do if not write-buffering the flash. In particular, we shouldn't del_timer() the timer we never initialised. */ if (!jffs2_is_writebuffered(c)) return 0; if (!down_trylock(&c->alloc_sem)) { up(&c->alloc_sem); printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); BUG(); } if (!c->wbuf_len) /* already checked c->wbuf above */ return 0; /* claim remaining space on the page this happens, if we have a change to a new block, or if fsync forces us to flush the writebuffer. if we have a switch to next page, we will not have enough remaining space for this. */ if (pad ) { c->wbuf_len = PAD(c->wbuf_len); /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR with 8 byte page size */ memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); } } /* else jffs2_flash_writev has actually filled in the rest of the buffer for us, and will deal with the node refs etc. later. */ #ifdef BREAKME static int breakme; if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs); breakme = 0; c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, brokenbuf, NULL, c->oobinfo); ret = -EIO; } else #endif if (jffs2_cleanmarker_oob(c)) ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); else ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); if (ret || retlen != c->wbuf_pagesize) { if (ret) printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret); else { printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", retlen, c->wbuf_pagesize); ret = -EIO; } jffs2_wbuf_recover(c); return ret; } spin_lock(&c->erase_completion_lock); /* Adjust free size of the block if we padded. */ if (pad) { struct jffs2_eraseblock *jeb; jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n", (jeb==c->nextblock)?"next":"", jeb->offset)); /* wbuf_pagesize - wbuf_len is the amount of space that's to be padded. If there is less free space in the block than that, something screwed up */ if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) { printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len); printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", jeb->offset, jeb->free_size); BUG(); } jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len); c->free_size -= (c->wbuf_pagesize - c->wbuf_len); jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len); c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); } /* Stick any now-obsoleted blocks on the erase_pending_list */ jffs2_refile_wbuf_blocks(c); jffs2_clear_wbuf_ino_list(c); spin_unlock(&c->erase_completion_lock); memset(c->wbuf,0xff,c->wbuf_pagesize); /* adjust write buffer offset, else we get a non contiguous write bug */ c->wbuf_ofs += c->wbuf_pagesize; c->wbuf_len = 0; return 0; } /* Trigger garbage collection to flush the write-buffer. If ino arg is zero, do it if _any_ real (i.e. not GC) writes are outstanding. If ino arg non-zero, do it only if a write for the given inode is outstanding. */ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) { uint32_t old_wbuf_ofs; uint32_t old_wbuf_len; int ret = 0; D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino)); if (!c->wbuf) return 0; down(&c->alloc_sem); if (!jffs2_wbuf_pending_for_ino(c, ino)) { D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino)); up(&c->alloc_sem); return 0; } old_wbuf_ofs = c->wbuf_ofs; old_wbuf_len = c->wbuf_len; if (c->unchecked_size) { /* GC won't make any progress for a while */ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); /* retry flushing wbuf in case jffs2_wbuf_recover left some data in the wbuf */ if (ret) ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); up_write(&c->wbuf_sem); } else while (old_wbuf_len && old_wbuf_ofs == c->wbuf_ofs) { up(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n")); ret = jffs2_garbage_collect_pass(c); if (ret) { /* GC failed. Flush it with padding instead */ down(&c->alloc_sem); down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); /* retry flushing wbuf in case jffs2_wbuf_recover left some data in the wbuf */ if (ret) ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); up_write(&c->wbuf_sem); break; } down(&c->alloc_sem); } D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n")); up(&c->alloc_sem); return ret; } /* Pad write-buffer to end and write it, wasting space. */ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) { int ret; if (!c->wbuf) return 0; down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); /* retry - maybe wbuf recover left some data in wbuf. */ if (ret) ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); up_write(&c->wbuf_sem); return ret; } int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino) { struct kvec outvecs[3]; uint32_t totlen = 0; uint32_t split_ofs = 0; uint32_t old_totlen; int ret, splitvec = -1; int invec, outvec; size_t wbuf_retlen; unsigned char *wbuf_ptr; size_t donelen = 0; uint32_t outvec_to = to; /* If not NAND flash, don't bother */ if (!jffs2_is_writebuffered(c)) return jffs2_flash_direct_writev(c, invecs, count, to, retlen); down_write(&c->wbuf_sem); /* If wbuf_ofs is not initialized, set it to target address */ if (c->wbuf_ofs == 0xFFFFFFFF) { c->wbuf_ofs = PAGE_DIV(to); c->wbuf_len = PAGE_MOD(to); memset(c->wbuf,0xff,c->wbuf_pagesize); } /* Fixup the wbuf if we are moving to a new eraseblock. The checks below fail for ECC'd NOR because cleanmarker == 16, so a block starts at xxx0010. */ if (jffs2_nor_ecc(c)) { if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) { c->wbuf_ofs = PAGE_DIV(to); c->wbuf_len = PAGE_MOD(to); memset(c->wbuf,0xff,c->wbuf_pagesize); } } /* Sanity checks on target address. It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to write at the beginning of a new erase block. Anything else, and you die. New block starts at xxx000c (0-b = block header) */ if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { /* It's a write to a new block */ if (c->wbuf_len) { D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); *retlen = 0; goto exit; } } /* set pointer to new block */ c->wbuf_ofs = PAGE_DIV(to); c->wbuf_len = PAGE_MOD(to); } if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { /* We're not writing immediately after the writebuffer. Bad. */ printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to); if (c->wbuf_len) printk(KERN_CRIT "wbuf was previously %08x-%08x\n", c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); BUG(); } /* Note outvecs[3] above. We know count is never greater than 2 */ if (count > 2) { printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count); BUG(); } invec = 0; outvec = 0; /* Fill writebuffer first, if already in use */ if (c->wbuf_len) { uint32_t invec_ofs = 0; /* adjust alignment offset */ if (c->wbuf_len != PAGE_MOD(to)) { c->wbuf_len = PAGE_MOD(to); /* take care of alignment to next page */ if (!c->wbuf_len) c->wbuf_len = c->wbuf_pagesize; } while(c->wbuf_len < c->wbuf_pagesize) { uint32_t thislen; if (invec == count) goto alldone; thislen = c->wbuf_pagesize - c->wbuf_len; if (thislen >= invecs[invec].iov_len) thislen = invecs[invec].iov_len; invec_ofs = thislen; memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen); c->wbuf_len += thislen; donelen += thislen; /* Get next invec, if actual did not fill the buffer */ if (c->wbuf_len < c->wbuf_pagesize) invec++; } /* write buffer is full, flush buffer */ ret = __jffs2_flush_wbuf(c, NOPAD); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); /* Retlen zero to make sure our caller doesn't mark the space dirty. We've already done everything that's necessary */ *retlen = 0; goto exit; } outvec_to += donelen; c->wbuf_ofs = outvec_to; /* All invecs done ? */ if (invec == count) goto alldone; /* Set up the first outvec, containing the remainder of the invec we partially used */ if (invecs[invec].iov_len > invec_ofs) { outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs; totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs; if (totlen > c->wbuf_pagesize) { splitvec = outvec; split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen); } outvec++; } invec++; } /* OK, now we've flushed the wbuf and the start of the bits we have been asked to write, now to write the rest.... */ /* totlen holds the amount of data still to be written */ old_totlen = totlen; for ( ; invec < count; invec++,outvec++ ) { outvecs[outvec].iov_base = invecs[invec].iov_base; totlen += outvecs[outvec].iov_len = invecs[invec].iov_len; if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) { splitvec = outvec; split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen); old_totlen = totlen; } } /* Now the outvecs array holds all the remaining data to write */ /* Up to splitvec,split_ofs is to be written immediately. The rest goes into the (now-empty) wbuf */ if (splitvec != -1) { uint32_t remainder; remainder = outvecs[splitvec].iov_len - split_ofs; outvecs[splitvec].iov_len = split_ofs; /* We did cross a page boundary, so we write some now */ if (jffs2_cleanmarker_oob(c)) ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); else ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { /* At this point we have no problem, c->wbuf is empty. However refile nextblock to avoid writing again to same address. */ struct jffs2_eraseblock *jeb; spin_lock(&c->erase_completion_lock); jeb = &c->blocks[outvec_to / c->sector_size]; jffs2_block_refile(c, jeb, REFILE_ANYWAY); *retlen = 0; spin_unlock(&c->erase_completion_lock); goto exit; } donelen += wbuf_retlen; c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen); if (remainder) { outvecs[splitvec].iov_base += split_ofs; outvecs[splitvec].iov_len = remainder; } else { splitvec++; } } else { splitvec = 0; } /* Now splitvec points to the start of the bits we have to copy into the wbuf */ wbuf_ptr = c->wbuf; for ( ; splitvec < outvec; splitvec++) { /* Don't copy the wbuf into itself */ if (outvecs[splitvec].iov_base == c->wbuf) continue; memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len); wbuf_ptr += outvecs[splitvec].iov_len; donelen += outvecs[splitvec].iov_len; } c->wbuf_len = wbuf_ptr - c->wbuf; /* If there's a remainder in the wbuf and it's a non-GC write, remember that the wbuf affects this ino */ alldone: *retlen = donelen; if (jffs2_sum_active()) { int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to); if (res) return res; } if (c->wbuf_len && ino) jffs2_wbuf_dirties_inode(c, ino); ret = 0; exit: up_write(&c->wbuf_sem); return ret; } /* * This is the entry for flash write. * Check, if we work on NAND FLASH, if so build an kvec and write it via vritev */ int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) { struct kvec vecs[1]; if (!jffs2_is_writebuffered(c)) return jffs2_flash_direct_write(c, ofs, len, retlen, buf); vecs[0].iov_base = (unsigned char *) buf; vecs[0].iov_len = len; return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0); } /* Handle readback from writebuffer and ECC failure return */ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf) { loff_t orbf = 0, owbf = 0, lwbf = 0; int ret; if (!jffs2_is_writebuffered(c)) return c->mtd->read(c->mtd, ofs, len, retlen, buf); /* Read flash */ down_read(&c->wbuf_sem); if (jffs2_cleanmarker_oob(c)) ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); else ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); if ( (ret == -EBADMSG) && (*retlen == len) ) { printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", len, ofs); /* * We have the raw data without ECC correction in the buffer, maybe * we are lucky and all data or parts are correct. We check the node. * If data are corrupted node check will sort it out. * We keep this block, it will fail on write or erase and the we * mark it bad. Or should we do that now? But we should give him a chance. * Maybe we had a system crash or power loss before the ecc write or * a erase was completed. * So we return success. :) */ ret = 0; } /* if no writebuffer available or write buffer empty, return */ if (!c->wbuf_pagesize || !c->wbuf_len) goto exit; /* if we read in a different block, return */ if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs)) goto exit; if (ofs >= c->wbuf_ofs) { owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ goto exit; lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ if (lwbf > len) lwbf = len; } else { orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ if (orbf > len) /* is write beyond write buffer ? */ goto exit; lwbf = len - orbf; /* number of bytes to copy */ if (lwbf > c->wbuf_len) lwbf = c->wbuf_len; } if (lwbf > 0) memcpy(buf+orbf,c->wbuf+owbf,lwbf); exit: up_read(&c->wbuf_sem); return ret; } /* * Check, if the out of band area is empty */ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) { unsigned char *buf; int ret = 0; int i,len,page; size_t retlen; int oob_size; /* allocate a buffer for all oob data in this sector */ oob_size = c->mtd->oobsize; len = 4 * oob_size; buf = kmalloc(len, GFP_KERNEL); if (!buf) { printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); return -ENOMEM; } /* * if mode = 0, we scan for a total empty oob area, else we have * to take care of the cleanmarker in the first page of the block */ ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); if (ret) { D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); goto out; } if (retlen < len) { D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); ret = -EIO; goto out; } /* Special check for first page */ for(i = 0; i < oob_size ; i++) { /* Yeah, we know about the cleanmarker. */ if (mode && i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len) continue; if (buf[i] != 0xFF) { D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", buf[i], i, jeb->offset)); ret = 1; goto out; } } /* we know, we are aligned :) */ for (page = oob_size; page < len; page += sizeof(long)) { unsigned long dat = *(unsigned long *)(&buf[page]); if(dat != -1) { ret = 1; goto out; } } out: kfree(buf); return ret; } /* * Scan for a valid cleanmarker and for bad blocks * For virtual blocks (concatenated physical blocks) check the cleanmarker * only in the first page of the first physical block, but scan for bad blocks in all * physical blocks */ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; unsigned char buf[2 * NAND_MAX_OOBSIZE]; unsigned char *p; int ret, i, cnt, retval = 0; size_t retlen, offset; int oob_size; offset = jeb->offset; oob_size = c->mtd->oobsize; /* Loop through the physical blocks */ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { /* Check first if the block is bad. */ if (c->mtd->block_isbad (c->mtd, offset)) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); return 2; } /* * We read oob data from page 0 and 1 of the block. * page 0 contains cleanmarker and badblock info * page 1 contains failure count of this block */ ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); if (ret) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); return ret; } if (retlen < (oob_size << 1)) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); return -EIO; } /* Check cleanmarker only on the first physical block */ if (!cnt) { n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32 (8); p = (unsigned char *) &n; for (i = 0; i < c->fsdata_len; i++) { if (buf[c->fsdata_pos + i] != p[i]) { retval = 1; } } D1(if (retval == 1) { printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); printk(KERN_WARNING "OOB at %08x was ", offset); for (i=0; i < oob_size; i++) { printk("%02x ", buf[i]); } printk("\n"); }) } offset += c->mtd->erasesize; } return retval; } int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; int ret; size_t retlen; n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32(8); ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); if (ret) { D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); return ret; } if (retlen != c->fsdata_len) { D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); return ret; } return 0; } /* * On NAND we try to mark this block bad. If the block was erased more * than MAX_ERASE_FAILURES we mark it finaly bad. * Don't care about failures. This block remains on the erase-pending * or badblock list as long as nobody manipulates the flash with * a bootloader or something like that. */ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { int ret; /* if the count is < max, we try to write the counter to the 2nd page oob area */ if( ++jeb->bad_count < MAX_ERASE_FAILURES) return 0; if (!c->mtd->block_markbad) return 1; // What else can we do? D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset)); ret = c->mtd->block_markbad(c->mtd, bad_offset); if (ret) { D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); return ret; } return 1; } #define NAND_JFFS2_OOB16_FSDALEN 8 static struct nand_oobinfo jffs2_oobinfo_docecc = { .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, .eccpos = {0,1,2,3,4,5} }; static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { struct nand_oobinfo *oinfo = &c->mtd->oobinfo; /* Do this only, if we have an oob buffer */ if (!c->mtd->oobsize) return 0; /* Cleanmarker is out-of-band, so inline size zero */ c->cleanmarker_size = 0; /* Should we use autoplacement ? */ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); /* Get the position of the free bytes */ if (!oinfo->oobfree[0][1]) { printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); return -ENOSPC; } c->fsdata_pos = oinfo->oobfree[0][0]; c->fsdata_len = oinfo->oobfree[0][1]; if (c->fsdata_len > 8) c->fsdata_len = 8; } else { /* This is just a legacy fallback and should go away soon */ switch(c->mtd->ecctype) { case MTD_ECC_RS_DiskOnChip: printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n"); c->oobinfo = &jffs2_oobinfo_docecc; c->fsdata_pos = 6; c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; c->badblock_pos = 15; break; default: D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); return -EINVAL; } } return 0; } int jffs2_nand_flash_setup(struct jffs2_sb_info *c) { int res; /* Initialise write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->oobblock; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; res = jffs2_nand_set_oobinfo(c); #ifdef BREAKME if (!brokenbuf) brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!brokenbuf) { kfree(c->wbuf); return -ENOMEM; } memset(brokenbuf, 0xdb, c->wbuf_pagesize); #endif return res; } void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } int jffs2_dataflash_setup(struct jffs2_sb_info *c) { c->cleanmarker_size = 0; /* No cleanmarkers needed */ /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->erasesize; /* Find a suitable c->sector_size * - Not too much sectors * - Sectors have to be at least 4 K + some bytes * - All known dataflashes have erase sizes of 528 or 1056 * - we take at least 8 eraseblocks and want to have at least 8K size * - The concatenation should be a power of 2 */ c->sector_size = 8 * c->mtd->erasesize; while (c->sector_size < 8192) { c->sector_size *= 2; } /* It may be necessary to adjust the flash size */ c->flash_size = c->mtd->size; if ((c->flash_size % c->sector_size) != 0) { c->flash_size = (c->flash_size / c->sector_size) * c->sector_size; printk(KERN_WARNING "JFFS2 flash size adjusted to %dKiB\n", c->flash_size); }; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); return 0; } void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) { /* Cleanmarker is actually larger on the flashes */ c->cleanmarker_size = 16; /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->eccsize; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; return 0; } void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { /* Cleanmarker currently occupies a whole programming region */ c->cleanmarker_size = MTD_PROGREGION_SIZE(c->mtd); /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = MTD_PROGREGION_SIZE(c->mtd); c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; return 0; } void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } linux-2.6.17/fs/jffs2/.svn/text-base/Makefile.svn-base0000444000000000000000000000114110574207642021025 0ustar rootroot# # Makefile for the Linux Journalling Flash File System v2 (JFFS2) # # $Id: Makefile.common,v 1.11 2005/09/07 08:34:53 havasi Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o debug.o jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o linux-2.6.17/fs/jffs2/.svn/text-base/histo_mips.h.svn-base0000444000000000000000000000015110574207642021714 0ustar rootroot#define BIT_DIVIDER_MIPS 1043 static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */ linux-2.6.17/fs/jffs2/.svn/text-base/gc.c.svn-base0000444000000000000000000012510610574207642020132 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: gc.c,v 1.155 2005/11/07 11:14:39 gleixner Exp $ * */ #include #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_raw_node_ref *raw); static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd); static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end); static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end); static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f); /* Called with erase_completion_lock held */ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) { struct jffs2_eraseblock *ret; struct list_head *nextlist = NULL; int n = jiffies % 128; /* Pick an eraseblock to garbage collect next. This is where we'll put the clever wear-levelling algorithms. Eventually. */ /* We possibly want to favour the dirtier blocks more when the number of free blocks is low. */ again: if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) { D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); nextlist = &c->bad_used_list; } else if (n < 50 && !list_empty(&c->erasable_list)) { /* Note that most of them will have gone directly to be erased. So don't favour the erasable_list _too_ much. */ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n")); nextlist = &c->erasable_list; } else if (n < 110 && !list_empty(&c->very_dirty_list)) { /* Most of the time, pick one off the very_dirty list */ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n")); nextlist = &c->very_dirty_list; } else if (n < 126 && !list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->clean_list)) { D1(printk(KERN_DEBUG "Picking block from clean_list to GC next\n")); nextlist = &c->clean_list; } else if (!list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->very_dirty_list)) { D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n")); nextlist = &c->very_dirty_list; } else if (!list_empty(&c->erasable_list)) { D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); nextlist = &c->erasable_list; } else if (!list_empty(&c->erasable_pending_wbuf_list)) { /* There are blocks are wating for the wbuf sync */ D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n")); spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); goto again; } else { /* Eep. All were empty */ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); return NULL; } ret = list_entry(nextlist->next, struct jffs2_eraseblock, list); list_del(&ret->list); c->gcblock = ret; ret->gc_node = ret->first_node; if (!ret->gc_node) { printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); BUG(); } /* Have we accidentally picked a clean block with wasted space ? */ if (ret->wasted_size) { D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size)); ret->dirty_size += ret->wasted_size; c->wasted_size -= ret->wasted_size; c->dirty_size += ret->wasted_size; ret->wasted_size = 0; } return ret; } /* jffs2_garbage_collect_pass * Make a single attempt to progress GC. Move one node, and possibly * start erasing one eraseblock. */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) { struct jffs2_inode_info *f; struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; int ret = 0, inum, nlink; if (down_interruptible(&c->alloc_sem)) return -EINTR; for (;;) { spin_lock(&c->erase_completion_lock); if (!c->unchecked_size) break; /* We can't start doing GC yet. We haven't finished checking the node CRCs etc. Do it now. */ /* checked_ino is protected by the alloc_sem */ if (c->checked_ino > c->highest_ino) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); BUG(); } spin_unlock(&c->erase_completion_lock); spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, c->checked_ino++); if (!ic) { spin_unlock(&c->inocache_lock); continue; } if (!ic->nlink) { D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", ic->ino)); spin_unlock(&c->inocache_lock); continue; } switch(ic->state) { case INO_STATE_CHECKEDABSENT: case INO_STATE_PRESENT: D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino)); spin_unlock(&c->inocache_lock); continue; case INO_STATE_GC: case INO_STATE_CHECKING: printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state); spin_unlock(&c->inocache_lock); BUG(); case INO_STATE_READING: /* We need to wait for it to finish, lest we move on and trigger the BUG() above while we haven't yet finished checking all its nodes */ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino)); up(&c->alloc_sem); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); return 0; default: BUG(); case INO_STATE_UNCHECKED: ; } ic->state = INO_STATE_CHECKING; spin_unlock(&c->inocache_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino)); ret = jffs2_do_crccheck_inode(c, ic); if (ret) printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); up(&c->alloc_sem); return ret; } /* First, work out which block we're garbage-collecting */ jeb = c->gcblock; if (!jeb) jeb = jffs2_find_gc_block(c); if (!jeb) { D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -EIO; } D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); D1(if (c->nextblock) printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); if (!jeb->used_size) { up(&c->alloc_sem); goto eraseit; } raw = jeb->gc_node; while(ref_obsolete(raw)) { D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); raw = raw->next_phys; if (unlikely(!raw)) { printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); BUG(); } } jeb->gc_node = raw; D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); if (!raw->next_in_ino) { /* Inode-less node. Clean marker, snapshot or something like that */ /* FIXME: If it's something that needs to be copied, including something we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); up(&c->alloc_sem); goto eraseit_lock; } ic = jffs2_raw_ref_to_ic(raw); /* We need to hold the inocache. Either the erase_completion_lock or the inocache_lock are sufficient; we trade down since the inocache_lock causes less contention. */ spin_lock(&c->inocache_lock); spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino)); /* Three possibilities: 1. Inode is already in-core. We must iget it and do proper updating to its fragtree, etc. 2. Inode is not in-core, node is REF_PRISTINE. We lock the inocache to prevent a read_inode(), copy the node intact. 3. Inode is not in-core, node is not pristine. We must iget() and take the slow path. */ switch(ic->state) { case INO_STATE_CHECKEDABSENT: /* It's been checked, but it's not currently in-core. We can just copy any pristine nodes, but have to prevent anyone else from doing read_inode() while we're at it, so we set the state accordingly */ if (ref_flags(raw) == REF_PRISTINE) ic->state = INO_STATE_GC; else { D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", ic->ino)); } break; case INO_STATE_PRESENT: /* It's in-core. GC must iget() it. */ break; case INO_STATE_UNCHECKED: case INO_STATE_CHECKING: case INO_STATE_GC: /* Should never happen. We should have finished checking by the time we actually start doing any GC, and since we're holding the alloc_sem, no other garbage collection can happen. */ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ic->ino, ic->state); up(&c->alloc_sem); spin_unlock(&c->inocache_lock); BUG(); case INO_STATE_READING: /* Someone's currently trying to read it. We must wait for them to finish and then go through the full iget() route to do the GC. However, sometimes read_inode() needs to get the alloc_sem() (for marking nodes invalid) so we must drop the alloc_sem before sleeping. */ up(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); /* And because we dropped the alloc_sem we must start again from the beginning. Ponder chance of livelock here -- we're returning success without actually making any progress. Q: What are the chances that the inode is back in INO_STATE_READING again by the time we next enter this function? And that this happens enough times to cause a real delay? A: Small enough that I don't care :) */ return 0; } /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the node intact, and we don't have to muck about with the fragtree etc. because we know it's not in-core. If it _was_ in-core, we go through all the iget() crap anyway */ if (ic->state == INO_STATE_GC) { spin_unlock(&c->inocache_lock); ret = jffs2_garbage_collect_pristine(c, ic, raw); spin_lock(&c->inocache_lock); ic->state = INO_STATE_CHECKEDABSENT; wake_up(&c->inocache_wq); if (ret != -EBADFD) { spin_unlock(&c->inocache_lock); goto release_sem; } /* Fall through if it wanted us to, with inocache_lock held */ } /* Prevent the fairly unlikely race where the gcblock is entirely obsoleted by the final close of a file which had the only valid nodes in the block, followed by erasure, followed by freeing of the ic because the erased block(s) held _all_ the nodes of that inode.... never been seen but it's vaguely possible. */ inum = ic->ino; nlink = ic->nlink; spin_unlock(&c->inocache_lock); f = jffs2_gc_fetch_inode(c, inum, nlink); if (IS_ERR(f)) { ret = PTR_ERR(f); goto release_sem; } if (!f) { ret = 0; goto release_sem; } ret = jffs2_garbage_collect_live(c, jeb, raw, f); jffs2_gc_release_inode(c, f); release_sem: up(&c->alloc_sem); eraseit_lock: /* If we've finished this block, start it erasing */ spin_lock(&c->erase_completion_lock); eraseit: if (c->gcblock && !c->gcblock->used_size) { D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); /* We're GC'ing an empty block? */ list_add_tail(&c->gcblock->list, &c->erase_pending_list); c->gcblock = NULL; c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } spin_unlock(&c->erase_completion_lock); return ret; } static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f) { struct jffs2_node_frag *frag; struct jffs2_full_dnode *fn = NULL; struct jffs2_full_dirent *fd; uint32_t start = 0, end = 0, nrfrags = 0; int ret = 0; down(&f->sem); /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ spin_lock(&c->erase_completion_lock); if (c->gcblock != jeb) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n")); goto upnout; } if (ref_obsolete(raw)) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); /* They'll call again */ goto upnout; } spin_unlock(&c->erase_completion_lock); /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */ if (f->metadata && f->metadata->raw == raw) { fn = f->metadata; ret = jffs2_garbage_collect_metadata(c, jeb, f, fn); goto upnout; } /* FIXME. Read node and do lookup? */ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { if (frag->node && frag->node->raw == raw) { fn = frag->node; end = frag->ofs + frag->size; if (!nrfrags++) start = frag->ofs; if (nrfrags == frag->node->frags) break; /* We've found them all */ } } if (fn) { if (ref_flags(raw) == REF_PRISTINE) { ret = jffs2_garbage_collect_pristine(c, f->inocache, raw); if (!ret) { /* Urgh. Return it sensibly. */ frag->node->raw = f->inocache->nodes; } if (ret != -EBADFD) goto upnout; } /* We found a datanode. Do the GC */ if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) { /* It crosses a page boundary. Therefore, it must be a hole. */ ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end); } else { /* It could still be a hole. But we GC the page this way anyway */ ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end); } goto upnout; } /* Wasn't a dnode. Try dirent */ for (fd = f->dents; fd; fd=fd->next) { if (fd->raw == raw) break; } if (fd && fd->ino) { ret = jffs2_garbage_collect_dirent(c, jeb, f, fd); } else if (fd) { ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd); } else { printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n", ref_offset(raw), f->inocache->ino); if (ref_obsolete(raw)) { printk(KERN_WARNING "But it's obsolete so we don't mind too much\n"); } else { jffs2_dbg_dump_node(c, ref_offset(raw)); BUG(); } } upnout: up(&f->sem); return ret; } static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_raw_node_ref *raw) { union jffs2_node_union *node; struct jffs2_raw_node_ref *nraw; size_t retlen; int ret; uint32_t phys_ofs, alloclen; uint32_t crc, rawlen; int retried = 0; D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); rawlen = ref_totlen(c, c->gcblock, raw); /* Ask for a small amount of space (or the totlen if smaller) because we don't want to force wastage of the end of a block if splitting would work. */ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, rawlen), &phys_ofs, &alloclen, rawlen); /* this is not the exact summary size of it, it is only an upper estimation */ if (ret) return ret; if (alloclen < rawlen) { /* Doesn't fit untouched. We'll go the old route and split it */ return -EBADFD; } node = kmalloc(rawlen, GFP_KERNEL); if (!node) return -ENOMEM; ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); if (!ret && retlen != rawlen) ret = -EIO; if (ret) goto out_node; crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4); if (je32_to_cpu(node->u.hdr_crc) != crc) { printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc); goto bail; } switch(je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: crc = crc32(0, node, sizeof(node->i)-8); if (je32_to_cpu(node->i.node_crc) != crc) { printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->i.node_crc), crc); goto bail; } if (je32_to_cpu(node->i.dsize)) { crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize)); if (je32_to_cpu(node->i.data_crc) != crc) { printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->i.data_crc), crc); goto bail; } } break; case JFFS2_NODETYPE_DIRENT: crc = crc32(0, node, sizeof(node->d)-8); if (je32_to_cpu(node->d.node_crc) != crc) { printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->d.node_crc), crc); goto bail; } if (node->d.nsize) { crc = crc32(0, node->d.name, node->d.nsize); if (je32_to_cpu(node->d.name_crc) != crc) { printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->d.name_crc), crc); goto bail; } } break; default: printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", ref_offset(raw), je16_to_cpu(node->u.nodetype)); goto bail; } nraw = jffs2_alloc_raw_node_ref(); if (!nraw) { ret = -ENOMEM; goto out_node; } /* OK, all the CRCs are good; this node can just be copied as-is. */ retry: nraw->flash_offset = phys_ofs; nraw->__totlen = rawlen; nraw->next_phys = NULL; ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); if (ret || (retlen != rawlen)) { printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", rawlen, phys_ofs, ret, retlen); if (retlen) { /* Doesn't belong to any inode */ nraw->next_in_ino = NULL; nraw->flash_offset |= REF_OBSOLETE; jffs2_add_physical_node_ref(c, nraw); jffs2_mark_node_obsolete(c, nraw); } else { printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); jffs2_free_raw_node_ref(nraw); } if (!retried && (nraw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; retried = 1; D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n")); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy, rawlen); /* this is not the exact summary size of it, it is only an upper estimation */ if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); jffs2_free_raw_node_ref(nraw); } jffs2_free_raw_node_ref(nraw); if (!ret) ret = -EIO; goto out_node; } nraw->flash_offset |= REF_PRISTINE; jffs2_add_physical_node_ref(c, nraw); /* Link into per-inode list. This is safe because of the ic state being INO_STATE_GC. Note that if we're doing this for an inode which is in-core, the 'nraw' pointer is then going to be fetched from ic->nodes by our caller. */ spin_lock(&c->erase_completion_lock); nraw->next_in_ino = ic->nodes; ic->nodes = nraw; spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); out_node: kfree(node); return ret; bail: ret = -EBADFD; goto out_node; } static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; struct jffs2_node_frag *last_frag; jint16_t dev; char *mdata = NULL, mdatalen = 0; uint32_t alloclen, phys_ofs, ilen; int ret; if (S_ISBLK(JFFS2_F_I_MODE(f)) || S_ISCHR(JFFS2_F_I_MODE(f)) ) { /* For these, we don't actually need to read the old node */ /* FIXME: for minor or major > 255. */ dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | JFFS2_F_I_RDEV_MIN(f))); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(JFFS2_F_I_MODE(f))) { mdatalen = fn->size; mdata = kmalloc(fn->size, GFP_KERNEL); if (!mdata) { printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n"); return -ENOMEM; } ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen); if (ret) { printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret); kfree(mdata); return ret; } D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bites of symlink target\n", mdatalen)); } ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n", sizeof(ri)+ mdatalen, ret); goto out; } last_frag = frag_last(&f->fragtree); if (last_frag) /* Fetch the inode length from the fragtree rather then * from i_size since i_size may have not been updated yet */ ilen = last_frag->ofs + last_frag->size; else ilen = JFFS2_F_I_SIZE(f); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ri.isize = cpu_to_je32(ilen); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ri.offset = cpu_to_je32(0); ri.csize = cpu_to_je32(mdatalen); ri.dsize = cpu_to_je32(mdatalen); ri.compr = JFFS2_COMPR_NONE; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); ret = PTR_ERR(new_fn); goto out; } jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); f->metadata = new_fn; out: if (S_ISLNK(JFFS2_F_I_MODE(f))) kfree(mdata); return ret; } static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) { struct jffs2_full_dirent *new_fd; struct jffs2_raw_dirent rd; uint32_t alloclen, phys_ofs; int ret; rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd.nsize = strlen(fd->name); rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize); rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4)); rd.pino = cpu_to_je32(f->inocache->ino); rd.version = cpu_to_je32(++f->highest_version); rd.ino = cpu_to_je32(fd->ino); /* If the times on this inode were set by explicit utime() they can be different, so refrain from splatting them. */ if (JFFS2_F_I_MTIME(f) == JFFS2_F_I_CTIME(f)) rd.mctime = cpu_to_je32(JFFS2_F_I_MTIME(f)); else rd.mctime = cpu_to_je32(0); rd.type = fd->type; rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8)); rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize)); ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen, JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize)); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n", sizeof(rd)+rd.nsize, ret); return ret; } new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC); if (IS_ERR(new_fd)) { printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd)); return PTR_ERR(new_fd); } jffs2_add_fd_to_list(c, new_fd, &f->dents); return 0; } static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) { struct jffs2_full_dirent **fdp = &f->dents; int found = 0; /* On a medium where we can't actually mark nodes obsolete pernamently, such as NAND flash, we need to work out whether this deletion dirent is still needed to actively delete a 'real' dirent with the same name that's still somewhere else on the flash. */ if (!jffs2_can_mark_obsolete(c)) { struct jffs2_raw_dirent *rd; struct jffs2_raw_node_ref *raw; int ret; size_t retlen; int name_len = strlen(fd->name); uint32_t name_crc = crc32(0, fd->name, name_len); uint32_t rawlen = ref_totlen(c, jeb, fd->raw); rd = kmalloc(rawlen, GFP_KERNEL); if (!rd) return -ENOMEM; /* Prevent the erase code from nicking the obsolete node refs while we're looking at them. I really don't like this extra lock but can't see any alternative. Suggestions on a postcard to... */ down(&c->erase_free_sem); for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { /* We only care about obsolete ones */ if (!(ref_obsolete(raw))) continue; /* Any dirent with the same name is going to have the same length... */ if (ref_totlen(c, NULL, raw) != rawlen) continue; /* Doesn't matter if there's one in the same erase block. We're going to delete it too at the same time. */ if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset)) continue; D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); /* This is an obsolete node belonging to the same directory, and it's of the right length. We need to take a closer look...*/ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); if (ret) { printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw)); /* If we can't read it, we don't need to continue to obsolete it. Continue */ continue; } if (retlen != rawlen) { printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n", retlen, rawlen, ref_offset(raw)); continue; } if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) continue; /* If the name CRC doesn't match, skip */ if (je32_to_cpu(rd->name_crc) != name_crc) continue; /* If the name length doesn't match, or it's another deletion dirent, skip */ if (rd->nsize != name_len || !je32_to_cpu(rd->ino)) continue; /* OK, check the actual name now */ if (memcmp(rd->name, fd->name, name_len)) continue; /* OK. The name really does match. There really is still an older node on the flash which our deletion dirent obsoletes. So we have to write out a new deletion dirent to replace it */ up(&c->erase_free_sem); D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); kfree(rd); return jffs2_garbage_collect_dirent(c, jeb, f, fd); } up(&c->erase_free_sem); kfree(rd); } /* FIXME: If we're deleting a dirent which contains the current mtime and ctime, we should update the metadata node with those times accordingly */ /* No need for it any more. Just mark it obsolete and remove it from the list */ while (*fdp) { if ((*fdp) == fd) { found = 1; *fdp = fd->next; break; } fdp = &(*fdp)->next; } if (!found) { printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino); } jffs2_mark_node_obsolete(c, fd->raw); jffs2_free_full_dirent(fd); return 0; } static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { struct jffs2_raw_inode ri; struct jffs2_node_frag *frag; struct jffs2_full_dnode *new_fn; uint32_t alloclen, phys_ofs, ilen; int ret; D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n", f->inocache->ino, start, end)); memset(&ri, 0, sizeof(ri)); if(fn->frags > 1) { size_t readlen; uint32_t crc; /* It's partially obsoleted by a later write. So we have to write it out again with the _same_ version as before */ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri); if (readlen != sizeof(ri) || ret) { printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen); goto fill; } if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n", ref_offset(fn->raw), je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE); return -EIO; } if (je32_to_cpu(ri.totlen) != sizeof(ri)) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n", ref_offset(fn->raw), je32_to_cpu(ri.totlen), sizeof(ri)); return -EIO; } crc = crc32(0, &ri, sizeof(ri)-8); if (crc != je32_to_cpu(ri.node_crc)) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n", ref_offset(fn->raw), je32_to_cpu(ri.node_crc), crc); /* FIXME: We could possibly deal with this by writing new holes for each frag */ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", start, end, f->inocache->ino); goto fill; } if (ri.compr != JFFS2_COMPR_ZERO) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw)); printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", start, end, f->inocache->ino); goto fill; } } else { fill: ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.offset = cpu_to_je32(start); ri.dsize = cpu_to_je32(end - start); ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; } frag = frag_last(&f->fragtree); if (frag) /* Fetch the inode length from the fragtree rather then * from i_size since i_size may have not been updated yet */ ilen = frag->ofs + frag->size; else ilen = JFFS2_F_I_SIZE(f); ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ri.isize = cpu_to_je32(ilen); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ri.data_crc = cpu_to_je32(0); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n", sizeof(ri), ret); return ret; } new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); return PTR_ERR(new_fn); } if (je32_to_cpu(ri.version) == f->highest_version) { jffs2_add_full_dnode_to_inode(c, f, new_fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } return 0; } /* * We should only get here in the case where the node we are * replacing had more than one frag, so we kept the same version * number as before. (Except in case of error -- see 'goto fill;' * above.) */ D1(if(unlikely(fn->frags <= 1)) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n", fn->frags, je32_to_cpu(ri.version), f->highest_version, je32_to_cpu(ri.ino)); }); /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */ mark_ref_normal(new_fn->raw); for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); frag; frag = frag_next(frag)) { if (frag->ofs > fn->size + fn->ofs) break; if (frag->node == fn) { frag->node = new_fn; new_fn->frags++; fn->frags--; } } if (fn->frags) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Old node still has frags!\n"); BUG(); } if (!new_fn->frags) { printk(KERN_WARNING "jffs2_garbage_collect_hole: New node has no frags!\n"); BUG(); } jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); return 0; } static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; unsigned long pg; unsigned char *pg_ptr; memset(&ri, 0, sizeof(ri)); D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n", f->inocache->ino, start, end)); orig_end = end; orig_start = start; if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) { /* Attempt to do some merging. But only expand to cover logically adjacent frags if the block containing them is already considered to be dirty. Otherwise we end up with GC just going round in circles dirtying the nodes it already wrote out, especially on NAND where we have small eraseblocks and hence a much higher chance of nodes having to be split to cross boundaries. */ struct jffs2_node_frag *frag; uint32_t min, max; min = start & ~(PAGE_CACHE_SIZE-1); max = min + PAGE_CACHE_SIZE; frag = jffs2_lookup_node_frag(&f->fragtree, start); /* BUG_ON(!frag) but that'll happen anyway... */ BUG_ON(frag->ofs != start); /* First grow down... */ while((frag = frag_prev(frag)) && frag->ofs >= min) { /* If the previous frag doesn't even reach the beginning, there's excessive fragmentation. Just merge. */ if (frag->ofs > min) { D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n", frag->ofs, frag->ofs+frag->size)); start = frag->ofs; continue; } /* OK. This frag holds the first byte of the page. */ if (!frag->node || !frag->node->raw) { D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n", frag->ofs, frag->ofs+frag->size)); break; } else { /* OK, it's a frag which extends to the beginning of the page. Does it live in a block which is still considered clean? If so, don't obsolete it. If not, cover it anyway. */ struct jffs2_raw_node_ref *raw = frag->node->raw; struct jffs2_eraseblock *jeb; jeb = &c->blocks[raw->flash_offset / c->sector_size]; if (jeb == c->gcblock) { D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n", frag->ofs, frag->ofs+frag->size, ref_offset(raw))); start = frag->ofs; break; } if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); break; } D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); start = frag->ofs; break; } } /* ... then up */ /* Find last frag which is actually part of the node we're to GC. */ frag = jffs2_lookup_node_frag(&f->fragtree, end-1); while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) { /* If the previous frag doesn't even reach the beginning, there's lots of fragmentation. Just merge. */ if (frag->ofs+frag->size < max) { D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n", frag->ofs, frag->ofs+frag->size)); end = frag->ofs + frag->size; continue; } if (!frag->node || !frag->node->raw) { D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n", frag->ofs, frag->ofs+frag->size)); break; } else { /* OK, it's a frag which extends to the beginning of the page. Does it live in a block which is still considered clean? If so, don't obsolete it. If not, cover it anyway. */ struct jffs2_raw_node_ref *raw = frag->node->raw; struct jffs2_eraseblock *jeb; jeb = &c->blocks[raw->flash_offset / c->sector_size]; if (jeb == c->gcblock) { D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n", frag->ofs, frag->ofs+frag->size, ref_offset(raw))); end = frag->ofs + frag->size; break; } if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); break; } D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); end = frag->ofs + frag->size; break; } } D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", orig_start, orig_end, start, end)); D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size)); BUG_ON(end < orig_end); BUG_ON(start > orig_start); } /* First, use readpage() to read the appropriate page into the page cache */ /* Q: What happens if we actually try to GC the _same_ page for which commit_write() * triggered garbage collection in the first place? * A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the * page OK. We'll actually write it out again in commit_write, which is a little * suboptimal, but at least we're correct. */ pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); if (IS_ERR(pg_ptr)) { printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); return PTR_ERR(pg_ptr); } offset = start; while(offset < orig_end) { uint32_t datalen; uint32_t cdatalen; uint16_t comprtype = JFFS2_COMPR_NONE; ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n", sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret); break; } cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset); datalen = end - offset; writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ri.offset = cpu_to_je32(offset); ri.csize = cpu_to_je32(cdatalen); ri.dsize = cpu_to_je32(datalen); ri.compr = comprtype & 0xff; ri.usercompr = (comprtype >> 8) & 0xff; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); jffs2_free_comprbuf(comprbuf, writebuf); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); ret = PTR_ERR(new_fn); break; } ret = jffs2_add_full_dnode_to_inode(c, f, new_fn); offset += datalen; if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } } jffs2_gc_release_page(c, pg_ptr, &pg); return ret; } linux-2.6.17/fs/jffs2/.svn/text-base/compr_rubin.c.svn-base0000444000000000000000000001705710574207642022065 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by Arjan van de Ven * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $ * */ #include #include #include #include "compr_rubin.h" #include "histo_mips.h" #include "compr.h" static void init_rubin(struct rubin_state *rs, int div, int *bits) { int c; rs->q = 0; rs->p = (long) (2 * UPPER_BIT_RUBIN); rs->bit_number = (long) 0; rs->bit_divider = div; for (c=0; c<8; c++) rs->bits[c] = bits[c]; } static int encode(struct rubin_state *rs, long A, long B, int symbol) { long i0, i1; int ret; while ((rs->q >= UPPER_BIT_RUBIN) || ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) { rs->bit_number++; ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0); if (ret) return ret; rs->q &= LOWER_BITS_RUBIN; rs->q <<= 1; rs->p <<= 1; } i0 = A * rs->p / (A + B); if (i0 <= 0) { i0 = 1; } if (i0 >= rs->p) { i0 = rs->p - 1; } i1 = rs->p - i0; if (symbol == 0) rs->p = i0; else { rs->p = i1; rs->q += i0; } return 0; } static void end_rubin(struct rubin_state *rs) { int i; for (i = 0; i < RUBIN_REG_SIZE; i++) { pushbit(&rs->pp, (UPPER_BIT_RUBIN & rs->q) ? 1 : 0, 1); rs->q &= LOWER_BITS_RUBIN; rs->q <<= 1; } } static void init_decode(struct rubin_state *rs, int div, int *bits) { init_rubin(rs, div, bits); /* behalve lower */ rs->rec_q = 0; for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp))) ; } static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q) { register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN; unsigned long rec_q; int c, bits = 0; /* * First, work out how many bits we need from the input stream. * Note that we have already done the initial check on this * loop prior to calling this function. */ do { bits++; q &= lower_bits_rubin; q <<= 1; p <<= 1; } while ((q >= UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN)); rs->p = p; rs->q = q; rs->bit_number += bits; /* * Now get the bits. We really want this to be "get n bits". */ rec_q = rs->rec_q; do { c = pullbit(&rs->pp); rec_q &= lower_bits_rubin; rec_q <<= 1; rec_q += c; } while (--bits); rs->rec_q = rec_q; } static int decode(struct rubin_state *rs, long A, long B) { unsigned long p = rs->p, q = rs->q; long i0, threshold; int symbol; if (q >= UPPER_BIT_RUBIN || ((p + q) <= UPPER_BIT_RUBIN)) __do_decode(rs, p, q); i0 = A * rs->p / (A + B); if (i0 <= 0) { i0 = 1; } if (i0 >= rs->p) { i0 = rs->p - 1; } threshold = rs->q + i0; symbol = rs->rec_q >= threshold; if (rs->rec_q >= threshold) { rs->q += i0; i0 = rs->p - i0; } rs->p = i0; return symbol; } static int out_byte(struct rubin_state *rs, unsigned char byte) { int i, ret; struct rubin_state rs_copy; rs_copy = *rs; for (i=0;i<8;i++) { ret = encode(rs, rs->bit_divider-rs->bits[i],rs->bits[i],byte&1); if (ret) { /* Failed. Restore old state */ *rs = rs_copy; return ret; } byte=byte>>1; } return 0; } static int in_byte(struct rubin_state *rs) { int i, result = 0, bit_divider = rs->bit_divider; for (i = 0; i < 8; i++) result |= decode(rs, bit_divider - rs->bits[i], rs->bits[i]) << i; return result; } static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) { int outpos = 0; int pos=0; struct rubin_state rs; init_pushpull(&rs.pp, cpage_out, *dstlen * 8, 0, 32); init_rubin(&rs, bit_divider, bits); while (pos < (*sourcelen) && !out_byte(&rs, data_in[pos])) pos++; end_rubin(&rs); if (outpos > pos) { /* We failed */ return -1; } /* Tell the caller how much we managed to compress, * and how much space it took */ outpos = (pushedbits(&rs.pp)+7)/8; if (outpos >= pos) return -1; /* We didn't actually compress */ *sourcelen = pos; *dstlen = outpos; return 0; } #if 0 /* _compress returns the compressed size, -1 if bigger */ int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); } #endif static int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { int bits[8]; unsigned char histo[256]; int i; int ret; uint32_t mysrclen, mydstlen; mysrclen = *sourcelen; mydstlen = *dstlen - 8; if (*dstlen <= 12) return -1; memset(histo, 0, 256); for (i=0; i 255) bits[i] = 255; cpage_out[i] = bits[i]; } ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, &mydstlen); if (ret) return ret; /* Add back the 8 bytes we took for the probabilities */ mydstlen += 8; if (mysrclen <= mydstlen) { /* We compressed */ return -1; } *sourcelen = mysrclen; *dstlen = mydstlen; return 0; } static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, unsigned char *page_out, uint32_t srclen, uint32_t destlen) { int outpos = 0; struct rubin_state rs; init_pushpull(&rs.pp, cdata_in, srclen, 0, 0); init_decode(&rs, bit_divider, bits); while (outpos < destlen) { page_out[outpos++] = in_byte(&rs); } } static int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t sourcelen, uint32_t dstlen, void *model) { rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); return 0; } static int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t sourcelen, uint32_t dstlen, void *model) { int bits[8]; int c; for (c=0; c<8; c++) bits[c] = data_in[c]; rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); return 0; } static struct jffs2_compressor jffs2_rubinmips_comp = { .priority = JFFS2_RUBINMIPS_PRIORITY, .name = "rubinmips", .compr = JFFS2_COMPR_DYNRUBIN, .compress = NULL, /*&jffs2_rubinmips_compress,*/ .decompress = &jffs2_rubinmips_decompress, #ifdef JFFS2_RUBINMIPS_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int jffs2_rubinmips_init(void) { return jffs2_register_compressor(&jffs2_rubinmips_comp); } void jffs2_rubinmips_exit(void) { jffs2_unregister_compressor(&jffs2_rubinmips_comp); } static struct jffs2_compressor jffs2_dynrubin_comp = { .priority = JFFS2_DYNRUBIN_PRIORITY, .name = "dynrubin", .compr = JFFS2_COMPR_RUBINMIPS, .compress = jffs2_dynrubin_compress, .decompress = &jffs2_dynrubin_decompress, #ifdef JFFS2_DYNRUBIN_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int jffs2_dynrubin_init(void) { return jffs2_register_compressor(&jffs2_dynrubin_comp); } void jffs2_dynrubin_exit(void) { jffs2_unregister_compressor(&jffs2_dynrubin_comp); } linux-2.6.17/fs/jffs2/.svn/text-base/compr_rubin.h.svn-base0000444000000000000000000000101010574207642022050 0ustar rootroot/* Rubin encoder/decoder header */ /* work started at : aug 3, 1994 */ /* last modification : aug 15, 1994 */ /* $Id: compr_rubin.h,v 1.7 2005/11/07 11:14:38 gleixner Exp $ */ #include "pushpull.h" #define RUBIN_REG_SIZE 16 #define UPPER_BIT_RUBIN (((long) 1)<<(RUBIN_REG_SIZE-1)) #define LOWER_BITS_RUBIN ((((long) 1)<<(RUBIN_REG_SIZE-1))-1) struct rubin_state { unsigned long p; unsigned long q; unsigned long rec_q; long bit_number; struct pushpull pp; int bit_divider; int bits[8]; }; linux-2.6.17/fs/jffs2/.svn/text-base/compr_rtime.c.svn-base0000444000000000000000000000557110574207642022064 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by Arjan van de Ven * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ * * * Very simple lz77-ish encoder. * * Theory of operation: Both encoder and decoder have a list of "last * occurrences" for every possible source-value; after sending the * first source-byte, the second byte indicated the "run" length of * matches * * The algorithm is intended to only send "whole bytes", no bit-messing. * */ #include #include #include #include #include #include "compr.h" /* _compress returns the compressed size, -1 if bigger */ static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { short positions[256]; int outpos = 0; int pos=0; memset(positions,0,sizeof(positions)); while (pos < (*sourcelen) && outpos <= (*dstlen)-2) { int backpos, runlen=0; unsigned char value; value = data_in[pos]; cpage_out[outpos++] = data_in[pos++]; backpos = positions[value]; positions[value]=pos; while ((backpos < pos) && (pos < (*sourcelen)) && (data_in[pos]==data_in[backpos++]) && (runlen<255)) { pos++; runlen++; } cpage_out[outpos++] = runlen; } if (outpos >= pos) { /* We failed */ return -1; } /* Tell the caller how much we managed to compress, and how much space it took */ *sourcelen = pos; *dstlen = outpos; return 0; } static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen, void *model) { short positions[256]; int outpos = 0; int pos=0; memset(positions,0,sizeof(positions)); while (outpos= outpos) { while(repeat) { cpage_out[outpos++] = cpage_out[backoffs++]; repeat--; } } else { memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat); outpos+=repeat; } } } return 0; } static struct jffs2_compressor jffs2_rtime_comp = { .priority = JFFS2_RTIME_PRIORITY, .name = "rtime", .compr = JFFS2_COMPR_RTIME, .compress = &jffs2_rtime_compress, .decompress = &jffs2_rtime_decompress, #ifdef JFFS2_RTIME_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int jffs2_rtime_init(void) { return jffs2_register_compressor(&jffs2_rtime_comp); } void jffs2_rtime_exit(void) { jffs2_unregister_compressor(&jffs2_rtime_comp); } linux-2.6.17/fs/jffs2/.svn/text-base/nodemgmt.c.svn-base0000444000000000000000000006543510574207642021363 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: nodemgmt.c,v 1.127 2005/09/20 15:49:12 dedekind Exp $ * */ #include #include #include #include #include /* For cond_resched() */ #include "nodelist.h" #include "debug.h" /** * jffs2_reserve_space - request physical space to write nodes to flash * @c: superblock info * @minsize: Minimum acceptable size of allocation * @ofs: Returned value of node offset * @len: Returned value of allocation length * @prio: Allocation type - ALLOC_{NORMAL,DELETION} * * Requests a block of physical space on the flash. Returns zero for success * and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC * or other error if appropriate. * * If it returns zero, jffs2_reserve_space() also downs the per-filesystem * allocation semaphore, to prevent more than one allocation from being * active at any time. The semaphore is later released by jffs2_commit_allocation() * * jffs2_reserve_space() may trigger garbage collection in order to make room * for the requested allocation. */ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize); int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio, uint32_t sumsize) { int ret = -EAGAIN; int blocksneeded = c->resv_blocks_write; /* align it */ minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); down(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); spin_lock(&c->erase_completion_lock); /* this needs a little more thought (true :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { int ret; uint32_t dirty, avail; /* calculate real dirty size * dirty_size contains blocks on erase_pending_list * those blocks are counted in c->nr_erasing_blocks. * If one block is actually erased, it is not longer counted as dirty_space * but it is counted in c->nr_erasing_blocks, so we add it and subtract it * with c->nr_erasing_blocks * c->sector_size again. * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks * This helps us to force gc and pick eventually a clean block to spread the load. * We add unchecked_size here, as we hopefully will find some space to use. * This will affect the sum only once, as gc first finishes checking * of nodes. */ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; if (dirty < c->nospc_dirty_size) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", dirty, c->unchecked_size, c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } /* Calc possibly available space. Possibly available means that we * don't know, if unchecked size contains obsoleted nodes, which could give us some * more usable space. This will affect the sum only once, as gc first finishes checking * of nodes. + Return -ENOSPC, if the maximum possibly available space is less or equal than * blocksneeded * sector_size. * This blocks endless gc looping on a filesystem, which is nearly full, even if * the check above passes. */ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; if ( (avail / c->sector_size) <= blocksneeded) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", avail, blocksneeded * c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } up(&c->alloc_sem); D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); if (ret) return ret; cond_resched(); if (signal_pending(current)) return -EINTR; down(&c->alloc_sem); spin_lock(&c->erase_completion_lock); } ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); if (ret) up(&c->alloc_sem); return ret; } int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize) { int ret = -EAGAIN; minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize)); spin_lock(&c->erase_completion_lock); while(ret == -EAGAIN) { ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); return ret; } /* Classify nextblock (clean, dirty of verydirty) and force to select an other one */ static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { /* Check, if we have a dirty block now, or if it was dirty already */ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->dirty_size += jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->very_dirty_list); } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->dirty_list); } } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->clean_list); } c->nextblock = NULL; } /* Select a new jeb for nextblock */ static int jffs2_find_nextblock(struct jffs2_sb_info *c) { struct list_head *next; /* Take the next block off the 'free' list */ if (list_empty(&c->free_list)) { if (!c->nr_erasing_blocks && !list_empty(&c->erasable_list)) { struct jffs2_eraseblock *ejeb; ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); list_del(&ejeb->list); list_add_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } if (!c->nr_erasing_blocks && !list_empty(&c->erasable_pending_wbuf_list)) { D1(printk(KERN_DEBUG "jffs2_find_nextblock: Flushing write buffer\n")); /* c->nextblock is NULL, no update to c->nextblock allowed */ spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); /* Have another go. It'll be on the erasable_list now */ return -EAGAIN; } if (!c->nr_erasing_blocks) { /* Ouch. We're in GC, or we wouldn't have got here. And there's no space left. At all. */ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); return -ENOSPC; } spin_unlock(&c->erase_completion_lock); /* Don't wait for it; just erase one right now */ jffs2_erase_pending_blocks(c, 1); spin_lock(&c->erase_completion_lock); /* An erase may have failed, decreasing the amount of free space available. So we must restart from the beginning */ return -EAGAIN; } next = c->free_list.next; list_del(next); c->nextblock = list_entry(next, struct jffs2_eraseblock, list); c->nr_free_blocks--; jffs2_sum_reset_collected(c->summary); /* reset collected summary */ D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset)); return 0; } /* Called with alloc sem _and_ erase_completion_lock */ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize) { struct jffs2_eraseblock *jeb = c->nextblock; uint32_t reserved_size; /* for summary information at the end of the jeb */ int ret; restart: reserved_size = 0; if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) { /* NOSUM_SIZE means not to generate summary */ if (jeb) { reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); dbg_summary("minsize=%d , jeb->free=%d ," "summary->size=%d , sumsize=%d\n", minsize, jeb->free_size, c->summary->sum_size, sumsize); } /* Is there enough space for writing out the current node, or we have to write out summary information now, close this jeb and select new nextblock? */ if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) { /* Has summary been disabled for this jeb? */ if (jffs2_sum_is_disabled(c->summary)) { sumsize = JFFS2_SUMMARY_NOSUM_SIZE; goto restart; } /* Writing out the collected summary information */ dbg_summary("generating summary for 0x%08x.\n", jeb->offset); ret = jffs2_sum_write_sumnode(c); if (ret) return ret; if (jffs2_sum_is_disabled(c->summary)) { /* jffs2_write_sumnode() couldn't write out the summary information diabling summary for this jeb and free the collected information */ sumsize = JFFS2_SUMMARY_NOSUM_SIZE; goto restart; } jffs2_close_nextblock(c, jeb); jeb = NULL; /* keep always valid value in reserved_size */ reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); } } else { if (jeb && minsize > jeb->free_size) { /* Skip the end of this block and file it as having some dirty space */ /* If there's a pending write to it, flush now */ if (jffs2_wbuf_dirty(c)) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); jeb = c->nextblock; goto restart; } c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->wasted_size += jeb->free_size; jeb->free_size = 0; jffs2_close_nextblock(c, jeb); jeb = NULL; } } if (!jeb) { ret = jffs2_find_nextblock(c); if (ret) return ret; jeb = c->nextblock; if (jeb->free_size != c->sector_size - c->cleanmarker_size) { printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); goto restart; } } /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has enough space */ *ofs = jeb->offset + (c->sector_size - jeb->free_size); *len = jeb->free_size - reserved_size; if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && !jeb->first_node->next_in_ino) { /* Only node in it beforehand was a CLEANMARKER node (we think). So mark it obsolete now that there's going to be another node in the block. This will reduce used_size to zero but We've already set c->nextblock so that jffs2_mark_node_obsolete() won't try to refile it to the dirty_list. */ spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, jeb->first_node); spin_lock(&c->erase_completion_lock); } D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); return 0; } /** * jffs2_add_physical_node_ref - add a physical node reference to the list * @c: superblock info * @new: new node reference to add * @len: length of this physical node * @dirty: dirty flag for new node * * Should only be used to report nodes for which space has been allocated * by jffs2_reserve_space. * * Must be called with the alloc_sem held. */ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) { struct jffs2_eraseblock *jeb; uint32_t len; jeb = &c->blocks[new->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, new); D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); #if 1 /* we could get some obsolete nodes after nextblock was refiled in wbuf.c */ if ((c->nextblock || !ref_obsolete(new)) &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) { printk(KERN_WARNING "argh. node added in wrong place\n"); jffs2_free_raw_node_ref(new); return -EINVAL; } #endif spin_lock(&c->erase_completion_lock); if (!jeb->first_node) jeb->first_node = new; if (jeb->last_node) jeb->last_node->next_phys = new; jeb->last_node = new; jeb->free_size -= len; c->free_size -= len; if (ref_obsolete(new)) { jeb->dirty_size += len; c->dirty_size += len; } else { jeb->used_size += len; c->used_size += len; } if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) { /* If it lives on the dirty_list, jffs2_reserve_space will put it there */ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); if (jffs2_wbuf_dirty(c)) { /* Flush the last write in the block if it's outstanding */ spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); } list_add_tail(&jeb->list, &c->clean_list); c->nextblock = NULL; } jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); return 0; } void jffs2_complete_reservation(struct jffs2_sb_info *c) { D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n")); jffs2_garbage_collect_trigger(c); up(&c->alloc_sem); } static inline int on_list(struct list_head *obj, struct list_head *head) { struct list_head *this; list_for_each(this, head) { if (this == obj) { D1(printk("%p is on list at %p\n", obj, head)); return 1; } } return 0; } void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref) { struct jffs2_eraseblock *jeb; int blocknr; struct jffs2_unknown_node n; int ret, addedsize; size_t retlen; if(!ref) { printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n"); return; } if (ref_obsolete(ref)) { D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref))); return; } blocknr = ref->flash_offset / c->sector_size; if (blocknr >= c->nr_blocks) { printk(KERN_NOTICE "raw node at 0x%08x is off the end of device!\n", ref->flash_offset); BUG(); } jeb = &c->blocks[blocknr]; if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) && !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) { /* Hm. This may confuse static lock analysis. If any of the above three conditions is false, we're going to return from this function without actually obliterating any nodes or freeing any jffs2_raw_node_refs. So we don't need to stop erases from happening, or protect against people holding an obsolete jffs2_raw_node_ref without the erase_completion_lock. */ down(&c->erase_free_sem); } spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); jeb->unchecked_size -= ref_totlen(c, jeb, ref); c->unchecked_size -= ref_totlen(c, jeb, ref); } else { D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); jeb->used_size -= ref_totlen(c, jeb, ref); c->used_size -= ref_totlen(c, jeb, ref); } // Take care, that wasted size is taken into concern if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { D1(printk(KERN_DEBUG "Dirtying\n")); addedsize = ref_totlen(c, jeb, ref); jeb->dirty_size += ref_totlen(c, jeb, ref); c->dirty_size += ref_totlen(c, jeb, ref); /* Convert wasted space to dirty, if not a bad block */ if (jeb->wasted_size) { if (on_list(&jeb->list, &c->bad_used_list)) { D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n", jeb->offset)); addedsize = 0; /* To fool the refiling code later */ } else { D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n", jeb->wasted_size, jeb->offset)); addedsize += jeb->wasted_size; jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; } } } else { D1(printk(KERN_DEBUG "Wasting\n")); addedsize = 0; jeb->wasted_size += ref_totlen(c, jeb, ref); c->wasted_size += ref_totlen(c, jeb, ref); } ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; jffs2_dbg_acct_sanity_check_nolock(c, jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); if (c->flags & JFFS2_SB_FLAG_SCANNING) { /* Flash scanning is in progress. Don't muck about with the block lists because they're not ready yet, and don't actually obliterate nodes that look obsolete. If they weren't marked obsolete on the flash at the time they _became_ obsolete, there was probably a reason for that. */ spin_unlock(&c->erase_completion_lock); /* We didn't lock the erase_free_sem */ return; } if (jeb == c->nextblock) { D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); } else if (!jeb->used_size && !jeb->unchecked_size) { if (jeb == c->gcblock) { D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset)); c->gcblock = NULL; } else { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset)); list_del(&jeb->list); } if (jffs2_wbuf_dirty(c)) { D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); } else { if (jiffies & 127) { /* Most of the time, we just erase it immediately. Otherwise we spend ages scanning it on mount, etc. */ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); list_add_tail(&jeb->list, &c->erasable_list); } } D1(printk(KERN_DEBUG "Done OK\n")); } else if (jeb == c->gcblock) { D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset)); } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset)); list_del(&jeb->list); D1(printk(KERN_DEBUG "...and adding to dirty_list\n")); list_add_tail(&jeb->list, &c->dirty_list); } else if (VERYDIRTY(c, jeb->dirty_size) && !VERYDIRTY(c, jeb->dirty_size - addedsize)) { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); list_del(&jeb->list); D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); list_add_tail(&jeb->list, &c->very_dirty_list); } else { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); } spin_unlock(&c->erase_completion_lock); if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) || (c->flags & JFFS2_SB_FLAG_BUILDING)) { /* We didn't lock the erase_free_sem */ return; } /* The erase_free_sem is locked, and has been since before we marked the node obsolete and potentially put its eraseblock onto the erase_pending_list. Thus, we know that the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet by jffs2_free_all_node_refs() in erase.c. Which is nice. */ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); goto out_erase_sem; } if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); goto out_erase_sem; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); goto out_erase_sem; } /* XXX FIXME: This is ugly now */ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); goto out_erase_sem; } /* Nodes which have been marked obsolete no longer need to be associated with any inode. Remove them from the per-inode list. Note we can't do this for NAND at the moment because we need obsolete dirent nodes to stay on the lists, because of the horridness in jffs2_garbage_collect_deletion_dirent(). Also because we delete the inocache, and on NAND we need that to stay around until all the nodes are actually erased, in order to stop us from giving the same inode number to another newly created inode. */ if (ref->next_in_ino) { struct jffs2_inode_cache *ic; struct jffs2_raw_node_ref **p; spin_lock(&c->erase_completion_lock); ic = jffs2_raw_ref_to_ic(ref); for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino)) ; *p = ref->next_in_ino; ref->next_in_ino = NULL; if (ic->nodes == (void *)ic && ic->nlink == 0) jffs2_del_ino_cache(c, ic); spin_unlock(&c->erase_completion_lock); } /* Merge with the next node in the physical list, if there is one and if it's also obsolete and if it doesn't belong to any inode */ if (ref->next_phys && ref_obsolete(ref->next_phys) && !ref->next_phys->next_in_ino) { struct jffs2_raw_node_ref *n = ref->next_phys; spin_lock(&c->erase_completion_lock); ref->__totlen += n->__totlen; ref->next_phys = n->next_phys; if (jeb->last_node == n) jeb->last_node = ref; if (jeb->gc_node == n) { /* gc will be happy continuing gc on this node */ jeb->gc_node=ref; } spin_unlock(&c->erase_completion_lock); jffs2_free_raw_node_ref(n); } /* Also merge with the previous node in the list, if there is one and that one is obsolete */ if (ref != jeb->first_node ) { struct jffs2_raw_node_ref *p = jeb->first_node; spin_lock(&c->erase_completion_lock); while (p->next_phys != ref) p = p->next_phys; if (ref_obsolete(p) && !ref->next_in_ino) { p->__totlen += ref->__totlen; if (jeb->last_node == ref) { jeb->last_node = p; } if (jeb->gc_node == ref) { /* gc will be happy continuing gc on this node */ jeb->gc_node=p; } p->next_phys = ref->next_phys; jffs2_free_raw_node_ref(ref); } spin_unlock(&c->erase_completion_lock); } out_erase_sem: up(&c->erase_free_sem); } int jffs2_thread_should_wake(struct jffs2_sb_info *c) { int ret = 0; uint32_t dirty; if (c->unchecked_size) { D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", c->unchecked_size, c->checked_ino)); return 1; } /* dirty_size contains blocks on erase_pending_list * those blocks are counted in c->nr_erasing_blocks. * If one block is actually erased, it is not longer counted as dirty_space * but it is counted in c->nr_erasing_blocks, so we add it and subtract it * with c->nr_erasing_blocks * c->sector_size again. * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks * This helps us to force gc and pick eventually a clean block to spread the load. */ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && (dirty > c->nospc_dirty_size)) ret = 1; D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); return ret; } linux-2.6.17/fs/jffs2/.svn/text-base/pushpull.h.svn-base0000444000000000000000000000243610574207642021422 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $ * */ #ifndef __PUSHPULL_H__ #define __PUSHPULL_H__ #include struct pushpull { unsigned char *buf; unsigned int buflen; unsigned int ofs; unsigned int reserve; }; static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) { pp->buf = buf; pp->buflen = buflen; pp->ofs = ofs; pp->reserve = reserve; } static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) { if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { return -ENOSPC; } if (bit) { pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); } else { pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); } pp->ofs++; return 0; } static inline int pushedbits(struct pushpull *pp) { return pp->ofs; } static inline int pullbit(struct pushpull *pp) { int bit; bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1; pp->ofs++; return bit; } static inline int pulledbits(struct pushpull *pp) { return pp->ofs; } #endif /* __PUSHPULL_H__ */ linux-2.6.17/fs/jffs2/.svn/text-base/os-linux.h.svn-base0000444000000000000000000001745510574207642021333 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2002-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: os-linux.h,v 1.64 2005/09/30 13:59:13 dedekind Exp $ * */ #ifndef __JFFS2_OS_LINUX_H__ #define __JFFS2_OS_LINUX_H__ /* JFFS2 uses Linux mode bits natively -- no need for conversion */ #define os_to_jffs2_mode(x) (x) #define jffs2_to_os_mode(x) (x) struct kstatfs; struct kvec; #define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) #define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) #define JFFS2_SB_INFO(sb) (sb->s_fs_info) #define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv) #define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size) #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) #define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f))) #define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f))) #define ITIME(sec) ((struct timespec){sec, 0}) #define I_SEC(tv) ((tv).tv_sec) #define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec) #define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec) #define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec) #define sleep_on_spinunlock(wq, s) \ do { \ DECLARE_WAITQUEUE(__wait, current); \ add_wait_queue((wq), &__wait); \ set_current_state(TASK_UNINTERRUPTIBLE); \ spin_unlock(s); \ schedule(); \ remove_wait_queue((wq), &__wait); \ } while(0) static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) { f->highest_version = 0; f->fragtree = RB_ROOT; f->metadata = NULL; f->dents = NULL; f->target = NULL; f->flags = 0; f->usercompr = 0; } #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) #define SECTOR_ADDR(x) ( (((unsigned long)(x) / c->sector_size) * c->sector_size) ) #ifndef CONFIG_JFFS2_FS_WRITEBUFFER #ifdef CONFIG_JFFS2_SUMMARY #define jffs2_can_mark_obsolete(c) (0) #else #define jffs2_can_mark_obsolete(c) (1) #endif #define jffs2_is_writebuffered(c) (0) #define jffs2_cleanmarker_oob(c) (0) #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) #define jffs2_flash_write(c, ofs, len, retlen, buf) jffs2_flash_direct_write(c, ofs, len, retlen, buf) #define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flush_wbuf_pad(c) ({ do{} while(0); (void)(c), 0; }) #define jffs2_flush_wbuf_gc(c, i) ({ do{} while(0); (void)(c), (void) i, 0; }) #define jffs2_write_nand_badblock(c,jeb,bad_offset) (1) #define jffs2_nand_flash_setup(c) (0) #define jffs2_nand_flash_cleanup(c) do {} while(0) #define jffs2_wbuf_dirty(c) (0) #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) #define jffs2_wbuf_timeout NULL #define jffs2_wbuf_process NULL #define jffs2_nor_ecc(c) (0) #define jffs2_dataflash(c) (0) #define jffs2_nor_wbuf_flash(c) (0) #define jffs2_nor_ecc_flash_setup(c) (0) #define jffs2_nor_ecc_flash_cleanup(c) do {} while (0) #define jffs2_dataflash_setup(c) (0) #define jffs2_dataflash_cleanup(c) do {} while (0) #define jffs2_nor_wbuf_flash_setup(c) (0) #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0) #else /* NAND and/or ECC'd NOR support present */ #define jffs2_is_writebuffered(c) (c->wbuf != NULL) #ifdef CONFIG_JFFS2_SUMMARY #define jffs2_can_mark_obsolete(c) (0) #else #define jffs2_can_mark_obsolete(c) \ ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_ECC|MTD_PROGRAM_REGIONS))) || \ c->mtd->type == MTD_RAM) #endif #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len) /* wbuf.c */ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino); int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf); int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); void jffs2_wbuf_timeout(unsigned long data); void jffs2_wbuf_process(void *data); int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_nand_flash_setup(struct jffs2_sb_info *c); void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); #define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC)) int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c); void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c); #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH) int jffs2_dataflash_setup(struct jffs2_sb_info *c); void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); #define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_PROGRAM_REGIONS)) int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); #endif /* WRITEBUFFER */ /* erase.c */ static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) { OFNI_BS_2SFFJ(c)->s_dirt = 1; } /* background.c */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); /* dir.c */ extern const struct file_operations jffs2_dir_operations; extern struct inode_operations jffs2_dir_inode_operations; /* file.c */ extern const struct file_operations jffs2_file_operations; extern struct inode_operations jffs2_file_inode_operations; extern struct address_space_operations jffs2_file_address_operations; int jffs2_fsync(struct file *, struct dentry *, int); int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); /* ioctl.c */ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* symlink.c */ extern struct inode_operations jffs2_symlink_inode_operations; /* fs.c */ int jffs2_setattr (struct dentry *, struct iattr *); void jffs2_read_inode (struct inode *); void jffs2_clear_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode); struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri); int jffs2_statfs (struct super_block *, struct kstatfs *); void jffs2_write_super (struct super_block *); int jffs2_remount_fs (struct super_block *, int *, char *); int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink); unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned long offset, unsigned long *priv); void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv); void jffs2_flash_cleanup(struct jffs2_sb_info *c); /* writev.c */ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); #endif /* __JFFS2_OS_LINUX_H__ */ linux-2.6.17/fs/jffs2/.svn/text-base/dir.c.svn-base0000444000000000000000000005301610574207642020317 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: dir.c,v 1.90 2005/11/07 11:14:39 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include #include "nodelist.h" static int jffs2_readdir (struct file *, void *, filldir_t); static int jffs2_create (struct inode *,struct dentry *,int, struct nameidata *); static struct dentry *jffs2_lookup (struct inode *,struct dentry *, struct nameidata *); static int jffs2_link (struct dentry *,struct inode *,struct dentry *); static int jffs2_unlink (struct inode *,struct dentry *); static int jffs2_symlink (struct inode *,struct dentry *,const char *); static int jffs2_mkdir (struct inode *,struct dentry *,int); static int jffs2_rmdir (struct inode *,struct dentry *); static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *); const struct file_operations jffs2_dir_operations = { .read = generic_read_dir, .readdir = jffs2_readdir, .ioctl = jffs2_ioctl, .fsync = jffs2_fsync }; struct inode_operations jffs2_dir_inode_operations = { .create = jffs2_create, .lookup = jffs2_lookup, .link = jffs2_link, .unlink = jffs2_unlink, .symlink = jffs2_symlink, .mkdir = jffs2_mkdir, .rmdir = jffs2_rmdir, .mknod = jffs2_mknod, .rename = jffs2_rename, .setattr = jffs2_setattr, }; /***********************************************************************/ /* We keep the dirent list sorted in increasing order of name hash, and we use the same hash function as the dentries. Makes this nice and simple */ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, struct nameidata *nd) { struct jffs2_inode_info *dir_f; struct jffs2_sb_info *c; struct jffs2_full_dirent *fd = NULL, *fd_list; uint32_t ino = 0; struct inode *inode = NULL; D1(printk(KERN_DEBUG "jffs2_lookup()\n")); dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); down(&dir_f->sem); /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) { if (fd_list->nhash == target->d_name.hash && (!fd || fd_list->version > fd->version) && strlen(fd_list->name) == target->d_name.len && !strncmp(fd_list->name, target->d_name.name, target->d_name.len)) { fd = fd_list; } } if (fd) ino = fd->ino; up(&dir_f->sem); if (ino) { inode = iget(dir_i->i_sb, ino); if (!inode) { printk(KERN_WARNING "iget() failed for ino #%u\n", ino); return (ERR_PTR(-EIO)); } } d_add(target, inode); return NULL; } /***********************************************************************/ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct jffs2_inode_info *f; struct jffs2_sb_info *c; struct inode *inode = filp->f_dentry->d_inode; struct jffs2_full_dirent *fd; unsigned long offset, curofs; D1(printk(KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", filp->f_dentry->d_inode->i_ino)); f = JFFS2_INODE_INFO(inode); c = JFFS2_SB_INFO(inode->i_sb); offset = filp->f_pos; if (offset == 0) { D1(printk(KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino)); if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) goto out; offset++; } if (offset == 1) { unsigned long pino = parent_ino(filp->f_dentry); D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino)); if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0) goto out; offset++; } curofs=1; down(&f->sem); for (fd = f->dents; fd; fd = fd->next) { curofs++; /* First loop: curofs = 2; offset = 2 */ if (curofs < offset) { D2(printk(KERN_DEBUG "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n", fd->name, fd->ino, fd->type, curofs, offset)); continue; } if (!fd->ino) { D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name)); offset++; continue; } D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type)); if (filldir(dirent, fd->name, strlen(fd->name), offset, fd->ino, fd->type) < 0) break; offset++; } up(&f->sem); out: filp->f_pos = offset; return 0; } /***********************************************************************/ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, struct nameidata *nd) { struct jffs2_raw_inode *ri; struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; int ret; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); D1(printk(KERN_DEBUG "jffs2_create()\n")); inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); jffs2_free_raw_inode(ri); return PTR_ERR(inode); } inode->i_op = &jffs2_file_inode_operations; inode->i_fop = &jffs2_file_operations; inode->i_mapping->a_ops = &jffs2_file_address_operations; inode->i_mapping->nrpages = 0; f = JFFS2_INODE_INFO(inode); dir_f = JFFS2_INODE_INFO(dir_i); ret = jffs2_do_create(c, dir_f, f, ri, dentry->d_name.name, dentry->d_name.len); if (ret) { make_bad_inode(inode); iput(inode); jffs2_free_raw_inode(ri); return ret; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); jffs2_free_raw_inode(ri); d_instantiate(dentry, inode); D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); return 0; } /***********************************************************************/ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry) { struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb); struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode); int ret; uint32_t now = get_seconds(); ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, dentry->d_name.len, dead_f, now); if (dead_f->inocache) dentry->d_inode->i_nlink = dead_f->inocache->nlink; if (!ret) dir_i->i_mtime = dir_i->i_ctime = ITIME(now); return ret; } /***********************************************************************/ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) { struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); int ret; uint8_t type; uint32_t now; /* Don't let people make hard links to bad inodes. */ if (!f->inocache) return -EIO; if (S_ISDIR(old_dentry->d_inode->i_mode)) return -EPERM; /* XXX: This is ugly */ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; now = get_seconds(); ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now); if (!ret) { down(&f->sem); old_dentry->d_inode->i_nlink = ++f->inocache->nlink; up(&f->sem); d_instantiate(dentry, old_dentry->d_inode); dir_i->i_mtime = dir_i->i_ctime = ITIME(now); atomic_inc(&old_dentry->d_inode->i_count); } return ret; } /***********************************************************************/ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char *target) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen, phys_ofs; int ret, targetlen = strlen(target); /* FIXME: If you care. We'd need to use frags for the target if it grows much more than this */ if (targetlen > 254) return -EINVAL; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_symlink_inode_operations; f = JFFS2_INODE_INFO(inode); inode->i_size = targetlen; ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->compr = JFFS2_COMPR_NONE; ri->data_crc = cpu_to_je32(crc32(0, target, targetlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); } /* We use f->target field to store the target path. */ f->target = kmalloc(targetlen + 1, GFP_KERNEL); if (!f->target) { printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } memcpy(f->target, target, targetlen + 1); D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target)); /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } dir_f = JFFS2_INODE_INFO(dir_i); down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = DT_LNK; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); return 0; } static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen, phys_ofs; int ret; mode |= S_IFDIR; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_dir_inode_operations; inode->i_fop = &jffs2_dir_operations; /* Directories get nlink 2 at start */ inode->i_nlink = 2; f = JFFS2_INODE_INFO(inode); ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } dir_f = JFFS2_INODE_INFO(dir_i); down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = DT_DIR; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); dir_i->i_nlink++; jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); return 0; } static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); struct jffs2_full_dirent *fd; int ret; for (fd = f->dents ; fd; fd = fd->next) { if (fd->ino) return -ENOTEMPTY; } ret = jffs2_unlink(dir_i, dentry); if (!ret) dir_i->i_nlink--; return ret; } static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; jint16_t dev; int devlen = 0; uint32_t alloclen, phys_ofs; int ret; if (!old_valid_dev(rdev)) return -EINVAL; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); if (S_ISBLK(mode) || S_ISCHR(mode)) { dev = cpu_to_je16(old_encode_dev(rdev)); devlen = sizeof(dev); } /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_file_inode_operations; init_special_inode(inode, inode->i_mode, rdev); f = JFFS2_INODE_INFO(inode); ri->dsize = ri->csize = cpu_to_je32(devlen); ri->totlen = cpu_to_je32(sizeof(*ri) + devlen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->compr = JFFS2_COMPR_NONE; ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; } dir_f = JFFS2_INODE_INFO(dir_i); down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; /* XXX: This is ugly. */ rd->type = (mode & S_IFMT) >> 12; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); return 0; } static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, struct inode *new_dir_i, struct dentry *new_dentry) { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); struct jffs2_inode_info *victim_f = NULL; uint8_t type; uint32_t now; /* The VFS will check for us and prevent trying to rename a * file over a directory and vice versa, but if it's a directory, * the VFS can't check whether the victim is empty. The filesystem * needs to do that for itself. */ if (new_dentry->d_inode) { victim_f = JFFS2_INODE_INFO(new_dentry->d_inode); if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct jffs2_full_dirent *fd; down(&victim_f->sem); for (fd = victim_f->dents; fd; fd = fd->next) { if (fd->ino) { up(&victim_f->sem); return -ENOTEMPTY; } } up(&victim_f->sem); } } /* XXX: We probably ought to alloc enough space for both nodes at the same time. Writing the new link, then getting -ENOSPC, is quite bad :) */ /* Make a hard link */ /* XXX: This is ugly */ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; now = get_seconds(); ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), old_dentry->d_inode->i_ino, type, new_dentry->d_name.name, new_dentry->d_name.len, now); if (ret) return ret; if (victim_f) { /* There was a victim. Kill it off nicely */ new_dentry->d_inode->i_nlink--; /* Don't oops if the victim was a dirent pointing to an inode which didn't exist. */ if (victim_f->inocache) { down(&victim_f->sem); victim_f->inocache->nlink--; up(&victim_f->sem); } } /* If it was a directory we moved, and there was no victim, increase i_nlink on its new parent */ if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f) new_dir_i->i_nlink++; /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); /* We don't touch inode->i_nlink */ if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); down(&f->sem); old_dentry->d_inode->i_nlink++; if (f->inocache) f->inocache->nlink++; up(&f->sem); printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); /* Might as well let the VFS know */ d_instantiate(new_dentry, old_dentry->d_inode); atomic_inc(&old_dentry->d_inode->i_count); new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now); return ret; } if (S_ISDIR(old_dentry->d_inode->i_mode)) old_dir_i->i_nlink--; new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); return 0; } linux-2.6.17/fs/jffs2/.svn/text-base/summary.c.svn-base0000444000000000000000000004535010574207642021240 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: summary.c,v 1.4 2005/09/26 11:37:21 havasi Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" #include "debug.h" int jffs2_sum_init(struct jffs2_sb_info *c) { c->summary = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!c->summary) { JFFS2_WARNING("Can't allocate memory for summary information!\n"); return -ENOMEM; } memset(c->summary, 0, sizeof(struct jffs2_summary)); c->summary->sum_buf = vmalloc(c->sector_size); if (!c->summary->sum_buf) { JFFS2_WARNING("Can't allocate buffer for writing out summary information!\n"); kfree(c->summary); return -ENOMEM; } dbg_summary("returned succesfully\n"); return 0; } void jffs2_sum_exit(struct jffs2_sb_info *c) { dbg_summary("called\n"); jffs2_sum_disable_collecting(c->summary); vfree(c->summary->sum_buf); c->summary->sum_buf = NULL; kfree(c->summary); c->summary = NULL; } static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item) { if (!s->sum_list_head) s->sum_list_head = (union jffs2_sum_mem *) item; if (s->sum_list_tail) s->sum_list_tail->u.next = (union jffs2_sum_mem *) item; s->sum_list_tail = (union jffs2_sum_mem *) item; switch (je16_to_cpu(item->u.nodetype)) { case JFFS2_NODETYPE_INODE: s->sum_size += JFFS2_SUMMARY_INODE_SIZE; s->sum_num++; dbg_summary("inode (%u) added to summary\n", je32_to_cpu(item->i.inode)); break; case JFFS2_NODETYPE_DIRENT: s->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize); s->sum_num++; dbg_summary("dirent (%u) added to summary\n", je32_to_cpu(item->d.ino)); break; default: JFFS2_WARNING("UNKNOWN node type %u\n", je16_to_cpu(item->u.nodetype)); return 1; } return 0; } /* The following 3 functions are called from scan.c to collect summary info for not closed jeb */ int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size) { dbg_summary("called with %u\n", size); s->sum_padded += size; return 0; } int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs) { struct jffs2_sum_inode_mem *temp = kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL); if (!temp) return -ENOMEM; temp->nodetype = ri->nodetype; temp->inode = ri->ino; temp->version = ri->version; temp->offset = cpu_to_je32(ofs); /* relative offset from the begining of the jeb */ temp->totlen = ri->totlen; temp->next = NULL; return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); } int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs) { struct jffs2_sum_dirent_mem *temp = kmalloc(sizeof(struct jffs2_sum_dirent_mem) + rd->nsize, GFP_KERNEL); if (!temp) return -ENOMEM; temp->nodetype = rd->nodetype; temp->totlen = rd->totlen; temp->offset = cpu_to_je32(ofs); /* relative from the begining of the jeb */ temp->pino = rd->pino; temp->version = rd->version; temp->ino = rd->ino; temp->nsize = rd->nsize; temp->type = rd->type; temp->next = NULL; memcpy(temp->name, rd->name, rd->nsize); return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); } /* Cleanup every collected summary information */ static void jffs2_sum_clean_collected(struct jffs2_summary *s) { union jffs2_sum_mem *temp; if (!s->sum_list_head) { dbg_summary("already empty\n"); } while (s->sum_list_head) { temp = s->sum_list_head; s->sum_list_head = s->sum_list_head->u.next; kfree(temp); } s->sum_list_tail = NULL; s->sum_padded = 0; s->sum_num = 0; } void jffs2_sum_reset_collected(struct jffs2_summary *s) { dbg_summary("called\n"); jffs2_sum_clean_collected(s); s->sum_size = 0; } void jffs2_sum_disable_collecting(struct jffs2_summary *s) { dbg_summary("called\n"); jffs2_sum_clean_collected(s); s->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; } int jffs2_sum_is_disabled(struct jffs2_summary *s) { return (s->sum_size == JFFS2_SUMMARY_NOSUM_SIZE); } /* Move the collected summary information into sb (called from scan.c) */ void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s) { dbg_summary("oldsize=0x%x oldnum=%u => newsize=0x%x newnum=%u\n", c->summary->sum_size, c->summary->sum_num, s->sum_size, s->sum_num); c->summary->sum_size = s->sum_size; c->summary->sum_num = s->sum_num; c->summary->sum_padded = s->sum_padded; c->summary->sum_list_head = s->sum_list_head; c->summary->sum_list_tail = s->sum_list_tail; s->sum_list_head = s->sum_list_tail = NULL; } /* Called from wbuf.c to collect writed node info */ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, uint32_t ofs) { union jffs2_node_union *node; struct jffs2_eraseblock *jeb; node = invecs[0].iov_base; jeb = &c->blocks[ofs / c->sector_size]; ofs -= jeb->offset; switch (je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_mem *temp = kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL); if (!temp) goto no_mem; temp->nodetype = node->i.nodetype; temp->inode = node->i.ino; temp->version = node->i.version; temp->offset = cpu_to_je32(ofs); temp->totlen = node->i.totlen; temp->next = NULL; return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_mem *temp = kmalloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize, GFP_KERNEL); if (!temp) goto no_mem; temp->nodetype = node->d.nodetype; temp->totlen = node->d.totlen; temp->offset = cpu_to_je32(ofs); temp->pino = node->d.pino; temp->version = node->d.version; temp->ino = node->d.ino; temp->nsize = node->d.nsize; temp->type = node->d.type; temp->next = NULL; switch (count) { case 1: memcpy(temp->name,node->d.name,node->d.nsize); break; case 2: memcpy(temp->name,invecs[1].iov_base,node->d.nsize); break; default: BUG(); /* impossible count value */ break; } return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); } case JFFS2_NODETYPE_PADDING: dbg_summary("node PADDING\n"); c->summary->sum_padded += je32_to_cpu(node->u.totlen); break; case JFFS2_NODETYPE_CLEANMARKER: dbg_summary("node CLEANMARKER\n"); break; case JFFS2_NODETYPE_SUMMARY: dbg_summary("node SUMMARY\n"); break; default: /* If you implement a new node type you should also implement summary support for it or disable summary. */ BUG(); break; } return 0; no_mem: JFFS2_WARNING("MEMORY ALLOCATION ERROR!"); return -ENOMEM; } /* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_summary *summary, uint32_t *pseudo_random) { struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; void *sp; int i, ino; sp = summary->sum; for (i=0; isum_num); i++) { dbg_summary("processing summary index %d\n", i); switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *spi; spi = sp; ino = je32_to_cpu(spi->inode); dbg_summary("Inode at 0x%08x\n", jeb->offset + je32_to_cpu(spi->offset)); raw = jffs2_alloc_raw_node_ref(); if (!raw) { JFFS2_NOTICE("allocation of node reference failed\n"); kfree(summary); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) { JFFS2_NOTICE("scan_make_ino_cache failed\n"); jffs2_free_raw_node_ref(raw); kfree(summary); return -ENOMEM; } raw->flash_offset = (jeb->offset + je32_to_cpu(spi->offset)) | REF_UNCHECKED; raw->__totlen = PAD(je32_to_cpu(spi->totlen)); raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; *pseudo_random += je32_to_cpu(spi->version); UNCHECKED_SPACE(PAD(je32_to_cpu(spi->totlen))); sp += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *spd; spd = sp; dbg_summary("Dirent at 0x%08x\n", jeb->offset + je32_to_cpu(spd->offset)); fd = jffs2_alloc_full_dirent(spd->nsize+1); if (!fd) { kfree(summary); return -ENOMEM; } memcpy(&fd->name, spd->name, spd->nsize); fd->name[spd->nsize] = 0; raw = jffs2_alloc_raw_node_ref(); if (!raw) { jffs2_free_full_dirent(fd); JFFS2_NOTICE("allocation of node reference failed\n"); kfree(summary); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino)); if (!ic) { jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); kfree(summary); return -ENOMEM; } raw->__totlen = PAD(je32_to_cpu(spd->totlen)); raw->flash_offset = (jeb->offset + je32_to_cpu(spd->offset)) | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; fd->raw = raw; fd->next = NULL; fd->version = je32_to_cpu(spd->version); fd->ino = je32_to_cpu(spd->ino); fd->nhash = full_name_hash(fd->name, spd->nsize); fd->type = spd->type; USED_SPACE(PAD(je32_to_cpu(spd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); *pseudo_random += je32_to_cpu(spd->version); sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); break; } default : { JFFS2_WARNING("Unsupported node type found in summary! Exiting..."); kfree(summary); return -EIO; } } } kfree(summary); return 0; } /* Process the summary node - called from jffs2_scan_eraseblock() */ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t ofs, uint32_t *pseudo_random) { struct jffs2_unknown_node crcnode; struct jffs2_raw_node_ref *cache_ref; struct jffs2_raw_summary *summary; int ret, sumsize; uint32_t crc; sumsize = c->sector_size - ofs; ofs += jeb->offset; dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n", jeb->offset, ofs, sumsize); summary = kmalloc(sumsize, GFP_KERNEL); if (!summary) { return -ENOMEM; } ret = jffs2_fill_scan_buf(c, (unsigned char *)summary, ofs, sumsize); if (ret) { kfree(summary); return ret; } /* OK, now check for node validity and CRC */ crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); crcnode.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); crcnode.totlen = summary->totlen; crc = crc32(0, &crcnode, sizeof(crcnode)-4); if (je32_to_cpu(summary->hdr_crc) != crc) { dbg_summary("Summary node header is corrupt (bad CRC or " "no summary at all)\n"); goto crc_err; } if (je32_to_cpu(summary->totlen) != sumsize) { dbg_summary("Summary node is corrupt (wrong erasesize?)\n"); goto crc_err; } crc = crc32(0, summary, sizeof(struct jffs2_raw_summary)-8); if (je32_to_cpu(summary->node_crc) != crc) { dbg_summary("Summary node is corrupt (bad CRC)\n"); goto crc_err; } crc = crc32(0, summary->sum, sumsize - sizeof(struct jffs2_raw_summary)); if (je32_to_cpu(summary->sum_crc) != crc) { dbg_summary("Summary node data is corrupt (bad CRC)\n"); goto crc_err; } if ( je32_to_cpu(summary->cln_mkr) ) { dbg_summary("Summary : CLEANMARKER node \n"); if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) { dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n", je32_to_cpu(summary->cln_mkr), c->cleanmarker_size); UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr))); } else if (jeb->first_node) { dbg_summary("CLEANMARKER node not first node in block " "(0x%08x)\n", jeb->offset); UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr))); } else { struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { JFFS2_NOTICE("Failed to allocate node ref for clean marker\n"); kfree(summary); return -ENOMEM; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset | REF_NORMAL; marker_ref->__totlen = je32_to_cpu(summary->cln_mkr); jeb->first_node = jeb->last_node = marker_ref; USED_SPACE( PAD(je32_to_cpu(summary->cln_mkr)) ); } } if (je32_to_cpu(summary->padded)) { DIRTY_SPACE(je32_to_cpu(summary->padded)); } ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random); if (ret) return ret; /* for PARANOIA_CHECK */ cache_ref = jffs2_alloc_raw_node_ref(); if (!cache_ref) { JFFS2_NOTICE("Failed to allocate node ref for cache\n"); return -ENOMEM; } cache_ref->next_in_ino = NULL; cache_ref->next_phys = NULL; cache_ref->flash_offset = ofs | REF_NORMAL; cache_ref->__totlen = sumsize; if (!jeb->first_node) jeb->first_node = cache_ref; if (jeb->last_node) jeb->last_node->next_phys = cache_ref; jeb->last_node = cache_ref; USED_SPACE(sumsize); jeb->wasted_size += jeb->free_size; c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->free_size = 0; return jffs2_scan_classify_jeb(c, jeb); crc_err: JFFS2_WARNING("Summary node crc error, skipping summary information.\n"); return 0; } /* Write summary data to flash - helper function for jffs2_sum_write_sumnode() */ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t infosize, uint32_t datasize, int padsize) { struct jffs2_raw_summary isum; union jffs2_sum_mem *temp; struct jffs2_sum_marker *sm; struct kvec vecs[2]; void *wpage; int ret; size_t retlen; memset(c->summary->sum_buf, 0xff, datasize); memset(&isum, 0, sizeof(isum)); isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); isum.totlen = cpu_to_je32(infosize); isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); isum.padded = cpu_to_je32(c->summary->sum_padded); isum.cln_mkr = cpu_to_je32(c->cleanmarker_size); isum.sum_num = cpu_to_je32(c->summary->sum_num); wpage = c->summary->sum_buf; while (c->summary->sum_num) { switch (je16_to_cpu(c->summary->sum_list_head->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *sino_ptr = wpage; sino_ptr->nodetype = c->summary->sum_list_head->i.nodetype; sino_ptr->inode = c->summary->sum_list_head->i.inode; sino_ptr->version = c->summary->sum_list_head->i.version; sino_ptr->offset = c->summary->sum_list_head->i.offset; sino_ptr->totlen = c->summary->sum_list_head->i.totlen; wpage += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; sdrnt_ptr->nodetype = c->summary->sum_list_head->d.nodetype; sdrnt_ptr->totlen = c->summary->sum_list_head->d.totlen; sdrnt_ptr->offset = c->summary->sum_list_head->d.offset; sdrnt_ptr->pino = c->summary->sum_list_head->d.pino; sdrnt_ptr->version = c->summary->sum_list_head->d.version; sdrnt_ptr->ino = c->summary->sum_list_head->d.ino; sdrnt_ptr->nsize = c->summary->sum_list_head->d.nsize; sdrnt_ptr->type = c->summary->sum_list_head->d.type; memcpy(sdrnt_ptr->name, c->summary->sum_list_head->d.name, c->summary->sum_list_head->d.nsize); wpage += JFFS2_SUMMARY_DIRENT_SIZE(c->summary->sum_list_head->d.nsize); break; } default : { BUG(); /* unknown node in summary information */ } } temp = c->summary->sum_list_head; c->summary->sum_list_head = c->summary->sum_list_head->u.next; kfree(temp); c->summary->sum_num--; } jffs2_sum_reset_collected(c->summary); wpage += padsize; sm = wpage; sm->offset = cpu_to_je32(c->sector_size - jeb->free_size); sm->magic = cpu_to_je32(JFFS2_SUM_MAGIC); isum.sum_crc = cpu_to_je32(crc32(0, c->summary->sum_buf, datasize)); isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8)); vecs[0].iov_base = &isum; vecs[0].iov_len = sizeof(isum); vecs[1].iov_base = c->summary->sum_buf; vecs[1].iov_len = datasize; dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n", jeb->offset + c->sector_size - jeb->free_size); spin_unlock(&c->erase_completion_lock); ret = jffs2_flash_writev(c, vecs, 2, jeb->offset + c->sector_size - jeb->free_size, &retlen, 0); spin_lock(&c->erase_completion_lock); if (ret || (retlen != infosize)) { JFFS2_WARNING("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", infosize, jeb->offset + c->sector_size - jeb->free_size, ret, retlen); c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; WASTED_SPACE(infosize); return 1; } return 0; } /* Write out summary information - called from jffs2_do_reserve_space */ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c) { struct jffs2_raw_node_ref *summary_ref; int datasize, infosize, padsize, ret; struct jffs2_eraseblock *jeb; dbg_summary("called\n"); jeb = c->nextblock; if (!c->summary->sum_num || !c->summary->sum_list_head) { JFFS2_WARNING("Empty summary info!!!\n"); BUG(); } datasize = c->summary->sum_size + sizeof(struct jffs2_sum_marker); infosize = sizeof(struct jffs2_raw_summary) + datasize; padsize = jeb->free_size - infosize; infosize += padsize; datasize += padsize; /* Is there enough space for summary? */ if (padsize < 0) { /* don't try to write out summary for this jeb */ jffs2_sum_disable_collecting(c->summary); JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize); return 0; } ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize); if (ret) return 0; /* can't write out summary, block is marked as NOSUM_SIZE */ /* for ACCT_PARANOIA_CHECK */ spin_unlock(&c->erase_completion_lock); summary_ref = jffs2_alloc_raw_node_ref(); spin_lock(&c->erase_completion_lock); if (!summary_ref) { JFFS2_NOTICE("Failed to allocate node ref for summary\n"); return -ENOMEM; } summary_ref->next_in_ino = NULL; summary_ref->next_phys = NULL; summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL; summary_ref->__totlen = infosize; if (!jeb->first_node) jeb->first_node = summary_ref; if (jeb->last_node) jeb->last_node->next_phys = summary_ref; jeb->last_node = summary_ref; USED_SPACE(infosize); return 0; } linux-2.6.17/fs/jffs2/.svn/text-base/nodelist.h.svn-base0000444000000000000000000003673110574207642021374 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: nodelist.h,v 1.140 2005/09/07 08:34:54 havasi Exp $ * */ #ifndef __JFFS2_NODELIST_H__ #define __JFFS2_NODELIST_H__ #include #include #include #include #include #include #include "summary.h" #ifdef __ECOS #include "os-ecos.h" #else #include /* For compatibility with older kernels */ #include "os-linux.h" #endif #define JFFS2_NATIVE_ENDIAN /* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from whatever OS we're actually running on here too. */ #if defined(JFFS2_NATIVE_ENDIAN) #define cpu_to_je16(x) ((jint16_t){x}) #define cpu_to_je32(x) ((jint32_t){x}) #define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) #define je16_to_cpu(x) ((x).v16) #define je32_to_cpu(x) ((x).v32) #define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) #elif defined(JFFS2_BIG_ENDIAN) #define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) #define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) #define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) #define je16_to_cpu(x) (be16_to_cpu(x.v16)) #define je32_to_cpu(x) (be32_to_cpu(x.v32)) #define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) #elif defined(JFFS2_LITTLE_ENDIAN) #define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) #define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) #define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) #define je16_to_cpu(x) (le16_to_cpu(x.v16)) #define je32_to_cpu(x) (le32_to_cpu(x.v32)) #define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) #else #error wibble #endif /* The minimal node header size */ #define JFFS2_MIN_NODE_HEADER sizeof(struct jffs2_raw_dirent) /* This is all we need to keep in-core for each raw node during normal operation. As and when we do read_inode on a particular inode, we can scan the nodes which are listed for it and build up a proper map of which nodes are currently valid. JFFSv1 always used to keep that whole map in core for each inode. */ struct jffs2_raw_node_ref { struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref for this inode. If this is the last, it points to the inode_cache for this inode instead. The inode_cache will have NULL in the first word so you know when you've got there :) */ struct jffs2_raw_node_ref *next_phys; uint32_t flash_offset; uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ }; /* flash_offset & 3 always has to be zero, because nodes are always aligned at 4 bytes. So we have a couple of extra bits to play with, which indicate the node's status; see below: */ #define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ #define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */ #define REF_PRISTINE 2 /* Completely clean. GC without looking */ #define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */ #define ref_flags(ref) ((ref)->flash_offset & 3) #define ref_offset(ref) ((ref)->flash_offset & ~3) #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) /* For each inode in the filesystem, we need to keep a record of nlink, because it would be a PITA to scan the whole directory tree at read_inode() time to calculate it, and to keep sufficient information in the raw_node_ref (basically both parent and child inode number for dirent nodes) would take more space than this does. We also keep a pointer to the first physical node which is part of this inode, too. */ struct jffs2_inode_cache { struct jffs2_full_dirent *scan_dents; /* Used during scan to hold temporary lists of dirents, and later must be set to NULL to mark the end of the raw_node_ref->next_in_ino chain. */ struct jffs2_inode_cache *next; struct jffs2_raw_node_ref *nodes; uint32_t ino; int nlink; int state; }; /* Inode states for 'state' above. We need the 'GC' state to prevent someone from doing a read_inode() while we're moving a 'REF_PRISTINE' node without going through all the iget() nonsense */ #define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */ #define INO_STATE_CHECKING 1 /* CRC checks in progress */ #define INO_STATE_PRESENT 2 /* In core */ #define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */ #define INO_STATE_GC 4 /* GCing a 'pristine' node */ #define INO_STATE_READING 5 /* In read_inode() */ #define INO_STATE_CLEARING 6 /* In clear_inode() */ #define INOCACHE_HASHSIZE 128 /* Larger representation of a raw node, kept in-core only when the struct inode for this particular ino is instantiated. */ struct jffs2_full_dnode { struct jffs2_raw_node_ref *raw; uint32_t ofs; /* The offset to which the data of this node belongs */ uint32_t size; uint32_t frags; /* Number of fragments which currently refer to this node. When this reaches zero, the node is obsolete. */ }; /* Even larger representation of a raw node, kept in-core only while we're actually building up the original map of which nodes go where, in read_inode() */ struct jffs2_tmp_dnode_info { struct rb_node rb; struct jffs2_full_dnode *fn; uint32_t version; uint32_t data_crc; uint32_t partial_crc; uint32_t csize; }; struct jffs2_full_dirent { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *next; uint32_t version; uint32_t ino; /* == zero for unlink */ unsigned int nhash; unsigned char type; unsigned char name[0]; }; /* Fragments - used to build a map of which raw node to obtain data from for each part of the ino */ struct jffs2_node_frag { struct rb_node rb; struct jffs2_full_dnode *node; /* NULL for holes */ uint32_t size; uint32_t ofs; /* The offset to which this fragment belongs */ }; struct jffs2_eraseblock { struct list_head list; int bad_count; uint32_t offset; /* of this block in the MTD */ uint32_t unchecked_size; uint32_t used_size; uint32_t dirty_size; uint32_t wasted_size; uint32_t free_size; /* Note that sector_size - free_size is the address of the first free space */ struct jffs2_raw_node_ref *first_node; struct jffs2_raw_node_ref *last_node; struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */ }; static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c) { return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024); } /* Calculate totlen from surrounding nodes or eraseblock */ static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *ref) { uint32_t ref_end; if (ref->next_phys) ref_end = ref_offset(ref->next_phys); else { if (!jeb) jeb = &c->blocks[ref->flash_offset / c->sector_size]; /* Last node in block. Use free_space */ BUG_ON(ref != jeb->last_node); ref_end = jeb->offset + c->sector_size - jeb->free_size; } return ref_end - ref_offset(ref); } static inline uint32_t ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *ref) { uint32_t ret; #if CONFIG_JFFS2_FS_DEBUG > 0 if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); BUG(); } #endif #if 1 ret = ref->__totlen; #else /* This doesn't actually work yet */ ret = __ref_totlen(c, jeb, ref); if (ret != ref->__totlen) { printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, ret, ref->__totlen); if (!jeb) jeb = &c->blocks[ref->flash_offset / c->sector_size]; jffs2_dbg_dump_node_refs_nolock(c, jeb); BUG(); } #endif return ret; } #define ALLOC_NORMAL 0 /* Normal allocation */ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */ #define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */ /* How much dirty space before it goes on the very_dirty_list */ #define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) /* check if dirty space is more than 255 Byte */ #define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) #define PAD(x) (((x)+3)&~3) static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) { while(raw->next_in_ino) { raw = raw->next_in_ino; } return ((struct jffs2_inode_cache *)raw); } static inline struct jffs2_node_frag *frag_first(struct rb_root *root) { struct rb_node *node = root->rb_node; if (!node) return NULL; while(node->rb_left) node = node->rb_left; return rb_entry(node, struct jffs2_node_frag, rb); } static inline struct jffs2_node_frag *frag_last(struct rb_root *root) { struct rb_node *node = root->rb_node; if (!node) return NULL; while(node->rb_right) node = node->rb_right; return rb_entry(node, struct jffs2_node_frag, rb); } #define rb_parent(rb) ((rb)->rb_parent) #define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb) #define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) #define frag_erase(frag, list) rb_erase(&frag->rb, list); /* nodelist.c */ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state); struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino); void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new); void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old); void jffs2_free_ino_caches(struct jffs2_sb_info *c); void jffs2_free_raw_node_refs(struct jffs2_sb_info *c); struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset); void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete); struct rb_node *rb_next(struct rb_node *); struct rb_node *rb_prev(struct rb_node *); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this); int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn); /* nodemgmt.c */ int jffs2_thread_should_wake(struct jffs2_sb_info *c); int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio, uint32_t sumsize); int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize); int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); void jffs2_complete_reservation(struct jffs2_sb_info *c); void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); /* write.c */ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode); struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode); int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, unsigned char *buf, uint32_t offset, uint32_t writelen, uint32_t *retlen); int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen); int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time); int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time); /* readinode.c */ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t ino, struct jffs2_raw_inode *latest_node); int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); /* malloc.c */ int jffs2_create_slab_caches(void); void jffs2_destroy_slab_caches(void); struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize); void jffs2_free_full_dirent(struct jffs2_full_dirent *); struct jffs2_full_dnode *jffs2_alloc_full_dnode(void); void jffs2_free_full_dnode(struct jffs2_full_dnode *); struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void); void jffs2_free_raw_dirent(struct jffs2_raw_dirent *); struct jffs2_raw_inode *jffs2_alloc_raw_inode(void); void jffs2_free_raw_inode(struct jffs2_raw_inode *); struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void); void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *); struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void); void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *); struct jffs2_node_frag *jffs2_alloc_node_frag(void); void jffs2_free_node_frag(struct jffs2_node_frag *); struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); void jffs2_free_inode_cache(struct jffs2_inode_cache *); /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); /* read.c */ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len); int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *buf, uint32_t offset, uint32_t len); char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f); /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); void jffs2_rotate_lists(struct jffs2_sb_info *c); int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf, uint32_t ofs, uint32_t len); struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino); int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); /* build.c */ int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* wbuf.c */ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #endif #include "debug.h" #endif /* __JFFS2_NODELIST_H__ */ linux-2.6.17/fs/jffs2/.svn/text-base/write.c.svn-base0000444000000000000000000005176110574207642020700 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: write.c,v 1.97 2005/11/07 11:14:42 gleixner Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) { struct jffs2_inode_cache *ic; ic = jffs2_alloc_inode_cache(); if (!ic) { return -ENOMEM; } memset(ic, 0, sizeof(*ic)); f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_PRESENT; jffs2_add_ino_cache(c, f->inocache); D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); ri->ino = cpu_to_je32(f->inocache->ino); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(PAD(sizeof(*ri))); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->mode = cpu_to_jemode(mode); f->highest_version = 1; ri->version = cpu_to_je32(f->highest_version); return 0; } /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, write it to the flash, link it into the existing inode/fragment list */ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dnode *fn; size_t retlen; struct kvec vecs[2]; int ret; int retried = 0; unsigned long cnt = 2; D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n"); BUG(); } ); vecs[0].iov_base = ri; vecs[0].iov_len = sizeof(*ri); vecs[1].iov_base = (unsigned char *)data; vecs[1].iov_len = datalen; jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen); } raw = jffs2_alloc_raw_node_ref(); if (!raw) return ERR_PTR(-ENOMEM); fn = jffs2_alloc_full_dnode(); if (!fn) { jffs2_free_raw_node_ref(raw); return ERR_PTR(-ENOMEM); } fn->ofs = je32_to_cpu(ri->offset); fn->size = je32_to_cpu(ri->dsize); fn->frags = 0; /* check number of valid vecs */ if (!datalen || !data) cnt = 1; retry: fn->raw = raw; raw->flash_offset = flash_ofs; raw->__totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) { BUG_ON(!retried); D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, " "highest version %d -> updating dnode\n", je32_to_cpu(ri->version), f->highest_version)); ri->version = cpu_to_je32(++f->highest_version); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); } ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:f->inocache->ino); if (ret || (retlen != sizeof(*ri) + datalen)) { printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*ri)+datalen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ if (retlen) { /* Doesn't belong to any inode */ raw->next_in_ino = NULL; /* Don't change raw->size to match retlen. We may have written the node header already, and only the data will seem corrupted, in which case the scan would skip over any node we write before the original intended end of this node */ raw->flash_offset |= REF_OBSOLETE; jffs2_add_physical_node_ref(c, raw); jffs2_mark_node_obsolete(c, raw); } else { printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); jffs2_free_raw_node_ref(raw); } if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; retried = 1; D1(printk(KERN_DEBUG "Retrying failed write.\n")); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, JFFS2_SUMMARY_INODE_SIZE); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE); down(&f->sem); } if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); jffs2_free_raw_node_ref(raw); } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dnode(fn); return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ /* If node covers at least a whole page, or if it starts at the beginning of a page and runs to the end of the file, or if it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. */ if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) || ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) && (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) { raw->flash_offset |= REF_PRISTINE; } else { raw->flash_offset |= REF_NORMAL; } jffs2_add_physical_node_ref(c, raw); /* Link into per-inode list */ spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); if (retried) { jffs2_dbg_acct_sanity_check(c,NULL); } return fn; } struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; size_t retlen; struct kvec vecs[2]; int retried = 0; int ret; D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), je32_to_cpu(rd->name_crc))); D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n"); BUG(); } ); vecs[0].iov_base = rd; vecs[0].iov_len = sizeof(*rd); vecs[1].iov_base = (unsigned char *)name; vecs[1].iov_len = namelen; jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); raw = jffs2_alloc_raw_node_ref(); if (!raw) return ERR_PTR(-ENOMEM); fd = jffs2_alloc_full_dirent(namelen+1); if (!fd) { jffs2_free_raw_node_ref(raw); return ERR_PTR(-ENOMEM); } fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(name, strlen(name)); fd->type = rd->type; memcpy(fd->name, name, namelen); fd->name[namelen]=0; retry: fd->raw = raw; raw->flash_offset = flash_ofs; raw->__totlen = PAD(sizeof(*rd)+namelen); raw->next_phys = NULL; if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) { BUG_ON(!retried); D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, " "highest version %d -> updating dirent\n", je32_to_cpu(rd->version), f->highest_version)); rd->version = cpu_to_je32(++f->highest_version); fd->version = je32_to_cpu(rd->version); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); } ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); if (ret || (retlen != sizeof(*rd) + namelen)) { printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*rd)+namelen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ if (retlen) { raw->next_in_ino = NULL; raw->flash_offset |= REF_OBSOLETE; jffs2_add_physical_node_ref(c, raw); jffs2_mark_node_obsolete(c, raw); } else { printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); jffs2_free_raw_node_ref(raw); } if (!retried && (raw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; retried = 1; D1(printk(KERN_DEBUG "Retrying failed write.\n")); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); down(&f->sem); } if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); jffs2_free_raw_node_ref(raw); } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dirent(fd); return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ raw->flash_offset |= REF_PRISTINE; jffs2_add_physical_node_ref(c, raw); spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; spin_unlock(&c->erase_completion_lock); if (retried) { jffs2_dbg_acct_sanity_check(c,NULL); } return fd; } /* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that we don't have to go digging in struct inode or its equivalent. It should set: mode, uid, gid, (starting)isize, atime, ctime, mtime */ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, unsigned char *buf, uint32_t offset, uint32_t writelen, uint32_t *retlen) { int ret = 0; uint32_t writtenlen = 0; D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n", f->inocache->ino, offset, writelen)); while(writelen) { struct jffs2_full_dnode *fn; unsigned char *comprbuf = NULL; uint16_t comprtype = JFFS2_COMPR_NONE; uint32_t phys_ofs, alloclen; uint32_t datalen, cdatalen; int retried = 0; retry: D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); break; } down(&f->sem); datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->ino = cpu_to_je32(f->inocache->ino); ri->version = cpu_to_je32(++f->highest_version); ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen)); ri->offset = cpu_to_je32(offset); ri->csize = cpu_to_je32(cdatalen); ri->dsize = cpu_to_je32(datalen); ri->compr = comprtype & 0xff; ri->usercompr = (comprtype >> 8 ) & 0xff; ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY); jffs2_free_comprbuf(comprbuf, buf); if (IS_ERR(fn)) { ret = PTR_ERR(fn); up(&f->sem); jffs2_complete_reservation(c); if (!retried) { /* Write error to be retried */ retried = 1; D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n")); goto retry; } break; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } if (ret) { /* Eep */ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret)); jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); up(&f->sem); jffs2_complete_reservation(c); break; } up(&f->sem); jffs2_complete_reservation(c); if (!datalen) { printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n"); ret = -EIO; break; } D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen)); writtenlen += datalen; offset += datalen; writelen -= datalen; buf += datalen; } *retlen = writtenlen; return ret; } int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen) { struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; uint32_t alloclen, phys_ofs; int ret; /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { up(&f->sem); return ret; } ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", jemode_to_cpu(ri->mode))); if (IS_ERR(fn)) { D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; up(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); return ret; } rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); return -ENOMEM; } down(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_f->inocache->ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = ri->ino; rd->mctime = ri->ctime; rd->nsize = namelen; rd->type = DT_REG; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); up(&dir_f->sem); return PTR_ERR(fd); } /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); up(&dir_f->sem); return 0; } int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time) { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; uint32_t alloclen, phys_ofs; int ret; if (1 /* alternative branch needs testing */ || !jffs2_can_mark_obsolete(c)) { /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */ rd = jffs2_alloc_raw_dirent(); if (!rd) return -ENOMEM; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); return ret; } down(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_f->inocache->ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(0); rd->mctime = cpu_to_je32(time); rd->nsize = namelen; rd->type = DT_UNKNOWN; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { jffs2_complete_reservation(c); up(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); } else { struct jffs2_full_dirent **prev = &dir_f->dents; uint32_t nhash = full_name_hash(name, namelen); down(&dir_f->sem); while ((*prev) && (*prev)->nhash <= nhash) { if ((*prev)->nhash == nhash && !memcmp((*prev)->name, name, namelen) && !(*prev)->name[namelen]) { struct jffs2_full_dirent *this = *prev; D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n", this->ino, ref_offset(this->raw))); *prev = this->next; jffs2_mark_node_obsolete(c, (this->raw)); jffs2_free_full_dirent(this); break; } prev = &((*prev)->next); } up(&dir_f->sem); } /* dead_f is NULL if this was a rename not a real unlink */ /* Also catch the !f->inocache case, where there was a dirent pointing to an inode which didn't exist. */ if (dead_f && dead_f->inocache) { down(&dead_f->sem); if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { while (dead_f->dents) { /* There can be only deleted ones */ fd = dead_f->dents; dead_f->dents = fd->next; if (fd->ino) { printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", dead_f->inocache->ino, fd->name, fd->ino); } else { D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); } jffs2_mark_node_obsolete(c, fd->raw); jffs2_free_full_dirent(fd); } } dead_f->inocache->nlink--; /* NB: Caller must set inode nlink if appropriate */ up(&dead_f->sem); } jffs2_complete_reservation(c); return 0; } int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time) { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; uint32_t alloclen, phys_ofs; int ret; rd = jffs2_alloc_raw_dirent(); if (!rd) return -ENOMEM; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); return ret; } down(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_f->inocache->ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(ino); rd->mctime = cpu_to_je32(time); rd->nsize = namelen; rd->type = type; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { jffs2_complete_reservation(c); up(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); up(&dir_f->sem); return 0; } linux-2.6.17/fs/jffs2/.svn/text-base/build.c.svn-base0000444000000000000000000002411210574207642020633 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: build.c,v 1.85 2005/11/07 11:14:38 gleixner Exp $ * */ #include #include #include #include #include #include "nodelist.h" static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); static inline struct jffs2_inode_cache * first_inode_chain(int *i, struct jffs2_sb_info *c) { for (; *i < INOCACHE_HASHSIZE; (*i)++) { if (c->inocache_list[*i]) return c->inocache_list[*i]; } return NULL; } static inline struct jffs2_inode_cache * next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c) { /* More in this chain? */ if (ic->next) return ic->next; (*i)++; return first_inode_chain(i, c); } #define for_each_inode(i, c, ic) \ for (i = 0, ic = first_inode_chain(&i, (c)); \ ic; \ ic = next_inode(&i, ic, (c))) static void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { struct jffs2_full_dirent *fd; dbg_fsbuild("building directory inode #%u\n", ic->ino); /* For each child, increase nlink */ for(fd = ic->scan_dents; fd; fd = fd->next) { struct jffs2_inode_cache *child_ic; if (!fd->ino) continue; /* we can get high latency here with huge directories */ child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", fd->name, fd->ino, ic->ino); jffs2_mark_node_obsolete(c, fd->raw); continue; } if (child_ic->nlink++ && fd->type == DT_DIR) { JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); /* TODO: What do we do about it? */ } dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino); /* Can't free scan_dents so far. We might need them in pass 2 */ } } /* Scan plan: - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go - Scan directory tree from top down, setting nlink in inocaches - Scan inocaches for inodes with nlink==0 */ static int jffs2_build_filesystem(struct jffs2_sb_info *c) { int ret; int i; struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; struct jffs2_full_dirent *dead_fds = NULL; dbg_fsbuild("build FS data structures\n"); /* First, scan the medium and build all the inode caches with lists of physical nodes */ c->flags |= JFFS2_SB_FLAG_SCANNING; ret = jffs2_scan_medium(c); c->flags &= ~JFFS2_SB_FLAG_SCANNING; if (ret) goto exit; dbg_fsbuild("scanned flash completely\n"); jffs2_dbg_dump_block_lists_nolock(c); dbg_fsbuild("pass 1 starting\n"); c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { if (ic->scan_dents) { jffs2_build_inode_pass1(c, ic); cond_resched(); } } dbg_fsbuild("pass 1 complete\n"); /* Next, scan for inodes with nlink == 0 and remove them. If they were directories, then decrement the nlink of their children too, and repeat the scan. As that's going to be a fairly uncommon occurrence, it's not so evil to do it this way. Recursion bad. */ dbg_fsbuild("pass 2 starting\n"); for_each_inode(i, c, ic) { if (ic->nlink) continue; jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); cond_resched(); } dbg_fsbuild("pass 2a starting\n"); while (dead_fds) { fd = dead_fds; dead_fds = fd->next; ic = jffs2_get_ino_cache(c, fd->ino); if (ic) jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); jffs2_free_full_dirent(fd); } dbg_fsbuild("pass 2a complete\n"); dbg_fsbuild("freeing temporary data structures\n"); /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { while(ic->scan_dents) { fd = ic->scan_dents; ic->scan_dents = fd->next; jffs2_free_full_dirent(fd); } ic->scan_dents = NULL; cond_resched(); } c->flags &= ~JFFS2_SB_FLAG_BUILDING; dbg_fsbuild("FS build complete\n"); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); ret = 0; exit: if (ret) { for_each_inode(i, c, ic) { while(ic->scan_dents) { fd = ic->scan_dents; ic->scan_dents = fd->next; jffs2_free_full_dirent(fd); } } } return ret; } static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; dbg_fsbuild("removing ino #%u with nlink == zero.\n", ic->ino); raw = ic->nodes; while (raw != (void *)ic) { struct jffs2_raw_node_ref *next = raw->next_in_ino; dbg_fsbuild("obsoleting node at 0x%08x\n", ref_offset(raw)); jffs2_mark_node_obsolete(c, raw); raw = next; } if (ic->scan_dents) { int whinged = 0; dbg_fsbuild("inode #%u was a directory which may have children...\n", ic->ino); while(ic->scan_dents) { struct jffs2_inode_cache *child_ic; fd = ic->scan_dents; ic->scan_dents = fd->next; if (!fd->ino) { /* It's a deletion dirent. Ignore it */ dbg_fsbuild("child \"%s\" is a deletion dirent, skipping...\n", fd->name); jffs2_free_full_dirent(fd); continue; } if (!whinged) whinged = 1; dbg_fsbuild("removing child \"%s\", ino #%u\n", fd->name, fd->ino); child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { dbg_fsbuild("cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); jffs2_free_full_dirent(fd); continue; } /* Reduce nlink of the child. If it's now zero, stick it on the dead_fds list to be cleaned up later. Else just free the fd */ child_ic->nlink--; if (!child_ic->nlink) { dbg_fsbuild("inode #%u (\"%s\") has now got zero nlink, adding to dead_fds list.\n", fd->ino, fd->name); fd->next = *dead_fds; *dead_fds = fd; } else { dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", fd->ino, fd->name, child_ic->nlink); jffs2_free_full_dirent(fd); } } } /* We don't delete the inocache from the hash list and free it yet. The erase code will do that, when all the nodes are completely gone. */ } static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) { uint32_t size; /* Deletion should almost _always_ be allowed. We're fairly buggered once we stop allowing people to delete stuff because there's not enough free space... */ c->resv_blocks_deletion = 2; /* Be conservative about how much space we need before we allow writes. On top of that which is required for deletia, require an extra 2% of the medium to be available, for overhead caused by nodes being split across blocks, etc. */ size = c->flash_size / 50; /* 2% of flash size */ size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */ size += c->sector_size - 1; /* ... and round up */ c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size); /* When do we let the GC thread run in the background */ c->resv_blocks_gctrigger = c->resv_blocks_write + 1; /* When do we allow garbage collection to merge nodes to make long-term progress at the expense of short-term space exhaustion? */ c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1; /* When do we allow garbage collection to eat from bad blocks rather than actually making progress? */ c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; /* If there's less than this amount of dirty space, don't bother trying to GC to make more space. It'll be a fruitless task */ c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); dbg_fsbuild("JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks); dbg_fsbuild("Blocks required to allow deletion: %d (%d KiB)\n", c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024); dbg_fsbuild("Blocks required to allow writes: %d (%d KiB)\n", c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024); dbg_fsbuild("Blocks required to quiesce GC thread: %d (%d KiB)\n", c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024); dbg_fsbuild("Blocks required to allow GC merges: %d (%d KiB)\n", c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024); dbg_fsbuild("Blocks required to GC bad blocks: %d (%d KiB)\n", c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024); dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n", c->nospc_dirty_size); } int jffs2_do_mount_fs(struct jffs2_sb_info *c) { int ret; int i; int size; c->free_size = c->flash_size; c->nr_blocks = c->flash_size / c->sector_size; size = sizeof(struct jffs2_eraseblock) * c->nr_blocks; #ifndef __ECOS if (jffs2_blocks_use_vmalloc(c)) c->blocks = vmalloc(size); else #endif c->blocks = kmalloc(size, GFP_KERNEL); if (!c->blocks) return -ENOMEM; memset(c->blocks, 0, size); for (i=0; inr_blocks; i++) { INIT_LIST_HEAD(&c->blocks[i].list); c->blocks[i].offset = i * c->sector_size; c->blocks[i].free_size = c->sector_size; } INIT_LIST_HEAD(&c->clean_list); INIT_LIST_HEAD(&c->very_dirty_list); INIT_LIST_HEAD(&c->dirty_list); INIT_LIST_HEAD(&c->erasable_list); INIT_LIST_HEAD(&c->erasing_list); INIT_LIST_HEAD(&c->erase_pending_list); INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); INIT_LIST_HEAD(&c->erase_complete_list); INIT_LIST_HEAD(&c->free_list); INIT_LIST_HEAD(&c->bad_list); INIT_LIST_HEAD(&c->bad_used_list); c->highest_ino = 1; c->summary = NULL; ret = jffs2_sum_init(c); if (ret) return ret; if (jffs2_build_filesystem(c)) { dbg_fsbuild("build_fs failed\n"); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); #ifndef __ECOS if (jffs2_blocks_use_vmalloc(c)) vfree(c->blocks); else #endif kfree(c->blocks); return -EIO; } jffs2_calc_trigger_levels(c); return 0; } linux-2.6.17/fs/jffs2/.svn/text-base/nodelist.c.svn-base0000444000000000000000000007261210574207642021365 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: nodelist.c,v 1.115 2005/11/07 11:14:40 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) { struct jffs2_full_dirent **prev = list; dbg_dentlist("add dirent \"%s\", ino #%u\n", new->name, new->ino); while ((*prev) && (*prev)->nhash <= new->nhash) { if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) { /* Duplicate. Free one */ if (new->version < (*prev)->version) { dbg_dentlist("Eep! Marking new dirent node is obsolete, old is \"%s\", ino #%u\n", (*prev)->name, (*prev)->ino); jffs2_mark_node_obsolete(c, new->raw); jffs2_free_full_dirent(new); } else { dbg_dentlist("marking old dirent \"%s\", ino #%u bsolete\n", (*prev)->name, (*prev)->ino); new->next = (*prev)->next; jffs2_mark_node_obsolete(c, ((*prev)->raw)); jffs2_free_full_dirent(*prev); *prev = new; } return; } prev = &((*prev)->next); } new->next = *prev; *prev = new; } void jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) { struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); dbg_fragtree("truncating fragtree to 0x%08x bytes\n", size); /* We know frag->ofs <= size. That's what lookup does for us */ if (frag && frag->ofs != size) { if (frag->ofs+frag->size > size) { frag->size = size - frag->ofs; } frag = frag_next(frag); } while (frag && frag->ofs >= size) { struct jffs2_node_frag *next = frag_next(frag); frag_erase(frag, list); jffs2_obsolete_node_frag(c, frag); frag = next; } if (size == 0) return; /* * If the last fragment starts at the RAM page boundary, it is * REF_PRISTINE irrespective of its size. */ frag = frag_last(list); if (frag->node && (frag->ofs & (PAGE_CACHE_SIZE - 1)) == 0) { dbg_fragtree2("marking the last fragment 0x%08x-0x%08x REF_PRISTINE.\n", frag->ofs, frag->ofs + frag->size); frag->node->raw->flash_offset = ref_offset(frag->node->raw) | REF_PRISTINE; } } void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) { if (this->node) { this->node->frags--; if (!this->node->frags) { /* The node has no valid frags left. It's totally obsoleted */ dbg_fragtree2("marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size); jffs2_mark_node_obsolete(c, this->node->raw); jffs2_free_full_dnode(this->node); } else { dbg_fragtree2("marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n", ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size, this->node->frags); mark_ref_normal(this->node->raw); } } jffs2_free_node_frag(this); } static void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) { struct rb_node *parent = &base->rb; struct rb_node **link = &parent; dbg_fragtree2("insert frag (0x%04x-0x%04x)\n", newfrag->ofs, newfrag->ofs + newfrag->size); while (*link) { parent = *link; base = rb_entry(parent, struct jffs2_node_frag, rb); if (newfrag->ofs > base->ofs) link = &base->rb.rb_right; else if (newfrag->ofs < base->ofs) link = &base->rb.rb_left; else { JFFS2_ERROR("duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); BUG(); } } rb_link_node(&newfrag->rb, &base->rb, link); } /* * Allocate and initializes a new fragment. */ static struct jffs2_node_frag * new_fragment(struct jffs2_full_dnode *fn, uint32_t ofs, uint32_t size) { struct jffs2_node_frag *newfrag; newfrag = jffs2_alloc_node_frag(); if (likely(newfrag)) { newfrag->ofs = ofs; newfrag->size = size; newfrag->node = fn; } else { JFFS2_ERROR("cannot allocate a jffs2_node_frag object\n"); } return newfrag; } /* * Called when there is no overlapping fragment exist. Inserts a hole before the new * fragment and inserts the new fragment to the fragtree. */ static int no_overlapping_node(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag, struct jffs2_node_frag *this, uint32_t lastend) { if (lastend < newfrag->node->ofs) { /* put a hole in before the new fragment */ struct jffs2_node_frag *holefrag; holefrag= new_fragment(NULL, lastend, newfrag->node->ofs - lastend); if (unlikely(!holefrag)) { jffs2_free_node_frag(newfrag); return -ENOMEM; } if (this) { /* By definition, the 'this' node has no right-hand child, because there are no frags with offset greater than it. So that's where we want to put the hole */ dbg_fragtree2("add hole frag %#04x-%#04x on the right of the new frag.\n", holefrag->ofs, holefrag->ofs + holefrag->size); rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); } else { dbg_fragtree2("Add hole frag %#04x-%#04x to the root of the tree.\n", holefrag->ofs, holefrag->ofs + holefrag->size); rb_link_node(&holefrag->rb, NULL, &root->rb_node); } rb_insert_color(&holefrag->rb, root); this = holefrag; } if (this) { /* By definition, the 'this' node has no right-hand child, because there are no frags with offset greater than it. So that's where we want to put new fragment */ dbg_fragtree2("add the new node at the right\n"); rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); } else { dbg_fragtree2("insert the new node at the root of the tree\n"); rb_link_node(&newfrag->rb, NULL, &root->rb_node); } rb_insert_color(&newfrag->rb, root); return 0; } /* Doesn't set inode->i_size */ static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag) { struct jffs2_node_frag *this; uint32_t lastend; /* Skip all the nodes which are completed before this one starts */ this = jffs2_lookup_node_frag(root, newfrag->node->ofs); if (this) { dbg_fragtree2("lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this); lastend = this->ofs + this->size; } else { dbg_fragtree2("lookup gave no frag\n"); lastend = 0; } /* See if we ran off the end of the fragtree */ if (lastend <= newfrag->ofs) { /* We did */ /* Check if 'this' node was on the same page as the new node. If so, both 'this' and the new node get marked REF_NORMAL so the GC can take a look. */ if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { if (this->node) mark_ref_normal(this->node->raw); mark_ref_normal(newfrag->node->raw); } return no_overlapping_node(c, root, newfrag, this, lastend); } if (this->node) dbg_fragtree2("dealing with frag %u-%u, phys %#08x(%d).\n", this->ofs, this->ofs + this->size, ref_offset(this->node->raw), ref_flags(this->node->raw)); else dbg_fragtree2("dealing with hole frag %u-%u.\n", this->ofs, this->ofs + this->size); /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes, * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs */ if (newfrag->ofs > this->ofs) { /* This node isn't completely obsoleted. The start of it remains valid */ /* Mark the new node and the partially covered node REF_NORMAL -- let the GC take a look at them */ mark_ref_normal(newfrag->node->raw); if (this->node) mark_ref_normal(this->node->raw); if (this->ofs + this->size > newfrag->ofs + newfrag->size) { /* The new node splits 'this' frag into two */ struct jffs2_node_frag *newfrag2; if (this->node) dbg_fragtree2("split old frag 0x%04x-0x%04x, phys 0x%08x\n", this->ofs, this->ofs+this->size, ref_offset(this->node->raw)); else dbg_fragtree2("split old hole frag 0x%04x-0x%04x\n", this->ofs, this->ofs+this->size); /* New second frag pointing to this's node */ newfrag2 = new_fragment(this->node, newfrag->ofs + newfrag->size, this->ofs + this->size - newfrag->ofs - newfrag->size); if (unlikely(!newfrag2)) return -ENOMEM; if (this->node) this->node->frags++; /* Adjust size of original 'this' */ this->size = newfrag->ofs - this->ofs; /* Now, we know there's no node with offset greater than this->ofs but smaller than newfrag2->ofs or newfrag->ofs, for obvious reasons. So we can do a tree insert from 'this' to insert newfrag, and a tree insert from newfrag to insert newfrag2. */ jffs2_fragtree_insert(newfrag, this); rb_insert_color(&newfrag->rb, root); jffs2_fragtree_insert(newfrag2, newfrag); rb_insert_color(&newfrag2->rb, root); return 0; } /* New node just reduces 'this' frag in size, doesn't split it */ this->size = newfrag->ofs - this->ofs; /* Again, we know it lives down here in the tree */ jffs2_fragtree_insert(newfrag, this); rb_insert_color(&newfrag->rb, root); } else { /* New frag starts at the same point as 'this' used to. Replace it in the tree without doing a delete and insertion */ dbg_fragtree2("inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n", newfrag, newfrag->ofs, newfrag->ofs+newfrag->size, this, this->ofs, this->ofs+this->size); rb_replace_node(&this->rb, &newfrag->rb, root); if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { dbg_fragtree2("obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size); jffs2_obsolete_node_frag(c, this); } else { this->ofs += newfrag->size; this->size -= newfrag->size; jffs2_fragtree_insert(this, newfrag); rb_insert_color(&this->rb, root); return 0; } } /* OK, now we have newfrag added in the correct place in the tree, but frag_next(newfrag) may be a fragment which is overlapped by it */ while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { /* 'this' frag is obsoleted completely. */ dbg_fragtree2("obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size); rb_erase(&this->rb, root); jffs2_obsolete_node_frag(c, this); } /* Now we're pointing at the first frag which isn't totally obsoleted by the new frag */ if (!this || newfrag->ofs + newfrag->size == this->ofs) return 0; /* Still some overlap but we don't need to move it in the tree */ this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); this->ofs = newfrag->ofs + newfrag->size; /* And mark them REF_NORMAL so the GC takes a look at them */ if (this->node) mark_ref_normal(this->node->raw); mark_ref_normal(newfrag->node->raw); return 0; } /* * Given an inode, probably with existing tree of fragments, add the new node * to the fragment tree. */ int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) { int ret; struct jffs2_node_frag *newfrag; if (unlikely(!fn->size)) return 0; newfrag = new_fragment(fn, fn->ofs, fn->size); if (unlikely(!newfrag)) return -ENOMEM; newfrag->node->frags = 1; dbg_fragtree("adding node %#04x-%#04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); if (unlikely(ret)) return ret; /* If we now share a page with other nodes, mark either previous or next node REF_NORMAL, as appropriate. */ if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { struct jffs2_node_frag *prev = frag_prev(newfrag); mark_ref_normal(fn->raw); /* If we don't start at zero there's _always_ a previous */ if (prev->node) mark_ref_normal(prev->node->raw); } if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { struct jffs2_node_frag *next = frag_next(newfrag); if (next) { mark_ref_normal(fn->raw); if (next->node) mark_ref_normal(next->node->raw); } } jffs2_dbg_fragtree_paranoia_check_nolock(f); return 0; } /* * Check the data CRC of the node. * * Returns: 0 if the data CRC is correct; * 1 - if incorrect; * error code if an error occured. */ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) { struct jffs2_raw_node_ref *ref = tn->fn->raw; int err = 0, pointed = 0; struct jffs2_eraseblock *jeb; unsigned char *buffer; uint32_t crc, ofs, len; size_t retlen; BUG_ON(tn->csize == 0); if (!jffs2_is_writebuffered(c)) goto adj_acc; /* Calculate how many bytes were already checked */ ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode); len = ofs % c->wbuf_pagesize; if (likely(len)) len = c->wbuf_pagesize - len; if (len >= tn->csize) { dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", ref_offset(ref), tn->csize, ofs); goto adj_acc; } ofs += len; len = tn->csize - len; dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n", ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len); #ifndef __ECOS /* TODO: instead, incapsulate point() stuff to jffs2_flash_read(), * adding and jffs2_flash_read_end() interface. */ if (c->mtd->point) { err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); if (!err && retlen < tn->csize) { JFFS2_WARNING("MTD point returned len too short: %zu " "instead of %u.\n", retlen, tn->csize); c->mtd->unpoint(c->mtd, buffer, ofs, len); } else if (err) JFFS2_WARNING("MTD point failed: error code %d.\n", err); else pointed = 1; /* succefully pointed to device */ } #endif if (!pointed) { buffer = kmalloc(len, GFP_KERNEL); if (unlikely(!buffer)) return -ENOMEM; /* TODO: this is very frequent pattern, make it a separate * routine */ err = jffs2_flash_read(c, ofs, len, &retlen, buffer); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err); goto free_out; } if (retlen != len) { JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len); err = -EIO; goto free_out; } } /* Continue calculating CRC */ crc = crc32(tn->partial_crc, buffer, len); if(!pointed) kfree(buffer); #ifndef __ECOS else c->mtd->unpoint(c->mtd, buffer, ofs, len); #endif if (crc != tn->data_crc) { JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", ofs, tn->data_crc, crc); return 1; } adj_acc: jeb = &c->blocks[ref->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, ref); /* * Mark the node as having been checked and fix the * accounting accordingly. */ spin_lock(&c->erase_completion_lock); jeb->used_size += len; jeb->unchecked_size -= len; c->used_size += len; c->unchecked_size -= len; spin_unlock(&c->erase_completion_lock); return 0; free_out: if(!pointed) kfree(buffer); #ifndef __ECOS else c->mtd->unpoint(c->mtd, buffer, ofs, len); #endif return err; } /* * Helper function for jffs2_add_older_frag_to_fragtree(). * * Checks the node if we are in the checking stage. */ static int check_node(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn) { int ret; BUG_ON(ref_obsolete(tn->fn->raw)); /* We only check the data CRC of unchecked nodes */ if (ref_flags(tn->fn->raw) != REF_UNCHECKED) return 0; dbg_fragtree2("check node %#04x-%#04x, phys offs %#08x.\n", tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw)); ret = check_node_data(c, tn); if (unlikely(ret < 0)) { JFFS2_ERROR("check_node_data() returned error: %d.\n", ret); } else if (unlikely(ret > 0)) { dbg_fragtree2("CRC error, mark it obsolete.\n"); jffs2_mark_node_obsolete(c, tn->fn->raw); } return ret; } /* * Helper function for jffs2_add_older_frag_to_fragtree(). * * Called when the new fragment that is being inserted * splits a hole fragment. */ static int split_hole(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag, struct jffs2_node_frag *hole) { dbg_fragtree2("fragment %#04x-%#04x splits the hole %#04x-%#04x\n", newfrag->ofs, newfrag->ofs + newfrag->size, hole->ofs, hole->ofs + hole->size); if (hole->ofs == newfrag->ofs) { /* * Well, the new fragment actually starts at the same offset as * the hole. */ if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { /* * We replace the overlapped left part of the hole by * the new node. */ dbg_fragtree2("insert fragment %#04x-%#04x and cut the left part of the hole\n", newfrag->ofs, newfrag->ofs + newfrag->size); rb_replace_node(&hole->rb, &newfrag->rb, root); hole->ofs += newfrag->size; hole->size -= newfrag->size; /* * We know that 'hole' should be the right hand * fragment. */ jffs2_fragtree_insert(hole, newfrag); rb_insert_color(&hole->rb, root); } else { /* * Ah, the new fragment is of the same size as the hole. * Relace the hole by it. */ dbg_fragtree2("insert fragment %#04x-%#04x and overwrite hole\n", newfrag->ofs, newfrag->ofs + newfrag->size); rb_replace_node(&hole->rb, &newfrag->rb, root); jffs2_free_node_frag(hole); } } else { /* The new fragment lefts some hole space at the left */ struct jffs2_node_frag * newfrag2 = NULL; if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { /* The new frag also lefts some space at the right */ newfrag2 = new_fragment(NULL, newfrag->ofs + newfrag->size, hole->ofs + hole->size - newfrag->ofs - newfrag->size); if (unlikely(!newfrag2)) { jffs2_free_node_frag(newfrag); return -ENOMEM; } } hole->size = newfrag->ofs - hole->ofs; dbg_fragtree2("left the hole %#04x-%#04x at the left and inserd fragment %#04x-%#04x\n", hole->ofs, hole->ofs + hole->size, newfrag->ofs, newfrag->ofs + newfrag->size); jffs2_fragtree_insert(newfrag, hole); rb_insert_color(&newfrag->rb, root); if (newfrag2) { dbg_fragtree2("left the hole %#04x-%#04x at the right\n", newfrag2->ofs, newfrag2->ofs + newfrag2->size); jffs2_fragtree_insert(newfrag2, newfrag); rb_insert_color(&newfrag2->rb, root); } } return 0; } /* * This function is used when we build inode. It expects the nodes are passed * in the decreasing version order. The whole point of this is to improve the * inodes checking on NAND: we check the nodes' data CRC only when they are not * obsoleted. Previously, add_frag_to_fragtree() function was used and * nodes were passed to it in the increasing version ordes and CRCs of all * nodes were checked. * * Note: tn->fn->size shouldn't be zero. * * Returns 0 if the node was inserted * 1 if it wasn't inserted (since it is obsolete) * < 0 an if error occured */ int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn) { struct jffs2_node_frag *this, *newfrag; uint32_t lastend; struct jffs2_full_dnode *fn = tn->fn; struct rb_root *root = &f->fragtree; uint32_t fn_size = fn->size, fn_ofs = fn->ofs; int err, checked = 0; int ref_flag; dbg_fragtree("insert fragment %#04x-%#04x, ver %u\n", fn_ofs, fn_ofs + fn_size, tn->version); /* Skip all the nodes which are completed before this one starts */ this = jffs2_lookup_node_frag(root, fn_ofs); if (this) dbg_fragtree2("'this' found %#04x-%#04x (%s)\n", this->ofs, this->ofs + this->size, this->node ? "data" : "hole"); if (this) lastend = this->ofs + this->size; else lastend = 0; /* Detect the preliminary type of node */ if (fn->size >= PAGE_CACHE_SIZE) ref_flag = REF_PRISTINE; else ref_flag = REF_NORMAL; /* See if we ran off the end of the root */ if (lastend <= fn_ofs) { /* We did */ /* * We are going to insert the new node into the * fragment tree, so check it. */ err = check_node(c, f, tn); if (err != 0) return err; fn->frags = 1; newfrag = new_fragment(fn, fn_ofs, fn_size); if (unlikely(!newfrag)) return -ENOMEM; err = no_overlapping_node(c, root, newfrag, this, lastend); if (unlikely(err != 0)) { jffs2_free_node_frag(newfrag); return err; } goto out_ok; } fn->frags = 0; while (1) { /* * Here we have: * fn_ofs < this->ofs + this->size && fn_ofs >= this->ofs. * * Remember, 'this' has higher version, any non-hole node * which is already in the fragtree is newer then the newly * inserted. */ if (!this->node) { /* * 'this' is the hole fragment, so at least the * beginning of the new fragment is valid. */ /* * We are going to insert the new node into the * fragment tree, so check it. */ if (!checked) { err = check_node(c, f, tn); if (unlikely(err != 0)) return err; checked = 1; } if (this->ofs + this->size >= fn_ofs + fn_size) { /* We split the hole on two parts */ fn->frags += 1; newfrag = new_fragment(fn, fn_ofs, fn_size); if (unlikely(!newfrag)) return -ENOMEM; err = split_hole(c, root, newfrag, this); if (unlikely(err)) return err; goto out_ok; } /* * The beginning of the new fragment is valid since it * overlaps the hole node. */ ref_flag = REF_NORMAL; fn->frags += 1; newfrag = new_fragment(fn, fn_ofs, this->ofs + this->size - fn_ofs); if (unlikely(!newfrag)) return -ENOMEM; if (fn_ofs == this->ofs) { /* * The new node starts at the same offset as * the hole and supersieds the hole. */ dbg_fragtree2("add the new fragment instead of hole %#04x-%#04x, refcnt %d\n", fn_ofs, fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); rb_replace_node(&this->rb, &newfrag->rb, root); jffs2_free_node_frag(this); } else { /* * The hole becomes shorter as its right part * is supersieded by the new fragment. */ dbg_fragtree2("reduce size of hole %#04x-%#04x to %#04x-%#04x\n", this->ofs, this->ofs + this->size, this->ofs, this->ofs + this->size - newfrag->size); dbg_fragtree2("add new fragment %#04x-%#04x, refcnt %d\n", fn_ofs, fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); this->size -= newfrag->size; jffs2_fragtree_insert(newfrag, this); rb_insert_color(&newfrag->rb, root); } fn_ofs += newfrag->size; fn_size -= newfrag->size; this = rb_entry(rb_next(&newfrag->rb), struct jffs2_node_frag, rb); dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); } /* * 'This' node is not the hole so it obsoletes the new fragment * either fully or partially. */ if (this->ofs + this->size >= fn_ofs + fn_size) { /* The new node is obsolete, drop it */ if (fn->frags == 0) { dbg_fragtree2("%#04x-%#04x is obsolete, mark it obsolete\n", fn_ofs, fn_ofs + fn_size); ref_flag = REF_OBSOLETE; } goto out_ok; } else { struct jffs2_node_frag *new_this; /* 'This' node obsoletes the beginning of the new node */ dbg_fragtree2("the beginning %#04x-%#04x is obsolete\n", fn_ofs, this->ofs + this->size); ref_flag = REF_NORMAL; fn_size -= this->ofs + this->size - fn_ofs; fn_ofs = this->ofs + this->size; dbg_fragtree2("now considering %#04x-%#04x\n", fn_ofs, fn_ofs + fn_size); new_this = rb_entry(rb_next(&this->rb), struct jffs2_node_frag, rb); if (!new_this) { /* * There is no next fragment. Add the rest of * the new node as the right-hand child. */ if (!checked) { err = check_node(c, f, tn); if (unlikely(err != 0)) return err; checked = 1; } fn->frags += 1; newfrag = new_fragment(fn, fn_ofs, fn_size); if (unlikely(!newfrag)) return -ENOMEM; dbg_fragtree2("there are no more fragments, insert %#04x-%#04x\n", newfrag->ofs, newfrag->ofs + newfrag->size); rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); rb_insert_color(&newfrag->rb, root); goto out_ok; } else { this = new_this; dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); } } } out_ok: BUG_ON(fn->size < PAGE_CACHE_SIZE && ref_flag == REF_PRISTINE); if (ref_flag == REF_OBSOLETE) { dbg_fragtree2("the node is obsolete now\n"); /* jffs2_mark_node_obsolete() will adjust space accounting */ jffs2_mark_node_obsolete(c, fn->raw); return 1; } dbg_fragtree2("the node is \"%s\" now\n", ref_flag == REF_NORMAL ? "REF_NORMAL" : "REF_PRISTINE"); /* Space accounting was adjusted at check_node_data() */ spin_lock(&c->erase_completion_lock); fn->raw->flash_offset = ref_offset(fn->raw) | ref_flag; spin_unlock(&c->erase_completion_lock); return 0; } void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) { spin_lock(&c->inocache_lock); ic->state = state; wake_up(&c->inocache_wq); spin_unlock(&c->inocache_lock); } /* During mount, this needs no locking. During normal operation, its callers want to do other stuff while still holding the inocache_lock. Rather than introducing special case get_ino_cache functions or callbacks, we just let the caller do the locking itself. */ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inode_cache *ret; ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; while (ret && ret->ino < ino) { ret = ret->next; } if (ret && ret->ino != ino) ret = NULL; return ret; } void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new) { struct jffs2_inode_cache **prev; spin_lock(&c->inocache_lock); if (!new->ino) new->ino = ++c->highest_ino; dbg_inocache("add %p (ino #%u)\n", new, new->ino); prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE]; while ((*prev) && (*prev)->ino < new->ino) { prev = &(*prev)->next; } new->next = *prev; *prev = new; spin_unlock(&c->inocache_lock); } void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old) { struct jffs2_inode_cache **prev; dbg_inocache("del %p (ino #%u)\n", old, old->ino); spin_lock(&c->inocache_lock); prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE]; while ((*prev) && (*prev)->ino < old->ino) { prev = &(*prev)->next; } if ((*prev) == old) { *prev = old->next; } /* Free it now unless it's in READING or CLEARING state, which are the transitions upon read_inode() and clear_inode(). The rest of the time we know nobody else is looking at it, and if it's held by read_inode() or clear_inode() they'll free it for themselves. */ if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING) jffs2_free_inode_cache(old); spin_unlock(&c->inocache_lock); } void jffs2_free_ino_caches(struct jffs2_sb_info *c) { int i; struct jffs2_inode_cache *this, *next; for (i=0; iinocache_list[i]; while (this) { next = this->next; jffs2_free_inode_cache(this); this = next; } c->inocache_list[i] = NULL; } } void jffs2_free_raw_node_refs(struct jffs2_sb_info *c) { int i; struct jffs2_raw_node_ref *this, *next; for (i=0; inr_blocks; i++) { this = c->blocks[i].first_node; while(this) { next = this->next_phys; jffs2_free_raw_node_ref(this); this = next; } c->blocks[i].first_node = c->blocks[i].last_node = NULL; } } struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset) { /* The common case in lookup is that there will be a node which precisely matches. So we go looking for that first */ struct rb_node *next; struct jffs2_node_frag *prev = NULL; struct jffs2_node_frag *frag = NULL; dbg_fragtree2("root %p, offset %d\n", fragtree, offset); next = fragtree->rb_node; while(next) { frag = rb_entry(next, struct jffs2_node_frag, rb); if (frag->ofs + frag->size <= offset) { /* Remember the closest smaller match on the way down */ if (!prev || frag->ofs > prev->ofs) prev = frag; next = frag->rb.rb_right; } else if (frag->ofs > offset) { next = frag->rb.rb_left; } else { return frag; } } /* Exact match not found. Go back up looking at each parent, and return the closest smaller one */ if (prev) dbg_fragtree2("no match. Returning frag %#04x-%#04x, closest previous\n", prev->ofs, prev->ofs+prev->size); else dbg_fragtree2("returning NULL, empty fragtree\n"); return prev; } /* Pass 'c' argument to indicate that nodes should be marked obsolete as they're killed. */ void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c) { struct jffs2_node_frag *frag; struct jffs2_node_frag *parent; if (!root->rb_node) return; dbg_fragtree("killing\n"); frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); while(frag) { if (frag->rb.rb_left) { frag = frag_left(frag); continue; } if (frag->rb.rb_right) { frag = frag_right(frag); continue; } if (frag->node && !(--frag->node->frags)) { /* Not a hole, and it's the final remaining frag of this node. Free the node */ if (c) jffs2_mark_node_obsolete(c, frag->node->raw); jffs2_free_full_dnode(frag->node); } parent = frag_parent(frag); if (parent) { if (frag_left(parent) == frag) parent->rb.rb_left = NULL; else parent->rb.rb_right = NULL; } jffs2_free_node_frag(frag); frag = parent; cond_resched(); } } linux-2.6.17/fs/jffs2/.svn/text-base/fs.c.svn-base0000444000000000000000000004424210574207642020152 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: fs.c,v 1.66 2005/09/27 13:17:29 dedekind Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "nodelist.h" static int jffs2_flash_setup(struct jffs2_sb_info *c); static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) { struct jffs2_full_dnode *old_metadata, *new_metadata; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; unsigned short dev; unsigned char *mdata = NULL; int mdatalen = 0; unsigned int ivalid; uint32_t phys_ofs, alloclen; int ret; D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); ret = inode_change_ok(inode, iattr); if (ret) return ret; /* Special cases - we don't want more than one data node for these types on the medium at any time. So setattr must read the original data associated with the node (i.e. the device numbers or the target name) and write it out again with the appropriate data attached */ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ dev = old_encode_dev(inode->i_rdev); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(inode->i_mode)) { mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) return -ENOMEM; ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { kfree(mdata); return ret; } D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); } ri = jffs2_alloc_raw_inode(); if (!ri) { if (S_ISLNK(inode->i_mode)) kfree(mdata); return -ENOMEM; } ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); if (S_ISLNK(inode->i_mode & S_IFMT)) kfree(mdata); return ret; } down(&f->sem); ivalid = iattr->ia_valid; ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->ino = cpu_to_je32(inode->i_ino); ri->version = cpu_to_je32(++f->highest_version); ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid); ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid); if (ivalid & ATTR_MODE) if (iattr->ia_mode & S_ISGID && !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID)) ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID); else ri->mode = cpu_to_jemode(iattr->ia_mode); else ri->mode = cpu_to_jemode(inode->i_mode); ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size); ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime)); ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime)); ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime)); ri->offset = cpu_to_je32(0); ri->csize = ri->dsize = cpu_to_je32(mdatalen); ri->compr = JFFS2_COMPR_NONE; if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { /* It's an extension. Make it a hole node */ ri->compr = JFFS2_COMPR_ZERO; ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ri->offset = cpu_to_je32(inode->i_size); } ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); if (mdatalen) ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); else ri->data_crc = cpu_to_je32(0); new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL); if (S_ISLNK(inode->i_mode)) kfree(mdata); if (IS_ERR(new_metadata)) { jffs2_complete_reservation(c); jffs2_free_raw_inode(ri); up(&f->sem); return PTR_ERR(new_metadata); } /* It worked. Update the inode */ inode->i_atime = ITIME(je32_to_cpu(ri->atime)); inode->i_ctime = ITIME(je32_to_cpu(ri->ctime)); inode->i_mtime = ITIME(je32_to_cpu(ri->mtime)); inode->i_mode = jemode_to_cpu(ri->mode); inode->i_uid = je16_to_cpu(ri->uid); inode->i_gid = je16_to_cpu(ri->gid); old_metadata = f->metadata; if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) jffs2_truncate_fragtree (c, &f->fragtree, iattr->ia_size); if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { jffs2_add_full_dnode_to_inode(c, f, new_metadata); inode->i_size = iattr->ia_size; f->metadata = NULL; } else { f->metadata = new_metadata; } if (old_metadata) { jffs2_mark_node_obsolete(c, old_metadata->raw); jffs2_free_full_dnode(old_metadata); } jffs2_free_raw_inode(ri); up(&f->sem); jffs2_complete_reservation(c); /* We have to do the vmtruncate() without f->sem held, since some pages may be locked and waiting for it in readpage(). We are protected from a simultaneous write() extending i_size back past iattr->ia_size, because do_truncate() holds the generic inode semaphore. */ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) vmtruncate(inode, iattr->ia_size); return 0; } int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) { return jffs2_do_setattr(dentry->d_inode, iattr); } int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); unsigned long avail; buf->f_type = JFFS2_SUPER_MAGIC; buf->f_bsize = 1 << PAGE_SHIFT; buf->f_blocks = c->flash_size >> PAGE_SHIFT; buf->f_files = 0; buf->f_ffree = 0; buf->f_namelen = JFFS2_MAX_NAME_LEN; spin_lock(&c->erase_completion_lock); avail = c->dirty_size + c->free_size; if (avail > c->sector_size * c->resv_blocks_write) avail -= c->sector_size * c->resv_blocks_write; else avail = 0; spin_unlock(&c->erase_completion_lock); buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; return 0; } void jffs2_clear_inode (struct inode *inode) { /* We can forget about this inode for now - drop all * the nodelists associated with it, etc. */ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); jffs2_do_clear_inode(c, f); } void jffs2_read_inode (struct inode *inode) { struct jffs2_inode_info *f; struct jffs2_sb_info *c; struct jffs2_raw_inode latest_node; int ret; D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); f = JFFS2_INODE_INFO(inode); c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); down(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); if (ret) { make_bad_inode(inode); up(&f->sem); return; } inode->i_mode = jemode_to_cpu(latest_node.mode); inode->i_uid = je16_to_cpu(latest_node.uid); inode->i_gid = je16_to_cpu(latest_node.gid); inode->i_size = je32_to_cpu(latest_node.isize); inode->i_atime = ITIME(je32_to_cpu(latest_node.atime)); inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime)); inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime)); inode->i_nlink = f->inocache->nlink; inode->i_blksize = PAGE_SIZE; inode->i_blocks = (inode->i_size + 511) >> 9; switch (inode->i_mode & S_IFMT) { jint16_t rdev; case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; break; case S_IFDIR: { struct jffs2_full_dirent *fd; for (fd=f->dents; fd; fd = fd->next) { if (fd->type == DT_DIR && fd->ino) inode->i_nlink++; } /* and '..' */ inode->i_nlink++; /* Root dir gets i_nlink 3 for some reason */ if (inode->i_ino == 1) inode->i_nlink++; inode->i_op = &jffs2_dir_inode_operations; inode->i_fop = &jffs2_dir_operations; break; } case S_IFREG: inode->i_op = &jffs2_file_inode_operations; inode->i_fop = &jffs2_file_operations; inode->i_mapping->a_ops = &jffs2_file_address_operations; inode->i_mapping->nrpages = 0; break; case S_IFBLK: case S_IFCHR: /* Read the device numbers from the media */ D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); up(&f->sem); jffs2_do_clear_inode(c, f); make_bad_inode(inode); return; } case S_IFSOCK: case S_IFIFO: inode->i_op = &jffs2_file_inode_operations; init_special_inode(inode, inode->i_mode, old_decode_dev((je16_to_cpu(rdev)))); break; default: printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); } up(&f->sem); D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); } void jffs2_dirty_inode(struct inode *inode) { struct iattr iattr; if (!(inode->i_state & I_DIRTY_DATASYNC)) { D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); return; } D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino)); iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME; iattr.ia_mode = inode->i_mode; iattr.ia_uid = inode->i_uid; iattr.ia_gid = inode->i_gid; iattr.ia_atime = inode->i_atime; iattr.ia_mtime = inode->i_mtime; iattr.ia_ctime = inode->i_ctime; jffs2_do_setattr(inode, &iattr); } int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) return -EROFS; /* We stop if it was running, then restart if it needs to. This also catches the case where it was stopped and this is just a remount to restart it. Flush the writebuffer, if neccecary, else we loose it */ if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); } if (!(*flags & MS_RDONLY)) jffs2_start_garbage_collect_thread(c); *flags |= MS_NOATIME; return 0; } void jffs2_write_super (struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); sb->s_dirt = 0; if (sb->s_flags & MS_RDONLY) return; D1(printk(KERN_DEBUG "jffs2_write_super()\n")); jffs2_garbage_collect_trigger(c); jffs2_erase_pending_blocks(c, 0); jffs2_flush_wbuf_gc(c, 0); } /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, fill in the raw_inode while you're at it. */ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) { struct inode *inode; struct super_block *sb = dir_i->i_sb; struct jffs2_sb_info *c; struct jffs2_inode_info *f; int ret; D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); c = JFFS2_SB_INFO(sb); inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); f = JFFS2_INODE_INFO(inode); jffs2_init_inode_info(f); down(&f->sem); memset(ri, 0, sizeof(*ri)); /* Set OS-specific defaults for new inodes */ ri->uid = cpu_to_je16(current->fsuid); if (dir_i->i_mode & S_ISGID) { ri->gid = cpu_to_je16(dir_i->i_gid); if (S_ISDIR(mode)) mode |= S_ISGID; } else { ri->gid = cpu_to_je16(current->fsgid); } ri->mode = cpu_to_jemode(mode); ret = jffs2_do_new_inode (c, f, mode, ri); if (ret) { make_bad_inode(inode); iput(inode); return ERR_PTR(ret); } inode->i_nlink = 1; inode->i_ino = je32_to_cpu(ri->ino); inode->i_mode = jemode_to_cpu(ri->mode); inode->i_gid = je16_to_cpu(ri->gid); inode->i_uid = je16_to_cpu(ri->uid); inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); inode->i_blksize = PAGE_SIZE; inode->i_blocks = 0; inode->i_size = 0; insert_inode_hash(inode); return inode; } int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) { struct jffs2_sb_info *c; struct inode *root_i; int ret; size_t blocks; c = JFFS2_SB_INFO(sb); #ifndef CONFIG_JFFS2_FS_WRITEBUFFER if (c->mtd->type == MTD_NANDFLASH) { printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n"); return -EINVAL; } if (c->mtd->type == MTD_DATAFLASH) { printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n"); return -EINVAL; } #endif c->flash_size = c->mtd->size; c->sector_size = c->mtd->erasesize; blocks = c->flash_size / c->sector_size; /* * Size alignment check */ if ((c->sector_size * blocks) != c->flash_size) { c->flash_size = c->sector_size * blocks; printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n", c->flash_size / 1024); } if (c->flash_size < 5*c->sector_size) { printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size); return -EINVAL; } c->cleanmarker_size = sizeof(struct jffs2_unknown_node); /* NAND (or other bizarre) flash... do setup accordingly */ ret = jffs2_flash_setup(c); if (ret) return ret; c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); if (!c->inocache_list) { ret = -ENOMEM; goto out_wbuf; } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; ret = -EINVAL; D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); root_i = iget(sb, 1); if (is_bad_inode(root_i)) { D1(printk(KERN_WARNING "get root inode failed\n")); goto out_root_i; } D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); sb->s_root = d_alloc_root(root_i); if (!sb->s_root) goto out_root_i; sb->s_maxbytes = 0xFFFFFFFF; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = JFFS2_SUPER_MAGIC; if (!(sb->s_flags & MS_RDONLY)) jffs2_start_garbage_collect_thread(c); return 0; out_root_i: iput(root_i); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); if (jffs2_blocks_use_vmalloc(c)) vfree(c->blocks); else kfree(c->blocks); out_inohash: kfree(c->inocache_list); out_wbuf: jffs2_flash_cleanup(c); return ret; } void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { iput(OFNI_EDONI_2SFFJ(f)); } struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink) { struct inode *inode; struct jffs2_inode_cache *ic; if (!nlink) { /* The inode has zero nlink but its nodes weren't yet marked obsolete. This has to be because we're still waiting for the final (close() and) iput() to happen. There's a possibility that the final iput() could have happened while we were contemplating. In order to ensure that we don't cause a new read_inode() (which would fail) for the inode in question, we use ilookup() in this case instead of iget(). The nlink can't _become_ zero at this point because we're holding the alloc_sem, and jffs2_do_unlink() would also need that while decrementing nlink on any inode. */ inode = ilookup(OFNI_BS_2SFFJ(c), inum); if (!inode) { D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", inum)); spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, inum); if (!ic) { D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); spin_unlock(&c->inocache_lock); return NULL; } if (ic->state != INO_STATE_CHECKEDABSENT) { /* Wait for progress. Don't just loop */ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); } else { spin_unlock(&c->inocache_lock); } return NULL; } } else { /* Inode has links to it still; they're not going away because jffs2_do_unlink() would need the alloc_sem and we have it. Just iget() it, and if read_inode() is necessary that's OK. */ inode = iget(OFNI_BS_2SFFJ(c), inum); if (!inode) return ERR_PTR(-ENOMEM); } if (is_bad_inode(inode)) { printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", inum, nlink); /* NB. This will happen again. We need to do something appropriate here. */ iput(inode); return ERR_PTR(-EIO); } return JFFS2_INODE_INFO(inode); } unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned long offset, unsigned long *priv) { struct inode *inode = OFNI_EDONI_2SFFJ(f); struct page *pg; pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); if (IS_ERR(pg)) return (void *)pg; *priv = (unsigned long)pg; return kmap(pg); } void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *ptr, unsigned long *priv) { struct page *pg = (void *)*priv; kunmap(pg); page_cache_release(pg); } static int jffs2_flash_setup(struct jffs2_sb_info *c) { int ret = 0; if (jffs2_cleanmarker_oob(c)) { /* NAND flash... do setup accordingly */ ret = jffs2_nand_flash_setup(c); if (ret) return ret; } /* add setups for other bizarre flashes here... */ if (jffs2_nor_ecc(c)) { ret = jffs2_nor_ecc_flash_setup(c); if (ret) return ret; } /* and Dataflash */ if (jffs2_dataflash(c)) { ret = jffs2_dataflash_setup(c); if (ret) return ret; } /* and Intel "Sibley" flash */ if (jffs2_nor_wbuf_flash(c)) { ret = jffs2_nor_wbuf_flash_setup(c); if (ret) return ret; } return ret; } void jffs2_flash_cleanup(struct jffs2_sb_info *c) { if (jffs2_cleanmarker_oob(c)) { jffs2_nand_flash_cleanup(c); } /* add cleanups for other bizarre flashes here... */ if (jffs2_nor_ecc(c)) { jffs2_nor_ecc_flash_cleanup(c); } /* and DataFlash */ if (jffs2_dataflash(c)) { jffs2_dataflash_cleanup(c); } /* and Intel "Sibley" flash */ if (jffs2_nor_wbuf_flash(c)) { jffs2_nor_wbuf_flash_cleanup(c); } } linux-2.6.17/fs/jffs2/.svn/text-base/scan.c.svn-base0000444000000000000000000007244410574207642020473 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: scan.c,v 1.125 2005/09/30 13:59:13 dedekind Exp $ * */ #include #include #include #include #include #include #include #include "nodelist.h" #include "summary.h" #include "debug.h" #define DEFAULT_EMPTY_SCAN_SIZE 1024 #define noisy_printk(noise, args...) do { \ if (*(noise)) { \ printk(KERN_NOTICE args); \ (*(noise))--; \ if (!(*(noise))) { \ printk(KERN_NOTICE "Further such events for this erase block will not be printed\n"); \ } \ } \ } while(0) static uint32_t pseudo_random; static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s); /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. * Returning an error will abort the mount - bad checksums etc. should just mark the space * as dirty. */ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s); static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s); static inline int min_free(struct jffs2_sb_info *c) { uint32_t min = 2 * sizeof(struct jffs2_raw_inode); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) return c->wbuf_pagesize; #endif return min; } static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) { if (sector_size < DEFAULT_EMPTY_SCAN_SIZE) return sector_size; else return DEFAULT_EMPTY_SCAN_SIZE; } int jffs2_scan_medium(struct jffs2_sb_info *c) { int i, ret; uint32_t empty_blocks = 0, bad_blocks = 0; unsigned char *flashbuf = NULL; uint32_t buf_size = 0; struct jffs2_summary *s = NULL; /* summary info collected by the scan process */ #ifndef __ECOS size_t pointlen; if (c->mtd->point) { ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf); if (!ret && pointlen < c->mtd->size) { /* Don't muck about if it won't let us point to the whole flash */ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen)); c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); flashbuf = NULL; } if (ret) D1(printk(KERN_DEBUG "MTD point failed %d\n", ret)); } #endif if (!flashbuf) { /* For NAND it's quicker to read a whole eraseblock at a time, apparently */ if (jffs2_cleanmarker_oob(c)) buf_size = c->sector_size; else buf_size = PAGE_SIZE; /* Respect kmalloc limitations */ if (buf_size > 128*1024) buf_size = 128*1024; D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); flashbuf = kmalloc(buf_size, GFP_KERNEL); if (!flashbuf) return -ENOMEM; } if (jffs2_sum_active()) { s = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!s) { JFFS2_WARNING("Can't allocate memory for summary\n"); return -ENOMEM; } memset(s, 0, sizeof(struct jffs2_summary)); } for (i=0; inr_blocks; i++) { struct jffs2_eraseblock *jeb = &c->blocks[i]; /* 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 (ret < 0) goto out; jffs2_dbg_acct_paranoia_check_nolock(c, jeb); /* Now decide which list to put it on */ switch(ret) { case BLK_STATE_ALLFF: /* * Empty block. Since we can't be sure it * was entirely erased, we just queue it for erase * again. It will be marked as such when the erase * is complete. Meanwhile we still count it as empty * for later checks. */ empty_blocks++; list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; break; case BLK_STATE_CLEANMARKER: /* Only a CLEANMARKER node is valid */ if (!jeb->dirty_size) { /* It's actually free */ list_add(&jeb->list, &c->free_list); c->nr_free_blocks++; } else { /* Dirt */ D1(printk(KERN_DEBUG "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; } break; case BLK_STATE_CLEAN: /* Full (or almost full) of clean data. Clean list */ list_add(&jeb->list, &c->clean_list); break; case BLK_STATE_PARTDIRTY: /* Some data, but not full. Dirty list. */ /* We want to remember the block with most free space and stick it in the 'nextblock' position to start writing to it. */ if (jeb->free_size > min_free(c) && (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ if (c->nextblock) { c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->free_size -= c->nextblock->free_size; c->wasted_size -= c->nextblock->wasted_size; c->nextblock->free_size = c->nextblock->wasted_size = 0; if (VERYDIRTY(c, c->nextblock->dirty_size)) { list_add(&c->nextblock->list, &c->very_dirty_list); } else { list_add(&c->nextblock->list, &c->dirty_list); } /* deleting summary information of the old nextblock */ jffs2_sum_reset_collected(c->summary); } /* update collected summary infromation for the current nextblock */ jffs2_sum_move_collected(c, s); D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset)); c->nextblock = jeb; } else { jeb->dirty_size += jeb->free_size + jeb->wasted_size; c->dirty_size += jeb->free_size + jeb->wasted_size; c->free_size -= jeb->free_size; c->wasted_size -= jeb->wasted_size; jeb->free_size = jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { list_add(&jeb->list, &c->very_dirty_list); } else { list_add(&jeb->list, &c->dirty_list); } } break; case BLK_STATE_ALLDIRTY: /* Nothing valid - not even a clean marker. Needs erasing. */ /* For now we just put it on the erasing list. We'll start the erases later */ D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; break; case BLK_STATE_BADBLOCK: D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset)); list_add(&jeb->list, &c->bad_list); c->bad_size += c->sector_size; c->free_size -= c->sector_size; bad_blocks++; break; default: printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n"); BUG(); } } if (jffs2_sum_active() && s) kfree(s); /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */ if (c->nextblock && (c->nextblock->dirty_size)) { c->nextblock->wasted_size += c->nextblock->dirty_size; c->wasted_size += c->nextblock->dirty_size; c->dirty_size -= c->nextblock->dirty_size; c->nextblock->dirty_size = 0; } #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) { /* If we're going to start writing into a block which already contains data, and the end of the data isn't page-aligned, skip a little and align it. */ uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize; D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", skip)); c->nextblock->wasted_size += skip; c->wasted_size += skip; c->nextblock->free_size -= skip; c->free_size -= skip; } #endif if (c->nr_erasing_blocks) { if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks); ret = -EIO; goto out; } jffs2_erase_pending_trigger(c); } ret = 0; out: if (buf_size) kfree(flashbuf); #ifndef __ECOS else c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); #endif return ret; } int jffs2_fill_scan_buf (struct jffs2_sb_info *c, void *buf, uint32_t ofs, uint32_t len) { int ret; size_t retlen; ret = jffs2_flash_read(c, ofs, len, &retlen, buf); if (ret) { D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret)); return ret; } if (retlen < len) { D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen)); return -EIO; } return 0; } int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size && (!jeb->first_node || !jeb->first_node->next_phys) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { c->dirty_size -= jeb->dirty_size; c->wasted_size += jeb->dirty_size; jeb->wasted_size += jeb->dirty_size; jeb->dirty_size = 0; return BLK_STATE_CLEAN; } else if (jeb->used_size || jeb->unchecked_size) return BLK_STATE_PARTDIRTY; else return BLK_STATE_ALLDIRTY; } static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { struct jffs2_unknown_node *node; struct jffs2_unknown_node crcnode; struct jffs2_sum_marker *sm; uint32_t ofs, prevofs; uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; #ifdef CONFIG_JFFS2_FS_WRITEBUFFER int cleanmarkerfound = 0; #endif ofs = jeb->offset; prevofs = jeb->offset - 1; D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { int ret = jffs2_check_nand_cleanmarker(c, jeb); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); /* Even if it's not found, we still scan to see if the block is empty. We use this information to decide whether to erase it or not. */ switch (ret) { case 0: cleanmarkerfound = 1; break; case 1: break; case 2: return BLK_STATE_BADBLOCK; case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ default: return ret; } } #endif if (jffs2_sum_active()) { sm = kmalloc(sizeof(struct jffs2_sum_marker), GFP_KERNEL); if (!sm) { return -ENOMEM; } err = jffs2_fill_scan_buf(c, (unsigned char *) sm, jeb->offset + c->sector_size - sizeof(struct jffs2_sum_marker), sizeof(struct jffs2_sum_marker)); if (err) { kfree(sm); return err; } if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC ) { err = jffs2_sum_scan_sumnode(c, jeb, je32_to_cpu(sm->offset), &pseudo_random); if (err) { kfree(sm); return err; } } kfree(sm); ofs = jeb->offset; prevofs = jeb->offset - 1; } buf_ofs = jeb->offset; if (!buf_size) { buf_len = c->sector_size; if (jffs2_sum_active()) { /* must reread because of summary test */ err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; } } else { buf_len = EMPTY_SCAN_SIZE(c->sector_size); err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; } /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ofs = 0; /* Scan only 4KiB of 0xFF before declaring it's empty */ while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ofs += 4; if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) { #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { /* scan oob, take care of cleanmarker */ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret)); switch (ret) { case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; case 1: return BLK_STATE_ALLDIRTY; default: return ret; } } #endif D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); if (c->cleanmarker_size == 0) return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */ else return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ } if (ofs) { D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, jeb->offset + ofs)); DIRTY_SPACE(ofs); } /* Now ofs is a complete physical flash offset as it always was... */ ofs += jeb->offset; noise = 10; dbg_summary("no summary found in jeb 0x%08x. Apply original scan.\n",jeb->offset); scan_more: while(ofs < jeb->offset + c->sector_size) { jffs2_dbg_acct_paranoia_check_nolock(c, jeb); cond_resched(); if (ofs & 3) { printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); ofs = PAD(ofs); continue; } if (ofs == prevofs) { printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs); DIRTY_SPACE(4); ofs += 4; continue; } prevofs = ofs; if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node), jeb->offset, c->sector_size, ofs, sizeof(*node))); DIRTY_SPACE((jeb->offset + c->sector_size)-ofs); break; } if (buf_ofs + buf_len < ofs + sizeof(*node)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n", sizeof(struct jffs2_unknown_node), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; } node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { uint32_t inbuf_ofs; uint32_t empty_start; empty_start = ofs; ofs += 4; D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); more_empty: inbuf_ofs = ofs - buf_ofs; while (inbuf_ofs < buf_len) { if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) { printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); DIRTY_SPACE(ofs-empty_start); goto scan_more; } inbuf_ofs+=4; ofs += 4; } /* Ran off end. */ D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs)); /* If we're only checking the beginning of a block with a cleanmarker, bail now */ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) { D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); return BLK_STATE_CLEANMARKER; } /* 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) { /* No more to read. Break out of main loop without marking this range of empty space as dirty (because it's not) */ D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n", empty_start)); break; } D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; goto more_empty; } if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); DIRTY_SPACE(4); ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs)); DIRTY_SPACE(4); ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) { printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); DIRTY_SPACE(4); ofs += 4; continue; } if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { /* OK. We're out of possibilities. Whinge and move on */ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, je16_to_cpu(node->magic)); DIRTY_SPACE(4); ofs += 4; continue; } /* We seem to have a node of sorts. Check the CRC */ crcnode.magic = node->magic; crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE); crcnode.totlen = node->totlen; hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4); if (hdr_crc != je32_to_cpu(node->hdr_crc)) { noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n", ofs, je16_to_cpu(node->magic), je16_to_cpu(node->nodetype), je32_to_cpu(node->totlen), je32_to_cpu(node->hdr_crc), hdr_crc); DIRTY_SPACE(4); ofs += 4; continue; } if (ofs + je32_to_cpu(node->totlen) > jeb->offset + c->sector_size) { /* Eep. Node goes over the end of the erase block. */ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", ofs, je32_to_cpu(node->totlen)); printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); DIRTY_SPACE(4); ofs += 4; continue; } if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { /* Wheee. This is an obsoleted node */ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs)); DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); continue; } switch(je16_to_cpu(node->nodetype)) { case JFFS2_NODETYPE_INODE: if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n", sizeof(struct jffs2_raw_inode), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_DIRENT: if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n", je32_to_cpu(node->totlen), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_CLEANMARKER: D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", ofs, je32_to_cpu(node->totlen), c->cleanmarker_size); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ofs += PAD(sizeof(struct jffs2_unknown_node)); } else if (jeb->first_node) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ofs += PAD(sizeof(struct jffs2_unknown_node)); } else { struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n"); return -ENOMEM; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = ofs | REF_NORMAL; marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; USED_SPACE(PAD(c->cleanmarker_size)); ofs += PAD(c->cleanmarker_size); } break; case JFFS2_NODETYPE_PADDING: if (jffs2_sum_active()) jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen)); DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; default: switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_ROCOMPAT: printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); c->flags |= JFFS2_SB_FLAG_RO; if (!(jffs2_is_readonly(c))) return -EROFS; DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_FEATURE_INCOMPAT: printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); return -EINVAL; case JFFS2_FEATURE_RWCOMPAT_DELETE: D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); USED_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; } } } if (jffs2_sum_active()) { if (PAD(s->sum_size + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size) { dbg_summary("There is not enough space for " "summary information, disabling for this jeb!\n"); jffs2_sum_disable_collecting(s); } } D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size)); /* mark_node_obsolete can add to wasted !! */ if (jeb->wasted_size) { jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; } return jffs2_scan_classify_jeb(c, jeb); } struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inode_cache *ic; ic = jffs2_get_ino_cache(c, ino); if (ic) return ic; if (ino > c->highest_ino) c->highest_ino = ino; ic = jffs2_alloc_inode_cache(); if (!ic) { printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n"); return NULL; } memset(ic, 0, sizeof(*ic)); ic->ino = ino; ic->nodes = (void *)ic; jffs2_add_ino_cache(c, ic); if (ino == 1) ic->nlink = 1; return ic; } static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; uint32_t ino = je32_to_cpu(ri->ino); D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs)); /* We do very little here now. Just check the ino# to which we should attribute this node; we can do all the CRC checking etc. later. There's a tradeoff here -- we used to scan the flash once only, reading everything we want from it into memory, then building all our in-core data structures and freeing the extra information. Now we allow the first part of the mount to complete a lot quicker, but we have to go _back_ to the flash in order to finish the CRC checking, etc. Which means that the _full_ amount of time to get to proper write mode with GC operational may actually be _longer_ than before. Sucks to be me. */ raw = jffs2_alloc_raw_node_ref(); if (!raw) { printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n"); return -ENOMEM; } ic = jffs2_get_ino_cache(c, ino); if (!ic) { /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the first node we found for this inode. Do a CRC check to protect against the former case */ uint32_t crc = crc32(0, ri, sizeof(*ri)-8); if (crc != je32_to_cpu(ri->node_crc)) { printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(ri->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); jffs2_free_raw_node_ref(raw); return 0; } ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) { jffs2_free_raw_node_ref(raw); return -ENOMEM; } } /* Wheee. It worked */ raw->flash_offset = ofs | REF_UNCHECKED; raw->__totlen = PAD(je32_to_cpu(ri->totlen)); raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", je32_to_cpu(ri->ino), je32_to_cpu(ri->version), je32_to_cpu(ri->offset), je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize))); pseudo_random += je32_to_cpu(ri->version); UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen))); if (jffs2_sum_active()) { jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset); } return 0; } static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; struct jffs2_inode_cache *ic; uint32_t crc; D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); /* We don't get here unless the node is still valid, so we don't have to mask in the ACCURATE bit any more. */ crc = crc32(0, rd, sizeof(*rd)-8); if (crc != je32_to_cpu(rd->node_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); return 0; } pseudo_random += je32_to_cpu(rd->version); fd = jffs2_alloc_full_dirent(rd->nsize+1); if (!fd) { return -ENOMEM; } memcpy(&fd->name, rd->name, rd->nsize); fd->name[rd->nsize] = 0; crc = crc32(0, fd->name, rd->nsize); if (crc != je32_to_cpu(rd->name_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->name_crc), crc); D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino))); jffs2_free_full_dirent(fd); /* FIXME: Why do we believe totlen? */ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); return 0; } raw = jffs2_alloc_raw_node_ref(); if (!raw) { jffs2_free_full_dirent(fd); printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); if (!ic) { jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); return -ENOMEM; } raw->__totlen = PAD(je32_to_cpu(rd->totlen)); raw->flash_offset = ofs | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; fd->raw = raw; fd->next = NULL; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(fd->name, rd->nsize); fd->type = rd->type; USED_SPACE(PAD(je32_to_cpu(rd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); if (jffs2_sum_active()) { jffs2_sum_add_dirent_mem(s, rd, ofs - jeb->offset); } return 0; } static int count_list(struct list_head *l) { uint32_t count = 0; struct list_head *tmp; list_for_each(tmp, l) { count++; } return count; } /* Note: This breaks if list_empty(head). I don't care. You might, if you copy this code and use it elsewhere :) */ static void rotate_list(struct list_head *head, uint32_t count) { struct list_head *n = head->next; list_del(head); while(count--) { n = n->next; } list_add(head, n); } void jffs2_rotate_lists(struct jffs2_sb_info *c) { uint32_t x; uint32_t rotateby; x = count_list(&c->clean_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->clean_list), rotateby); } x = count_list(&c->very_dirty_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->very_dirty_list), rotateby); } x = count_list(&c->dirty_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->dirty_list), rotateby); } x = count_list(&c->erasable_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->erasable_list), rotateby); } if (c->nr_erasing_blocks) { rotateby = pseudo_random % c->nr_erasing_blocks; rotate_list((&c->erase_pending_list), rotateby); } if (c->nr_free_blocks) { rotateby = pseudo_random % c->nr_free_blocks; rotate_list((&c->free_list), rotateby); } } linux-2.6.17/fs/jffs2/.svn/text-base/comprtest.c.svn-base0000444000000000000000000006366210574207642021571 0ustar rootroot/* $Id: comprtest.c,v 1.6 2005/11/07 11:14:38 gleixner Exp $ */ #include #include #include #include #if 0 #define TESTDATA_LEN 512 static unsigned char testdata[TESTDATA_LEN] = { 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xb0, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x06, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x80, 0x04, 0x08, 0x34, 0x80, 0x04, 0x08, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xf4, 0x80, 0x04, 0x08, 0xf4, 0x80, 0x04, 0x08, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x0d, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x10, 0x95, 0x04, 0x08, 0x10, 0x95, 0x04, 0x08, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x58, 0x95, 0x04, 0x08, 0x58, 0x95, 0x04, 0x08, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x81, 0x04, 0x08, 0x08, 0x81, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2e, 0x73, 0x6f, 0x2e, 0x32, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x0c, 0x83, 0x04, 0x08, 0x81, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1c, 0x83, 0x04, 0x08, 0xac, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x2c, 0x83, 0x04, 0x08, 0xdd, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x3c, 0x83, 0x04, 0x08, 0x2e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x4c, 0x83, 0x04, 0x08, 0x7d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x85, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x67, 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, 0x63, 0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x5f, 0x5f, 0x63}; #else #define TESTDATA_LEN 3481 static unsigned char testdata[TESTDATA_LEN] = { 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x22, 0x64, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x62, 0x75, 0x66, 0x5b, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5d, 0x3b, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x7d, 0x20, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5d, 0x3b, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x3b, 0x0a, 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x49, 0x4e, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x3d, 0x20, 0x73, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x7c, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x7c, 0x3d, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2c, 0x20, 0x30, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x26, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3e, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3c, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x69, 0x74, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x20, 0x25, 0x20, 0x31, 0x30, 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x62, 0x75, 0x66, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6d, 0x65, 0x6d, 0x73, 0x65, 0x74, 0x28, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x31, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x30, 0x37, 0x30, 0x30, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6e, 0x65, 0x77, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6f, 0x6c, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6e, 0x65, 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x26, 0x73, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x53, 0x5f, 0x49, 0x53, 0x44, 0x49, 0x52, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x35, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x35, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a }; #endif static unsigned char comprbuf[TESTDATA_LEN]; static unsigned char decomprbuf[TESTDATA_LEN]; int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *datalen, uint32_t *cdatalen); int init_module(void ) { unsigned char comprtype; uint32_t c, d; int ret; printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", testdata[0],testdata[1],testdata[2],testdata[3], testdata[4],testdata[5],testdata[6],testdata[7], testdata[8],testdata[9],testdata[10],testdata[11], testdata[12],testdata[13],testdata[14],testdata[15]); d = TESTDATA_LEN; c = TESTDATA_LEN; comprtype = jffs2_compress(testdata, comprbuf, &d, &c); printk("jffs2_compress used compression type %d. Compressed size %d, uncompressed size %d\n", comprtype, c, d); printk("Compressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", comprbuf[0],comprbuf[1],comprbuf[2],comprbuf[3], comprbuf[4],comprbuf[5],comprbuf[6],comprbuf[7], comprbuf[8],comprbuf[9],comprbuf[10],comprbuf[11], comprbuf[12],comprbuf[13],comprbuf[14],comprbuf[15]); ret = jffs2_decompress(comprtype, comprbuf, decomprbuf, c, d); printk("jffs2_decompress returned %d\n", ret); printk("Decompressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", decomprbuf[0],decomprbuf[1],decomprbuf[2],decomprbuf[3], decomprbuf[4],decomprbuf[5],decomprbuf[6],decomprbuf[7], decomprbuf[8],decomprbuf[9],decomprbuf[10],decomprbuf[11], decomprbuf[12],decomprbuf[13],decomprbuf[14],decomprbuf[15]); if (memcmp(decomprbuf, testdata, d)) printk("Compression and decompression corrupted data\n"); else printk("Compression good for %d bytes\n", d); return 1; } linux-2.6.17/fs/jffs2/.svn/text-base/readinode.c.svn-base0000444000000000000000000007127710574207642021504 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: readinode.c,v 1.143 2005/11/07 11:14:41 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" /* * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in * order of increasing version. */ static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list) { struct rb_node **p = &list->rb_node; struct rb_node * parent = NULL; struct jffs2_tmp_dnode_info *this; while (*p) { parent = *p; this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); /* There may actually be a collision here, but it doesn't actually matter. As long as the two nodes with the same version are together, it's all fine. */ if (tn->version > this->version) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&tn->rb, parent, p); rb_insert_color(&tn->rb, list); } static void jffs2_free_tmp_dnode_info_list(struct rb_root *list) { struct rb_node *this; struct jffs2_tmp_dnode_info *tn; this = list->rb_node; /* Now at bottom of tree */ while (this) { if (this->rb_left) this = this->rb_left; else if (this->rb_right) this = this->rb_right; else { tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb); jffs2_free_full_dnode(tn->fn); jffs2_free_tmp_dnode_info(tn); this = this->rb_parent; if (!this) break; if (this->rb_left == &tn->rb) this->rb_left = NULL; else if (this->rb_right == &tn->rb) this->rb_right = NULL; else BUG(); } } list->rb_node = NULL; } static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) { struct jffs2_full_dirent *next; while (fd) { next = fd->next; jffs2_free_full_dirent(fd); fd = next; } } /* Returns first valid node after 'ref'. May return 'ref' */ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref) { while (ref && ref->next_in_ino) { if (!ref_obsolete(ref)) return ref; dbg_noderef("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)); ref = ref->next_in_ino; } return NULL; } /* * Helper function for jffs2_get_inode_nodes(). * It is called every time an directory entry node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_raw_dirent *rd, size_t read, struct jffs2_full_dirent **fdp, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_full_dirent *fd; /* The direntry nodes are checked during the flash scanning */ BUG_ON(ref_flags(ref) == REF_UNCHECKED); /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); /* Sanity check */ if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); return 1; } fd = jffs2_alloc_full_dirent(rd->nsize + 1); if (unlikely(!fd)) return -ENOMEM; fd->raw = ref; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->type = rd->type; /* Pick out the mctime of the latest dirent */ if(fd->version > *mctime_ver && je32_to_cpu(rd->mctime)) { *mctime_ver = fd->version; *latest_mctime = je32_to_cpu(rd->mctime); } /* * Copy as much of the name as possible from the raw * dirent we've already read from the flash. */ if (read > sizeof(*rd)) memcpy(&fd->name[0], &rd->name[0], min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) )); /* Do we need to copy any more of the name directly from the flash? */ if (rd->nsize + sizeof(*rd) > read) { /* FIXME: point() */ int err; int already = read - sizeof(*rd); err = jffs2_flash_read(c, (ref_offset(ref)) + read, rd->nsize - already, &read, &fd->name[already]); if (unlikely(read != rd->nsize - already) && likely(!err)) return -EIO; if (unlikely(err)) { JFFS2_ERROR("read remainder of name: error %d\n", err); jffs2_free_full_dirent(fd); return -EIO; } } fd->nhash = full_name_hash(fd->name, rd->nsize); fd->next = NULL; fd->name[rd->nsize] = '\0'; /* * Wheee. We now have a complete jffs2_full_dirent structure, with * the name in it and everything. Link it into the list */ jffs2_add_fd_to_list(c, fd, fdp); return 0; } /* * Helper function for jffs2_get_inode_nodes(). * It is called every time an inode node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_raw_inode *rd, struct rb_root *tnp, int rdlen, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_tmp_dnode_info *tn; uint32_t len, csize; int ret = 1; /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); tn = jffs2_alloc_tmp_dnode_info(); if (!tn) { JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn)); return -ENOMEM; } tn->partial_crc = 0; csize = je32_to_cpu(rd->csize); /* If we've never checked the CRCs on this node, check them now */ if (ref_flags(ref) == REF_UNCHECKED) { uint32_t crc; crc = crc32(0, rd, sizeof(*rd) - 8); if (unlikely(crc != je32_to_cpu(rd->node_crc))) { JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n", ref_offset(ref), je32_to_cpu(rd->node_crc), crc); goto free_out; } /* Sanity checks */ if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) || unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) { JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref)); jffs2_dbg_dump_node(c, ref_offset(ref)); goto free_out; } if (jffs2_is_writebuffered(c) && csize != 0) { /* At this point we are supposed to check the data CRC * of our unchecked node. But thus far, we do not * know whether the node is valid or obsolete. To * figure this out, we need to walk all the nodes of * the inode and build the inode fragtree. We don't * want to spend time checking data of nodes which may * later be found to be obsolete. So we put off the full * data CRC checking until we have read all the inode * nodes and have started building the fragtree. * * The fragtree is being built starting with nodes * having the highest version number, so we'll be able * to detect whether a node is valid (i.e., it is not * overlapped by a node with higher version) or not. * And we'll be able to check only those nodes, which * are not obsolete. * * Of course, this optimization only makes sense in case * of NAND flashes (or other flashes whith * !jffs2_can_mark_obsolete()), since on NOR flashes * nodes are marked obsolete physically. * * Since NAND flashes (or other flashes with * jffs2_is_writebuffered(c)) are anyway read by * fractions of c->wbuf_pagesize, and we have just read * the node header, it is likely that the starting part * of the node data is also read when we read the * header. So we don't mind to check the CRC of the * starting part of the data of the node now, and check * the second part later (in jffs2_check_node_data()). * Of course, we will not need to re-read and re-check * the NAND page which we have just read. This is why we * read the whole NAND page at jffs2_get_inode_nodes(), * while we needed only the node header. */ unsigned char *buf; /* 'buf' will point to the start of data */ buf = (unsigned char *)rd + sizeof(*rd); /* len will be the read data length */ len = min_t(uint32_t, rdlen - sizeof(*rd), csize); tn->partial_crc = crc32(0, buf, len); dbg_readinode("Calculates CRC (%#08x) for %d bytes, csize %d\n", tn->partial_crc, len, csize); /* If we actually calculated the whole data CRC * and it is wrong, drop the node. */ if (len >= csize && unlikely(tn->partial_crc != je32_to_cpu(rd->data_crc))) { JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", ref_offset(ref), tn->partial_crc, je32_to_cpu(rd->data_crc)); goto free_out; } } else if (csize == 0) { /* * We checked the header CRC. If the node has no data, adjust * the space accounting now. For other nodes this will be done * later either when the node is marked obsolete or when its * data is checked. */ struct jffs2_eraseblock *jeb; dbg_readinode("the node has no data.\n"); jeb = &c->blocks[ref->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, ref); spin_lock(&c->erase_completion_lock); jeb->used_size += len; jeb->unchecked_size -= len; c->used_size += len; c->unchecked_size -= len; ref->flash_offset = ref_offset(ref) | REF_NORMAL; spin_unlock(&c->erase_completion_lock); } } tn->fn = jffs2_alloc_full_dnode(); if (!tn->fn) { JFFS2_ERROR("alloc fn failed\n"); ret = -ENOMEM; goto free_out; } tn->version = je32_to_cpu(rd->version); tn->fn->ofs = je32_to_cpu(rd->offset); tn->data_crc = je32_to_cpu(rd->data_crc); tn->csize = csize; tn->fn->raw = ref; /* There was a bug where we wrote hole nodes out with csize/dsize swapped. Deal with it */ if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && csize) tn->fn->size = csize; else // normal case... tn->fn->size = je32_to_cpu(rd->dsize); dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); jffs2_add_tn_to_tree(tn, tnp); return 0; free_out: jffs2_free_tmp_dnode_info(tn); return ret; } /* * Helper function for jffs2_get_inode_nodes(). * It is called every time an unknown node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un) { /* We don't mark unknown nodes as REF_UNCHECKED */ BUG_ON(ref_flags(ref) == REF_UNCHECKED); un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) { /* Hmmm. This should have been caught at scan time. */ JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref)); jffs2_dbg_dump_node(c, ref_offset(ref)); return 1; } else { switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_INCOMPAT: JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); /* EEP */ BUG(); break; case JFFS2_FEATURE_ROCOMPAT: JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); break; case JFFS2_FEATURE_RWCOMPAT_DELETE: JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); return 1; } } return 0; } /* * Helper function for jffs2_get_inode_nodes(). * The function detects whether more data should be read and reads it if yes. * * Returns: 0 on succes; * negative error code on failure. */ static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, int right_size, int *rdlen, unsigned char *buf, unsigned char *bufstart) { int right_len, err, len; size_t retlen; uint32_t offs; if (jffs2_is_writebuffered(c)) { right_len = c->wbuf_pagesize - (bufstart - buf); if (right_size + (int)(bufstart - buf) > c->wbuf_pagesize) right_len += c->wbuf_pagesize; } else right_len = right_size; if (*rdlen == right_len) return 0; /* We need to read more data */ offs = ref_offset(ref) + *rdlen; if (jffs2_is_writebuffered(c)) { bufstart = buf + c->wbuf_pagesize; len = c->wbuf_pagesize; } else { bufstart = buf + *rdlen; len = right_size - *rdlen; } dbg_readinode("read more %d bytes\n", len); err = jffs2_flash_read(c, offs, len, &retlen, bufstart); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, offs, err); return err; } if (retlen < len) { JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", offs, retlen, len); return -EIO; } *rdlen = right_len; return 0; } /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated with this ino, returning the former in order of version */ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct rb_root *tnp, struct jffs2_full_dirent **fdp, uint32_t *highest_version, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_raw_node_ref *ref, *valid_ref; struct rb_root ret_tn = RB_ROOT; struct jffs2_full_dirent *ret_fd = NULL; unsigned char *buf = NULL; union jffs2_node_union *node; size_t retlen; int len, err; *mctime_ver = 0; dbg_readinode("ino #%u\n", f->inocache->ino); if (jffs2_is_writebuffered(c)) { /* * If we have the write buffer, we assume the minimal I/O unit * is c->wbuf_pagesize. We implement some optimizations which in * this case and we need a temporary buffer of size = * 2*c->wbuf_pagesize bytes (see comments in read_dnode()). * Basically, we want to read not only the node header, but the * whole wbuf (NAND page in case of NAND) or 2, if the node * header overlaps the border between the 2 wbufs. */ len = 2*c->wbuf_pagesize; } else { /* * When there is no write buffer, the size of the temporary * buffer is the size of the larges node header. */ len = sizeof(union jffs2_node_union); } /* FIXME: in case of NOR and available ->point() this * needs to be fixed. */ buf = kmalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; spin_lock(&c->erase_completion_lock); valid_ref = jffs2_first_valid_node(f->inocache->nodes); if (!valid_ref && f->inocache->ino != 1) JFFS2_WARNING("Eep. No valid nodes for ino #%u.\n", f->inocache->ino); while (valid_ref) { unsigned char *bufstart; /* We can hold a pointer to a non-obsolete node without the spinlock, but _obsolete_ nodes may disappear at any time, if the block they're in gets erased. So if we mark 'ref' obsolete while we're not holding the lock, it can go away immediately. For that reason, we find the next valid node first, before processing 'ref'. */ ref = valid_ref; valid_ref = jffs2_first_valid_node(ref->next_in_ino); spin_unlock(&c->erase_completion_lock); cond_resched(); /* * At this point we don't know the type of the node we're going * to read, so we do not know the size of its header. In order * to minimize the amount of flash IO we assume the node has * size = JFFS2_MIN_NODE_HEADER. */ if (jffs2_is_writebuffered(c)) { /* * We treat 'buf' as 2 adjacent wbufs. We want to * adjust bufstart such as it points to the * beginning of the node within this wbuf. */ bufstart = buf + (ref_offset(ref) % c->wbuf_pagesize); /* We will read either one wbuf or 2 wbufs. */ len = c->wbuf_pagesize - (bufstart - buf); if (JFFS2_MIN_NODE_HEADER + (int)(bufstart - buf) > c->wbuf_pagesize) { /* The header spans the border of the first wbuf */ len += c->wbuf_pagesize; } } else { bufstart = buf; len = JFFS2_MIN_NODE_HEADER; } dbg_readinode("read %d bytes at %#08x(%d).\n", len, ref_offset(ref), ref_flags(ref)); /* FIXME: point() */ err = jffs2_flash_read(c, ref_offset(ref), len, &retlen, bufstart); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, ref_offset(ref), err); goto free_out; } if (retlen < len) { JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ref_offset(ref), retlen, len); err = -EIO; goto free_out; } node = (union jffs2_node_union *)bufstart; switch (je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_DIRENT: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent)) { err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf, bufstart); if (unlikely(err)) goto free_out; } err = read_direntry(c, ref, &node->d, retlen, &ret_fd, latest_mctime, mctime_ver); if (err == 1) { jffs2_mark_node_obsolete(c, ref); break; } else if (unlikely(err)) goto free_out; if (je32_to_cpu(node->d.version) > *highest_version) *highest_version = je32_to_cpu(node->d.version); break; case JFFS2_NODETYPE_INODE: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode)) { err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf, bufstart); if (unlikely(err)) goto free_out; } err = read_dnode(c, ref, &node->i, &ret_tn, len, latest_mctime, mctime_ver); if (err == 1) { jffs2_mark_node_obsolete(c, ref); break; } else if (unlikely(err)) goto free_out; if (je32_to_cpu(node->i.version) > *highest_version) *highest_version = je32_to_cpu(node->i.version); break; default: if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node)) { err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf, bufstart); if (unlikely(err)) goto free_out; } err = read_unknown(c, ref, &node->u); if (err == 1) { jffs2_mark_node_obsolete(c, ref); break; } else if (unlikely(err)) goto free_out; } spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); *tnp = ret_tn; *fdp = ret_fd; kfree(buf); dbg_readinode("nodes of inode #%u were read, the highest version is %u, latest_mctime %u, mctime_ver %u.\n", f->inocache->ino, *highest_version, *latest_mctime, *mctime_ver); return 0; free_out: jffs2_free_tmp_dnode_info_list(&ret_tn); jffs2_free_full_dirent_list(ret_fd); kfree(buf); return err; } static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *latest_node) { struct jffs2_tmp_dnode_info *tn; struct rb_root tn_list; struct rb_node *rb, *repl_rb; struct jffs2_full_dirent *fd_list = NULL; struct jffs2_full_dnode *fn, *first_fn = NULL; uint32_t crc; uint32_t latest_mctime = 0, mctime_ver; size_t retlen; int ret; dbg_readinode("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink); /* Grab all nodes relevant to this ino */ ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { JFFS2_ERROR("cannot read nodes for ino %u, returned error is %d\n", f->inocache->ino, ret); if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return ret; } f->dents = fd_list; rb = rb_first(&tn_list); while (rb) { cond_resched(); tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb); fn = tn->fn; ret = 1; dbg_readinode("consider node ver %u, phys offset " "%#08x(%d), range %u-%u.\n", tn->version, ref_offset(fn->raw), ref_flags(fn->raw), fn->ofs, fn->ofs + fn->size); if (fn->size) { ret = jffs2_add_older_frag_to_fragtree(c, f, tn); /* TODO: the error code isn't checked, check it */ jffs2_dbg_fragtree_paranoia_check_nolock(f); BUG_ON(ret < 0); if (!first_fn && ret == 0) first_fn = fn; } else if (!first_fn) { first_fn = fn; f->metadata = fn; ret = 0; /* Prevent freeing the metadata update node */ } else jffs2_mark_node_obsolete(c, fn->raw); BUG_ON(rb->rb_left); if (rb->rb_parent && rb->rb_parent->rb_left == rb) { /* We were then left-hand child of our parent. We need * to move our own right-hand child into our place. */ repl_rb = rb->rb_right; if (repl_rb) repl_rb->rb_parent = rb->rb_parent; } else repl_rb = NULL; rb = rb_next(rb); /* Remove the spent tn from the tree; don't bother rebalancing * but put our right-hand child in our own place. */ if (tn->rb.rb_parent) { if (tn->rb.rb_parent->rb_left == &tn->rb) tn->rb.rb_parent->rb_left = repl_rb; else if (tn->rb.rb_parent->rb_right == &tn->rb) tn->rb.rb_parent->rb_right = repl_rb; else BUG(); } else if (tn->rb.rb_right) tn->rb.rb_right->rb_parent = NULL; jffs2_free_tmp_dnode_info(tn); if (ret) { dbg_readinode("delete dnode %u-%u.\n", fn->ofs, fn->ofs + fn->size); jffs2_free_full_dnode(fn); } } jffs2_dbg_fragtree_paranoia_check_nolock(f); BUG_ON(first_fn && ref_obsolete(first_fn->raw)); fn = first_fn; if (unlikely(!first_fn)) { /* No data nodes for this inode. */ if (f->inocache->ino != 1) { JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino); if (!fd_list) { if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return -EIO; } JFFS2_NOTICE("but it has children so we fake some modes for it\n"); } latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); latest_node->version = cpu_to_je32(0); latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); latest_node->isize = cpu_to_je32(0); latest_node->gid = cpu_to_je16(0); latest_node->uid = cpu_to_je16(0); if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); if (ret || retlen != sizeof(*latest_node)) { JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ up(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } crc = crc32(0, latest_node, sizeof(*latest_node)-8); if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { case S_IFDIR: if (mctime_ver > je32_to_cpu(latest_node->version)) { /* The times in the latest_node are actually older than mctime in the latest dirent. Cheat. */ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); } break; case S_IFREG: /* If it was a regular file, truncate it to the latest node's isize */ jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize)); break; case S_IFLNK: /* Hack to work around broken isize in old symlink code. Remove this when dwmw2 comes to his senses and stops symlinks from being an entirely gratuitous special case. */ if (!je32_to_cpu(latest_node->isize)) latest_node->isize = latest_node->dsize; if (f->inocache->state != INO_STATE_CHECKING) { /* Symlink's inode data is the target path. Read it and * keep in RAM to facilitate quick follow symlink * operation. */ f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); up(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); if (ret || retlen != je32_to_cpu(latest_node->csize)) { if (retlen != je32_to_cpu(latest_node->csize)) ret = -EIO; kfree(f->target); f->target = NULL; up(&f->sem); jffs2_do_clear_inode(c, f); return -ret; } f->target[je32_to_cpu(latest_node->csize)] = '\0'; dbg_readinode("symlink's target '%s' cached\n", f->target); } /* fall through... */ case S_IFBLK: case S_IFCHR: /* Certain inode types should have only one data node, and it's kept as the metadata node */ if (f->metadata) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } if (!frag_first(&f->fragtree)) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* ASSERT: f->fraglist != NULL */ if (frag_next(frag_first(&f->fragtree))) { JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* OK. We're happy */ f->metadata = frag_first(&f->fragtree)->node; jffs2_free_node_frag(frag_first(&f->fragtree)); f->fragtree = RB_ROOT; break; } if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } /* Scan the list of all nodes present for this ino, build map of versions, etc. */ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t ino, struct jffs2_raw_inode *latest_node) { dbg_readinode("read inode #%u\n", ino); retry_inocache: spin_lock(&c->inocache_lock); f->inocache = jffs2_get_ino_cache(c, ino); if (f->inocache) { /* Check its state. We may need to wait before we can use it */ switch(f->inocache->state) { case INO_STATE_UNCHECKED: case INO_STATE_CHECKEDABSENT: f->inocache->state = INO_STATE_READING; break; case INO_STATE_CHECKING: case INO_STATE_GC: /* If it's in either of these states, we need to wait for whoever's got it to finish and put it back. */ dbg_readinode("waiting for ino #%u in state %d\n", ino, f->inocache->state); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); goto retry_inocache; case INO_STATE_READING: case INO_STATE_PRESENT: /* Eep. This should never happen. It can happen if Linux calls read_inode() again before clear_inode() has finished though. */ JFFS2_ERROR("Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state); /* Fail. That's probably better than allowing it to succeed */ f->inocache = NULL; break; default: BUG(); } } spin_unlock(&c->inocache_lock); if (!f->inocache && ino == 1) { /* Special case - no root inode on medium */ f->inocache = jffs2_alloc_inode_cache(); if (!f->inocache) { JFFS2_ERROR("cannot allocate inocache for root inode\n"); return -ENOMEM; } dbg_readinode("creating inocache for root inode\n"); memset(f->inocache, 0, sizeof(struct jffs2_inode_cache)); f->inocache->ino = f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_READING; jffs2_add_ino_cache(c, f->inocache); } if (!f->inocache) { JFFS2_ERROR("requestied to read an nonexistent ino %u\n", ino); return -ENOENT; } return jffs2_do_read_inode_internal(c, f, latest_node); } int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { struct jffs2_raw_inode n; struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); int ret; if (!f) return -ENOMEM; memset(f, 0, sizeof(*f)); init_MUTEX_LOCKED(&f->sem); f->inocache = ic; ret = jffs2_do_read_inode_internal(c, f, &n); if (!ret) { up(&f->sem); jffs2_do_clear_inode(c, f); } kfree (f); return ret; } void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { struct jffs2_full_dirent *fd, *fds; int deleted; down(&f->sem); deleted = f->inocache && !f->inocache->nlink; if (f->inocache && f->inocache->state != INO_STATE_CHECKING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING); if (f->metadata) { if (deleted) jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); } jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); if (f->target) { kfree(f->target); f->target = NULL; } fds = f->dents; while(fds) { fd = fds; fds = fd->next; jffs2_free_full_dirent(fd); } if (f->inocache && f->inocache->state != INO_STATE_CHECKING) { jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); if (f->inocache->nodes == (void *)f->inocache) jffs2_del_ino_cache(c, f->inocache); } up(&f->sem); } linux-2.6.17/fs/jffs2/.svn/text-base/file.c.svn-base0000444000000000000000000002023410574207642020454 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: file.c,v 1.104 2005/10/18 23:29:35 tpoynor Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" static int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end); static int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end); static int jffs2_readpage (struct file *filp, struct page *pg); int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); /* Trigger GC to flush any pending writes for this inode */ jffs2_flush_wbuf_gc(c, inode->i_ino); return 0; } const struct file_operations jffs2_file_operations = { .llseek = generic_file_llseek, .open = generic_file_open, .read = generic_file_read, .write = generic_file_write, .ioctl = jffs2_ioctl, .mmap = generic_file_readonly_mmap, .fsync = jffs2_fsync, .sendfile = generic_file_sendfile }; /* jffs2_file_inode_operations */ struct inode_operations jffs2_file_inode_operations = { .setattr = jffs2_setattr }; struct address_space_operations jffs2_file_address_operations = { .readpage = jffs2_readpage, .prepare_write =jffs2_prepare_write, .commit_write = jffs2_commit_write }; static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); unsigned char *pg_buf; int ret; D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); BUG_ON(!PageLocked(pg)); pg_buf = kmap(pg); /* FIXME: Can kmap fail? */ ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); if (ret) { ClearPageUptodate(pg); SetPageError(pg); } else { SetPageUptodate(pg); ClearPageError(pg); } flush_dcache_page(pg); kunmap(pg); D2(printk(KERN_DEBUG "readpage finished\n")); return 0; } int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg) { int ret = jffs2_do_readpage_nolock(inode, pg); unlock_page(pg); return ret; } static int jffs2_readpage (struct file *filp, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; down(&f->sem); ret = jffs2_do_readpage_unlock(pg->mapping->host, pg); up(&f->sem); return ret; } static int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end) { struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; int ret = 0; D1(printk(KERN_DEBUG "jffs2_prepare_write()\n")); if (pageofs > inode->i_size) { /* Make new hole frag from old EOF to new page */ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode ri; struct jffs2_full_dnode *fn; uint32_t phys_ofs, alloc_len; D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", (unsigned int)inode->i_size, pageofs)); ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) return ret; down(&f->sem); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.mode = cpu_to_jemode(inode->i_mode); ri.uid = cpu_to_je16(inode->i_uid); ri.gid = cpu_to_je16(inode->i_gid); ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs)); ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds()); ri.offset = cpu_to_je32(inode->i_size); ri.dsize = cpu_to_je32(pageofs - inode->i_size); ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(0); fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fn)) { ret = PTR_ERR(fn); jffs2_complete_reservation(c); up(&f->sem); return ret; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } if (ret) { D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); jffs2_complete_reservation(c); up(&f->sem); return ret; } jffs2_complete_reservation(c); inode->i_size = pageofs; up(&f->sem); } /* Read in the page if it wasn't already present, unless it's a whole page */ if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { down(&f->sem); ret = jffs2_do_readpage_nolock(inode, pg); up(&f->sem); } D1(printk(KERN_DEBUG "end prepare_write(). pg->flags %lx\n", pg->flags)); return ret; } static int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple */ struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; unsigned aligned_start = start & ~3; int ret = 0; uint32_t writtenlen = 0; D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); if (!start && end == PAGE_CACHE_SIZE) { /* We need to avoid deadlock with page_cache_read() in jffs2_garbage_collect_pass(). So we have to mark the page up to date, to prevent page_cache_read() from trying to re-lock it. */ SetPageUptodate(pg); } ri = jffs2_alloc_raw_inode(); if (!ri) { D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n")); return -ENOMEM; } /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ri->ino = cpu_to_je32(inode->i_ino); ri->mode = cpu_to_jemode(inode->i_mode); ri->uid = cpu_to_je16(inode->i_uid); ri->gid = cpu_to_je16(inode->i_gid); ri->isize = cpu_to_je32((uint32_t)inode->i_size); ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds()); /* In 2.4, it was already kmapped by generic_file_write(). Doesn't hurt to do it again. The alternative is ifdefs, which are ugly. */ kmap(pg); ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start, (pg->index << PAGE_CACHE_SHIFT) + aligned_start, end - aligned_start, &writtenlen); kunmap(pg); if (ret) { /* There was an error writing. */ SetPageError(pg); } /* Adjust writtenlen for the padding we did, so we don't confuse our caller */ if (writtenlen < (start&3)) writtenlen = 0; else writtenlen -= (start&3); if (writtenlen) { if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; inode->i_blocks = (inode->i_size + 511) >> 9; inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); } } jffs2_free_raw_inode(ri); if (start+writtenlen < end) { /* generic_file_write has written more to the page cache than we've actually written to the medium. Mark the page !Uptodate so that it gets reread */ D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate\n")); SetPageError(pg); ClearPageUptodate(pg); } D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",start+writtenlen==end?0:ret)); return start+writtenlen==end?0:ret; } linux-2.6.17/fs/jffs2/.svn/text-base/debug.h.svn-base0000444000000000000000000001777110574207642020644 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: debug.h,v 1.21 2005/11/07 11:14:39 gleixner Exp $ * */ #ifndef _JFFS2_DEBUG_H_ #define _JFFS2_DEBUG_H_ #include #ifndef CONFIG_JFFS2_FS_DEBUG #define CONFIG_JFFS2_FS_DEBUG 0 #endif #if CONFIG_JFFS2_FS_DEBUG > 0 /* Enable "paranoia" checks and dumps */ #define JFFS2_DBG_PARANOIA_CHECKS #define JFFS2_DBG_DUMPS /* * By defining/undefining the below macros one may select debugging messages * fro specific JFFS2 subsystems. */ #define JFFS2_DBG_READINODE_MESSAGES #define JFFS2_DBG_FRAGTREE_MESSAGES #define JFFS2_DBG_DENTLIST_MESSAGES #define JFFS2_DBG_NODEREF_MESSAGES #define JFFS2_DBG_INOCACHE_MESSAGES #define JFFS2_DBG_SUMMARY_MESSAGES #define JFFS2_DBG_FSBUILD_MESSAGES #endif #if CONFIG_JFFS2_FS_DEBUG > 1 #define JFFS2_DBG_FRAGTREE2_MESSAGES #define JFFS2_DBG_MEMALLOC_MESSAGES #endif /* Sanity checks are supposed to be light-weight and enabled by default */ #define JFFS2_DBG_SANITY_CHECKS /* * Dx() are mainly used for debugging messages, they must go away and be * superseded by nicer dbg_xxx() macros... */ #if CONFIG_JFFS2_FS_DEBUG > 0 #define D1(x) x #else #define D1(x) #endif #if CONFIG_JFFS2_FS_DEBUG > 1 #define D2(x) x #else #define D2(x) #endif /* The prefixes of JFFS2 messages */ #define JFFS2_DBG_PREFIX "[JFFS2 DBG]" #define JFFS2_ERR_PREFIX "JFFS2 error:" #define JFFS2_WARN_PREFIX "JFFS2 warning:" #define JFFS2_NOTICE_PREFIX "JFFS2 notice:" #define JFFS2_ERR KERN_ERR #define JFFS2_WARN KERN_WARNING #define JFFS2_NOT KERN_NOTICE #define JFFS2_DBG KERN_DEBUG #define JFFS2_DBG_MSG_PREFIX JFFS2_DBG JFFS2_DBG_PREFIX #define JFFS2_ERR_MSG_PREFIX JFFS2_ERR JFFS2_ERR_PREFIX #define JFFS2_WARN_MSG_PREFIX JFFS2_WARN JFFS2_WARN_PREFIX #define JFFS2_NOTICE_MSG_PREFIX JFFS2_NOT JFFS2_NOTICE_PREFIX /* JFFS2 message macros */ #define JFFS2_ERROR(fmt, ...) \ do { \ printk(JFFS2_ERR_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) #define JFFS2_WARNING(fmt, ...) \ do { \ printk(JFFS2_WARN_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) #define JFFS2_NOTICE(fmt, ...) \ do { \ printk(JFFS2_NOTICE_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) #define JFFS2_DEBUG(fmt, ...) \ do { \ printk(JFFS2_DBG_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) /* * We split our debugging messages on several parts, depending on the JFFS2 * subsystem the message belongs to. */ /* Read inode debugging messages */ #ifdef JFFS2_DBG_READINODE_MESSAGES #define dbg_readinode(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_readinode(fmt, ...) #endif /* Fragtree build debugging messages */ #ifdef JFFS2_DBG_FRAGTREE_MESSAGES #define dbg_fragtree(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_fragtree(fmt, ...) #endif #ifdef JFFS2_DBG_FRAGTREE2_MESSAGES #define dbg_fragtree2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_fragtree2(fmt, ...) #endif /* Directory entry list manilulation debugging messages */ #ifdef JFFS2_DBG_DENTLIST_MESSAGES #define dbg_dentlist(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_dentlist(fmt, ...) #endif /* Print the messages about manipulating node_refs */ #ifdef JFFS2_DBG_NODEREF_MESSAGES #define dbg_noderef(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_noderef(fmt, ...) #endif /* Manipulations with the list of inodes (JFFS2 inocache) */ #ifdef JFFS2_DBG_INOCACHE_MESSAGES #define dbg_inocache(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_inocache(fmt, ...) #endif /* Summary debugging messages */ #ifdef JFFS2_DBG_SUMMARY_MESSAGES #define dbg_summary(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_summary(fmt, ...) #endif /* File system build messages */ #ifdef JFFS2_DBG_FSBUILD_MESSAGES #define dbg_fsbuild(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_fsbuild(fmt, ...) #endif /* Watch the object allocations */ #ifdef JFFS2_DBG_MEMALLOC_MESSAGES #define dbg_memalloc(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_memalloc(fmt, ...) #endif /* "Sanity" checks */ void __jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); /* "Paranoia" checks */ void __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f); void __jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f); void __jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, uint32_t ofs, int len); /* "Dump" functions */ void __jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c); void __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c); void __jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f); void __jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f); void __jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs); void __jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs); #ifdef JFFS2_DBG_PARANOIA_CHECKS #define jffs2_dbg_fragtree_paranoia_check(f) \ __jffs2_dbg_fragtree_paranoia_check(f) #define jffs2_dbg_fragtree_paranoia_check_nolock(f) \ __jffs2_dbg_fragtree_paranoia_check_nolock(f) #define jffs2_dbg_acct_paranoia_check(c, jeb) \ __jffs2_dbg_acct_paranoia_check(c,jeb) #define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) \ __jffs2_dbg_acct_paranoia_check_nolock(c,jeb) #define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) \ __jffs2_dbg_prewrite_paranoia_check(c, ofs, len) #else #define jffs2_dbg_fragtree_paranoia_check(f) #define jffs2_dbg_fragtree_paranoia_check_nolock(f) #define jffs2_dbg_acct_paranoia_check(c, jeb) #define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) #define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) #endif /* !JFFS2_PARANOIA_CHECKS */ #ifdef JFFS2_DBG_DUMPS #define jffs2_dbg_dump_jeb(c, jeb) \ __jffs2_dbg_dump_jeb(c, jeb); #define jffs2_dbg_dump_jeb_nolock(jeb) \ __jffs2_dbg_dump_jeb_nolock(jeb); #define jffs2_dbg_dump_block_lists(c) \ __jffs2_dbg_dump_block_lists(c) #define jffs2_dbg_dump_block_lists_nolock(c) \ __jffs2_dbg_dump_block_lists_nolock(c) #define jffs2_dbg_dump_fragtree(f) \ __jffs2_dbg_dump_fragtree(f); #define jffs2_dbg_dump_fragtree_nolock(f) \ __jffs2_dbg_dump_fragtree_nolock(f); #define jffs2_dbg_dump_buffer(buf, len, offs) \ __jffs2_dbg_dump_buffer(*buf, len, offs); #define jffs2_dbg_dump_node(c, ofs) \ __jffs2_dbg_dump_node(c, ofs); #else #define jffs2_dbg_dump_jeb(c, jeb) #define jffs2_dbg_dump_jeb_nolock(jeb) #define jffs2_dbg_dump_block_lists(c) #define jffs2_dbg_dump_block_lists_nolock(c) #define jffs2_dbg_dump_fragtree(f) #define jffs2_dbg_dump_fragtree_nolock(f) #define jffs2_dbg_dump_buffer(buf, len, offs) #define jffs2_dbg_dump_node(c, ofs) #endif /* !JFFS2_DBG_DUMPS */ #ifdef JFFS2_DBG_SANITY_CHECKS #define jffs2_dbg_acct_sanity_check(c, jeb) \ __jffs2_dbg_acct_sanity_check(c, jeb) #define jffs2_dbg_acct_sanity_check_nolock(c, jeb) \ __jffs2_dbg_acct_sanity_check_nolock(c, jeb) #else #define jffs2_dbg_acct_sanity_check(c, jeb) #define jffs2_dbg_acct_sanity_check_nolock(c, jeb) #endif /* !JFFS2_DBG_SANITY_CHECKS */ #endif /* _JFFS2_DEBUG_H_ */ linux-2.6.17/fs/jffs2/.svn/text-base/LICENCE.svn-base0000444000000000000000000000304710574207642020361 0ustar rootrootThe files in this directory and elsewhere which refer to this LICENCE file are part of JFFS2, the Journalling Flash File System v2. Copyright (C) 2001, 2002 Red Hat, Inc. JFFS2 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. JFFS2 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 JFFS2; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. As a special exception, if other files instantiate templates or use macros or inline functions from these files, or you compile these files and link them with other works to produce a work based on these files, these files do not by themselves cause the resulting work to be covered by the GNU General Public License. However the source code for these files must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. For information on obtaining alternative licences for JFFS2, see http://sources.redhat.com/jffs2/jffs2-licence.html $Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $ linux-2.6.17/fs/jffs2/.svn/text-base/compr_zlib.c.svn-base0000444000000000000000000001353510574207642021703 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr_zlib.c,v 1.32 2005/11/07 11:14:38 gleixner Exp $ * */ #if !defined(__KERNEL__) && !defined(__ECOS) #error "The userspace support got too messy and was removed. Update your mkfs.jffs2" #endif #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" /* Plan: call deflate() with avail_in == *sourcelen, avail_out = *dstlen - 12 and flush == Z_FINISH. If it doesn't manage to finish, call it again with avail_in == 0 and avail_out set to the remaining 12 bytes for it to clean up. Q: Is 12 bytes sufficient? */ #define STREAM_END_SPACE 12 static DEFINE_MUTEX(deflate_mutex); static DEFINE_MUTEX(inflate_mutex); static z_stream inf_strm, def_strm; #ifdef __KERNEL__ /* Linux-only */ #include #include #include static int __init alloc_workspaces(void) { def_strm.workspace = vmalloc(zlib_deflate_workspacesize()); if (!def_strm.workspace) { printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); return -ENOMEM; } D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); inf_strm.workspace = vmalloc(zlib_inflate_workspacesize()); if (!inf_strm.workspace) { printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); vfree(def_strm.workspace); return -ENOMEM; } D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); return 0; } static void free_workspaces(void) { vfree(def_strm.workspace); vfree(inf_strm.workspace); } #else #define alloc_workspaces() (0) #define free_workspaces() do { } while(0) #endif /* __KERNEL__ */ static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { int ret; if (*dstlen <= STREAM_END_SPACE) return -1; mutex_lock(&deflate_mutex); if (Z_OK != zlib_deflateInit(&def_strm, 3)) { printk(KERN_WARNING "deflateInit failed\n"); mutex_unlock(&deflate_mutex); return -1; } def_strm.next_in = data_in; def_strm.total_in = 0; def_strm.next_out = cpage_out; def_strm.total_out = 0; while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) { def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE); def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out); D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n", def_strm.avail_in, def_strm.avail_out)); ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH); D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out)); if (ret != Z_OK) { D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret)); zlib_deflateEnd(&def_strm); mutex_unlock(&deflate_mutex); return -1; } } def_strm.avail_out += STREAM_END_SPACE; def_strm.avail_in = 0; ret = zlib_deflate(&def_strm, Z_FINISH); zlib_deflateEnd(&def_strm); if (ret != Z_STREAM_END) { D1(printk(KERN_DEBUG "final deflate returned %d\n", ret)); ret = -1; goto out; } if (def_strm.total_out >= def_strm.total_in) { D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n", def_strm.total_in, def_strm.total_out)); ret = -1; goto out; } D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", def_strm.total_in, def_strm.total_out)); *dstlen = def_strm.total_out; *sourcelen = def_strm.total_in; ret = 0; out: mutex_unlock(&deflate_mutex); return ret; } static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen, void *model) { int ret; int wbits = MAX_WBITS; mutex_lock(&inflate_mutex); inf_strm.next_in = data_in; inf_strm.avail_in = srclen; inf_strm.total_in = 0; inf_strm.next_out = cpage_out; inf_strm.avail_out = destlen; inf_strm.total_out = 0; /* If it's deflate, and it's got no preset dictionary, then we can tell zlib to skip the adler32 check. */ if (srclen > 2 && !(data_in[1] & PRESET_DICT) && ((data_in[0] & 0x0f) == Z_DEFLATED) && !(((data_in[0]<<8) + data_in[1]) % 31)) { D2(printk(KERN_DEBUG "inflate skipping adler32\n")); wbits = -((data_in[0] >> 4) + 8); inf_strm.next_in += 2; inf_strm.avail_in -= 2; } else { /* Let this remain D1 for now -- it should never happen */ D1(printk(KERN_DEBUG "inflate not skipping adler32\n")); } if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { printk(KERN_WARNING "inflateInit failed\n"); mutex_unlock(&inflate_mutex); return 1; } while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) ; if (ret != Z_STREAM_END) { printk(KERN_NOTICE "inflate returned %d\n", ret); } zlib_inflateEnd(&inf_strm); mutex_unlock(&inflate_mutex); return 0; } static struct jffs2_compressor jffs2_zlib_comp = { .priority = JFFS2_ZLIB_PRIORITY, .name = "zlib", .compr = JFFS2_COMPR_ZLIB, .compress = &jffs2_zlib_compress, .decompress = &jffs2_zlib_decompress, #ifdef JFFS2_ZLIB_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int __init jffs2_zlib_init(void) { int ret; ret = alloc_workspaces(); if (ret) return ret; ret = jffs2_register_compressor(&jffs2_zlib_comp); if (ret) free_workspaces(); return ret; } void jffs2_zlib_exit(void) { jffs2_unregister_compressor(&jffs2_zlib_comp); free_workspaces(); } linux-2.6.17/fs/jffs2/.svn/text-base/malloc.c.svn-base0000444000000000000000000001167210574207642021012 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: malloc.c,v 1.31 2005/11/07 11:14:40 gleixner Exp $ * */ #include #include #include #include #include "nodelist.h" /* These are initialised to NULL in the kernel startup code. If you're porting to other operating systems, beware */ static kmem_cache_t *full_dnode_slab; static kmem_cache_t *raw_dirent_slab; static kmem_cache_t *raw_inode_slab; static kmem_cache_t *tmp_dnode_info_slab; static kmem_cache_t *raw_node_ref_slab; static kmem_cache_t *node_frag_slab; static kmem_cache_t *inode_cache_slab; int __init jffs2_create_slab_caches(void) { full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, 0, NULL, NULL); if (!full_dnode_slab) goto err; raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, 0, NULL, NULL); if (!raw_dirent_slab) goto err; raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, 0, NULL, NULL); if (!raw_inode_slab) goto err; tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, 0, NULL, NULL); if (!tmp_dnode_info_slab) goto err; raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, 0, NULL, NULL); if (!raw_node_ref_slab) goto err; node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, 0, NULL, NULL); if (!node_frag_slab) goto err; inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, 0, NULL, NULL); if (inode_cache_slab) return 0; err: jffs2_destroy_slab_caches(); return -ENOMEM; } void jffs2_destroy_slab_caches(void) { if(full_dnode_slab) kmem_cache_destroy(full_dnode_slab); if(raw_dirent_slab) kmem_cache_destroy(raw_dirent_slab); if(raw_inode_slab) kmem_cache_destroy(raw_inode_slab); if(tmp_dnode_info_slab) kmem_cache_destroy(tmp_dnode_info_slab); if(raw_node_ref_slab) kmem_cache_destroy(raw_node_ref_slab); if(node_frag_slab) kmem_cache_destroy(node_frag_slab); if(inode_cache_slab) kmem_cache_destroy(inode_cache_slab); } struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) { struct jffs2_full_dirent *ret; ret = kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_full_dirent(struct jffs2_full_dirent *x) { dbg_memalloc("%p\n", x); kfree(x); } struct jffs2_full_dnode *jffs2_alloc_full_dnode(void) { struct jffs2_full_dnode *ret; ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_full_dnode(struct jffs2_full_dnode *x) { dbg_memalloc("%p\n", x); kmem_cache_free(full_dnode_slab, x); } struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void) { struct jffs2_raw_dirent *ret; ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x) { dbg_memalloc("%p\n", x); kmem_cache_free(raw_dirent_slab, x); } struct jffs2_raw_inode *jffs2_alloc_raw_inode(void) { struct jffs2_raw_inode *ret; ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_raw_inode(struct jffs2_raw_inode *x) { dbg_memalloc("%p\n", x); kmem_cache_free(raw_inode_slab, x); } struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void) { struct jffs2_tmp_dnode_info *ret; ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x) { dbg_memalloc("%p\n", x); kmem_cache_free(tmp_dnode_info_slab, x); } struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void) { struct jffs2_raw_node_ref *ret; ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x) { dbg_memalloc("%p\n", x); kmem_cache_free(raw_node_ref_slab, x); } struct jffs2_node_frag *jffs2_alloc_node_frag(void) { struct jffs2_node_frag *ret; ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_node_frag(struct jffs2_node_frag *x) { dbg_memalloc("%p\n", x); kmem_cache_free(node_frag_slab, x); } struct jffs2_inode_cache *jffs2_alloc_inode_cache(void) { struct jffs2_inode_cache *ret; ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL); dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_inode_cache(struct jffs2_inode_cache *x) { dbg_memalloc("%p\n", x); kmem_cache_free(inode_cache_slab, x); } linux-2.6.17/fs/jffs2/.svn/text-base/erase.c.svn-base0000444000000000000000000003240210574207642020634 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: erase.c,v 1.85 2005/09/20 14:53:15 dedekind Exp $ * */ #include #include #include #include #include #include #include #include "nodelist.h" struct erase_priv_struct { struct jffs2_eraseblock *jeb; struct jffs2_sb_info *c; }; #ifndef __ECOS static void jffs2_erase_callback(struct erase_info *); #endif static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; uint32_t bad_offset; #ifdef __ECOS ret = jffs2_flash_erase(c, jeb); if (!ret) { jffs2_erase_succeeded(c, jeb); return; } bad_offset = jeb->offset; #else /* Linux */ struct erase_info *instr; D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#08x (range %#08x-%#08x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size)); instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); return; } memset(instr, 0, sizeof(*instr)); instr->mtd = c->mtd; instr->addr = jeb->offset; instr->len = c->sector_size; instr->callback = jffs2_erase_callback; instr->priv = (unsigned long)(&instr[1]); instr->fail_addr = 0xffffffff; ((struct erase_priv_struct *)instr->priv)->jeb = jeb; ((struct erase_priv_struct *)instr->priv)->c = c; ret = c->mtd->erase(c->mtd, instr); if (!ret) return; bad_offset = instr->fail_addr; kfree(instr); #endif /* __ECOS */ if (ret == -ENOMEM || ret == -EAGAIN) { /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); return; } if (ret == -EROFS) printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset); else printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); jffs2_erase_failed(c, jeb, bad_offset); } void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); while (!list_empty(&c->erase_complete_list) || !list_empty(&c->erase_pending_list)) { if (!list_empty(&c->erase_complete_list)) { jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); list_del(&jeb->list); spin_unlock(&c->erase_completion_lock); jffs2_mark_erased_block(c, jeb); if (!--count) { D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); goto done; } } else if (!list_empty(&c->erase_pending_list)) { jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); list_del(&jeb->list); c->erasing_size += c->sector_size; c->wasted_size -= jeb->wasted_size; c->free_size -= jeb->free_size; c->used_size -= jeb->used_size; c->dirty_size -= jeb->dirty_size; jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; jffs2_free_all_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); jffs2_erase_block(c, jeb); } else { BUG(); } /* Be nice */ cond_resched(); spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); up(&c->erase_free_sem); } static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); } static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { /* For NAND, if the failure did not occur at the device level for a specific physical page, don't bother updating the bad block table. */ if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) { /* We had a device-level failure to erase. Let's see if we've failed too many times. */ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { /* We'd like to give this block another try. */ spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); return; } } spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; list_del(&jeb->list); list_add(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); } #ifndef __ECOS static void jffs2_erase_callback(struct erase_info *instr) { struct erase_priv_struct *priv = (void *)instr->priv; if(instr->state != MTD_ERASE_DONE) { printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); } else { jffs2_erase_succeeded(priv->c, priv->jeb); } kfree(instr); } #endif /* !__ECOS */ /* Hmmm. Maybe we should accept the extra space it takes and make this a standard doubly-linked list? */ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb) { struct jffs2_inode_cache *ic = NULL; struct jffs2_raw_node_ref **prev; prev = &ref->next_in_ino; /* Walk the inode's list once, removing any nodes from this eraseblock */ while (1) { if (!(*prev)->next_in_ino) { /* We're looking at the jffs2_inode_cache, which is at the end of the linked list. Stash it and continue from the beginning of the list */ ic = (struct jffs2_inode_cache *)(*prev); prev = &ic->nodes; continue; } if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) { /* It's in the block we're erasing */ struct jffs2_raw_node_ref *this; this = *prev; *prev = this->next_in_ino; this->next_in_ino = NULL; if (this == ref) break; continue; } /* Not to be deleted. Skip */ prev = &((*prev)->next_in_ino); } /* PARANOIA */ if (!ic) { printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n"); return; } D1(printk(KERN_DEBUG "Removed nodes in range 0x%08x-0x%08x from ino #%u\n", jeb->offset, jeb->offset + c->sector_size, ic->ino)); D2({ int i=0; struct jffs2_raw_node_ref *this; printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG); this = ic->nodes; while(this) { printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this)); if (++i == 5) { printk("\n" KERN_DEBUG); i=0; } this = this->next_in_ino; } printk("\n"); }); if (ic->nodes == (void *)ic && ic->nlink == 0) jffs2_del_ino_cache(c, ic); } static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *ref; D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset)); while(jeb->first_node) { ref = jeb->first_node; jeb->first_node = ref->next_phys; /* Remove from the inode-list */ if (ref->next_in_ino) jffs2_remove_node_refs_from_ino_list(c, ref, jeb); /* else it was a non-inode node or already removed, so don't bother */ jffs2_free_raw_node_ref(ref); } jeb->last_node = NULL; } static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset) { void *ebuf; uint32_t ofs; size_t retlen; int ret = -EIO; ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!ebuf) { printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); return -EAGAIN; } D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) { uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); int i; *bad_offset = ofs; ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); goto fail; } if (retlen != readlen) { printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); goto fail; } for (i=0; ioffset)); bad_offset = jeb->offset; /* Cleanmarker in oob area or no cleanmarker at all ? */ if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) { if (jffs2_cleanmarker_oob(c)) { if (jffs2_write_nand_cleanmarker(c, jeb)) goto filebad; } jeb->first_node = jeb->last_node = NULL; jeb->free_size = c->sector_size; jeb->used_size = 0; jeb->dirty_size = 0; jeb->wasted_size = 0; } else { struct kvec vecs[1]; struct jffs2_unknown_node marker = { .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), .totlen = cpu_to_je32(c->cleanmarker_size) }; marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n"); goto refile; } marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); vecs[0].iov_base = (unsigned char *) ▮ vecs[0].iov_len = sizeof(marker); ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); if (ret || retlen != sizeof(marker)) { if (ret) printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); else printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", jeb->offset, sizeof(marker), retlen); jffs2_free_raw_node_ref(marker_ref); goto filebad; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset | REF_NORMAL; marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; jeb->free_size = c->sector_size - c->cleanmarker_size; jeb->used_size = c->cleanmarker_size; jeb->dirty_size = 0; jeb->wasted_size = 0; } spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->free_size += jeb->free_size; c->used_size += jeb->used_size; jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); list_add_tail(&jeb->list, &c->free_list); c->nr_erasing_blocks--; c->nr_free_blocks++; spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); return; filebad: spin_lock(&c->erase_completion_lock); /* Stick it on a list (any list) so erase_failed can take it right off again. Silly, but shouldn't happen often. */ list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); jffs2_erase_failed(c, jeb, bad_offset); return; refile: /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); spin_lock(&c->erase_completion_lock); list_add(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); return; } linux-2.6.17/fs/jffs2/.svn/text-base/super.c.svn-base0000444000000000000000000002274110574207642020700 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: super.c,v 1.110 2005/11/07 11:14:42 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "compr.h" #include "nodelist.h" static void jffs2_put_super(struct super_block *); static kmem_cache_t *jffs2_inode_cachep; static struct inode *jffs2_alloc_inode(struct super_block *sb) { struct jffs2_inode_info *ei; ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } static void jffs2_destroy_inode(struct inode *inode) { kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); } static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { init_MUTEX(&ei->sem); inode_init_once(&ei->vfs_inode); } } static int jffs2_sync_fs(struct super_block *sb, int wait) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); return 0; } static struct super_operations jffs2_super_operations = { .alloc_inode = jffs2_alloc_inode, .destroy_inode =jffs2_destroy_inode, .read_inode = jffs2_read_inode, .put_super = jffs2_put_super, .write_super = jffs2_write_super, .statfs = jffs2_statfs, .remount_fs = jffs2_remount_fs, .clear_inode = jffs2_clear_inode, .dirty_inode = jffs2_dirty_inode, .sync_fs = jffs2_sync_fs, }; static int jffs2_sb_compare(struct super_block *sb, void *data) { struct jffs2_sb_info *p = data; struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); /* The superblocks are considered to be equivalent if the underlying MTD device is the same one */ if (c->mtd == p->mtd) { D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name)); return 1; } else { D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n", c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name)); return 0; } } static int jffs2_sb_set(struct super_block *sb, void *data) { struct jffs2_sb_info *p = data; /* For persistence of NFS exports etc. we use the same s_dev each time we mount the device, don't just use an anonymous device */ sb->s_fs_info = p; p->os_priv = sb; sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index); return 0; } static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct mtd_info *mtd) { struct super_block *sb; struct jffs2_sb_info *c; int ret; c = kmalloc(sizeof(*c), GFP_KERNEL); if (!c) return ERR_PTR(-ENOMEM); memset(c, 0, sizeof(*c)); c->mtd = mtd; sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); if (IS_ERR(sb)) goto out_put; if (sb->s_root) { /* New mountpoint for JFFS2 which is already mounted */ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", mtd->index, mtd->name)); goto out_put; } D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", mtd->index, mtd->name)); /* Initialize JFFS2 superblock locks, the further initialization will be * done later */ init_MUTEX(&c->alloc_sem); init_MUTEX(&c->erase_free_sem); init_waitqueue_head(&c->erase_wait); init_waitqueue_head(&c->inocache_wq); spin_lock_init(&c->erase_completion_lock); spin_lock_init(&c->inocache_lock); sb->s_op = &jffs2_super_operations; sb->s_flags = flags | MS_NOATIME; ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); if (ret) { /* Failure case... */ up_write(&sb->s_umount); deactivate_super(sb); return ERR_PTR(ret); } sb->s_flags |= MS_ACTIVE; return sb; out_put: kfree(c); put_mtd_device(mtd); return sb; } static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, int mtdnr) { struct mtd_info *mtd; mtd = get_mtd_device(NULL, mtdnr); if (!mtd) { D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); return ERR_PTR(-EINVAL); } return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); } static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { int err; struct nameidata nd; int mtdnr; if (!dev_name) return ERR_PTR(-EINVAL); D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); /* The preferred way of mounting in future; especially when CONFIG_BLK_DEV is implemented - we specify the underlying MTD device by number or by name, so that we don't require block device support to be present in the kernel. */ /* FIXME: How to do the root fs this way? */ if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { /* Probably mounting without the blkdev crap */ if (dev_name[3] == ':') { struct mtd_info *mtd; /* Mount by MTD device name */ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { mtd = get_mtd_device(NULL, mtdnr); if (mtd) { if (!strcmp(mtd->name, dev_name+4)) return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); put_mtd_device(mtd); } } printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); } else if (isdigit(dev_name[3])) { /* Mount by MTD device number name */ char *endptr; mtdnr = simple_strtoul(dev_name+3, &endptr, 0); if (!*endptr) { /* It was a valid number */ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); } } } /* Try the old way - the hack where we allowed users to mount /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", err, nd.dentry->d_inode)); if (err) return ERR_PTR(err); err = -EINVAL; if (!S_ISBLK(nd.dentry->d_inode->i_mode)) goto out; if (nd.mnt->mnt_flags & MNT_NODEV) { err = -EACCES; goto out; } if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { if (!(flags & MS_SILENT)) printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", dev_name); goto out; } mtdnr = iminor(nd.dentry->d_inode); path_release(&nd); return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); out: path_release(&nd); return ERR_PTR(err); } static void jffs2_put_super (struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); jffs2_sum_exit(c); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); if (jffs2_blocks_use_vmalloc(c)) vfree(c->blocks); else kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) c->mtd->sync(c->mtd); D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); } static void jffs2_kill_sb(struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); if (!(sb->s_flags & MS_RDONLY)) jffs2_stop_garbage_collect_thread(c); generic_shutdown_super(sb); put_mtd_device(c->mtd); kfree(c); } static struct file_system_type jffs2_fs_type = { .owner = THIS_MODULE, .name = "jffs2", .get_sb = jffs2_get_sb, .kill_sb = jffs2_kill_sb, }; static int __init init_jffs2_fs(void) { int ret; printk(KERN_INFO "JFFS2 version 2.2." #ifdef CONFIG_JFFS2_FS_WRITEBUFFER " (NAND)" #endif #ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY) " #endif " (C) 2001-2003 Red Hat, Inc.\n"); jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), jffs2_i_init_once, NULL); if (!jffs2_inode_cachep) { printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); return -ENOMEM; } ret = jffs2_compressors_init(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n"); goto out; } ret = jffs2_create_slab_caches(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); goto out_compressors; } ret = register_filesystem(&jffs2_fs_type); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); goto out_slab; } return 0; out_slab: jffs2_destroy_slab_caches(); out_compressors: jffs2_compressors_exit(); out: kmem_cache_destroy(jffs2_inode_cachep); return ret; } static void __exit exit_jffs2_fs(void) { unregister_filesystem(&jffs2_fs_type); jffs2_destroy_slab_caches(); jffs2_compressors_exit(); kmem_cache_destroy(jffs2_inode_cachep); } module_init(init_jffs2_fs); module_exit(exit_jffs2_fs); MODULE_DESCRIPTION("The Journalling Flash File System, v2"); MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for // the sake of this tag. It's Free Software. linux-2.6.17/fs/jffs2/.svn/text-base/writev.c.svn-base0000444000000000000000000000354510574207642021063 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: writev.c,v 1.8 2005/09/09 15:11:58 havasi Exp $ * */ #include #include #include "nodelist.h" /* This ought to be in core MTD code. All registered MTD devices without writev should have this put in place. Bug the MTD maintainer */ static inline int mtd_fake_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { unsigned long i; size_t totlen = 0, thislen; int ret = 0; for (i=0; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); totlen += thislen; if (ret || thislen != vecs[i].iov_len) break; to += vecs[i].iov_len; } if (retlen) *retlen = totlen; return ret; } int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { if (!jffs2_is_writebuffered(c)) { if (jffs2_sum_active()) { int res; res = jffs2_sum_add_kvec(c, vecs, count, (uint32_t) to); if (res) { return res; } } } if (c->mtd->writev) return c->mtd->writev(c->mtd, vecs, count, to, retlen); else { return mtd_fake_writev(c->mtd, vecs, count, to, retlen); } } int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) { int ret; ret = c->mtd->write(c->mtd, ofs, len, retlen, buf); if (jffs2_sum_active()) { struct kvec vecs[1]; int res; vecs[0].iov_base = (unsigned char *) buf; vecs[0].iov_len = len; res = jffs2_sum_add_kvec(c, vecs, 1, (uint32_t) ofs); if (res) { return res; } } return ret; } linux-2.6.17/fs/jffs2/.svn/text-base/histo.h.svn-base0000444000000000000000000000025110574207642020665 0ustar rootroot/* This file provides the bit-probabilities for the input file */ #define BIT_DIVIDER 629 static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */ linux-2.6.17/fs/jffs2/.svn/text-base/README.Locking.svn-base0000444000000000000000000001442610574207642021644 0ustar rootroot $Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $ JFFS2 LOCKING DOCUMENTATION --------------------------- At least theoretically, JFFS2 does not require the Big Kernel Lock (BKL), which was always helpfully obtained for it by Linux 2.4 VFS code. It has its own locking, as described below. This document attempts to describe the existing locking rules for JFFS2. It is not expected to remain perfectly up to date, but ought to be fairly close. alloc_sem --------- The alloc_sem is a per-filesystem semaphore, used primarily to ensure contiguous allocation of space on the medium. It is automatically obtained during space allocations (jffs2_reserve_space()) and freed upon write completion (jffs2_complete_reservation()). Note that the garbage collector will obtain this right at the beginning of jffs2_garbage_collect_pass() and release it at the end, thereby preventing any other write activity on the file system during a garbage collect pass. When writing new nodes, the alloc_sem must be held until the new nodes have been properly linked into the data structures for the inode to which they belong. This is for the benefit of NAND flash - adding new nodes to an inode may obsolete old ones, and by holding the alloc_sem until this happens we ensure that any data in the write-buffer at the time this happens are part of the new node, not just something that was written afterwards. Hence, we can ensure the newly-obsoleted nodes don't actually get erased until the write-buffer has been flushed to the medium. With the introduction of NAND flash support and the write-buffer, the alloc_sem is also used to protect the wbuf-related members of the jffs2_sb_info structure. Atomically reading the wbuf_len member to see if the wbuf is currently holding any data is permitted, though. Ordering constraints: See f->sem. File Semaphore f->sem --------------------- This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. It protects the contents of the jffs2_inode_info private inode data, including the linked list of node fragments (but see the notes below on erase_completion_lock), etc. The reason that the i_sem itself isn't used for this purpose is to avoid deadlocks with garbage collection -- the VFS will lock the i_sem before calling a function which may need to allocate space. The allocation may trigger garbage-collection, which may need to move a node belonging to the inode which was locked in the first place by the VFS. If the garbage collection code were to attempt to lock the i_sem of the inode from which it's garbage-collecting a physical node, this lead to deadlock, unless we played games with unlocking the i_sem before calling the space allocation functions. Instead of playing such games, we just have an extra internal semaphore, which is obtained by the garbage collection code and also by the normal file system code _after_ allocation of space. Ordering constraints: 1. Never attempt to allocate space or lock alloc_sem with any f->sem held. 2. Never attempt to lock two file semaphores in one thread. No ordering rules have been made for doing so. erase_completion_lock spinlock ------------------------------ This is used to serialise access to the eraseblock lists, to the per-eraseblock lists of physical jffs2_raw_node_ref structures, and (NB) the per-inode list of physical nodes. The latter is a special case - see below. As the MTD API no longer permits erase-completion callback functions to be called from bottom-half (timer) context (on the basis that nobody ever actually implemented such a thing), it's now sufficient to use a simple spin_lock() rather than spin_lock_bh(). Note that the per-inode list of physical nodes (f->nodes) is a special case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in the list are protected by the file semaphore f->sem. But the erase code may remove _obsolete_ nodes from the list while holding only the erase_completion_lock. So you can walk the list only while holding the erase_completion_lock, and can drop the lock temporarily mid-walk as long as the pointer you're holding is to a _valid_ node, not an obsolete one. The erase_completion_lock is also used to protect the c->gc_task pointer when the garbage collection thread exits. The code to kill the GC thread locks it, sends the signal, then unlocks it - while the GC thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. inocache_lock spinlock ---------------------- This spinlock protects the hashed list (c->inocache_list) of the in-core jffs2_inode_cache objects (each inode in JFFS2 has the correspondent jffs2_inode_cache object). So, the inocache_lock has to be locked while walking the c->inocache_list hash buckets. This spinlock also covers allocation of new inode numbers, which is currently just '++->highest_ino++', but might one day get more complicated if we need to deal with wrapping after 4 milliard inode numbers are used. Note, the f->sem guarantees that the correspondent jffs2_inode_cache will not be removed. So, it is allowed to access it without locking the inocache_lock spinlock. Ordering constraints: If both erase_completion_lock and inocache_lock are needed, the c->erase_completion has to be acquired first. erase_free_sem -------------- This semaphore is only used by the erase code which frees obsolete node references and the jffs2_garbage_collect_deletion_dirent() function. The latter function on NAND flash must read _obsolete_ nodes to determine whether the 'deletion dirent' under consideration can be discarded or whether it is still required to show that an inode has been unlinked. Because reading from the flash may sleep, the erase_completion_lock cannot be held, so an alternative, more heavyweight lock was required to prevent the erase code from freeing the jffs2_raw_node_ref structures in question while the garbage collection code is looking at them. Suggestions for alternative solutions to this problem would be welcomed. wbuf_sem -------- This read/write semaphore protects against concurrent access to the write-behind buffer ('wbuf') used for flash chips where we must write in blocks. It protects both the contents of the wbuf and the metadata which indicates which flash region (if any) is currently covered by the buffer. Ordering constraints: Lock wbuf_sem last, after the alloc_sem or and f->sem. linux-2.6.17/fs/jffs2/.svn/text-base/debug.c.svn-base0000444000000000000000000005301010574207642020621 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: debug.c,v 1.12 2005/11/07 11:14:39 gleixner Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" #include "debug.h" #ifdef JFFS2_DBG_SANITY_CHECKS void __jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { if (unlikely(jeb && jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size)) { JFFS2_ERROR("eeep, space accounting for block at 0x%08x is screwed.\n", jeb->offset); JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + wasted %#08x + unchecked %#08x != total %#08x.\n", jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); BUG(); } if (unlikely(c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size)) { JFFS2_ERROR("eeep, space accounting superblock info is screwed.\n"); JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + erasing %#08x + bad %#08x + wasted %#08x + unchecked %#08x != total %#08x.\n", c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); BUG(); } } void __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); jffs2_dbg_acct_sanity_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); } #endif /* JFFS2_DBG_SANITY_CHECKS */ #ifdef JFFS2_DBG_PARANOIA_CHECKS /* * Check the fragtree. */ void __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f) { down(&f->sem); __jffs2_dbg_fragtree_paranoia_check_nolock(f); up(&f->sem); } void __jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f) { struct jffs2_node_frag *frag; int bitched = 0; for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { struct jffs2_full_dnode *fn = frag->node; if (!fn || !fn->raw) continue; if (ref_flags(fn->raw) == REF_PRISTINE) { if (fn->frags > 1) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2.\n", ref_offset(fn->raw), fn->frags); bitched = 1; } /* A hole node which isn't multi-page should be garbage-collected and merged anyway, so we just check for the frag size here, rather than mucking around with actually reading the node and checking the compression type, which is the real way to tell a hole node. */ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2.\n", ref_offset(fn->raw)); bitched = 1; } if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2.\n", ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); bitched = 1; } } } if (bitched) { JFFS2_ERROR("fragtree is corrupted.\n"); __jffs2_dbg_dump_fragtree_nolock(f); BUG(); } } /* * Check if the flash contains all 0xFF before we start writing. */ void __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, uint32_t ofs, int len) { size_t retlen; int ret, i; unsigned char *buf; buf = kmalloc(len, GFP_KERNEL); if (!buf) return; ret = jffs2_flash_read(c, ofs, len, &retlen, buf); if (ret || (retlen != len)) { JFFS2_WARNING("read %d bytes failed or short. ret %d, retlen %zd.\n", len, ret, retlen); kfree(buf); return; } ret = 0; for (i = 0; i < len; i++) if (buf[i] != 0xff) ret = 1; if (ret) { JFFS2_ERROR("argh, about to write node to %#08x on flash, but there are data already there. The first corrupted byte is at %#08x offset.\n", ofs, ofs + i); __jffs2_dbg_dump_buffer(buf, len, ofs); kfree(buf); BUG(); } kfree(buf); } /* * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'. */ void __jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_acct_paranoia_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { uint32_t my_used_size = 0; uint32_t my_unchecked_size = 0; uint32_t my_dirty_size = 0; struct jffs2_raw_node_ref *ref2 = jeb->first_node; while (ref2) { uint32_t totlen = ref_totlen(c, jeb, ref2); if (ref2->flash_offset < jeb->offset || ref2->flash_offset > jeb->offset + c->sector_size) { JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n", ref_offset(ref2), jeb->offset); goto error; } if (ref_flags(ref2) == REF_UNCHECKED) my_unchecked_size += totlen; else if (!ref_obsolete(ref2)) my_used_size += totlen; else my_dirty_size += totlen; if ((!ref2->next_phys) != (ref2 == jeb->last_node)) { JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next_phys at %#08x (mem %p), last_node is at %#08x (mem %p).\n", ref_offset(ref2), ref2, ref_offset(ref2->next_phys), ref2->next_phys, ref_offset(jeb->last_node), jeb->last_node); goto error; } ref2 = ref2->next_phys; } if (my_used_size != jeb->used_size) { JFFS2_ERROR("Calculated used size %#08x != stored used size %#08x.\n", my_used_size, jeb->used_size); goto error; } if (my_unchecked_size != jeb->unchecked_size) { JFFS2_ERROR("Calculated unchecked size %#08x != stored unchecked size %#08x.\n", my_unchecked_size, jeb->unchecked_size); goto error; } #if 0 /* This should work when we implement ref->__totlen elemination */ if (my_dirty_size != jeb->dirty_size + jeb->wasted_size) { JFFS2_ERROR("Calculated dirty+wasted size %#08x != stored dirty + wasted size %#08x\n", my_dirty_size, jeb->dirty_size + jeb->wasted_size); goto error; } if (jeb->free_size == 0 && my_used_size + my_unchecked_size + my_dirty_size != c->sector_size) { JFFS2_ERROR("The sum of all nodes in block (%#x) != size of block (%#x)\n", my_used_size + my_unchecked_size + my_dirty_size, c->sector_size); goto error; } #endif return; error: __jffs2_dbg_dump_node_refs_nolock(c, jeb); __jffs2_dbg_dump_jeb_nolock(jeb); __jffs2_dbg_dump_block_lists_nolock(c); BUG(); } #endif /* JFFS2_DBG_PARANOIA_CHECKS */ #if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS) /* * Dump the node_refs of the 'jeb' JFFS2 eraseblock. */ void __jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_dump_node_refs_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *ref; int i = 0; printk(JFFS2_DBG_MSG_PREFIX " Dump node_refs of the eraseblock %#08x\n", jeb->offset); if (!jeb->first_node) { printk(JFFS2_DBG_MSG_PREFIX " no nodes in the eraseblock %#08x\n", jeb->offset); return; } printk(JFFS2_DBG); for (ref = jeb->first_node; ; ref = ref->next_phys) { printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); if (ref->next_phys) printk("->"); else break; if (++i == 4) { i = 0; printk("\n" JFFS2_DBG); } } printk("\n"); } /* * Dump an eraseblock's space accounting. */ void __jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_dump_jeb_nolock(jeb); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb) { if (!jeb) return; printk(JFFS2_DBG_MSG_PREFIX " dump space accounting for the eraseblock at %#08x:\n", jeb->offset); printk(JFFS2_DBG "used_size: %#08x\n", jeb->used_size); printk(JFFS2_DBG "dirty_size: %#08x\n", jeb->dirty_size); printk(JFFS2_DBG "wasted_size: %#08x\n", jeb->wasted_size); printk(JFFS2_DBG "unchecked_size: %#08x\n", jeb->unchecked_size); printk(JFFS2_DBG "free_size: %#08x\n", jeb->free_size); } void __jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c) { spin_lock(&c->erase_completion_lock); __jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); } void __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) { printk(JFFS2_DBG_MSG_PREFIX " dump JFFS2 blocks lists:\n"); printk(JFFS2_DBG "flash_size: %#08x\n", c->flash_size); printk(JFFS2_DBG "used_size: %#08x\n", c->used_size); printk(JFFS2_DBG "dirty_size: %#08x\n", c->dirty_size); printk(JFFS2_DBG "wasted_size: %#08x\n", c->wasted_size); printk(JFFS2_DBG "unchecked_size: %#08x\n", c->unchecked_size); printk(JFFS2_DBG "free_size: %#08x\n", c->free_size); printk(JFFS2_DBG "erasing_size: %#08x\n", c->erasing_size); printk(JFFS2_DBG "bad_size: %#08x\n", c->bad_size); printk(JFFS2_DBG "sector_size: %#08x\n", c->sector_size); printk(JFFS2_DBG "jffs2_reserved_blocks size: %#08x\n", c->sector_size * c->resv_blocks_write); if (c->nextblock) printk(JFFS2_DBG "nextblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size); else printk(JFFS2_DBG "nextblock: NULL\n"); if (c->gcblock) printk(JFFS2_DBG "gcblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); else printk(JFFS2_DBG "gcblock: NULL\n"); if (list_empty(&c->clean_list)) { printk(JFFS2_DBG "clean_list: empty\n"); } else { struct list_head *this; int numblocks = 0; uint32_t dirty = 0; list_for_each(this, &c->clean_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); numblocks ++; dirty += jeb->wasted_size; if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "clean_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } printk (JFFS2_DBG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks); } if (list_empty(&c->very_dirty_list)) { printk(JFFS2_DBG "very_dirty_list: empty\n"); } else { struct list_head *this; int numblocks = 0; uint32_t dirty = 0; list_for_each(this, &c->very_dirty_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); numblocks ++; dirty += jeb->dirty_size; if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "very_dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } printk (JFFS2_DBG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", numblocks, dirty, dirty / numblocks); } if (list_empty(&c->dirty_list)) { printk(JFFS2_DBG "dirty_list: empty\n"); } else { struct list_head *this; int numblocks = 0; uint32_t dirty = 0; list_for_each(this, &c->dirty_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); numblocks ++; dirty += jeb->dirty_size; if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } printk (JFFS2_DBG "contains %d blocks with total dirty size %u, average dirty size: %u\n", numblocks, dirty, dirty / numblocks); } if (list_empty(&c->erasable_list)) { printk(JFFS2_DBG "erasable_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erasable_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erasable_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->erasing_list)) { printk(JFFS2_DBG "erasing_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erasing_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erasing_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->erase_pending_list)) { printk(JFFS2_DBG "erase_pending_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erase_pending_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erase_pending_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->erasable_pending_wbuf_list)) { printk(JFFS2_DBG "erasable_pending_wbuf_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->erasable_pending_wbuf_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "erasable_pending_wbuf_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->free_list)) { printk(JFFS2_DBG "free_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->free_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "free_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->bad_list)) { printk(JFFS2_DBG "bad_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->bad_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "bad_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } if (list_empty(&c->bad_used_list)) { printk(JFFS2_DBG "bad_used_list: empty\n"); } else { struct list_head *this; list_for_each(this, &c->bad_used_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { printk(JFFS2_DBG "bad_used_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); } } } } void __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f) { down(&f->sem); jffs2_dbg_dump_fragtree_nolock(f); up(&f->sem); } void __jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f) { struct jffs2_node_frag *this = frag_first(&f->fragtree); uint32_t lastofs = 0; int buggy = 0; printk(JFFS2_DBG_MSG_PREFIX " dump fragtree of ino #%u\n", f->inocache->ino); while(this) { if (this->node) printk(JFFS2_DBG "frag %#04x-%#04x: %#08x(%d) on flash (*%p), left (%p), right (%p), parent (%p)\n", this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw), this, frag_left(this), frag_right(this), frag_parent(this)); else printk(JFFS2_DBG "frag %#04x-%#04x: hole (*%p). left (%p), right (%p), parent (%p)\n", this->ofs, this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this)); if (this->ofs != lastofs) buggy = 1; lastofs = this->ofs + this->size; this = frag_next(this); } if (f->metadata) printk(JFFS2_DBG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); if (buggy) { JFFS2_ERROR("frag tree got a hole in it.\n"); BUG(); } } #define JFFS2_BUFDUMP_BYTES_PER_LINE 32 void __jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs) { int skip; int i; printk(JFFS2_DBG_MSG_PREFIX " dump from offset %#08x to offset %#08x (%x bytes).\n", offs, offs + len, len); i = skip = offs % JFFS2_BUFDUMP_BYTES_PER_LINE; offs = offs & ~(JFFS2_BUFDUMP_BYTES_PER_LINE - 1); if (skip != 0) printk(JFFS2_DBG "%#08x: ", offs); while (skip--) printk(" "); while (i < len) { if ((i % JFFS2_BUFDUMP_BYTES_PER_LINE) == 0 && i != len -1) { if (i != 0) printk("\n"); offs += JFFS2_BUFDUMP_BYTES_PER_LINE; printk(JFFS2_DBG "%0#8x: ", offs); } printk("%02x ", buf[i]); i += 1; } printk("\n"); } /* * Dump a JFFS2 node. */ void __jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs) { union jffs2_node_union node; int len = sizeof(union jffs2_node_union); size_t retlen; uint32_t crc; int ret; printk(JFFS2_DBG_MSG_PREFIX " dump node at offset %#08x.\n", ofs); ret = jffs2_flash_read(c, ofs, len, &retlen, (unsigned char *)&node); if (ret || (retlen != len)) { JFFS2_ERROR("read %d bytes failed or short. ret %d, retlen %zd.\n", len, ret, retlen); return; } printk(JFFS2_DBG "magic:\t%#04x\n", je16_to_cpu(node.u.magic)); printk(JFFS2_DBG "nodetype:\t%#04x\n", je16_to_cpu(node.u.nodetype)); printk(JFFS2_DBG "totlen:\t%#08x\n", je32_to_cpu(node.u.totlen)); printk(JFFS2_DBG "hdr_crc:\t%#08x\n", je32_to_cpu(node.u.hdr_crc)); crc = crc32(0, &node.u, sizeof(node.u) - 4); if (crc != je32_to_cpu(node.u.hdr_crc)) { JFFS2_ERROR("wrong common header CRC.\n"); return; } if (je16_to_cpu(node.u.magic) != JFFS2_MAGIC_BITMASK && je16_to_cpu(node.u.magic) != JFFS2_OLD_MAGIC_BITMASK) { JFFS2_ERROR("wrong node magic: %#04x instead of %#04x.\n", je16_to_cpu(node.u.magic), JFFS2_MAGIC_BITMASK); return; } switch(je16_to_cpu(node.u.nodetype)) { case JFFS2_NODETYPE_INODE: printk(JFFS2_DBG "the node is inode node\n"); printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.i.ino)); printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.i.version)); printk(JFFS2_DBG "mode:\t%#08x\n", node.i.mode.m); printk(JFFS2_DBG "uid:\t%#04x\n", je16_to_cpu(node.i.uid)); printk(JFFS2_DBG "gid:\t%#04x\n", je16_to_cpu(node.i.gid)); printk(JFFS2_DBG "isize:\t%#08x\n", je32_to_cpu(node.i.isize)); printk(JFFS2_DBG "atime:\t%#08x\n", je32_to_cpu(node.i.atime)); printk(JFFS2_DBG "mtime:\t%#08x\n", je32_to_cpu(node.i.mtime)); printk(JFFS2_DBG "ctime:\t%#08x\n", je32_to_cpu(node.i.ctime)); printk(JFFS2_DBG "offset:\t%#08x\n", je32_to_cpu(node.i.offset)); printk(JFFS2_DBG "csize:\t%#08x\n", je32_to_cpu(node.i.csize)); printk(JFFS2_DBG "dsize:\t%#08x\n", je32_to_cpu(node.i.dsize)); printk(JFFS2_DBG "compr:\t%#02x\n", node.i.compr); printk(JFFS2_DBG "usercompr:\t%#02x\n", node.i.usercompr); printk(JFFS2_DBG "flags:\t%#04x\n", je16_to_cpu(node.i.flags)); printk(JFFS2_DBG "data_crc:\t%#08x\n", je32_to_cpu(node.i.data_crc)); printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.i.node_crc)); crc = crc32(0, &node.i, sizeof(node.i) - 8); if (crc != je32_to_cpu(node.i.node_crc)) { JFFS2_ERROR("wrong node header CRC.\n"); return; } break; case JFFS2_NODETYPE_DIRENT: printk(JFFS2_DBG "the node is dirent node\n"); printk(JFFS2_DBG "pino:\t%#08x\n", je32_to_cpu(node.d.pino)); printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.d.version)); printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.d.ino)); printk(JFFS2_DBG "mctime:\t%#08x\n", je32_to_cpu(node.d.mctime)); printk(JFFS2_DBG "nsize:\t%#02x\n", node.d.nsize); printk(JFFS2_DBG "type:\t%#02x\n", node.d.type); printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.d.node_crc)); printk(JFFS2_DBG "name_crc:\t%#08x\n", je32_to_cpu(node.d.name_crc)); node.d.name[node.d.nsize] = '\0'; printk(JFFS2_DBG "name:\t\"%s\"\n", node.d.name); crc = crc32(0, &node.d, sizeof(node.d) - 8); if (crc != je32_to_cpu(node.d.node_crc)) { JFFS2_ERROR("wrong node header CRC.\n"); return; } break; default: printk(JFFS2_DBG "node type is unknown\n"); break; } } #endif /* JFFS2_DBG_DUMPS || JFFS2_DBG_PARANOIA_CHECKS */ linux-2.6.17/fs/jffs2/.svn/text-base/read.c.svn-base0000444000000000000000000001541310574207642020453 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: read.c,v 1.42 2005/11/07 11:14:41 gleixner Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) { struct jffs2_raw_inode *ri; size_t readlen; uint32_t crc; unsigned char *decomprbuf = NULL; unsigned char *readbuf = NULL; int ret = 0; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri); if (ret) { jffs2_free_raw_inode(ri); printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret); return ret; } if (readlen != sizeof(*ri)) { jffs2_free_raw_inode(ri); printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", ref_offset(fd->raw), sizeof(*ri), readlen); return -EIO; } crc = crc32(0, ri, sizeof(*ri)-8); D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", ref_offset(fd->raw), je32_to_cpu(ri->node_crc), crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), je32_to_cpu(ri->offset), buf)); if (crc != je32_to_cpu(ri->node_crc)) { printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw)); ret = -EIO; goto out_ri; } /* There was a bug where we wrote hole nodes out with csize/dsize swapped. Deal with it */ if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && je32_to_cpu(ri->csize)) { ri->dsize = ri->csize; ri->csize = cpu_to_je32(0); } D1(if(ofs + len > je32_to_cpu(ri->dsize)) { printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, je32_to_cpu(ri->dsize)); ret = -EINVAL; goto out_ri; }); if (ri->compr == JFFS2_COMPR_ZERO) { memset(buf, 0, len); goto out_ri; } /* Cases: Reading whole node and it's uncompressed - read directly to buffer provided, check CRC. Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy */ if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) { readbuf = buf; } else { readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL); if (!readbuf) { ret = -ENOMEM; goto out_ri; } } if (ri->compr != JFFS2_COMPR_NONE) { if (len < je32_to_cpu(ri->dsize)) { decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL); if (!decomprbuf) { ret = -ENOMEM; goto out_readbuf; } } else { decomprbuf = buf; } } else { decomprbuf = readbuf; } D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize), readbuf)); ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri), je32_to_cpu(ri->csize), &readlen, readbuf); if (!ret && readlen != je32_to_cpu(ri->csize)) ret = -EIO; if (ret) goto out_decomprbuf; crc = crc32(0, readbuf, je32_to_cpu(ri->csize)); if (crc != je32_to_cpu(ri->data_crc)) { printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw)); ret = -EIO; goto out_decomprbuf; } D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc)); if (ri->compr != JFFS2_COMPR_NONE) { D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); if (ret) { printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); goto out_decomprbuf; } } if (len < je32_to_cpu(ri->dsize)) { memcpy(buf, decomprbuf+ofs, len); } out_decomprbuf: if(decomprbuf != buf && decomprbuf != readbuf) kfree(decomprbuf); out_readbuf: if(readbuf != buf) kfree(readbuf); out_ri: jffs2_free_raw_inode(ri); return ret; } int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *buf, uint32_t offset, uint32_t len) { uint32_t end = offset + len; struct jffs2_node_frag *frag; int ret; D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n", f->inocache->ino, offset, offset+len)); frag = jffs2_lookup_node_frag(&f->fragtree, offset); /* XXX FIXME: Where a single physical node actually shows up in two frags, we read it twice. Don't do that. */ /* Now we're pointing at the first frag which overlaps our page */ while(offset < end) { D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end)); if (unlikely(!frag || frag->ofs > offset)) { uint32_t holesize = end - offset; if (frag) { D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); holesize = min(holesize, frag->ofs - offset); } D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); memset(buf, 0, holesize); buf += holesize; offset += holesize; continue; } else if (unlikely(!frag->node)) { uint32_t holeend = min(end, frag->ofs + frag->size); D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size)); memset(buf, 0, holeend - offset); buf += holeend - offset; offset = holeend; frag = frag_next(frag); continue; } else { uint32_t readlen; uint32_t fragofs; /* offset within the frag to start reading */ fragofs = offset - frag->ofs; readlen = min(frag->size - fragofs, end - offset); D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n", frag->ofs+fragofs, frag->ofs+fragofs+readlen, ref_offset(frag->node->raw), ref_flags(frag->node->raw))); ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); D2(printk(KERN_DEBUG "node read done\n")); if (ret) { D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret)); memset(buf, 0, readlen); return ret; } buf += readlen; offset += readlen; frag = frag_next(frag); D2(printk(KERN_DEBUG "node read was OK. Looping\n")); } } return 0; } linux-2.6.17/fs/jffs2/.svn/text-base/symlink.c.svn-base0000444000000000000000000000341310574207642021223 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: symlink.c,v 1.19 2005/11/07 11:14:42 gleixner Exp $ * */ #include #include #include #include #include "nodelist.h" static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = jffs2_follow_link, .setattr = jffs2_setattr }; static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); char *p = (char *)f->target; /* * We don't acquire the f->sem mutex here since the only data we * use is f->target. * * 1. If we are here the inode has already built and f->target has * to point to the target path. * 2. Nobody uses f->target (if the inode is symlink's inode). The * exception is inode freeing function which frees f->target. But * it can't be called while we are here and before VFS has * stopped using our f->target string which we provide by means of * nd_set_link() call. */ if (!p) { printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); p = ERR_PTR(-EIO); } D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target)); nd_set_link(nd, p); /* * We will unlock the f->sem mutex but VFS will use the f->target string. This is safe * since the only way that may cause f->target to be changed is iput() operation. * But VFS will not use f->target after iput() has been called. */ return NULL; } linux-2.6.17/fs/jffs2/.svn/text-base/ioctl.c.svn-base0000444000000000000000000000105310574207642020645 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: ioctl.c,v 1.10 2005/11/07 11:14:40 gleixner Exp $ * */ #include int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which will include compression support etc. */ return -ENOTTY; } linux-2.6.17/fs/jffs2/.svn/text-base/compr.h.svn-base0000444000000000000000000000702110574207642020661 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * * $Id: compr.h,v 1.9 2005/11/07 11:14:38 gleixner Exp $ * */ #ifndef __JFFS2_COMPR_H__ #define __JFFS2_COMPR_H__ #include #include #include #include #include #include #include #include #include #include #include #include "nodelist.h" #define JFFS2_RUBINMIPS_PRIORITY 10 #define JFFS2_DYNRUBIN_PRIORITY 20 #define JFFS2_LZARI_PRIORITY 30 #define JFFS2_LZO_PRIORITY 40 #define JFFS2_RTIME_PRIORITY 50 #define JFFS2_ZLIB_PRIORITY 60 #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ #define JFFS2_COMPR_MODE_NONE 0 #define JFFS2_COMPR_MODE_PRIORITY 1 #define JFFS2_COMPR_MODE_SIZE 2 struct jffs2_compressor { struct list_head list; int priority; /* used by prirority comr. mode */ char *name; char compr; /* JFFS2_COMPR_XXX */ int (*compress)(unsigned char *data_in, unsigned char *cpage_out, uint32_t *srclen, uint32_t *destlen, void *model); int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen, void *model); int usecount; int disabled; /* if seted the compressor won't compress */ unsigned char *compr_buf; /* used by size compr. mode */ uint32_t compr_buf_size; /* used by size compr. mode */ uint32_t stat_compr_orig_size; uint32_t stat_compr_new_size; uint32_t stat_compr_blocks; uint32_t stat_decompr_blocks; }; int jffs2_register_compressor(struct jffs2_compressor *comp); int jffs2_unregister_compressor(struct jffs2_compressor *comp); int jffs2_compressors_init(void); int jffs2_compressors_exit(void); uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen); int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); #ifdef CONFIG_JFFS2_PROC int jffs2_enable_compressor_name(const char *name); int jffs2_disable_compressor_name(const char *name); int jffs2_set_compression_mode_name(const char *mode_name); char *jffs2_get_compression_mode_name(void); int jffs2_set_compressor_priority(const char *mode_name, int priority); char *jffs2_list_compressors(void); char *jffs2_stats(void); #endif /* Compressor modules */ /* These functions will be called by jffs2_compressors_init/exit */ #ifdef CONFIG_JFFS2_RUBIN int jffs2_rubinmips_init(void); void jffs2_rubinmips_exit(void); int jffs2_dynrubin_init(void); void jffs2_dynrubin_exit(void); #endif #ifdef CONFIG_JFFS2_RTIME int jffs2_rtime_init(void); void jffs2_rtime_exit(void); #endif #ifdef CONFIG_JFFS2_ZLIB int jffs2_zlib_init(void); void jffs2_zlib_exit(void); #endif #endif /* __JFFS2_COMPR_H__ */ linux-2.6.17/fs/jffs2/.svn/text-base/TODO.svn-base0000444000000000000000000000411110574207642020055 0ustar rootroot$Id: TODO,v 1.18 2005/09/22 11:24:56 dedekind Exp $ - support asynchronous operation -- add a per-fs 'reserved_space' count, let each outstanding write reserve the _maximum_ amount of physical space it could take. Let GC flush the outstanding writes because the reservations will necessarily be pessimistic. With this we could even do shared writable mmap, if we can have a fs hook for do_wp_page() to make the reservation. - disable compression in commit_write()? - fine-tune the allocation / GC thresholds - chattr support - turning on/off and tuning compression per-inode - checkpointing (do we need this? scan is quite fast) - make the scan code populate real inodes so read_inode just after mount doesn't have to read the flash twice for large files. Make this a per-inode option, changable with chattr, so you can decide which inodes should be in-core immediately after mount. - test, test, test - NAND flash support: - almost done :) - use bad block check instead of the hardwired byte check - Optimisations: - Split writes so they go to two separate blocks rather than just c->nextblock. By writing _new_ nodes to one block, and garbage-collected REF_PRISTINE nodes to a different one, we can separate clean nodes from those which are likely to become dirty, and end up with blocks which are each far closer to 100% or 0% clean, hence speeding up later GC progress dramatically. - Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in the full dirent, we only need to go to the flash in lookup() when we think we've got a match, and in readdir(). - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately? - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into jffs2_mark_node_obsolete(). Can all callers work it out? - Remove size from jffs2_raw_node_frag. dedekind: 1. __jffs2_flush_wbuf() has a strange 'pad' parameter. Eliminate. 2. get_sb()->build_fs()->scan() path... Why get_sb() removes scan()'s crap in case of failure? scan() does not clean everything. Fix. linux-2.6.17/fs/jffs2/.svn/text-base/summary.h.svn-base0000444000000000000000000001302310574207642021235 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: summary.h,v 1.2 2005/09/26 11:37:21 havasi Exp $ * */ #ifndef JFFS2_SUMMARY_H #define JFFS2_SUMMARY_H #include #include #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->dirty_size += _x; \ jeb->free_size -= _x ; jeb->dirty_size += _x; \ }while(0) #define USED_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->used_size += _x; \ jeb->free_size -= _x ; jeb->used_size += _x; \ }while(0) #define WASTED_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->wasted_size += _x; \ jeb->free_size -= _x ; jeb->wasted_size += _x; \ }while(0) #define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->unchecked_size += _x; \ jeb->free_size -= _x ; jeb->unchecked_size += _x; \ }while(0) #define BLK_STATE_ALLFF 0 #define BLK_STATE_CLEAN 1 #define BLK_STATE_PARTDIRTY 2 #define BLK_STATE_CLEANMARKER 3 #define BLK_STATE_ALLDIRTY 4 #define BLK_STATE_BADBLOCK 5 #define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff #define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash)) #define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x)) /* Summary structures used on flash */ struct jffs2_sum_unknown_flash { jint16_t nodetype; /* node type */ }; struct jffs2_sum_inode_flash { jint16_t nodetype; /* node type */ jint32_t inode; /* inode number */ jint32_t version; /* inode version */ jint32_t offset; /* offset on jeb */ jint32_t totlen; /* record length */ } __attribute__((packed)); struct jffs2_sum_dirent_flash { jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ jint32_t totlen; /* record length */ jint32_t offset; /* offset on jeb */ jint32_t pino; /* parent inode */ jint32_t version; /* dirent version */ jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ uint8_t name[0]; /* dirent name */ } __attribute__((packed)); union jffs2_sum_flash { struct jffs2_sum_unknown_flash u; struct jffs2_sum_inode_flash i; struct jffs2_sum_dirent_flash d; }; /* Summary structures used in the memory */ struct jffs2_sum_unknown_mem { union jffs2_sum_mem *next; jint16_t nodetype; /* node type */ }; struct jffs2_sum_inode_mem { union jffs2_sum_mem *next; jint16_t nodetype; /* node type */ jint32_t inode; /* inode number */ jint32_t version; /* inode version */ jint32_t offset; /* offset on jeb */ jint32_t totlen; /* record length */ } __attribute__((packed)); struct jffs2_sum_dirent_mem { union jffs2_sum_mem *next; jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ jint32_t totlen; /* record length */ jint32_t offset; /* ofset on jeb */ jint32_t pino; /* parent inode */ jint32_t version; /* dirent version */ jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ uint8_t name[0]; /* dirent name */ } __attribute__((packed)); union jffs2_sum_mem { struct jffs2_sum_unknown_mem u; struct jffs2_sum_inode_mem i; struct jffs2_sum_dirent_mem d; }; /* Summary related information stored in superblock */ struct jffs2_summary { uint32_t sum_size; /* collected summary information for nextblock */ uint32_t sum_num; uint32_t sum_padded; union jffs2_sum_mem *sum_list_head; union jffs2_sum_mem *sum_list_tail; jint32_t *sum_buf; /* buffer for writing out summary */ }; /* Summary marker is stored at the end of every sumarized erase block */ struct jffs2_sum_marker { jint32_t offset; /* offset of the summary node in the jeb */ jint32_t magic; /* == JFFS2_SUM_MAGIC */ }; #define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker)) #ifdef CONFIG_JFFS2_SUMMARY /* SUMMARY SUPPORT ENABLED */ #define jffs2_sum_active() (1) int jffs2_sum_init(struct jffs2_sb_info *c); void jffs2_sum_exit(struct jffs2_sb_info *c); void jffs2_sum_disable_collecting(struct jffs2_summary *s); int jffs2_sum_is_disabled(struct jffs2_summary *s); void jffs2_sum_reset_collected(struct jffs2_summary *s); void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s); int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, uint32_t to); int jffs2_sum_write_sumnode(struct jffs2_sb_info *c); int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size); int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs); int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs); int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t ofs, uint32_t *pseudo_random); #else /* SUMMARY DISABLED */ #define jffs2_sum_active() (0) #define jffs2_sum_init(a) (0) #define jffs2_sum_exit(a) #define jffs2_sum_disable_collecting(a) #define jffs2_sum_is_disabled(a) (0) #define jffs2_sum_reset_collected(a) #define jffs2_sum_add_kvec(a,b,c,d) (0) #define jffs2_sum_move_collected(a,b) #define jffs2_sum_write_sumnode(a) (0) #define jffs2_sum_add_padding_mem(a,b) #define jffs2_sum_add_inode_mem(a,b,c) #define jffs2_sum_add_dirent_mem(a,b,c) #define jffs2_sum_scan_sumnode(a,b,c,d) (0) #endif /* CONFIG_JFFS2_SUMMARY */ #endif /* JFFS2_SUMMARY_H */ linux-2.6.17/fs/jffs2/.svn/text-base/background.c.svn-base0000444000000000000000000000732610574207642021663 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: background.c,v 1.54 2005/05/20 21:37:12 gleixner Exp $ * */ #include #include #include #include #include #include "nodelist.h" static int jffs2_garbage_collect_thread(void *); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { spin_lock(&c->erase_completion_lock); if (c->gc_task && jffs2_thread_should_wake(c)) send_sig(SIGHUP, c->gc_task, 1); spin_unlock(&c->erase_completion_lock); } /* This must only ever be called when no GC thread is currently running */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) { pid_t pid; int ret = 0; BUG_ON(c->gc_task); init_completion(&c->gc_thread_start); init_completion(&c->gc_thread_exit); pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); if (pid < 0) { printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid); complete(&c->gc_thread_exit); ret = pid; } else { /* Wait for it... */ D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); wait_for_completion(&c->gc_thread_start); } return ret; } void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) { int wait = 0; spin_lock(&c->erase_completion_lock); if (c->gc_task) { D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); send_sig(SIGKILL, c->gc_task, 1); wait = 1; } spin_unlock(&c->erase_completion_lock); if (wait) wait_for_completion(&c->gc_thread_exit); } static int jffs2_garbage_collect_thread(void *_c) { struct jffs2_sb_info *c = _c; daemonize("jffs2_gcd_mtd%d", c->mtd->index); allow_signal(SIGKILL); allow_signal(SIGSTOP); allow_signal(SIGCONT); c->gc_task = current; complete(&c->gc_thread_start); set_user_nice(current, 10); for (;;) { allow_signal(SIGHUP); if (!jffs2_thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); /* Yes, there's a race here; we checked jffs2_thread_should_wake() before setting current->state to TASK_INTERRUPTIBLE. But it doesn't matter - We don't care if we miss a wakeup, because the GC thread is only an optimisation anyway. */ schedule(); } if (try_to_freeze()) continue; cond_resched(); /* Put_super will send a SIGKILL and then wait on the sem. */ while (signal_pending(current)) { siginfo_t info; unsigned long signr; signr = dequeue_signal_lock(current, ¤t->blocked, &info); switch(signr) { case SIGSTOP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n")); set_current_state(TASK_STOPPED); schedule(); break; case SIGKILL: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); goto die; case SIGHUP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n")); break; default: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); } } /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ disallow_signal(SIGHUP); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n"); goto die; } } die: spin_lock(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); } linux-2.6.17/fs/jffs2/.svn/text-base/compr.c.svn-base0000444000000000000000000004146210574207642020663 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * Created by Arjan van de Ven * * Copyright (C) 2004 Ferenc Havasi , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr.c,v 1.46 2005/11/07 11:14:38 gleixner Exp $ * */ #include "compr.h" static DEFINE_SPINLOCK(jffs2_compressor_list_lock); /* Available compressors are on this list */ static LIST_HEAD(jffs2_compressor_list); /* Actual compression mode */ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; /* Statistics for blocks stored without compression */ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; /* jffs2_compress: * @data: Pointer to uncompressed data * @cdata: Pointer to returned pointer to buffer for compressed data * @datalen: On entry, holds the amount of data available for compression. * On exit, expected to hold the amount of data actually compressed. * @cdatalen: On entry, holds the amount of space available for compressed * data. On exit, expected to hold the actual size of the compressed * data. * * Returns: Lower byte to be stored with data indicating compression type used. * Zero is used to show that the data could not be compressed - the * compressed version was actually larger than the original. * Upper byte will be used later. (soon) * * If the cdata buffer isn't large enough to hold all the uncompressed data, * jffs2_compress should compress as much as will fit, and should set * *datalen accordingly to show the amount of data which were compressed. */ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen) { int ret = JFFS2_COMPR_NONE; int compr_ret; struct jffs2_compressor *this, *best=NULL; unsigned char *output_buf = NULL, *tmp_buf; uint32_t orig_slen, orig_dlen; uint32_t best_slen=0, best_dlen=0; switch (jffs2_compression_mode) { case JFFS2_COMPR_MODE_NONE: break; case JFFS2_COMPR_MODE_PRIORITY: output_buf = kmalloc(*cdatalen,GFP_KERNEL); if (!output_buf) { printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); goto out; } orig_slen = *datalen; orig_dlen = *cdatalen; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { /* Skip decompress-only backwards-compatibility and disabled modules */ if ((!this->compress)||(this->disabled)) continue; this->usecount++; spin_unlock(&jffs2_compressor_list_lock); *datalen = orig_slen; *cdatalen = orig_dlen; compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); spin_lock(&jffs2_compressor_list_lock); this->usecount--; if (!compr_ret) { ret = this->compr; this->stat_compr_blocks++; this->stat_compr_orig_size += *datalen; this->stat_compr_new_size += *cdatalen; break; } } spin_unlock(&jffs2_compressor_list_lock); if (ret == JFFS2_COMPR_NONE) kfree(output_buf); break; case JFFS2_COMPR_MODE_SIZE: orig_slen = *datalen; orig_dlen = *cdatalen; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { /* Skip decompress-only backwards-compatibility and disabled modules */ if ((!this->compress)||(this->disabled)) continue; /* Allocating memory for output buffer if necessary */ if ((this->compr_buf_sizecompr_buf)) { spin_unlock(&jffs2_compressor_list_lock); kfree(this->compr_buf); spin_lock(&jffs2_compressor_list_lock); this->compr_buf_size=0; this->compr_buf=NULL; } if (!this->compr_buf) { spin_unlock(&jffs2_compressor_list_lock); tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); spin_lock(&jffs2_compressor_list_lock); if (!tmp_buf) { printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); continue; } else { this->compr_buf = tmp_buf; this->compr_buf_size = orig_dlen; } } this->usecount++; spin_unlock(&jffs2_compressor_list_lock); *datalen = orig_slen; *cdatalen = orig_dlen; compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); spin_lock(&jffs2_compressor_list_lock); this->usecount--; if (!compr_ret) { if ((!best_dlen)||(best_dlen>*cdatalen)) { best_dlen = *cdatalen; best_slen = *datalen; best = this; } } } if (best_dlen) { *cdatalen = best_dlen; *datalen = best_slen; output_buf = best->compr_buf; best->compr_buf = NULL; best->compr_buf_size = 0; best->stat_compr_blocks++; best->stat_compr_orig_size += best_slen; best->stat_compr_new_size += best_dlen; ret = best->compr; } spin_unlock(&jffs2_compressor_list_lock); break; default: printk(KERN_ERR "JFFS2: unknow compression mode.\n"); } out: if (ret == JFFS2_COMPR_NONE) { *cpage_out = data_in; *datalen = *cdatalen; none_stat_compr_blocks++; none_stat_compr_size += *datalen; } else { *cpage_out = output_buf; } return ret; } int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) { struct jffs2_compressor *this; int ret; /* Older code had a bug where it would write non-zero 'usercompr' fields. Deal with it. */ if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) comprtype &= 0xff; switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ memcpy(data_out, cdata_in, datalen); none_stat_decompr_blocks++; break; case JFFS2_COMPR_ZERO: memset(data_out, 0, datalen); break; default: spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (comprtype == this->compr) { this->usecount++; spin_unlock(&jffs2_compressor_list_lock); ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); spin_lock(&jffs2_compressor_list_lock); if (ret) { printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); } else { this->stat_decompr_blocks++; } this->usecount--; spin_unlock(&jffs2_compressor_list_lock); return ret; } } printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); spin_unlock(&jffs2_compressor_list_lock); return -EIO; } return 0; } int jffs2_register_compressor(struct jffs2_compressor *comp) { struct jffs2_compressor *this; if (!comp->name) { printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); return -1; } comp->compr_buf_size=0; comp->compr_buf=NULL; comp->usecount=0; comp->stat_compr_orig_size=0; comp->stat_compr_new_size=0; comp->stat_compr_blocks=0; comp->stat_decompr_blocks=0; D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (this->priority < comp->priority) { list_add(&comp->list, this->list.prev); goto out; } } list_add_tail(&comp->list, &jffs2_compressor_list); out: D2(list_for_each_entry(this, &jffs2_compressor_list, list) { printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); }) spin_unlock(&jffs2_compressor_list_lock); return 0; } int jffs2_unregister_compressor(struct jffs2_compressor *comp) { D2(struct jffs2_compressor *this;) D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); spin_lock(&jffs2_compressor_list_lock); if (comp->usecount) { spin_unlock(&jffs2_compressor_list_lock); printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); return -1; } list_del(&comp->list); D2(list_for_each_entry(this, &jffs2_compressor_list, list) { printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); }) spin_unlock(&jffs2_compressor_list_lock); return 0; } #ifdef CONFIG_JFFS2_PROC #define JFFS2_STAT_BUF_SIZE 16000 char *jffs2_list_compressors(void) { struct jffs2_compressor *this; char *buf, *act_buf; act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); list_for_each_entry(this, &jffs2_compressor_list, list) { act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); if ((this->disabled)||(!this->compress)) act_buf += sprintf(act_buf,"disabled"); else act_buf += sprintf(act_buf,"enabled"); act_buf += sprintf(act_buf,"\n"); } return buf; } char *jffs2_stats(void) { struct jffs2_compressor *this; char *buf, *act_buf; act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n"); act_buf += sprintf(act_buf,"%10s ","none"); act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, none_stat_compr_size, none_stat_decompr_blocks); spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { act_buf += sprintf(act_buf,"%10s ",this->name); if ((this->disabled)||(!this->compress)) act_buf += sprintf(act_buf,"- "); else act_buf += sprintf(act_buf,"+ "); act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, this->stat_compr_new_size, this->stat_compr_orig_size, this->stat_decompr_blocks); act_buf += sprintf(act_buf,"\n"); } spin_unlock(&jffs2_compressor_list_lock); return buf; } char *jffs2_get_compression_mode_name(void) { switch (jffs2_compression_mode) { case JFFS2_COMPR_MODE_NONE: return "none"; case JFFS2_COMPR_MODE_PRIORITY: return "priority"; case JFFS2_COMPR_MODE_SIZE: return "size"; } return "unkown"; } int jffs2_set_compression_mode_name(const char *name) { if (!strcmp("none",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; return 0; } if (!strcmp("priority",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; return 0; } if (!strcmp("size",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; return 0; } return 1; } static int jffs2_compressor_Xable(const char *name, int disabled) { struct jffs2_compressor *this; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (!strcmp(this->name, name)) { this->disabled = disabled; spin_unlock(&jffs2_compressor_list_lock); return 0; } } spin_unlock(&jffs2_compressor_list_lock); printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); return 1; } int jffs2_enable_compressor_name(const char *name) { return jffs2_compressor_Xable(name, 0); } int jffs2_disable_compressor_name(const char *name) { return jffs2_compressor_Xable(name, 1); } int jffs2_set_compressor_priority(const char *name, int priority) { struct jffs2_compressor *this,*comp; spin_lock(&jffs2_compressor_list_lock); list_for_each_entry(this, &jffs2_compressor_list, list) { if (!strcmp(this->name, name)) { this->priority = priority; comp = this; goto reinsert; } } spin_unlock(&jffs2_compressor_list_lock); printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); return 1; reinsert: /* list is sorted in the order of priority, so if we change it we have to reinsert it into the good place */ list_del(&comp->list); list_for_each_entry(this, &jffs2_compressor_list, list) { if (this->priority < comp->priority) { list_add(&comp->list, this->list.prev); spin_unlock(&jffs2_compressor_list_lock); return 0; } } list_add_tail(&comp->list, &jffs2_compressor_list); spin_unlock(&jffs2_compressor_list_lock); return 0; } #endif void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) { if (orig != comprbuf) kfree(comprbuf); } int jffs2_compressors_init(void) { /* Registering compressors */ #ifdef CONFIG_JFFS2_ZLIB jffs2_zlib_init(); #endif #ifdef CONFIG_JFFS2_RTIME jffs2_rtime_init(); #endif #ifdef CONFIG_JFFS2_RUBIN jffs2_rubinmips_init(); jffs2_dynrubin_init(); #endif /* Setting default compression mode */ #ifdef CONFIG_JFFS2_CMODE_NONE jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) #else #ifdef CONFIG_JFFS2_CMODE_SIZE jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) #else D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) #endif #endif return 0; } int jffs2_compressors_exit(void) { /* Unregistering compressors */ #ifdef CONFIG_JFFS2_RUBIN jffs2_dynrubin_exit(); jffs2_rubinmips_exit(); #endif #ifdef CONFIG_JFFS2_RTIME jffs2_rtime_exit(); #endif #ifdef CONFIG_JFFS2_ZLIB jffs2_zlib_exit(); #endif return 0; } linux-2.6.17/fs/jffs2/.svn/prop-base/0000755000000000000000000000000010574207642015651 5ustar rootrootlinux-2.6.17/fs/jffs2/os-linux.h0000644000000000000000000001745510564537530015041 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2002-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: os-linux.h,v 1.64 2005/09/30 13:59:13 dedekind Exp $ * */ #ifndef __JFFS2_OS_LINUX_H__ #define __JFFS2_OS_LINUX_H__ /* JFFS2 uses Linux mode bits natively -- no need for conversion */ #define os_to_jffs2_mode(x) (x) #define jffs2_to_os_mode(x) (x) struct kstatfs; struct kvec; #define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) #define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) #define JFFS2_SB_INFO(sb) (sb->s_fs_info) #define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv) #define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size) #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) #define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f))) #define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f))) #define ITIME(sec) ((struct timespec){sec, 0}) #define I_SEC(tv) ((tv).tv_sec) #define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec) #define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec) #define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec) #define sleep_on_spinunlock(wq, s) \ do { \ DECLARE_WAITQUEUE(__wait, current); \ add_wait_queue((wq), &__wait); \ set_current_state(TASK_UNINTERRUPTIBLE); \ spin_unlock(s); \ schedule(); \ remove_wait_queue((wq), &__wait); \ } while(0) static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) { f->highest_version = 0; f->fragtree = RB_ROOT; f->metadata = NULL; f->dents = NULL; f->target = NULL; f->flags = 0; f->usercompr = 0; } #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) #define SECTOR_ADDR(x) ( (((unsigned long)(x) / c->sector_size) * c->sector_size) ) #ifndef CONFIG_JFFS2_FS_WRITEBUFFER #ifdef CONFIG_JFFS2_SUMMARY #define jffs2_can_mark_obsolete(c) (0) #else #define jffs2_can_mark_obsolete(c) (1) #endif #define jffs2_is_writebuffered(c) (0) #define jffs2_cleanmarker_oob(c) (0) #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) #define jffs2_flash_write(c, ofs, len, retlen, buf) jffs2_flash_direct_write(c, ofs, len, retlen, buf) #define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flush_wbuf_pad(c) ({ do{} while(0); (void)(c), 0; }) #define jffs2_flush_wbuf_gc(c, i) ({ do{} while(0); (void)(c), (void) i, 0; }) #define jffs2_write_nand_badblock(c,jeb,bad_offset) (1) #define jffs2_nand_flash_setup(c) (0) #define jffs2_nand_flash_cleanup(c) do {} while(0) #define jffs2_wbuf_dirty(c) (0) #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) #define jffs2_wbuf_timeout NULL #define jffs2_wbuf_process NULL #define jffs2_nor_ecc(c) (0) #define jffs2_dataflash(c) (0) #define jffs2_nor_wbuf_flash(c) (0) #define jffs2_nor_ecc_flash_setup(c) (0) #define jffs2_nor_ecc_flash_cleanup(c) do {} while (0) #define jffs2_dataflash_setup(c) (0) #define jffs2_dataflash_cleanup(c) do {} while (0) #define jffs2_nor_wbuf_flash_setup(c) (0) #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0) #else /* NAND and/or ECC'd NOR support present */ #define jffs2_is_writebuffered(c) (c->wbuf != NULL) #ifdef CONFIG_JFFS2_SUMMARY #define jffs2_can_mark_obsolete(c) (0) #else #define jffs2_can_mark_obsolete(c) \ ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_ECC|MTD_PROGRAM_REGIONS))) || \ c->mtd->type == MTD_RAM) #endif #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len) /* wbuf.c */ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino); int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf); int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); void jffs2_wbuf_timeout(unsigned long data); void jffs2_wbuf_process(void *data); int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_nand_flash_setup(struct jffs2_sb_info *c); void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); #define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC)) int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c); void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c); #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH) int jffs2_dataflash_setup(struct jffs2_sb_info *c); void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); #define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_PROGRAM_REGIONS)) int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); #endif /* WRITEBUFFER */ /* erase.c */ static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) { OFNI_BS_2SFFJ(c)->s_dirt = 1; } /* background.c */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); /* dir.c */ extern const struct file_operations jffs2_dir_operations; extern struct inode_operations jffs2_dir_inode_operations; /* file.c */ extern const struct file_operations jffs2_file_operations; extern struct inode_operations jffs2_file_inode_operations; extern struct address_space_operations jffs2_file_address_operations; int jffs2_fsync(struct file *, struct dentry *, int); int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); /* ioctl.c */ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* symlink.c */ extern struct inode_operations jffs2_symlink_inode_operations; /* fs.c */ int jffs2_setattr (struct dentry *, struct iattr *); void jffs2_read_inode (struct inode *); void jffs2_clear_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode); struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri); int jffs2_statfs (struct super_block *, struct kstatfs *); void jffs2_write_super (struct super_block *); int jffs2_remount_fs (struct super_block *, int *, char *); int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink); unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned long offset, unsigned long *priv); void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv); void jffs2_flash_cleanup(struct jffs2_sb_info *c); /* writev.c */ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); #endif /* __JFFS2_OS_LINUX_H__ */ linux-2.6.17/fs/jffs2/read.c0000644000000000000000000001541310564537530014161 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: read.c,v 1.42 2005/11/07 11:14:41 gleixner Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) { struct jffs2_raw_inode *ri; size_t readlen; uint32_t crc; unsigned char *decomprbuf = NULL; unsigned char *readbuf = NULL; int ret = 0; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri); if (ret) { jffs2_free_raw_inode(ri); printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret); return ret; } if (readlen != sizeof(*ri)) { jffs2_free_raw_inode(ri); printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", ref_offset(fd->raw), sizeof(*ri), readlen); return -EIO; } crc = crc32(0, ri, sizeof(*ri)-8); D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", ref_offset(fd->raw), je32_to_cpu(ri->node_crc), crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), je32_to_cpu(ri->offset), buf)); if (crc != je32_to_cpu(ri->node_crc)) { printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw)); ret = -EIO; goto out_ri; } /* There was a bug where we wrote hole nodes out with csize/dsize swapped. Deal with it */ if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && je32_to_cpu(ri->csize)) { ri->dsize = ri->csize; ri->csize = cpu_to_je32(0); } D1(if(ofs + len > je32_to_cpu(ri->dsize)) { printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, je32_to_cpu(ri->dsize)); ret = -EINVAL; goto out_ri; }); if (ri->compr == JFFS2_COMPR_ZERO) { memset(buf, 0, len); goto out_ri; } /* Cases: Reading whole node and it's uncompressed - read directly to buffer provided, check CRC. Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy */ if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) { readbuf = buf; } else { readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL); if (!readbuf) { ret = -ENOMEM; goto out_ri; } } if (ri->compr != JFFS2_COMPR_NONE) { if (len < je32_to_cpu(ri->dsize)) { decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL); if (!decomprbuf) { ret = -ENOMEM; goto out_readbuf; } } else { decomprbuf = buf; } } else { decomprbuf = readbuf; } D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize), readbuf)); ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri), je32_to_cpu(ri->csize), &readlen, readbuf); if (!ret && readlen != je32_to_cpu(ri->csize)) ret = -EIO; if (ret) goto out_decomprbuf; crc = crc32(0, readbuf, je32_to_cpu(ri->csize)); if (crc != je32_to_cpu(ri->data_crc)) { printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw)); ret = -EIO; goto out_decomprbuf; } D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc)); if (ri->compr != JFFS2_COMPR_NONE) { D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); if (ret) { printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); goto out_decomprbuf; } } if (len < je32_to_cpu(ri->dsize)) { memcpy(buf, decomprbuf+ofs, len); } out_decomprbuf: if(decomprbuf != buf && decomprbuf != readbuf) kfree(decomprbuf); out_readbuf: if(readbuf != buf) kfree(readbuf); out_ri: jffs2_free_raw_inode(ri); return ret; } int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *buf, uint32_t offset, uint32_t len) { uint32_t end = offset + len; struct jffs2_node_frag *frag; int ret; D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n", f->inocache->ino, offset, offset+len)); frag = jffs2_lookup_node_frag(&f->fragtree, offset); /* XXX FIXME: Where a single physical node actually shows up in two frags, we read it twice. Don't do that. */ /* Now we're pointing at the first frag which overlaps our page */ while(offset < end) { D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end)); if (unlikely(!frag || frag->ofs > offset)) { uint32_t holesize = end - offset; if (frag) { D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); holesize = min(holesize, frag->ofs - offset); } D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); memset(buf, 0, holesize); buf += holesize; offset += holesize; continue; } else if (unlikely(!frag->node)) { uint32_t holeend = min(end, frag->ofs + frag->size); D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size)); memset(buf, 0, holeend - offset); buf += holeend - offset; offset = holeend; frag = frag_next(frag); continue; } else { uint32_t readlen; uint32_t fragofs; /* offset within the frag to start reading */ fragofs = offset - frag->ofs; readlen = min(frag->size - fragofs, end - offset); D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n", frag->ofs+fragofs, frag->ofs+fragofs+readlen, ref_offset(frag->node->raw), ref_flags(frag->node->raw))); ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); D2(printk(KERN_DEBUG "node read done\n")); if (ret) { D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret)); memset(buf, 0, readlen); return ret; } buf += readlen; offset += readlen; frag = frag_next(frag); D2(printk(KERN_DEBUG "node read was OK. Looping\n")); } } return 0; } linux-2.6.17/fs/jffs2/build.c0000644000000000000000000002411210564537530014341 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: build.c,v 1.85 2005/11/07 11:14:38 gleixner Exp $ * */ #include #include #include #include #include #include "nodelist.h" static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); static inline struct jffs2_inode_cache * first_inode_chain(int *i, struct jffs2_sb_info *c) { for (; *i < INOCACHE_HASHSIZE; (*i)++) { if (c->inocache_list[*i]) return c->inocache_list[*i]; } return NULL; } static inline struct jffs2_inode_cache * next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c) { /* More in this chain? */ if (ic->next) return ic->next; (*i)++; return first_inode_chain(i, c); } #define for_each_inode(i, c, ic) \ for (i = 0, ic = first_inode_chain(&i, (c)); \ ic; \ ic = next_inode(&i, ic, (c))) static void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { struct jffs2_full_dirent *fd; dbg_fsbuild("building directory inode #%u\n", ic->ino); /* For each child, increase nlink */ for(fd = ic->scan_dents; fd; fd = fd->next) { struct jffs2_inode_cache *child_ic; if (!fd->ino) continue; /* we can get high latency here with huge directories */ child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", fd->name, fd->ino, ic->ino); jffs2_mark_node_obsolete(c, fd->raw); continue; } if (child_ic->nlink++ && fd->type == DT_DIR) { JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); /* TODO: What do we do about it? */ } dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino); /* Can't free scan_dents so far. We might need them in pass 2 */ } } /* Scan plan: - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go - Scan directory tree from top down, setting nlink in inocaches - Scan inocaches for inodes with nlink==0 */ static int jffs2_build_filesystem(struct jffs2_sb_info *c) { int ret; int i; struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; struct jffs2_full_dirent *dead_fds = NULL; dbg_fsbuild("build FS data structures\n"); /* First, scan the medium and build all the inode caches with lists of physical nodes */ c->flags |= JFFS2_SB_FLAG_SCANNING; ret = jffs2_scan_medium(c); c->flags &= ~JFFS2_SB_FLAG_SCANNING; if (ret) goto exit; dbg_fsbuild("scanned flash completely\n"); jffs2_dbg_dump_block_lists_nolock(c); dbg_fsbuild("pass 1 starting\n"); c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { if (ic->scan_dents) { jffs2_build_inode_pass1(c, ic); cond_resched(); } } dbg_fsbuild("pass 1 complete\n"); /* Next, scan for inodes with nlink == 0 and remove them. If they were directories, then decrement the nlink of their children too, and repeat the scan. As that's going to be a fairly uncommon occurrence, it's not so evil to do it this way. Recursion bad. */ dbg_fsbuild("pass 2 starting\n"); for_each_inode(i, c, ic) { if (ic->nlink) continue; jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); cond_resched(); } dbg_fsbuild("pass 2a starting\n"); while (dead_fds) { fd = dead_fds; dead_fds = fd->next; ic = jffs2_get_ino_cache(c, fd->ino); if (ic) jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); jffs2_free_full_dirent(fd); } dbg_fsbuild("pass 2a complete\n"); dbg_fsbuild("freeing temporary data structures\n"); /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { while(ic->scan_dents) { fd = ic->scan_dents; ic->scan_dents = fd->next; jffs2_free_full_dirent(fd); } ic->scan_dents = NULL; cond_resched(); } c->flags &= ~JFFS2_SB_FLAG_BUILDING; dbg_fsbuild("FS build complete\n"); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); ret = 0; exit: if (ret) { for_each_inode(i, c, ic) { while(ic->scan_dents) { fd = ic->scan_dents; ic->scan_dents = fd->next; jffs2_free_full_dirent(fd); } } } return ret; } static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; dbg_fsbuild("removing ino #%u with nlink == zero.\n", ic->ino); raw = ic->nodes; while (raw != (void *)ic) { struct jffs2_raw_node_ref *next = raw->next_in_ino; dbg_fsbuild("obsoleting node at 0x%08x\n", ref_offset(raw)); jffs2_mark_node_obsolete(c, raw); raw = next; } if (ic->scan_dents) { int whinged = 0; dbg_fsbuild("inode #%u was a directory which may have children...\n", ic->ino); while(ic->scan_dents) { struct jffs2_inode_cache *child_ic; fd = ic->scan_dents; ic->scan_dents = fd->next; if (!fd->ino) { /* It's a deletion dirent. Ignore it */ dbg_fsbuild("child \"%s\" is a deletion dirent, skipping...\n", fd->name); jffs2_free_full_dirent(fd); continue; } if (!whinged) whinged = 1; dbg_fsbuild("removing child \"%s\", ino #%u\n", fd->name, fd->ino); child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { dbg_fsbuild("cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); jffs2_free_full_dirent(fd); continue; } /* Reduce nlink of the child. If it's now zero, stick it on the dead_fds list to be cleaned up later. Else just free the fd */ child_ic->nlink--; if (!child_ic->nlink) { dbg_fsbuild("inode #%u (\"%s\") has now got zero nlink, adding to dead_fds list.\n", fd->ino, fd->name); fd->next = *dead_fds; *dead_fds = fd; } else { dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", fd->ino, fd->name, child_ic->nlink); jffs2_free_full_dirent(fd); } } } /* We don't delete the inocache from the hash list and free it yet. The erase code will do that, when all the nodes are completely gone. */ } static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) { uint32_t size; /* Deletion should almost _always_ be allowed. We're fairly buggered once we stop allowing people to delete stuff because there's not enough free space... */ c->resv_blocks_deletion = 2; /* Be conservative about how much space we need before we allow writes. On top of that which is required for deletia, require an extra 2% of the medium to be available, for overhead caused by nodes being split across blocks, etc. */ size = c->flash_size / 50; /* 2% of flash size */ size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */ size += c->sector_size - 1; /* ... and round up */ c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size); /* When do we let the GC thread run in the background */ c->resv_blocks_gctrigger = c->resv_blocks_write + 1; /* When do we allow garbage collection to merge nodes to make long-term progress at the expense of short-term space exhaustion? */ c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1; /* When do we allow garbage collection to eat from bad blocks rather than actually making progress? */ c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; /* If there's less than this amount of dirty space, don't bother trying to GC to make more space. It'll be a fruitless task */ c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); dbg_fsbuild("JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks); dbg_fsbuild("Blocks required to allow deletion: %d (%d KiB)\n", c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024); dbg_fsbuild("Blocks required to allow writes: %d (%d KiB)\n", c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024); dbg_fsbuild("Blocks required to quiesce GC thread: %d (%d KiB)\n", c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024); dbg_fsbuild("Blocks required to allow GC merges: %d (%d KiB)\n", c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024); dbg_fsbuild("Blocks required to GC bad blocks: %d (%d KiB)\n", c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024); dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n", c->nospc_dirty_size); } int jffs2_do_mount_fs(struct jffs2_sb_info *c) { int ret; int i; int size; c->free_size = c->flash_size; c->nr_blocks = c->flash_size / c->sector_size; size = sizeof(struct jffs2_eraseblock) * c->nr_blocks; #ifndef __ECOS if (jffs2_blocks_use_vmalloc(c)) c->blocks = vmalloc(size); else #endif c->blocks = kmalloc(size, GFP_KERNEL); if (!c->blocks) return -ENOMEM; memset(c->blocks, 0, size); for (i=0; inr_blocks; i++) { INIT_LIST_HEAD(&c->blocks[i].list); c->blocks[i].offset = i * c->sector_size; c->blocks[i].free_size = c->sector_size; } INIT_LIST_HEAD(&c->clean_list); INIT_LIST_HEAD(&c->very_dirty_list); INIT_LIST_HEAD(&c->dirty_list); INIT_LIST_HEAD(&c->erasable_list); INIT_LIST_HEAD(&c->erasing_list); INIT_LIST_HEAD(&c->erase_pending_list); INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); INIT_LIST_HEAD(&c->erase_complete_list); INIT_LIST_HEAD(&c->free_list); INIT_LIST_HEAD(&c->bad_list); INIT_LIST_HEAD(&c->bad_used_list); c->highest_ino = 1; c->summary = NULL; ret = jffs2_sum_init(c); if (ret) return ret; if (jffs2_build_filesystem(c)) { dbg_fsbuild("build_fs failed\n"); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); #ifndef __ECOS if (jffs2_blocks_use_vmalloc(c)) vfree(c->blocks); else #endif kfree(c->blocks); return -EIO; } jffs2_calc_trigger_levels(c); return 0; } linux-2.6.17/fs/jffs2/LICENCE0000644000000000000000000000304710564537530014067 0ustar rootrootThe files in this directory and elsewhere which refer to this LICENCE file are part of JFFS2, the Journalling Flash File System v2. Copyright (C) 2001, 2002 Red Hat, Inc. JFFS2 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. JFFS2 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 JFFS2; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. As a special exception, if other files instantiate templates or use macros or inline functions from these files, or you compile these files and link them with other works to produce a work based on these files, these files do not by themselves cause the resulting work to be covered by the GNU General Public License. However the source code for these files must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. For information on obtaining alternative licences for JFFS2, see http://sources.redhat.com/jffs2/jffs2-licence.html $Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $ linux-2.6.17/fs/jffs2/compr.h0000644000000000000000000000702110564537530014367 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * * $Id: compr.h,v 1.9 2005/11/07 11:14:38 gleixner Exp $ * */ #ifndef __JFFS2_COMPR_H__ #define __JFFS2_COMPR_H__ #include #include #include #include #include #include #include #include #include #include #include #include "nodelist.h" #define JFFS2_RUBINMIPS_PRIORITY 10 #define JFFS2_DYNRUBIN_PRIORITY 20 #define JFFS2_LZARI_PRIORITY 30 #define JFFS2_LZO_PRIORITY 40 #define JFFS2_RTIME_PRIORITY 50 #define JFFS2_ZLIB_PRIORITY 60 #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ #define JFFS2_COMPR_MODE_NONE 0 #define JFFS2_COMPR_MODE_PRIORITY 1 #define JFFS2_COMPR_MODE_SIZE 2 struct jffs2_compressor { struct list_head list; int priority; /* used by prirority comr. mode */ char *name; char compr; /* JFFS2_COMPR_XXX */ int (*compress)(unsigned char *data_in, unsigned char *cpage_out, uint32_t *srclen, uint32_t *destlen, void *model); int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen, void *model); int usecount; int disabled; /* if seted the compressor won't compress */ unsigned char *compr_buf; /* used by size compr. mode */ uint32_t compr_buf_size; /* used by size compr. mode */ uint32_t stat_compr_orig_size; uint32_t stat_compr_new_size; uint32_t stat_compr_blocks; uint32_t stat_decompr_blocks; }; int jffs2_register_compressor(struct jffs2_compressor *comp); int jffs2_unregister_compressor(struct jffs2_compressor *comp); int jffs2_compressors_init(void); int jffs2_compressors_exit(void); uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen); int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); #ifdef CONFIG_JFFS2_PROC int jffs2_enable_compressor_name(const char *name); int jffs2_disable_compressor_name(const char *name); int jffs2_set_compression_mode_name(const char *mode_name); char *jffs2_get_compression_mode_name(void); int jffs2_set_compressor_priority(const char *mode_name, int priority); char *jffs2_list_compressors(void); char *jffs2_stats(void); #endif /* Compressor modules */ /* These functions will be called by jffs2_compressors_init/exit */ #ifdef CONFIG_JFFS2_RUBIN int jffs2_rubinmips_init(void); void jffs2_rubinmips_exit(void); int jffs2_dynrubin_init(void); void jffs2_dynrubin_exit(void); #endif #ifdef CONFIG_JFFS2_RTIME int jffs2_rtime_init(void); void jffs2_rtime_exit(void); #endif #ifdef CONFIG_JFFS2_ZLIB int jffs2_zlib_init(void); void jffs2_zlib_exit(void); #endif #endif /* __JFFS2_COMPR_H__ */ linux-2.6.17/fs/jffs2/summary.c0000644000000000000000000004535010564537530014746 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: summary.c,v 1.4 2005/09/26 11:37:21 havasi Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" #include "debug.h" int jffs2_sum_init(struct jffs2_sb_info *c) { c->summary = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!c->summary) { JFFS2_WARNING("Can't allocate memory for summary information!\n"); return -ENOMEM; } memset(c->summary, 0, sizeof(struct jffs2_summary)); c->summary->sum_buf = vmalloc(c->sector_size); if (!c->summary->sum_buf) { JFFS2_WARNING("Can't allocate buffer for writing out summary information!\n"); kfree(c->summary); return -ENOMEM; } dbg_summary("returned succesfully\n"); return 0; } void jffs2_sum_exit(struct jffs2_sb_info *c) { dbg_summary("called\n"); jffs2_sum_disable_collecting(c->summary); vfree(c->summary->sum_buf); c->summary->sum_buf = NULL; kfree(c->summary); c->summary = NULL; } static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item) { if (!s->sum_list_head) s->sum_list_head = (union jffs2_sum_mem *) item; if (s->sum_list_tail) s->sum_list_tail->u.next = (union jffs2_sum_mem *) item; s->sum_list_tail = (union jffs2_sum_mem *) item; switch (je16_to_cpu(item->u.nodetype)) { case JFFS2_NODETYPE_INODE: s->sum_size += JFFS2_SUMMARY_INODE_SIZE; s->sum_num++; dbg_summary("inode (%u) added to summary\n", je32_to_cpu(item->i.inode)); break; case JFFS2_NODETYPE_DIRENT: s->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize); s->sum_num++; dbg_summary("dirent (%u) added to summary\n", je32_to_cpu(item->d.ino)); break; default: JFFS2_WARNING("UNKNOWN node type %u\n", je16_to_cpu(item->u.nodetype)); return 1; } return 0; } /* The following 3 functions are called from scan.c to collect summary info for not closed jeb */ int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size) { dbg_summary("called with %u\n", size); s->sum_padded += size; return 0; } int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs) { struct jffs2_sum_inode_mem *temp = kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL); if (!temp) return -ENOMEM; temp->nodetype = ri->nodetype; temp->inode = ri->ino; temp->version = ri->version; temp->offset = cpu_to_je32(ofs); /* relative offset from the begining of the jeb */ temp->totlen = ri->totlen; temp->next = NULL; return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); } int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs) { struct jffs2_sum_dirent_mem *temp = kmalloc(sizeof(struct jffs2_sum_dirent_mem) + rd->nsize, GFP_KERNEL); if (!temp) return -ENOMEM; temp->nodetype = rd->nodetype; temp->totlen = rd->totlen; temp->offset = cpu_to_je32(ofs); /* relative from the begining of the jeb */ temp->pino = rd->pino; temp->version = rd->version; temp->ino = rd->ino; temp->nsize = rd->nsize; temp->type = rd->type; temp->next = NULL; memcpy(temp->name, rd->name, rd->nsize); return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); } /* Cleanup every collected summary information */ static void jffs2_sum_clean_collected(struct jffs2_summary *s) { union jffs2_sum_mem *temp; if (!s->sum_list_head) { dbg_summary("already empty\n"); } while (s->sum_list_head) { temp = s->sum_list_head; s->sum_list_head = s->sum_list_head->u.next; kfree(temp); } s->sum_list_tail = NULL; s->sum_padded = 0; s->sum_num = 0; } void jffs2_sum_reset_collected(struct jffs2_summary *s) { dbg_summary("called\n"); jffs2_sum_clean_collected(s); s->sum_size = 0; } void jffs2_sum_disable_collecting(struct jffs2_summary *s) { dbg_summary("called\n"); jffs2_sum_clean_collected(s); s->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; } int jffs2_sum_is_disabled(struct jffs2_summary *s) { return (s->sum_size == JFFS2_SUMMARY_NOSUM_SIZE); } /* Move the collected summary information into sb (called from scan.c) */ void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s) { dbg_summary("oldsize=0x%x oldnum=%u => newsize=0x%x newnum=%u\n", c->summary->sum_size, c->summary->sum_num, s->sum_size, s->sum_num); c->summary->sum_size = s->sum_size; c->summary->sum_num = s->sum_num; c->summary->sum_padded = s->sum_padded; c->summary->sum_list_head = s->sum_list_head; c->summary->sum_list_tail = s->sum_list_tail; s->sum_list_head = s->sum_list_tail = NULL; } /* Called from wbuf.c to collect writed node info */ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, uint32_t ofs) { union jffs2_node_union *node; struct jffs2_eraseblock *jeb; node = invecs[0].iov_base; jeb = &c->blocks[ofs / c->sector_size]; ofs -= jeb->offset; switch (je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_mem *temp = kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL); if (!temp) goto no_mem; temp->nodetype = node->i.nodetype; temp->inode = node->i.ino; temp->version = node->i.version; temp->offset = cpu_to_je32(ofs); temp->totlen = node->i.totlen; temp->next = NULL; return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_mem *temp = kmalloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize, GFP_KERNEL); if (!temp) goto no_mem; temp->nodetype = node->d.nodetype; temp->totlen = node->d.totlen; temp->offset = cpu_to_je32(ofs); temp->pino = node->d.pino; temp->version = node->d.version; temp->ino = node->d.ino; temp->nsize = node->d.nsize; temp->type = node->d.type; temp->next = NULL; switch (count) { case 1: memcpy(temp->name,node->d.name,node->d.nsize); break; case 2: memcpy(temp->name,invecs[1].iov_base,node->d.nsize); break; default: BUG(); /* impossible count value */ break; } return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); } case JFFS2_NODETYPE_PADDING: dbg_summary("node PADDING\n"); c->summary->sum_padded += je32_to_cpu(node->u.totlen); break; case JFFS2_NODETYPE_CLEANMARKER: dbg_summary("node CLEANMARKER\n"); break; case JFFS2_NODETYPE_SUMMARY: dbg_summary("node SUMMARY\n"); break; default: /* If you implement a new node type you should also implement summary support for it or disable summary. */ BUG(); break; } return 0; no_mem: JFFS2_WARNING("MEMORY ALLOCATION ERROR!"); return -ENOMEM; } /* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_summary *summary, uint32_t *pseudo_random) { struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; void *sp; int i, ino; sp = summary->sum; for (i=0; isum_num); i++) { dbg_summary("processing summary index %d\n", i); switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *spi; spi = sp; ino = je32_to_cpu(spi->inode); dbg_summary("Inode at 0x%08x\n", jeb->offset + je32_to_cpu(spi->offset)); raw = jffs2_alloc_raw_node_ref(); if (!raw) { JFFS2_NOTICE("allocation of node reference failed\n"); kfree(summary); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) { JFFS2_NOTICE("scan_make_ino_cache failed\n"); jffs2_free_raw_node_ref(raw); kfree(summary); return -ENOMEM; } raw->flash_offset = (jeb->offset + je32_to_cpu(spi->offset)) | REF_UNCHECKED; raw->__totlen = PAD(je32_to_cpu(spi->totlen)); raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; *pseudo_random += je32_to_cpu(spi->version); UNCHECKED_SPACE(PAD(je32_to_cpu(spi->totlen))); sp += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *spd; spd = sp; dbg_summary("Dirent at 0x%08x\n", jeb->offset + je32_to_cpu(spd->offset)); fd = jffs2_alloc_full_dirent(spd->nsize+1); if (!fd) { kfree(summary); return -ENOMEM; } memcpy(&fd->name, spd->name, spd->nsize); fd->name[spd->nsize] = 0; raw = jffs2_alloc_raw_node_ref(); if (!raw) { jffs2_free_full_dirent(fd); JFFS2_NOTICE("allocation of node reference failed\n"); kfree(summary); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino)); if (!ic) { jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); kfree(summary); return -ENOMEM; } raw->__totlen = PAD(je32_to_cpu(spd->totlen)); raw->flash_offset = (jeb->offset + je32_to_cpu(spd->offset)) | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; fd->raw = raw; fd->next = NULL; fd->version = je32_to_cpu(spd->version); fd->ino = je32_to_cpu(spd->ino); fd->nhash = full_name_hash(fd->name, spd->nsize); fd->type = spd->type; USED_SPACE(PAD(je32_to_cpu(spd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); *pseudo_random += je32_to_cpu(spd->version); sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); break; } default : { JFFS2_WARNING("Unsupported node type found in summary! Exiting..."); kfree(summary); return -EIO; } } } kfree(summary); return 0; } /* Process the summary node - called from jffs2_scan_eraseblock() */ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t ofs, uint32_t *pseudo_random) { struct jffs2_unknown_node crcnode; struct jffs2_raw_node_ref *cache_ref; struct jffs2_raw_summary *summary; int ret, sumsize; uint32_t crc; sumsize = c->sector_size - ofs; ofs += jeb->offset; dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n", jeb->offset, ofs, sumsize); summary = kmalloc(sumsize, GFP_KERNEL); if (!summary) { return -ENOMEM; } ret = jffs2_fill_scan_buf(c, (unsigned char *)summary, ofs, sumsize); if (ret) { kfree(summary); return ret; } /* OK, now check for node validity and CRC */ crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); crcnode.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); crcnode.totlen = summary->totlen; crc = crc32(0, &crcnode, sizeof(crcnode)-4); if (je32_to_cpu(summary->hdr_crc) != crc) { dbg_summary("Summary node header is corrupt (bad CRC or " "no summary at all)\n"); goto crc_err; } if (je32_to_cpu(summary->totlen) != sumsize) { dbg_summary("Summary node is corrupt (wrong erasesize?)\n"); goto crc_err; } crc = crc32(0, summary, sizeof(struct jffs2_raw_summary)-8); if (je32_to_cpu(summary->node_crc) != crc) { dbg_summary("Summary node is corrupt (bad CRC)\n"); goto crc_err; } crc = crc32(0, summary->sum, sumsize - sizeof(struct jffs2_raw_summary)); if (je32_to_cpu(summary->sum_crc) != crc) { dbg_summary("Summary node data is corrupt (bad CRC)\n"); goto crc_err; } if ( je32_to_cpu(summary->cln_mkr) ) { dbg_summary("Summary : CLEANMARKER node \n"); if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) { dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n", je32_to_cpu(summary->cln_mkr), c->cleanmarker_size); UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr))); } else if (jeb->first_node) { dbg_summary("CLEANMARKER node not first node in block " "(0x%08x)\n", jeb->offset); UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr))); } else { struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { JFFS2_NOTICE("Failed to allocate node ref for clean marker\n"); kfree(summary); return -ENOMEM; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset | REF_NORMAL; marker_ref->__totlen = je32_to_cpu(summary->cln_mkr); jeb->first_node = jeb->last_node = marker_ref; USED_SPACE( PAD(je32_to_cpu(summary->cln_mkr)) ); } } if (je32_to_cpu(summary->padded)) { DIRTY_SPACE(je32_to_cpu(summary->padded)); } ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random); if (ret) return ret; /* for PARANOIA_CHECK */ cache_ref = jffs2_alloc_raw_node_ref(); if (!cache_ref) { JFFS2_NOTICE("Failed to allocate node ref for cache\n"); return -ENOMEM; } cache_ref->next_in_ino = NULL; cache_ref->next_phys = NULL; cache_ref->flash_offset = ofs | REF_NORMAL; cache_ref->__totlen = sumsize; if (!jeb->first_node) jeb->first_node = cache_ref; if (jeb->last_node) jeb->last_node->next_phys = cache_ref; jeb->last_node = cache_ref; USED_SPACE(sumsize); jeb->wasted_size += jeb->free_size; c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->free_size = 0; return jffs2_scan_classify_jeb(c, jeb); crc_err: JFFS2_WARNING("Summary node crc error, skipping summary information.\n"); return 0; } /* Write summary data to flash - helper function for jffs2_sum_write_sumnode() */ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t infosize, uint32_t datasize, int padsize) { struct jffs2_raw_summary isum; union jffs2_sum_mem *temp; struct jffs2_sum_marker *sm; struct kvec vecs[2]; void *wpage; int ret; size_t retlen; memset(c->summary->sum_buf, 0xff, datasize); memset(&isum, 0, sizeof(isum)); isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); isum.totlen = cpu_to_je32(infosize); isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); isum.padded = cpu_to_je32(c->summary->sum_padded); isum.cln_mkr = cpu_to_je32(c->cleanmarker_size); isum.sum_num = cpu_to_je32(c->summary->sum_num); wpage = c->summary->sum_buf; while (c->summary->sum_num) { switch (je16_to_cpu(c->summary->sum_list_head->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *sino_ptr = wpage; sino_ptr->nodetype = c->summary->sum_list_head->i.nodetype; sino_ptr->inode = c->summary->sum_list_head->i.inode; sino_ptr->version = c->summary->sum_list_head->i.version; sino_ptr->offset = c->summary->sum_list_head->i.offset; sino_ptr->totlen = c->summary->sum_list_head->i.totlen; wpage += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; sdrnt_ptr->nodetype = c->summary->sum_list_head->d.nodetype; sdrnt_ptr->totlen = c->summary->sum_list_head->d.totlen; sdrnt_ptr->offset = c->summary->sum_list_head->d.offset; sdrnt_ptr->pino = c->summary->sum_list_head->d.pino; sdrnt_ptr->version = c->summary->sum_list_head->d.version; sdrnt_ptr->ino = c->summary->sum_list_head->d.ino; sdrnt_ptr->nsize = c->summary->sum_list_head->d.nsize; sdrnt_ptr->type = c->summary->sum_list_head->d.type; memcpy(sdrnt_ptr->name, c->summary->sum_list_head->d.name, c->summary->sum_list_head->d.nsize); wpage += JFFS2_SUMMARY_DIRENT_SIZE(c->summary->sum_list_head->d.nsize); break; } default : { BUG(); /* unknown node in summary information */ } } temp = c->summary->sum_list_head; c->summary->sum_list_head = c->summary->sum_list_head->u.next; kfree(temp); c->summary->sum_num--; } jffs2_sum_reset_collected(c->summary); wpage += padsize; sm = wpage; sm->offset = cpu_to_je32(c->sector_size - jeb->free_size); sm->magic = cpu_to_je32(JFFS2_SUM_MAGIC); isum.sum_crc = cpu_to_je32(crc32(0, c->summary->sum_buf, datasize)); isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8)); vecs[0].iov_base = &isum; vecs[0].iov_len = sizeof(isum); vecs[1].iov_base = c->summary->sum_buf; vecs[1].iov_len = datasize; dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n", jeb->offset + c->sector_size - jeb->free_size); spin_unlock(&c->erase_completion_lock); ret = jffs2_flash_writev(c, vecs, 2, jeb->offset + c->sector_size - jeb->free_size, &retlen, 0); spin_lock(&c->erase_completion_lock); if (ret || (retlen != infosize)) { JFFS2_WARNING("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", infosize, jeb->offset + c->sector_size - jeb->free_size, ret, retlen); c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; WASTED_SPACE(infosize); return 1; } return 0; } /* Write out summary information - called from jffs2_do_reserve_space */ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c) { struct jffs2_raw_node_ref *summary_ref; int datasize, infosize, padsize, ret; struct jffs2_eraseblock *jeb; dbg_summary("called\n"); jeb = c->nextblock; if (!c->summary->sum_num || !c->summary->sum_list_head) { JFFS2_WARNING("Empty summary info!!!\n"); BUG(); } datasize = c->summary->sum_size + sizeof(struct jffs2_sum_marker); infosize = sizeof(struct jffs2_raw_summary) + datasize; padsize = jeb->free_size - infosize; infosize += padsize; datasize += padsize; /* Is there enough space for summary? */ if (padsize < 0) { /* don't try to write out summary for this jeb */ jffs2_sum_disable_collecting(c->summary); JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize); return 0; } ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize); if (ret) return 0; /* can't write out summary, block is marked as NOSUM_SIZE */ /* for ACCT_PARANOIA_CHECK */ spin_unlock(&c->erase_completion_lock); summary_ref = jffs2_alloc_raw_node_ref(); spin_lock(&c->erase_completion_lock); if (!summary_ref) { JFFS2_NOTICE("Failed to allocate node ref for summary\n"); return -ENOMEM; } summary_ref->next_in_ino = NULL; summary_ref->next_phys = NULL; summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL; summary_ref->__totlen = infosize; if (!jeb->first_node) jeb->first_node = summary_ref; if (jeb->last_node) jeb->last_node->next_phys = summary_ref; jeb->last_node = summary_ref; USED_SPACE(infosize); return 0; } linux-2.6.17/fs/jffs2/erase.c0000644000000000000000000003240210564537530014342 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: erase.c,v 1.85 2005/09/20 14:53:15 dedekind Exp $ * */ #include #include #include #include #include #include #include #include "nodelist.h" struct erase_priv_struct { struct jffs2_eraseblock *jeb; struct jffs2_sb_info *c; }; #ifndef __ECOS static void jffs2_erase_callback(struct erase_info *); #endif static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; uint32_t bad_offset; #ifdef __ECOS ret = jffs2_flash_erase(c, jeb); if (!ret) { jffs2_erase_succeeded(c, jeb); return; } bad_offset = jeb->offset; #else /* Linux */ struct erase_info *instr; D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#08x (range %#08x-%#08x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size)); instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); return; } memset(instr, 0, sizeof(*instr)); instr->mtd = c->mtd; instr->addr = jeb->offset; instr->len = c->sector_size; instr->callback = jffs2_erase_callback; instr->priv = (unsigned long)(&instr[1]); instr->fail_addr = 0xffffffff; ((struct erase_priv_struct *)instr->priv)->jeb = jeb; ((struct erase_priv_struct *)instr->priv)->c = c; ret = c->mtd->erase(c->mtd, instr); if (!ret) return; bad_offset = instr->fail_addr; kfree(instr); #endif /* __ECOS */ if (ret == -ENOMEM || ret == -EAGAIN) { /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); return; } if (ret == -EROFS) printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset); else printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); jffs2_erase_failed(c, jeb, bad_offset); } void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; down(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); while (!list_empty(&c->erase_complete_list) || !list_empty(&c->erase_pending_list)) { if (!list_empty(&c->erase_complete_list)) { jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); list_del(&jeb->list); spin_unlock(&c->erase_completion_lock); jffs2_mark_erased_block(c, jeb); if (!--count) { D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); goto done; } } else if (!list_empty(&c->erase_pending_list)) { jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); list_del(&jeb->list); c->erasing_size += c->sector_size; c->wasted_size -= jeb->wasted_size; c->free_size -= jeb->free_size; c->used_size -= jeb->used_size; c->dirty_size -= jeb->dirty_size; jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; jffs2_free_all_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); jffs2_erase_block(c, jeb); } else { BUG(); } /* Be nice */ cond_resched(); spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); up(&c->erase_free_sem); } static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); } static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { /* For NAND, if the failure did not occur at the device level for a specific physical page, don't bother updating the bad block table. */ if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) { /* We had a device-level failure to erase. Let's see if we've failed too many times. */ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { /* We'd like to give this block another try. */ spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); return; } } spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; list_del(&jeb->list); list_add(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); } #ifndef __ECOS static void jffs2_erase_callback(struct erase_info *instr) { struct erase_priv_struct *priv = (void *)instr->priv; if(instr->state != MTD_ERASE_DONE) { printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); } else { jffs2_erase_succeeded(priv->c, priv->jeb); } kfree(instr); } #endif /* !__ECOS */ /* Hmmm. Maybe we should accept the extra space it takes and make this a standard doubly-linked list? */ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb) { struct jffs2_inode_cache *ic = NULL; struct jffs2_raw_node_ref **prev; prev = &ref->next_in_ino; /* Walk the inode's list once, removing any nodes from this eraseblock */ while (1) { if (!(*prev)->next_in_ino) { /* We're looking at the jffs2_inode_cache, which is at the end of the linked list. Stash it and continue from the beginning of the list */ ic = (struct jffs2_inode_cache *)(*prev); prev = &ic->nodes; continue; } if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) { /* It's in the block we're erasing */ struct jffs2_raw_node_ref *this; this = *prev; *prev = this->next_in_ino; this->next_in_ino = NULL; if (this == ref) break; continue; } /* Not to be deleted. Skip */ prev = &((*prev)->next_in_ino); } /* PARANOIA */ if (!ic) { printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n"); return; } D1(printk(KERN_DEBUG "Removed nodes in range 0x%08x-0x%08x from ino #%u\n", jeb->offset, jeb->offset + c->sector_size, ic->ino)); D2({ int i=0; struct jffs2_raw_node_ref *this; printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG); this = ic->nodes; while(this) { printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this)); if (++i == 5) { printk("\n" KERN_DEBUG); i=0; } this = this->next_in_ino; } printk("\n"); }); if (ic->nodes == (void *)ic && ic->nlink == 0) jffs2_del_ino_cache(c, ic); } static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *ref; D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset)); while(jeb->first_node) { ref = jeb->first_node; jeb->first_node = ref->next_phys; /* Remove from the inode-list */ if (ref->next_in_ino) jffs2_remove_node_refs_from_ino_list(c, ref, jeb); /* else it was a non-inode node or already removed, so don't bother */ jffs2_free_raw_node_ref(ref); } jeb->last_node = NULL; } static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset) { void *ebuf; uint32_t ofs; size_t retlen; int ret = -EIO; ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!ebuf) { printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); return -EAGAIN; } D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) { uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); int i; *bad_offset = ofs; ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); goto fail; } if (retlen != readlen) { printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); goto fail; } for (i=0; ioffset)); bad_offset = jeb->offset; /* Cleanmarker in oob area or no cleanmarker at all ? */ if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) { if (jffs2_cleanmarker_oob(c)) { if (jffs2_write_nand_cleanmarker(c, jeb)) goto filebad; } jeb->first_node = jeb->last_node = NULL; jeb->free_size = c->sector_size; jeb->used_size = 0; jeb->dirty_size = 0; jeb->wasted_size = 0; } else { struct kvec vecs[1]; struct jffs2_unknown_node marker = { .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), .totlen = cpu_to_je32(c->cleanmarker_size) }; marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n"); goto refile; } marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); vecs[0].iov_base = (unsigned char *) ▮ vecs[0].iov_len = sizeof(marker); ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); if (ret || retlen != sizeof(marker)) { if (ret) printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); else printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", jeb->offset, sizeof(marker), retlen); jffs2_free_raw_node_ref(marker_ref); goto filebad; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset | REF_NORMAL; marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; jeb->free_size = c->sector_size - c->cleanmarker_size; jeb->used_size = c->cleanmarker_size; jeb->dirty_size = 0; jeb->wasted_size = 0; } spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->free_size += jeb->free_size; c->used_size += jeb->used_size; jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); list_add_tail(&jeb->list, &c->free_list); c->nr_erasing_blocks--; c->nr_free_blocks++; spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); return; filebad: spin_lock(&c->erase_completion_lock); /* Stick it on a list (any list) so erase_failed can take it right off again. Silly, but shouldn't happen often. */ list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); jffs2_erase_failed(c, jeb, bad_offset); return; refile: /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); spin_lock(&c->erase_completion_lock); list_add(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); return; } linux-2.6.17/fs/jffs2/histo.h0000644000000000000000000000025110564537530014373 0ustar rootroot/* This file provides the bit-probabilities for the input file */ #define BIT_DIVIDER 629 static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */ linux-2.6.17/fs/jffs2/ioctl.c0000644000000000000000000000105310564537530014353 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: ioctl.c,v 1.10 2005/11/07 11:14:40 gleixner Exp $ * */ #include int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which will include compression support etc. */ return -ENOTTY; } linux-2.6.17/fs/jffs2/scan.c0000644000000000000000000007244410564537530014201 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: scan.c,v 1.125 2005/09/30 13:59:13 dedekind Exp $ * */ #include #include #include #include #include #include #include #include "nodelist.h" #include "summary.h" #include "debug.h" #define DEFAULT_EMPTY_SCAN_SIZE 1024 #define noisy_printk(noise, args...) do { \ if (*(noise)) { \ printk(KERN_NOTICE args); \ (*(noise))--; \ if (!(*(noise))) { \ printk(KERN_NOTICE "Further such events for this erase block will not be printed\n"); \ } \ } \ } while(0) static uint32_t pseudo_random; static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s); /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. * Returning an error will abort the mount - bad checksums etc. should just mark the space * as dirty. */ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s); static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s); static inline int min_free(struct jffs2_sb_info *c) { uint32_t min = 2 * sizeof(struct jffs2_raw_inode); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) return c->wbuf_pagesize; #endif return min; } static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) { if (sector_size < DEFAULT_EMPTY_SCAN_SIZE) return sector_size; else return DEFAULT_EMPTY_SCAN_SIZE; } int jffs2_scan_medium(struct jffs2_sb_info *c) { int i, ret; uint32_t empty_blocks = 0, bad_blocks = 0; unsigned char *flashbuf = NULL; uint32_t buf_size = 0; struct jffs2_summary *s = NULL; /* summary info collected by the scan process */ #ifndef __ECOS size_t pointlen; if (c->mtd->point) { ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf); if (!ret && pointlen < c->mtd->size) { /* Don't muck about if it won't let us point to the whole flash */ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen)); c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); flashbuf = NULL; } if (ret) D1(printk(KERN_DEBUG "MTD point failed %d\n", ret)); } #endif if (!flashbuf) { /* For NAND it's quicker to read a whole eraseblock at a time, apparently */ if (jffs2_cleanmarker_oob(c)) buf_size = c->sector_size; else buf_size = PAGE_SIZE; /* Respect kmalloc limitations */ if (buf_size > 128*1024) buf_size = 128*1024; D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); flashbuf = kmalloc(buf_size, GFP_KERNEL); if (!flashbuf) return -ENOMEM; } if (jffs2_sum_active()) { s = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!s) { JFFS2_WARNING("Can't allocate memory for summary\n"); return -ENOMEM; } memset(s, 0, sizeof(struct jffs2_summary)); } for (i=0; inr_blocks; i++) { struct jffs2_eraseblock *jeb = &c->blocks[i]; /* 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 (ret < 0) goto out; jffs2_dbg_acct_paranoia_check_nolock(c, jeb); /* Now decide which list to put it on */ switch(ret) { case BLK_STATE_ALLFF: /* * Empty block. Since we can't be sure it * was entirely erased, we just queue it for erase * again. It will be marked as such when the erase * is complete. Meanwhile we still count it as empty * for later checks. */ empty_blocks++; list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; break; case BLK_STATE_CLEANMARKER: /* Only a CLEANMARKER node is valid */ if (!jeb->dirty_size) { /* It's actually free */ list_add(&jeb->list, &c->free_list); c->nr_free_blocks++; } else { /* Dirt */ D1(printk(KERN_DEBUG "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; } break; case BLK_STATE_CLEAN: /* Full (or almost full) of clean data. Clean list */ list_add(&jeb->list, &c->clean_list); break; case BLK_STATE_PARTDIRTY: /* Some data, but not full. Dirty list. */ /* We want to remember the block with most free space and stick it in the 'nextblock' position to start writing to it. */ if (jeb->free_size > min_free(c) && (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ if (c->nextblock) { c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->free_size -= c->nextblock->free_size; c->wasted_size -= c->nextblock->wasted_size; c->nextblock->free_size = c->nextblock->wasted_size = 0; if (VERYDIRTY(c, c->nextblock->dirty_size)) { list_add(&c->nextblock->list, &c->very_dirty_list); } else { list_add(&c->nextblock->list, &c->dirty_list); } /* deleting summary information of the old nextblock */ jffs2_sum_reset_collected(c->summary); } /* update collected summary infromation for the current nextblock */ jffs2_sum_move_collected(c, s); D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset)); c->nextblock = jeb; } else { jeb->dirty_size += jeb->free_size + jeb->wasted_size; c->dirty_size += jeb->free_size + jeb->wasted_size; c->free_size -= jeb->free_size; c->wasted_size -= jeb->wasted_size; jeb->free_size = jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { list_add(&jeb->list, &c->very_dirty_list); } else { list_add(&jeb->list, &c->dirty_list); } } break; case BLK_STATE_ALLDIRTY: /* Nothing valid - not even a clean marker. Needs erasing. */ /* For now we just put it on the erasing list. We'll start the erases later */ D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; break; case BLK_STATE_BADBLOCK: D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset)); list_add(&jeb->list, &c->bad_list); c->bad_size += c->sector_size; c->free_size -= c->sector_size; bad_blocks++; break; default: printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n"); BUG(); } } if (jffs2_sum_active() && s) kfree(s); /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */ if (c->nextblock && (c->nextblock->dirty_size)) { c->nextblock->wasted_size += c->nextblock->dirty_size; c->wasted_size += c->nextblock->dirty_size; c->dirty_size -= c->nextblock->dirty_size; c->nextblock->dirty_size = 0; } #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) { /* If we're going to start writing into a block which already contains data, and the end of the data isn't page-aligned, skip a little and align it. */ uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize; D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", skip)); c->nextblock->wasted_size += skip; c->wasted_size += skip; c->nextblock->free_size -= skip; c->free_size -= skip; } #endif if (c->nr_erasing_blocks) { if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks); ret = -EIO; goto out; } jffs2_erase_pending_trigger(c); } ret = 0; out: if (buf_size) kfree(flashbuf); #ifndef __ECOS else c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); #endif return ret; } int jffs2_fill_scan_buf (struct jffs2_sb_info *c, void *buf, uint32_t ofs, uint32_t len) { int ret; size_t retlen; ret = jffs2_flash_read(c, ofs, len, &retlen, buf); if (ret) { D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret)); return ret; } if (retlen < len) { D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen)); return -EIO; } return 0; } int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size && (!jeb->first_node || !jeb->first_node->next_phys) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { c->dirty_size -= jeb->dirty_size; c->wasted_size += jeb->dirty_size; jeb->wasted_size += jeb->dirty_size; jeb->dirty_size = 0; return BLK_STATE_CLEAN; } else if (jeb->used_size || jeb->unchecked_size) return BLK_STATE_PARTDIRTY; else return BLK_STATE_ALLDIRTY; } static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { struct jffs2_unknown_node *node; struct jffs2_unknown_node crcnode; struct jffs2_sum_marker *sm; uint32_t ofs, prevofs; uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; #ifdef CONFIG_JFFS2_FS_WRITEBUFFER int cleanmarkerfound = 0; #endif ofs = jeb->offset; prevofs = jeb->offset - 1; D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { int ret = jffs2_check_nand_cleanmarker(c, jeb); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); /* Even if it's not found, we still scan to see if the block is empty. We use this information to decide whether to erase it or not. */ switch (ret) { case 0: cleanmarkerfound = 1; break; case 1: break; case 2: return BLK_STATE_BADBLOCK; case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ default: return ret; } } #endif if (jffs2_sum_active()) { sm = kmalloc(sizeof(struct jffs2_sum_marker), GFP_KERNEL); if (!sm) { return -ENOMEM; } err = jffs2_fill_scan_buf(c, (unsigned char *) sm, jeb->offset + c->sector_size - sizeof(struct jffs2_sum_marker), sizeof(struct jffs2_sum_marker)); if (err) { kfree(sm); return err; } if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC ) { err = jffs2_sum_scan_sumnode(c, jeb, je32_to_cpu(sm->offset), &pseudo_random); if (err) { kfree(sm); return err; } } kfree(sm); ofs = jeb->offset; prevofs = jeb->offset - 1; } buf_ofs = jeb->offset; if (!buf_size) { buf_len = c->sector_size; if (jffs2_sum_active()) { /* must reread because of summary test */ err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; } } else { buf_len = EMPTY_SCAN_SIZE(c->sector_size); err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; } /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ofs = 0; /* Scan only 4KiB of 0xFF before declaring it's empty */ while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ofs += 4; if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) { #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { /* scan oob, take care of cleanmarker */ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret)); switch (ret) { case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; case 1: return BLK_STATE_ALLDIRTY; default: return ret; } } #endif D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); if (c->cleanmarker_size == 0) return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */ else return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ } if (ofs) { D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, jeb->offset + ofs)); DIRTY_SPACE(ofs); } /* Now ofs is a complete physical flash offset as it always was... */ ofs += jeb->offset; noise = 10; dbg_summary("no summary found in jeb 0x%08x. Apply original scan.\n",jeb->offset); scan_more: while(ofs < jeb->offset + c->sector_size) { jffs2_dbg_acct_paranoia_check_nolock(c, jeb); cond_resched(); if (ofs & 3) { printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); ofs = PAD(ofs); continue; } if (ofs == prevofs) { printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs); DIRTY_SPACE(4); ofs += 4; continue; } prevofs = ofs; if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node), jeb->offset, c->sector_size, ofs, sizeof(*node))); DIRTY_SPACE((jeb->offset + c->sector_size)-ofs); break; } if (buf_ofs + buf_len < ofs + sizeof(*node)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n", sizeof(struct jffs2_unknown_node), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; } node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { uint32_t inbuf_ofs; uint32_t empty_start; empty_start = ofs; ofs += 4; D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); more_empty: inbuf_ofs = ofs - buf_ofs; while (inbuf_ofs < buf_len) { if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) { printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); DIRTY_SPACE(ofs-empty_start); goto scan_more; } inbuf_ofs+=4; ofs += 4; } /* Ran off end. */ D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs)); /* If we're only checking the beginning of a block with a cleanmarker, bail now */ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) { D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); return BLK_STATE_CLEANMARKER; } /* 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) { /* No more to read. Break out of main loop without marking this range of empty space as dirty (because it's not) */ D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n", empty_start)); break; } D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; goto more_empty; } if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); DIRTY_SPACE(4); ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs)); DIRTY_SPACE(4); ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) { printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); DIRTY_SPACE(4); ofs += 4; continue; } if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { /* OK. We're out of possibilities. Whinge and move on */ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, je16_to_cpu(node->magic)); DIRTY_SPACE(4); ofs += 4; continue; } /* We seem to have a node of sorts. Check the CRC */ crcnode.magic = node->magic; crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE); crcnode.totlen = node->totlen; hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4); if (hdr_crc != je32_to_cpu(node->hdr_crc)) { noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n", ofs, je16_to_cpu(node->magic), je16_to_cpu(node->nodetype), je32_to_cpu(node->totlen), je32_to_cpu(node->hdr_crc), hdr_crc); DIRTY_SPACE(4); ofs += 4; continue; } if (ofs + je32_to_cpu(node->totlen) > jeb->offset + c->sector_size) { /* Eep. Node goes over the end of the erase block. */ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", ofs, je32_to_cpu(node->totlen)); printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); DIRTY_SPACE(4); ofs += 4; continue; } if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { /* Wheee. This is an obsoleted node */ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs)); DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); continue; } switch(je16_to_cpu(node->nodetype)) { case JFFS2_NODETYPE_INODE: if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n", sizeof(struct jffs2_raw_inode), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_DIRENT: if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n", je32_to_cpu(node->totlen), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_CLEANMARKER: D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", ofs, je32_to_cpu(node->totlen), c->cleanmarker_size); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ofs += PAD(sizeof(struct jffs2_unknown_node)); } else if (jeb->first_node) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ofs += PAD(sizeof(struct jffs2_unknown_node)); } else { struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n"); return -ENOMEM; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = ofs | REF_NORMAL; marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; USED_SPACE(PAD(c->cleanmarker_size)); ofs += PAD(c->cleanmarker_size); } break; case JFFS2_NODETYPE_PADDING: if (jffs2_sum_active()) jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen)); DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; default: switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_ROCOMPAT: printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); c->flags |= JFFS2_SB_FLAG_RO; if (!(jffs2_is_readonly(c))) return -EROFS; DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_FEATURE_INCOMPAT: printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); return -EINVAL; case JFFS2_FEATURE_RWCOMPAT_DELETE: D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); USED_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; } } } if (jffs2_sum_active()) { if (PAD(s->sum_size + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size) { dbg_summary("There is not enough space for " "summary information, disabling for this jeb!\n"); jffs2_sum_disable_collecting(s); } } D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size)); /* mark_node_obsolete can add to wasted !! */ if (jeb->wasted_size) { jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; } return jffs2_scan_classify_jeb(c, jeb); } struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inode_cache *ic; ic = jffs2_get_ino_cache(c, ino); if (ic) return ic; if (ino > c->highest_ino) c->highest_ino = ino; ic = jffs2_alloc_inode_cache(); if (!ic) { printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n"); return NULL; } memset(ic, 0, sizeof(*ic)); ic->ino = ino; ic->nodes = (void *)ic; jffs2_add_ino_cache(c, ic); if (ino == 1) ic->nlink = 1; return ic; } static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; uint32_t ino = je32_to_cpu(ri->ino); D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs)); /* We do very little here now. Just check the ino# to which we should attribute this node; we can do all the CRC checking etc. later. There's a tradeoff here -- we used to scan the flash once only, reading everything we want from it into memory, then building all our in-core data structures and freeing the extra information. Now we allow the first part of the mount to complete a lot quicker, but we have to go _back_ to the flash in order to finish the CRC checking, etc. Which means that the _full_ amount of time to get to proper write mode with GC operational may actually be _longer_ than before. Sucks to be me. */ raw = jffs2_alloc_raw_node_ref(); if (!raw) { printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n"); return -ENOMEM; } ic = jffs2_get_ino_cache(c, ino); if (!ic) { /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the first node we found for this inode. Do a CRC check to protect against the former case */ uint32_t crc = crc32(0, ri, sizeof(*ri)-8); if (crc != je32_to_cpu(ri->node_crc)) { printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(ri->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); jffs2_free_raw_node_ref(raw); return 0; } ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) { jffs2_free_raw_node_ref(raw); return -ENOMEM; } } /* Wheee. It worked */ raw->flash_offset = ofs | REF_UNCHECKED; raw->__totlen = PAD(je32_to_cpu(ri->totlen)); raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", je32_to_cpu(ri->ino), je32_to_cpu(ri->version), je32_to_cpu(ri->offset), je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize))); pseudo_random += je32_to_cpu(ri->version); UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen))); if (jffs2_sum_active()) { jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset); } return 0; } static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; struct jffs2_inode_cache *ic; uint32_t crc; D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); /* We don't get here unless the node is still valid, so we don't have to mask in the ACCURATE bit any more. */ crc = crc32(0, rd, sizeof(*rd)-8); if (crc != je32_to_cpu(rd->node_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); return 0; } pseudo_random += je32_to_cpu(rd->version); fd = jffs2_alloc_full_dirent(rd->nsize+1); if (!fd) { return -ENOMEM; } memcpy(&fd->name, rd->name, rd->nsize); fd->name[rd->nsize] = 0; crc = crc32(0, fd->name, rd->nsize); if (crc != je32_to_cpu(rd->name_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->name_crc), crc); D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino))); jffs2_free_full_dirent(fd); /* FIXME: Why do we believe totlen? */ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); return 0; } raw = jffs2_alloc_raw_node_ref(); if (!raw) { jffs2_free_full_dirent(fd); printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); if (!ic) { jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); return -ENOMEM; } raw->__totlen = PAD(je32_to_cpu(rd->totlen)); raw->flash_offset = ofs | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; fd->raw = raw; fd->next = NULL; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(fd->name, rd->nsize); fd->type = rd->type; USED_SPACE(PAD(je32_to_cpu(rd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); if (jffs2_sum_active()) { jffs2_sum_add_dirent_mem(s, rd, ofs - jeb->offset); } return 0; } static int count_list(struct list_head *l) { uint32_t count = 0; struct list_head *tmp; list_for_each(tmp, l) { count++; } return count; } /* Note: This breaks if list_empty(head). I don't care. You might, if you copy this code and use it elsewhere :) */ static void rotate_list(struct list_head *head, uint32_t count) { struct list_head *n = head->next; list_del(head); while(count--) { n = n->next; } list_add(head, n); } void jffs2_rotate_lists(struct jffs2_sb_info *c) { uint32_t x; uint32_t rotateby; x = count_list(&c->clean_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->clean_list), rotateby); } x = count_list(&c->very_dirty_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->very_dirty_list), rotateby); } x = count_list(&c->dirty_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->dirty_list), rotateby); } x = count_list(&c->erasable_list); if (x) { rotateby = pseudo_random % x; rotate_list((&c->erasable_list), rotateby); } if (c->nr_erasing_blocks) { rotateby = pseudo_random % c->nr_erasing_blocks; rotate_list((&c->erase_pending_list), rotateby); } if (c->nr_free_blocks) { rotateby = pseudo_random % c->nr_free_blocks; rotate_list((&c->free_list), rotateby); } } linux-2.6.17/fs/jffs2/gc.c0000644000000000000000000012510610564537530013640 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: gc.c,v 1.155 2005/11/07 11:14:39 gleixner Exp $ * */ #include #include #include #include #include #include #include #include "nodelist.h" #include "compr.h" static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_raw_node_ref *raw); static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd); static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end); static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end); static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f); /* Called with erase_completion_lock held */ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) { struct jffs2_eraseblock *ret; struct list_head *nextlist = NULL; int n = jiffies % 128; /* Pick an eraseblock to garbage collect next. This is where we'll put the clever wear-levelling algorithms. Eventually. */ /* We possibly want to favour the dirtier blocks more when the number of free blocks is low. */ again: if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) { D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); nextlist = &c->bad_used_list; } else if (n < 50 && !list_empty(&c->erasable_list)) { /* Note that most of them will have gone directly to be erased. So don't favour the erasable_list _too_ much. */ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n")); nextlist = &c->erasable_list; } else if (n < 110 && !list_empty(&c->very_dirty_list)) { /* Most of the time, pick one off the very_dirty list */ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n")); nextlist = &c->very_dirty_list; } else if (n < 126 && !list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->clean_list)) { D1(printk(KERN_DEBUG "Picking block from clean_list to GC next\n")); nextlist = &c->clean_list; } else if (!list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->very_dirty_list)) { D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n")); nextlist = &c->very_dirty_list; } else if (!list_empty(&c->erasable_list)) { D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); nextlist = &c->erasable_list; } else if (!list_empty(&c->erasable_pending_wbuf_list)) { /* There are blocks are wating for the wbuf sync */ D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n")); spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); goto again; } else { /* Eep. All were empty */ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); return NULL; } ret = list_entry(nextlist->next, struct jffs2_eraseblock, list); list_del(&ret->list); c->gcblock = ret; ret->gc_node = ret->first_node; if (!ret->gc_node) { printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); BUG(); } /* Have we accidentally picked a clean block with wasted space ? */ if (ret->wasted_size) { D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size)); ret->dirty_size += ret->wasted_size; c->wasted_size -= ret->wasted_size; c->dirty_size += ret->wasted_size; ret->wasted_size = 0; } return ret; } /* jffs2_garbage_collect_pass * Make a single attempt to progress GC. Move one node, and possibly * start erasing one eraseblock. */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) { struct jffs2_inode_info *f; struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; int ret = 0, inum, nlink; if (down_interruptible(&c->alloc_sem)) return -EINTR; for (;;) { spin_lock(&c->erase_completion_lock); if (!c->unchecked_size) break; /* We can't start doing GC yet. We haven't finished checking the node CRCs etc. Do it now. */ /* checked_ino is protected by the alloc_sem */ if (c->checked_ino > c->highest_ino) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); BUG(); } spin_unlock(&c->erase_completion_lock); spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, c->checked_ino++); if (!ic) { spin_unlock(&c->inocache_lock); continue; } if (!ic->nlink) { D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", ic->ino)); spin_unlock(&c->inocache_lock); continue; } switch(ic->state) { case INO_STATE_CHECKEDABSENT: case INO_STATE_PRESENT: D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino)); spin_unlock(&c->inocache_lock); continue; case INO_STATE_GC: case INO_STATE_CHECKING: printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state); spin_unlock(&c->inocache_lock); BUG(); case INO_STATE_READING: /* We need to wait for it to finish, lest we move on and trigger the BUG() above while we haven't yet finished checking all its nodes */ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino)); up(&c->alloc_sem); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); return 0; default: BUG(); case INO_STATE_UNCHECKED: ; } ic->state = INO_STATE_CHECKING; spin_unlock(&c->inocache_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino)); ret = jffs2_do_crccheck_inode(c, ic); if (ret) printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); up(&c->alloc_sem); return ret; } /* First, work out which block we're garbage-collecting */ jeb = c->gcblock; if (!jeb) jeb = jffs2_find_gc_block(c); if (!jeb) { D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -EIO; } D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); D1(if (c->nextblock) printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); if (!jeb->used_size) { up(&c->alloc_sem); goto eraseit; } raw = jeb->gc_node; while(ref_obsolete(raw)) { D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); raw = raw->next_phys; if (unlikely(!raw)) { printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); BUG(); } } jeb->gc_node = raw; D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); if (!raw->next_in_ino) { /* Inode-less node. Clean marker, snapshot or something like that */ /* FIXME: If it's something that needs to be copied, including something we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); up(&c->alloc_sem); goto eraseit_lock; } ic = jffs2_raw_ref_to_ic(raw); /* We need to hold the inocache. Either the erase_completion_lock or the inocache_lock are sufficient; we trade down since the inocache_lock causes less contention. */ spin_lock(&c->inocache_lock); spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino)); /* Three possibilities: 1. Inode is already in-core. We must iget it and do proper updating to its fragtree, etc. 2. Inode is not in-core, node is REF_PRISTINE. We lock the inocache to prevent a read_inode(), copy the node intact. 3. Inode is not in-core, node is not pristine. We must iget() and take the slow path. */ switch(ic->state) { case INO_STATE_CHECKEDABSENT: /* It's been checked, but it's not currently in-core. We can just copy any pristine nodes, but have to prevent anyone else from doing read_inode() while we're at it, so we set the state accordingly */ if (ref_flags(raw) == REF_PRISTINE) ic->state = INO_STATE_GC; else { D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", ic->ino)); } break; case INO_STATE_PRESENT: /* It's in-core. GC must iget() it. */ break; case INO_STATE_UNCHECKED: case INO_STATE_CHECKING: case INO_STATE_GC: /* Should never happen. We should have finished checking by the time we actually start doing any GC, and since we're holding the alloc_sem, no other garbage collection can happen. */ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ic->ino, ic->state); up(&c->alloc_sem); spin_unlock(&c->inocache_lock); BUG(); case INO_STATE_READING: /* Someone's currently trying to read it. We must wait for them to finish and then go through the full iget() route to do the GC. However, sometimes read_inode() needs to get the alloc_sem() (for marking nodes invalid) so we must drop the alloc_sem before sleeping. */ up(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); /* And because we dropped the alloc_sem we must start again from the beginning. Ponder chance of livelock here -- we're returning success without actually making any progress. Q: What are the chances that the inode is back in INO_STATE_READING again by the time we next enter this function? And that this happens enough times to cause a real delay? A: Small enough that I don't care :) */ return 0; } /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the node intact, and we don't have to muck about with the fragtree etc. because we know it's not in-core. If it _was_ in-core, we go through all the iget() crap anyway */ if (ic->state == INO_STATE_GC) { spin_unlock(&c->inocache_lock); ret = jffs2_garbage_collect_pristine(c, ic, raw); spin_lock(&c->inocache_lock); ic->state = INO_STATE_CHECKEDABSENT; wake_up(&c->inocache_wq); if (ret != -EBADFD) { spin_unlock(&c->inocache_lock); goto release_sem; } /* Fall through if it wanted us to, with inocache_lock held */ } /* Prevent the fairly unlikely race where the gcblock is entirely obsoleted by the final close of a file which had the only valid nodes in the block, followed by erasure, followed by freeing of the ic because the erased block(s) held _all_ the nodes of that inode.... never been seen but it's vaguely possible. */ inum = ic->ino; nlink = ic->nlink; spin_unlock(&c->inocache_lock); f = jffs2_gc_fetch_inode(c, inum, nlink); if (IS_ERR(f)) { ret = PTR_ERR(f); goto release_sem; } if (!f) { ret = 0; goto release_sem; } ret = jffs2_garbage_collect_live(c, jeb, raw, f); jffs2_gc_release_inode(c, f); release_sem: up(&c->alloc_sem); eraseit_lock: /* If we've finished this block, start it erasing */ spin_lock(&c->erase_completion_lock); eraseit: if (c->gcblock && !c->gcblock->used_size) { D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); /* We're GC'ing an empty block? */ list_add_tail(&c->gcblock->list, &c->erase_pending_list); c->gcblock = NULL; c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } spin_unlock(&c->erase_completion_lock); return ret; } static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f) { struct jffs2_node_frag *frag; struct jffs2_full_dnode *fn = NULL; struct jffs2_full_dirent *fd; uint32_t start = 0, end = 0, nrfrags = 0; int ret = 0; down(&f->sem); /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ spin_lock(&c->erase_completion_lock); if (c->gcblock != jeb) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n")); goto upnout; } if (ref_obsolete(raw)) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); /* They'll call again */ goto upnout; } spin_unlock(&c->erase_completion_lock); /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */ if (f->metadata && f->metadata->raw == raw) { fn = f->metadata; ret = jffs2_garbage_collect_metadata(c, jeb, f, fn); goto upnout; } /* FIXME. Read node and do lookup? */ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { if (frag->node && frag->node->raw == raw) { fn = frag->node; end = frag->ofs + frag->size; if (!nrfrags++) start = frag->ofs; if (nrfrags == frag->node->frags) break; /* We've found them all */ } } if (fn) { if (ref_flags(raw) == REF_PRISTINE) { ret = jffs2_garbage_collect_pristine(c, f->inocache, raw); if (!ret) { /* Urgh. Return it sensibly. */ frag->node->raw = f->inocache->nodes; } if (ret != -EBADFD) goto upnout; } /* We found a datanode. Do the GC */ if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) { /* It crosses a page boundary. Therefore, it must be a hole. */ ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end); } else { /* It could still be a hole. But we GC the page this way anyway */ ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end); } goto upnout; } /* Wasn't a dnode. Try dirent */ for (fd = f->dents; fd; fd=fd->next) { if (fd->raw == raw) break; } if (fd && fd->ino) { ret = jffs2_garbage_collect_dirent(c, jeb, f, fd); } else if (fd) { ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd); } else { printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n", ref_offset(raw), f->inocache->ino); if (ref_obsolete(raw)) { printk(KERN_WARNING "But it's obsolete so we don't mind too much\n"); } else { jffs2_dbg_dump_node(c, ref_offset(raw)); BUG(); } } upnout: up(&f->sem); return ret; } static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_raw_node_ref *raw) { union jffs2_node_union *node; struct jffs2_raw_node_ref *nraw; size_t retlen; int ret; uint32_t phys_ofs, alloclen; uint32_t crc, rawlen; int retried = 0; D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); rawlen = ref_totlen(c, c->gcblock, raw); /* Ask for a small amount of space (or the totlen if smaller) because we don't want to force wastage of the end of a block if splitting would work. */ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, rawlen), &phys_ofs, &alloclen, rawlen); /* this is not the exact summary size of it, it is only an upper estimation */ if (ret) return ret; if (alloclen < rawlen) { /* Doesn't fit untouched. We'll go the old route and split it */ return -EBADFD; } node = kmalloc(rawlen, GFP_KERNEL); if (!node) return -ENOMEM; ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); if (!ret && retlen != rawlen) ret = -EIO; if (ret) goto out_node; crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4); if (je32_to_cpu(node->u.hdr_crc) != crc) { printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc); goto bail; } switch(je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: crc = crc32(0, node, sizeof(node->i)-8); if (je32_to_cpu(node->i.node_crc) != crc) { printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->i.node_crc), crc); goto bail; } if (je32_to_cpu(node->i.dsize)) { crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize)); if (je32_to_cpu(node->i.data_crc) != crc) { printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->i.data_crc), crc); goto bail; } } break; case JFFS2_NODETYPE_DIRENT: crc = crc32(0, node, sizeof(node->d)-8); if (je32_to_cpu(node->d.node_crc) != crc) { printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->d.node_crc), crc); goto bail; } if (node->d.nsize) { crc = crc32(0, node->d.name, node->d.nsize); if (je32_to_cpu(node->d.name_crc) != crc) { printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ref_offset(raw), je32_to_cpu(node->d.name_crc), crc); goto bail; } } break; default: printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", ref_offset(raw), je16_to_cpu(node->u.nodetype)); goto bail; } nraw = jffs2_alloc_raw_node_ref(); if (!nraw) { ret = -ENOMEM; goto out_node; } /* OK, all the CRCs are good; this node can just be copied as-is. */ retry: nraw->flash_offset = phys_ofs; nraw->__totlen = rawlen; nraw->next_phys = NULL; ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); if (ret || (retlen != rawlen)) { printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", rawlen, phys_ofs, ret, retlen); if (retlen) { /* Doesn't belong to any inode */ nraw->next_in_ino = NULL; nraw->flash_offset |= REF_OBSOLETE; jffs2_add_physical_node_ref(c, nraw); jffs2_mark_node_obsolete(c, nraw); } else { printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); jffs2_free_raw_node_ref(nraw); } if (!retried && (nraw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; retried = 1; D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n")); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy, rawlen); /* this is not the exact summary size of it, it is only an upper estimation */ if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); jffs2_free_raw_node_ref(nraw); } jffs2_free_raw_node_ref(nraw); if (!ret) ret = -EIO; goto out_node; } nraw->flash_offset |= REF_PRISTINE; jffs2_add_physical_node_ref(c, nraw); /* Link into per-inode list. This is safe because of the ic state being INO_STATE_GC. Note that if we're doing this for an inode which is in-core, the 'nraw' pointer is then going to be fetched from ic->nodes by our caller. */ spin_lock(&c->erase_completion_lock); nraw->next_in_ino = ic->nodes; ic->nodes = nraw; spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); out_node: kfree(node); return ret; bail: ret = -EBADFD; goto out_node; } static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; struct jffs2_node_frag *last_frag; jint16_t dev; char *mdata = NULL, mdatalen = 0; uint32_t alloclen, phys_ofs, ilen; int ret; if (S_ISBLK(JFFS2_F_I_MODE(f)) || S_ISCHR(JFFS2_F_I_MODE(f)) ) { /* For these, we don't actually need to read the old node */ /* FIXME: for minor or major > 255. */ dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | JFFS2_F_I_RDEV_MIN(f))); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(JFFS2_F_I_MODE(f))) { mdatalen = fn->size; mdata = kmalloc(fn->size, GFP_KERNEL); if (!mdata) { printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n"); return -ENOMEM; } ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen); if (ret) { printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret); kfree(mdata); return ret; } D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bites of symlink target\n", mdatalen)); } ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n", sizeof(ri)+ mdatalen, ret); goto out; } last_frag = frag_last(&f->fragtree); if (last_frag) /* Fetch the inode length from the fragtree rather then * from i_size since i_size may have not been updated yet */ ilen = last_frag->ofs + last_frag->size; else ilen = JFFS2_F_I_SIZE(f); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ri.isize = cpu_to_je32(ilen); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ri.offset = cpu_to_je32(0); ri.csize = cpu_to_je32(mdatalen); ri.dsize = cpu_to_je32(mdatalen); ri.compr = JFFS2_COMPR_NONE; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); ret = PTR_ERR(new_fn); goto out; } jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); f->metadata = new_fn; out: if (S_ISLNK(JFFS2_F_I_MODE(f))) kfree(mdata); return ret; } static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) { struct jffs2_full_dirent *new_fd; struct jffs2_raw_dirent rd; uint32_t alloclen, phys_ofs; int ret; rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd.nsize = strlen(fd->name); rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize); rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4)); rd.pino = cpu_to_je32(f->inocache->ino); rd.version = cpu_to_je32(++f->highest_version); rd.ino = cpu_to_je32(fd->ino); /* If the times on this inode were set by explicit utime() they can be different, so refrain from splatting them. */ if (JFFS2_F_I_MTIME(f) == JFFS2_F_I_CTIME(f)) rd.mctime = cpu_to_je32(JFFS2_F_I_MTIME(f)); else rd.mctime = cpu_to_je32(0); rd.type = fd->type; rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8)); rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize)); ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen, JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize)); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n", sizeof(rd)+rd.nsize, ret); return ret; } new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC); if (IS_ERR(new_fd)) { printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd)); return PTR_ERR(new_fd); } jffs2_add_fd_to_list(c, new_fd, &f->dents); return 0; } static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) { struct jffs2_full_dirent **fdp = &f->dents; int found = 0; /* On a medium where we can't actually mark nodes obsolete pernamently, such as NAND flash, we need to work out whether this deletion dirent is still needed to actively delete a 'real' dirent with the same name that's still somewhere else on the flash. */ if (!jffs2_can_mark_obsolete(c)) { struct jffs2_raw_dirent *rd; struct jffs2_raw_node_ref *raw; int ret; size_t retlen; int name_len = strlen(fd->name); uint32_t name_crc = crc32(0, fd->name, name_len); uint32_t rawlen = ref_totlen(c, jeb, fd->raw); rd = kmalloc(rawlen, GFP_KERNEL); if (!rd) return -ENOMEM; /* Prevent the erase code from nicking the obsolete node refs while we're looking at them. I really don't like this extra lock but can't see any alternative. Suggestions on a postcard to... */ down(&c->erase_free_sem); for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { /* We only care about obsolete ones */ if (!(ref_obsolete(raw))) continue; /* Any dirent with the same name is going to have the same length... */ if (ref_totlen(c, NULL, raw) != rawlen) continue; /* Doesn't matter if there's one in the same erase block. We're going to delete it too at the same time. */ if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset)) continue; D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); /* This is an obsolete node belonging to the same directory, and it's of the right length. We need to take a closer look...*/ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); if (ret) { printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw)); /* If we can't read it, we don't need to continue to obsolete it. Continue */ continue; } if (retlen != rawlen) { printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n", retlen, rawlen, ref_offset(raw)); continue; } if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) continue; /* If the name CRC doesn't match, skip */ if (je32_to_cpu(rd->name_crc) != name_crc) continue; /* If the name length doesn't match, or it's another deletion dirent, skip */ if (rd->nsize != name_len || !je32_to_cpu(rd->ino)) continue; /* OK, check the actual name now */ if (memcmp(rd->name, fd->name, name_len)) continue; /* OK. The name really does match. There really is still an older node on the flash which our deletion dirent obsoletes. So we have to write out a new deletion dirent to replace it */ up(&c->erase_free_sem); D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); kfree(rd); return jffs2_garbage_collect_dirent(c, jeb, f, fd); } up(&c->erase_free_sem); kfree(rd); } /* FIXME: If we're deleting a dirent which contains the current mtime and ctime, we should update the metadata node with those times accordingly */ /* No need for it any more. Just mark it obsolete and remove it from the list */ while (*fdp) { if ((*fdp) == fd) { found = 1; *fdp = fd->next; break; } fdp = &(*fdp)->next; } if (!found) { printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino); } jffs2_mark_node_obsolete(c, fd->raw); jffs2_free_full_dirent(fd); return 0; } static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { struct jffs2_raw_inode ri; struct jffs2_node_frag *frag; struct jffs2_full_dnode *new_fn; uint32_t alloclen, phys_ofs, ilen; int ret; D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n", f->inocache->ino, start, end)); memset(&ri, 0, sizeof(ri)); if(fn->frags > 1) { size_t readlen; uint32_t crc; /* It's partially obsoleted by a later write. So we have to write it out again with the _same_ version as before */ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri); if (readlen != sizeof(ri) || ret) { printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen); goto fill; } if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n", ref_offset(fn->raw), je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE); return -EIO; } if (je32_to_cpu(ri.totlen) != sizeof(ri)) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n", ref_offset(fn->raw), je32_to_cpu(ri.totlen), sizeof(ri)); return -EIO; } crc = crc32(0, &ri, sizeof(ri)-8); if (crc != je32_to_cpu(ri.node_crc)) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n", ref_offset(fn->raw), je32_to_cpu(ri.node_crc), crc); /* FIXME: We could possibly deal with this by writing new holes for each frag */ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", start, end, f->inocache->ino); goto fill; } if (ri.compr != JFFS2_COMPR_ZERO) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw)); printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", start, end, f->inocache->ino); goto fill; } } else { fill: ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.offset = cpu_to_je32(start); ri.dsize = cpu_to_je32(end - start); ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; } frag = frag_last(&f->fragtree); if (frag) /* Fetch the inode length from the fragtree rather then * from i_size since i_size may have not been updated yet */ ilen = frag->ofs + frag->size; else ilen = JFFS2_F_I_SIZE(f); ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ri.isize = cpu_to_je32(ilen); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ri.data_crc = cpu_to_je32(0); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n", sizeof(ri), ret); return ret; } new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); return PTR_ERR(new_fn); } if (je32_to_cpu(ri.version) == f->highest_version) { jffs2_add_full_dnode_to_inode(c, f, new_fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } return 0; } /* * We should only get here in the case where the node we are * replacing had more than one frag, so we kept the same version * number as before. (Except in case of error -- see 'goto fill;' * above.) */ D1(if(unlikely(fn->frags <= 1)) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n", fn->frags, je32_to_cpu(ri.version), f->highest_version, je32_to_cpu(ri.ino)); }); /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */ mark_ref_normal(new_fn->raw); for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); frag; frag = frag_next(frag)) { if (frag->ofs > fn->size + fn->ofs) break; if (frag->node == fn) { frag->node = new_fn; new_fn->frags++; fn->frags--; } } if (fn->frags) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Old node still has frags!\n"); BUG(); } if (!new_fn->frags) { printk(KERN_WARNING "jffs2_garbage_collect_hole: New node has no frags!\n"); BUG(); } jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); return 0; } static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; unsigned long pg; unsigned char *pg_ptr; memset(&ri, 0, sizeof(ri)); D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n", f->inocache->ino, start, end)); orig_end = end; orig_start = start; if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) { /* Attempt to do some merging. But only expand to cover logically adjacent frags if the block containing them is already considered to be dirty. Otherwise we end up with GC just going round in circles dirtying the nodes it already wrote out, especially on NAND where we have small eraseblocks and hence a much higher chance of nodes having to be split to cross boundaries. */ struct jffs2_node_frag *frag; uint32_t min, max; min = start & ~(PAGE_CACHE_SIZE-1); max = min + PAGE_CACHE_SIZE; frag = jffs2_lookup_node_frag(&f->fragtree, start); /* BUG_ON(!frag) but that'll happen anyway... */ BUG_ON(frag->ofs != start); /* First grow down... */ while((frag = frag_prev(frag)) && frag->ofs >= min) { /* If the previous frag doesn't even reach the beginning, there's excessive fragmentation. Just merge. */ if (frag->ofs > min) { D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n", frag->ofs, frag->ofs+frag->size)); start = frag->ofs; continue; } /* OK. This frag holds the first byte of the page. */ if (!frag->node || !frag->node->raw) { D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n", frag->ofs, frag->ofs+frag->size)); break; } else { /* OK, it's a frag which extends to the beginning of the page. Does it live in a block which is still considered clean? If so, don't obsolete it. If not, cover it anyway. */ struct jffs2_raw_node_ref *raw = frag->node->raw; struct jffs2_eraseblock *jeb; jeb = &c->blocks[raw->flash_offset / c->sector_size]; if (jeb == c->gcblock) { D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n", frag->ofs, frag->ofs+frag->size, ref_offset(raw))); start = frag->ofs; break; } if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); break; } D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); start = frag->ofs; break; } } /* ... then up */ /* Find last frag which is actually part of the node we're to GC. */ frag = jffs2_lookup_node_frag(&f->fragtree, end-1); while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) { /* If the previous frag doesn't even reach the beginning, there's lots of fragmentation. Just merge. */ if (frag->ofs+frag->size < max) { D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n", frag->ofs, frag->ofs+frag->size)); end = frag->ofs + frag->size; continue; } if (!frag->node || !frag->node->raw) { D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n", frag->ofs, frag->ofs+frag->size)); break; } else { /* OK, it's a frag which extends to the beginning of the page. Does it live in a block which is still considered clean? If so, don't obsolete it. If not, cover it anyway. */ struct jffs2_raw_node_ref *raw = frag->node->raw; struct jffs2_eraseblock *jeb; jeb = &c->blocks[raw->flash_offset / c->sector_size]; if (jeb == c->gcblock) { D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n", frag->ofs, frag->ofs+frag->size, ref_offset(raw))); end = frag->ofs + frag->size; break; } if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); break; } D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n", frag->ofs, frag->ofs+frag->size, jeb->offset)); end = frag->ofs + frag->size; break; } } D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", orig_start, orig_end, start, end)); D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size)); BUG_ON(end < orig_end); BUG_ON(start > orig_start); } /* First, use readpage() to read the appropriate page into the page cache */ /* Q: What happens if we actually try to GC the _same_ page for which commit_write() * triggered garbage collection in the first place? * A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the * page OK. We'll actually write it out again in commit_write, which is a little * suboptimal, but at least we're correct. */ pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); if (IS_ERR(pg_ptr)) { printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); return PTR_ERR(pg_ptr); } offset = start; while(offset < orig_end) { uint32_t datalen; uint32_t cdatalen; uint16_t comprtype = JFFS2_COMPR_NONE; ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n", sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret); break; } cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset); datalen = end - offset; writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ri.offset = cpu_to_je32(offset); ri.csize = cpu_to_je32(cdatalen); ri.dsize = cpu_to_je32(datalen); ri.compr = comprtype & 0xff; ri.usercompr = (comprtype >> 8) & 0xff; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); jffs2_free_comprbuf(comprbuf, writebuf); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); ret = PTR_ERR(new_fn); break; } ret = jffs2_add_full_dnode_to_inode(c, f, new_fn); offset += datalen; if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } } jffs2_gc_release_page(c, pg_ptr, &pg); return ret; } linux-2.6.17/fs/jffs2/symlink.c0000644000000000000000000000341310564537530014731 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: symlink.c,v 1.19 2005/11/07 11:14:42 gleixner Exp $ * */ #include #include #include #include #include "nodelist.h" static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = jffs2_follow_link, .setattr = jffs2_setattr }; static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); char *p = (char *)f->target; /* * We don't acquire the f->sem mutex here since the only data we * use is f->target. * * 1. If we are here the inode has already built and f->target has * to point to the target path. * 2. Nobody uses f->target (if the inode is symlink's inode). The * exception is inode freeing function which frees f->target. But * it can't be called while we are here and before VFS has * stopped using our f->target string which we provide by means of * nd_set_link() call. */ if (!p) { printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); p = ERR_PTR(-EIO); } D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target)); nd_set_link(nd, p); /* * We will unlock the f->sem mutex but VFS will use the f->target string. This is safe * since the only way that may cause f->target to be changed is iput() operation. * But VFS will not use f->target after iput() has been called. */ return NULL; } linux-2.6.17/fs/jffs2/nodelist.c0000644000000000000000000007261210564537530015073 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: nodelist.c,v 1.115 2005/11/07 11:14:40 gleixner Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) { struct jffs2_full_dirent **prev = list; dbg_dentlist("add dirent \"%s\", ino #%u\n", new->name, new->ino); while ((*prev) && (*prev)->nhash <= new->nhash) { if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) { /* Duplicate. Free one */ if (new->version < (*prev)->version) { dbg_dentlist("Eep! Marking new dirent node is obsolete, old is \"%s\", ino #%u\n", (*prev)->name, (*prev)->ino); jffs2_mark_node_obsolete(c, new->raw); jffs2_free_full_dirent(new); } else { dbg_dentlist("marking old dirent \"%s\", ino #%u bsolete\n", (*prev)->name, (*prev)->ino); new->next = (*prev)->next; jffs2_mark_node_obsolete(c, ((*prev)->raw)); jffs2_free_full_dirent(*prev); *prev = new; } return; } prev = &((*prev)->next); } new->next = *prev; *prev = new; } void jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) { struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); dbg_fragtree("truncating fragtree to 0x%08x bytes\n", size); /* We know frag->ofs <= size. That's what lookup does for us */ if (frag && frag->ofs != size) { if (frag->ofs+frag->size > size) { frag->size = size - frag->ofs; } frag = frag_next(frag); } while (frag && frag->ofs >= size) { struct jffs2_node_frag *next = frag_next(frag); frag_erase(frag, list); jffs2_obsolete_node_frag(c, frag); frag = next; } if (size == 0) return; /* * If the last fragment starts at the RAM page boundary, it is * REF_PRISTINE irrespective of its size. */ frag = frag_last(list); if (frag->node && (frag->ofs & (PAGE_CACHE_SIZE - 1)) == 0) { dbg_fragtree2("marking the last fragment 0x%08x-0x%08x REF_PRISTINE.\n", frag->ofs, frag->ofs + frag->size); frag->node->raw->flash_offset = ref_offset(frag->node->raw) | REF_PRISTINE; } } void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) { if (this->node) { this->node->frags--; if (!this->node->frags) { /* The node has no valid frags left. It's totally obsoleted */ dbg_fragtree2("marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size); jffs2_mark_node_obsolete(c, this->node->raw); jffs2_free_full_dnode(this->node); } else { dbg_fragtree2("marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n", ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size, this->node->frags); mark_ref_normal(this->node->raw); } } jffs2_free_node_frag(this); } static void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) { struct rb_node *parent = &base->rb; struct rb_node **link = &parent; dbg_fragtree2("insert frag (0x%04x-0x%04x)\n", newfrag->ofs, newfrag->ofs + newfrag->size); while (*link) { parent = *link; base = rb_entry(parent, struct jffs2_node_frag, rb); if (newfrag->ofs > base->ofs) link = &base->rb.rb_right; else if (newfrag->ofs < base->ofs) link = &base->rb.rb_left; else { JFFS2_ERROR("duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); BUG(); } } rb_link_node(&newfrag->rb, &base->rb, link); } /* * Allocate and initializes a new fragment. */ static struct jffs2_node_frag * new_fragment(struct jffs2_full_dnode *fn, uint32_t ofs, uint32_t size) { struct jffs2_node_frag *newfrag; newfrag = jffs2_alloc_node_frag(); if (likely(newfrag)) { newfrag->ofs = ofs; newfrag->size = size; newfrag->node = fn; } else { JFFS2_ERROR("cannot allocate a jffs2_node_frag object\n"); } return newfrag; } /* * Called when there is no overlapping fragment exist. Inserts a hole before the new * fragment and inserts the new fragment to the fragtree. */ static int no_overlapping_node(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag, struct jffs2_node_frag *this, uint32_t lastend) { if (lastend < newfrag->node->ofs) { /* put a hole in before the new fragment */ struct jffs2_node_frag *holefrag; holefrag= new_fragment(NULL, lastend, newfrag->node->ofs - lastend); if (unlikely(!holefrag)) { jffs2_free_node_frag(newfrag); return -ENOMEM; } if (this) { /* By definition, the 'this' node has no right-hand child, because there are no frags with offset greater than it. So that's where we want to put the hole */ dbg_fragtree2("add hole frag %#04x-%#04x on the right of the new frag.\n", holefrag->ofs, holefrag->ofs + holefrag->size); rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); } else { dbg_fragtree2("Add hole frag %#04x-%#04x to the root of the tree.\n", holefrag->ofs, holefrag->ofs + holefrag->size); rb_link_node(&holefrag->rb, NULL, &root->rb_node); } rb_insert_color(&holefrag->rb, root); this = holefrag; } if (this) { /* By definition, the 'this' node has no right-hand child, because there are no frags with offset greater than it. So that's where we want to put new fragment */ dbg_fragtree2("add the new node at the right\n"); rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); } else { dbg_fragtree2("insert the new node at the root of the tree\n"); rb_link_node(&newfrag->rb, NULL, &root->rb_node); } rb_insert_color(&newfrag->rb, root); return 0; } /* Doesn't set inode->i_size */ static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag) { struct jffs2_node_frag *this; uint32_t lastend; /* Skip all the nodes which are completed before this one starts */ this = jffs2_lookup_node_frag(root, newfrag->node->ofs); if (this) { dbg_fragtree2("lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this); lastend = this->ofs + this->size; } else { dbg_fragtree2("lookup gave no frag\n"); lastend = 0; } /* See if we ran off the end of the fragtree */ if (lastend <= newfrag->ofs) { /* We did */ /* Check if 'this' node was on the same page as the new node. If so, both 'this' and the new node get marked REF_NORMAL so the GC can take a look. */ if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { if (this->node) mark_ref_normal(this->node->raw); mark_ref_normal(newfrag->node->raw); } return no_overlapping_node(c, root, newfrag, this, lastend); } if (this->node) dbg_fragtree2("dealing with frag %u-%u, phys %#08x(%d).\n", this->ofs, this->ofs + this->size, ref_offset(this->node->raw), ref_flags(this->node->raw)); else dbg_fragtree2("dealing with hole frag %u-%u.\n", this->ofs, this->ofs + this->size); /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes, * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs */ if (newfrag->ofs > this->ofs) { /* This node isn't completely obsoleted. The start of it remains valid */ /* Mark the new node and the partially covered node REF_NORMAL -- let the GC take a look at them */ mark_ref_normal(newfrag->node->raw); if (this->node) mark_ref_normal(this->node->raw); if (this->ofs + this->size > newfrag->ofs + newfrag->size) { /* The new node splits 'this' frag into two */ struct jffs2_node_frag *newfrag2; if (this->node) dbg_fragtree2("split old frag 0x%04x-0x%04x, phys 0x%08x\n", this->ofs, this->ofs+this->size, ref_offset(this->node->raw)); else dbg_fragtree2("split old hole frag 0x%04x-0x%04x\n", this->ofs, this->ofs+this->size); /* New second frag pointing to this's node */ newfrag2 = new_fragment(this->node, newfrag->ofs + newfrag->size, this->ofs + this->size - newfrag->ofs - newfrag->size); if (unlikely(!newfrag2)) return -ENOMEM; if (this->node) this->node->frags++; /* Adjust size of original 'this' */ this->size = newfrag->ofs - this->ofs; /* Now, we know there's no node with offset greater than this->ofs but smaller than newfrag2->ofs or newfrag->ofs, for obvious reasons. So we can do a tree insert from 'this' to insert newfrag, and a tree insert from newfrag to insert newfrag2. */ jffs2_fragtree_insert(newfrag, this); rb_insert_color(&newfrag->rb, root); jffs2_fragtree_insert(newfrag2, newfrag); rb_insert_color(&newfrag2->rb, root); return 0; } /* New node just reduces 'this' frag in size, doesn't split it */ this->size = newfrag->ofs - this->ofs; /* Again, we know it lives down here in the tree */ jffs2_fragtree_insert(newfrag, this); rb_insert_color(&newfrag->rb, root); } else { /* New frag starts at the same point as 'this' used to. Replace it in the tree without doing a delete and insertion */ dbg_fragtree2("inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n", newfrag, newfrag->ofs, newfrag->ofs+newfrag->size, this, this->ofs, this->ofs+this->size); rb_replace_node(&this->rb, &newfrag->rb, root); if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { dbg_fragtree2("obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size); jffs2_obsolete_node_frag(c, this); } else { this->ofs += newfrag->size; this->size -= newfrag->size; jffs2_fragtree_insert(this, newfrag); rb_insert_color(&this->rb, root); return 0; } } /* OK, now we have newfrag added in the correct place in the tree, but frag_next(newfrag) may be a fragment which is overlapped by it */ while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { /* 'this' frag is obsoleted completely. */ dbg_fragtree2("obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size); rb_erase(&this->rb, root); jffs2_obsolete_node_frag(c, this); } /* Now we're pointing at the first frag which isn't totally obsoleted by the new frag */ if (!this || newfrag->ofs + newfrag->size == this->ofs) return 0; /* Still some overlap but we don't need to move it in the tree */ this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); this->ofs = newfrag->ofs + newfrag->size; /* And mark them REF_NORMAL so the GC takes a look at them */ if (this->node) mark_ref_normal(this->node->raw); mark_ref_normal(newfrag->node->raw); return 0; } /* * Given an inode, probably with existing tree of fragments, add the new node * to the fragment tree. */ int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) { int ret; struct jffs2_node_frag *newfrag; if (unlikely(!fn->size)) return 0; newfrag = new_fragment(fn, fn->ofs, fn->size); if (unlikely(!newfrag)) return -ENOMEM; newfrag->node->frags = 1; dbg_fragtree("adding node %#04x-%#04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); if (unlikely(ret)) return ret; /* If we now share a page with other nodes, mark either previous or next node REF_NORMAL, as appropriate. */ if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { struct jffs2_node_frag *prev = frag_prev(newfrag); mark_ref_normal(fn->raw); /* If we don't start at zero there's _always_ a previous */ if (prev->node) mark_ref_normal(prev->node->raw); } if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { struct jffs2_node_frag *next = frag_next(newfrag); if (next) { mark_ref_normal(fn->raw); if (next->node) mark_ref_normal(next->node->raw); } } jffs2_dbg_fragtree_paranoia_check_nolock(f); return 0; } /* * Check the data CRC of the node. * * Returns: 0 if the data CRC is correct; * 1 - if incorrect; * error code if an error occured. */ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) { struct jffs2_raw_node_ref *ref = tn->fn->raw; int err = 0, pointed = 0; struct jffs2_eraseblock *jeb; unsigned char *buffer; uint32_t crc, ofs, len; size_t retlen; BUG_ON(tn->csize == 0); if (!jffs2_is_writebuffered(c)) goto adj_acc; /* Calculate how many bytes were already checked */ ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode); len = ofs % c->wbuf_pagesize; if (likely(len)) len = c->wbuf_pagesize - len; if (len >= tn->csize) { dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", ref_offset(ref), tn->csize, ofs); goto adj_acc; } ofs += len; len = tn->csize - len; dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n", ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len); #ifndef __ECOS /* TODO: instead, incapsulate point() stuff to jffs2_flash_read(), * adding and jffs2_flash_read_end() interface. */ if (c->mtd->point) { err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); if (!err && retlen < tn->csize) { JFFS2_WARNING("MTD point returned len too short: %zu " "instead of %u.\n", retlen, tn->csize); c->mtd->unpoint(c->mtd, buffer, ofs, len); } else if (err) JFFS2_WARNING("MTD point failed: error code %d.\n", err); else pointed = 1; /* succefully pointed to device */ } #endif if (!pointed) { buffer = kmalloc(len, GFP_KERNEL); if (unlikely(!buffer)) return -ENOMEM; /* TODO: this is very frequent pattern, make it a separate * routine */ err = jffs2_flash_read(c, ofs, len, &retlen, buffer); if (err) { JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err); goto free_out; } if (retlen != len) { JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len); err = -EIO; goto free_out; } } /* Continue calculating CRC */ crc = crc32(tn->partial_crc, buffer, len); if(!pointed) kfree(buffer); #ifndef __ECOS else c->mtd->unpoint(c->mtd, buffer, ofs, len); #endif if (crc != tn->data_crc) { JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", ofs, tn->data_crc, crc); return 1; } adj_acc: jeb = &c->blocks[ref->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, ref); /* * Mark the node as having been checked and fix the * accounting accordingly. */ spin_lock(&c->erase_completion_lock); jeb->used_size += len; jeb->unchecked_size -= len; c->used_size += len; c->unchecked_size -= len; spin_unlock(&c->erase_completion_lock); return 0; free_out: if(!pointed) kfree(buffer); #ifndef __ECOS else c->mtd->unpoint(c->mtd, buffer, ofs, len); #endif return err; } /* * Helper function for jffs2_add_older_frag_to_fragtree(). * * Checks the node if we are in the checking stage. */ static int check_node(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn) { int ret; BUG_ON(ref_obsolete(tn->fn->raw)); /* We only check the data CRC of unchecked nodes */ if (ref_flags(tn->fn->raw) != REF_UNCHECKED) return 0; dbg_fragtree2("check node %#04x-%#04x, phys offs %#08x.\n", tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw)); ret = check_node_data(c, tn); if (unlikely(ret < 0)) { JFFS2_ERROR("check_node_data() returned error: %d.\n", ret); } else if (unlikely(ret > 0)) { dbg_fragtree2("CRC error, mark it obsolete.\n"); jffs2_mark_node_obsolete(c, tn->fn->raw); } return ret; } /* * Helper function for jffs2_add_older_frag_to_fragtree(). * * Called when the new fragment that is being inserted * splits a hole fragment. */ static int split_hole(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag, struct jffs2_node_frag *hole) { dbg_fragtree2("fragment %#04x-%#04x splits the hole %#04x-%#04x\n", newfrag->ofs, newfrag->ofs + newfrag->size, hole->ofs, hole->ofs + hole->size); if (hole->ofs == newfrag->ofs) { /* * Well, the new fragment actually starts at the same offset as * the hole. */ if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { /* * We replace the overlapped left part of the hole by * the new node. */ dbg_fragtree2("insert fragment %#04x-%#04x and cut the left part of the hole\n", newfrag->ofs, newfrag->ofs + newfrag->size); rb_replace_node(&hole->rb, &newfrag->rb, root); hole->ofs += newfrag->size; hole->size -= newfrag->size; /* * We know that 'hole' should be the right hand * fragment. */ jffs2_fragtree_insert(hole, newfrag); rb_insert_color(&hole->rb, root); } else { /* * Ah, the new fragment is of the same size as the hole. * Relace the hole by it. */ dbg_fragtree2("insert fragment %#04x-%#04x and overwrite hole\n", newfrag->ofs, newfrag->ofs + newfrag->size); rb_replace_node(&hole->rb, &newfrag->rb, root); jffs2_free_node_frag(hole); } } else { /* The new fragment lefts some hole space at the left */ struct jffs2_node_frag * newfrag2 = NULL; if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { /* The new frag also lefts some space at the right */ newfrag2 = new_fragment(NULL, newfrag->ofs + newfrag->size, hole->ofs + hole->size - newfrag->ofs - newfrag->size); if (unlikely(!newfrag2)) { jffs2_free_node_frag(newfrag); return -ENOMEM; } } hole->size = newfrag->ofs - hole->ofs; dbg_fragtree2("left the hole %#04x-%#04x at the left and inserd fragment %#04x-%#04x\n", hole->ofs, hole->ofs + hole->size, newfrag->ofs, newfrag->ofs + newfrag->size); jffs2_fragtree_insert(newfrag, hole); rb_insert_color(&newfrag->rb, root); if (newfrag2) { dbg_fragtree2("left the hole %#04x-%#04x at the right\n", newfrag2->ofs, newfrag2->ofs + newfrag2->size); jffs2_fragtree_insert(newfrag2, newfrag); rb_insert_color(&newfrag2->rb, root); } } return 0; } /* * This function is used when we build inode. It expects the nodes are passed * in the decreasing version order. The whole point of this is to improve the * inodes checking on NAND: we check the nodes' data CRC only when they are not * obsoleted. Previously, add_frag_to_fragtree() function was used and * nodes were passed to it in the increasing version ordes and CRCs of all * nodes were checked. * * Note: tn->fn->size shouldn't be zero. * * Returns 0 if the node was inserted * 1 if it wasn't inserted (since it is obsolete) * < 0 an if error occured */ int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn) { struct jffs2_node_frag *this, *newfrag; uint32_t lastend; struct jffs2_full_dnode *fn = tn->fn; struct rb_root *root = &f->fragtree; uint32_t fn_size = fn->size, fn_ofs = fn->ofs; int err, checked = 0; int ref_flag; dbg_fragtree("insert fragment %#04x-%#04x, ver %u\n", fn_ofs, fn_ofs + fn_size, tn->version); /* Skip all the nodes which are completed before this one starts */ this = jffs2_lookup_node_frag(root, fn_ofs); if (this) dbg_fragtree2("'this' found %#04x-%#04x (%s)\n", this->ofs, this->ofs + this->size, this->node ? "data" : "hole"); if (this) lastend = this->ofs + this->size; else lastend = 0; /* Detect the preliminary type of node */ if (fn->size >= PAGE_CACHE_SIZE) ref_flag = REF_PRISTINE; else ref_flag = REF_NORMAL; /* See if we ran off the end of the root */ if (lastend <= fn_ofs) { /* We did */ /* * We are going to insert the new node into the * fragment tree, so check it. */ err = check_node(c, f, tn); if (err != 0) return err; fn->frags = 1; newfrag = new_fragment(fn, fn_ofs, fn_size); if (unlikely(!newfrag)) return -ENOMEM; err = no_overlapping_node(c, root, newfrag, this, lastend); if (unlikely(err != 0)) { jffs2_free_node_frag(newfrag); return err; } goto out_ok; } fn->frags = 0; while (1) { /* * Here we have: * fn_ofs < this->ofs + this->size && fn_ofs >= this->ofs. * * Remember, 'this' has higher version, any non-hole node * which is already in the fragtree is newer then the newly * inserted. */ if (!this->node) { /* * 'this' is the hole fragment, so at least the * beginning of the new fragment is valid. */ /* * We are going to insert the new node into the * fragment tree, so check it. */ if (!checked) { err = check_node(c, f, tn); if (unlikely(err != 0)) return err; checked = 1; } if (this->ofs + this->size >= fn_ofs + fn_size) { /* We split the hole on two parts */ fn->frags += 1; newfrag = new_fragment(fn, fn_ofs, fn_size); if (unlikely(!newfrag)) return -ENOMEM; err = split_hole(c, root, newfrag, this); if (unlikely(err)) return err; goto out_ok; } /* * The beginning of the new fragment is valid since it * overlaps the hole node. */ ref_flag = REF_NORMAL; fn->frags += 1; newfrag = new_fragment(fn, fn_ofs, this->ofs + this->size - fn_ofs); if (unlikely(!newfrag)) return -ENOMEM; if (fn_ofs == this->ofs) { /* * The new node starts at the same offset as * the hole and supersieds the hole. */ dbg_fragtree2("add the new fragment instead of hole %#04x-%#04x, refcnt %d\n", fn_ofs, fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); rb_replace_node(&this->rb, &newfrag->rb, root); jffs2_free_node_frag(this); } else { /* * The hole becomes shorter as its right part * is supersieded by the new fragment. */ dbg_fragtree2("reduce size of hole %#04x-%#04x to %#04x-%#04x\n", this->ofs, this->ofs + this->size, this->ofs, this->ofs + this->size - newfrag->size); dbg_fragtree2("add new fragment %#04x-%#04x, refcnt %d\n", fn_ofs, fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); this->size -= newfrag->size; jffs2_fragtree_insert(newfrag, this); rb_insert_color(&newfrag->rb, root); } fn_ofs += newfrag->size; fn_size -= newfrag->size; this = rb_entry(rb_next(&newfrag->rb), struct jffs2_node_frag, rb); dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); } /* * 'This' node is not the hole so it obsoletes the new fragment * either fully or partially. */ if (this->ofs + this->size >= fn_ofs + fn_size) { /* The new node is obsolete, drop it */ if (fn->frags == 0) { dbg_fragtree2("%#04x-%#04x is obsolete, mark it obsolete\n", fn_ofs, fn_ofs + fn_size); ref_flag = REF_OBSOLETE; } goto out_ok; } else { struct jffs2_node_frag *new_this; /* 'This' node obsoletes the beginning of the new node */ dbg_fragtree2("the beginning %#04x-%#04x is obsolete\n", fn_ofs, this->ofs + this->size); ref_flag = REF_NORMAL; fn_size -= this->ofs + this->size - fn_ofs; fn_ofs = this->ofs + this->size; dbg_fragtree2("now considering %#04x-%#04x\n", fn_ofs, fn_ofs + fn_size); new_this = rb_entry(rb_next(&this->rb), struct jffs2_node_frag, rb); if (!new_this) { /* * There is no next fragment. Add the rest of * the new node as the right-hand child. */ if (!checked) { err = check_node(c, f, tn); if (unlikely(err != 0)) return err; checked = 1; } fn->frags += 1; newfrag = new_fragment(fn, fn_ofs, fn_size); if (unlikely(!newfrag)) return -ENOMEM; dbg_fragtree2("there are no more fragments, insert %#04x-%#04x\n", newfrag->ofs, newfrag->ofs + newfrag->size); rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); rb_insert_color(&newfrag->rb, root); goto out_ok; } else { this = new_this; dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); } } } out_ok: BUG_ON(fn->size < PAGE_CACHE_SIZE && ref_flag == REF_PRISTINE); if (ref_flag == REF_OBSOLETE) { dbg_fragtree2("the node is obsolete now\n"); /* jffs2_mark_node_obsolete() will adjust space accounting */ jffs2_mark_node_obsolete(c, fn->raw); return 1; } dbg_fragtree2("the node is \"%s\" now\n", ref_flag == REF_NORMAL ? "REF_NORMAL" : "REF_PRISTINE"); /* Space accounting was adjusted at check_node_data() */ spin_lock(&c->erase_completion_lock); fn->raw->flash_offset = ref_offset(fn->raw) | ref_flag; spin_unlock(&c->erase_completion_lock); return 0; } void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) { spin_lock(&c->inocache_lock); ic->state = state; wake_up(&c->inocache_wq); spin_unlock(&c->inocache_lock); } /* During mount, this needs no locking. During normal operation, its callers want to do other stuff while still holding the inocache_lock. Rather than introducing special case get_ino_cache functions or callbacks, we just let the caller do the locking itself. */ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inode_cache *ret; ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; while (ret && ret->ino < ino) { ret = ret->next; } if (ret && ret->ino != ino) ret = NULL; return ret; } void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new) { struct jffs2_inode_cache **prev; spin_lock(&c->inocache_lock); if (!new->ino) new->ino = ++c->highest_ino; dbg_inocache("add %p (ino #%u)\n", new, new->ino); prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE]; while ((*prev) && (*prev)->ino < new->ino) { prev = &(*prev)->next; } new->next = *prev; *prev = new; spin_unlock(&c->inocache_lock); } void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old) { struct jffs2_inode_cache **prev; dbg_inocache("del %p (ino #%u)\n", old, old->ino); spin_lock(&c->inocache_lock); prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE]; while ((*prev) && (*prev)->ino < old->ino) { prev = &(*prev)->next; } if ((*prev) == old) { *prev = old->next; } /* Free it now unless it's in READING or CLEARING state, which are the transitions upon read_inode() and clear_inode(). The rest of the time we know nobody else is looking at it, and if it's held by read_inode() or clear_inode() they'll free it for themselves. */ if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING) jffs2_free_inode_cache(old); spin_unlock(&c->inocache_lock); } void jffs2_free_ino_caches(struct jffs2_sb_info *c) { int i; struct jffs2_inode_cache *this, *next; for (i=0; iinocache_list[i]; while (this) { next = this->next; jffs2_free_inode_cache(this); this = next; } c->inocache_list[i] = NULL; } } void jffs2_free_raw_node_refs(struct jffs2_sb_info *c) { int i; struct jffs2_raw_node_ref *this, *next; for (i=0; inr_blocks; i++) { this = c->blocks[i].first_node; while(this) { next = this->next_phys; jffs2_free_raw_node_ref(this); this = next; } c->blocks[i].first_node = c->blocks[i].last_node = NULL; } } struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset) { /* The common case in lookup is that there will be a node which precisely matches. So we go looking for that first */ struct rb_node *next; struct jffs2_node_frag *prev = NULL; struct jffs2_node_frag *frag = NULL; dbg_fragtree2("root %p, offset %d\n", fragtree, offset); next = fragtree->rb_node; while(next) { frag = rb_entry(next, struct jffs2_node_frag, rb); if (frag->ofs + frag->size <= offset) { /* Remember the closest smaller match on the way down */ if (!prev || frag->ofs > prev->ofs) prev = frag; next = frag->rb.rb_right; } else if (frag->ofs > offset) { next = frag->rb.rb_left; } else { return frag; } } /* Exact match not found. Go back up looking at each parent, and return the closest smaller one */ if (prev) dbg_fragtree2("no match. Returning frag %#04x-%#04x, closest previous\n", prev->ofs, prev->ofs+prev->size); else dbg_fragtree2("returning NULL, empty fragtree\n"); return prev; } /* Pass 'c' argument to indicate that nodes should be marked obsolete as they're killed. */ void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c) { struct jffs2_node_frag *frag; struct jffs2_node_frag *parent; if (!root->rb_node) return; dbg_fragtree("killing\n"); frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); while(frag) { if (frag->rb.rb_left) { frag = frag_left(frag); continue; } if (frag->rb.rb_right) { frag = frag_right(frag); continue; } if (frag->node && !(--frag->node->frags)) { /* Not a hole, and it's the final remaining frag of this node. Free the node */ if (c) jffs2_mark_node_obsolete(c, frag->node->raw); jffs2_free_full_dnode(frag->node); } parent = frag_parent(frag); if (parent) { if (frag_left(parent) == frag) parent->rb.rb_left = NULL; else parent->rb.rb_right = NULL; } jffs2_free_node_frag(frag); frag = parent; cond_resched(); } } linux-2.6.17/fs/jffs2/fs.c0000644000000000000000000004424210564537530013660 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: fs.c,v 1.66 2005/09/27 13:17:29 dedekind Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "nodelist.h" static int jffs2_flash_setup(struct jffs2_sb_info *c); static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) { struct jffs2_full_dnode *old_metadata, *new_metadata; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; unsigned short dev; unsigned char *mdata = NULL; int mdatalen = 0; unsigned int ivalid; uint32_t phys_ofs, alloclen; int ret; D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); ret = inode_change_ok(inode, iattr); if (ret) return ret; /* Special cases - we don't want more than one data node for these types on the medium at any time. So setattr must read the original data associated with the node (i.e. the device numbers or the target name) and write it out again with the appropriate data attached */ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ dev = old_encode_dev(inode->i_rdev); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(inode->i_mode)) { mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) return -ENOMEM; ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { kfree(mdata); return ret; } D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); } ri = jffs2_alloc_raw_inode(); if (!ri) { if (S_ISLNK(inode->i_mode)) kfree(mdata); return -ENOMEM; } ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); if (S_ISLNK(inode->i_mode & S_IFMT)) kfree(mdata); return ret; } down(&f->sem); ivalid = iattr->ia_valid; ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->ino = cpu_to_je32(inode->i_ino); ri->version = cpu_to_je32(++f->highest_version); ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid); ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid); if (ivalid & ATTR_MODE) if (iattr->ia_mode & S_ISGID && !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID)) ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID); else ri->mode = cpu_to_jemode(iattr->ia_mode); else ri->mode = cpu_to_jemode(inode->i_mode); ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size); ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime)); ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime)); ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime)); ri->offset = cpu_to_je32(0); ri->csize = ri->dsize = cpu_to_je32(mdatalen); ri->compr = JFFS2_COMPR_NONE; if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { /* It's an extension. Make it a hole node */ ri->compr = JFFS2_COMPR_ZERO; ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ri->offset = cpu_to_je32(inode->i_size); } ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); if (mdatalen) ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); else ri->data_crc = cpu_to_je32(0); new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL); if (S_ISLNK(inode->i_mode)) kfree(mdata); if (IS_ERR(new_metadata)) { jffs2_complete_reservation(c); jffs2_free_raw_inode(ri); up(&f->sem); return PTR_ERR(new_metadata); } /* It worked. Update the inode */ inode->i_atime = ITIME(je32_to_cpu(ri->atime)); inode->i_ctime = ITIME(je32_to_cpu(ri->ctime)); inode->i_mtime = ITIME(je32_to_cpu(ri->mtime)); inode->i_mode = jemode_to_cpu(ri->mode); inode->i_uid = je16_to_cpu(ri->uid); inode->i_gid = je16_to_cpu(ri->gid); old_metadata = f->metadata; if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) jffs2_truncate_fragtree (c, &f->fragtree, iattr->ia_size); if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { jffs2_add_full_dnode_to_inode(c, f, new_metadata); inode->i_size = iattr->ia_size; f->metadata = NULL; } else { f->metadata = new_metadata; } if (old_metadata) { jffs2_mark_node_obsolete(c, old_metadata->raw); jffs2_free_full_dnode(old_metadata); } jffs2_free_raw_inode(ri); up(&f->sem); jffs2_complete_reservation(c); /* We have to do the vmtruncate() without f->sem held, since some pages may be locked and waiting for it in readpage(). We are protected from a simultaneous write() extending i_size back past iattr->ia_size, because do_truncate() holds the generic inode semaphore. */ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) vmtruncate(inode, iattr->ia_size); return 0; } int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) { return jffs2_do_setattr(dentry->d_inode, iattr); } int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); unsigned long avail; buf->f_type = JFFS2_SUPER_MAGIC; buf->f_bsize = 1 << PAGE_SHIFT; buf->f_blocks = c->flash_size >> PAGE_SHIFT; buf->f_files = 0; buf->f_ffree = 0; buf->f_namelen = JFFS2_MAX_NAME_LEN; spin_lock(&c->erase_completion_lock); avail = c->dirty_size + c->free_size; if (avail > c->sector_size * c->resv_blocks_write) avail -= c->sector_size * c->resv_blocks_write; else avail = 0; spin_unlock(&c->erase_completion_lock); buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; return 0; } void jffs2_clear_inode (struct inode *inode) { /* We can forget about this inode for now - drop all * the nodelists associated with it, etc. */ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); jffs2_do_clear_inode(c, f); } void jffs2_read_inode (struct inode *inode) { struct jffs2_inode_info *f; struct jffs2_sb_info *c; struct jffs2_raw_inode latest_node; int ret; D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); f = JFFS2_INODE_INFO(inode); c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); down(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); if (ret) { make_bad_inode(inode); up(&f->sem); return; } inode->i_mode = jemode_to_cpu(latest_node.mode); inode->i_uid = je16_to_cpu(latest_node.uid); inode->i_gid = je16_to_cpu(latest_node.gid); inode->i_size = je32_to_cpu(latest_node.isize); inode->i_atime = ITIME(je32_to_cpu(latest_node.atime)); inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime)); inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime)); inode->i_nlink = f->inocache->nlink; inode->i_blksize = PAGE_SIZE; inode->i_blocks = (inode->i_size + 511) >> 9; switch (inode->i_mode & S_IFMT) { jint16_t rdev; case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; break; case S_IFDIR: { struct jffs2_full_dirent *fd; for (fd=f->dents; fd; fd = fd->next) { if (fd->type == DT_DIR && fd->ino) inode->i_nlink++; } /* and '..' */ inode->i_nlink++; /* Root dir gets i_nlink 3 for some reason */ if (inode->i_ino == 1) inode->i_nlink++; inode->i_op = &jffs2_dir_inode_operations; inode->i_fop = &jffs2_dir_operations; break; } case S_IFREG: inode->i_op = &jffs2_file_inode_operations; inode->i_fop = &jffs2_file_operations; inode->i_mapping->a_ops = &jffs2_file_address_operations; inode->i_mapping->nrpages = 0; break; case S_IFBLK: case S_IFCHR: /* Read the device numbers from the media */ D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); up(&f->sem); jffs2_do_clear_inode(c, f); make_bad_inode(inode); return; } case S_IFSOCK: case S_IFIFO: inode->i_op = &jffs2_file_inode_operations; init_special_inode(inode, inode->i_mode, old_decode_dev((je16_to_cpu(rdev)))); break; default: printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); } up(&f->sem); D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); } void jffs2_dirty_inode(struct inode *inode) { struct iattr iattr; if (!(inode->i_state & I_DIRTY_DATASYNC)) { D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); return; } D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino)); iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME; iattr.ia_mode = inode->i_mode; iattr.ia_uid = inode->i_uid; iattr.ia_gid = inode->i_gid; iattr.ia_atime = inode->i_atime; iattr.ia_mtime = inode->i_mtime; iattr.ia_ctime = inode->i_ctime; jffs2_do_setattr(inode, &iattr); } int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) return -EROFS; /* We stop if it was running, then restart if it needs to. This also catches the case where it was stopped and this is just a remount to restart it. Flush the writebuffer, if neccecary, else we loose it */ if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); } if (!(*flags & MS_RDONLY)) jffs2_start_garbage_collect_thread(c); *flags |= MS_NOATIME; return 0; } void jffs2_write_super (struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); sb->s_dirt = 0; if (sb->s_flags & MS_RDONLY) return; D1(printk(KERN_DEBUG "jffs2_write_super()\n")); jffs2_garbage_collect_trigger(c); jffs2_erase_pending_blocks(c, 0); jffs2_flush_wbuf_gc(c, 0); } /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, fill in the raw_inode while you're at it. */ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) { struct inode *inode; struct super_block *sb = dir_i->i_sb; struct jffs2_sb_info *c; struct jffs2_inode_info *f; int ret; D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); c = JFFS2_SB_INFO(sb); inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); f = JFFS2_INODE_INFO(inode); jffs2_init_inode_info(f); down(&f->sem); memset(ri, 0, sizeof(*ri)); /* Set OS-specific defaults for new inodes */ ri->uid = cpu_to_je16(current->fsuid); if (dir_i->i_mode & S_ISGID) { ri->gid = cpu_to_je16(dir_i->i_gid); if (S_ISDIR(mode)) mode |= S_ISGID; } else { ri->gid = cpu_to_je16(current->fsgid); } ri->mode = cpu_to_jemode(mode); ret = jffs2_do_new_inode (c, f, mode, ri); if (ret) { make_bad_inode(inode); iput(inode); return ERR_PTR(ret); } inode->i_nlink = 1; inode->i_ino = je32_to_cpu(ri->ino); inode->i_mode = jemode_to_cpu(ri->mode); inode->i_gid = je16_to_cpu(ri->gid); inode->i_uid = je16_to_cpu(ri->uid); inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); inode->i_blksize = PAGE_SIZE; inode->i_blocks = 0; inode->i_size = 0; insert_inode_hash(inode); return inode; } int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) { struct jffs2_sb_info *c; struct inode *root_i; int ret; size_t blocks; c = JFFS2_SB_INFO(sb); #ifndef CONFIG_JFFS2_FS_WRITEBUFFER if (c->mtd->type == MTD_NANDFLASH) { printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n"); return -EINVAL; } if (c->mtd->type == MTD_DATAFLASH) { printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n"); return -EINVAL; } #endif c->flash_size = c->mtd->size; c->sector_size = c->mtd->erasesize; blocks = c->flash_size / c->sector_size; /* * Size alignment check */ if ((c->sector_size * blocks) != c->flash_size) { c->flash_size = c->sector_size * blocks; printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n", c->flash_size / 1024); } if (c->flash_size < 5*c->sector_size) { printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size); return -EINVAL; } c->cleanmarker_size = sizeof(struct jffs2_unknown_node); /* NAND (or other bizarre) flash... do setup accordingly */ ret = jffs2_flash_setup(c); if (ret) return ret; c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); if (!c->inocache_list) { ret = -ENOMEM; goto out_wbuf; } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; ret = -EINVAL; D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); root_i = iget(sb, 1); if (is_bad_inode(root_i)) { D1(printk(KERN_WARNING "get root inode failed\n")); goto out_root_i; } D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); sb->s_root = d_alloc_root(root_i); if (!sb->s_root) goto out_root_i; sb->s_maxbytes = 0xFFFFFFFF; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = JFFS2_SUPER_MAGIC; if (!(sb->s_flags & MS_RDONLY)) jffs2_start_garbage_collect_thread(c); return 0; out_root_i: iput(root_i); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); if (jffs2_blocks_use_vmalloc(c)) vfree(c->blocks); else kfree(c->blocks); out_inohash: kfree(c->inocache_list); out_wbuf: jffs2_flash_cleanup(c); return ret; } void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { iput(OFNI_EDONI_2SFFJ(f)); } struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink) { struct inode *inode; struct jffs2_inode_cache *ic; if (!nlink) { /* The inode has zero nlink but its nodes weren't yet marked obsolete. This has to be because we're still waiting for the final (close() and) iput() to happen. There's a possibility that the final iput() could have happened while we were contemplating. In order to ensure that we don't cause a new read_inode() (which would fail) for the inode in question, we use ilookup() in this case instead of iget(). The nlink can't _become_ zero at this point because we're holding the alloc_sem, and jffs2_do_unlink() would also need that while decrementing nlink on any inode. */ inode = ilookup(OFNI_BS_2SFFJ(c), inum); if (!inode) { D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", inum)); spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, inum); if (!ic) { D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); spin_unlock(&c->inocache_lock); return NULL; } if (ic->state != INO_STATE_CHECKEDABSENT) { /* Wait for progress. Don't just loop */ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); } else { spin_unlock(&c->inocache_lock); } return NULL; } } else { /* Inode has links to it still; they're not going away because jffs2_do_unlink() would need the alloc_sem and we have it. Just iget() it, and if read_inode() is necessary that's OK. */ inode = iget(OFNI_BS_2SFFJ(c), inum); if (!inode) return ERR_PTR(-ENOMEM); } if (is_bad_inode(inode)) { printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", inum, nlink); /* NB. This will happen again. We need to do something appropriate here. */ iput(inode); return ERR_PTR(-EIO); } return JFFS2_INODE_INFO(inode); } unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned long offset, unsigned long *priv) { struct inode *inode = OFNI_EDONI_2SFFJ(f); struct page *pg; pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); if (IS_ERR(pg)) return (void *)pg; *priv = (unsigned long)pg; return kmap(pg); } void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *ptr, unsigned long *priv) { struct page *pg = (void *)*priv; kunmap(pg); page_cache_release(pg); } static int jffs2_flash_setup(struct jffs2_sb_info *c) { int ret = 0; if (jffs2_cleanmarker_oob(c)) { /* NAND flash... do setup accordingly */ ret = jffs2_nand_flash_setup(c); if (ret) return ret; } /* add setups for other bizarre flashes here... */ if (jffs2_nor_ecc(c)) { ret = jffs2_nor_ecc_flash_setup(c); if (ret) return ret; } /* and Dataflash */ if (jffs2_dataflash(c)) { ret = jffs2_dataflash_setup(c); if (ret) return ret; } /* and Intel "Sibley" flash */ if (jffs2_nor_wbuf_flash(c)) { ret = jffs2_nor_wbuf_flash_setup(c); if (ret) return ret; } return ret; } void jffs2_flash_cleanup(struct jffs2_sb_info *c) { if (jffs2_cleanmarker_oob(c)) { jffs2_nand_flash_cleanup(c); } /* add cleanups for other bizarre flashes here... */ if (jffs2_nor_ecc(c)) { jffs2_nor_ecc_flash_cleanup(c); } /* and DataFlash */ if (jffs2_dataflash(c)) { jffs2_dataflash_cleanup(c); } /* and Intel "Sibley" flash */ if (jffs2_nor_wbuf_flash(c)) { jffs2_nor_wbuf_flash_cleanup(c); } } linux-2.6.17/fs/jffs2/Makefile0000644000000000000000000000114110564537530014533 0ustar rootroot# # Makefile for the Linux Journalling Flash File System v2 (JFFS2) # # $Id: Makefile.common,v 1.11 2005/09/07 08:34:53 havasi Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o debug.o jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o linux-2.6.17/fs/jffs2/file.c0000644000000000000000000002023410564537530014162 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: file.c,v 1.104 2005/10/18 23:29:35 tpoynor Exp $ * */ #include #include #include #include #include #include #include #include #include "nodelist.h" static int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end); static int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end); static int jffs2_readpage (struct file *filp, struct page *pg); int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); /* Trigger GC to flush any pending writes for this inode */ jffs2_flush_wbuf_gc(c, inode->i_ino); return 0; } const struct file_operations jffs2_file_operations = { .llseek = generic_file_llseek, .open = generic_file_open, .read = generic_file_read, .write = generic_file_write, .ioctl = jffs2_ioctl, .mmap = generic_file_readonly_mmap, .fsync = jffs2_fsync, .sendfile = generic_file_sendfile }; /* jffs2_file_inode_operations */ struct inode_operations jffs2_file_inode_operations = { .setattr = jffs2_setattr }; struct address_space_operations jffs2_file_address_operations = { .readpage = jffs2_readpage, .prepare_write =jffs2_prepare_write, .commit_write = jffs2_commit_write }; static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); unsigned char *pg_buf; int ret; D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); BUG_ON(!PageLocked(pg)); pg_buf = kmap(pg); /* FIXME: Can kmap fail? */ ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); if (ret) { ClearPageUptodate(pg); SetPageError(pg); } else { SetPageUptodate(pg); ClearPageError(pg); } flush_dcache_page(pg); kunmap(pg); D2(printk(KERN_DEBUG "readpage finished\n")); return 0; } int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg) { int ret = jffs2_do_readpage_nolock(inode, pg); unlock_page(pg); return ret; } static int jffs2_readpage (struct file *filp, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; down(&f->sem); ret = jffs2_do_readpage_unlock(pg->mapping->host, pg); up(&f->sem); return ret; } static int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end) { struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; int ret = 0; D1(printk(KERN_DEBUG "jffs2_prepare_write()\n")); if (pageofs > inode->i_size) { /* Make new hole frag from old EOF to new page */ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode ri; struct jffs2_full_dnode *fn; uint32_t phys_ofs, alloc_len; D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", (unsigned int)inode->i_size, pageofs)); ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) return ret; down(&f->sem); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.mode = cpu_to_jemode(inode->i_mode); ri.uid = cpu_to_je16(inode->i_uid); ri.gid = cpu_to_je16(inode->i_gid); ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs)); ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds()); ri.offset = cpu_to_je32(inode->i_size); ri.dsize = cpu_to_je32(pageofs - inode->i_size); ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(0); fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fn)) { ret = PTR_ERR(fn); jffs2_complete_reservation(c); up(&f->sem); return ret; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } if (ret) { D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); jffs2_complete_reservation(c); up(&f->sem); return ret; } jffs2_complete_reservation(c); inode->i_size = pageofs; up(&f->sem); } /* Read in the page if it wasn't already present, unless it's a whole page */ if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { down(&f->sem); ret = jffs2_do_readpage_nolock(inode, pg); up(&f->sem); } D1(printk(KERN_DEBUG "end prepare_write(). pg->flags %lx\n", pg->flags)); return ret; } static int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple */ struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; unsigned aligned_start = start & ~3; int ret = 0; uint32_t writtenlen = 0; D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); if (!start && end == PAGE_CACHE_SIZE) { /* We need to avoid deadlock with page_cache_read() in jffs2_garbage_collect_pass(). So we have to mark the page up to date, to prevent page_cache_read() from trying to re-lock it. */ SetPageUptodate(pg); } ri = jffs2_alloc_raw_inode(); if (!ri) { D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n")); return -ENOMEM; } /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ri->ino = cpu_to_je32(inode->i_ino); ri->mode = cpu_to_jemode(inode->i_mode); ri->uid = cpu_to_je16(inode->i_uid); ri->gid = cpu_to_je16(inode->i_gid); ri->isize = cpu_to_je32((uint32_t)inode->i_size); ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds()); /* In 2.4, it was already kmapped by generic_file_write(). Doesn't hurt to do it again. The alternative is ifdefs, which are ugly. */ kmap(pg); ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start, (pg->index << PAGE_CACHE_SHIFT) + aligned_start, end - aligned_start, &writtenlen); kunmap(pg); if (ret) { /* There was an error writing. */ SetPageError(pg); } /* Adjust writtenlen for the padding we did, so we don't confuse our caller */ if (writtenlen < (start&3)) writtenlen = 0; else writtenlen -= (start&3); if (writtenlen) { if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; inode->i_blocks = (inode->i_size + 511) >> 9; inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); } } jffs2_free_raw_inode(ri); if (start+writtenlen < end) { /* generic_file_write has written more to the page cache than we've actually written to the medium. Mark the page !Uptodate so that it gets reread */ D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate\n")); SetPageError(pg); ClearPageUptodate(pg); } D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",start+writtenlen==end?0:ret)); return start+writtenlen==end?0:ret; } linux-2.6.17/fs/jffs2/nodelist.h0000644000000000000000000003673110564537530015102 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: nodelist.h,v 1.140 2005/09/07 08:34:54 havasi Exp $ * */ #ifndef __JFFS2_NODELIST_H__ #define __JFFS2_NODELIST_H__ #include #include #include #include #include #include #include "summary.h" #ifdef __ECOS #include "os-ecos.h" #else #include /* For compatibility with older kernels */ #include "os-linux.h" #endif #define JFFS2_NATIVE_ENDIAN /* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from whatever OS we're actually running on here too. */ #if defined(JFFS2_NATIVE_ENDIAN) #define cpu_to_je16(x) ((jint16_t){x}) #define cpu_to_je32(x) ((jint32_t){x}) #define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) #define je16_to_cpu(x) ((x).v16) #define je32_to_cpu(x) ((x).v32) #define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) #elif defined(JFFS2_BIG_ENDIAN) #define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) #define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) #define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) #define je16_to_cpu(x) (be16_to_cpu(x.v16)) #define je32_to_cpu(x) (be32_to_cpu(x.v32)) #define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) #elif defined(JFFS2_LITTLE_ENDIAN) #define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) #define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) #define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) #define je16_to_cpu(x) (le16_to_cpu(x.v16)) #define je32_to_cpu(x) (le32_to_cpu(x.v32)) #define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) #else #error wibble #endif /* The minimal node header size */ #define JFFS2_MIN_NODE_HEADER sizeof(struct jffs2_raw_dirent) /* This is all we need to keep in-core for each raw node during normal operation. As and when we do read_inode on a particular inode, we can scan the nodes which are listed for it and build up a proper map of which nodes are currently valid. JFFSv1 always used to keep that whole map in core for each inode. */ struct jffs2_raw_node_ref { struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref for this inode. If this is the last, it points to the inode_cache for this inode instead. The inode_cache will have NULL in the first word so you know when you've got there :) */ struct jffs2_raw_node_ref *next_phys; uint32_t flash_offset; uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ }; /* flash_offset & 3 always has to be zero, because nodes are always aligned at 4 bytes. So we have a couple of extra bits to play with, which indicate the node's status; see below: */ #define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ #define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */ #define REF_PRISTINE 2 /* Completely clean. GC without looking */ #define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */ #define ref_flags(ref) ((ref)->flash_offset & 3) #define ref_offset(ref) ((ref)->flash_offset & ~3) #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) /* For each inode in the filesystem, we need to keep a record of nlink, because it would be a PITA to scan the whole directory tree at read_inode() time to calculate it, and to keep sufficient information in the raw_node_ref (basically both parent and child inode number for dirent nodes) would take more space than this does. We also keep a pointer to the first physical node which is part of this inode, too. */ struct jffs2_inode_cache { struct jffs2_full_dirent *scan_dents; /* Used during scan to hold temporary lists of dirents, and later must be set to NULL to mark the end of the raw_node_ref->next_in_ino chain. */ struct jffs2_inode_cache *next; struct jffs2_raw_node_ref *nodes; uint32_t ino; int nlink; int state; }; /* Inode states for 'state' above. We need the 'GC' state to prevent someone from doing a read_inode() while we're moving a 'REF_PRISTINE' node without going through all the iget() nonsense */ #define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */ #define INO_STATE_CHECKING 1 /* CRC checks in progress */ #define INO_STATE_PRESENT 2 /* In core */ #define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */ #define INO_STATE_GC 4 /* GCing a 'pristine' node */ #define INO_STATE_READING 5 /* In read_inode() */ #define INO_STATE_CLEARING 6 /* In clear_inode() */ #define INOCACHE_HASHSIZE 128 /* Larger representation of a raw node, kept in-core only when the struct inode for this particular ino is instantiated. */ struct jffs2_full_dnode { struct jffs2_raw_node_ref *raw; uint32_t ofs; /* The offset to which the data of this node belongs */ uint32_t size; uint32_t frags; /* Number of fragments which currently refer to this node. When this reaches zero, the node is obsolete. */ }; /* Even larger representation of a raw node, kept in-core only while we're actually building up the original map of which nodes go where, in read_inode() */ struct jffs2_tmp_dnode_info { struct rb_node rb; struct jffs2_full_dnode *fn; uint32_t version; uint32_t data_crc; uint32_t partial_crc; uint32_t csize; }; struct jffs2_full_dirent { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *next; uint32_t version; uint32_t ino; /* == zero for unlink */ unsigned int nhash; unsigned char type; unsigned char name[0]; }; /* Fragments - used to build a map of which raw node to obtain data from for each part of the ino */ struct jffs2_node_frag { struct rb_node rb; struct jffs2_full_dnode *node; /* NULL for holes */ uint32_t size; uint32_t ofs; /* The offset to which this fragment belongs */ }; struct jffs2_eraseblock { struct list_head list; int bad_count; uint32_t offset; /* of this block in the MTD */ uint32_t unchecked_size; uint32_t used_size; uint32_t dirty_size; uint32_t wasted_size; uint32_t free_size; /* Note that sector_size - free_size is the address of the first free space */ struct jffs2_raw_node_ref *first_node; struct jffs2_raw_node_ref *last_node; struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */ }; static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c) { return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024); } /* Calculate totlen from surrounding nodes or eraseblock */ static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *ref) { uint32_t ref_end; if (ref->next_phys) ref_end = ref_offset(ref->next_phys); else { if (!jeb) jeb = &c->blocks[ref->flash_offset / c->sector_size]; /* Last node in block. Use free_space */ BUG_ON(ref != jeb->last_node); ref_end = jeb->offset + c->sector_size - jeb->free_size; } return ref_end - ref_offset(ref); } static inline uint32_t ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *ref) { uint32_t ret; #if CONFIG_JFFS2_FS_DEBUG > 0 if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); BUG(); } #endif #if 1 ret = ref->__totlen; #else /* This doesn't actually work yet */ ret = __ref_totlen(c, jeb, ref); if (ret != ref->__totlen) { printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, ret, ref->__totlen); if (!jeb) jeb = &c->blocks[ref->flash_offset / c->sector_size]; jffs2_dbg_dump_node_refs_nolock(c, jeb); BUG(); } #endif return ret; } #define ALLOC_NORMAL 0 /* Normal allocation */ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */ #define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */ /* How much dirty space before it goes on the very_dirty_list */ #define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) /* check if dirty space is more than 255 Byte */ #define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) #define PAD(x) (((x)+3)&~3) static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) { while(raw->next_in_ino) { raw = raw->next_in_ino; } return ((struct jffs2_inode_cache *)raw); } static inline struct jffs2_node_frag *frag_first(struct rb_root *root) { struct rb_node *node = root->rb_node; if (!node) return NULL; while(node->rb_left) node = node->rb_left; return rb_entry(node, struct jffs2_node_frag, rb); } static inline struct jffs2_node_frag *frag_last(struct rb_root *root) { struct rb_node *node = root->rb_node; if (!node) return NULL; while(node->rb_right) node = node->rb_right; return rb_entry(node, struct jffs2_node_frag, rb); } #define rb_parent(rb) ((rb)->rb_parent) #define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb) #define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) #define frag_erase(frag, list) rb_erase(&frag->rb, list); /* nodelist.c */ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state); struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino); void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new); void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old); void jffs2_free_ino_caches(struct jffs2_sb_info *c); void jffs2_free_raw_node_refs(struct jffs2_sb_info *c); struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset); void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete); struct rb_node *rb_next(struct rb_node *); struct rb_node *rb_prev(struct rb_node *); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this); int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn); /* nodemgmt.c */ int jffs2_thread_should_wake(struct jffs2_sb_info *c); int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio, uint32_t sumsize); int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize); int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); void jffs2_complete_reservation(struct jffs2_sb_info *c); void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); /* write.c */ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode); struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode); int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, unsigned char *buf, uint32_t offset, uint32_t writelen, uint32_t *retlen); int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen); int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time); int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time); /* readinode.c */ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t ino, struct jffs2_raw_inode *latest_node); int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); /* malloc.c */ int jffs2_create_slab_caches(void); void jffs2_destroy_slab_caches(void); struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize); void jffs2_free_full_dirent(struct jffs2_full_dirent *); struct jffs2_full_dnode *jffs2_alloc_full_dnode(void); void jffs2_free_full_dnode(struct jffs2_full_dnode *); struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void); void jffs2_free_raw_dirent(struct jffs2_raw_dirent *); struct jffs2_raw_inode *jffs2_alloc_raw_inode(void); void jffs2_free_raw_inode(struct jffs2_raw_inode *); struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void); void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *); struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void); void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *); struct jffs2_node_frag *jffs2_alloc_node_frag(void); void jffs2_free_node_frag(struct jffs2_node_frag *); struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); void jffs2_free_inode_cache(struct jffs2_inode_cache *); /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); /* read.c */ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len); int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *buf, uint32_t offset, uint32_t len); char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f); /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); void jffs2_rotate_lists(struct jffs2_sb_info *c); int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf, uint32_t ofs, uint32_t len); struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino); int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); /* build.c */ int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* wbuf.c */ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #endif #include "debug.h" #endif /* __JFFS2_NODELIST_H__ */ linux-2.6.17/fs/jffs2/debug.h0000644000000000000000000001777110564537530014352 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: debug.h,v 1.21 2005/11/07 11:14:39 gleixner Exp $ * */ #ifndef _JFFS2_DEBUG_H_ #define _JFFS2_DEBUG_H_ #include #ifndef CONFIG_JFFS2_FS_DEBUG #define CONFIG_JFFS2_FS_DEBUG 0 #endif #if CONFIG_JFFS2_FS_DEBUG > 0 /* Enable "paranoia" checks and dumps */ #define JFFS2_DBG_PARANOIA_CHECKS #define JFFS2_DBG_DUMPS /* * By defining/undefining the below macros one may select debugging messages * fro specific JFFS2 subsystems. */ #define JFFS2_DBG_READINODE_MESSAGES #define JFFS2_DBG_FRAGTREE_MESSAGES #define JFFS2_DBG_DENTLIST_MESSAGES #define JFFS2_DBG_NODEREF_MESSAGES #define JFFS2_DBG_INOCACHE_MESSAGES #define JFFS2_DBG_SUMMARY_MESSAGES #define JFFS2_DBG_FSBUILD_MESSAGES #endif #if CONFIG_JFFS2_FS_DEBUG > 1 #define JFFS2_DBG_FRAGTREE2_MESSAGES #define JFFS2_DBG_MEMALLOC_MESSAGES #endif /* Sanity checks are supposed to be light-weight and enabled by default */ #define JFFS2_DBG_SANITY_CHECKS /* * Dx() are mainly used for debugging messages, they must go away and be * superseded by nicer dbg_xxx() macros... */ #if CONFIG_JFFS2_FS_DEBUG > 0 #define D1(x) x #else #define D1(x) #endif #if CONFIG_JFFS2_FS_DEBUG > 1 #define D2(x) x #else #define D2(x) #endif /* The prefixes of JFFS2 messages */ #define JFFS2_DBG_PREFIX "[JFFS2 DBG]" #define JFFS2_ERR_PREFIX "JFFS2 error:" #define JFFS2_WARN_PREFIX "JFFS2 warning:" #define JFFS2_NOTICE_PREFIX "JFFS2 notice:" #define JFFS2_ERR KERN_ERR #define JFFS2_WARN KERN_WARNING #define JFFS2_NOT KERN_NOTICE #define JFFS2_DBG KERN_DEBUG #define JFFS2_DBG_MSG_PREFIX JFFS2_DBG JFFS2_DBG_PREFIX #define JFFS2_ERR_MSG_PREFIX JFFS2_ERR JFFS2_ERR_PREFIX #define JFFS2_WARN_MSG_PREFIX JFFS2_WARN JFFS2_WARN_PREFIX #define JFFS2_NOTICE_MSG_PREFIX JFFS2_NOT JFFS2_NOTICE_PREFIX /* JFFS2 message macros */ #define JFFS2_ERROR(fmt, ...) \ do { \ printk(JFFS2_ERR_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) #define JFFS2_WARNING(fmt, ...) \ do { \ printk(JFFS2_WARN_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) #define JFFS2_NOTICE(fmt, ...) \ do { \ printk(JFFS2_NOTICE_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) #define JFFS2_DEBUG(fmt, ...) \ do { \ printk(JFFS2_DBG_MSG_PREFIX \ " (%d) %s: " fmt, current->pid, \ __FUNCTION__ , ##__VA_ARGS__); \ } while(0) /* * We split our debugging messages on several parts, depending on the JFFS2 * subsystem the message belongs to. */ /* Read inode debugging messages */ #ifdef JFFS2_DBG_READINODE_MESSAGES #define dbg_readinode(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_readinode(fmt, ...) #endif /* Fragtree build debugging messages */ #ifdef JFFS2_DBG_FRAGTREE_MESSAGES #define dbg_fragtree(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_fragtree(fmt, ...) #endif #ifdef JFFS2_DBG_FRAGTREE2_MESSAGES #define dbg_fragtree2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_fragtree2(fmt, ...) #endif /* Directory entry list manilulation debugging messages */ #ifdef JFFS2_DBG_DENTLIST_MESSAGES #define dbg_dentlist(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_dentlist(fmt, ...) #endif /* Print the messages about manipulating node_refs */ #ifdef JFFS2_DBG_NODEREF_MESSAGES #define dbg_noderef(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_noderef(fmt, ...) #endif /* Manipulations with the list of inodes (JFFS2 inocache) */ #ifdef JFFS2_DBG_INOCACHE_MESSAGES #define dbg_inocache(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_inocache(fmt, ...) #endif /* Summary debugging messages */ #ifdef JFFS2_DBG_SUMMARY_MESSAGES #define dbg_summary(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_summary(fmt, ...) #endif /* File system build messages */ #ifdef JFFS2_DBG_FSBUILD_MESSAGES #define dbg_fsbuild(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_fsbuild(fmt, ...) #endif /* Watch the object allocations */ #ifdef JFFS2_DBG_MEMALLOC_MESSAGES #define dbg_memalloc(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_memalloc(fmt, ...) #endif /* "Sanity" checks */ void __jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); /* "Paranoia" checks */ void __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f); void __jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f); void __jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, uint32_t ofs, int len); /* "Dump" functions */ void __jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c); void __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c); void __jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f); void __jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f); void __jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs); void __jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs); #ifdef JFFS2_DBG_PARANOIA_CHECKS #define jffs2_dbg_fragtree_paranoia_check(f) \ __jffs2_dbg_fragtree_paranoia_check(f) #define jffs2_dbg_fragtree_paranoia_check_nolock(f) \ __jffs2_dbg_fragtree_paranoia_check_nolock(f) #define jffs2_dbg_acct_paranoia_check(c, jeb) \ __jffs2_dbg_acct_paranoia_check(c,jeb) #define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) \ __jffs2_dbg_acct_paranoia_check_nolock(c,jeb) #define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) \ __jffs2_dbg_prewrite_paranoia_check(c, ofs, len) #else #define jffs2_dbg_fragtree_paranoia_check(f) #define jffs2_dbg_fragtree_paranoia_check_nolock(f) #define jffs2_dbg_acct_paranoia_check(c, jeb) #define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) #define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) #endif /* !JFFS2_PARANOIA_CHECKS */ #ifdef JFFS2_DBG_DUMPS #define jffs2_dbg_dump_jeb(c, jeb) \ __jffs2_dbg_dump_jeb(c, jeb); #define jffs2_dbg_dump_jeb_nolock(jeb) \ __jffs2_dbg_dump_jeb_nolock(jeb); #define jffs2_dbg_dump_block_lists(c) \ __jffs2_dbg_dump_block_lists(c) #define jffs2_dbg_dump_block_lists_nolock(c) \ __jffs2_dbg_dump_block_lists_nolock(c) #define jffs2_dbg_dump_fragtree(f) \ __jffs2_dbg_dump_fragtree(f); #define jffs2_dbg_dump_fragtree_nolock(f) \ __jffs2_dbg_dump_fragtree_nolock(f); #define jffs2_dbg_dump_buffer(buf, len, offs) \ __jffs2_dbg_dump_buffer(*buf, len, offs); #define jffs2_dbg_dump_node(c, ofs) \ __jffs2_dbg_dump_node(c, ofs); #else #define jffs2_dbg_dump_jeb(c, jeb) #define jffs2_dbg_dump_jeb_nolock(jeb) #define jffs2_dbg_dump_block_lists(c) #define jffs2_dbg_dump_block_lists_nolock(c) #define jffs2_dbg_dump_fragtree(f) #define jffs2_dbg_dump_fragtree_nolock(f) #define jffs2_dbg_dump_buffer(buf, len, offs) #define jffs2_dbg_dump_node(c, ofs) #endif /* !JFFS2_DBG_DUMPS */ #ifdef JFFS2_DBG_SANITY_CHECKS #define jffs2_dbg_acct_sanity_check(c, jeb) \ __jffs2_dbg_acct_sanity_check(c, jeb) #define jffs2_dbg_acct_sanity_check_nolock(c, jeb) \ __jffs2_dbg_acct_sanity_check_nolock(c, jeb) #else #define jffs2_dbg_acct_sanity_check(c, jeb) #define jffs2_dbg_acct_sanity_check_nolock(c, jeb) #endif /* !JFFS2_DBG_SANITY_CHECKS */ #endif /* _JFFS2_DEBUG_H_ */ linux-2.6.17/fs/jffs2/wbuf.c0000644000000000000000000011027610564537530014214 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * Copyright (C) 2004 Thomas Gleixner * * Created by David Woodhouse * Modified debugged and enhanced by Thomas Gleixner * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: wbuf.c,v 1.100 2005/09/30 13:59:13 dedekind Exp $ * */ #include #include #include #include #include #include #include "nodelist.h" /* For testing write failures */ #undef BREAKME #undef BREAKMEHEADER #ifdef BREAKME static unsigned char *brokenbuf; #endif #define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) ) #define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) ) /* max. erase failures before we mark a block bad */ #define MAX_ERASE_FAILURES 2 struct jffs2_inodirty { uint32_t ino; struct jffs2_inodirty *next; }; static struct jffs2_inodirty inodirty_nomem; static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inodirty *this = c->wbuf_inodes; /* If a malloc failed, consider _everything_ dirty */ if (this == &inodirty_nomem) return 1; /* If ino == 0, _any_ non-GC writes mean 'yes' */ if (this && !ino) return 1; /* Look to see if the inode in question is pending in the wbuf */ while (this) { if (this->ino == ino) return 1; this = this->next; } return 0; } static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c) { struct jffs2_inodirty *this; this = c->wbuf_inodes; if (this != &inodirty_nomem) { while (this) { struct jffs2_inodirty *next = this->next; kfree(this); this = next; } } c->wbuf_inodes = NULL; } static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inodirty *new; /* Mark the superblock dirty so that kupdated will flush... */ jffs2_erase_pending_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) return; new = kmalloc(sizeof(*new), GFP_KERNEL); if (!new) { D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n")); jffs2_clear_wbuf_ino_list(c); c->wbuf_inodes = &inodirty_nomem; return; } new->ino = ino; new->next = c->wbuf_inodes; c->wbuf_inodes = new; return; } static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) { struct list_head *this, *next; static int n; if (list_empty(&c->erasable_pending_wbuf_list)) return; list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset)); list_del(this); if ((jiffies + (n++)) & 127) { /* Most of the time, we just erase it immediately. Otherwise we spend ages scanning it on mount, etc. */ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); list_add_tail(&jeb->list, &c->erasable_list); } } } #define REFILE_NOTEMPTY 0 #define REFILE_ANYWAY 1 static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty) { D1(printk("About to refile bad block at %08x\n", jeb->offset)); /* File the existing block on the bad_used_list.... */ if (c->nextblock == jeb) c->nextblock = NULL; else /* Not sure this should ever happen... need more coffee */ list_del(&jeb->list); if (jeb->first_node) { D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset)); list_add(&jeb->list, &c->bad_used_list); } else { BUG_ON(allow_empty == REFILE_NOTEMPTY); /* It has to have had some nodes or we couldn't be here */ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } /* Adjust its size counts accordingly */ c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->wasted_size += jeb->free_size; jeb->free_size = 0; jffs2_dbg_dump_block_lists_nolock(c); jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); } /* Recover from failure to write wbuf. Recover the nodes up to the * wbuf, not the one which we were starting to try to write. */ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) { struct jffs2_eraseblock *jeb, *new_jeb; struct jffs2_raw_node_ref **first_raw, **raw; size_t retlen; int ret; unsigned char *buf; uint32_t start, end, ofs, len; spin_lock(&c->erase_completion_lock); jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; jffs2_block_refile(c, jeb, REFILE_NOTEMPTY); /* Find the first node to be recovered, by skipping over every node which ends before the wbuf starts, or which is obsolete. */ first_raw = &jeb->first_node; while (*first_raw && (ref_obsolete(*first_raw) || (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ref_offset(*first_raw), ref_flags(*first_raw), (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), c->wbuf_ofs)); first_raw = &(*first_raw)->next_phys; } if (!*first_raw) { /* All nodes were obsolete. Nothing to recover. */ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n")); spin_unlock(&c->erase_completion_lock); return; } start = ref_offset(*first_raw); end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); /* Find the last node to be recovered */ raw = first_raw; while ((*raw)) { if (!ref_obsolete(*raw)) end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); raw = &(*raw)->next_phys; } spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end)); buf = NULL; if (start < c->wbuf_ofs) { /* First affected node was already partially written. * Attempt to reread the old data into our buffer. */ buf = kmalloc(end - start, GFP_KERNEL); if (!buf) { printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n"); goto read_failed; } /* Do the read... */ if (jffs2_cleanmarker_oob(c)) ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); else ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { /* ECC recovered */ ret = 0; } if (ret || retlen != c->wbuf_ofs - start) { printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); kfree(buf); buf = NULL; read_failed: first_raw = &(*first_raw)->next_phys; /* If this was the only node to be recovered, give up */ if (!(*first_raw)) return; /* It wasn't. Go on and try to recover nodes complete in the wbuf */ start = ref_offset(*first_raw); } else { /* Read succeeded. Copy the remaining data from the wbuf */ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); } } /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. Either 'buf' contains the data, or we find it in the wbuf */ /* ... and get an allocation of space from a shiny new block instead */ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len, JFFS2_SUMMARY_NOSUM_SIZE); if (ret) { printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); kfree(buf); return; } if (end-start >= c->wbuf_pagesize) { /* Need to do another write immediately, but it's possible that this is just because the wbuf itself is completely full, and there's nothing earlier read back from the flash. Hence 'buf' isn't necessarily what we're writing from. */ unsigned char *rewrite_buf = buf?:c->wbuf; uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n", towrite, ofs)); #ifdef BREAKMEHEADER static int breakme; if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs); breakme = 0; c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, brokenbuf, NULL, c->oobinfo); ret = -EIO; } else #endif if (jffs2_cleanmarker_oob(c)) ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, rewrite_buf, NULL, c->oobinfo); else ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); kfree(buf); if (retlen) { struct jffs2_raw_node_ref *raw2; raw2 = jffs2_alloc_raw_node_ref(); if (!raw2) return; raw2->flash_offset = ofs | REF_OBSOLETE; raw2->__totlen = ref_totlen(c, jeb, *first_raw); raw2->next_phys = NULL; raw2->next_in_ino = NULL; jffs2_add_physical_node_ref(c, raw2); } return; } printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs); c->wbuf_len = (end - start) - towrite; c->wbuf_ofs = ofs + towrite; memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len); /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ kfree(buf); } else { /* OK, now we're left with the dregs in whichever buffer we're using */ if (buf) { memcpy(c->wbuf, buf, end-start); kfree(buf); } else { memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); } c->wbuf_ofs = ofs; c->wbuf_len = end - start; } /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ new_jeb = &c->blocks[ofs / c->sector_size]; spin_lock(&c->erase_completion_lock); if (new_jeb->first_node) { /* Odd, but possible with ST flash later maybe */ new_jeb->last_node->next_phys = *first_raw; } else { new_jeb->first_node = *first_raw; } raw = first_raw; while (*raw) { uint32_t rawlen = ref_totlen(c, jeb, *raw); D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); if (ref_obsolete(*raw)) { /* Shouldn't really happen much */ new_jeb->dirty_size += rawlen; new_jeb->free_size -= rawlen; c->dirty_size += rawlen; } else { new_jeb->used_size += rawlen; new_jeb->free_size -= rawlen; jeb->dirty_size += rawlen; jeb->used_size -= rawlen; c->dirty_size += rawlen; } c->free_size -= rawlen; (*raw)->flash_offset = ofs | ref_flags(*raw); ofs += rawlen; new_jeb->last_node = *raw; raw = &(*raw)->next_phys; } /* Fix up the original jeb now it's on the bad_list */ *first_raw = NULL; if (first_raw == &jeb->first_node) { jeb->last_node = NULL; D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } else jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys); jffs2_dbg_acct_sanity_check_nolock(c, jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); jffs2_dbg_acct_sanity_check_nolock(c, new_jeb); jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "wbuf recovery completed OK\n")); } /* Meaning of pad argument: 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. 1: Pad, do not adjust nextblock free_size 2: Pad, adjust nextblock free_size */ #define NOPAD 0 #define PAD_NOACCOUNT 1 #define PAD_ACCOUNTING 2 static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { int ret; size_t retlen; /* Nothing to do if not write-buffering the flash. In particular, we shouldn't del_timer() the timer we never initialised. */ if (!jffs2_is_writebuffered(c)) return 0; if (!down_trylock(&c->alloc_sem)) { up(&c->alloc_sem); printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); BUG(); } if (!c->wbuf_len) /* already checked c->wbuf above */ return 0; /* claim remaining space on the page this happens, if we have a change to a new block, or if fsync forces us to flush the writebuffer. if we have a switch to next page, we will not have enough remaining space for this. */ if (pad ) { c->wbuf_len = PAD(c->wbuf_len); /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR with 8 byte page size */ memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); } } /* else jffs2_flash_writev has actually filled in the rest of the buffer for us, and will deal with the node refs etc. later. */ #ifdef BREAKME static int breakme; if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs); breakme = 0; c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, brokenbuf, NULL, c->oobinfo); ret = -EIO; } else #endif if (jffs2_cleanmarker_oob(c)) ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); else ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); if (ret || retlen != c->wbuf_pagesize) { if (ret) printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret); else { printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", retlen, c->wbuf_pagesize); ret = -EIO; } jffs2_wbuf_recover(c); return ret; } spin_lock(&c->erase_completion_lock); /* Adjust free size of the block if we padded. */ if (pad) { struct jffs2_eraseblock *jeb; jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n", (jeb==c->nextblock)?"next":"", jeb->offset)); /* wbuf_pagesize - wbuf_len is the amount of space that's to be padded. If there is less free space in the block than that, something screwed up */ if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) { printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len); printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", jeb->offset, jeb->free_size); BUG(); } jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len); c->free_size -= (c->wbuf_pagesize - c->wbuf_len); jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len); c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); } /* Stick any now-obsoleted blocks on the erase_pending_list */ jffs2_refile_wbuf_blocks(c); jffs2_clear_wbuf_ino_list(c); spin_unlock(&c->erase_completion_lock); memset(c->wbuf,0xff,c->wbuf_pagesize); /* adjust write buffer offset, else we get a non contiguous write bug */ c->wbuf_ofs += c->wbuf_pagesize; c->wbuf_len = 0; return 0; } /* Trigger garbage collection to flush the write-buffer. If ino arg is zero, do it if _any_ real (i.e. not GC) writes are outstanding. If ino arg non-zero, do it only if a write for the given inode is outstanding. */ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) { uint32_t old_wbuf_ofs; uint32_t old_wbuf_len; int ret = 0; D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino)); if (!c->wbuf) return 0; down(&c->alloc_sem); if (!jffs2_wbuf_pending_for_ino(c, ino)) { D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino)); up(&c->alloc_sem); return 0; } old_wbuf_ofs = c->wbuf_ofs; old_wbuf_len = c->wbuf_len; if (c->unchecked_size) { /* GC won't make any progress for a while */ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); /* retry flushing wbuf in case jffs2_wbuf_recover left some data in the wbuf */ if (ret) ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); up_write(&c->wbuf_sem); } else while (old_wbuf_len && old_wbuf_ofs == c->wbuf_ofs) { up(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n")); ret = jffs2_garbage_collect_pass(c); if (ret) { /* GC failed. Flush it with padding instead */ down(&c->alloc_sem); down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); /* retry flushing wbuf in case jffs2_wbuf_recover left some data in the wbuf */ if (ret) ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); up_write(&c->wbuf_sem); break; } down(&c->alloc_sem); } D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n")); up(&c->alloc_sem); return ret; } /* Pad write-buffer to end and write it, wasting space. */ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) { int ret; if (!c->wbuf) return 0; down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); /* retry - maybe wbuf recover left some data in wbuf. */ if (ret) ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); up_write(&c->wbuf_sem); return ret; } int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino) { struct kvec outvecs[3]; uint32_t totlen = 0; uint32_t split_ofs = 0; uint32_t old_totlen; int ret, splitvec = -1; int invec, outvec; size_t wbuf_retlen; unsigned char *wbuf_ptr; size_t donelen = 0; uint32_t outvec_to = to; /* If not NAND flash, don't bother */ if (!jffs2_is_writebuffered(c)) return jffs2_flash_direct_writev(c, invecs, count, to, retlen); down_write(&c->wbuf_sem); /* If wbuf_ofs is not initialized, set it to target address */ if (c->wbuf_ofs == 0xFFFFFFFF) { c->wbuf_ofs = PAGE_DIV(to); c->wbuf_len = PAGE_MOD(to); memset(c->wbuf,0xff,c->wbuf_pagesize); } /* Fixup the wbuf if we are moving to a new eraseblock. The checks below fail for ECC'd NOR because cleanmarker == 16, so a block starts at xxx0010. */ if (jffs2_nor_ecc(c)) { if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) { c->wbuf_ofs = PAGE_DIV(to); c->wbuf_len = PAGE_MOD(to); memset(c->wbuf,0xff,c->wbuf_pagesize); } } /* Sanity checks on target address. It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to write at the beginning of a new erase block. Anything else, and you die. New block starts at xxx000c (0-b = block header) */ if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { /* It's a write to a new block */ if (c->wbuf_len) { D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); *retlen = 0; goto exit; } } /* set pointer to new block */ c->wbuf_ofs = PAGE_DIV(to); c->wbuf_len = PAGE_MOD(to); } if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { /* We're not writing immediately after the writebuffer. Bad. */ printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to); if (c->wbuf_len) printk(KERN_CRIT "wbuf was previously %08x-%08x\n", c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); BUG(); } /* Note outvecs[3] above. We know count is never greater than 2 */ if (count > 2) { printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count); BUG(); } invec = 0; outvec = 0; /* Fill writebuffer first, if already in use */ if (c->wbuf_len) { uint32_t invec_ofs = 0; /* adjust alignment offset */ if (c->wbuf_len != PAGE_MOD(to)) { c->wbuf_len = PAGE_MOD(to); /* take care of alignment to next page */ if (!c->wbuf_len) c->wbuf_len = c->wbuf_pagesize; } while(c->wbuf_len < c->wbuf_pagesize) { uint32_t thislen; if (invec == count) goto alldone; thislen = c->wbuf_pagesize - c->wbuf_len; if (thislen >= invecs[invec].iov_len) thislen = invecs[invec].iov_len; invec_ofs = thislen; memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen); c->wbuf_len += thislen; donelen += thislen; /* Get next invec, if actual did not fill the buffer */ if (c->wbuf_len < c->wbuf_pagesize) invec++; } /* write buffer is full, flush buffer */ ret = __jffs2_flush_wbuf(c, NOPAD); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); /* Retlen zero to make sure our caller doesn't mark the space dirty. We've already done everything that's necessary */ *retlen = 0; goto exit; } outvec_to += donelen; c->wbuf_ofs = outvec_to; /* All invecs done ? */ if (invec == count) goto alldone; /* Set up the first outvec, containing the remainder of the invec we partially used */ if (invecs[invec].iov_len > invec_ofs) { outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs; totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs; if (totlen > c->wbuf_pagesize) { splitvec = outvec; split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen); } outvec++; } invec++; } /* OK, now we've flushed the wbuf and the start of the bits we have been asked to write, now to write the rest.... */ /* totlen holds the amount of data still to be written */ old_totlen = totlen; for ( ; invec < count; invec++,outvec++ ) { outvecs[outvec].iov_base = invecs[invec].iov_base; totlen += outvecs[outvec].iov_len = invecs[invec].iov_len; if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) { splitvec = outvec; split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen); old_totlen = totlen; } } /* Now the outvecs array holds all the remaining data to write */ /* Up to splitvec,split_ofs is to be written immediately. The rest goes into the (now-empty) wbuf */ if (splitvec != -1) { uint32_t remainder; remainder = outvecs[splitvec].iov_len - split_ofs; outvecs[splitvec].iov_len = split_ofs; /* We did cross a page boundary, so we write some now */ if (jffs2_cleanmarker_oob(c)) ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); else ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { /* At this point we have no problem, c->wbuf is empty. However refile nextblock to avoid writing again to same address. */ struct jffs2_eraseblock *jeb; spin_lock(&c->erase_completion_lock); jeb = &c->blocks[outvec_to / c->sector_size]; jffs2_block_refile(c, jeb, REFILE_ANYWAY); *retlen = 0; spin_unlock(&c->erase_completion_lock); goto exit; } donelen += wbuf_retlen; c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen); if (remainder) { outvecs[splitvec].iov_base += split_ofs; outvecs[splitvec].iov_len = remainder; } else { splitvec++; } } else { splitvec = 0; } /* Now splitvec points to the start of the bits we have to copy into the wbuf */ wbuf_ptr = c->wbuf; for ( ; splitvec < outvec; splitvec++) { /* Don't copy the wbuf into itself */ if (outvecs[splitvec].iov_base == c->wbuf) continue; memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len); wbuf_ptr += outvecs[splitvec].iov_len; donelen += outvecs[splitvec].iov_len; } c->wbuf_len = wbuf_ptr - c->wbuf; /* If there's a remainder in the wbuf and it's a non-GC write, remember that the wbuf affects this ino */ alldone: *retlen = donelen; if (jffs2_sum_active()) { int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to); if (res) return res; } if (c->wbuf_len && ino) jffs2_wbuf_dirties_inode(c, ino); ret = 0; exit: up_write(&c->wbuf_sem); return ret; } /* * This is the entry for flash write. * Check, if we work on NAND FLASH, if so build an kvec and write it via vritev */ int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) { struct kvec vecs[1]; if (!jffs2_is_writebuffered(c)) return jffs2_flash_direct_write(c, ofs, len, retlen, buf); vecs[0].iov_base = (unsigned char *) buf; vecs[0].iov_len = len; return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0); } /* Handle readback from writebuffer and ECC failure return */ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf) { loff_t orbf = 0, owbf = 0, lwbf = 0; int ret; if (!jffs2_is_writebuffered(c)) return c->mtd->read(c->mtd, ofs, len, retlen, buf); /* Read flash */ down_read(&c->wbuf_sem); if (jffs2_cleanmarker_oob(c)) ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); else ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); if ( (ret == -EBADMSG) && (*retlen == len) ) { printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", len, ofs); /* * We have the raw data without ECC correction in the buffer, maybe * we are lucky and all data or parts are correct. We check the node. * If data are corrupted node check will sort it out. * We keep this block, it will fail on write or erase and the we * mark it bad. Or should we do that now? But we should give him a chance. * Maybe we had a system crash or power loss before the ecc write or * a erase was completed. * So we return success. :) */ ret = 0; } /* if no writebuffer available or write buffer empty, return */ if (!c->wbuf_pagesize || !c->wbuf_len) goto exit; /* if we read in a different block, return */ if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs)) goto exit; if (ofs >= c->wbuf_ofs) { owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ goto exit; lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ if (lwbf > len) lwbf = len; } else { orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ if (orbf > len) /* is write beyond write buffer ? */ goto exit; lwbf = len - orbf; /* number of bytes to copy */ if (lwbf > c->wbuf_len) lwbf = c->wbuf_len; } if (lwbf > 0) memcpy(buf+orbf,c->wbuf+owbf,lwbf); exit: up_read(&c->wbuf_sem); return ret; } /* * Check, if the out of band area is empty */ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) { unsigned char *buf; int ret = 0; int i,len,page; size_t retlen; int oob_size; /* allocate a buffer for all oob data in this sector */ oob_size = c->mtd->oobsize; len = 4 * oob_size; buf = kmalloc(len, GFP_KERNEL); if (!buf) { printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); return -ENOMEM; } /* * if mode = 0, we scan for a total empty oob area, else we have * to take care of the cleanmarker in the first page of the block */ ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); if (ret) { D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); goto out; } if (retlen < len) { D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); ret = -EIO; goto out; } /* Special check for first page */ for(i = 0; i < oob_size ; i++) { /* Yeah, we know about the cleanmarker. */ if (mode && i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len) continue; if (buf[i] != 0xFF) { D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", buf[i], i, jeb->offset)); ret = 1; goto out; } } /* we know, we are aligned :) */ for (page = oob_size; page < len; page += sizeof(long)) { unsigned long dat = *(unsigned long *)(&buf[page]); if(dat != -1) { ret = 1; goto out; } } out: kfree(buf); return ret; } /* * Scan for a valid cleanmarker and for bad blocks * For virtual blocks (concatenated physical blocks) check the cleanmarker * only in the first page of the first physical block, but scan for bad blocks in all * physical blocks */ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; unsigned char buf[2 * NAND_MAX_OOBSIZE]; unsigned char *p; int ret, i, cnt, retval = 0; size_t retlen, offset; int oob_size; offset = jeb->offset; oob_size = c->mtd->oobsize; /* Loop through the physical blocks */ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { /* Check first if the block is bad. */ if (c->mtd->block_isbad (c->mtd, offset)) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); return 2; } /* * We read oob data from page 0 and 1 of the block. * page 0 contains cleanmarker and badblock info * page 1 contains failure count of this block */ ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); if (ret) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); return ret; } if (retlen < (oob_size << 1)) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); return -EIO; } /* Check cleanmarker only on the first physical block */ if (!cnt) { n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32 (8); p = (unsigned char *) &n; for (i = 0; i < c->fsdata_len; i++) { if (buf[c->fsdata_pos + i] != p[i]) { retval = 1; } } D1(if (retval == 1) { printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); printk(KERN_WARNING "OOB at %08x was ", offset); for (i=0; i < oob_size; i++) { printk("%02x ", buf[i]); } printk("\n"); }) } offset += c->mtd->erasesize; } return retval; } int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; int ret; size_t retlen; n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32(8); ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); if (ret) { D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); return ret; } if (retlen != c->fsdata_len) { D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); return ret; } return 0; } /* * On NAND we try to mark this block bad. If the block was erased more * than MAX_ERASE_FAILURES we mark it finaly bad. * Don't care about failures. This block remains on the erase-pending * or badblock list as long as nobody manipulates the flash with * a bootloader or something like that. */ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { int ret; /* if the count is < max, we try to write the counter to the 2nd page oob area */ if( ++jeb->bad_count < MAX_ERASE_FAILURES) return 0; if (!c->mtd->block_markbad) return 1; // What else can we do? D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset)); ret = c->mtd->block_markbad(c->mtd, bad_offset); if (ret) { D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); return ret; } return 1; } #define NAND_JFFS2_OOB16_FSDALEN 8 static struct nand_oobinfo jffs2_oobinfo_docecc = { .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, .eccpos = {0,1,2,3,4,5} }; static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { struct nand_oobinfo *oinfo = &c->mtd->oobinfo; /* Do this only, if we have an oob buffer */ if (!c->mtd->oobsize) return 0; /* Cleanmarker is out-of-band, so inline size zero */ c->cleanmarker_size = 0; /* Should we use autoplacement ? */ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); /* Get the position of the free bytes */ if (!oinfo->oobfree[0][1]) { printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); return -ENOSPC; } c->fsdata_pos = oinfo->oobfree[0][0]; c->fsdata_len = oinfo->oobfree[0][1]; if (c->fsdata_len > 8) c->fsdata_len = 8; } else { /* This is just a legacy fallback and should go away soon */ switch(c->mtd->ecctype) { case MTD_ECC_RS_DiskOnChip: printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n"); c->oobinfo = &jffs2_oobinfo_docecc; c->fsdata_pos = 6; c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; c->badblock_pos = 15; break; default: D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); return -EINVAL; } } return 0; } int jffs2_nand_flash_setup(struct jffs2_sb_info *c) { int res; /* Initialise write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->oobblock; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; res = jffs2_nand_set_oobinfo(c); #ifdef BREAKME if (!brokenbuf) brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!brokenbuf) { kfree(c->wbuf); return -ENOMEM; } memset(brokenbuf, 0xdb, c->wbuf_pagesize); #endif return res; } void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } int jffs2_dataflash_setup(struct jffs2_sb_info *c) { c->cleanmarker_size = 0; /* No cleanmarkers needed */ /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->erasesize; /* Find a suitable c->sector_size * - Not too much sectors * - Sectors have to be at least 4 K + some bytes * - All known dataflashes have erase sizes of 528 or 1056 * - we take at least 8 eraseblocks and want to have at least 8K size * - The concatenation should be a power of 2 */ c->sector_size = 8 * c->mtd->erasesize; while (c->sector_size < 8192) { c->sector_size *= 2; } /* It may be necessary to adjust the flash size */ c->flash_size = c->mtd->size; if ((c->flash_size % c->sector_size) != 0) { c->flash_size = (c->flash_size / c->sector_size) * c->sector_size; printk(KERN_WARNING "JFFS2 flash size adjusted to %dKiB\n", c->flash_size); }; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); return 0; } void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) { /* Cleanmarker is actually larger on the flashes */ c->cleanmarker_size = 16; /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->eccsize; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; return 0; } void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { /* Cleanmarker currently occupies a whole programming region */ c->cleanmarker_size = MTD_PROGREGION_SIZE(c->mtd); /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = MTD_PROGREGION_SIZE(c->mtd); c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; return 0; } void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } linux-2.6.17/fs/jffs2/compr_rtime.c0000644000000000000000000000557110564537530015572 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by Arjan van de Ven * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ * * * Very simple lz77-ish encoder. * * Theory of operation: Both encoder and decoder have a list of "last * occurrences" for every possible source-value; after sending the * first source-byte, the second byte indicated the "run" length of * matches * * The algorithm is intended to only send "whole bytes", no bit-messing. * */ #include #include #include #include #include #include "compr.h" /* _compress returns the compressed size, -1 if bigger */ static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen, void *model) { short positions[256]; int outpos = 0; int pos=0; memset(positions,0,sizeof(positions)); while (pos < (*sourcelen) && outpos <= (*dstlen)-2) { int backpos, runlen=0; unsigned char value; value = data_in[pos]; cpage_out[outpos++] = data_in[pos++]; backpos = positions[value]; positions[value]=pos; while ((backpos < pos) && (pos < (*sourcelen)) && (data_in[pos]==data_in[backpos++]) && (runlen<255)) { pos++; runlen++; } cpage_out[outpos++] = runlen; } if (outpos >= pos) { /* We failed */ return -1; } /* Tell the caller how much we managed to compress, and how much space it took */ *sourcelen = pos; *dstlen = outpos; return 0; } static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen, void *model) { short positions[256]; int outpos = 0; int pos=0; memset(positions,0,sizeof(positions)); while (outpos= outpos) { while(repeat) { cpage_out[outpos++] = cpage_out[backoffs++]; repeat--; } } else { memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat); outpos+=repeat; } } } return 0; } static struct jffs2_compressor jffs2_rtime_comp = { .priority = JFFS2_RTIME_PRIORITY, .name = "rtime", .compr = JFFS2_COMPR_RTIME, .compress = &jffs2_rtime_compress, .decompress = &jffs2_rtime_decompress, #ifdef JFFS2_RTIME_DISABLED .disabled = 1, #else .disabled = 0, #endif }; int jffs2_rtime_init(void) { return jffs2_register_compressor(&jffs2_rtime_comp); } void jffs2_rtime_exit(void) { jffs2_unregister_compressor(&jffs2_rtime_comp); } linux-2.6.17/fs/jffs2/comprtest.c0000644000000000000000000006366210564537530015277 0ustar rootroot/* $Id: comprtest.c,v 1.6 2005/11/07 11:14:38 gleixner Exp $ */ #include #include #include #include #if 0 #define TESTDATA_LEN 512 static unsigned char testdata[TESTDATA_LEN] = { 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xb0, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x06, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x80, 0x04, 0x08, 0x34, 0x80, 0x04, 0x08, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xf4, 0x80, 0x04, 0x08, 0xf4, 0x80, 0x04, 0x08, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x0d, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x10, 0x95, 0x04, 0x08, 0x10, 0x95, 0x04, 0x08, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x58, 0x95, 0x04, 0x08, 0x58, 0x95, 0x04, 0x08, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x81, 0x04, 0x08, 0x08, 0x81, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2e, 0x73, 0x6f, 0x2e, 0x32, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x0c, 0x83, 0x04, 0x08, 0x81, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1c, 0x83, 0x04, 0x08, 0xac, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x2c, 0x83, 0x04, 0x08, 0xdd, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x3c, 0x83, 0x04, 0x08, 0x2e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x4c, 0x83, 0x04, 0x08, 0x7d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x85, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x67, 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, 0x63, 0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x5f, 0x5f, 0x63}; #else #define TESTDATA_LEN 3481 static unsigned char testdata[TESTDATA_LEN] = { 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x22, 0x64, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x62, 0x75, 0x66, 0x5b, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5d, 0x3b, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x7d, 0x20, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5d, 0x3b, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x3b, 0x0a, 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x49, 0x4e, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x3d, 0x20, 0x73, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x7c, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x7c, 0x3d, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2c, 0x20, 0x30, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x26, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3e, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3c, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x69, 0x74, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x20, 0x25, 0x20, 0x31, 0x30, 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x62, 0x75, 0x66, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6d, 0x65, 0x6d, 0x73, 0x65, 0x74, 0x28, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x31, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x30, 0x37, 0x30, 0x30, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6e, 0x65, 0x77, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6f, 0x6c, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6e, 0x65, 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x26, 0x73, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x53, 0x5f, 0x49, 0x53, 0x44, 0x49, 0x52, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x35, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x35, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a }; #endif static unsigned char comprbuf[TESTDATA_LEN]; static unsigned char decomprbuf[TESTDATA_LEN]; int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *datalen, uint32_t *cdatalen); int init_module(void ) { unsigned char comprtype; uint32_t c, d; int ret; printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", testdata[0],testdata[1],testdata[2],testdata[3], testdata[4],testdata[5],testdata[6],testdata[7], testdata[8],testdata[9],testdata[10],testdata[11], testdata[12],testdata[13],testdata[14],testdata[15]); d = TESTDATA_LEN; c = TESTDATA_LEN; comprtype = jffs2_compress(testdata, comprbuf, &d, &c); printk("jffs2_compress used compression type %d. Compressed size %d, uncompressed size %d\n", comprtype, c, d); printk("Compressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", comprbuf[0],comprbuf[1],comprbuf[2],comprbuf[3], comprbuf[4],comprbuf[5],comprbuf[6],comprbuf[7], comprbuf[8],comprbuf[9],comprbuf[10],comprbuf[11], comprbuf[12],comprbuf[13],comprbuf[14],comprbuf[15]); ret = jffs2_decompress(comprtype, comprbuf, decomprbuf, c, d); printk("jffs2_decompress returned %d\n", ret); printk("Decompressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", decomprbuf[0],decomprbuf[1],decomprbuf[2],decomprbuf[3], decomprbuf[4],decomprbuf[5],decomprbuf[6],decomprbuf[7], decomprbuf[8],decomprbuf[9],decomprbuf[10],decomprbuf[11], decomprbuf[12],decomprbuf[13],decomprbuf[14],decomprbuf[15]); if (memcmp(decomprbuf, testdata, d)) printk("Compression and decompression corrupted data\n"); else printk("Compression good for %d bytes\n", d); return 1; } linux-2.6.17/fs/jffs2/background.c0000644000000000000000000000732610564537530015371 0ustar rootroot/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: background.c,v 1.54 2005/05/20 21:37:12 gleixner Exp $ * */ #include #include #include #include #include #include "nodelist.h" static int jffs2_garbage_collect_thread(void *); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { spin_lock(&c->erase_completion_lock); if (c->gc_task && jffs2_thread_should_wake(c)) send_sig(SIGHUP, c->gc_task, 1); spin_unlock(&c->erase_completion_lock); } /* This must only ever be called when no GC thread is currently running */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) { pid_t pid; int ret = 0; BUG_ON(c->gc_task); init_completion(&c->gc_thread_start); init_completion(&c->gc_thread_exit); pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); if (pid < 0) { printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid); complete(&c->gc_thread_exit); ret = pid; } else { /* Wait for it... */ D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); wait_for_completion(&c->gc_thread_start); } return ret; } void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) { int wait = 0; spin_lock(&c->erase_completion_lock); if (c->gc_task) { D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); send_sig(SIGKILL, c->gc_task, 1); wait = 1; } spin_unlock(&c->erase_completion_lock); if (wait) wait_for_completion(&c->gc_thread_exit); } static int jffs2_garbage_collect_thread(void *_c) { struct jffs2_sb_info *c = _c; daemonize("jffs2_gcd_mtd%d", c->mtd->index); allow_signal(SIGKILL); allow_signal(SIGSTOP); allow_signal(SIGCONT); c->gc_task = current; complete(&c->gc_thread_start); set_user_nice(current, 10); for (;;) { allow_signal(SIGHUP); if (!jffs2_thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); /* Yes, there's a race here; we checked jffs2_thread_should_wake() before setting current->state to TASK_INTERRUPTIBLE. But it doesn't matter - We don't care if we miss a wakeup, because the GC thread is only an optimisation anyway. */ schedule(); } if (try_to_freeze()) continue; cond_resched(); /* Put_super will send a SIGKILL and then wait on the sem. */ while (signal_pending(current)) { siginfo_t info; unsigned long signr; signr = dequeue_signal_lock(current, ¤t->blocked, &info); switch(signr) { case SIGSTOP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n")); set_current_state(TASK_STOPPED); schedule(); break; case SIGKILL: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); goto die; case SIGHUP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n")); break; default: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); } } /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ disallow_signal(SIGHUP); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n"); goto die; } } die: spin_lock(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); } linux-2.6.17/fs/block_dev.c0000644000000000000000000007170510564537530014172 0ustar rootroot/* * linux/fs/block_dev.c * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 2001 Andrea Arcangeli SuSE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct bdev_inode { struct block_device bdev; struct inode vfs_inode; }; static inline struct bdev_inode *BDEV_I(struct inode *inode) { return container_of(inode, struct bdev_inode, vfs_inode); } inline struct block_device *I_BDEV(struct inode *inode) { return &BDEV_I(inode)->bdev; } EXPORT_SYMBOL(I_BDEV); static sector_t max_block(struct block_device *bdev) { sector_t retval = ~((sector_t)0); loff_t sz = i_size_read(bdev->bd_inode); if (sz) { unsigned int size = block_size(bdev); unsigned int sizebits = blksize_bits(size); retval = (sz >> sizebits); } return retval; } /* Kill _all_ buffers, dirty or not.. */ static void kill_bdev(struct block_device *bdev) { invalidate_bdev(bdev, 1); truncate_inode_pages(bdev->bd_inode->i_mapping, 0); } int set_blocksize(struct block_device *bdev, int size) { /* Size must be a power of two, and between 512 and PAGE_SIZE */ if (size > PAGE_SIZE || size < 512 || (size & (size-1))) return -EINVAL; /* Size cannot be smaller than the size supported by the device */ if (size < bdev_hardsect_size(bdev)) return -EINVAL; /* Don't change the size if it is same as current */ if (bdev->bd_block_size != size) { sync_blockdev(bdev); bdev->bd_block_size = size; bdev->bd_inode->i_blkbits = blksize_bits(size); kill_bdev(bdev); } return 0; } EXPORT_SYMBOL(set_blocksize); int sb_set_blocksize(struct super_block *sb, int size) { if (set_blocksize(sb->s_bdev, size)) return 0; /* If we get here, we know size is power of two * and it's value is between 512 and PAGE_SIZE */ sb->s_blocksize = size; sb->s_blocksize_bits = blksize_bits(size); return sb->s_blocksize; } EXPORT_SYMBOL(sb_set_blocksize); int sb_min_blocksize(struct super_block *sb, int size) { int minsize = bdev_hardsect_size(sb->s_bdev); if (size < minsize) size = minsize; return sb_set_blocksize(sb, size); } EXPORT_SYMBOL(sb_min_blocksize); static int blkdev_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create) { if (iblock >= max_block(I_BDEV(inode))) { if (create) return -EIO; /* * for reads, we're just trying to fill a partial page. * return a hole, they will have to call get_block again * before they can fill it, and they will get -EIO at that * time */ return 0; } bh->b_bdev = I_BDEV(inode); bh->b_blocknr = iblock; set_buffer_mapped(bh); return 0; } static int blkdev_get_blocks(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create) { sector_t end_block = max_block(I_BDEV(inode)); unsigned long max_blocks = bh->b_size >> inode->i_blkbits; if ((iblock + max_blocks) > end_block) { max_blocks = end_block - iblock; if ((long)max_blocks <= 0) { if (create) return -EIO; /* write fully beyond EOF */ /* * It is a read which is fully beyond EOF. We return * a !buffer_mapped buffer */ max_blocks = 0; } } bh->b_bdev = I_BDEV(inode); bh->b_blocknr = iblock; bh->b_size = max_blocks << inode->i_blkbits; if (max_blocks) set_buffer_mapped(bh); return 0; } static ssize_t blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; return blockdev_direct_IO_no_locking(rw, iocb, inode, I_BDEV(inode), iov, offset, nr_segs, blkdev_get_blocks, NULL); } static int blkdev_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page, blkdev_get_block, wbc); } static int blkdev_readpage(struct file * file, struct page * page) { return block_read_full_page(page, blkdev_get_block); } static int blkdev_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { return block_prepare_write(page, from, to, blkdev_get_block); } static int blkdev_commit_write(struct file *file, struct page *page, unsigned from, unsigned to) { return block_commit_write(page, from, to); } /* * private llseek: * for a block special file file->f_dentry->d_inode->i_size is zero * so we compute the size by hand (just as in block_read/write above) */ static loff_t block_llseek(struct file *file, loff_t offset, int origin) { struct inode *bd_inode = file->f_mapping->host; loff_t size; loff_t retval; mutex_lock(&bd_inode->i_mutex); size = i_size_read(bd_inode); switch (origin) { case 2: offset += size; break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset >= 0 && offset <= size) { if (offset != file->f_pos) { file->f_pos = offset; } retval = offset; } mutex_unlock(&bd_inode->i_mutex); return retval; } /* * Filp is never NULL; the only case when ->fsync() is called with * NULL first argument is nfsd_sync_dir() and that's not a directory. */ static int block_fsync(struct file *filp, struct dentry *dentry, int datasync) { return sync_blockdev(I_BDEV(filp->f_mapping->host)); } /* * pseudo-fs */ static __cacheline_aligned_in_smp DEFINE_SPINLOCK(bdev_lock); static kmem_cache_t * bdev_cachep __read_mostly; static struct inode *bdev_alloc_inode(struct super_block *sb) { struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } static void bdev_destroy_inode(struct inode *inode) { struct bdev_inode *bdi = BDEV_I(inode); bdi->bdev.bd_inode_backing_dev_info = NULL; kmem_cache_free(bdev_cachep, bdi); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct bdev_inode *ei = (struct bdev_inode *) foo; struct block_device *bdev = &ei->bdev; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { memset(bdev, 0, sizeof(*bdev)); mutex_init(&bdev->bd_mutex); mutex_init(&bdev->bd_mount_mutex); INIT_LIST_HEAD(&bdev->bd_inodes); INIT_LIST_HEAD(&bdev->bd_list); #ifdef CONFIG_SYSFS INIT_LIST_HEAD(&bdev->bd_holder_list); #endif inode_init_once(&ei->vfs_inode); } } static inline void __bd_forget(struct inode *inode) { list_del_init(&inode->i_devices); inode->i_bdev = NULL; inode->i_mapping = &inode->i_data; } static void bdev_clear_inode(struct inode *inode) { struct block_device *bdev = &BDEV_I(inode)->bdev; struct list_head *p; spin_lock(&bdev_lock); while ( (p = bdev->bd_inodes.next) != &bdev->bd_inodes ) { __bd_forget(list_entry(p, struct inode, i_devices)); } list_del_init(&bdev->bd_list); spin_unlock(&bdev_lock); } static struct super_operations bdev_sops = { .statfs = simple_statfs, .alloc_inode = bdev_alloc_inode, .destroy_inode = bdev_destroy_inode, .drop_inode = generic_delete_inode, .clear_inode = bdev_clear_inode, }; static struct super_block *bd_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_pseudo(fs_type, "bdev:", &bdev_sops, 0x62646576); } static struct file_system_type bd_type = { .name = "bdev", .get_sb = bd_get_sb, .kill_sb = kill_anon_super, }; static struct vfsmount *bd_mnt __read_mostly; struct super_block *blockdev_superblock; void __init bdev_cache_init(void) { int err; bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode), 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD|SLAB_PANIC), init_once, NULL); err = register_filesystem(&bd_type); if (err) panic("Cannot register bdev pseudo-fs"); bd_mnt = kern_mount(&bd_type); err = PTR_ERR(bd_mnt); if (IS_ERR(bd_mnt)) panic("Cannot create bdev pseudo-fs"); blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */ } /* * Most likely _very_ bad one - but then it's hardly critical for small * /dev and can be fixed when somebody will need really large one. * Keep in mind that it will be fed through icache hash function too. */ static inline unsigned long hash(dev_t dev) { return MAJOR(dev)+MINOR(dev); } static int bdev_test(struct inode *inode, void *data) { return BDEV_I(inode)->bdev.bd_dev == *(dev_t *)data; } static int bdev_set(struct inode *inode, void *data) { BDEV_I(inode)->bdev.bd_dev = *(dev_t *)data; return 0; } static LIST_HEAD(all_bdevs); struct block_device *bdget(dev_t dev) { struct block_device *bdev; struct inode *inode; inode = iget5_locked(bd_mnt->mnt_sb, hash(dev), bdev_test, bdev_set, &dev); if (!inode) return NULL; bdev = &BDEV_I(inode)->bdev; if (inode->i_state & I_NEW) { bdev->bd_contains = NULL; bdev->bd_inode = inode; bdev->bd_block_size = (1 << inode->i_blkbits); bdev->bd_part_count = 0; bdev->bd_invalidated = 0; inode->i_mode = S_IFBLK; inode->i_rdev = dev; inode->i_bdev = bdev; inode->i_data.a_ops = &def_blk_aops; mapping_set_gfp_mask(&inode->i_data, GFP_USER); inode->i_data.backing_dev_info = &default_backing_dev_info; spin_lock(&bdev_lock); list_add(&bdev->bd_list, &all_bdevs); spin_unlock(&bdev_lock); unlock_new_inode(inode); } return bdev; } EXPORT_SYMBOL(bdget); long nr_blockdev_pages(void) { struct list_head *p; long ret = 0; spin_lock(&bdev_lock); list_for_each(p, &all_bdevs) { struct block_device *bdev; bdev = list_entry(p, struct block_device, bd_list); ret += bdev->bd_inode->i_mapping->nrpages; } spin_unlock(&bdev_lock); return ret; } void bdput(struct block_device *bdev) { iput(bdev->bd_inode); } EXPORT_SYMBOL(bdput); static struct block_device *bd_acquire(struct inode *inode) { struct block_device *bdev; spin_lock(&bdev_lock); bdev = inode->i_bdev; if (bdev && igrab(bdev->bd_inode)) { spin_unlock(&bdev_lock); return bdev; } spin_unlock(&bdev_lock); bdev = bdget(inode->i_rdev); if (bdev) { spin_lock(&bdev_lock); if (inode->i_bdev) __bd_forget(inode); inode->i_bdev = bdev; inode->i_mapping = bdev->bd_inode->i_mapping; list_add(&inode->i_devices, &bdev->bd_inodes); spin_unlock(&bdev_lock); } return bdev; } /* Call when you free inode */ void bd_forget(struct inode *inode) { spin_lock(&bdev_lock); if (inode->i_bdev) __bd_forget(inode); spin_unlock(&bdev_lock); } int bd_claim(struct block_device *bdev, void *holder) { int res; spin_lock(&bdev_lock); /* first decide result */ if (bdev->bd_holder == holder) res = 0; /* already a holder */ else if (bdev->bd_holder != NULL) res = -EBUSY; /* held by someone else */ else if (bdev->bd_contains == bdev) res = 0; /* is a whole device which isn't held */ else if (bdev->bd_contains->bd_holder == bd_claim) res = 0; /* is a partition of a device that is being partitioned */ else if (bdev->bd_contains->bd_holder != NULL) res = -EBUSY; /* is a partition of a held device */ else res = 0; /* is a partition of an un-held device */ /* now impose change */ if (res==0) { /* note that for a whole device bd_holders * will be incremented twice, and bd_holder will * be set to bd_claim before being set to holder */ bdev->bd_contains->bd_holders ++; bdev->bd_contains->bd_holder = bd_claim; bdev->bd_holders++; bdev->bd_holder = holder; } spin_unlock(&bdev_lock); return res; } EXPORT_SYMBOL(bd_claim); void bd_release(struct block_device *bdev) { spin_lock(&bdev_lock); if (!--bdev->bd_contains->bd_holders) bdev->bd_contains->bd_holder = NULL; if (!--bdev->bd_holders) bdev->bd_holder = NULL; spin_unlock(&bdev_lock); } EXPORT_SYMBOL(bd_release); #ifdef CONFIG_SYSFS /* * Functions for bd_claim_by_kobject / bd_release_from_kobject * * If a kobject is passed to bd_claim_by_kobject() * and the kobject has a parent directory, * following symlinks are created: * o from the kobject to the claimed bdev * o from "holders" directory of the bdev to the parent of the kobject * bd_release_from_kobject() removes these symlinks. * * Example: * If /dev/dm-0 maps to /dev/sda, kobject corresponding to * /sys/block/dm-0/slaves is passed to bd_claim_by_kobject(), then: * /sys/block/dm-0/slaves/sda --> /sys/block/sda * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0 */ static struct kobject *bdev_get_kobj(struct block_device *bdev) { if (bdev->bd_contains != bdev) return kobject_get(&bdev->bd_part->kobj); else return kobject_get(&bdev->bd_disk->kobj); } static struct kobject *bdev_get_holder(struct block_device *bdev) { if (bdev->bd_contains != bdev) return kobject_get(bdev->bd_part->holder_dir); else return kobject_get(bdev->bd_disk->holder_dir); } static void add_symlink(struct kobject *from, struct kobject *to) { if (!from || !to) return; sysfs_create_link(from, to, kobject_name(to)); } static void del_symlink(struct kobject *from, struct kobject *to) { if (!from || !to) return; sysfs_remove_link(from, kobject_name(to)); } /* * 'struct bd_holder' contains pointers to kobjects symlinked by * bd_claim_by_kobject. * It's connected to bd_holder_list which is protected by bdev->bd_sem. */ struct bd_holder { struct list_head list; /* chain of holders of the bdev */ int count; /* references from the holder */ struct kobject *sdir; /* holder object, e.g. "/block/dm-0/slaves" */ struct kobject *hdev; /* e.g. "/block/dm-0" */ struct kobject *hdir; /* e.g. "/block/sda/holders" */ struct kobject *sdev; /* e.g. "/block/sda" */ }; /* * Get references of related kobjects at once. * Returns 1 on success. 0 on failure. * * Should call bd_holder_release_dirs() after successful use. */ static int bd_holder_grab_dirs(struct block_device *bdev, struct bd_holder *bo) { if (!bdev || !bo) return 0; bo->sdir = kobject_get(bo->sdir); if (!bo->sdir) return 0; bo->hdev = kobject_get(bo->sdir->parent); if (!bo->hdev) goto fail_put_sdir; bo->sdev = bdev_get_kobj(bdev); if (!bo->sdev) goto fail_put_hdev; bo->hdir = bdev_get_holder(bdev); if (!bo->hdir) goto fail_put_sdev; return 1; fail_put_sdev: kobject_put(bo->sdev); fail_put_hdev: kobject_put(bo->hdev); fail_put_sdir: kobject_put(bo->sdir); return 0; } /* Put references of related kobjects at once. */ static void bd_holder_release_dirs(struct bd_holder *bo) { kobject_put(bo->hdir); kobject_put(bo->sdev); kobject_put(bo->hdev); kobject_put(bo->sdir); } static struct bd_holder *alloc_bd_holder(struct kobject *kobj) { struct bd_holder *bo; bo = kzalloc(sizeof(*bo), GFP_KERNEL); if (!bo) return NULL; bo->count = 1; bo->sdir = kobj; return bo; } static void free_bd_holder(struct bd_holder *bo) { kfree(bo); } /** * add_bd_holder - create sysfs symlinks for bd_claim() relationship * * @bdev: block device to be bd_claimed * @bo: preallocated and initialized by alloc_bd_holder() * * If there is no matching entry with @bo in @bdev->bd_holder_list, * add @bo to the list, create symlinks. * * Returns 1 if @bo was added to the list. * Returns 0 if @bo wasn't used by any reason and should be freed. */ static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo) { struct bd_holder *tmp; if (!bo) return 0; list_for_each_entry(tmp, &bdev->bd_holder_list, list) { if (tmp->sdir == bo->sdir) { tmp->count++; return 0; } } if (!bd_holder_grab_dirs(bdev, bo)) return 0; add_symlink(bo->sdir, bo->sdev); add_symlink(bo->hdir, bo->hdev); list_add_tail(&bo->list, &bdev->bd_holder_list); return 1; } /** * del_bd_holder - delete sysfs symlinks for bd_claim() relationship * * @bdev: block device to be bd_claimed * @kobj: holder's kobject * * If there is matching entry with @kobj in @bdev->bd_holder_list * and no other bd_claim() from the same kobject, * remove the struct bd_holder from the list, delete symlinks for it. * * Returns a pointer to the struct bd_holder when it's removed from the list * and ready to be freed. * Returns NULL if matching claim isn't found or there is other bd_claim() * by the same kobject. */ static struct bd_holder *del_bd_holder(struct block_device *bdev, struct kobject *kobj) { struct bd_holder *bo; list_for_each_entry(bo, &bdev->bd_holder_list, list) { if (bo->sdir == kobj) { bo->count--; BUG_ON(bo->count < 0); if (!bo->count) { list_del(&bo->list); del_symlink(bo->sdir, bo->sdev); del_symlink(bo->hdir, bo->hdev); bd_holder_release_dirs(bo); return bo; } break; } } return NULL; } /** * bd_claim_by_kobject - bd_claim() with additional kobject signature * * @bdev: block device to be claimed * @holder: holder's signature * @kobj: holder's kobject * * Do bd_claim() and if it succeeds, create sysfs symlinks between * the bdev and the holder's kobject. * Use bd_release_from_kobject() when relesing the claimed bdev. * * Returns 0 on success. (same as bd_claim()) * Returns errno on failure. */ static int bd_claim_by_kobject(struct block_device *bdev, void *holder, struct kobject *kobj) { int res; struct bd_holder *bo; if (!kobj) return -EINVAL; bo = alloc_bd_holder(kobj); if (!bo) return -ENOMEM; mutex_lock(&bdev->bd_mutex); res = bd_claim(bdev, holder); if (res || !add_bd_holder(bdev, bo)) free_bd_holder(bo); mutex_unlock(&bdev->bd_mutex); return res; } /** * bd_release_from_kobject - bd_release() with additional kobject signature * * @bdev: block device to be released * @kobj: holder's kobject * * Do bd_release() and remove sysfs symlinks created by bd_claim_by_kobject(). */ static void bd_release_from_kobject(struct block_device *bdev, struct kobject *kobj) { struct bd_holder *bo; if (!kobj) return; mutex_lock(&bdev->bd_mutex); bd_release(bdev); if ((bo = del_bd_holder(bdev, kobj))) free_bd_holder(bo); mutex_unlock(&bdev->bd_mutex); } /** * bd_claim_by_disk - wrapper function for bd_claim_by_kobject() * * @bdev: block device to be claimed * @holder: holder's signature * @disk: holder's gendisk * * Call bd_claim_by_kobject() with getting @disk->slave_dir. */ int bd_claim_by_disk(struct block_device *bdev, void *holder, struct gendisk *disk) { return bd_claim_by_kobject(bdev, holder, kobject_get(disk->slave_dir)); } EXPORT_SYMBOL_GPL(bd_claim_by_disk); /** * bd_release_from_disk - wrapper function for bd_release_from_kobject() * * @bdev: block device to be claimed * @disk: holder's gendisk * * Call bd_release_from_kobject() and put @disk->slave_dir. */ void bd_release_from_disk(struct block_device *bdev, struct gendisk *disk) { bd_release_from_kobject(bdev, disk->slave_dir); kobject_put(disk->slave_dir); } EXPORT_SYMBOL_GPL(bd_release_from_disk); #endif /* * Tries to open block device by device number. Use it ONLY if you * really do not have anything better - i.e. when you are behind a * truly sucky interface and all you are given is a device number. _Never_ * to be used for internal purposes. If you ever need it - reconsider * your API. */ struct block_device *open_by_devnum(dev_t dev, unsigned mode) { struct block_device *bdev = bdget(dev); int err = -ENOMEM; int flags = mode & FMODE_WRITE ? O_RDWR : O_RDONLY; if (bdev) err = blkdev_get(bdev, mode, flags); return err ? ERR_PTR(err) : bdev; } EXPORT_SYMBOL(open_by_devnum); /* * This routine checks whether a removable media has been changed, * and invalidates all buffer-cache-entries in that case. This * is a relatively slow routine, so we have to try to minimize using * it. Thus it is called only upon a 'mount' or 'open'. This * is the best way of combining speed and utility, I think. * People changing diskettes in the middle of an operation deserve * to lose :-) */ int check_disk_change(struct block_device *bdev) { struct gendisk *disk = bdev->bd_disk; struct block_device_operations * bdops = disk->fops; if (!bdops->media_changed) return 0; if (!bdops->media_changed(bdev->bd_disk)) return 0; if (__invalidate_device(bdev)) printk("VFS: busy inodes on changed media.\n"); if (bdops->revalidate_disk) bdops->revalidate_disk(bdev->bd_disk); if (bdev->bd_disk->minors > 1) bdev->bd_invalidated = 1; return 1; } EXPORT_SYMBOL(check_disk_change); void bd_set_size(struct block_device *bdev, loff_t size) { unsigned bsize = bdev_hardsect_size(bdev); bdev->bd_inode->i_size = size; while (bsize < PAGE_CACHE_SIZE) { if (size & bsize) break; bsize <<= 1; } bdev->bd_block_size = bsize; bdev->bd_inode->i_blkbits = blksize_bits(bsize); } EXPORT_SYMBOL(bd_set_size); static int do_open(struct block_device *bdev, struct file *file) { struct module *owner = NULL; struct gendisk *disk; int ret = -ENXIO; int part; file->f_mapping = bdev->bd_inode->i_mapping; lock_kernel(); disk = get_gendisk(bdev->bd_dev, &part); if (!disk) { unlock_kernel(); bdput(bdev); return ret; } owner = disk->fops->owner; mutex_lock(&bdev->bd_mutex); if (!bdev->bd_openers) { bdev->bd_disk = disk; bdev->bd_contains = bdev; if (!part) { struct backing_dev_info *bdi; if (disk->fops->open) { ret = disk->fops->open(bdev->bd_inode, file); if (ret) goto out_first; } if (!bdev->bd_openers) { bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); bdi = blk_get_backing_dev_info(bdev); if (bdi == NULL) bdi = &default_backing_dev_info; bdev->bd_inode->i_data.backing_dev_info = bdi; } if (bdev->bd_invalidated) rescan_partitions(disk, bdev); } else { struct hd_struct *p; struct block_device *whole; whole = bdget_disk(disk, 0); ret = -ENOMEM; if (!whole) goto out_first; ret = blkdev_get(whole, file->f_mode, file->f_flags); if (ret) goto out_first; bdev->bd_contains = whole; mutex_lock(&whole->bd_mutex); whole->bd_part_count++; p = disk->part[part - 1]; bdev->bd_inode->i_data.backing_dev_info = whole->bd_inode->i_data.backing_dev_info; if (!(disk->flags & GENHD_FL_UP) || !p || !p->nr_sects) { whole->bd_part_count--; mutex_unlock(&whole->bd_mutex); ret = -ENXIO; goto out_first; } kobject_get(&p->kobj); bdev->bd_part = p; bd_set_size(bdev, (loff_t) p->nr_sects << 9); mutex_unlock(&whole->bd_mutex); } } else { put_disk(disk); module_put(owner); if (bdev->bd_contains == bdev) { if (bdev->bd_disk->fops->open) { ret = bdev->bd_disk->fops->open(bdev->bd_inode, file); if (ret) goto out; } if (bdev->bd_invalidated) rescan_partitions(bdev->bd_disk, bdev); } else { mutex_lock(&bdev->bd_contains->bd_mutex); bdev->bd_contains->bd_part_count++; mutex_unlock(&bdev->bd_contains->bd_mutex); } } bdev->bd_openers++; mutex_unlock(&bdev->bd_mutex); unlock_kernel(); return 0; out_first: bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) blkdev_put(bdev->bd_contains); bdev->bd_contains = NULL; put_disk(disk); module_put(owner); out: mutex_unlock(&bdev->bd_mutex); unlock_kernel(); if (ret) bdput(bdev); return ret; } int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags) { /* * This crockload is due to bad choice of ->open() type. * It will go away. * For now, block device ->open() routine must _not_ * examine anything in 'inode' argument except ->i_rdev. */ struct file fake_file = {}; struct dentry fake_dentry = {}; fake_file.f_mode = mode; fake_file.f_flags = flags; fake_file.f_dentry = &fake_dentry; fake_dentry.d_inode = bdev->bd_inode; return do_open(bdev, &fake_file); } EXPORT_SYMBOL(blkdev_get); static int blkdev_open(struct inode * inode, struct file * filp) { struct block_device *bdev; int res; /* * Preserve backwards compatibility and allow large file access * even if userspace doesn't ask for it explicitly. Some mkfs * binary needs it. We might want to drop this workaround * during an unstable branch. */ filp->f_flags |= O_LARGEFILE; bdev = bd_acquire(inode); res = do_open(bdev, filp); if (res) return res; if (!(filp->f_flags & O_EXCL) ) return 0; if (!(res = bd_claim(bdev, filp))) return 0; blkdev_put(bdev); return res; } int blkdev_put(struct block_device *bdev) { int ret = 0; struct inode *bd_inode = bdev->bd_inode; struct gendisk *disk = bdev->bd_disk; mutex_lock(&bdev->bd_mutex); lock_kernel(); if (!--bdev->bd_openers) { sync_blockdev(bdev); kill_bdev(bdev); } if (bdev->bd_contains == bdev) { if (disk->fops->release) ret = disk->fops->release(bd_inode, NULL); } else { mutex_lock(&bdev->bd_contains->bd_mutex); bdev->bd_contains->bd_part_count--; mutex_unlock(&bdev->bd_contains->bd_mutex); } if (!bdev->bd_openers) { struct module *owner = disk->fops->owner; put_disk(disk); module_put(owner); if (bdev->bd_contains != bdev) { kobject_put(&bdev->bd_part->kobj); bdev->bd_part = NULL; } bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) { blkdev_put(bdev->bd_contains); } bdev->bd_contains = NULL; } unlock_kernel(); mutex_unlock(&bdev->bd_mutex); bdput(bdev); return ret; } EXPORT_SYMBOL(blkdev_put); static int blkdev_close(struct inode * inode, struct file * filp) { struct block_device *bdev = I_BDEV(filp->f_mapping->host); if (bdev->bd_holder == filp) bd_release(bdev); return blkdev_put(bdev); } static ssize_t blkdev_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct iovec local_iov = { .iov_base = (void __user *)buf, .iov_len = count }; return generic_file_write_nolock(file, &local_iov, 1, ppos); } static ssize_t blkdev_file_aio_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos) { struct iovec local_iov = { .iov_base = (void __user *)buf, .iov_len = count }; return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); } static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg) { return blkdev_ioctl(file->f_mapping->host, file, cmd, arg); } struct address_space_operations def_blk_aops = { .readpage = blkdev_readpage, .writepage = blkdev_writepage, .sync_page = block_sync_page, .prepare_write = blkdev_prepare_write, .commit_write = blkdev_commit_write, .writepages = generic_writepages, .direct_IO = blkdev_direct_IO, }; const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_close, .llseek = block_llseek, .read = generic_file_read, .write = blkdev_file_write, .aio_read = generic_file_aio_read, .aio_write = blkdev_file_aio_write, .mmap = generic_file_mmap, .fsync = block_fsync, .unlocked_ioctl = block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif .readv = generic_file_readv, .writev = generic_file_write_nolock, .sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg) { int res; mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); res = blkdev_ioctl(bdev->bd_inode, NULL, cmd, arg); set_fs(old_fs); return res; } EXPORT_SYMBOL(ioctl_by_bdev); /** * lookup_bdev - lookup a struct block_device by name * * @path: special file representing the block device * * Get a reference to the blockdevice at @path in the current * namespace if possible and return it. Return ERR_PTR(error) * otherwise. */ struct block_device *lookup_bdev(const char *path) { struct block_device *bdev; struct inode *inode; struct nameidata nd; int error; if (!path || !*path) return ERR_PTR(-EINVAL); error = path_lookup(path, LOOKUP_FOLLOW, &nd); if (error) return ERR_PTR(error); inode = nd.dentry->d_inode; error = -ENOTBLK; if (!S_ISBLK(inode->i_mode)) goto fail; error = -EACCES; if (nd.mnt->mnt_flags & MNT_NODEV) goto fail; error = -ENOMEM; bdev = bd_acquire(inode); if (!bdev) goto fail; out: path_release(&nd); return bdev; fail: bdev = ERR_PTR(error); goto out; } /** * open_bdev_excl - open a block device by name and set it up for use * * @path: special file representing the block device * @flags: %MS_RDONLY for opening read-only * @holder: owner for exclusion * * Open the blockdevice described by the special file at @path, claim it * for the @holder. */ struct block_device *open_bdev_excl(const char *path, int flags, void *holder) { struct block_device *bdev; mode_t mode = FMODE_READ; int error = 0; bdev = lookup_bdev(path); if (IS_ERR(bdev)) return bdev; if (!(flags & MS_RDONLY)) mode |= FMODE_WRITE; error = blkdev_get(bdev, mode, 0); if (error) return ERR_PTR(error); error = -EACCES; if (!(flags & MS_RDONLY) && bdev_read_only(bdev)) goto blkdev_put; error = bd_claim(bdev, holder); if (error) goto blkdev_put; return bdev; blkdev_put: blkdev_put(bdev); return ERR_PTR(error); } EXPORT_SYMBOL(open_bdev_excl); /** * close_bdev_excl - release a blockdevice openen by open_bdev_excl() * * @bdev: blockdevice to close * * This is the counterpart to open_bdev_excl(). */ void close_bdev_excl(struct block_device *bdev) { bd_release(bdev); blkdev_put(bdev); } EXPORT_SYMBOL(close_bdev_excl); linux-2.6.17/fs/binfmt_em86.c0000644000000000000000000000546310564537530014356 0ustar rootroot/* * linux/fs/binfmt_em86.c * * Based on linux/fs/binfmt_script.c * Copyright (C) 1996 Martin von Löwis * original #!-checking implemented by tytso. * * em86 changes Copyright (C) 1997 Jim Paradis */ #include #include #include #include #include #include #include #include #include #include #include #define EM86_INTERP "/usr/bin/em86" #define EM86_I_NAME "em86" static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs) { char *interp, *i_name, *i_arg; struct file * file; int retval; struct elfhdr elf_ex; /* Make sure this is a Linux/Intel ELF executable... */ elf_ex = *((struct elfhdr *)bprm->buf); if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0) return -ENOEXEC; /* First of all, some simple consistency checks */ if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || (!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) || (!bprm->file->f_op || !bprm->file->f_op->mmap)) { return -ENOEXEC; } bprm->sh_bang++; /* Well, the bang-shell is implicit... */ allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; /* Unlike in the script case, we don't have to do any hairy * parsing to find our interpreter... it's hardcoded! */ interp = EM86_INTERP; i_name = EM86_I_NAME; i_arg = NULL; /* We reserve the right to add an arg later */ /* * Splice in (1) the interpreter's name for argv[0] * (2) (optional) argument to interpreter * (3) filename of emulated file (replace argv[0]) * * This is done in reverse order, because of how the * user environment and arguments are stored. */ remove_arg_zero(bprm); retval = copy_strings_kernel(1, &bprm->filename, bprm); if (retval < 0) return retval; bprm->argc++; if (i_arg) { retval = copy_strings_kernel(1, &i_arg, bprm); if (retval < 0) return retval; bprm->argc++; } retval = copy_strings_kernel(1, &i_name, bprm); if (retval < 0) return retval; bprm->argc++; /* * OK, now restart the process with the interpreter's inode. * Note that we use open_exec() as the name is now in kernel * space, and we don't need to copy it. */ file = open_exec(interp); if (IS_ERR(file)) return PTR_ERR(file); bprm->file = file; retval = prepare_binprm(bprm); if (retval < 0) return retval; return search_binary_handler(bprm, regs); } static struct linux_binfmt em86_format = { .module = THIS_MODULE, .load_binary = load_em86, }; static int __init init_em86_binfmt(void) { return register_binfmt(&em86_format); } static void __exit exit_em86_binfmt(void) { unregister_binfmt(&em86_format); } core_initcall(init_em86_binfmt); module_exit(exit_em86_binfmt); MODULE_LICENSE("GPL"); linux-2.6.17/fs/stat.c0000644000000000000000000002504010564537530013204 0ustar rootroot/* * linux/fs/stat.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include #include #include #include #include #include #include #include #include #include #include #include #include void generic_fillattr(struct inode *inode, struct kstat *stat) { stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; stat->mode = inode->i_mode; stat->nlink = inode->i_nlink; stat->uid = inode->i_uid; stat->gid = inode->i_gid; stat->rdev = inode->i_rdev; stat->atime = inode->i_atime; stat->mtime = inode->i_mtime; stat->ctime = inode->i_ctime; stat->size = i_size_read(inode); stat->blocks = inode->i_blocks; stat->blksize = inode->i_blksize; } EXPORT_SYMBOL(generic_fillattr); int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode = dentry->d_inode; int retval; retval = security_inode_getattr(mnt, dentry); if (retval) return retval; if (inode->i_op->getattr) return inode->i_op->getattr(mnt, dentry, stat); generic_fillattr(inode, stat); if (!stat->blksize) { struct super_block *s = 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; } return 0; } EXPORT_SYMBOL(vfs_getattr); int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat) { struct nameidata nd; int error; error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd); if (!error) { error = vfs_getattr(nd.mnt, nd.dentry, stat); path_release(&nd); } return error; } int vfs_stat(char __user *name, struct kstat *stat) { return vfs_stat_fd(AT_FDCWD, name, stat); } EXPORT_SYMBOL(vfs_stat); int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat) { struct nameidata nd; int error; error = __user_walk_fd(dfd, name, 0, &nd); if (!error) { error = vfs_getattr(nd.mnt, nd.dentry, stat); path_release(&nd); } return error; } int vfs_lstat(char __user *name, struct kstat *stat) { return vfs_lstat_fd(AT_FDCWD, name, stat); } EXPORT_SYMBOL(vfs_lstat); int vfs_fstat(unsigned int fd, struct kstat *stat) { struct file *f = fget(fd); int error = -EBADF; if (f) { error = vfs_getattr(f->f_vfsmnt, f->f_dentry, stat); fput(f); } return error; } EXPORT_SYMBOL(vfs_fstat); #ifdef __ARCH_WANT_OLD_STAT /* * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * statbuf) { static int warncount = 5; struct __old_kernel_stat tmp; if (warncount > 0) { warncount--; printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n", current->comm); } else if (warncount < 0) { /* it's laughable, but... */ warncount = 0; } memset(&tmp, 0, sizeof(struct __old_kernel_stat)); tmp.st_dev = old_encode_dev(stat->dev); tmp.st_ino = stat->ino; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; SET_UID(tmp.st_uid, stat->uid); SET_GID(tmp.st_gid, stat->gid); tmp.st_rdev = old_encode_dev(stat->rdev); #if BITS_PER_LONG == 32 if (stat->size > MAX_NON_LFS) return -EOVERFLOW; #endif tmp.st_size = stat->size; tmp.st_atime = stat->atime.tv_sec; tmp.st_mtime = stat->mtime.tv_sec; tmp.st_ctime = stat->ctime.tv_sec; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long sys_stat(char __user * filename, struct __old_kernel_stat __user * statbuf) { struct kstat stat; int error = vfs_stat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_old_stat(&stat, statbuf); return error; } asmlinkage long sys_lstat(char __user * filename, struct __old_kernel_stat __user * statbuf) { struct kstat stat; int error = vfs_lstat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_old_stat(&stat, statbuf); return error; } asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat __user * statbuf) { struct kstat stat; int error = vfs_fstat(fd, &stat); if (!error) error = cp_old_stat(&stat, statbuf); return error; } #endif /* __ARCH_WANT_OLD_STAT */ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) { struct stat tmp; #if BITS_PER_LONG == 32 if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev)) return -EOVERFLOW; #else if (!new_valid_dev(stat->dev) || !new_valid_dev(stat->rdev)) return -EOVERFLOW; #endif memset(&tmp, 0, sizeof(tmp)); #if BITS_PER_LONG == 32 tmp.st_dev = old_encode_dev(stat->dev); #else tmp.st_dev = new_encode_dev(stat->dev); #endif tmp.st_ino = stat->ino; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; SET_UID(tmp.st_uid, stat->uid); SET_GID(tmp.st_gid, stat->gid); #if BITS_PER_LONG == 32 tmp.st_rdev = old_encode_dev(stat->rdev); #else tmp.st_rdev = new_encode_dev(stat->rdev); #endif #if BITS_PER_LONG == 32 if (stat->size > MAX_NON_LFS) return -EOVERFLOW; #endif tmp.st_size = stat->size; tmp.st_atime = stat->atime.tv_sec; tmp.st_mtime = stat->mtime.tv_sec; tmp.st_ctime = stat->ctime.tv_sec; #ifdef STAT_HAVE_NSEC tmp.st_atime_nsec = stat->atime.tv_nsec; tmp.st_mtime_nsec = stat->mtime.tv_nsec; tmp.st_ctime_nsec = stat->ctime.tv_nsec; #endif tmp.st_blocks = stat->blocks; tmp.st_blksize = stat->blksize; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long sys_newstat(char __user *filename, struct stat __user *statbuf) { struct kstat stat; int error = vfs_stat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_new_stat(&stat, statbuf); return error; } asmlinkage long sys_newlstat(char __user *filename, struct stat __user *statbuf) { struct kstat stat; int error = vfs_lstat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_new_stat(&stat, statbuf); return error; } #if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT) asmlinkage long sys_newfstatat(int dfd, char __user *filename, struct stat __user *statbuf, int flag) { struct kstat stat; int error = -EINVAL; if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) goto out; if (flag & AT_SYMLINK_NOFOLLOW) error = vfs_lstat_fd(dfd, filename, &stat); else error = vfs_stat_fd(dfd, filename, &stat); if (!error) error = cp_new_stat(&stat, statbuf); out: return error; } #endif asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf) { struct kstat stat; int error = vfs_fstat(fd, &stat); if (!error) error = cp_new_stat(&stat, statbuf); return error; } asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf, int bufsiz) { struct nameidata nd; int error; if (bufsiz <= 0) return -EINVAL; error = __user_walk_fd(dfd, path, 0, &nd); if (!error) { struct inode * inode = nd.dentry->d_inode; error = -EINVAL; if (inode->i_op && inode->i_op->readlink) { error = security_inode_readlink(nd.dentry); if (!error) { touch_atime(nd.mnt, nd.dentry); error = inode->i_op->readlink(nd.dentry, buf, bufsiz); } } path_release(&nd); } return error; } asmlinkage long sys_readlink(const char __user *path, char __user *buf, int bufsiz) { return sys_readlinkat(AT_FDCWD, path, buf, bufsiz); } /* ---------- LFS-64 ----------- */ #ifdef __ARCH_WANT_STAT64 static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf) { struct stat64 tmp; memset(&tmp, 0, sizeof(struct stat64)); #ifdef CONFIG_MIPS /* mips has weird padding, so we don't get 64 bits there */ if (!new_valid_dev(stat->dev) || !new_valid_dev(stat->rdev)) return -EOVERFLOW; tmp.st_dev = new_encode_dev(stat->dev); tmp.st_rdev = new_encode_dev(stat->rdev); #else tmp.st_dev = huge_encode_dev(stat->dev); tmp.st_rdev = huge_encode_dev(stat->rdev); #endif tmp.st_ino = stat->ino; #ifdef STAT64_HAS_BROKEN_ST_INO tmp.__st_ino = stat->ino; #endif tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; tmp.st_uid = stat->uid; tmp.st_gid = stat->gid; tmp.st_atime = stat->atime.tv_sec; tmp.st_atime_nsec = stat->atime.tv_nsec; tmp.st_mtime = stat->mtime.tv_sec; tmp.st_mtime_nsec = stat->mtime.tv_nsec; tmp.st_ctime = stat->ctime.tv_sec; tmp.st_ctime_nsec = stat->ctime.tv_nsec; tmp.st_size = stat->size; tmp.st_blocks = stat->blocks; tmp.st_blksize = stat->blksize; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long sys_stat64(char __user * filename, struct stat64 __user * statbuf) { struct kstat stat; int error = vfs_stat(filename, &stat); if (!error) error = cp_new_stat64(&stat, statbuf); return error; } asmlinkage long sys_lstat64(char __user * filename, struct stat64 __user * statbuf) { struct kstat stat; int error = vfs_lstat(filename, &stat); if (!error) error = cp_new_stat64(&stat, statbuf); return error; } asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user * statbuf) { struct kstat stat; int error = vfs_fstat(fd, &stat); if (!error) error = cp_new_stat64(&stat, statbuf); return error; } asmlinkage long sys_fstatat64(int dfd, char __user *filename, struct stat64 __user *statbuf, int flag) { struct kstat stat; int error = -EINVAL; if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) goto out; if (flag & AT_SYMLINK_NOFOLLOW) error = vfs_lstat_fd(dfd, filename, &stat); else error = vfs_stat_fd(dfd, filename, &stat); if (!error) error = cp_new_stat64(&stat, statbuf); out: return error; } #endif /* __ARCH_WANT_STAT64 */ void inode_add_bytes(struct inode *inode, loff_t bytes) { spin_lock(&inode->i_lock); inode->i_blocks += bytes >> 9; bytes &= 511; inode->i_bytes += bytes; if (inode->i_bytes >= 512) { inode->i_blocks++; inode->i_bytes -= 512; } spin_unlock(&inode->i_lock); } EXPORT_SYMBOL(inode_add_bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes) { spin_lock(&inode->i_lock); inode->i_blocks -= bytes >> 9; bytes &= 511; if (inode->i_bytes < bytes) { inode->i_blocks--; inode->i_bytes += 512; } inode->i_bytes -= bytes; spin_unlock(&inode->i_lock); } EXPORT_SYMBOL(inode_sub_bytes); loff_t inode_get_bytes(struct inode *inode) { loff_t ret; spin_lock(&inode->i_lock); ret = (((loff_t)inode->i_blocks) << 9) + inode->i_bytes; spin_unlock(&inode->i_lock); return ret; } EXPORT_SYMBOL(inode_get_bytes); void inode_set_bytes(struct inode *inode, loff_t bytes) { /* Caller is here responsible for sufficient locking * (ie. inode->i_lock) */ inode->i_blocks = bytes >> 9; inode->i_bytes = bytes & 511; } EXPORT_SYMBOL(inode_set_bytes); linux-2.6.17/fs/Kconfig0000644000000000000000000021117410564537530013375 0ustar rootroot# # File system configuration # menu "File systems" config EXT2_FS tristate "Second extended fs support" help Ext2 is a standard Linux file system for hard disks. To compile this file system support as a module, choose M here: the module will be called ext2. Be aware however that the file system of your root partition (the one containing the directory /) cannot be compiled as a module, and so this could be dangerous. If unsure, say Y. config EXT2_FS_XATTR bool "Ext2 extended attributes" depends on EXT2_FS help Extended attributes are name:value pairs associated with inodes by the kernel or by users (see the attr(5) manual page, or visit for details). If unsure, say N. config EXT2_FS_POSIX_ACL bool "Ext2 POSIX Access Control Lists" depends on EXT2_FS_XATTR select FS_POSIX_ACL help Posix Access Control Lists (ACLs) support permissions for users and groups beyond the owner/group/world scheme. To learn more about Access Control Lists, visit the Posix ACLs for Linux website . If you don't know what Access Control Lists are, say N config EXT2_FS_SECURITY bool "Ext2 Security Labels" depends on EXT2_FS_XATTR help Security labels support alternative access control models implemented by security modules like SELinux. This option enables an extended attribute handler for file security labels in the ext2 filesystem. If you are not using a security module that requires using extended attributes for file security labels, say N. config EXT2_FS_XIP bool "Ext2 execute in place support" depends on EXT2_FS help Execute in place can be used on memory-backed block devices. If you enable this option, you can select to mount block devices which are capable of this feature without using the page cache. If you do not use a block device that is capable of using this, or if unsure, say N. config FS_XIP # execute in place bool depends on EXT2_FS_XIP default y config EXT3_FS tristate "Ext3 journalling file system support" select JBD help This is the journaling version of the Second extended file system (often called ext3), the de facto standard Linux file system (method to organize files on a storage device) for hard disks. The journaling code included in this driver means you do not have to run e2fsck (file system checker) on your file systems after a crash. The journal keeps track of any changes that were being made at the time the system crashed, and can ensure that your file system is consistent without the need for a lengthy check. Other than adding the journal to the file system, the on-disk format of ext3 is identical to ext2. It is possible to freely switch between using the ext3 driver and the ext2 driver, as long as the file system has been cleanly unmounted, or e2fsck is run on the file system. To add a journal on an existing ext2 file system or change the behavior of ext3 file systems, you can use the tune2fs utility ("man tune2fs"). To modify attributes of files and directories on ext3 file systems, use chattr ("man chattr"). You need to be using e2fsprogs version 1.20 or later in order to create ext3 journals (available at ). To compile this file system support as a module, choose M here: the module will be called ext3. Be aware however that the file system of your root partition (the one containing the directory /) cannot be compiled as a module, and so this may be dangerous. config EXT3_FS_XATTR bool "Ext3 extended attributes" depends on EXT3_FS default y help Extended attributes are name:value pairs associated with inodes by the kernel or by users (see the attr(5) manual page, or visit for details). If unsure, say N. You need this for POSIX ACL support on ext3. config EXT3_FS_POSIX_ACL bool "Ext3 POSIX Access Control Lists" depends on EXT3_FS_XATTR select FS_POSIX_ACL help Posix Access Control Lists (ACLs) support permissions for users and groups beyond the owner/group/world scheme. To learn more about Access Control Lists, visit the Posix ACLs for Linux website . If you don't know what Access Control Lists are, say N config EXT3_FS_SECURITY bool "Ext3 Security Labels" depends on EXT3_FS_XATTR help Security labels support alternative access control models implemented by security modules like SELinux. This option enables an extended attribute handler for file security labels in the ext3 filesystem. If you are not using a security module that requires using extended attributes for file security labels, say N. config JBD tristate help This is a generic journaling layer for block devices. It is currently used by the ext3 and OCFS2 file systems, but it could also be used to add journal support to other file systems or block devices such as RAID or LVM. If you are using the ext3 or OCFS2 file systems, you need to say Y here. If you are not using ext3 OCFS2 then you will probably want to say N. To compile this device as a module, choose M here: the module will be called jbd. If you are compiling ext3 or OCFS2 into the kernel, you cannot compile this code as a module. config JBD_DEBUG bool "JBD (ext3) debugging support" depends on JBD help If you are using the ext3 journaled file system (or potentially any other file system/device using JBD), this option allows you to enable debugging output while the system is running, in order to help track down any problems you are having. By default the debugging output will be turned off. If you select Y here, then you will be able to turn on debugging with "echo N > /proc/sys/fs/jbd-debug", where N is a number between 1 and 5, the higher the number, the more debugging output is generated. To turn debugging off again, do "echo 0 > /proc/sys/fs/jbd-debug". config FS_MBCACHE # Meta block cache for Extended Attributes (ext2/ext3) tristate depends on EXT2_FS_XATTR || EXT3_FS_XATTR default y if EXT2_FS=y || EXT3_FS=y default m if EXT2_FS=m || EXT3_FS=m config REISERFS_FS tristate "Reiserfs support" help Stores not just filenames but the files themselves in a balanced tree. Uses journaling. Balanced trees are more efficient than traditional file system architectural foundations. In general, ReiserFS is as fast as ext2, but is very efficient with large directories and small files. Additional patches are needed for NFS and quotas, please see for links. It is more easily extended to have features currently found in database and keyword search systems than block allocation based file systems are. The next version will be so extended, and will support plugins consistent with our motto ``It takes more than a license to make source code open.'' Read to learn more about reiserfs. Sponsored by Threshold Networks, Emusic.com, and Bigstorage.com. If you like it, you can pay us to add new features to it that you need, buy a support contract, or pay us to port it to another OS. config REISERFS_CHECK bool "Enable reiserfs debug mode" depends on REISERFS_FS help If you set this to Y, then ReiserFS will perform every check it can possibly imagine of its internal consistency throughout its operation. It will also go substantially slower. More than once we have forgotten that this was on, and then gone despondent over the latest benchmarks.:-) Use of this option allows our team to go all out in checking for consistency when debugging without fear of its effect on end users. If you are on the verge of sending in a bug report, say Y and you might get a useful error message. Almost everyone should say N. config REISERFS_PROC_INFO bool "Stats in /proc/fs/reiserfs" depends on REISERFS_FS help Create under /proc/fs/reiserfs a hierarchy of files, displaying various ReiserFS statistics and internal data at the expense of making your kernel or module slightly larger (+8 KB). This also increases the amount of kernel memory required for each mount. Almost everyone but ReiserFS developers and people fine-tuning reiserfs or tracing problems should say N. config REISERFS_FS_XATTR bool "ReiserFS extended attributes" depends on REISERFS_FS help Extended attributes are name:value pairs associated with inodes by the kernel or by users (see the attr(5) manual page, or visit for details). If unsure, say N. config REISERFS_FS_POSIX_ACL bool "ReiserFS POSIX Access Control Lists" depends on REISERFS_FS_XATTR select FS_POSIX_ACL help Posix Access Control Lists (ACLs) support permissions for users and groups beyond the owner/group/world scheme. To learn more about Access Control Lists, visit the Posix ACLs for Linux website . If you don't know what Access Control Lists are, say N config REISERFS_FS_SECURITY bool "ReiserFS Security Labels" depends on REISERFS_FS_XATTR help Security labels support alternative access control models implemented by security modules like SELinux. This option enables an extended attribute handler for file security labels in the ReiserFS filesystem. If you are not using a security module that requires using extended attributes for file security labels, say N. config JFS_FS tristate "JFS filesystem support" select NLS help This is a port of IBM's Journaled Filesystem . More information is available in the file . If you do not intend to use the JFS filesystem, say N. config JFS_POSIX_ACL bool "JFS POSIX Access Control Lists" depends on JFS_FS select FS_POSIX_ACL help Posix Access Control Lists (ACLs) support permissions for users and groups beyond the owner/group/world scheme. To learn more about Access Control Lists, visit the Posix ACLs for Linux website . If you don't know what Access Control Lists are, say N config JFS_SECURITY bool "JFS Security Labels" depends on JFS_FS help Security labels support alternative access control models implemented by security modules like SELinux. This option enables an extended attribute handler for file security labels in the jfs filesystem. If you are not using a security module that requires using extended attributes for file security labels, say N. config JFS_DEBUG bool "JFS debugging" depends on JFS_FS help If you are experiencing any problems with the JFS filesystem, say Y here. This will result in additional debugging messages to be written to the system log. Under normal circumstances, this results in very little overhead. config JFS_STATISTICS bool "JFS statistics" depends on JFS_FS help Enabling this option will cause statistics from the JFS file system to be made available to the user in the /proc/fs/jfs/ directory. config FS_POSIX_ACL # Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs) # # NOTE: you can implement Posix ACLs without these helpers (XFS does). # Never use this symbol for ifdefs. # bool default n source "fs/xfs/Kconfig" config OCFS2_FS tristate "OCFS2 file system support (EXPERIMENTAL)" depends on NET && EXPERIMENTAL select CONFIGFS_FS select JBD select CRC32 select INET help OCFS2 is a general purpose extent based shared disk cluster file system with many similarities to ext3. It supports 64 bit inode numbers, and has automatically extending metadata groups which may also make it attractive for non-clustered use. You'll want to install the ocfs2-tools package in order to at least get "mount.ocfs2". Project web page: http://oss.oracle.com/projects/ocfs2 Tools web page: http://oss.oracle.com/projects/ocfs2-tools OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/ Note: Features which OCFS2 does not support yet: - extended attributes - shared writeable mmap - loopback is supported, but data written will not be cluster coherent. - quotas - cluster aware flock - Directory change notification (F_NOTIFY) - Distributed Caching (F_SETLEASE/F_GETLEASE/break_lease) - POSIX ACLs - readpages / writepages (not user visible) config MINIX_FS tristate "Minix fs support" help Minix is a simple operating system used in many classes about OS's. The minix file system (method to organize files on a hard disk partition or a floppy disk) was the original file system for Linux, but has been superseded by the second extended file system ext2fs. You don't want to use the minix file system on your hard disk because of certain built-in restrictions, but it is sometimes found on older Linux floppy disks. This option will enlarge your kernel by about 28 KB. If unsure, say N. To compile this file system support as a module, choose M here: the module will be called minix. Note that the file system of your root partition (the one containing the directory /) cannot be compiled as a module. config ROMFS_FS tristate "ROM file system support" ---help--- This is a very small read-only file system mainly intended for initial ram disks of installation disks, but it could be used for other read-only media as well. Read for details. To compile this file system support as a module, choose M here: the module will be called romfs. Note that the file system of your root partition (the one containing the directory /) cannot be a module. If you don't know whether you need it, then you don't need it: answer N. config INOTIFY bool "Inotify file change notification support" default y ---help--- Say Y here to enable inotify support and the associated system calls. Inotify is a file change notification system and a replacement for dnotify. Inotify fixes numerous shortcomings in dnotify and introduces several new features. It allows monitoring of both files and directories via a single open fd. Other features include multiple file events, one-shot support, and unmount notification. For more information, see Documentation/filesystems/inotify.txt If unsure, say Y. config QUOTA bool "Quota support" help If you say Y here, you will be able to set per user limits for disk usage (also called disk quotas). Currently, it works for the ext2, ext3, and reiserfs file system. ext3 also supports journalled quotas for which you don't need to run quotacheck(8) after an unclean shutdown. For further details, read the Quota mini-HOWTO, available from , or the documentation provided with the quota tools. Probably the quota support is only useful for multi user systems. If unsure, say N. config QFMT_V1 tristate "Old quota format support" depends on QUOTA help This quota format was (is) used by kernels earlier than 2.4.22. If you have quota working and you don't want to convert to new quota format say Y here. config QFMT_V2 tristate "Quota format v2 support" depends on QUOTA help This quota format allows using quotas with 32-bit UIDs/GIDs. If you need this functionality say Y here. config QUOTACTL bool depends on XFS_QUOTA || QUOTA default y config DNOTIFY bool "Dnotify support" if EMBEDDED default y help Dnotify is a directory-based per-fd file change notification system that uses signals to communicate events to user-space. There exist superior alternatives, but some applications may still rely on dnotify. Because of this, if unsure, say Y. config AUTOFS_FS tristate "Kernel automounter support" help The automounter is a tool to automatically mount remote file systems on demand. This implementation is partially kernel-based to reduce overhead in the already-mounted case; this is unlike the BSD automounter (amd), which is a pure user space daemon. To use the automounter you need the user-space tools from the autofs package; you can find the location in . You also want to answer Y to "NFS file system support", below. If you want to use the newer version of the automounter with more features, say N here and say Y to "Kernel automounter v4 support", below. To compile this support as a module, choose M here: the module will be called autofs. If you are not a part of a fairly large, distributed network, you probably do not need an automounter, and can say N here. config AUTOFS4_FS tristate "Kernel automounter version 4 support (also supports v3)" help The automounter is a tool to automatically mount remote file systems on demand. This implementation is partially kernel-based to reduce overhead in the already-mounted case; this is unlike the BSD automounter (amd), which is a pure user space daemon. To use the automounter you need the user-space tools from ; you also want to answer Y to "NFS file system support", below. To compile this support as a module, choose M here: the module will be called autofs4. You will need to add "alias autofs autofs4" to your modules configuration file. If you are not a part of a fairly large, distributed network or don't have a laptop which needs to dynamically reconfigure to the local network, you probably do not need an automounter, and can say N here. config FUSE_FS tristate "Filesystem in Userspace support" help With FUSE it is possible to implement a fully functional filesystem in a userspace program. There's also companion library: libfuse. This library along with utilities is available from the FUSE homepage: See for more information. See for needed library/utility version. If you want to develop a userspace FS, or if you want to use a filesystem based on FUSE, answer Y or M. menu "CD-ROM/DVD Filesystems" config ISO9660_FS tristate "ISO 9660 CDROM file system support" help This is the standard file system used on CD-ROMs. It was previously known as "High Sierra File System" and is called "hsfs" on other Unix systems. The so-called Rock-Ridge extensions which allow for long Unix filenames and symbolic links are also supported by this driver. If you have a CD-ROM drive and want to do more with it than just listen to audio CDs and watch its LEDs, say Y (and read and the CD-ROM-HOWTO, available from ), thereby enlarging your kernel by about 27 KB; otherwise say N. To compile this file system support as a module, choose M here: the module will be called isofs. config JOLIET bool "Microsoft Joliet CDROM extensions" depends on ISO9660_FS select NLS help Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system which allows for long filenames in unicode format (unicode is the new 16 bit character code, successor to ASCII, which encodes the characters of almost all languages of the world; see for more information). Say Y here if you want to be able to read Joliet CD-ROMs under Linux. config ZISOFS bool "Transparent decompression extension" depends on ISO9660_FS select ZLIB_INFLATE help This is a Linux-specific extension to RockRidge which lets you store data in compressed form on a CD-ROM and have it transparently decompressed when the CD-ROM is accessed. See for the tools necessary to create such a filesystem. Say Y here if you want to be able to read such compressed CD-ROMs. config ZISOFS_FS # for fs/nls/Config.in tristate depends on ZISOFS default ISO9660_FS config UDF_FS tristate "UDF file system support" help This is the new file system used on some CD-ROMs and DVDs. Say Y if you intend to mount DVD discs or CDRW's written in packet mode, or if written to by other UDF utilities, such as DirectCD. Please read . To compile this file system support as a module, choose M here: the module will be called udf. If unsure, say N. config UDF_NLS bool default y depends on (UDF_FS=m && NLS) || (UDF_FS=y && NLS=y) endmenu menu "DOS/FAT/NT Filesystems" config FAT_FS tristate select NLS help If you want to use one of the FAT-based file systems (the MS-DOS and VFAT (Windows 95) file systems), then you must say Y or M here to include FAT support. You will then be able to mount partitions or diskettes with FAT-based file systems and transparently access the files on them, i.e. MSDOS files will look and behave just like all other Unix files. This FAT support is not a file system in itself, it only provides the foundation for the other file systems. You will have to say Y or M to at least one of "MSDOS fs support" or "VFAT fs support" in order to make use of it. Another way to read and write MSDOS floppies and hard drive partitions from within Linux (but not transparently) is with the mtools ("man mtools") program suite. You don't need to say Y here in order to do that. If you need to move large files on floppies between a DOS and a Linux box, say Y here, mount the floppy under Linux with an MSDOS file system and use GNU tar's M option. GNU tar is a program available for Unix and DOS ("man tar" or "info tar"). It is now also becoming possible to read and write compressed FAT file systems; read for details. The FAT support will enlarge your kernel by about 37 KB. If unsure, say Y. To compile this as a module, choose M here: the module will be called fat. Note that if you compile the FAT support as a module, you cannot compile any of the FAT-based file systems into the kernel -- they will have to be modules as well. config MSDOS_FS tristate "MSDOS fs support" select FAT_FS help This allows you to mount MSDOS partitions of your hard drive (unless they are compressed; to access compressed MSDOS partitions under Linux, you can either use the DOS emulator DOSEMU, described in the DOSEMU-HOWTO, available from , or try dmsdosfs in . If you intend to use dosemu with a non-compressed MSDOS partition, say Y here) and MSDOS floppies. This means that file access becomes transparent, i.e. the MSDOS files look and behave just like all other Unix files. If you have Windows 95 or Windows NT installed on your MSDOS partitions, you should use the VFAT file system (say Y to "VFAT fs support" below), or you will not be able to see the long filenames generated by Windows 95 / Windows NT. This option will enlarge your kernel by about 7 KB. If unsure, answer Y. This will only work if you said Y to "DOS FAT fs support" as well. To compile this as a module, choose M here: the module will be called msdos. config VFAT_FS tristate "VFAT (Windows-95) fs support" select FAT_FS help This option provides support for normal Windows file systems with long filenames. That includes non-compressed FAT-based file systems used by Windows 95, Windows 98, Windows NT 4.0, and the Unix programs from the mtools package. The VFAT support enlarges your kernel by about 10 KB and it only works if you said Y to the "DOS FAT fs support" above. Please read the file for details. If unsure, say Y. To compile this as a module, choose M here: the module will be called vfat. config FAT_DEFAULT_CODEPAGE int "Default codepage for FAT" depends on MSDOS_FS || VFAT_FS default 437 help This option should be set to the codepage of your FAT filesystems. It can be overridden with the "codepage" mount option. See for more information. config FAT_DEFAULT_IOCHARSET string "Default iocharset for FAT" depends on VFAT_FS default "iso8859-1" help Set this to the default input/output character set you'd like FAT to use. It should probably match the character set that most of your FAT filesystems use, and can be overridden with the "iocharset" mount option for FAT filesystems. Note that "utf8" is not recommended for FAT filesystems. If unsure, you shouldn't set "utf8" here. See for more information. config NTFS_FS tristate "NTFS file system support" select NLS help NTFS is the file system of Microsoft Windows NT, 2000, XP and 2003. Saying Y or M here enables read support. There is partial, but safe, write support available. For write support you must also say Y to "NTFS write support" below. There are also a number of user-space tools available, called ntfsprogs. These include ntfsundelete and ntfsresize, that work without NTFS support enabled in the kernel. This is a rewrite from scratch of Linux NTFS support and replaced the old NTFS code starting with Linux 2.5.11. A backport to the Linux 2.4 kernel series is separately available as a patch from the project web site. For more information see and . To compile this file system support as a module, choose M here: the module will be called ntfs. If you are not using Windows NT, 2000, XP or 2003 in addition to Linux on your computer it is safe to say N. config NTFS_DEBUG bool "NTFS debugging support" depends on NTFS_FS help If you are experiencing any problems with the NTFS file system, say Y here. This will result in additional consistency checks to be performed by the driver as well as additional debugging messages to be written to the system log. Note that debugging messages are disabled by default. To enable them, supply the option debug_msgs=1 at the kernel command line when booting the kernel or as an option to insmod when loading the ntfs module. Once the driver is active, you can enable debugging messages by doing (as root): echo 1 > /proc/sys/fs/ntfs-debug Replacing the "1" with "0" would disable debug messages. If you leave debugging messages disabled, this results in little overhead, but enabling debug messages results in very significant slowdown of the system. When reporting bugs, please try to have available a full dump of debugging messages while the misbehaviour was occurring. config NTFS_RW bool "NTFS write support" depends on NTFS_FS help This enables the partial, but safe, write support in the NTFS driver. The only supported operation is overwriting existing files, without changing the file length. No file or directory creation, deletion or renaming is possible. Note only non-resident files can be written to so you may find that some very small files (<500 bytes or so) cannot be written to. While we cannot guarantee that it will not damage any data, we have so far not received a single report where the driver would have damaged someones data so we assume it is perfectly safe to use. Note: While write support is safe in this version (a rewrite from scratch of the NTFS support), it should be noted that the old NTFS write support, included in Linux 2.5.10 and before (since 1997), is not safe. This is currently useful with TopologiLinux. TopologiLinux is run on top of any DOS/Microsoft Windows system without partitioning your hard disk. Unlike other Linux distributions TopologiLinux does not need its own partition. For more information see It is perfectly safe to say N here. endmenu menu "Pseudo filesystems" config PROC_FS bool "/proc file system support" help This is a virtual file system providing information about the status of the system. "Virtual" means that it doesn't take up any space on your hard disk: the files are created on the fly by the kernel when you try to access them. Also, you cannot read the files with older version of the program less: you need to use more or cat. It's totally cool; for example, "cat /proc/interrupts" gives information about what the different IRQs are used for at the moment (there is a small number of Interrupt ReQuest lines in your computer that are used by the attached devices to gain the CPU's attention -- often a source of trouble if two devices are mistakenly configured to use the same IRQ). The program procinfo to display some information about your system gathered from the /proc file system. Before you can use the /proc file system, it has to be mounted, meaning it has to be given a location in the directory hierarchy. That location should be /proc. A command such as "mount -t proc proc /proc" or the equivalent line in /etc/fstab does the job. The /proc file system is explained in the file and on the proc(5) manpage ("man 5 proc"). This option will enlarge your kernel by about 67 KB. Several programs depend on this, so everyone should say Y here. config PROC_KCORE bool "/proc/kcore support" if !ARM depends on PROC_FS && MMU config PROC_VMCORE bool "/proc/vmcore support (EXPERIMENTAL)" depends on PROC_FS && EXPERIMENTAL && CRASH_DUMP default y help Exports the dump image of crashed kernel in ELF format. config SYSFS bool "sysfs file system support" if EMBEDDED default y help The sysfs filesystem is a virtual filesystem that the kernel uses to export internal kernel objects, their attributes, and their relationships to one another. Users can use sysfs to ascertain useful information about the running kernel, such as the devices the kernel has discovered on each bus and which driver each is bound to. sysfs can also be used to tune devices and other kernel subsystems. Some system agents rely on the information in sysfs to operate. /sbin/hotplug uses device and object attributes in sysfs to assist in delegating policy decisions, like persistantly naming devices. sysfs is currently used by the block subsystem to mount the root partition. If sysfs is disabled you must specify the boot device on the kernel boot command line via its major and minor numbers. For example, "root=03:01" for /dev/hda1. Designers of embedded systems may wish to say N here to conserve space. config TMPFS bool "Virtual memory file system support (former shm fs)" help Tmpfs is a file system which keeps all files in virtual memory. Everything in tmpfs is temporary in the sense that no files will be created on your hard drive. The files live in memory and swap space. If you unmount a tmpfs instance, everything stored therein is lost. See for details. config HUGETLBFS bool "HugeTLB file system support" depends X86 || IA64 || PPC64 || SPARC64 || SUPERH || BROKEN help hugetlbfs is a filesystem backing for HugeTLB pages, based on ramfs. For architectures that support it, say Y here and read for details. If unsure, say N. config HUGETLB_PAGE def_bool HUGETLBFS config RAMFS bool default y ---help--- Ramfs is a file system which keeps all files in RAM. It allows read and write access. It is more of an programming example than a useable file system. If you need a file system which lives in RAM with limit checking use tmpfs. To compile this as a module, choose M here: the module will be called ramfs. config CONFIGFS_FS tristate "Userspace-driven configuration filesystem (EXPERIMENTAL)" depends on SYSFS && EXPERIMENTAL help configfs is a ram-based filesystem that provides the converse of sysfs's functionality. Where sysfs is a filesystem-based view of kernel objects, configfs is a filesystem-based manager of kernel objects, or config_items. Both sysfs and configfs can and should exist together on the same system. One is not a replacement for the other. endmenu menu "Miscellaneous filesystems" config ADFS_FS tristate "ADFS file system support (EXPERIMENTAL)" depends on EXPERIMENTAL help The Acorn Disc Filing System is the standard file system of the RiscOS operating system which runs on Acorn's ARM-based Risc PC systems and the Acorn Archimedes range of machines. If you say Y here, Linux will be able to read from ADFS partitions on hard drives and from ADFS-formatted floppy discs. If you also want to be able to write to those devices, say Y to "ADFS write support" below. The ADFS partition should be the first partition (i.e., /dev/[hs]d?1) on each of your drives. Please read the file for further details. To compile this code as a module, choose M here: the module will be called adfs. If unsure, say N. config ADFS_FS_RW bool "ADFS write support (DANGEROUS)" depends on ADFS_FS help If you say Y here, you will be able to write to ADFS partitions on hard drives and ADFS-formatted floppy disks. This is experimental codes, so if you're unsure, say N. config AFFS_FS tristate "Amiga FFS file system support (EXPERIMENTAL)" depends on EXPERIMENTAL help The Fast File System (FFS) is the common file system used on hard disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20). Say Y if you want to be able to read and write files from and to an Amiga FFS partition on your hard drive. Amiga floppies however cannot be read with this driver due to an incompatibility of the floppy controller used in an Amiga and the standard floppy controller in PCs and workstations. Read and . With this driver you can also mount disk files used by Bernd Schmidt's Un*X Amiga Emulator (). If you want to do this, you will also need to say Y or M to "Loop device support", above. To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. config HFS_FS tristate "Apple Macintosh file system support (EXPERIMENTAL)" depends on EXPERIMENTAL select NLS help If you say Y here, you will be able to mount Macintosh-formatted floppy disks and hard drive partitions with full read-write access. Please read to learn about the available mount options. To compile this file system support as a module, choose M here: the module will be called hfs. config HFSPLUS_FS tristate "Apple Extended HFS file system support" select NLS select NLS_UTF8 help If you say Y here, you will be able to mount extended format Macintosh-formatted hard drive partitions with full read-write access. This file system is often called HFS+ and was introduced with MacOS 8. It includes all Mac specific filesystem data such as data forks and creator codes, but it also has several UNIX style features such as file ownership and permissions. config BEFS_FS tristate "BeOS file system (BeFS) support (read only) (EXPERIMENTAL)" depends on EXPERIMENTAL select NLS help The BeOS File System (BeFS) is the native file system of Be, Inc's BeOS. Notable features include support for arbitrary attributes on files and directories, and database-like indeces on selected attributes. (Also note that this driver doesn't make those features available at this time). It is a 64 bit filesystem, so it supports extremly large volumes and files. If you use this filesystem, you should also say Y to at least one of the NLS (native language support) options below. If you don't know what this is about, say N. To compile this as a module, choose M here: the module will be called befs. config BEFS_DEBUG bool "Debug BeFS" depends on BEFS_FS help If you say Y here, you can use the 'debug' mount option to enable debugging output from the driver. config BFS_FS tristate "BFS file system support (EXPERIMENTAL)" depends on EXPERIMENTAL help Boot File System (BFS) is a file system used under SCO UnixWare to allow the bootloader access to the kernel image and other important files during the boot process. It is usually mounted under /stand and corresponds to the slice marked as "STAND" in the UnixWare partition. You should say Y if you want to read or write the files on your /stand slice from within Linux. You then also need to say Y to "UnixWare slices support", below. More information about the BFS file system is contained in the file . If you don't know what this is about, say N. To compile this as a module, choose M here: the module will be called bfs. Note that the file system of your root partition (the one containing the directory /) cannot be compiled as a module. config EFS_FS tristate "EFS file system support (read only) (EXPERIMENTAL)" depends on EXPERIMENTAL help EFS is an older file system used for non-ISO9660 CD-ROMs and hard disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer uses the XFS file system for hard disk partitions however). This implementation only offers read-only access. If you don't know what all this is about, it's safe to say N. For more information about EFS see its home page at . To compile the EFS file system support as a module, choose M here: the module will be called efs. config JFFS_FS tristate "Journalling Flash File System (JFFS) support" depends on MTD help JFFS is the Journaling Flash File System developed by Axis Communications in Sweden, aimed at providing a crash/powerdown-safe file system for disk-less embedded devices. Further information is available at (). config JFFS_FS_VERBOSE int "JFFS debugging verbosity (0 = quiet, 3 = noisy)" depends on JFFS_FS default "0" help Determines the verbosity level of the JFFS debugging messages. config JFFS_PROC_FS bool "JFFS stats available in /proc filesystem" depends on JFFS_FS && PROC_FS help Enabling this option will cause statistics from mounted JFFS file systems to be made available to the user in the /proc/fs/jffs/ directory. config JFFS2_FS tristate "Journalling Flash File System v2 (JFFS2) support" select CRC32 depends on MTD help JFFS2 is the second generation of the Journalling Flash File System for use on diskless embedded devices. It provides improved wear levelling, compression and support for hard links. You cannot use this on normal block devices, only on 'MTD' devices. Further information on the design and implementation of JFFS2 is available at . config JFFS2_FS_DEBUG int "JFFS2 debugging verbosity (0 = quiet, 2 = noisy)" depends on JFFS2_FS default "0" help This controls the amount of debugging messages produced by the JFFS2 code. Set it to zero for use in production systems. For evaluation, testing and debugging, it's advisable to set it to one. This will enable a few assertions and will print debugging messages at the KERN_DEBUG loglevel, where they won't normally be visible. Level 2 is unlikely to be useful - it enables extra debugging in certain areas which at one point needed debugging, but when the bugs were located and fixed, the detailed messages were relegated to level 2. If reporting bugs, please try to have available a full dump of the messages at debug level 1 while the misbehaviour was occurring. config JFFS2_FS_WRITEBUFFER bool "JFFS2 write-buffering support" depends on JFFS2_FS default y help This enables the write-buffering support in JFFS2. This functionality is required to support JFFS2 on the following types of flash devices: - NAND flash - NOR flash with transparent ECC - DataFlash config JFFS2_SUMMARY bool "JFFS2 summary support (EXPERIMENTAL)" depends on JFFS2_FS && EXPERIMENTAL default n help This feature makes it possible to use summary information for faster filesystem mount. The summary information can be inserted into a filesystem image by the utility 'sumtool'. If unsure, say 'N'. config JFFS2_COMPRESSION_OPTIONS bool "Advanced compression options for JFFS2" depends on JFFS2_FS default n help Enabling this option allows you to explicitly choose which compression modules, if any, are enabled in JFFS2. Removing compressors and mean you cannot read existing file systems, and enabling experimental compressors can mean that you write a file system which cannot be read by a standard kernel. If unsure, you should _definitely_ say 'N'. config JFFS2_ZLIB bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS select ZLIB_INFLATE select ZLIB_DEFLATE depends on JFFS2_FS default y help Zlib is designed to be a free, general-purpose, legally unencumbered, lossless data-compression library for use on virtually any computer hardware and operating system. See for further information. Say 'Y' if unsure. config JFFS2_RTIME bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS depends on JFFS2_FS default y help Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. config JFFS2_RUBIN bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS depends on JFFS2_FS default n help RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. choice prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS default JFFS2_CMODE_PRIORITY depends on JFFS2_FS help You can set here the default compression mode of JFFS2 from the available compression modes. Don't touch if unsure. config JFFS2_CMODE_NONE bool "no compression" help Uses no compression. config JFFS2_CMODE_PRIORITY bool "priority" help Tries the compressors in a predefinied order and chooses the first successful one. config JFFS2_CMODE_SIZE bool "size (EXPERIMENTAL)" help Tries all compressors and chooses the one which has the smallest result. endchoice config CRAMFS tristate "Compressed ROM file system support (cramfs)" select ZLIB_INFLATE help Saying Y here includes support for CramFs (Compressed ROM File System). CramFs is designed to be a simple, small, and compressed file system for ROM based embedded systems. CramFs is read-only, limited to 256MB file systems (with 16MB files), and doesn't support 16/32 bits uid/gid, hard links and timestamps. See and for further information. To compile this as a module, choose M here: the module will be called cramfs. Note that the root file system (the one containing the directory /) cannot be compiled as a module. If unsure, say N. config SQUASHFS tristate "SquashFS 3.1 - Squashed file system support" select ZLIB_INFLATE help Saying Y here includes support for SquashFS 3.1 (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.1 supports 64 bit filesystems and files (larger than 4GB), full uid/gid information, hard links and timestamps. Squashfs is intended for general read-only filesystem use, for archival use (i.e. in cases where a .tar.gz file may be used), and in embedded systems where low overhead is needed. Further information and filesystem tools are available from http://squashfs.sourceforge.net. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read . The module will be called squashfs. Note that the root file system (the one containing the directory /) cannot be compiled as a module. If unsure, say N. config SQUASHFS_EMBEDDED bool "Additional options for memory-constrained systems" depends on SQUASHFS default n help Saying Y here allows you to specify cache sizes and how Squashfs allocates memory. This is only intended for memory constrained systems. If unsure, say N. config SQUASHFS_FRAGMENT_CACHE_SIZE int "Number of fragments cached" if SQUASHFS_EMBEDDED depends on SQUASHFS default "3" help By default SquashFS caches the last 3 fragments read from the filesystem. Increasing this amount may mean SquashFS has to re-read fragments less often from disk, at the expense of extra system memory. Decreasing this amount will mean SquashFS uses less memory at the expense of extra reads from disk. Note there must be at least one cached fragment. Anything much more than three will probably not make much difference. config SQUASHFS_VMALLOC bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED depends on SQUASHFS default n help By default SquashFS uses kmalloc to obtain fragment cache memory. Kmalloc memory is the standard kernel allocator, but it can fail on memory constrained systems. Because of the way Vmalloc works, Vmalloc can succeed when kmalloc fails. Specifying this option will make SquashFS always use Vmalloc to allocate the fragment cache memory. If unsure, say N. config VXFS_FS tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" help FreeVxFS is a file system driver that support the VERITAS VxFS(TM) file system format. VERITAS VxFS(TM) is the standard file system of SCO UnixWare (and possibly others) and optionally available for Sunsoft Solaris, HP-UX and many other operating systems. Currently only readonly access is supported. NOTE: the file system type as used by mount(1), mount(2) and fstab(5) is 'vxfs' as it describes the file system format, not the actual driver. To compile this as a module, choose M here: the module will be called freevxfs. If unsure, say N. config HPFS_FS tristate "OS/2 HPFS file system support" help OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS is the file system used for organizing files on OS/2 hard disk partitions. Say Y if you want to be able to read files from and write files to an OS/2 HPFS partition on your hard drive. OS/2 floppies however are in regular MSDOS format, so you don't need this option in order to be able to read them. Read . To compile this file system support as a module, choose M here: the module will be called hpfs. If unsure, say N. config QNX4FS_FS tristate "QNX4 file system support (read only)" help This is the file system used by the real-time operating systems QNX 4 and QNX 6 (the latter is also called QNX RTP). Further information is available at . Say Y if you intend to mount QNX hard disks or floppies. Unless you say Y to "QNX4FS read-write support" below, you will only be able to read these file systems. To compile this file system support as a module, choose M here: the module will be called qnx4. If you don't know whether you need it, then you don't need it: answer N. config QNX4FS_RW bool "QNX4FS write support (DANGEROUS)" depends on QNX4FS_FS && EXPERIMENTAL && BROKEN help Say Y if you want to test write support for QNX4 file systems. It's currently broken, so for now: answer N. config SYSV_FS tristate "System V/Xenix/V7/Coherent file system support" help SCO, Xenix and Coherent are commercial Unix systems for Intel machines, and Version 7 was used on the DEC PDP-11. Saying Y here would allow you to read from their floppies and hard disk partitions. If you have floppies or hard disk partitions like that, it is likely that they contain binaries from those other Unix systems; in order to run these binaries, you will want to install linux-abi which is a a set of kernel modules that lets you run SCO, Xenix, Wyse, UnixWare, Dell Unix and System V programs under Linux. It is available via FTP (user: ftp) from ). NOTE: that will work only for binaries from Intel-based systems; PDP ones will have to wait until somebody ports Linux to -11 ;-) If you only intend to mount files from some other Unix over the network using NFS, you don't need the System V file system support (but you need NFS file system support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes (and even other operating systems) is given by the tar program ("man tar" or preferably "info tar"). Note also that this option has nothing whatsoever to do with the option "System V IPC". Read about the System V file system in . Saying Y here will enlarge your kernel by about 27 KB. To compile this as a module, choose M here: the module will be called sysv. If you haven't heard about all of this before, it's safe to say N. config UFS_FS tristate "UFS file system support (read only)" help BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD, OpenBSD and NeXTstep) use a file system called UFS. Some System V Unixes can create and mount hard disk partitions and diskettes using this file system as well. Saying Y here will allow you to read from these partitions; if you also want to write to them, say Y to the experimental "UFS file system write support", below. Please read the file for more information. The recently released UFS2 variant (used in FreeBSD 5.x) is READ-ONLY supported. If you only intend to mount files from some other Unix over the network using NFS, you don't need the UFS file system support (but you need NFS file system support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes (and even other operating systems) is given by the tar program ("man tar" or preferably "info tar"). When accessing NeXTstep files, you may need to convert them from the NeXT character set to the Latin1 character set; use the program recode ("info recode") for this purpose. To compile the UFS file system support as a module, choose M here: the module will be called ufs. If you haven't heard about all of this before, it's safe to say N. config UFS_FS_WRITE bool "UFS file system write support (DANGEROUS)" depends on UFS_FS && EXPERIMENTAL && BROKEN help Say Y here if you want to try writing to UFS partitions. This is experimental, so you should back up your UFS partitions beforehand. endmenu menu "Network File Systems" depends on NET config NFS_FS tristate "NFS file system support" depends on INET select LOCKD select SUNRPC select NFS_ACL_SUPPORT if NFS_V3_ACL help If you are connected to some other (usually local) Unix computer (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing on that computer (the NFS server) using the Network File Sharing protocol, say Y. "Mounting files" means that the client can access the files with usual UNIX commands as if they were sitting on the client's hard disk. For this to work, the server must run the programs nfsd and mountd (but does not need to have NFS file system support enabled in its kernel). NFS is explained in the Network Administrator's Guide, available from , on its man page: "man nfs", and in the NFS-HOWTO. A superior but less widely used alternative to NFS is provided by the Coda file system; see "Coda file system support" below. If you say Y here, you should have said Y to TCP/IP networking also. This option would enlarge your kernel by about 27 KB. To compile this file system support as a module, choose M here: the module will be called nfs. If you are configuring a diskless machine which will mount its root file system over NFS at boot time, say Y here and to "Kernel level IP autoconfiguration" above and to "Root file system on NFS" below. You cannot compile this driver as a module in this case. There are two packages designed for booting diskless machines over the net: netboot, available from , and Etherboot, available from . If you don't know what all this is about, say N. config NFS_V3 bool "Provide NFSv3 client support" depends on NFS_FS help Say Y here if you want your NFS client to be able to speak version 3 of the NFS protocol. If unsure, say Y. config NFS_V3_ACL bool "Provide client support for the NFSv3 ACL protocol extension" depends on NFS_V3 help Implement the NFSv3 ACL protocol extension for manipulating POSIX Access Control Lists. The server should also be compiled with the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option. If unsure, say N. config NFS_V4 bool "Provide NFSv4 client support (EXPERIMENTAL)" depends on NFS_FS && EXPERIMENTAL select RPCSEC_GSS_KRB5 help Say Y here if you want your NFS client to be able to speak the newer version 4 of the NFS protocol. Note: Requires auxiliary userspace daemons which may be found on http://www.citi.umich.edu/projects/nfsv4/ If unsure, say N. config NFS_DIRECTIO bool "Allow direct I/O on NFS files (EXPERIMENTAL)" depends on NFS_FS && EXPERIMENTAL help This option enables applications to perform uncached I/O on files in NFS file systems using the O_DIRECT open() flag. When O_DIRECT is set for a file, its data is not cached in the system's page cache. Data is moved to and from user-level application buffers directly. Unlike local disk-based file systems, NFS O_DIRECT has no alignment restrictions. Unless your program is designed to use O_DIRECT properly, you are much better off allowing the NFS client to manage data caching for you. Misusing O_DIRECT can cause poor server performance or network storms. This kernel build option defaults OFF to avoid exposing system administrators unwittingly to a potentially hazardous feature. For more details on NFS O_DIRECT, see fs/nfs/direct.c. If unsure, say N. This reduces the size of the NFS client, and causes open() to return EINVAL if a file residing in NFS is opened with the O_DIRECT flag. config NFSD tristate "NFS server support" depends on INET select LOCKD select SUNRPC select EXPORTFS select NFS_ACL_SUPPORT if NFSD_V3_ACL || NFSD_V2_ACL help If you want your Linux box to act as an NFS *server*, so that other computers on your local network which support NFS can access certain directories on your box transparently, you have two options: you can use the self-contained user space program nfsd, in which case you should say N here, or you can say Y and use the kernel based NFS server. The advantage of the kernel based solution is that it is faster. In either case, you will need support software; the respective locations are given in the file in the NFS section. If you say Y here, you will get support for version 2 of the NFS protocol (NFSv2). If you also want NFSv3, say Y to the next question as well. Please read the NFS-HOWTO, available from . To compile the NFS server support as a module, choose M here: the module will be called nfsd. If unsure, say N. config NFSD_V2_ACL bool depends on NFSD config NFSD_V3 bool "Provide NFSv3 server support" depends on NFSD help If you would like to include the NFSv3 server as well as the NFSv2 server, say Y here. If unsure, say Y. config NFSD_V3_ACL bool "Provide server support for the NFSv3 ACL protocol extension" depends on NFSD_V3 select NFSD_V2_ACL help Implement the NFSv3 ACL protocol extension for manipulating POSIX Access Control Lists on exported file systems. NFS clients should be compiled with the NFSv3 ACL protocol extension; see the CONFIG_NFS_V3_ACL option. If unsure, say N. config NFSD_V4 bool "Provide NFSv4 server support (EXPERIMENTAL)" depends on NFSD_V3 && EXPERIMENTAL select NFSD_TCP select CRYPTO_MD5 select CRYPTO select FS_POSIX_ACL help If you would like to include the NFSv4 server as well as the NFSv2 and NFSv3 servers, say Y here. This feature is experimental, and should only be used if you are interested in helping to test NFSv4. If unsure, say N. config NFSD_TCP bool "Provide NFS server over TCP support" depends on NFSD default y help If you want your NFS server to support TCP connections, say Y here. TCP connections usually perform better than the default UDP when the network is lossy or congested. If unsure, say Y. config ROOT_NFS bool "Root file system on NFS" depends on NFS_FS=y && IP_PNP help If you want your Linux box to mount its whole root file system (the one containing the directory /) from some other computer over the net via NFS (presumably because your box doesn't have a hard disk), say Y. Read for details. It is likely that in this case, you also want to say Y to "Kernel level IP autoconfiguration" so that your box can discover its network address at boot time. Most people say N here. config LOCKD tristate config LOCKD_V4 bool depends on NFSD_V3 || NFS_V3 default y config EXPORTFS tristate config NFS_ACL_SUPPORT tristate select FS_POSIX_ACL config NFS_COMMON bool depends on NFSD || NFS_FS default y config SUNRPC tristate config SUNRPC_GSS tristate config RPCSEC_GSS_KRB5 tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" depends on SUNRPC && EXPERIMENTAL select SUNRPC_GSS select CRYPTO select CRYPTO_MD5 select CRYPTO_DES help Provides for secure RPC calls by means of a gss-api mechanism based on Kerberos V5. This is required for NFSv4. Note: Requires an auxiliary userspace daemon which may be found on http://www.citi.umich.edu/projects/nfsv4/ If unsure, say N. config RPCSEC_GSS_SPKM3 tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)" depends on SUNRPC && EXPERIMENTAL select SUNRPC_GSS select CRYPTO select CRYPTO_MD5 select CRYPTO_DES select CRYPTO_CAST5 help Provides for secure RPC calls by means of a gss-api mechanism based on the SPKM3 public-key mechanism. Note: Requires an auxiliary userspace daemon which may be found on http://www.citi.umich.edu/projects/nfsv4/ If unsure, say N. config SMB_FS tristate "SMB file system support (to mount Windows shares etc.)" depends on INET select NLS help SMB (Server Message Block) is the protocol Windows for Workgroups (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share files and printers over local networks. Saying Y here allows you to mount their file systems (often called "shares" in this context) and access them just like any other Unix directory. Currently, this works only if the Windows machines use TCP/IP as the underlying transport protocol, and not NetBEUI. For details, read and the SMB-HOWTO, available from . Note: if you just want your box to act as an SMB *server* and make files and printing services available to Windows clients (which need to have a TCP/IP stack), you don't need to say Y here; you can use the program SAMBA (available from ) for that. General information about how to connect Linux, Windows machines and Macs is on the WWW at . To compile the SMB support as a module, choose M here: the module will be called smbfs. Most people say N, however. config SMB_NLS_DEFAULT bool "Use a default NLS" depends on SMB_FS help Enabling this will make smbfs use nls translations by default. You need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls settings and you need to give the default nls for the SMB server as CONFIG_SMB_NLS_REMOTE. The nls settings can be changed at mount time, if your smbmount supports that, using the codepage and iocharset parameters. smbmount from samba 2.2.0 or later supports this. config SMB_NLS_REMOTE string "Default Remote NLS Option" depends on SMB_NLS_DEFAULT default "cp437" help This setting allows you to specify a default value for which codepage the server uses. If this field is left blank no translations will be done by default. The local codepage/charset default to CONFIG_NLS_DEFAULT. The nls settings can be changed at mount time, if your smbmount supports that, using the codepage and iocharset parameters. smbmount from samba 2.2.0 or later supports this. config CIFS tristate "CIFS support (advanced network filesystem for Samba, Window and other CIFS compliant servers)" depends on INET select NLS help This is the client VFS module for the Common Internet File System (CIFS) protocol which is the successor to the Server Message Block (SMB) protocol, the native file sharing mechanism for most early PC operating systems. The CIFS protocol is fully supported by file servers such as Windows 2000 (including Windows 2003, NT 4 and Windows XP) as well by Samba (which provides excellent CIFS server support for Linux and many other operating systems). Limited support for Windows ME and similar servers is provided as well. You must use the smbfs client filesystem to access older SMB servers such as OS/2 and DOS. The intent of the cifs module is to provide an advanced network file system client for mounting to CIFS compliant servers, including support for dfs (hierarchical name space), secure per-user session establishment, safe distributed caching (oplock), optional packet signing, Unicode and other internationalization improvements, and optional Winbind (nsswitch) integration. You do not need to enable cifs if running only a (Samba) server. It is possible to enable both smbfs and cifs (e.g. if you are using CIFS for accessing Windows 2003 and Samba 3 servers, and smbfs for accessing old servers). If you need to mount to Samba or Windows from this machine, say Y. config CIFS_STATS bool "CIFS statistics" depends on CIFS help Enabling this option will cause statistics for each server share mounted by the cifs client to be displayed in /proc/fs/cifs/Stats config CIFS_STATS2 bool "CIFS extended statistics" depends on CIFS_STATS help Enabling this option will allow more detailed statistics on SMB request timing to be displayed in /proc/fs/cifs/DebugData and also allow optional logging of slow responses to dmesg (depending on the value of /proc/fs/cifs/cifsFYI, see fs/cifs/README for more details). These additional statistics may have a minor effect on performance and memory utilization. Unless you are a developer or are doing network performance analysis or tuning, say N. config CIFS_XATTR bool "CIFS extended attributes" depends on CIFS help Extended attributes are name:value pairs associated with inodes by the kernel or by users (see the attr(5) manual page, or visit for details). CIFS maps the name of extended attributes beginning with the user namespace prefix to SMB/CIFS EAs. EAs are stored on Windows servers without the user namespace prefix, but their names are seen by Linux cifs clients prefaced by the user namespace prefix. The system namespace (used by some filesystems to store ACLs) is not supported at this time. If unsure, say N. config CIFS_POSIX bool "CIFS POSIX Extensions" depends on CIFS_XATTR help Enabling this option will cause the cifs client to attempt to negotiate a newer dialect with servers, such as Samba 3.0.5 or later, that optionally can handle more POSIX like (rather than Windows like) file behavior. It also enables support for POSIX ACLs (getfacl and setfacl) to servers (such as Samba 3.10 and later) which can negotiate CIFS POSIX ACL support. If unsure, say N. config CIFS_EXPERIMENTAL bool "CIFS Experimental Features (EXPERIMENTAL)" depends on CIFS && EXPERIMENTAL help Enables cifs features under testing. These features are experimental and currently include support for writepages (multipage writebehind performance improvements) and directory change notification ie fcntl(F_DNOTIFY) as well as some security improvements. Some also depend on setting at runtime the pseudo-file /proc/fs/cifs/Experimental (which is disabled by default). See the file fs/cifs/README for more details. If unsure, say N. config CIFS_UPCALL bool "CIFS Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)" depends on CIFS_EXPERIMENTAL select CONNECTOR help Enables an upcall mechanism for CIFS which will be used to contact userspace helper utilities to provide SPNEGO packaged Kerberos tickets which are needed to mount to certain secure servers (for which more secure Kerberos authentication is required). If unsure, say N. config NCP_FS tristate "NCP file system support (to mount NetWare volumes)" depends on IPX!=n || INET help NCP (NetWare Core Protocol) is a protocol that runs over IPX and is used by Novell NetWare clients to talk to file servers. It is to IPX what NFS is to TCP/IP, if that helps. Saying Y here allows you to mount NetWare file server volumes and to access them just like any other Unix directory. For details, please read the file in the kernel source and the IPX-HOWTO from . You do not have to say Y here if you want your Linux box to act as a file *server* for Novell NetWare clients. General information about how to connect Linux, Windows machines and Macs is on the WWW at . To compile this as a module, choose M here: the module will be called ncpfs. Say N unless you are connected to a Novell network. source "fs/ncpfs/Kconfig" config CODA_FS tristate "Coda file system support (advanced network fs)" depends on INET help Coda is an advanced network file system, similar to NFS in that it enables you to mount file systems of a remote server and access them with regular Unix commands as if they were sitting on your hard disk. Coda has several advantages over NFS: support for disconnected operation (e.g. for laptops), read/write server replication, security model for authentication and encryption, persistent client caches and write back caching. If you say Y here, your Linux box will be able to act as a Coda *client*. You will need user level code as well, both for the client and server. Servers are currently user level, i.e. they need no kernel support. Please read and check out the Coda home page . To compile the coda client support as a module, choose M here: the module will be called coda. config CODA_FS_OLD_API bool "Use 96-bit Coda file identifiers" depends on CODA_FS help A new kernel-userspace API had to be introduced for Coda v6.0 to support larger 128-bit file identifiers as needed by the new realms implementation. However this new API is not backward compatible with older clients. If you really need to run the old Coda userspace cache manager then say Y. For most cases you probably want to say N. config AFS_FS # for fs/nls/Config.in tristate "Andrew File System support (AFS) (Experimental)" depends on INET && EXPERIMENTAL select RXRPC help If you say Y here, you will get an experimental Andrew File System driver. It currently only supports unsecured read-only AFS access. See for more intormation. If unsure, say N. config RXRPC tristate config 9P_FS tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" depends on INET && EXPERIMENTAL help If you say Y here, you will get experimental support for Plan 9 resource sharing via the 9P2000 protocol. See for more information. If unsure, say N. endmenu menu "Partition Types" source "fs/partitions/Kconfig" endmenu source "fs/nls/Kconfig" endmenu linux-2.6.17/fs/pnode.h0000644000000000000000000000176510564537530013353 0ustar rootroot/* * linux/fs/pnode.h * * (C) Copyright IBM Corporation 2005. * Released under GPL v2. * */ #ifndef _LINUX_PNODE_H #define _LINUX_PNODE_H #include #include #define IS_MNT_SHARED(mnt) (mnt->mnt_flags & MNT_SHARED) #define IS_MNT_SLAVE(mnt) (mnt->mnt_master) #define IS_MNT_NEW(mnt) (!mnt->mnt_namespace) #define CLEAR_MNT_SHARED(mnt) (mnt->mnt_flags &= ~MNT_SHARED) #define IS_MNT_UNBINDABLE(mnt) (mnt->mnt_flags & MNT_UNBINDABLE) #define CL_EXPIRE 0x01 #define CL_SLAVE 0x02 #define CL_COPY_ALL 0x04 #define CL_MAKE_SHARED 0x08 #define CL_PROPAGATION 0x10 static inline void set_mnt_shared(struct vfsmount *mnt) { mnt->mnt_flags &= ~MNT_PNODE_MASK; mnt->mnt_flags |= MNT_SHARED; } void change_mnt_propagation(struct vfsmount *, int); int propagate_mnt(struct vfsmount *, struct dentry *, struct vfsmount *, struct list_head *); int propagate_umount(struct list_head *); int propagate_mount_busy(struct vfsmount *, int); #endif /* _LINUX_PNODE_H */ linux-2.6.17/fs/fifo.c0000644000000000000000000000623010564537530013154 0ustar rootroot/* * linux/fs/fifo.c * * written by Paul H. Hargrove * * Fixes: * 10-06-1999, AV: fixed OOM handling in fifo_open(), moved * initialization there, switched to external * allocation of pipe_inode_info. */ #include #include #include #include #include static void wait_for_partner(struct inode* inode, unsigned int *cnt) { int cur = *cnt; while (cur == *cnt) { pipe_wait(inode->i_pipe); if (signal_pending(current)) break; } } static void wake_up_partner(struct inode* inode) { wake_up_interruptible(&inode->i_pipe->wait); } static int fifo_open(struct inode *inode, struct file *filp) { struct pipe_inode_info *pipe; int ret; mutex_lock(&inode->i_mutex); pipe = inode->i_pipe; if (!pipe) { ret = -ENOMEM; pipe = alloc_pipe_info(inode); if (!pipe) goto err_nocleanup; inode->i_pipe = pipe; } filp->f_version = 0; /* We can only do regular read/write on fifos */ filp->f_mode &= (FMODE_READ | FMODE_WRITE); switch (filp->f_mode) { case 1: /* * O_RDONLY * POSIX.1 says that O_NONBLOCK means return with the FIFO * opened, even when there is no process writing the FIFO. */ filp->f_op = &read_fifo_fops; pipe->r_counter++; if (pipe->readers++ == 0) wake_up_partner(inode); if (!pipe->writers) { if ((filp->f_flags & O_NONBLOCK)) { /* suppress POLLHUP until we have * seen a writer */ filp->f_version = pipe->w_counter; } else { wait_for_partner(inode, &pipe->w_counter); if(signal_pending(current)) goto err_rd; } } break; case 2: /* * O_WRONLY * POSIX.1 says that O_NONBLOCK means return -1 with * errno=ENXIO when there is no process reading the FIFO. */ ret = -ENXIO; if ((filp->f_flags & O_NONBLOCK) && !pipe->readers) goto err; filp->f_op = &write_fifo_fops; pipe->w_counter++; if (!pipe->writers++) wake_up_partner(inode); if (!pipe->readers) { wait_for_partner(inode, &pipe->r_counter); if (signal_pending(current)) goto err_wr; } break; case 3: /* * O_RDWR * POSIX.1 leaves this case "undefined" when O_NONBLOCK is set. * This implementation will NEVER block on a O_RDWR open, since * the process can at least talk to itself. */ filp->f_op = &rdwr_fifo_fops; pipe->readers++; pipe->writers++; pipe->r_counter++; pipe->w_counter++; if (pipe->readers == 1 || pipe->writers == 1) wake_up_partner(inode); break; default: ret = -EINVAL; goto err; } /* Ok! */ mutex_unlock(&inode->i_mutex); return 0; err_rd: if (!--pipe->readers) wake_up_interruptible(&pipe->wait); ret = -ERESTARTSYS; goto err; err_wr: if (!--pipe->writers) wake_up_interruptible(&pipe->wait); ret = -ERESTARTSYS; goto err; err: if (!pipe->readers && !pipe->writers) free_pipe_info(inode); err_nocleanup: mutex_unlock(&inode->i_mutex); return ret; } /* * Dummy default file-operations: the only thing this does * is contain the open that then fills in the correct operations * depending on the access mode of the file... */ const struct file_operations def_fifo_fops = { .open = fifo_open, /* will set read or write pipe_fops */ }; linux-2.6.17/fs/quota_v1.c0000644000000000000000000001331410564537530013771 0ustar rootroot#include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Jan Kara"); MODULE_DESCRIPTION("Old quota format support"); MODULE_LICENSE("GPL"); static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d) { m->dqb_ihardlimit = d->dqb_ihardlimit; m->dqb_isoftlimit = d->dqb_isoftlimit; m->dqb_curinodes = d->dqb_curinodes; m->dqb_bhardlimit = d->dqb_bhardlimit; m->dqb_bsoftlimit = d->dqb_bsoftlimit; m->dqb_curspace = ((qsize_t)d->dqb_curblocks) << QUOTABLOCK_BITS; m->dqb_itime = d->dqb_itime; m->dqb_btime = d->dqb_btime; } static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m) { d->dqb_ihardlimit = m->dqb_ihardlimit; d->dqb_isoftlimit = m->dqb_isoftlimit; d->dqb_curinodes = m->dqb_curinodes; d->dqb_bhardlimit = m->dqb_bhardlimit; d->dqb_bsoftlimit = m->dqb_bsoftlimit; d->dqb_curblocks = toqb(m->dqb_curspace); d->dqb_itime = m->dqb_itime; d->dqb_btime = m->dqb_btime; } static int v1_read_dqblk(struct dquot *dquot) { int type = dquot->dq_type; struct v1_disk_dqblk dqblk; if (!sb_dqopt(dquot->dq_sb)->files[type]) return -EINVAL; /* Set structure to 0s in case read fails/is after end of file */ memset(&dqblk, 0, sizeof(struct v1_disk_dqblk)); dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk); if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 && dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0) set_bit(DQ_FAKE_B, &dquot->dq_flags); dqstats.reads++; return 0; } static int v1_commit_dqblk(struct dquot *dquot) { short type = dquot->dq_type; ssize_t ret; struct v1_disk_dqblk dqblk; v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb); if (dquot->dq_id == 0) { dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace; dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace; } ret = 0; if (sb_dqopt(dquot->dq_sb)->files[type]) ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); if (ret != sizeof(struct v1_disk_dqblk)) { printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", dquot->dq_sb->s_id); if (ret >= 0) ret = -EIO; goto out; } ret = 0; out: dqstats.writes++; return ret; } /* Magics of new quota format */ #define V2_INITQMAGICS {\ 0xd9c01f11, /* USRQUOTA */\ 0xd9c01927 /* GRPQUOTA */\ } /* Header of new quota format */ struct v2_disk_dqheader { __le32 dqh_magic; /* Magic number identifying file */ __le32 dqh_version; /* File version */ }; static int v1_check_quota_file(struct super_block *sb, int type) { struct inode *inode = sb_dqopt(sb)->files[type]; ulong blocks; size_t off; struct v2_disk_dqheader dqhead; ssize_t size; loff_t isize; static const uint quota_magics[] = V2_INITQMAGICS; isize = i_size_read(inode); if (!isize) return 0; blocks = isize >> BLOCK_SIZE_BITS; off = isize & (BLOCK_SIZE - 1); if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) % sizeof(struct v1_disk_dqblk)) return 0; /* Doublecheck whether we didn't get file with new format - with old quotactl() this could happen */ size = sb->s_op->quota_read(sb, type, (char *)&dqhead, sizeof(struct v2_disk_dqheader), 0); if (size != sizeof(struct v2_disk_dqheader)) return 1; /* Probably not new format */ if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type]) return 1; /* Definitely not new format */ printk(KERN_INFO "VFS: %s: Refusing to turn on old quota format on given file. It probably contains newer quota format.\n", sb->s_id); return 0; /* Seems like a new format file -> refuse it */ } static int v1_read_file_info(struct super_block *sb, int type) { struct quota_info *dqopt = sb_dqopt(sb); struct v1_disk_dqblk dqblk; int ret; if ((ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(0))) != sizeof(struct v1_disk_dqblk)) { if (ret >= 0) ret = -EIO; goto out; } ret = 0; dqopt->info[type].dqi_igrace = dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME; dqopt->info[type].dqi_bgrace = dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME; out: return ret; } static int v1_write_file_info(struct super_block *sb, int type) { struct quota_info *dqopt = sb_dqopt(sb); struct v1_disk_dqblk dqblk; int ret; dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY; if ((ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(0))) != sizeof(struct v1_disk_dqblk)) { if (ret >= 0) ret = -EIO; goto out; } dqblk.dqb_itime = dqopt->info[type].dqi_igrace; dqblk.dqb_btime = dqopt->info[type].dqi_bgrace; ret = sb->s_op->quota_write(sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(0)); if (ret == sizeof(struct v1_disk_dqblk)) ret = 0; else if (ret > 0) ret = -EIO; out: return ret; } static struct quota_format_ops v1_format_ops = { .check_quota_file = v1_check_quota_file, .read_file_info = v1_read_file_info, .write_file_info = v1_write_file_info, .free_file_info = NULL, .read_dqblk = v1_read_dqblk, .commit_dqblk = v1_commit_dqblk, }; static struct quota_format_type v1_quota_format = { .qf_fmt_id = QFMT_VFS_OLD, .qf_ops = &v1_format_ops, .qf_owner = THIS_MODULE }; static int __init init_v1_quota_format(void) { return register_quota_format(&v1_quota_format); } static void __exit exit_v1_quota_format(void) { unregister_quota_format(&v1_quota_format); } module_init(init_v1_quota_format); module_exit(exit_v1_quota_format); linux-2.6.17/fs/hugetlbfs/0000755000000000000000000000000010574207612014043 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/0000755000000000000000000000000010613366603014727 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/format0000644000000000000000000000000210574207612016132 0ustar rootroot8 linux-2.6.17/fs/hugetlbfs/.svn/entries0000444000000000000000000000075210613366571016331 0ustar rootroot8 dir 88 svn+ssh://192.168.2.6/home/svn-root/repos/FVT/linux-2.6.17/fs/hugetlbfs svn+ssh://192.168.2.6/home/svn-root/repos/FVT 2007-02-14T07:50:48.156956Z 1 root svn:special svn:externals svn:needs-lock 4bdaaa2a-274d-416c-a84c-08d2865ac13c Makefile file 2007-02-14T07:50:48.000000Z 96e39787824129b083ef31659d6fd9db 2007-02-14T07:50:48.156956Z 1 root inode.c file 2007-02-14T07:50:48.000000Z e0119e756b9219f120c7b4d99218c862 2007-02-14T07:50:48.156956Z 1 root linux-2.6.17/fs/hugetlbfs/.svn/tmp/0000755000000000000000000000000010613366571015533 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/tmp/props/0000755000000000000000000000000010574207612016672 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/tmp/text-base/0000755000000000000000000000000010574207612017423 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/tmp/prop-base/0000755000000000000000000000000010574207612017417 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/props/0000755000000000000000000000000010574207612016072 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/text-base/0000755000000000000000000000000010574207612016623 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/.svn/text-base/Makefile.svn-base0000444000000000000000000000016010574207612021773 0ustar rootroot# # Makefile for the linux ramfs routines. # obj-$(CONFIG_HUGETLBFS) += hugetlbfs.o hugetlbfs-objs := inode.o linux-2.6.17/fs/hugetlbfs/.svn/text-base/inode.c.svn-base0000444000000000000000000004702710574207612021612 0ustar rootroot/* * hugetlbpage-backed filesystem. Based on ramfs. * * William Irwin, 2002 * * Copyright (C) 2002 Linus Torvalds. */ #include #include #include #include /* remove ASAP */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* some random number */ #define HUGETLBFS_MAGIC 0x958458f6 static struct super_operations hugetlbfs_ops; static struct address_space_operations hugetlbfs_aops; const struct file_operations hugetlbfs_file_operations; static struct inode_operations hugetlbfs_dir_inode_operations; static struct inode_operations hugetlbfs_inode_operations; static struct backing_dev_info hugetlbfs_backing_dev_info = { .ra_pages = 0, /* No readahead */ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, }; int sysctl_hugetlb_shm_group; static void huge_pagevec_release(struct pagevec *pvec) { int i; for (i = 0; i < pagevec_count(pvec); ++i) put_page(pvec->pages[i]); pagevec_reinit(pvec); } static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file->f_dentry->d_inode; struct hugetlbfs_inode_info *info = HUGETLBFS_I(inode); loff_t len, vma_len; int ret; if (vma->vm_pgoff & (HPAGE_SIZE / PAGE_SIZE - 1)) return -EINVAL; if (vma->vm_start & ~HPAGE_MASK) return -EINVAL; if (vma->vm_end & ~HPAGE_MASK) return -EINVAL; if (vma->vm_end - vma->vm_start < HPAGE_SIZE) return -EINVAL; vma_len = (loff_t)(vma->vm_end - vma->vm_start); mutex_lock(&inode->i_mutex); file_accessed(file); vma->vm_flags |= VM_HUGETLB | VM_RESERVED; vma->vm_ops = &hugetlb_vm_ops; ret = -ENOMEM; len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size) goto out; if (vma->vm_flags & VM_MAYSHARE) if (hugetlb_extend_reservation(info, len >> HPAGE_SHIFT) != 0) goto out; ret = 0; hugetlb_prefault_arch_hook(vma->vm_mm); if (inode->i_size < len) inode->i_size = len; out: mutex_unlock(&inode->i_mutex); return ret; } /* * Called under down_write(mmap_sem). */ #ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); #else static unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; unsigned long start_addr; if (len & ~HPAGE_MASK) return -EINVAL; if (len > TASK_SIZE) return -ENOMEM; if (addr) { addr = ALIGN(addr, HPAGE_SIZE); vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && (!vma || addr + len <= vma->vm_start)) return addr; } start_addr = mm->free_area_cache; if (len <= mm->cached_hole_size) start_addr = TASK_UNMAPPED_BASE; full_search: addr = ALIGN(start_addr, HPAGE_SIZE); for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { /* At this point: (!vma || addr < vma->vm_end). */ if (TASK_SIZE - len < addr) { /* * Start a new search - just in case we missed * some holes. */ if (start_addr != TASK_UNMAPPED_BASE) { start_addr = TASK_UNMAPPED_BASE; goto full_search; } return -ENOMEM; } if (!vma || addr + len <= vma->vm_start) return addr; addr = ALIGN(vma->vm_end, HPAGE_SIZE); } } #endif /* * Read a page. Again trivial. If it didn't already exist * in the page cache, it is zero-filled. */ static int hugetlbfs_readpage(struct file *file, struct page * page) { unlock_page(page); return -EINVAL; } static int hugetlbfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { return -EINVAL; } static int hugetlbfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { return -EINVAL; } static void truncate_huge_page(struct page *page) { clear_page_dirty(page); ClearPageUptodate(page); remove_from_page_cache(page); put_page(page); } static void truncate_hugepages(struct inode *inode, loff_t lstart) { struct address_space *mapping = &inode->i_data; const pgoff_t start = lstart >> HPAGE_SHIFT; struct pagevec pvec; pgoff_t next; int i; hugetlb_truncate_reservation(HUGETLBFS_I(inode), lstart >> HPAGE_SHIFT); if (!mapping->nrpages) return; pagevec_init(&pvec, 0); next = start; while (1) { if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { if (next == start) break; next = start; continue; } for (i = 0; i < pagevec_count(&pvec); ++i) { struct page *page = pvec.pages[i]; lock_page(page); if (page->index > next) next = page->index; ++next; truncate_huge_page(page); unlock_page(page); hugetlb_put_quota(mapping); } huge_pagevec_release(&pvec); } BUG_ON(!lstart && mapping->nrpages); } static void hugetlbfs_delete_inode(struct inode *inode) { truncate_hugepages(inode, 0); clear_inode(inode); } static void hugetlbfs_forget_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; if (!hlist_unhashed(&inode->i_hash)) { if (!(inode->i_state & (I_DIRTY|I_LOCK))) list_move(&inode->i_list, &inode_unused); inodes_stat.nr_unused++; if (!sb || (sb->s_flags & MS_ACTIVE)) { spin_unlock(&inode_lock); return; } inode->i_state |= I_WILL_FREE; spin_unlock(&inode_lock); /* * write_inode_now is a noop as we set BDI_CAP_NO_WRITEBACK * in our backing_dev_info. */ write_inode_now(inode, 1); spin_lock(&inode_lock); inode->i_state &= ~I_WILL_FREE; inodes_stat.nr_unused--; hlist_del_init(&inode->i_hash); } list_del_init(&inode->i_list); list_del_init(&inode->i_sb_list); inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); truncate_hugepages(inode, 0); clear_inode(inode); destroy_inode(inode); } static void hugetlbfs_drop_inode(struct inode *inode) { if (!inode->i_nlink) generic_delete_inode(inode); else hugetlbfs_forget_inode(inode); } /* * h_pgoff is in HPAGE_SIZE units. * vma->vm_pgoff is in PAGE_SIZE units. */ static inline void hugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff) { struct vm_area_struct *vma; struct prio_tree_iter iter; vma_prio_tree_foreach(vma, &iter, root, h_pgoff, ULONG_MAX) { unsigned long h_vm_pgoff; unsigned long v_offset; h_vm_pgoff = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT); v_offset = (h_pgoff - h_vm_pgoff) << HPAGE_SHIFT; /* * Is this VMA fully outside the truncation point? */ if (h_vm_pgoff >= h_pgoff) v_offset = 0; unmap_hugepage_range(vma, vma->vm_start + v_offset, vma->vm_end); } } /* * Expanding truncates are not allowed. */ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) { unsigned long pgoff; struct address_space *mapping = inode->i_mapping; if (offset > inode->i_size) return -EINVAL; BUG_ON(offset & ~HPAGE_MASK); pgoff = offset >> HPAGE_SHIFT; inode->i_size = offset; spin_lock(&mapping->i_mmap_lock); if (!prio_tree_empty(&mapping->i_mmap)) hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff); spin_unlock(&mapping->i_mmap_lock); truncate_hugepages(inode, offset); return 0; } static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int error; unsigned int ia_valid = attr->ia_valid; BUG_ON(!inode); error = inode_change_ok(inode, attr); if (error) goto out; if (ia_valid & ATTR_SIZE) { error = -EINVAL; if (!(attr->ia_size & ~HPAGE_MASK)) error = hugetlb_vmtruncate(inode, attr->ia_size); if (error) goto out; attr->ia_valid &= ~ATTR_SIZE; } error = inode_setattr(inode, attr); out: return error; } static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, gid_t gid, int mode, dev_t dev) { struct inode *inode; inode = new_inode(sb); if (inode) { struct hugetlbfs_inode_info *info; inode->i_mode = mode; inode->i_uid = uid; inode->i_gid = gid; inode->i_blksize = HPAGE_SIZE; inode->i_blocks = 0; inode->i_mapping->a_ops = &hugetlbfs_aops; inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; info = HUGETLBFS_I(inode); mpol_shared_policy_init(&info->policy, MPOL_DEFAULT, NULL); switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); break; case S_IFREG: inode->i_op = &hugetlbfs_inode_operations; inode->i_fop = &hugetlbfs_file_operations; break; case S_IFDIR: inode->i_op = &hugetlbfs_dir_inode_operations; inode->i_fop = &simple_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; break; } } return inode; } /* * File creation. Allocate an inode, and we're done.. */ static int hugetlbfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode; int error = -ENOSPC; gid_t gid; if (dir->i_mode & S_ISGID) { gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; } else { gid = current->fsgid; } inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, mode, dev); if (inode) { dir->i_ctime = dir->i_mtime = CURRENT_TIME; d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ error = 0; } return error; } static int hugetlbfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0); if (!retval) dir->i_nlink++; return retval; } static int hugetlbfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { return hugetlbfs_mknod(dir, dentry, mode | S_IFREG, 0); } static int hugetlbfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct inode *inode; int error = -ENOSPC; gid_t gid; if (dir->i_mode & S_ISGID) gid = dir->i_gid; else gid = current->fsgid; inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, S_IFLNK|S_IRWXUGO, 0); if (inode) { int l = strlen(symname)+1; error = page_symlink(inode, symname, l); if (!error) { d_instantiate(dentry, inode); dget(dentry); } else iput(inode); } dir->i_ctime = dir->i_mtime = CURRENT_TIME; return error; } /* * For direct-IO reads into hugetlb pages */ static int hugetlbfs_set_page_dirty(struct page *page) { return 0; } static int hugetlbfs_statfs(struct super_block *sb, struct kstatfs *buf) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); buf->f_type = HUGETLBFS_MAGIC; buf->f_bsize = HPAGE_SIZE; if (sbinfo) { spin_lock(&sbinfo->stat_lock); /* If no limits set, just report 0 for max/free/used * blocks, like simple_statfs() */ if (sbinfo->max_blocks >= 0) { buf->f_blocks = sbinfo->max_blocks; buf->f_bavail = buf->f_bfree = sbinfo->free_blocks; buf->f_files = sbinfo->max_inodes; buf->f_ffree = sbinfo->free_inodes; } spin_unlock(&sbinfo->stat_lock); } buf->f_namelen = NAME_MAX; return 0; } static void hugetlbfs_put_super(struct super_block *sb) { struct hugetlbfs_sb_info *sbi = HUGETLBFS_SB(sb); if (sbi) { sb->s_fs_info = NULL; kfree(sbi); } } static inline int hugetlbfs_dec_free_inodes(struct hugetlbfs_sb_info *sbinfo) { if (sbinfo->free_inodes >= 0) { spin_lock(&sbinfo->stat_lock); if (unlikely(!sbinfo->free_inodes)) { spin_unlock(&sbinfo->stat_lock); return 0; } sbinfo->free_inodes--; spin_unlock(&sbinfo->stat_lock); } return 1; } static void hugetlbfs_inc_free_inodes(struct hugetlbfs_sb_info *sbinfo) { if (sbinfo->free_inodes >= 0) { spin_lock(&sbinfo->stat_lock); sbinfo->free_inodes++; spin_unlock(&sbinfo->stat_lock); } } static kmem_cache_t *hugetlbfs_inode_cachep; static struct inode *hugetlbfs_alloc_inode(struct super_block *sb) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); struct hugetlbfs_inode_info *p; if (unlikely(!hugetlbfs_dec_free_inodes(sbinfo))) return NULL; p = kmem_cache_alloc(hugetlbfs_inode_cachep, SLAB_KERNEL); if (unlikely(!p)) { hugetlbfs_inc_free_inodes(sbinfo); return NULL; } p->prereserved_hpages = 0; return &p->vfs_inode; } static void hugetlbfs_destroy_inode(struct inode *inode) { hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb)); mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy); kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode)); } static struct address_space_operations hugetlbfs_aops = { .readpage = hugetlbfs_readpage, .prepare_write = hugetlbfs_prepare_write, .commit_write = hugetlbfs_commit_write, .set_page_dirty = hugetlbfs_set_page_dirty, }; static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags) { struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) inode_init_once(&ei->vfs_inode); } const struct file_operations hugetlbfs_file_operations = { .mmap = hugetlbfs_file_mmap, .fsync = simple_sync_file, .get_unmapped_area = hugetlb_get_unmapped_area, }; static struct inode_operations hugetlbfs_dir_inode_operations = { .create = hugetlbfs_create, .lookup = simple_lookup, .link = simple_link, .unlink = simple_unlink, .symlink = hugetlbfs_symlink, .mkdir = hugetlbfs_mkdir, .rmdir = simple_rmdir, .mknod = hugetlbfs_mknod, .rename = simple_rename, .setattr = hugetlbfs_setattr, }; static struct inode_operations hugetlbfs_inode_operations = { .setattr = hugetlbfs_setattr, }; static struct super_operations hugetlbfs_ops = { .alloc_inode = hugetlbfs_alloc_inode, .destroy_inode = hugetlbfs_destroy_inode, .statfs = hugetlbfs_statfs, .delete_inode = hugetlbfs_delete_inode, .drop_inode = hugetlbfs_drop_inode, .put_super = hugetlbfs_put_super, }; static int hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) { char *opt, *value, *rest; if (!options) return 0; while ((opt = strsep(&options, ",")) != NULL) { if (!*opt) continue; value = strchr(opt, '='); if (!value || !*value) return -EINVAL; else *value++ = '\0'; if (!strcmp(opt, "uid")) pconfig->uid = simple_strtoul(value, &value, 0); else if (!strcmp(opt, "gid")) pconfig->gid = simple_strtoul(value, &value, 0); else if (!strcmp(opt, "mode")) pconfig->mode = simple_strtoul(value,&value,0) & 0777U; else if (!strcmp(opt, "size")) { unsigned long long size = memparse(value, &rest); if (*rest == '%') { size <<= HPAGE_SHIFT; size *= max_huge_pages; do_div(size, 100); rest++; } size &= HPAGE_MASK; pconfig->nr_blocks = (size >> HPAGE_SHIFT); value = rest; } else if (!strcmp(opt,"nr_inodes")) { pconfig->nr_inodes = memparse(value, &rest); value = rest; } else return -EINVAL; if (*value) return -EINVAL; } return 0; } static int hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode * inode; struct dentry * root; int ret; struct hugetlbfs_config config; struct hugetlbfs_sb_info *sbinfo; config.nr_blocks = -1; /* No limit on size by default */ config.nr_inodes = -1; /* No limit on number of inodes by default */ config.uid = current->fsuid; config.gid = current->fsgid; config.mode = 0755; ret = hugetlbfs_parse_options(data, &config); if (ret) return ret; sbinfo = kmalloc(sizeof(struct hugetlbfs_sb_info), GFP_KERNEL); if (!sbinfo) return -ENOMEM; sb->s_fs_info = sbinfo; spin_lock_init(&sbinfo->stat_lock); sbinfo->max_blocks = config.nr_blocks; sbinfo->free_blocks = config.nr_blocks; sbinfo->max_inodes = config.nr_inodes; sbinfo->free_inodes = config.nr_inodes; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = HPAGE_SIZE; sb->s_blocksize_bits = HPAGE_SHIFT; sb->s_magic = HUGETLBFS_MAGIC; sb->s_op = &hugetlbfs_ops; sb->s_time_gran = 1; inode = hugetlbfs_get_inode(sb, config.uid, config.gid, S_IFDIR | config.mode, 0); if (!inode) goto out_free; root = d_alloc_root(inode); if (!root) { iput(inode); goto out_free; } sb->s_root = root; return 0; out_free: kfree(sbinfo); return -ENOMEM; } int hugetlb_get_quota(struct address_space *mapping) { int ret = 0; struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb); if (sbinfo->free_blocks > -1) { spin_lock(&sbinfo->stat_lock); if (sbinfo->free_blocks > 0) sbinfo->free_blocks--; else ret = -ENOMEM; spin_unlock(&sbinfo->stat_lock); } return ret; } void hugetlb_put_quota(struct address_space *mapping) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb); if (sbinfo->free_blocks > -1) { spin_lock(&sbinfo->stat_lock); sbinfo->free_blocks++; spin_unlock(&sbinfo->stat_lock); } } static struct super_block *hugetlbfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super); } static struct file_system_type hugetlbfs_fs_type = { .name = "hugetlbfs", .get_sb = hugetlbfs_get_sb, .kill_sb = kill_litter_super, }; static struct vfsmount *hugetlbfs_vfsmount; static int can_do_hugetlb_shm(void) { return likely(capable(CAP_IPC_LOCK) || in_group_p(sysctl_hugetlb_shm_group) || can_do_mlock()); } struct file *hugetlb_zero_setup(size_t size) { int error = -ENOMEM; struct file *file; struct inode *inode; struct dentry *dentry, *root; struct qstr quick_string; char buf[16]; static atomic_t counter; if (!can_do_hugetlb_shm()) return ERR_PTR(-EPERM); if (!user_shm_lock(size, current->user)) return ERR_PTR(-ENOMEM); root = hugetlbfs_vfsmount->mnt_root; snprintf(buf, 16, "%u", atomic_inc_return(&counter)); quick_string.name = buf; quick_string.len = strlen(quick_string.name); quick_string.hash = 0; dentry = d_alloc(root, &quick_string); if (!dentry) goto out_shm_unlock; error = -ENFILE; file = get_empty_filp(); if (!file) goto out_dentry; error = -ENOSPC; inode = hugetlbfs_get_inode(root->d_sb, current->fsuid, current->fsgid, S_IFREG | S_IRWXUGO, 0); if (!inode) goto out_file; error = -ENOMEM; if (hugetlb_extend_reservation(HUGETLBFS_I(inode), size >> HPAGE_SHIFT) != 0) goto out_inode; d_instantiate(dentry, inode); inode->i_size = size; inode->i_nlink = 0; file->f_vfsmnt = mntget(hugetlbfs_vfsmount); file->f_dentry = dentry; file->f_mapping = inode->i_mapping; file->f_op = &hugetlbfs_file_operations; file->f_mode = FMODE_WRITE | FMODE_READ; return file; out_inode: iput(inode); out_file: put_filp(file); out_dentry: dput(dentry); out_shm_unlock: user_shm_unlock(size, current->user); return ERR_PTR(error); } static int __init init_hugetlbfs_fs(void) { int error; struct vfsmount *vfsmount; hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache", sizeof(struct hugetlbfs_inode_info), 0, 0, init_once, NULL); if (hugetlbfs_inode_cachep == NULL) return -ENOMEM; error = register_filesystem(&hugetlbfs_fs_type); if (error) goto out; vfsmount = kern_mount(&hugetlbfs_fs_type); if (!IS_ERR(vfsmount)) { hugetlbfs_vfsmount = vfsmount; return 0; } error = PTR_ERR(vfsmount); out: if (error) kmem_cache_destroy(hugetlbfs_inode_cachep); return error; } static void __exit exit_hugetlbfs_fs(void) { kmem_cache_destroy(hugetlbfs_inode_cachep); unregister_filesystem(&hugetlbfs_fs_type); } module_init(init_hugetlbfs_fs) module_exit(exit_hugetlbfs_fs) MODULE_LICENSE("GPL"); linux-2.6.17/fs/hugetlbfs/.svn/prop-base/0000755000000000000000000000000010574207612016617 5ustar rootrootlinux-2.6.17/fs/hugetlbfs/inode.c0000644000000000000000000004702710564537530015323 0ustar rootroot/* * hugetlbpage-backed filesystem. Based on ramfs. * * William Irwin, 2002 * * Copyright (C) 2002 Linus Torvalds. */ #include #include #include #include /* remove ASAP */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* some random number */ #define HUGETLBFS_MAGIC 0x958458f6 static struct super_operations hugetlbfs_ops; static struct address_space_operations hugetlbfs_aops; const struct file_operations hugetlbfs_file_operations; static struct inode_operations hugetlbfs_dir_inode_operations; static struct inode_operations hugetlbfs_inode_operations; static struct backing_dev_info hugetlbfs_backing_dev_info = { .ra_pages = 0, /* No readahead */ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, }; int sysctl_hugetlb_shm_group; static void huge_pagevec_release(struct pagevec *pvec) { int i; for (i = 0; i < pagevec_count(pvec); ++i) put_page(pvec->pages[i]); pagevec_reinit(pvec); } static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file->f_dentry->d_inode; struct hugetlbfs_inode_info *info = HUGETLBFS_I(inode); loff_t len, vma_len; int ret; if (vma->vm_pgoff & (HPAGE_SIZE / PAGE_SIZE - 1)) return -EINVAL; if (vma->vm_start & ~HPAGE_MASK) return -EINVAL; if (vma->vm_end & ~HPAGE_MASK) return -EINVAL; if (vma->vm_end - vma->vm_start < HPAGE_SIZE) return -EINVAL; vma_len = (loff_t)(vma->vm_end - vma->vm_start); mutex_lock(&inode->i_mutex); file_accessed(file); vma->vm_flags |= VM_HUGETLB | VM_RESERVED; vma->vm_ops = &hugetlb_vm_ops; ret = -ENOMEM; len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size) goto out; if (vma->vm_flags & VM_MAYSHARE) if (hugetlb_extend_reservation(info, len >> HPAGE_SHIFT) != 0) goto out; ret = 0; hugetlb_prefault_arch_hook(vma->vm_mm); if (inode->i_size < len) inode->i_size = len; out: mutex_unlock(&inode->i_mutex); return ret; } /* * Called under down_write(mmap_sem). */ #ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); #else static unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; unsigned long start_addr; if (len & ~HPAGE_MASK) return -EINVAL; if (len > TASK_SIZE) return -ENOMEM; if (addr) { addr = ALIGN(addr, HPAGE_SIZE); vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && (!vma || addr + len <= vma->vm_start)) return addr; } start_addr = mm->free_area_cache; if (len <= mm->cached_hole_size) start_addr = TASK_UNMAPPED_BASE; full_search: addr = ALIGN(start_addr, HPAGE_SIZE); for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { /* At this point: (!vma || addr < vma->vm_end). */ if (TASK_SIZE - len < addr) { /* * Start a new search - just in case we missed * some holes. */ if (start_addr != TASK_UNMAPPED_BASE) { start_addr = TASK_UNMAPPED_BASE; goto full_search; } return -ENOMEM; } if (!vma || addr + len <= vma->vm_start) return addr; addr = ALIGN(vma->vm_end, HPAGE_SIZE); } } #endif /* * Read a page. Again trivial. If it didn't already exist * in the page cache, it is zero-filled. */ static int hugetlbfs_readpage(struct file *file, struct page * page) { unlock_page(page); return -EINVAL; } static int hugetlbfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { return -EINVAL; } static int hugetlbfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { return -EINVAL; } static void truncate_huge_page(struct page *page) { clear_page_dirty(page); ClearPageUptodate(page); remove_from_page_cache(page); put_page(page); } static void truncate_hugepages(struct inode *inode, loff_t lstart) { struct address_space *mapping = &inode->i_data; const pgoff_t start = lstart >> HPAGE_SHIFT; struct pagevec pvec; pgoff_t next; int i; hugetlb_truncate_reservation(HUGETLBFS_I(inode), lstart >> HPAGE_SHIFT); if (!mapping->nrpages) return; pagevec_init(&pvec, 0); next = start; while (1) { if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { if (next == start) break; next = start; continue; } for (i = 0; i < pagevec_count(&pvec); ++i) { struct page *page = pvec.pages[i]; lock_page(page); if (page->index > next) next = page->index; ++next; truncate_huge_page(page); unlock_page(page); hugetlb_put_quota(mapping); } huge_pagevec_release(&pvec); } BUG_ON(!lstart && mapping->nrpages); } static void hugetlbfs_delete_inode(struct inode *inode) { truncate_hugepages(inode, 0); clear_inode(inode); } static void hugetlbfs_forget_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; if (!hlist_unhashed(&inode->i_hash)) { if (!(inode->i_state & (I_DIRTY|I_LOCK))) list_move(&inode->i_list, &inode_unused); inodes_stat.nr_unused++; if (!sb || (sb->s_flags & MS_ACTIVE)) { spin_unlock(&inode_lock); return; } inode->i_state |= I_WILL_FREE; spin_unlock(&inode_lock); /* * write_inode_now is a noop as we set BDI_CAP_NO_WRITEBACK * in our backing_dev_info. */ write_inode_now(inode, 1); spin_lock(&inode_lock); inode->i_state &= ~I_WILL_FREE; inodes_stat.nr_unused--; hlist_del_init(&inode->i_hash); } list_del_init(&inode->i_list); list_del_init(&inode->i_sb_list); inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); truncate_hugepages(inode, 0); clear_inode(inode); destroy_inode(inode); } static void hugetlbfs_drop_inode(struct inode *inode) { if (!inode->i_nlink) generic_delete_inode(inode); else hugetlbfs_forget_inode(inode); } /* * h_pgoff is in HPAGE_SIZE units. * vma->vm_pgoff is in PAGE_SIZE units. */ static inline void hugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff) { struct vm_area_struct *vma; struct prio_tree_iter iter; vma_prio_tree_foreach(vma, &iter, root, h_pgoff, ULONG_MAX) { unsigned long h_vm_pgoff; unsigned long v_offset; h_vm_pgoff = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT); v_offset = (h_pgoff - h_vm_pgoff) << HPAGE_SHIFT; /* * Is this VMA fully outside the truncation point? */ if (h_vm_pgoff >= h_pgoff) v_offset = 0; unmap_hugepage_range(vma, vma->vm_start + v_offset, vma->vm_end); } } /* * Expanding truncates are not allowed. */ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) { unsigned long pgoff; struct address_space *mapping = inode->i_mapping; if (offset > inode->i_size) return -EINVAL; BUG_ON(offset & ~HPAGE_MASK); pgoff = offset >> HPAGE_SHIFT; inode->i_size = offset; spin_lock(&mapping->i_mmap_lock); if (!prio_tree_empty(&mapping->i_mmap)) hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff); spin_unlock(&mapping->i_mmap_lock); truncate_hugepages(inode, offset); return 0; } static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int error; unsigned int ia_valid = attr->ia_valid; BUG_ON(!inode); error = inode_change_ok(inode, attr); if (error) goto out; if (ia_valid & ATTR_SIZE) { error = -EINVAL; if (!(attr->ia_size & ~HPAGE_MASK)) error = hugetlb_vmtruncate(inode, attr->ia_size); if (error) goto out; attr->ia_valid &= ~ATTR_SIZE; } error = inode_setattr(inode, attr); out: return error; } static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, gid_t gid, int mode, dev_t dev) { struct inode *inode; inode = new_inode(sb); if (inode) { struct hugetlbfs_inode_info *info; inode->i_mode = mode; inode->i_uid = uid; inode->i_gid = gid; inode->i_blksize = HPAGE_SIZE; inode->i_blocks = 0; inode->i_mapping->a_ops = &hugetlbfs_aops; inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; info = HUGETLBFS_I(inode); mpol_shared_policy_init(&info->policy, MPOL_DEFAULT, NULL); switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); break; case S_IFREG: inode->i_op = &hugetlbfs_inode_operations; inode->i_fop = &hugetlbfs_file_operations; break; case S_IFDIR: inode->i_op = &hugetlbfs_dir_inode_operations; inode->i_fop = &simple_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; break; } } return inode; } /* * File creation. Allocate an inode, and we're done.. */ static int hugetlbfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode; int error = -ENOSPC; gid_t gid; if (dir->i_mode & S_ISGID) { gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; } else { gid = current->fsgid; } inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, mode, dev); if (inode) { dir->i_ctime = dir->i_mtime = CURRENT_TIME; d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ error = 0; } return error; } static int hugetlbfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0); if (!retval) dir->i_nlink++; return retval; } static int hugetlbfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { return hugetlbfs_mknod(dir, dentry, mode | S_IFREG, 0); } static int hugetlbfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct inode *inode; int error = -ENOSPC; gid_t gid; if (dir->i_mode & S_ISGID) gid = dir->i_gid; else gid = current->fsgid; inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, S_IFLNK|S_IRWXUGO, 0); if (inode) { int l = strlen(symname)+1; error = page_symlink(inode, symname, l); if (!error) { d_instantiate(dentry, inode); dget(dentry); } else iput(inode); } dir->i_ctime = dir->i_mtime = CURRENT_TIME; return error; } /* * For direct-IO reads into hugetlb pages */ static int hugetlbfs_set_page_dirty(struct page *page) { return 0; } static int hugetlbfs_statfs(struct super_block *sb, struct kstatfs *buf) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); buf->f_type = HUGETLBFS_MAGIC; buf->f_bsize = HPAGE_SIZE; if (sbinfo) { spin_lock(&sbinfo->stat_lock); /* If no limits set, just report 0 for max/free/used * blocks, like simple_statfs() */ if (sbinfo->max_blocks >= 0) { buf->f_blocks = sbinfo->max_blocks; buf->f_bavail = buf->f_bfree = sbinfo->free_blocks; buf->f_files = sbinfo->max_inodes; buf->f_ffree = sbinfo->free_inodes; } spin_unlock(&sbinfo->stat_lock); } buf->f_namelen = NAME_MAX; return 0; } static void hugetlbfs_put_super(struct super_block *sb) { struct hugetlbfs_sb_info *sbi = HUGETLBFS_SB(sb); if (sbi) { sb->s_fs_info = NULL; kfree(sbi); } } static inline int hugetlbfs_dec_free_inodes(struct hugetlbfs_sb_info *sbinfo) { if (sbinfo->free_inodes >= 0) { spin_lock(&sbinfo->stat_lock); if (unlikely(!sbinfo->free_inodes)) { spin_unlock(&sbinfo->stat_lock); return 0; } sbinfo->free_inodes--; spin_unlock(&sbinfo->stat_lock); } return 1; } static void hugetlbfs_inc_free_inodes(struct hugetlbfs_sb_info *sbinfo) { if (sbinfo->free_inodes >= 0) { spin_lock(&sbinfo->stat_lock); sbinfo->free_inodes++; spin_unlock(&sbinfo->stat_lock); } } static kmem_cache_t *hugetlbfs_inode_cachep; static struct inode *hugetlbfs_alloc_inode(struct super_block *sb) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); struct hugetlbfs_inode_info *p; if (unlikely(!hugetlbfs_dec_free_inodes(sbinfo))) return NULL; p = kmem_cache_alloc(hugetlbfs_inode_cachep, SLAB_KERNEL); if (unlikely(!p)) { hugetlbfs_inc_free_inodes(sbinfo); return NULL; } p->prereserved_hpages = 0; return &p->vfs_inode; } static void hugetlbfs_destroy_inode(struct inode *inode) { hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb)); mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy); kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode)); } static struct address_space_operations hugetlbfs_aops = { .readpage = hugetlbfs_readpage, .prepare_write = hugetlbfs_prepare_write, .commit_write = hugetlbfs_commit_write, .set_page_dirty = hugetlbfs_set_page_dirty, }; static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags) { struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) inode_init_once(&ei->vfs_inode); } const struct file_operations hugetlbfs_file_operations = { .mmap = hugetlbfs_file_mmap, .fsync = simple_sync_file, .get_unmapped_area = hugetlb_get_unmapped_area, }; static struct inode_operations hugetlbfs_dir_inode_operations = { .create = hugetlbfs_create, .lookup = simple_lookup, .link = simple_link, .unlink = simple_unlink, .symlink = hugetlbfs_symlink, .mkdir = hugetlbfs_mkdir, .rmdir = simple_rmdir, .mknod = hugetlbfs_mknod, .rename = simple_rename, .setattr = hugetlbfs_setattr, }; static struct inode_operations hugetlbfs_inode_operations = { .setattr = hugetlbfs_setattr, }; static struct super_operations hugetlbfs_ops = { .alloc_inode = hugetlbfs_alloc_inode, .destroy_inode = hugetlbfs_destroy_inode, .statfs = hugetlbfs_statfs, .delete_inode = hugetlbfs_delete_inode, .drop_inode = hugetlbfs_drop_inode, .put_super = hugetlbfs_put_super, }; static int hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) { char *opt, *value, *rest; if (!options) return 0; while ((opt = strsep(&options, ",")) != NULL) { if (!*opt) continue; value = strchr(opt, '='); if (!value || !*value) return -EINVAL; else *value++ = '\0'; if (!strcmp(opt, "uid")) pconfig->uid = simple_strtoul(value, &value, 0); else if (!strcmp(opt, "gid")) pconfig->gid = simple_strtoul(value, &value, 0); else if (!strcmp(opt, "mode")) pconfig->mode = simple_strtoul(value,&value,0) & 0777U; else if (!strcmp(opt, "size")) { unsigned long long size = memparse(value, &rest); if (*rest == '%') { size <<= HPAGE_SHIFT; size *= max_huge_pages; do_div(size, 100); rest++; } size &= HPAGE_MASK; pconfig->nr_blocks = (size >> HPAGE_SHIFT); value = rest; } else if (!strcmp(opt,"nr_inodes")) { pconfig->nr_inodes = memparse(value, &rest); value = rest; } else return -EINVAL; if (*value) return -EINVAL; } return 0; } static int hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode * inode; struct dentry * root; int ret; struct hugetlbfs_config config; struct hugetlbfs_sb_info *sbinfo; config.nr_blocks = -1; /* No limit on size by default */ config.nr_inodes = -1; /* No limit on number of inodes by default */ config.uid = current->fsuid; config.gid = current->fsgid; config.mode = 0755; ret = hugetlbfs_parse_options(data, &config); if (ret) return ret; sbinfo = kmalloc(sizeof(struct hugetlbfs_sb_info), GFP_KERNEL); if (!sbinfo) return -ENOMEM; sb->s_fs_info = sbinfo; spin_lock_init(&sbinfo->stat_lock); sbinfo->max_blocks = config.nr_blocks; sbinfo->free_blocks = config.nr_blocks; sbinfo->max_inodes = config.nr_inodes; sbinfo->free_inodes = config.nr_inodes; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = HPAGE_SIZE; sb->s_blocksize_bits = HPAGE_SHIFT; sb->s_magic = HUGETLBFS_MAGIC; sb->s_op = &hugetlbfs_ops; sb->s_time_gran = 1; inode = hugetlbfs_get_inode(sb, config.uid, config.gid, S_IFDIR | config.mode, 0); if (!inode) goto out_free; root = d_alloc_root(inode); if (!root) { iput(inode); goto out_free; } sb->s_root = root; return 0; out_free: kfree(sbinfo); return -ENOMEM; } int hugetlb_get_quota(struct address_space *mapping) { int ret = 0; struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb); if (sbinfo->free_blocks > -1) { spin_lock(&sbinfo->stat_lock); if (sbinfo->free_blocks > 0) sbinfo->free_blocks--; else ret = -ENOMEM; spin_unlock(&sbinfo->stat_lock); } return ret; } void hugetlb_put_quota(struct address_space *mapping) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb); if (sbinfo->free_blocks > -1) { spin_lock(&sbinfo->stat_lock); sbinfo->free_blocks++; spin_unlock(&sbinfo->stat_lock); } } static struct super_block *hugetlbfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super); } static struct file_system_type hugetlbfs_fs_type = { .name = "hugetlbfs", .get_sb = hugetlbfs_get_sb, .kill_sb = kill_litter_super, }; static struct vfsmount *hugetlbfs_vfsmount; static int can_do_hugetlb_shm(void) { return likely(capable(CAP_IPC_LOCK) || in_group_p(sysctl_hugetlb_shm_group) || can_do_mlock()); } struct file *hugetlb_zero_setup(size_t size) { int error = -ENOMEM; struct file *file; struct inode *inode; struct dentry *dentry, *root; struct qstr quick_string; char buf[16]; static atomic_t counter; if (!can_do_hugetlb_shm()) return ERR_PTR(-EPERM); if (!user_shm_lock(size, current->user)) return ERR_PTR(-ENOMEM); root = hugetlbfs_vfsmount->mnt_root; snprintf(buf, 16, "%u", atomic_inc_return(&counter)); quick_string.name = buf; quick_string.len = strlen(quick_string.name); quick_string.hash = 0; dentry = d_alloc(root, &quick_string); if (!dentry) goto out_shm_unlock; error = -ENFILE; file = get_empty_filp(); if (!file) goto out_dentry; error = -ENOSPC; inode = hugetlbfs_get_inode(root->d_sb, current->fsuid, current->fsgid, S_IFREG | S_IRWXUGO, 0); if (!inode) goto out_file; error = -ENOMEM; if (hugetlb_extend_reservation(HUGETLBFS_I(inode), size >> HPAGE_SHIFT) != 0) goto out_inode; d_instantiate(dentry, inode); inode->i_size = size; inode->i_nlink = 0; file->f_vfsmnt = mntget(hugetlbfs_vfsmount); file->f_dentry = dentry; file->f_mapping = inode->i_mapping; file->f_op = &hugetlbfs_file_operations; file->f_mode = FMODE_WRITE | FMODE_READ; return file; out_inode: iput(inode); out_file: put_filp(file); out_dentry: dput(dentry); out_shm_unlock: user_shm_unlock(size, current->user); return ERR_PTR(error); } static int __init init_hugetlbfs_fs(void) { int error; struct vfsmount *vfsmount; hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache", sizeof(struct hugetlbfs_inode_info), 0, 0, init_once, NULL); if (hugetlbfs_inode_cachep == NULL) return -ENOMEM; error = register_filesystem(&hugetlbfs_fs_type); if (error) goto out; vfsmount = kern_mount(&hugetlbfs_fs_type); if (!IS_ERR(vfsmount)) { hugetlbfs_vfsmount = vfsmount; return 0; } error = PTR_ERR(vfsmount); out: if (error) kmem_cache_destroy(hugetlbfs_inode_cachep); return error; } static void __exit exit_hugetlbfs_fs(void) { kmem_cache_destroy(hugetlbfs_inode_cachep); unregister_filesystem(&hugetlbfs_fs_type); } module_init(init_hugetlbfs_fs) module_exit(exit_hugetlbfs_fs) MODULE_LICENSE("GPL"); linux-2.6.17/fs/hugetlbfs/Makefile0000644000000000000000000000016010564537530015504 0ustar rootroot# # Makefile for the linux ramfs routines. # obj-$(CONFIG_HUGETLBFS) += hugetlbfs.o hugetlbfs-objs := inode.o linux-2.6.17/fs/sync.c0000644000000000000000000001066710564537530013216 0ustar rootroot/* * High-level sync()-related operations */ #include #include #include #include #include #include #include #include #define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \ SYNC_FILE_RANGE_WAIT_AFTER) /* * sys_sync_file_range() permits finely controlled syncing over a segment of * a file in the range offset .. (offset+nbytes-1) inclusive. If nbytes is * zero then sys_sync_file_range() will operate from offset out to EOF. * * The flag bits are: * * SYNC_FILE_RANGE_WAIT_BEFORE: wait upon writeout of all pages in the range * before performing the write. * * SYNC_FILE_RANGE_WRITE: initiate writeout of all those dirty pages in the * range which are not presently under writeback. * * SYNC_FILE_RANGE_WAIT_AFTER: wait upon writeout of all pages in the range * after performing the write. * * Useful combinations of the flag bits are: * * SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE: ensures that all pages * in the range which were dirty on entry to sys_sync_file_range() are placed * under writeout. This is a start-write-for-data-integrity operation. * * SYNC_FILE_RANGE_WRITE: start writeout of all dirty pages in the range which * are not presently under writeout. This is an asynchronous flush-to-disk * operation. Not suitable for data integrity operations. * * SYNC_FILE_RANGE_WAIT_BEFORE (or SYNC_FILE_RANGE_WAIT_AFTER): wait for * completion of writeout of all pages in the range. This will be used after an * earlier SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE operation to wait * for that operation to complete and to return the result. * * SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER: * a traditional sync() operation. This is a write-for-data-integrity operation * which will ensure that all pages in the range which were dirty on entry to * sys_sync_file_range() are committed to disk. * * * SYNC_FILE_RANGE_WAIT_BEFORE and SYNC_FILE_RANGE_WAIT_AFTER will detect any * I/O errors or ENOSPC conditions and will return those to the caller, after * clearing the EIO and ENOSPC flags in the address_space. * * It should be noted that none of these operations write out the file's * metadata. So unless the application is strictly performing overwrites of * already-instantiated disk blocks, there are no guarantees here that the data * will be available after a crash. */ asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes, unsigned int flags) { int ret; struct file *file; loff_t endbyte; /* inclusive */ int fput_needed; umode_t i_mode; ret = -EINVAL; if (flags & ~VALID_FLAGS) goto out; endbyte = offset + nbytes; if ((s64)offset < 0) goto out; if ((s64)endbyte < 0) goto out; if (endbyte < offset) goto out; if (sizeof(pgoff_t) == 4) { if (offset >= (0x100000000ULL << PAGE_CACHE_SHIFT)) { /* * The range starts outside a 32 bit machine's * pagecache addressing capabilities. Let it "succeed" */ ret = 0; goto out; } if (endbyte >= (0x100000000ULL << PAGE_CACHE_SHIFT)) { /* * Out to EOF */ nbytes = 0; } } if (nbytes == 0) endbyte = -1; else endbyte--; /* inclusive */ ret = -EBADF; file = fget_light(fd, &fput_needed); if (!file) goto out; i_mode = file->f_dentry->d_inode->i_mode; ret = -ESPIPE; if (!S_ISREG(i_mode) && !S_ISBLK(i_mode) && !S_ISDIR(i_mode) && !S_ISLNK(i_mode)) goto out_put; ret = do_sync_file_range(file, offset, endbyte, flags); out_put: fput_light(file, fput_needed); out: return ret; } /* * `endbyte' is inclusive */ int do_sync_file_range(struct file *file, loff_t offset, loff_t endbyte, unsigned int flags) { int ret; struct address_space *mapping; mapping = file->f_mapping; if (!mapping) { ret = -EINVAL; goto out; } ret = 0; if (flags & SYNC_FILE_RANGE_WAIT_BEFORE) { ret = wait_on_page_writeback_range(mapping, offset >> PAGE_CACHE_SHIFT, endbyte >> PAGE_CACHE_SHIFT); if (ret < 0) goto out; } if (flags & SYNC_FILE_RANGE_WRITE) { ret = __filemap_fdatawrite_range(mapping, offset, endbyte, WB_SYNC_NONE); if (ret < 0) goto out; } if (flags & SYNC_FILE_RANGE_WAIT_AFTER) { ret = wait_on_page_writeback_range(mapping, offset >> PAGE_CACHE_SHIFT, endbyte >> PAGE_CACHE_SHIFT); } out: return ret; } EXPORT_SYMBOL_GPL(do_sync_file_range); linux-2.6.17/fs/hppfs/0000755000000000000000000000000010574207633013203 5ustar rootrootlinux-2.6.17/fs/hppfs/hppfs_kern.c0000644000000000000000000004165310564537530015520 0ustar rootroot/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ #include #include #include #include #include #include #include #include #include #include #include #include #include "os.h" static int init_inode(struct inode *inode, struct dentry *dentry); struct hppfs_data { struct list_head list; char contents[PAGE_SIZE - sizeof(struct list_head)]; }; struct hppfs_private { struct file *proc_file; int host_fd; loff_t len; struct hppfs_data *contents; }; struct hppfs_inode_info { struct dentry *proc_dentry; struct inode vfs_inode; }; static inline struct hppfs_inode_info *HPPFS_I(struct inode *inode) { return container_of(inode, struct hppfs_inode_info, vfs_inode); } #define HPPFS_SUPER_MAGIC 0xb00000ee static struct super_operations hppfs_sbops; static int is_pid(struct dentry *dentry) { struct super_block *sb; int i; sb = dentry->d_sb; if((sb->s_op != &hppfs_sbops) || (dentry->d_parent != sb->s_root)) return(0); for(i = 0; i < dentry->d_name.len; i++){ if(!isdigit(dentry->d_name.name[i])) return(0); } return(1); } static char *dentry_name(struct dentry *dentry, int extra) { struct dentry *parent; char *root, *name; const char *seg_name; int len, seg_len; len = 0; parent = dentry; while(parent->d_parent != parent){ if(is_pid(parent)) len += strlen("pid") + 1; else len += parent->d_name.len + 1; parent = parent->d_parent; } root = "proc"; len += strlen(root); name = kmalloc(len + extra + 1, GFP_KERNEL); if(name == NULL) return(NULL); name[len] = '\0'; parent = dentry; while(parent->d_parent != parent){ if(is_pid(parent)){ seg_name = "pid"; seg_len = strlen("pid"); } else { seg_name = parent->d_name.name; seg_len = parent->d_name.len; } len -= seg_len + 1; name[len] = '/'; strncpy(&name[len + 1], seg_name, seg_len); parent = parent->d_parent; } strncpy(name, root, strlen(root)); return(name); } struct dentry_operations hppfs_dentry_ops = { }; static int file_removed(struct dentry *dentry, const char *file) { char *host_file; int extra, fd; extra = 0; if(file != NULL) extra += strlen(file) + 1; host_file = dentry_name(dentry, extra + strlen("/remove")); if(host_file == NULL){ printk("file_removed : allocation failed\n"); return(-ENOMEM); } if(file != NULL){ strcat(host_file, "/"); strcat(host_file, file); } strcat(host_file, "/remove"); fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); kfree(host_file); if(fd > 0){ os_close_file(fd); return(1); } return(0); } static void hppfs_read_inode(struct inode *ino) { struct inode *proc_ino; if(HPPFS_I(ino)->proc_dentry == NULL) return; proc_ino = HPPFS_I(ino)->proc_dentry->d_inode; ino->i_uid = proc_ino->i_uid; ino->i_gid = proc_ino->i_gid; ino->i_atime = proc_ino->i_atime; ino->i_mtime = proc_ino->i_mtime; ino->i_ctime = proc_ino->i_ctime; ino->i_ino = proc_ino->i_ino; ino->i_mode = proc_ino->i_mode; ino->i_nlink = proc_ino->i_nlink; ino->i_size = proc_ino->i_size; ino->i_blksize = proc_ino->i_blksize; ino->i_blocks = proc_ino->i_blocks; } static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry, struct nameidata *nd) { struct dentry *proc_dentry, *new, *parent; struct inode *inode; int err, deleted; deleted = file_removed(dentry, NULL); if(deleted < 0) return(ERR_PTR(deleted)); else if(deleted) return(ERR_PTR(-ENOENT)); err = -ENOMEM; parent = HPPFS_I(ino)->proc_dentry; mutex_lock(&parent->d_inode->i_mutex); proc_dentry = d_lookup(parent, &dentry->d_name); if(proc_dentry == NULL){ proc_dentry = d_alloc(parent, &dentry->d_name); if(proc_dentry == NULL){ mutex_unlock(&parent->d_inode->i_mutex); goto out; } new = (*parent->d_inode->i_op->lookup)(parent->d_inode, proc_dentry, NULL); if(new){ dput(proc_dentry); proc_dentry = new; } } mutex_unlock(&parent->d_inode->i_mutex); if(IS_ERR(proc_dentry)) return(proc_dentry); inode = iget(ino->i_sb, 0); if(inode == NULL) goto out_dput; err = init_inode(inode, proc_dentry); if(err) goto out_put; hppfs_read_inode(inode); d_add(dentry, inode); dentry->d_op = &hppfs_dentry_ops; return(NULL); out_put: iput(inode); out_dput: dput(proc_dentry); out: return(ERR_PTR(err)); } static struct inode_operations hppfs_file_iops = { }; static ssize_t read_proc(struct file *file, char __user *buf, ssize_t count, loff_t *ppos, int is_user) { ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); ssize_t n; read = file->f_dentry->d_inode->i_fop->read; if(!is_user) set_fs(KERNEL_DS); n = (*read)(file, buf, count, &file->f_pos); if(!is_user) set_fs(USER_DS); if(ppos) *ppos = file->f_pos; return n; } static ssize_t hppfs_read_file(int fd, char __user *buf, ssize_t count) { ssize_t n; int cur, err; char *new_buf; n = -ENOMEM; new_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if(new_buf == NULL){ printk("hppfs_read_file : kmalloc failed\n"); goto out; } n = 0; while(count > 0){ cur = min_t(ssize_t, count, PAGE_SIZE); err = os_read_file(fd, new_buf, cur); if(err < 0){ printk("hppfs_read : read failed, errno = %d\n", err); n = err; goto out_free; } else if(err == 0) break; if(copy_to_user(buf, new_buf, err)){ n = -EFAULT; goto out_free; } n += err; count -= err; } out_free: kfree(new_buf); out: return n; } static ssize_t hppfs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct hppfs_private *hppfs = file->private_data; struct hppfs_data *data; loff_t off; int err; if(hppfs->contents != NULL){ if(*ppos >= hppfs->len) return(0); data = hppfs->contents; off = *ppos; while(off >= sizeof(data->contents)){ data = list_entry(data->list.next, struct hppfs_data, list); off -= sizeof(data->contents); } if(off + count > hppfs->len) count = hppfs->len - off; copy_to_user(buf, &data->contents[off], count); *ppos += count; } else if(hppfs->host_fd != -1){ err = os_seek_file(hppfs->host_fd, *ppos); if(err){ printk("hppfs_read : seek failed, errno = %d\n", err); return(err); } count = hppfs_read_file(hppfs->host_fd, buf, count); if(count > 0) *ppos += count; } else count = read_proc(hppfs->proc_file, buf, count, ppos, 1); return(count); } static ssize_t hppfs_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct hppfs_private *data = file->private_data; struct file *proc_file = data->proc_file; ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); int err; write = proc_file->f_dentry->d_inode->i_fop->write; proc_file->f_pos = file->f_pos; err = (*write)(proc_file, buf, len, &proc_file->f_pos); file->f_pos = proc_file->f_pos; return(err); } static int open_host_sock(char *host_file, int *filter_out) { char *end; int fd; end = &host_file[strlen(host_file)]; strcpy(end, "/rw"); *filter_out = 1; fd = os_connect_socket(host_file); if(fd > 0) return(fd); strcpy(end, "/r"); *filter_out = 0; fd = os_connect_socket(host_file); return(fd); } static void free_contents(struct hppfs_data *head) { struct hppfs_data *data; struct list_head *ele, *next; if(head == NULL) return; list_for_each_safe(ele, next, &head->list){ data = list_entry(ele, struct hppfs_data, list); kfree(data); } kfree(head); } static struct hppfs_data *hppfs_get_data(int fd, int filter, struct file *proc_file, struct file *hppfs_file, loff_t *size_out) { struct hppfs_data *data, *new, *head; int n, err; err = -ENOMEM; data = kmalloc(sizeof(*data), GFP_KERNEL); if(data == NULL){ printk("hppfs_get_data : head allocation failed\n"); goto failed; } INIT_LIST_HEAD(&data->list); head = data; *size_out = 0; if(filter){ while((n = read_proc(proc_file, data->contents, sizeof(data->contents), NULL, 0)) > 0) os_write_file(fd, data->contents, n); err = os_shutdown_socket(fd, 0, 1); if(err){ printk("hppfs_get_data : failed to shut down " "socket\n"); goto failed_free; } } while(1){ n = os_read_file(fd, data->contents, sizeof(data->contents)); if(n < 0){ err = n; printk("hppfs_get_data : read failed, errno = %d\n", err); goto failed_free; } else if(n == 0) break; *size_out += n; if(n < sizeof(data->contents)) break; new = kmalloc(sizeof(*data), GFP_KERNEL); if(new == 0){ printk("hppfs_get_data : data allocation failed\n"); err = -ENOMEM; goto failed_free; } INIT_LIST_HEAD(&new->list); list_add(&new->list, &data->list); data = new; } return(head); failed_free: free_contents(head); failed: return(ERR_PTR(err)); } static struct hppfs_private *hppfs_data(void) { struct hppfs_private *data; data = kmalloc(sizeof(*data), GFP_KERNEL); if(data == NULL) return(data); *data = ((struct hppfs_private ) { .host_fd = -1, .len = -1, .contents = NULL } ); return(data); } static int file_mode(int fmode) { if(fmode == (FMODE_READ | FMODE_WRITE)) return(O_RDWR); if(fmode == FMODE_READ) return(O_RDONLY); if(fmode == FMODE_WRITE) return(O_WRONLY); return(0); } static int hppfs_open(struct inode *inode, struct file *file) { struct hppfs_private *data; struct dentry *proc_dentry; char *host_file; int err, fd, type, filter; err = -ENOMEM; data = hppfs_data(); if(data == NULL) goto out; host_file = dentry_name(file->f_dentry, strlen("/rw")); if(host_file == NULL) goto out_free2; proc_dentry = HPPFS_I(inode)->proc_dentry; /* XXX This isn't closed anywhere */ data->proc_file = dentry_open(dget(proc_dentry), NULL, file_mode(file->f_mode)); err = PTR_ERR(data->proc_file); if(IS_ERR(data->proc_file)) goto out_free1; type = os_file_type(host_file); if(type == OS_TYPE_FILE){ fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); if(fd >= 0) data->host_fd = fd; else printk("hppfs_open : failed to open '%s', errno = %d\n", host_file, -fd); data->contents = NULL; } else if(type == OS_TYPE_DIR){ fd = open_host_sock(host_file, &filter); if(fd > 0){ data->contents = hppfs_get_data(fd, filter, data->proc_file, file, &data->len); if(!IS_ERR(data->contents)) data->host_fd = fd; } else printk("hppfs_open : failed to open a socket in " "'%s', errno = %d\n", host_file, -fd); } kfree(host_file); file->private_data = data; return(0); out_free1: kfree(host_file); out_free2: free_contents(data->contents); kfree(data); out: return(err); } static int hppfs_dir_open(struct inode *inode, struct file *file) { struct hppfs_private *data; struct dentry *proc_dentry; int err; err = -ENOMEM; data = hppfs_data(); if(data == NULL) goto out; proc_dentry = HPPFS_I(inode)->proc_dentry; data->proc_file = dentry_open(dget(proc_dentry), NULL, file_mode(file->f_mode)); err = PTR_ERR(data->proc_file); if(IS_ERR(data->proc_file)) goto out_free; file->private_data = data; return(0); out_free: kfree(data); out: return(err); } static loff_t hppfs_llseek(struct file *file, loff_t off, int where) { struct hppfs_private *data = file->private_data; struct file *proc_file = data->proc_file; loff_t (*llseek)(struct file *, loff_t, int); loff_t ret; llseek = proc_file->f_dentry->d_inode->i_fop->llseek; if(llseek != NULL){ ret = (*llseek)(proc_file, off, where); if(ret < 0) return(ret); } return(default_llseek(file, off, where)); } static const struct file_operations hppfs_file_fops = { .owner = NULL, .llseek = hppfs_llseek, .read = hppfs_read, .write = hppfs_write, .open = hppfs_open, }; struct hppfs_dirent { void *vfs_dirent; filldir_t filldir; struct dentry *dentry; }; static int hppfs_filldir(void *d, const char *name, int size, loff_t offset, ino_t inode, unsigned int type) { struct hppfs_dirent *dirent = d; if(file_removed(dirent->dentry, name)) return(0); return((*dirent->filldir)(dirent->vfs_dirent, name, size, offset, inode, type)); } static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir) { struct hppfs_private *data = file->private_data; struct file *proc_file = data->proc_file; int (*readdir)(struct file *, void *, filldir_t); struct hppfs_dirent dirent = ((struct hppfs_dirent) { .vfs_dirent = ent, .filldir = filldir, .dentry = file->f_dentry } ); int err; readdir = proc_file->f_dentry->d_inode->i_fop->readdir; proc_file->f_pos = file->f_pos; err = (*readdir)(proc_file, &dirent, hppfs_filldir); file->f_pos = proc_file->f_pos; return(err); } static int hppfs_fsync(struct file *file, struct dentry *dentry, int datasync) { return(0); } static const struct file_operations hppfs_dir_fops = { .owner = NULL, .readdir = hppfs_readdir, .open = hppfs_dir_open, .fsync = hppfs_fsync, }; static int hppfs_statfs(struct super_block *sb, struct kstatfs *sf) { sf->f_blocks = 0; sf->f_bfree = 0; sf->f_bavail = 0; sf->f_files = 0; sf->f_ffree = 0; sf->f_type = HPPFS_SUPER_MAGIC; return(0); } static struct inode *hppfs_alloc_inode(struct super_block *sb) { struct hppfs_inode_info *hi; hi = kmalloc(sizeof(*hi), GFP_KERNEL); if(hi == NULL) return(NULL); *hi = ((struct hppfs_inode_info) { .proc_dentry = NULL }); inode_init_once(&hi->vfs_inode); return(&hi->vfs_inode); } void hppfs_delete_inode(struct inode *ino) { clear_inode(ino); } static void hppfs_destroy_inode(struct inode *inode) { kfree(HPPFS_I(inode)); } static struct super_operations hppfs_sbops = { .alloc_inode = hppfs_alloc_inode, .destroy_inode = hppfs_destroy_inode, .read_inode = hppfs_read_inode, .delete_inode = hppfs_delete_inode, .statfs = hppfs_statfs, }; static int hppfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) { struct file *proc_file; struct dentry *proc_dentry; int ret; proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry; proc_file = dentry_open(dget(proc_dentry), NULL, O_RDONLY); if (IS_ERR(proc_file)) return PTR_ERR(proc_file); ret = proc_dentry->d_inode->i_op->readlink(proc_dentry, buffer, buflen); fput(proc_file); return ret; } static void* hppfs_follow_link(struct dentry *dentry, struct nameidata *nd) { struct file *proc_file; struct dentry *proc_dentry; void *ret; proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry; proc_file = dentry_open(dget(proc_dentry), NULL, O_RDONLY); if (IS_ERR(proc_file)) return proc_file; ret = proc_dentry->d_inode->i_op->follow_link(proc_dentry, nd); fput(proc_file); return ret; } static struct inode_operations hppfs_dir_iops = { .lookup = hppfs_lookup, }; static struct inode_operations hppfs_link_iops = { .readlink = hppfs_readlink, .follow_link = hppfs_follow_link, }; static int init_inode(struct inode *inode, struct dentry *dentry) { if(S_ISDIR(dentry->d_inode->i_mode)){ inode->i_op = &hppfs_dir_iops; inode->i_fop = &hppfs_dir_fops; } else if(S_ISLNK(dentry->d_inode->i_mode)){ inode->i_op = &hppfs_link_iops; inode->i_fop = &hppfs_file_fops; } else { inode->i_op = &hppfs_file_iops; inode->i_fop = &hppfs_file_fops; } HPPFS_I(inode)->proc_dentry = dentry; return(0); } static int hppfs_fill_super(struct super_block *sb, void *d, int silent) { struct inode *root_inode; struct file_system_type *procfs; struct super_block *proc_sb; int err; err = -ENOENT; procfs = get_fs_type("proc"); if(procfs == NULL) goto out; if(list_empty(&procfs->fs_supers)) goto out; proc_sb = list_entry(procfs->fs_supers.next, struct super_block, s_instances); sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; sb->s_magic = HPPFS_SUPER_MAGIC; sb->s_op = &hppfs_sbops; root_inode = iget(sb, 0); if(root_inode == NULL) goto out; err = init_inode(root_inode, proc_sb->s_root); if(err) goto out_put; err = -ENOMEM; sb->s_root = d_alloc_root(root_inode); if(sb->s_root == NULL) goto out_put; hppfs_read_inode(root_inode); return(0); out_put: iput(root_inode); out: return(err); } static struct super_block *hppfs_read_super(struct file_system_type *type, int flags, const char *dev_name, void *data) { return(get_sb_nodev(type, flags, data, hppfs_fill_super)); } static struct file_system_type hppfs_type = { .owner = THIS_MODULE, .name = "hppfs", .get_sb = hppfs_read_super, .kill_sb = kill_anon_super, .fs_flags = 0, }; static int __init init_hppfs(void) { return(register_filesystem(&hppfs_type)); } static void __exit exit_hppfs(void) { unregister_filesystem(&hppfs_type); } module_init(init_hppfs) module_exit(exit_hppfs) MODULE_LICENSE("GPL"); /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */ linux-2.6.17/fs/hppfs/.svn/0000755000000000000000000000000010613366603014064 5ustar rootrootlinux-2.6.17/fs/hppfs/.svn/format0000644000000000000000000000000210574207633015272 0ustar rootroot8 linux-2.6.17/fs/hppfs/.svn/entries0000444000000000000000000000075310613366571015467 0ustar rootroot8 dir 88 svn+ssh://192.168.2.6/home/svn-root/repos/FVT/linux-2.6.17/fs/hppfs svn+ssh://192.168.2.6/home/svn-root/repos/FVT 2007-02-14T07:50:48.156956Z 1 root svn:special svn:externals svn:needs-lock 4bdaaa2a-274d-416c-a84c-08d2865ac13c Makefile file 2007-02-14T07:50:48.000000Z 64cf4528754a09bd9c7bab7a7b85a1dd 2007-02-14T07:50:48.156956Z 1 root hppfs_kern.c file 2007-02-14T07:50:48.000000Z 3128966507e0dcc688b27a94ab8b5c0d 2007-02-14T07:50:48.156956Z 1 root linux-2.6.17/fs/hppfs/.svn/tmp/0000755000000000000000000000000010613366571014670 5ustar rootrootlinux-2.6.17/fs/hppfs/.svn/tmp/props/0000755000000000000000000000000010574207633016032 5ustar rootrootlinux-2.6.17/fs/hppfs/.svn/tmp/text-base/0000755000000000000000000000000010574207633016563 5ustar rootrootlinux-2.6.17/fs/hppfs/.svn/tmp/prop-base/0000755000000000000000000000000010574207633016557 5ustar rootrootlinux-2.6.17/fs/hppfs/.svn/props/0000755000000000000000000000000010574207633015232 5ustar rootrootlinux-2.6.17/fs/hppfs/.svn/text-base/0000755000000000000000000000000010574207633015763 5ustar rootrootlinux-2.6.17/fs/hppfs/.svn/text-base/Makefile.svn-base0000444000000000000000000000023110574207633021132 0ustar rootroot# # Copyright (C) 2002, 2003 Jeff Dike (jdike@karaya.com) # Licensed under the GPL # hppfs-objs := hppfs_kern.o obj-y = obj-$(CONFIG_HPPFS) += hppfs.o linux-2.6.17/fs/hppfs/.svn/text-base/hppfs_kern.c.svn-base0000444000000000000000000004165310574207633022012 0ustar rootroot/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ #include #include #include #include #include #include #include #include #include #include #include #include #include "os.h" static int init_inode(struct inode *inode, struct dentry *dentry); struct hppfs_data { struct list_head list; char contents[PAGE_SIZE - sizeof(struct list_head)]; }; struct hppfs_private { struct file *proc_file; int host_fd; loff_t len; struct hppfs_data *contents; }; struct hppfs_inode_info { struct dentry *proc_dentry; struct inode vfs_inode; }; static inline struct hppfs_inode_info *HPPFS_I(struct inode *inode) { return container_of(inode, struct hppfs_inode_info, vfs_inode); } #define HPPFS_SUPER_MAGIC 0xb00000ee static struct super_operations hppfs_sbops; static int is_pid(struct dentry *dentry) { struct super_block *sb; int i; sb = dentry->d_sb; if((sb->s_op != &hppfs_sbops) || (dentry->d_parent != sb->s_root)) return(0); for(i = 0; i < dentry->d_name.len; i++){ if(!isdigit(dentry->d_name.name[i])) return(0); } return(1); } static char *dentry_name(struct dentry *dentry, int extra) { struct dentry *parent; char *root, *name; const char *seg_name; int len, seg_len; len = 0; parent = dentry; while(parent->d_parent != parent){ if(is_pid(parent)) len += strlen("pid") + 1; else len += parent->d_name.len + 1; parent = parent->d_parent; } root = "proc"; len += strlen(root); name = kmalloc(len + extra + 1, GFP_KERNEL); if(name == NULL) return(NULL); name[len] = '\0'; parent = dentry; while(parent->d_parent != parent){ if(is_pid(parent)){ seg_name = "pid"; seg_len = strlen("pid"); } else { seg_name = parent->d_name.name; seg_len = parent->d_name.len; } len -= seg_len + 1; name[len] = '/'; strncpy(&name[len + 1], seg_name, seg_len); parent = parent->d_parent; } strncpy(name, root, strlen(root)); return(name); } struct dentry_operations hppfs_dentry_ops = { }; static int file_removed(struct dentry *dentry, const char *file) { char *host_file; int extra, fd; extra = 0; if(file != NULL) extra += strlen(file) + 1; host_file = dentry_name(dentry, extra + strlen("/remove")); if(host_file == NULL){ printk("file_removed : allocation failed\n"); return(-ENOMEM); } if(file != NULL){ strcat(host_file, "/"); strcat(host_file, file); } strcat(host_file, "/remove"); fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); kfree(host_file); if(fd > 0){ os_close_file(fd); return(1); } return(0); } static void hppfs_read_inode(struct inode *ino) { struct inode *proc_ino; if(HPPFS_I(ino)->proc_dentry == NULL) return; proc_ino = HPPFS_I(ino)->proc_dentry->d_inode; ino->i_uid = proc_ino->i_uid; ino->i_gid = proc_ino->i_gid; ino->i_atime = proc_ino->i_atime; ino->i_mtime = proc_ino->i_mtime; ino->i_ctime = proc_ino->i_ctime; ino->i_ino = proc_ino->i_ino; ino->i_mode = proc_ino->i_mode; ino->i_nlink = proc_ino->i_nlink; ino->i_size = proc_ino->i_size; ino->i_blksize = proc_ino->i_blksize; ino->i_blocks = proc_ino->i_blocks; } static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry, struct nameidata *nd) { struct dentry *proc_dentry, *new, *parent; struct inode *inode; int err, deleted; deleted = file_removed(dentry, NULL); if(deleted < 0) return(ERR_PTR(deleted)); else if(deleted) return(ERR_PTR(-ENOENT)); err = -ENOMEM; parent = HPPFS_I(ino)->proc_dentry; mutex_lock(&parent->d_inode->i_mutex); proc_dentry = d_lookup(parent, &dentry->d_name); if(proc_dentry == NULL){ proc_dentry = d_alloc(parent, &dentry->d_name); if(proc_dentry == NULL){ mutex_unlock(&parent->d_inode->i_mutex); goto out; } new = (*parent->d_inode->i_op->lookup)(parent->d_inode, proc_dentry, NULL); if(new){ dput(proc_dentry); proc_dentry = new; } } mutex_unlock(&parent->d_inode->i_mutex); if(IS_ERR(proc_dentry)) return(proc_dentry); inode = iget(ino->i_sb, 0); if(inode == NULL) goto out_dput; err = init_inode(inode, proc_dentry); if(err) goto out_put; hppfs_read_inode(inode); d_add(dentry, inode); dentry->d_op = &hppfs_dentry_ops; return(NULL); out_put: iput(inode); out_dput: dput(proc_dentry); out: return(ERR_PTR(err)); } static struct inode_operations hppfs_file_iops = { }; static ssize_t read_proc(struct file *file, char __user *buf, ssize_t count, loff_t *ppos, int is_user) { ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); ssize_t n; read = file->f_dentry->d_inode->i_fop->read; if(!is_user) set_fs(KERNEL_DS); n = (*read)(file, buf, count, &file->f_pos); if(!is_user) set_fs(USER_DS); if(ppos) *ppos = file->f_pos; return n; } static ssize_t hppfs_read_file(int fd, char __user *buf, ssize_t count) { ssize_t n; int cur, err; char *new_buf; n = -ENOMEM; new_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if(new_buf == NULL){ printk("hppfs_read_file : kmalloc failed\n"); goto out; } n = 0; while(count > 0){ cur = min_t(ssize_t, count, PAGE_SIZE); err = os_read_file(fd, new_buf, cur); if(err < 0){ printk("hppfs_read : read failed, errno = %d\n", err); n = err; goto out_free; } else if(err == 0) break; if(copy_to_user(buf, new_buf, err)){ n = -EFAULT; goto out_free; } n += err; count -= err; } out_free: kfree(new_buf); out: return n; } static ssize_t hppfs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct hppfs_private *hppfs = file->private_data; struct hppfs_data *data; loff_t off; int err; if(hppfs->contents != NULL){ if(*ppos >= hppfs->len) return(0); data = hppfs->contents; off = *ppos; while(off >= sizeof(data->contents)){ data = list_entry(data->list.next, struct hppfs_data, list); off -= sizeof(data->contents); } if(off + count > hppfs->len) count = hppfs->len - off; copy_to_user(buf, &data->contents[off], count); *ppos += count; } else if(hppfs->host_fd != -1){ err = os_seek_file(hppfs->host_fd, *ppos); if(err){ printk("hppfs_read : seek failed, errno = %d\n", err); return(err); } count = hppfs_read_file(hppfs->host_fd, buf, count); if(count > 0) *ppos += count; } else count = read_proc(hppfs->proc_file, buf, count, ppos, 1); return(count); } static ssize_t hppfs_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct hppfs_private *data = file->private_data; struct file *proc_file = data->proc_file; ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); int err; write = proc_file->f_dentry->d_inode->i_fop->write; proc_file->f_pos = file->f_pos; err = (*write)(proc_file, buf, len, &proc_file->f_pos); file->f_pos = proc_file->f_pos; return(err); } static int open_host_sock(char *host_file, int *filter_out) { char *end; int fd; end = &host_file[strlen(host_file)]; strcpy(end, "/rw"); *filter_out = 1; fd = os_connect_socket(host_file); if(fd > 0) return(fd); strcpy(end, "/r"); *filter_out = 0; fd = os_connect_socket(host_file); return(fd); } static void free_contents(struct hppfs_data *head) { struct hppfs_data *data; struct list_head *ele, *next; if(head == NULL) return; list_for_each_safe(ele, next, &head->list){ data = list_entry(ele, struct hppfs_data, list); kfree(data); } kfree(head); } static struct hppfs_data *hppfs_get_data(int fd, int filter, struct file *proc_file, struct file *hppfs_file, loff_t *size_out) { struct hppfs_data *data, *new, *head; int n, err; err = -ENOMEM; data = kmalloc(sizeof(*data), GFP_KERNEL); if(data == NULL){ printk("hppfs_get_data : head allocation failed\n"); goto failed; } INIT_LIST_HEAD(&data->list); head = data; *size_out = 0; if(filter){ while((n = read_proc(proc_file, data->contents, sizeof(data->contents), NULL, 0)) > 0) os_write_file(fd, data->contents, n); err = os_shutdown_socket(fd, 0, 1); if(err){ printk("hppfs_get_data : failed to shut down " "socket\n"); goto failed_free; } } while(1){ n = os_read_file(fd, data->contents, sizeof(data->contents)); if(n < 0){ err = n; printk("hppfs_get_data : read failed, errno = %d\n", err); goto failed_free; } else if(n == 0) break; *size_out += n; if(n < sizeof(data->contents)) break; new = kmalloc(sizeof(*data), GFP_KERNEL); if(new == 0){ printk("hppfs_get_data : data allocation failed\n"); err = -ENOMEM; goto failed_free; } INIT_LIST_HEAD(&new->list); list_add(&new->list, &data->list); data = new; } return(head); failed_free: free_contents(head); failed: return(ERR_PTR(err)); } static struct hppfs_private *hppfs_data(void) { struct hppfs_private *data; data = kmalloc(sizeof(*data), GFP_KERNEL); if(data == NULL) return(data); *data = ((struct hppfs_private ) { .host_fd = -1, .len = -1, .contents = NULL } ); return(data); } static int file_mode(int fmode) { if(fmode == (FMODE_READ | FMODE_WRITE)) return(O_RDWR); if(fmode == FMODE_READ) return(O_RDONLY); if(fmode == FMODE_WRITE) return(O_WRONLY); return(0); } static int hppfs_open(struct inode *inode, struct file *file) { struct hppfs_private *data; struct dentry *proc_dentry; char *host_file; int err, fd, type, filter; err = -ENOMEM; data = hppfs_data(); if(data == NULL) goto out; host_file = dentry_name(file->f_dentry, strlen("/rw")); if(host_file == NULL) goto out_free2; proc_dentry = HPPFS_I(inode)->proc_dentry; /* XXX This isn't closed anywhere */ data->proc_file = dentry_open(dget(proc_dentry), NULL, file_mode(file->f_mode)); err = PTR_ERR(data->proc_file); if(IS_ERR(data->proc_file)) goto out_free1; type = os_file_type(host_file); if(type == OS_TYPE_FILE){ fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); if(fd >= 0) data->host_fd = fd; else printk("hppfs_open : failed to open '%s', errno = %d\n", host_file, -fd); data->contents = NULL; } else if(type == OS_TYPE_DIR){ fd = open_host_sock(host_file, &filter); if(fd > 0){ data->contents = hppfs_get_data(fd, filter, data->proc_file, file, &data->len); if(!IS_ERR(data->contents)) data->host_fd = fd; } else printk("hppfs_open : failed to open a socket in " "'%s', errno = %d\n", host_file, -fd); } kfree(host_file); file->private_data = data; return(0); out_free1: kfree(host_file); out_free2: free_contents(data->contents); kfree(data); out: return(err); } static int hppfs_dir_open(struct inode *inode, struct file *file) { struct hppfs_private *data; struct dentry *proc_dentry; int err; err = -ENOMEM; data = hppfs_data(); if(data == NULL) goto out; proc_dentry = HPPFS_I(inode)->proc_dentry; data->proc_file = dentry_open(dget(proc_dentry), NULL, file_mode(file->f_mode)); err = PTR_ERR(data->proc_file); if(IS_ERR(data->proc_file)) goto out_free; file->private_data = data; return(0); out_free: kfree(data); out: return(err); } static loff_t hppfs_llseek(struct file *file, loff_t off, int where) { struct hppfs_private *data = file->private_data; struct file *proc_file = data->proc_file; loff_t (*llseek)(struct file *, loff_t, int); loff_t ret; llseek = proc_file->f_dentry->d_inode->i_fop->llseek; if(llseek != NULL){ ret = (*llseek)(proc_file, off, where); if(ret < 0) return(ret); } return(default_llseek(file, off, where)); } static const struct file_operations hppfs_file_fops = { .owner = NULL, .llseek = hppfs_llseek, .read = hppfs_read, .write = hppfs_write, .open = hppfs_open, }; struct hppfs_dirent { void *vfs_dirent; filldir_t filldir; struct dentry *dentry; }; static int hppfs_filldir(void *d, const char *name, int size, loff_t offset, ino_t inode, unsigned int type) { struct hppfs_dirent *dirent = d; if(file_removed(dirent->dentry, name)) return(0); return((*dirent->filldir)(dirent->vfs_dirent, name, size, offset, inode, type)); } static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir) { struct hppfs_private *data = file->private_data; struct file *proc_file = data->proc_file; int (*readdir)(struct file *, void *, filldir_t); struct hppfs_dirent dirent = ((struct hppfs_dirent) { .vfs_dirent = ent, .filldir = filldir, .dentry = file->f_dentry } ); int err; readdir = proc_file->f_dentry->d_inode->i_fop->readdir; proc_file->f_pos = file->f_pos; err = (*readdir)(proc_file, &dirent, hppfs_filldir); file->f_pos = proc_file->f_pos; return(err); } static int hppfs_fsync(struct file *file, struct dentry *dentry, int datasync) { return(0); } static const struct file_operations hppfs_dir_fops = { .owner = NULL, .readdir = hppfs_readdir, .open = hppfs_dir_open, .fsync = hppfs_fsync, }; static int hppfs_statfs(struct super_block *sb, struct kstatfs *sf) { sf->f_blocks = 0; sf->f_bfree = 0; sf->f_bavail = 0; sf->f_files = 0; sf->f_ffree = 0; sf->f_type = HPPFS_SUPER_MAGIC; return(0); } static struct inode *hppfs_alloc_inode(struct super_block *sb) { struct hppfs_inode_info *hi; hi = kmalloc(sizeof(*hi), GFP_KERNEL); if(hi == NULL) return(NULL); *hi = ((struct hppfs_inode_info) { .proc_dentry = NULL }); inode_init_once(&hi->vfs_inode); return(&hi->vfs_inode); } void hppfs_delete_inode(struct inode *ino) { clear_inode(ino); } static void hppfs_destroy_inode(struct inode *inode) { kfree(HPPFS_I(inode)); } static struct super_operations hppfs_sbops = { .alloc_inode = hppfs_alloc_inode, .destroy_inode = hppfs_destroy_inode, .read_inode = hppfs_read_inode, .delete_inode = hppfs_delete_inode, .statfs = hppfs_statfs, }; static int hppfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) { struct file *proc_file; struct dentry *proc_dentry; int ret; proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry; proc_file = dentry_open(dget(proc_dentry), NULL, O_RDONLY); if (IS_ERR(proc_file)) return PTR_ERR(proc_file); ret = proc_dentry->d_inode->i_op->readlink(proc_dentry, buffer, buflen); fput(proc_file); return ret; } static void* hppfs_follow_link(struct dentry *dentry, struct nameidata *nd) { struct file *proc_file; struct dentry *proc_dentry; void *ret; proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry; proc_file = dentry_open(dget(proc_dentry), NULL, O_RDONLY); if (IS_ERR(proc_file)) return proc_file; ret = proc_dentry->d_inode->i_op->follow_link(proc_dentry, nd); fput(proc_file); return ret; } static struct inode_operations hppfs_dir_iops = { .lookup = hppfs_lookup, }; static struct inode_operations hppfs_link_iops = { .readlink = hppfs_readlink, .follow_link = hppfs_follow_link, }; static int init_inode(struct inode *inode, struct dentry *dentry) { if(S_ISDIR(dentry->d_inode->i_mode)){ inode->i_op = &hppfs_dir_iops; inode->i_fop = &hppfs_dir_fops; } else if(S_ISLNK(dentry->d_inode->i_mode)){ inode->i_op = &hppfs_link_iops; inode->i_fop = &hppfs_file_fops; } else { inode->i_op = &hppfs_file_iops; inode->i_fop = &hppfs_file_fops; } HPPFS_I(inode)->proc_dentry = dentry; return(0); } static int hppfs_fill_super(struct super_block *sb, void *d, int silent) { struct inode *root_inode; struct file_system_type *procfs; struct super_block *proc_sb; int err; err = -ENOENT; procfs = get_fs_type("proc"); if(procfs == NULL) goto out; if(list_empty(&procfs->fs_supers)) goto out; proc_sb = list_entry(procfs->fs_supers.next, struct super_block, s_instances); sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; sb->s_magic = HPPFS_SUPER_MAGIC; sb->s_op = &hppfs_sbops; root_inode = iget(sb, 0); if(root_inode == NULL) goto out; err = init_inode(root_inode, proc_sb->s_root); if(err) goto out_put; err = -ENOMEM; sb->s_root = d_alloc_root(root_inode); if(sb->s_root == NULL) goto out_put; hppfs_read_inode(root_inode); return(0); out_put: iput(root_inode); out: return(err); } static struct super_block *hppfs_read_super(struct file_system_type *type, int flags, const char *dev_name, void *data) { return(get_sb_nodev(type, flags, data, hppfs_fill_super)); } static struct file_system_type hppfs_type = { .owner = THIS_MODULE, .name = "hppfs", .get_sb = hppfs_read_super, .kill_sb = kill_anon_super, .fs_flags = 0, }; static int __init init_hppfs(void) { return(register_filesystem(&hppfs_type)); } static void __exit exit_hppfs(void) { unregister_filesystem(&hppfs_type); } module_init(init_hppfs) module_exit(exit_hppfs) MODULE_LICENSE("GPL"); /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */ linux-2.6.17/fs/hppfs/.svn/prop-base/0000755000000000000000000000000010574207633015757 5ustar rootrootlinux-2.6.17/fs/hppfs/Makefile0000644000000000000000000000023110564537530014640 0ustar rootroot# # Copyright (C) 2002, 2003 Jeff Dike (jdike@karaya.com) # Licensed under the GPL # hppfs-objs := hppfs_kern.o obj-y = obj-$(CONFIG_HPPFS) += hppfs.o linux-2.6.17/fs/openpromfs/0000755000000000000000000000000010574207603014250 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/0000755000000000000000000000000010613366602015133 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/format0000644000000000000000000000000210574207603016337 0ustar rootroot8 linux-2.6.17/fs/openpromfs/.svn/entries0000444000000000000000000000075310613366571016537 0ustar rootroot8 dir 88 svn+ssh://192.168.2.6/home/svn-root/repos/FVT/linux-2.6.17/fs/openpromfs svn+ssh://192.168.2.6/home/svn-root/repos/FVT 2007-02-14T07:50:48.156956Z 1 root svn:special svn:externals svn:needs-lock 4bdaaa2a-274d-416c-a84c-08d2865ac13c Makefile file 2007-02-14T07:50:48.000000Z bd5043a54f3926724fba420cf4787996 2007-02-14T07:50:48.156956Z 1 root inode.c file 2007-02-14T07:50:48.000000Z 28854ff4fa92282da34a1186c2b95f1b 2007-02-14T07:50:48.156956Z 1 root linux-2.6.17/fs/openpromfs/.svn/tmp/0000755000000000000000000000000010613366571015740 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/tmp/props/0000755000000000000000000000000010574207603017077 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/tmp/text-base/0000755000000000000000000000000010574207603017630 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/tmp/prop-base/0000755000000000000000000000000010574207603017624 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/props/0000755000000000000000000000000010574207603016277 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/text-base/0000755000000000000000000000000010574207603017030 5ustar rootrootlinux-2.6.17/fs/openpromfs/.svn/text-base/Makefile.svn-base0000444000000000000000000000021110574207603022175 0ustar rootroot# # Makefile for the Linux Sun Openprom filesystem routines. # obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs.o openpromfs-objs := inode.o linux-2.6.17/fs/openpromfs/.svn/text-base/inode.c.svn-base0000444000000000000000000006252410574207603022016 0ustar rootroot/* $Id: inode.c,v 1.15 2001/11/12 09:43:39 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) */ #include #include #include #include #include #include #include #include #include #include #include #define ALIASES_NNODES 64 typedef struct { u16 parent; u16 next; u16 child; u16 first_prop; u32 node; } openpromfs_node; typedef struct { #define OPP_STRING 0x10 #define OPP_STRINGLIST 0x20 #define OPP_BINARY 0x40 #define OPP_HEXSTRING 0x80 #define OPP_DIRTY 0x01 #define OPP_QUOTED 0x02 #define OPP_NOTQUOTED 0x04 #define OPP_ASCIIZ 0x08 u32 flag; u32 alloclen; u32 len; char *value; char name[8]; } openprom_property; static openpromfs_node *nodes; static int alloced; static u16 last_node; static u16 first_prop; static u16 options = 0xffff; static u16 aliases = 0xffff; static int aliases_nodes; static char *alias_names [ALIASES_NNODES]; #define OPENPROM_ROOT_INO 16 #define OPENPROM_FIRST_INO OPENPROM_ROOT_INO #define NODE(ino) nodes[ino - OPENPROM_FIRST_INO] #define NODE2INO(node) (node + OPENPROM_FIRST_INO) #define NODEP2INO(no) (no + OPENPROM_FIRST_INO + last_node) static int openpromfs_create (struct inode *, struct dentry *, int, struct nameidata *); static int openpromfs_readdir(struct file *, void *, filldir_t); static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry, struct nameidata *nd); static int openpromfs_unlink (struct inode *, struct dentry *dentry); static ssize_t nodenum_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; char buffer[10]; if (count < 0 || !inode->u.generic_ip) return -EINVAL; sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip)); if (file->f_pos >= 9) return 0; if (count > 9 - file->f_pos) count = 9 - file->f_pos; if (copy_to_user(buf, buffer + file->f_pos, count)) return -EFAULT; *ppos += count; return count; } static ssize_t property_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; int i, j, k; u32 node; char *p, *s; u32 *q; openprom_property *op; char buffer[64]; if (!filp->private_data) { node = nodes[(u16)((long)inode->u.generic_ip)].node; i = ((u32)(long)inode->u.generic_ip) >> 16; if ((u16)((long)inode->u.generic_ip) == aliases) { if (i >= aliases_nodes) p = NULL; else p = alias_names [i]; } else for (p = prom_firstprop (node, buffer); i && p && *p; p = prom_nextprop (node, p, buffer), i--) /* nothing */ ; if (!p || !*p) return -EIO; i = prom_getproplen (node, p); if (i < 0) { if ((u16)((long)inode->u.generic_ip) == aliases) i = 0; else return -EIO; } k = i; if (i < 64) i = 64; filp->private_data = kmalloc (sizeof (openprom_property) + (j = strlen (p)) + 2 * i, GFP_KERNEL); if (!filp->private_data) return -ENOMEM; op = (openprom_property *)filp->private_data; op->flag = 0; op->alloclen = 2 * i; strcpy (op->name, p); op->value = (char *)(((unsigned long)(op->name + j + 4)) & ~3); op->len = k; if (k && prom_getproperty (node, p, op->value, i) < 0) return -EIO; op->value [k] = 0; if (k) { for (s = NULL, p = op->value; p < op->value + k; p++) { if ((*p >= ' ' && *p <= '~') || *p == '\n') { op->flag |= OPP_STRING; s = p; continue; } if (p > op->value && !*p && s == p - 1) { if (p < op->value + k - 1) op->flag |= OPP_STRINGLIST; else op->flag |= OPP_ASCIIZ; continue; } if (k == 1 && !*p) { op->flag |= (OPP_STRING|OPP_ASCIIZ); break; } op->flag &= ~(OPP_STRING|OPP_STRINGLIST); if (k & 3) op->flag |= OPP_HEXSTRING; else op->flag |= OPP_BINARY; break; } if (op->flag & OPP_STRINGLIST) op->flag &= ~(OPP_STRING); if (op->flag & OPP_ASCIIZ) op->len--; } } else op = (openprom_property *)filp->private_data; if (!count || !(op->len || (op->flag & OPP_ASCIIZ))) return 0; if (*ppos >= 0xffffff || count >= 0xffffff) return -EINVAL; if (op->flag & OPP_STRINGLIST) { for (k = 0, p = op->value; p < op->value + op->len; p++) if (!*p) k++; i = op->len + 4 * k + 3; } else if (op->flag & OPP_STRING) { i = op->len + 3; } else if (op->flag & OPP_BINARY) { i = (op->len * 9) >> 2; } else { i = (op->len << 1) + 1; } k = *ppos; if (k >= i) return 0; if (count > i - k) count = i - k; if (op->flag & OPP_STRING) { if (!k) { if (put_user('\'', buf)) return -EFAULT; k++; count--; } if (k + count >= i - 2) j = i - 2 - k; else j = count; if (j >= 0) { if (copy_to_user(buf + k - *ppos, op->value + k - 1, j)) return -EFAULT; count -= j; k += j; } if (count) { if (put_user('\'', &buf [k++ - *ppos])) return -EFAULT; } if (count > 1) { if (put_user('\n', &buf [k++ - *ppos])) return -EFAULT; } } else if (op->flag & OPP_STRINGLIST) { char *tmp; tmp = kmalloc (i, GFP_KERNEL); if (!tmp) return -ENOMEM; s = tmp; *s++ = '\''; for (p = op->value; p < op->value + op->len; p++) { if (!*p) { strcpy(s, "' + '"); s += 5; continue; } *s++ = *p; } strcpy(s, "'\n"); if (copy_to_user(buf, tmp + k, count)) return -EFAULT; kfree(tmp); k += count; } else if (op->flag & OPP_BINARY) { char buffer[10]; u32 *first, *last; int first_off, last_cnt; first = ((u32 *)op->value) + k / 9; first_off = k % 9; last = ((u32 *)op->value) + (k + count - 1) / 9; last_cnt = (k + count) % 9; if (!last_cnt) last_cnt = 9; if (first == last) { sprintf (buffer, "%08x.", *first); if (copy_to_user(buf, buffer + first_off, last_cnt - first_off)) return -EFAULT; buf += last_cnt - first_off; } else { for (q = first; q <= last; q++) { sprintf (buffer, "%08x.", *q); if (q == first) { if (copy_to_user(buf, buffer + first_off, 9 - first_off)) return -EFAULT; buf += 9 - first_off; } else if (q == last) { if (copy_to_user(buf, buffer, last_cnt)) return -EFAULT; buf += last_cnt; } else { if (copy_to_user(buf, buffer, 9)) return -EFAULT; buf += 9; } } } if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9) { if (put_user('\n', (buf - 1))) return -EFAULT; } k += count; } else if (op->flag & OPP_HEXSTRING) { char buffer[3]; if ((k < i - 1) && (k & 1)) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); if (put_user(buffer[1], &buf[k++ - *ppos])) return -EFAULT; count--; } for (; (count > 1) && (k < i - 1); k += 2) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); if (copy_to_user(buf + k - *ppos, buffer, 2)) return -EFAULT; count -= 2; } if (count && (k < i - 1)) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); if (put_user(buffer[0], &buf[k++ - *ppos])) return -EFAULT; count--; } if (count) { if (put_user('\n', &buf [k++ - *ppos])) return -EFAULT; } } count = k - *ppos; *ppos = k; return count; } static ssize_t property_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int i, j, k; char *p; u32 *q; void *b; openprom_property *op; if (*ppos >= 0xffffff || count >= 0xffffff) return -EINVAL; if (!filp->private_data) { i = property_read (filp, NULL, 0, NULL); if (i) return i; } k = *ppos; op = (openprom_property *)filp->private_data; if (!(op->flag & OPP_STRING)) { u32 *first, *last; int first_off, last_cnt; u32 mask, mask2; char tmp [9]; int forcelen = 0; j = k % 9; for (i = 0; i < count; i++, j++) { if (j == 9) j = 0; if (!j) { char ctmp; if (get_user(ctmp, &buf[i])) return -EFAULT; if (ctmp != '.') { if (ctmp != '\n') { if (op->flag & OPP_BINARY) return -EINVAL; else goto write_try_string; } else { count = i + 1; forcelen = 1; break; } } } else { char ctmp; if (get_user(ctmp, &buf[i])) return -EFAULT; if (ctmp < '0' || (ctmp > '9' && ctmp < 'A') || (ctmp > 'F' && ctmp < 'a') || ctmp > 'f') { if (op->flag & OPP_BINARY) return -EINVAL; else goto write_try_string; } } } op->flag |= OPP_BINARY; tmp [8] = 0; i = ((count + k + 8) / 9) << 2; if (op->alloclen <= i) { b = kmalloc (sizeof (openprom_property) + 2 * i, GFP_KERNEL); if (!b) return -ENOMEM; memcpy (b, filp->private_data, sizeof (openprom_property) + strlen (op->name) + op->alloclen); memset (((char *)b) + sizeof (openprom_property) + strlen (op->name) + op->alloclen, 0, 2 * i - op->alloclen); op = (openprom_property *)b; op->alloclen = 2*i; b = filp->private_data; filp->private_data = (void *)op; kfree (b); } first = ((u32 *)op->value) + (k / 9); first_off = k % 9; last = (u32 *)(op->value + i); last_cnt = (k + count) % 9; if (first + 1 == last) { memset (tmp, '0', 8); if (copy_from_user(tmp + first_off, buf, (count + first_off > 8) ? 8 - first_off : count)) return -EFAULT; mask = 0xffffffff; mask2 = 0xffffffff; for (j = 0; j < first_off; j++) mask >>= 1; for (j = 8 - count - first_off; j > 0; j--) mask2 <<= 1; mask &= mask2; if (mask) { *first &= ~mask; *first |= simple_strtoul (tmp, NULL, 16); op->flag |= OPP_DIRTY; } } else { op->flag |= OPP_DIRTY; for (q = first; q < last; q++) { if (q == first) { if (first_off < 8) { memset (tmp, '0', 8); if (copy_from_user(tmp + first_off, buf, 8 - first_off)) return -EFAULT; mask = 0xffffffff; for (j = 0; j < first_off; j++) mask >>= 1; *q &= ~mask; *q |= simple_strtoul (tmp,NULL,16); } buf += 9; } else if ((q == last - 1) && last_cnt && (last_cnt < 8)) { memset (tmp, '0', 8); if (copy_from_user(tmp, buf, last_cnt)) return -EFAULT; mask = 0xffffffff; for (j = 0; j < 8 - last_cnt; j++) mask <<= 1; *q &= ~mask; *q |= simple_strtoul (tmp, NULL, 16); buf += last_cnt; } else { char tchars[17]; /* XXX yuck... */ if (copy_from_user(tchars, buf, 16)) return -EFAULT; *q = simple_strtoul (tchars, NULL, 16); buf += 9; } } } if (!forcelen) { if (op->len < i) op->len = i; } else op->len = i; *ppos += count; } write_try_string: if (!(op->flag & OPP_BINARY)) { if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) { char ctmp; /* No way, if somebody starts writing from the middle, * we don't know whether he uses quotes around or not */ if (k > 0) return -EINVAL; if (get_user(ctmp, buf)) return -EFAULT; if (ctmp == '\'') { op->flag |= OPP_QUOTED; buf++; count--; (*ppos)++; if (!count) { op->flag |= OPP_STRING; return 1; } } else op->flag |= OPP_NOTQUOTED; } op->flag |= OPP_STRING; if (op->alloclen <= count + *ppos) { b = kmalloc (sizeof (openprom_property) + 2 * (count + *ppos), GFP_KERNEL); if (!b) return -ENOMEM; memcpy (b, filp->private_data, sizeof (openprom_property) + strlen (op->name) + op->alloclen); memset (((char *)b) + sizeof (openprom_property) + strlen (op->name) + op->alloclen, 0, 2*(count - *ppos) - op->alloclen); op = (openprom_property *)b; op->alloclen = 2*(count + *ppos); b = filp->private_data; filp->private_data = (void *)op; kfree (b); } p = op->value + *ppos - ((op->flag & OPP_QUOTED) ? 1 : 0); if (copy_from_user(p, buf, count)) return -EFAULT; op->flag |= OPP_DIRTY; for (i = 0; i < count; i++, p++) if (*p == '\n') { *p = 0; break; } if (i < count) { op->len = p - op->value; *ppos += i + 1; if ((p > op->value) && (op->flag & OPP_QUOTED) && (*(p - 1) == '\'')) op->len--; } else { if (p - op->value > op->len) op->len = p - op->value; *ppos += count; } } return *ppos - k; } int property_release (struct inode *inode, struct file *filp) { openprom_property *op = (openprom_property *)filp->private_data; int error; u32 node; if (!op) return 0; lock_kernel(); node = nodes[(u16)((long)inode->u.generic_ip)].node; if ((u16)((long)inode->u.generic_ip) == aliases) { if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) { char *p = op->name; int i = (op->value - op->name) - strlen (op->name) - 1; op->value [op->len] = 0; *(op->value - 1) = ' '; if (i) { for (p = op->value - i - 2; p >= op->name; p--) p[i] = *p; p = op->name + i; } memcpy (p - 8, "nvalias ", 8); prom_feval (p - 8); } } else if (op->flag & OPP_DIRTY) { if (op->flag & OPP_STRING) { op->value [op->len] = 0; error = prom_setprop (node, op->name, op->value, op->len + 1); if (error <= 0) printk (KERN_WARNING "openpromfs: " "Couldn't write property %s\n", op->name); } else if ((op->flag & OPP_BINARY) || !op->len) { error = prom_setprop (node, op->name, op->value, op->len); if (error <= 0) printk (KERN_WARNING "openpromfs: " "Couldn't write property %s\n", op->name); } else { printk (KERN_WARNING "openpromfs: " "Unknown property type of %s\n", op->name); } } unlock_kernel(); kfree (filp->private_data); return 0; } static const struct file_operations openpromfs_prop_ops = { .read = property_read, .write = property_write, .release = property_release, }; static const struct file_operations openpromfs_nodenum_ops = { .read = nodenum_read, }; static const struct file_operations openprom_operations = { .read = generic_read_dir, .readdir = openpromfs_readdir, }; static struct inode_operations openprom_alias_inode_operations = { .create = openpromfs_create, .lookup = openpromfs_lookup, .unlink = openpromfs_unlink, }; static struct inode_operations openprom_inode_operations = { .lookup = openpromfs_lookup, }; static int lookup_children(u16 n, const char * name, int len) { int ret; u16 node; for (; n != 0xffff; n = nodes[n].next) { node = nodes[n].child; if (node != 0xffff) { char buffer[128]; int i; char *p; while (node != 0xffff) { if (prom_getname (nodes[node].node, buffer, 128) >= 0) { i = strlen (buffer); if ((len == i) && !strncmp (buffer, name, len)) return NODE2INO(node); p = strchr (buffer, '@'); if (p && (len == p - buffer) && !strncmp (buffer, name, len)) return NODE2INO(node); } node = nodes[node].next; } } else continue; ret = lookup_children (nodes[n].child, name, len); if (ret) return ret; } return 0; } static struct dentry *openpromfs_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) { int ino = 0; #define OPFSL_DIR 0 #define OPFSL_PROPERTY 1 #define OPFSL_NODENUM 2 int type = 0; char buffer[128]; char *p; const char *name; u32 n; u16 dirnode; unsigned int len; int i; struct inode *inode; char buffer2[64]; inode = NULL; name = dentry->d_name.name; len = dentry->d_name.len; lock_kernel(); if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) { ino = NODEP2INO(NODE(dir->i_ino).first_prop); type = OPFSL_NODENUM; } if (!ino) { u16 node = NODE(dir->i_ino).child; while (node != 0xffff) { if (prom_getname (nodes[node].node, buffer, 128) >= 0) { i = strlen (buffer); if (len == i && !strncmp (buffer, name, len)) { ino = NODE2INO(node); type = OPFSL_DIR; break; } p = strchr (buffer, '@'); if (p && (len == p - buffer) && !strncmp (buffer, name, len)) { ino = NODE2INO(node); type = OPFSL_DIR; break; } } node = nodes[node].next; } } n = NODE(dir->i_ino).node; dirnode = dir->i_ino - OPENPROM_FIRST_INO; if (!ino) { int j = NODEP2INO(NODE(dir->i_ino).first_prop); if (dirnode != aliases) { for (p = prom_firstprop (n, buffer2); p && *p; p = prom_nextprop (n, p, buffer2)) { j++; if ((len == strlen (p)) && !strncmp (p, name, len)) { ino = j; type = OPFSL_PROPERTY; break; } } } else { int k; for (k = 0; k < aliases_nodes; k++) { j++; if (alias_names [k] && (len == strlen (alias_names [k])) && !strncmp (alias_names [k], name, len)) { ino = j; type = OPFSL_PROPERTY; break; } } } } if (!ino) { ino = lookup_children (NODE(dir->i_ino).child, name, len); if (ino) type = OPFSL_DIR; else { unlock_kernel(); return ERR_PTR(-ENOENT); } } inode = iget (dir->i_sb, ino); unlock_kernel(); if (!inode) return ERR_PTR(-EINVAL); switch (type) { case OPFSL_DIR: inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; if (ino == OPENPROM_FIRST_INO + aliases) { inode->i_mode |= S_IWUSR; inode->i_op = &openprom_alias_inode_operations; } else inode->i_op = &openprom_inode_operations; inode->i_fop = &openprom_operations; inode->i_nlink = 2; break; case OPFSL_NODENUM: inode->i_mode = S_IFREG | S_IRUGO; inode->i_fop = &openpromfs_nodenum_ops; inode->i_nlink = 1; inode->u.generic_ip = (void *)(long)(n); break; case OPFSL_PROPERTY: if ((dirnode == options) && (len == 17) && !strncmp (name, "security-password", 17)) inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; else { inode->i_mode = S_IFREG | S_IRUGO; if (dirnode == options || dirnode == aliases) { if (len != 4 || strncmp (name, "name", 4)) inode->i_mode |= S_IWUSR; } } inode->i_fop = &openpromfs_prop_ops; inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; inode->u.generic_ip = (void *)(long)(((u16)dirnode) | (((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16)); break; } inode->i_gid = 0; inode->i_uid = 0; d_add(dentry, inode); return NULL; } static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; unsigned int ino; u32 n; int i, j; char buffer[128]; u16 node; char *p; char buffer2[64]; lock_kernel(); ino = inode->i_ino; i = filp->f_pos; switch (i) { case 0: if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) goto out; i++; filp->f_pos++; /* fall thru */ case 1: if (filldir(dirent, "..", 2, i, (NODE(ino).parent == 0xffff) ? OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent), DT_DIR) < 0) goto out; i++; filp->f_pos++; /* fall thru */ default: i -= 2; node = NODE(ino).child; while (i && node != 0xffff) { node = nodes[node].next; i--; } while (node != 0xffff) { if (prom_getname (nodes[node].node, buffer, 128) < 0) goto out; if (filldir(dirent, buffer, strlen(buffer), filp->f_pos, NODE2INO(node), DT_DIR) < 0) goto out; filp->f_pos++; node = nodes[node].next; } j = NODEP2INO(NODE(ino).first_prop); if (!i) { if (filldir(dirent, ".node", 5, filp->f_pos, j, DT_REG) < 0) goto out; filp->f_pos++; } else i--; n = NODE(ino).node; if (ino == OPENPROM_FIRST_INO + aliases) { for (j++; i < aliases_nodes; i++, j++) { if (alias_names [i]) { if (filldir (dirent, alias_names [i], strlen (alias_names [i]), filp->f_pos, j, DT_REG) < 0) goto out; filp->f_pos++; } } } else { for (p = prom_firstprop (n, buffer2); p && *p; p = prom_nextprop (n, p, buffer2)) { j++; if (i) i--; else { if (filldir(dirent, p, strlen(p), filp->f_pos, j, DT_REG) < 0) goto out; filp->f_pos++; } } } } out: unlock_kernel(); return 0; } static int openpromfs_create (struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { char *p; struct inode *inode; if (!dir) return -ENOENT; if (dentry->d_name.len > 256) return -EINVAL; p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL); if (!p) return -ENOMEM; strncpy (p, dentry->d_name.name, dentry->d_name.len); p [dentry->d_name.len] = 0; lock_kernel(); if (aliases_nodes == ALIASES_NNODES) { kfree(p); unlock_kernel(); return -EIO; } alias_names [aliases_nodes++] = p; inode = iget (dir->i_sb, NODEP2INO(NODE(dir->i_ino).first_prop) + aliases_nodes); if (!inode) { unlock_kernel(); return -EINVAL; } inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; inode->i_fop = &openpromfs_prop_ops; inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; inode->u.generic_ip = (void *)(long)(((u16)aliases) | (((u16)(aliases_nodes - 1)) << 16)); unlock_kernel(); d_instantiate(dentry, inode); return 0; } static int openpromfs_unlink (struct inode *dir, struct dentry *dentry) { unsigned int len; char *p; const char *name; int i; name = dentry->d_name.name; len = dentry->d_name.len; lock_kernel(); for (i = 0; i < aliases_nodes; i++) if ((strlen (alias_names [i]) == len) && !strncmp (name, alias_names[i], len)) { char buffer[512]; p = alias_names [i]; alias_names [i] = NULL; kfree (p); strcpy (buffer, "nvunalias "); memcpy (buffer + 10, name, len); buffer [10 + len] = 0; prom_feval (buffer); } unlock_kernel(); return 0; } /* {{{ init section */ static int __init check_space (u16 n) { unsigned long pages; if ((1 << alloced) * PAGE_SIZE < (n + 2) * sizeof(openpromfs_node)) { pages = __get_free_pages (GFP_KERNEL, alloced + 1); if (!pages) return -1; if (nodes) { memcpy ((char *)pages, (char *)nodes, (1 << alloced) * PAGE_SIZE); free_pages ((unsigned long)nodes, alloced); } alloced++; nodes = (openpromfs_node *)pages; } return 0; } static u16 __init get_nodes (u16 parent, u32 node) { char *p; u16 n = last_node++, i; char buffer[64]; if (check_space (n) < 0) return 0xffff; nodes[n].parent = parent; nodes[n].node = node; nodes[n].next = 0xffff; nodes[n].child = 0xffff; nodes[n].first_prop = first_prop++; if (!parent) { char buffer[8]; int j; if ((j = prom_getproperty (node, "name", buffer, 8)) >= 0) { buffer[j] = 0; if (!strcmp (buffer, "options")) options = n; else if (!strcmp (buffer, "aliases")) aliases = n; } } if (n != aliases) for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; p = prom_nextprop (node, p, buffer)) first_prop++; else { char *q; for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; p = prom_nextprop (node, p, buffer)) { if (aliases_nodes == ALIASES_NNODES) break; for (i = 0; i < aliases_nodes; i++) if (!strcmp (p, alias_names [i])) break; if (i < aliases_nodes) continue; q = kmalloc (strlen (p) + 1, GFP_KERNEL); if (!q) return 0xffff; strcpy (q, p); alias_names [aliases_nodes++] = q; } first_prop += ALIASES_NNODES; } node = prom_getchild (node); if (node) { parent = get_nodes (n, node); if (parent == 0xffff) return 0xffff; nodes[n].child = parent; while ((node = prom_getsibling (node)) != 0) { i = get_nodes (n, node); if (i == 0xffff) return 0xffff; nodes[parent].next = i; parent = i; } } return n; } static void openprom_read_inode(struct inode * inode) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; if (inode->i_ino == OPENPROM_ROOT_INO) { inode->i_op = &openprom_inode_operations; inode->i_fop = &openprom_operations; inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; } } static int openprom_remount(struct super_block *sb, int *flags, char *data) { *flags |= MS_NOATIME; return 0; } static struct super_operations openprom_sops = { .read_inode = openprom_read_inode, .statfs = simple_statfs, .remount_fs = openprom_remount, }; static int openprom_fill_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; s->s_flags |= MS_NOATIME; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = OPENPROM_SUPER_MAGIC; s->s_op = &openprom_sops; s->s_time_gran = 1; root_inode = iget(s, OPENPROM_ROOT_INO); if (!root_inode) goto out_no_root; s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_no_root; return 0; out_no_root: printk("openprom_fill_super: get root inode failed\n"); iput(root_inode); return -ENOMEM; } static struct super_block *openprom_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_single(fs_type, flags, data, openprom_fill_super); } static struct file_system_type openprom_fs_type = { .owner = THIS_MODULE, .name = "openpromfs", .get_sb = openprom_get_sb, .kill_sb = kill_anon_super, }; static int __init init_openprom_fs(void) { nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0); if (!nodes) { printk (KERN_WARNING "openpromfs: can't get free page\n"); return -EIO; } if (get_nodes (0xffff, prom_root_node) == 0xffff) { printk (KERN_WARNING "openpromfs: couldn't setup tree\n"); return -EIO; } nodes[last_node].first_prop = first_prop; return register_filesystem(&openprom_fs_type); } static void __exit exit_openprom_fs(void) { int i; unregister_filesystem(&openprom_fs_type); free_pages ((unsigned long)nodes, alloced); for (i = 0; i < aliases_nodes; i++) kfree (alias_names [i]); nodes = NULL; } module_init(init_openprom_fs) module_exit(exit_openprom_fs) MODULE_LICENSE("GPL"); linux-2.6.17/fs/openpromfs/.svn/prop-base/0000755000000000000000000000000010574207603017024 5ustar rootrootlinux-2.6.17/fs/openpromfs/inode.c0000644000000000000000000006252410564537530015527 0ustar rootroot/* $Id: inode.c,v 1.15 2001/11/12 09:43:39 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) */ #include #include #include #include #include #include #include #include #include #include #include #define ALIASES_NNODES 64 typedef struct { u16 parent; u16 next; u16 child; u16 first_prop; u32 node; } openpromfs_node; typedef struct { #define OPP_STRING 0x10 #define OPP_STRINGLIST 0x20 #define OPP_BINARY 0x40 #define OPP_HEXSTRING 0x80 #define OPP_DIRTY 0x01 #define OPP_QUOTED 0x02 #define OPP_NOTQUOTED 0x04 #define OPP_ASCIIZ 0x08 u32 flag; u32 alloclen; u32 len; char *value; char name[8]; } openprom_property; static openpromfs_node *nodes; static int alloced; static u16 last_node; static u16 first_prop; static u16 options = 0xffff; static u16 aliases = 0xffff; static int aliases_nodes; static char *alias_names [ALIASES_NNODES]; #define OPENPROM_ROOT_INO 16 #define OPENPROM_FIRST_INO OPENPROM_ROOT_INO #define NODE(ino) nodes[ino - OPENPROM_FIRST_INO] #define NODE2INO(node) (node + OPENPROM_FIRST_INO) #define NODEP2INO(no) (no + OPENPROM_FIRST_INO + last_node) static int openpromfs_create (struct inode *, struct dentry *, int, struct nameidata *); static int openpromfs_readdir(struct file *, void *, filldir_t); static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry, struct nameidata *nd); static int openpromfs_unlink (struct inode *, struct dentry *dentry); static ssize_t nodenum_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; char buffer[10]; if (count < 0 || !inode->u.generic_ip) return -EINVAL; sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip)); if (file->f_pos >= 9) return 0; if (count > 9 - file->f_pos) count = 9 - file->f_pos; if (copy_to_user(buf, buffer + file->f_pos, count)) return -EFAULT; *ppos += count; return count; } static ssize_t property_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; int i, j, k; u32 node; char *p, *s; u32 *q; openprom_property *op; char buffer[64]; if (!filp->private_data) { node = nodes[(u16)((long)inode->u.generic_ip)].node; i = ((u32)(long)inode->u.generic_ip) >> 16; if ((u16)((long)inode->u.generic_ip) == aliases) { if (i >= aliases_nodes) p = NULL; else p = alias_names [i]; } else for (p = prom_firstprop (node, buffer); i && p && *p; p = prom_nextprop (node, p, buffer), i--) /* nothing */ ; if (!p || !*p) return -EIO; i = prom_getproplen (node, p); if (i < 0) { if ((u16)((long)inode->u.generic_ip) == aliases) i = 0; else return -EIO; } k = i; if (i < 64) i = 64; filp->private_data = kmalloc (sizeof (openprom_property) + (j = strlen (p)) + 2 * i, GFP_KERNEL); if (!filp->private_data) return -ENOMEM; op = (openprom_property *)filp->private_data; op->flag = 0; op->alloclen = 2 * i; strcpy (op->name, p); op->value = (char *)(((unsigned long)(op->name + j + 4)) & ~3); op->len = k; if (k && prom_getproperty (node, p, op->value, i) < 0) return -EIO; op->value [k] = 0; if (k) { for (s = NULL, p = op->value; p < op->value + k; p++) { if ((*p >= ' ' && *p <= '~') || *p == '\n') { op->flag |= OPP_STRING; s = p; continue; } if (p > op->value && !*p && s == p - 1) { if (p < op->value + k - 1) op->flag |= OPP_STRINGLIST; else op->flag |= OPP_ASCIIZ; continue; } if (k == 1 && !*p) { op->flag |= (OPP_STRING|OPP_ASCIIZ); break; } op->flag &= ~(OPP_STRING|OPP_STRINGLIST); if (k & 3) op->flag |= OPP_HEXSTRING; else op->flag |= OPP_BINARY; break; } if (op->flag & OPP_STRINGLIST) op->flag &= ~(OPP_STRING); if (op->flag & OPP_ASCIIZ) op->len--; } } else op = (openprom_property *)filp->private_data; if (!count || !(op->len || (op->flag & OPP_ASCIIZ))) return 0; if (*ppos >= 0xffffff || count >= 0xffffff) return -EINVAL; if (op->flag & OPP_STRINGLIST) { for (k = 0, p = op->value; p < op->value + op->len; p++) if (!*p) k++; i = op->len + 4 * k + 3; } else if (op->flag & OPP_STRING) { i = op->len + 3; } else if (op->flag & OPP_BINARY) { i = (op->len * 9) >> 2; } else { i = (op->len << 1) + 1; } k = *ppos; if (k >= i) return 0; if (count > i - k) count = i - k; if (op->flag & OPP_STRING) { if (!k) { if (put_user('\'', buf)) return -EFAULT; k++; count--; } if (k + count >= i - 2) j = i - 2 - k; else j = count; if (j >= 0) { if (copy_to_user(buf + k - *ppos, op->value + k - 1, j)) return -EFAULT; count -= j; k += j; } if (count) { if (put_user('\'', &buf [k++ - *ppos])) return -EFAULT; } if (count > 1) { if (put_user('\n', &buf [k++ - *ppos])) return -EFAULT; } } else if (op->flag & OPP_STRINGLIST) { char *tmp; tmp = kmalloc (i, GFP_KERNEL); if (!tmp) return -ENOMEM; s = tmp; *s++ = '\''; for (p = op->value; p < op->value + op->len; p++) { if (!*p) { strcpy(s, "' + '"); s += 5; continue; } *s++ = *p; } strcpy(s, "'\n"); if (copy_to_user(buf, tmp + k, count)) return -EFAULT; kfree(tmp); k += count; } else if (op->flag & OPP_BINARY) { char buffer[10]; u32 *first, *last; int first_off, last_cnt; first = ((u32 *)op->value) + k / 9; first_off = k % 9; last = ((u32 *)op->value) + (k + count - 1) / 9; last_cnt = (k + count) % 9; if (!last_cnt) last_cnt = 9; if (first == last) { sprintf (buffer, "%08x.", *first); if (copy_to_user(buf, buffer + first_off, last_cnt - first_off)) return -EFAULT; buf += last_cnt - first_off; } else { for (q = first; q <= last; q++) { sprintf (buffer, "%08x.", *q); if (q == first) { if (copy_to_user(buf, buffer + first_off, 9 - first_off)) return -EFAULT; buf += 9 - first_off; } else if (q == last) { if (copy_to_user(buf, buffer, last_cnt)) return -EFAULT; buf += last_cnt; } else { if (copy_to_user(buf, buffer, 9)) return -EFAULT; buf += 9; } } } if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9) { if (put_user('\n', (buf - 1))) return -EFAULT; } k += count; } else if (op->flag & OPP_HEXSTRING) { char buffer[3]; if ((k < i - 1) && (k & 1)) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); if (put_user(buffer[1], &buf[k++ - *ppos])) return -EFAULT; count--; } for (; (count > 1) && (k < i - 1); k += 2) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); if (copy_to_user(buf + k - *ppos, buffer, 2)) return -EFAULT; count -= 2; } if (count && (k < i - 1)) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); if (put_user(buffer[0], &buf[k++ - *ppos])) return -EFAULT; count--; } if (count) { if (put_user('\n', &buf [k++ - *ppos])) return -EFAULT; } } count = k - *ppos; *ppos = k; return count; } static ssize_t property_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int i, j, k; char *p; u32 *q; void *b; openprom_property *op; if (*ppos >= 0xffffff || count >= 0xffffff) return -EINVAL; if (!filp->private_data) { i = property_read (filp, NULL, 0, NULL); if (i) return i; } k = *ppos; op = (openprom_property *)filp->private_data; if (!(op->flag & OPP_STRING)) { u32 *first, *last; int first_off, last_cnt; u32 mask, mask2; char tmp [9]; int forcelen = 0; j = k % 9; for (i = 0; i < count; i++, j++) { if (j == 9) j = 0; if (!j) { char ctmp; if (get_user(ctmp, &buf[i])) return -EFAULT; if (ctmp != '.') { if (ctmp != '\n') { if (op->flag & OPP_BINARY) return -EINVAL; else goto write_try_string; } else { count = i + 1; forcelen = 1; break; } } } else { char ctmp; if (get_user(ctmp, &buf[i])) return -EFAULT; if (ctmp < '0' || (ctmp > '9' && ctmp < 'A') || (ctmp > 'F' && ctmp < 'a') || ctmp > 'f') { if (op->flag & OPP_BINARY) return -EINVAL; else goto write_try_string; } } } op->flag |= OPP_BINARY; tmp [8] = 0; i = ((count + k + 8) / 9) << 2; if (op->alloclen <= i) { b = kmalloc (sizeof (openprom_property) + 2 * i, GFP_KERNEL); if (!b) return -ENOMEM; memcpy (b, filp->private_data, sizeof (openprom_property) + strlen (op->name) + op->alloclen); memset (((char *)b) + sizeof (openprom_property) + strlen (op->name) + op->alloclen, 0, 2 * i - op->alloclen); op = (openprom_property *)b; op->alloclen = 2*i; b = filp->private_data; filp->private_data = (void *)op; kfree (b); } first = ((u32 *)op->value) + (k / 9); first_off = k % 9; last = (u32 *)(op->value + i); last_cnt = (k + count) % 9; if (first + 1 == last) { memset (tmp, '0', 8); if (copy_from_user(tmp + first_off, buf, (count + first_off > 8) ? 8 - first_off : count)) return -EFAULT; mask = 0xffffffff; mask2 = 0xffffffff; for (j = 0; j < first_off; j++) mask >>= 1; for (j = 8 - count - first_off; j > 0; j--) mask2 <<= 1; mask &= mask2; if (mask) { *first &= ~mask; *first |= simple_strtoul (tmp, NULL, 16); op->flag |= OPP_DIRTY; } } else { op->flag |= OPP_DIRTY; for (q = first; q < last; q++) { if (q == first) { if (first_off < 8) { memset (tmp, '0', 8); if (copy_from_user(tmp + first_off, buf, 8 - first_off)) return -EFAULT; mask = 0xffffffff; for (j = 0; j < first_off; j++) mask >>= 1; *q &= ~mask; *q |= simple_strtoul (tmp,NULL,16); } buf += 9; } else if ((q == last - 1) && last_cnt && (last_cnt < 8)) { memset (tmp, '0', 8); if (copy_from_user(tmp, buf, last_cnt)) return -EFAULT; mask = 0xffffffff; for (j = 0; j < 8 - last_cnt; j++) mask <<= 1; *q &= ~mask; *q |= simple_strtoul (tmp, NULL, 16); buf += last_cnt; } else { char tchars[17]; /* XXX yuck... */ if (copy_from_user(tchars, buf, 16)) return -EFAULT; *q = simple_strtoul (tchars, NULL, 16); buf += 9; } } } if (!forcelen) { if (op->len < i) op->len = i; } else op->len = i; *ppos += count; } write_try_string: if (!(op->flag & OPP_BINARY)) { if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) { char ctmp; /* No way, if somebody starts writing from the middle, * we don't know whether he uses quotes around or not */ if (k > 0) return -EINVAL; if (get_user(ctmp, buf)) return -EFAULT; if (ctmp == '\'') { op->flag |= OPP_QUOTED; buf++; count--; (*ppos)++; if (!count) { op->flag |= OPP_STRING; return 1; } } else op->flag |= OPP_NOTQUOTED; } op->flag |= OPP_STRING; if (op->alloclen <= count + *ppos) { b = kmalloc (sizeof (openprom_property) + 2 * (count + *ppos), GFP_KERNEL); if (!b) return -ENOMEM; memcpy (b, filp->private_data, sizeof (openprom_property) + strlen (op->name) + op->alloclen); memset (((char *)b) + sizeof (openprom_property) + strlen (op->name) + op->alloclen, 0, 2*(count - *ppos) - op->alloclen); op = (openprom_property *)b; op->alloclen = 2*(count + *ppos); b = filp->private_data; filp->private_data = (void *)op; kfree (b); } p = op->value + *ppos - ((op->flag & OPP_QUOTED) ? 1 : 0); if (copy_from_user(p, buf, count)) return -EFAULT; op->flag |= OPP_DIRTY; for (i = 0; i < count; i++, p++) if (*p == '\n') { *p = 0; break; } if (i < count) { op->len = p - op->value; *ppos += i + 1; if ((p > op->value) && (op->flag & OPP_QUOTED) && (*(p - 1) == '\'')) op->len--; } else { if (p - op->value > op->len) op->len = p - op->value; *ppos += count; } } return *ppos - k; } int property_release (struct inode *inode, struct file *filp) { openprom_property *op = (openprom_property *)filp->private_data; int error; u32 node; if (!op) return 0; lock_kernel(); node = nodes[(u16)((long)inode->u.generic_ip)].node; if ((u16)((long)inode->u.generic_ip) == aliases) { if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) { char *p = op->name; int i = (op->value - op->name) - strlen (op->name) - 1; op->value [op->len] = 0; *(op->value - 1) = ' '; if (i) { for (p = op->value - i - 2; p >= op->name; p--) p[i] = *p; p = op->name + i; } memcpy (p - 8, "nvalias ", 8); prom_feval (p - 8); } } else if (op->flag & OPP_DIRTY) { if (op->flag & OPP_STRING) { op->value [op->len] = 0; error = prom_setprop (node, op->name, op->value, op->len + 1); if (error <= 0) printk (KERN_WARNING "openpromfs: " "Couldn't write property %s\n", op->name); } else if ((op->flag & OPP_BINARY) || !op->len) { error = prom_setprop (node, op->name, op->value, op->len); if (error <= 0) printk (KERN_WARNING "openpromfs: " "Couldn't write property %s\n", op->name); } else { printk (KERN_WARNING "openpromfs: " "Unknown property type of %s\n", op->name); } } unlock_kernel(); kfree (filp->private_data); return 0; } static const struct file_operations openpromfs_prop_ops = { .read = property_read, .write = property_write, .release = property_release, }; static const struct file_operations openpromfs_nodenum_ops = { .read = nodenum_read, }; static const struct file_operations openprom_operations = { .read = generic_read_dir, .readdir = openpromfs_readdir, }; static struct inode_operations openprom_alias_inode_operations = { .create = openpromfs_create, .lookup = openpromfs_lookup, .unlink = openpromfs_unlink, }; static struct inode_operations openprom_inode_operations = { .lookup = openpromfs_lookup, }; static int lookup_children(u16 n, const char * name, int len) { int ret; u16 node; for (; n != 0xffff; n = nodes[n].next) { node = nodes[n].child; if (node != 0xffff) { char buffer[128]; int i; char *p; while (node != 0xffff) { if (prom_getname (nodes[node].node, buffer, 128) >= 0) { i = strlen (buffer); if ((len == i) && !strncmp (buffer, name, len)) return NODE2INO(node); p = strchr (buffer, '@'); if (p && (len == p - buffer) && !strncmp (buffer, name, len)) return NODE2INO(node); } node = nodes[node].next; } } else continue; ret = lookup_children (nodes[n].child, name, len); if (ret) return ret; } return 0; } static struct dentry *openpromfs_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) { int ino = 0; #define OPFSL_DIR 0 #define OPFSL_PROPERTY 1 #define OPFSL_NODENUM 2 int type = 0; char buffer[128]; char *p; const char *name; u32 n; u16 dirnode; unsigned int len; int i; struct inode *inode; char buffer2[64]; inode = NULL; name = dentry->d_name.name; len = dentry->d_name.len; lock_kernel(); if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) { ino = NODEP2INO(NODE(dir->i_ino).first_prop); type = OPFSL_NODENUM; } if (!ino) { u16 node = NODE(dir->i_ino).child; while (node != 0xffff) { if (prom_getname (nodes[node].node, buffer, 128) >= 0) { i = strlen (buffer); if (len == i && !strncmp (buffer, name, len)) { ino = NODE2INO(node); type = OPFSL_DIR; break; } p = strchr (buffer, '@'); if (p && (len == p - buffer) && !strncmp (buffer, name, len)) { ino = NODE2INO(node); type = OPFSL_DIR; break; } } node = nodes[node].next; } } n = NODE(dir->i_ino).node; dirnode = dir->i_ino - OPENPROM_FIRST_INO; if (!ino) { int j = NODEP2INO(NODE(dir->i_ino).first_prop); if (dirnode != aliases) { for (p = prom_firstprop (n, buffer2); p && *p; p = prom_nextprop (n, p, buffer2)) { j++; if ((len == strlen (p)) && !strncmp (p, name, len)) { ino = j; type = OPFSL_PROPERTY; break; } } } else { int k; for (k = 0; k < aliases_nodes; k++) { j++; if (alias_names [k] && (len == strlen (alias_names [k])) && !strncmp (alias_names [k], name, len)) { ino = j; type = OPFSL_PROPERTY; break; } } } } if (!ino) { ino = lookup_children (NODE(dir->i_ino).child, name, len); if (ino) type = OPFSL_DIR; else { unlock_kernel(); return ERR_PTR(-ENOENT); } } inode = iget (dir->i_sb, ino); unlock_kernel(); if (!inode) return ERR_PTR(-EINVAL); switch (type) { case OPFSL_DIR: inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; if (ino == OPENPROM_FIRST_INO + aliases) { inode->i_mode |= S_IWUSR; inode->i_op = &openprom_alias_inode_operations; } else inode->i_op = &openprom_inode_operations; inode->i_fop = &openprom_operations; inode->i_nlink = 2; break; case OPFSL_NODENUM: inode->i_mode = S_IFREG | S_IRUGO; inode->i_fop = &openpromfs_nodenum_ops; inode->i_nlink = 1; inode->u.generic_ip = (void *)(long)(n); break; case OPFSL_PROPERTY: if ((dirnode == options) && (len == 17) && !strncmp (name, "security-password", 17)) inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; else { inode->i_mode = S_IFREG | S_IRUGO; if (dirnode == options || dirnode == aliases) { if (len != 4 || strncmp (name, "name", 4)) inode->i_mode |= S_IWUSR; } } inode->i_fop = &openpromfs_prop_ops; inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; inode->u.generic_ip = (void *)(long)(((u16)dirnode) | (((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16)); break; } inode->i_gid = 0; inode->i_uid = 0; d_add(dentry, inode); return NULL; } static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; unsigned int ino; u32 n; int i, j; char buffer[128]; u16 node; char *p; char buffer2[64]; lock_kernel(); ino = inode->i_ino; i = filp->f_pos; switch (i) { case 0: if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) goto out; i++; filp->f_pos++; /* fall thru */ case 1: if (filldir(dirent, "..", 2, i, (NODE(ino).parent == 0xffff) ? OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent), DT_DIR) < 0) goto out; i++; filp->f_pos++; /* fall thru */ default: i -= 2; node = NODE(ino).child; while (i && node != 0xffff) { node = nodes[node].next; i--; } while (node != 0xffff) { if (prom_getname (nodes[node].node, buffer, 128) < 0) goto out; if (filldir(dirent, buffer, strlen(buffer), filp->f_pos, NODE2INO(node), DT_DIR) < 0) goto out; filp->f_pos++; node = nodes[node].next; } j = NODEP2INO(NODE(ino).first_prop); if (!i) { if (filldir(dirent, ".node", 5, filp->f_pos, j, DT_REG) < 0) goto out; filp->f_pos++; } else i--; n = NODE(ino).node; if (ino == OPENPROM_FIRST_INO + aliases) { for (j++; i < aliases_nodes; i++, j++) { if (alias_names [i]) { if (filldir (dirent, alias_names [i], strlen (alias_names [i]), filp->f_pos, j, DT_REG) < 0) goto out; filp->f_pos++; } } } else { for (p = prom_firstprop (n, buffer2); p && *p; p = prom_nextprop (n, p, buffer2)) { j++; if (i) i--; else { if (filldir(dirent, p, strlen(p), filp->f_pos, j, DT_REG) < 0) goto out; filp->f_pos++; } } } } out: unlock_kernel(); return 0; } static int openpromfs_create (struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { char *p; struct inode *inode; if (!dir) return -ENOENT; if (dentry->d_name.len > 256) return -EINVAL; p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL); if (!p) return -ENOMEM; strncpy (p, dentry->d_name.name, dentry->d_name.len); p [dentry->d_name.len] = 0; lock_kernel(); if (aliases_nodes == ALIASES_NNODES) { kfree(p); unlock_kernel(); return -EIO; } alias_names [aliases_nodes++] = p; inode = iget (dir->i_sb, NODEP2INO(NODE(dir->i_ino).first_prop) + aliases_nodes); if (!inode) { unlock_kernel(); return -EINVAL; } inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; inode->i_fop = &openpromfs_prop_ops; inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; inode->u.generic_ip = (void *)(long)(((u16)aliases) | (((u16)(aliases_nodes - 1)) << 16)); unlock_kernel(); d_instantiate(dentry, inode); return 0; } static int openpromfs_unlink (struct inode *dir, struct dentry *dentry) { unsigned int len; char *p; const char *name; int i; name = dentry->d_name.name; len = dentry->d_name.len; lock_kernel(); for (i = 0; i < aliases_nodes; i++) if ((strlen (alias_names [i]) == len) && !strncmp (name, alias_names[i], len)) { char buffer[512]; p = alias_names [i]; alias_names [i] = NULL; kfree (p); strcpy (buffer, "nvunalias "); memcpy (buffer + 10, name, len); buffer [10 + len] = 0; prom_feval (buffer); } unlock_kernel(); return 0; } /* {{{ init section */ static int __init check_space (u16 n) { unsigned long pages; if ((1 << alloced) * PAGE_SIZE < (n + 2) * sizeof(openpromfs_node)) { pages = __get_free_pages (GFP_KERNEL, alloced + 1); if (!pages) return -1; if (nodes) { memcpy ((char *)pages, (char *)nodes, (1 << alloced) * PAGE_SIZE); free_pages ((unsigned long)nodes, alloced); } alloced++; nodes = (openpromfs_node *)pages; } return 0; } static u16 __init get_nodes (u16 parent, u32 node) { char *p; u16 n = last_node++, i; char buffer[64]; if (check_space (n) < 0) return 0xffff; nodes[n].parent = parent; nodes[n].node = node; nodes[n].next = 0xffff; nodes[n].child = 0xffff; nodes[n].first_prop = first_prop++; if (!parent) { char buffer[8]; int j; if ((j = prom_getproperty (node, "name", buffer, 8)) >= 0) { buffer[j] = 0; if (!strcmp (buffer, "options")) options = n; else if (!strcmp (buffer, "aliases")) aliases = n; } } if (n != aliases) for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; p = prom_nextprop (node, p, buffer)) first_prop++; else { char *q; for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; p = prom_nextprop (node, p, buffer)) { if (aliases_nodes == ALIASES_NNODES) break; for (i = 0; i < aliases_nodes; i++) if (!strcmp (p, alias_names [i])) break; if (i < aliases_nodes) continue; q = kmalloc (strlen (p) + 1, GFP_KERNEL); if (!q) return 0xffff; strcpy (q, p); alias_names [aliases_nodes++] = q; } first_prop += ALIASES_NNODES; } node = prom_getchild (node); if (node) { parent = get_nodes (n, node); if (parent == 0xffff) return 0xffff; nodes[n].child = parent; while ((node = prom_getsibling (node)) != 0) { i = get_nodes (n, node); if (i == 0xffff) return 0xffff; nodes[parent].next = i; parent = i; } } return n; } static void openprom_read_inode(struct inode * inode) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; if (inode->i_ino == OPENPROM_ROOT_INO) { inode->i_op = &openprom_inode_operations; inode->i_fop = &openprom_operations; inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; } } static int openprom_remount(struct super_block *sb, int *flags, char *data) { *flags |= MS_NOATIME; return 0; } static struct super_operations openprom_sops = { .read_inode = openprom_read_inode, .statfs = simple_statfs, .remount_fs = openprom_remount, }; static int openprom_fill_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; s->s_flags |= MS_NOATIME; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = OPENPROM_SUPER_MAGIC; s->s_op = &openprom_sops; s->s_time_gran = 1; root_inode = iget(s, OPENPROM_ROOT_INO); if (!root_inode) goto out_no_root; s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_no_root; return 0; out_no_root: printk("openprom_fill_super: get root inode failed\n"); iput(root_inode); return -ENOMEM; } static struct super_block *openprom_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_single(fs_type, flags, data, openprom_fill_super); } static struct file_system_type openprom_fs_type = { .owner = THIS_MODULE, .name = "openpromfs", .get_sb = openprom_get_sb, .kill_sb = kill_anon_super, }; static int __init init_openprom_fs(void) { nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0); if (!nodes) { printk (KERN_WARNING "openpromfs: can't get free page\n"); return -EIO; } if (get_nodes (0xffff, prom_root_node) == 0xffff) { printk (KERN_WARNING "openpromfs: couldn't setup tree\n"); return -EIO; } nodes[last_node].first_prop = first_prop; return register_filesystem(&openprom_fs_type); } static void __exit exit_openprom_fs(void) { int i; unregister_filesystem(&openprom_fs_type); free_pages ((unsigned long)nodes, alloced); for (i = 0; i < aliases_nodes; i++) kfree (alias_names [i]); nodes = NULL; } module_init(init_openprom_fs) module_exit(exit_openprom_fs) MODULE_LICENSE("GPL"); linux-2.6.17/fs/openpromfs/Makefile0000644000000000000000000000021110564537530015706 0ustar rootroot# # Makefile for the Linux Sun Openprom filesystem routines. # obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs.o openpromfs-objs := inode.o linux-2.6.17/fs/qnx4/0000755000000000000000000000000010574207637012761 5ustar rootrootlinux-2.6.17/fs/qnx4/dir.c0000644000000000000000000000475010564537530013706 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 28-05-1998 by Richard Frowijn : first release. * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support. */ #include #include #include #include #include #include #include #include static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; unsigned int offset; struct buffer_head *bh; struct qnx4_inode_entry *de; struct qnx4_link_info *le; unsigned long blknum; int ix, ino; int size; QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size)); QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos)); lock_kernel(); while (filp->f_pos < inode->i_size) { blknum = qnx4_block_map( inode, filp->f_pos >> QNX4_BLOCK_SIZE_BITS ); bh = sb_bread(inode->i_sb, blknum); if(bh==NULL) { printk(KERN_ERR "qnx4_readdir: bread failed (%ld)\n", blknum); break; } ix = (int)(filp->f_pos >> QNX4_DIR_ENTRY_SIZE_BITS) % QNX4_INODES_PER_BLOCK; while (ix < QNX4_INODES_PER_BLOCK) { offset = ix * QNX4_DIR_ENTRY_SIZE; de = (struct qnx4_inode_entry *) (bh->b_data + offset); size = strlen(de->di_fname); if (size) { if ( !( de->di_status & QNX4_FILE_LINK ) && size > QNX4_SHORT_NAME_MAX ) size = QNX4_SHORT_NAME_MAX; else if ( size > QNX4_NAME_MAX ) size = QNX4_NAME_MAX; if ( ( de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK) ) != 0 ) { QNX4DEBUG(("qnx4_readdir:%.*s\n", size, de->di_fname)); if ( ( de->di_status & QNX4_FILE_LINK ) == 0 ) ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1; else { le = (struct qnx4_link_info*)de; ino = ( le32_to_cpu(le->dl_inode_blk) - 1 ) * QNX4_INODES_PER_BLOCK + le->dl_inode_ndx; } if (filldir(dirent, de->di_fname, size, filp->f_pos, ino, DT_UNKNOWN) < 0) { brelse(bh); goto out; } } } ix++; filp->f_pos += QNX4_DIR_ENTRY_SIZE; } brelse(bh); } out: unlock_kernel(); return 0; } const struct file_operations qnx4_dir_operations = { .read = generic_read_dir, .readdir = qnx4_readdir, .fsync = file_fsync, }; struct inode_operations qnx4_dir_inode_operations = { .lookup = qnx4_lookup, #ifdef CONFIG_QNX4FS_RW .create = qnx4_create, .unlink = qnx4_unlink, .rmdir = qnx4_rmdir, #endif }; linux-2.6.17/fs/qnx4/truncate.c0000644000000000000000000000136110564537530014750 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.1 * * Using parts of the xiafs filesystem. * * History : * * 30-06-1998 by Frank DENIS : ugly filler. */ #include #include #include #include #include #include #include #ifdef CONFIG_QNX4FS_RW void qnx4_truncate(struct inode *inode) { if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) { return; } lock_kernel(); if (!(S_ISDIR(inode->i_mode))) { /* TODO */ } QNX4DEBUG(("qnx4: qnx4_truncate called\n")); inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); unlock_kernel(); } #endif linux-2.6.17/fs/qnx4/.svn/0000755000000000000000000000000010613366603013636 5ustar rootrootlinux-2.6.17/fs/qnx4/.svn/format0000644000000000000000000000000210574207636015047 0ustar rootroot8 linux-2.6.17/fs/qnx4/.svn/entries0000444000000000000000000000241210613366571015233 0ustar rootroot8 dir 88 svn+ssh://192.168.2.6/home/svn-root/repos/FVT/linux-2.6.17/fs/qnx4 svn+ssh://192.168.2.6/home/svn-root/repos/FVT 2007-02-14T07:50:48.156956Z 1 root svn:special svn:externals svn:needs-lock 4bdaaa2a-274d-416c-a84c-08d2865ac13c Makefile file 2007-02-14T07:50:48.000000Z 183646e785ba26ce98c2823193ff8723 2007-02-14T07:50:48.156956Z 1 root README file 2007-02-14T07:50:48.000000Z f729f0db0dc70a75ebcfa41382c641fa 2007-02-14T07:50:48.156956Z 1 root bitmap.c file 2007-02-14T07:50:48.000000Z d97c8dc3e2c5264d8cf208c8761c0e18 2007-02-14T07:50:48.156956Z 1 root dir.c file 2007-02-14T07:50:48.000000Z b65869cf2e9bb27a305057a3e0b30d77 2007-02-14T07:50:48.156956Z 1 root file.c file 2007-02-14T07:50:48.000000Z ba4370a61773f71cbf3ad919877c74b5 2007-02-14T07:50:48.156956Z 1 root fsync.c file 2007-02-14T07:50:48.000000Z 6269aa48666a31e0e9219f7c45d4141b 2007-02-14T07:50:48.156956Z 1 root inode.c file 2007-02-14T07:50:48.000000Z b098b57a559899f5490dc75dc7ba0b28 2007-02-14T07:50:48.156956Z 1 root namei.c file 2007-02-14T07:50:48.000000Z ac4b9eb8495cf89205ce847b601d1217 2007-02-14T07:50:48.156956Z 1 root truncate.c file 2007-02-14T07:50:48.000000Z eac257663164adb42e9aae7b2ff6b078 2007-02-14T07:50:48.156956Z 1 root linux-2.6.17/fs/qnx4/.svn/tmp/0000755000000000000000000000000010613366571014442 5ustar rootrootlinux-2.6.17/fs/qnx4/.svn/tmp/props/0000755000000000000000000000000010574207636015607 5ustar rootrootlinux-2.6.17/fs/qnx4/.svn/tmp/text-base/0000755000000000000000000000000010574207637016341 5ustar rootrootlinux-2.6.17/fs/qnx4/.svn/tmp/prop-base/0000755000000000000000000000000010574207636016334 5ustar rootrootlinux-2.6.17/fs/qnx4/.svn/props/0000755000000000000000000000000010574207636015007 5ustar rootrootlinux-2.6.17/fs/qnx4/.svn/text-base/0000755000000000000000000000000010574207637015541 5ustar rootrootlinux-2.6.17/fs/qnx4/.svn/text-base/fsync.c.svn-base0000444000000000000000000000601410574207636020542 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.1 * * Using parts of the xiafs filesystem. * * History : * * 24-03-1998 by Richard Frowijn : first release. */ #include #include #include #include #include #include #include #include #include #include /* * The functions for qnx4 fs file synchronization. */ #ifdef CONFIG_QNX4FS_RW static int sync_block(struct inode *inode, unsigned short *block, int wait) { struct buffer_head *bh; unsigned short tmp; if (!*block) return 0; tmp = *block; bh = sb_find_get_block(inode->i_sb, *block); if (!bh) return 0; if (*block != tmp) { brelse(bh); return 1; } if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { brelse(bh); return -1; } if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { brelse(bh); return 0; } ll_rw_block(WRITE, 1, &bh); atomic_dec(&bh->b_count); return 0; } #ifdef WTF static int sync_iblock(struct inode *inode, unsigned short *iblock, struct buffer_head **bh, int wait) { int rc; unsigned short tmp; *bh = NULL; tmp = *iblock; if (!tmp) return 0; rc = sync_block(inode, iblock, wait); if (rc) return rc; *bh = sb_bread(inode->i_sb, tmp); if (tmp != *iblock) { brelse(*bh); *bh = NULL; return 1; } if (!*bh) return -1; return 0; } #endif static int sync_direct(struct inode *inode, int wait) { int i; int rc, err = 0; for (i = 0; i < 7; i++) { rc = sync_block(inode, (unsigned short *) qnx4_raw_inode(inode)->di_first_xtnt.xtnt_blk + i, wait); if (rc > 0) break; if (rc) err = rc; } return err; } #ifdef WTF static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait) { int i; struct buffer_head *ind_bh; int rc, err = 0; rc = sync_iblock(inode, iblock, &ind_bh, wait); if (rc || !ind_bh) return rc; for (i = 0; i < 512; i++) { rc = sync_block(inode, ((unsigned short *) ind_bh->b_data) + i, wait); if (rc > 0) break; if (rc) err = rc; } brelse(ind_bh); return err; } static int sync_dindirect(struct inode *inode, unsigned short *diblock, int wait) { int i; struct buffer_head *dind_bh; int rc, err = 0; rc = sync_iblock(inode, diblock, &dind_bh, wait); if (rc || !dind_bh) return rc; for (i = 0; i < 512; i++) { rc = sync_indirect(inode, ((unsigned short *) dind_bh->b_data) + i, wait); if (rc > 0) break; if (rc) err = rc; } brelse(dind_bh); return err; } #endif int qnx4_sync_file(struct file *file, struct dentry *dentry, int unused) { struct inode *inode = dentry->d_inode; int wait, err = 0; (void) file; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return -EINVAL; lock_kernel(); for (wait = 0; wait <= 1; wait++) { err |= sync_direct(inode, wait); } err |= qnx4_sync_inode(inode); unlock_kernel(); return (err < 0) ? -EIO : 0; } #endif linux-2.6.17/fs/qnx4/.svn/text-base/Makefile.svn-base0000444000000000000000000000024110574207636020710 0ustar rootroot# # Makefile for the linux qnx4-filesystem routines. # obj-$(CONFIG_QNX4FS_FS) += qnx4.o qnx4-objs := inode.o dir.o namei.o file.o bitmap.o truncate.o fsync.o linux-2.6.17/fs/qnx4/.svn/text-base/dir.c.svn-base0000444000000000000000000000475010574207636020203 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 28-05-1998 by Richard Frowijn : first release. * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support. */ #include #include #include #include #include #include #include #include static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; unsigned int offset; struct buffer_head *bh; struct qnx4_inode_entry *de; struct qnx4_link_info *le; unsigned long blknum; int ix, ino; int size; QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size)); QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos)); lock_kernel(); while (filp->f_pos < inode->i_size) { blknum = qnx4_block_map( inode, filp->f_pos >> QNX4_BLOCK_SIZE_BITS ); bh = sb_bread(inode->i_sb, blknum); if(bh==NULL) { printk(KERN_ERR "qnx4_readdir: bread failed (%ld)\n", blknum); break; } ix = (int)(filp->f_pos >> QNX4_DIR_ENTRY_SIZE_BITS) % QNX4_INODES_PER_BLOCK; while (ix < QNX4_INODES_PER_BLOCK) { offset = ix * QNX4_DIR_ENTRY_SIZE; de = (struct qnx4_inode_entry *) (bh->b_data + offset); size = strlen(de->di_fname); if (size) { if ( !( de->di_status & QNX4_FILE_LINK ) && size > QNX4_SHORT_NAME_MAX ) size = QNX4_SHORT_NAME_MAX; else if ( size > QNX4_NAME_MAX ) size = QNX4_NAME_MAX; if ( ( de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK) ) != 0 ) { QNX4DEBUG(("qnx4_readdir:%.*s\n", size, de->di_fname)); if ( ( de->di_status & QNX4_FILE_LINK ) == 0 ) ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1; else { le = (struct qnx4_link_info*)de; ino = ( le32_to_cpu(le->dl_inode_blk) - 1 ) * QNX4_INODES_PER_BLOCK + le->dl_inode_ndx; } if (filldir(dirent, de->di_fname, size, filp->f_pos, ino, DT_UNKNOWN) < 0) { brelse(bh); goto out; } } } ix++; filp->f_pos += QNX4_DIR_ENTRY_SIZE; } brelse(bh); } out: unlock_kernel(); return 0; } const struct file_operations qnx4_dir_operations = { .read = generic_read_dir, .readdir = qnx4_readdir, .fsync = file_fsync, }; struct inode_operations qnx4_dir_inode_operations = { .lookup = qnx4_lookup, #ifdef CONFIG_QNX4FS_RW .create = qnx4_create, .unlink = qnx4_unlink, .rmdir = qnx4_rmdir, #endif }; linux-2.6.17/fs/qnx4/.svn/text-base/bitmap.c.svn-base0000444000000000000000000000717310574207636020703 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 28-05-1998 by Richard Frowijn : first release. * 20-06-1998 by Frank Denis : basic optimisations. * 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap . * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) . */ #include #include #include #include #include #include #include #include #include #if 0 int qnx4_new_block(struct super_block *sb) { return 0; } #endif /* 0 */ static void count_bits(register const char *bmPart, register int size, int *const tf) { char b; int tot = *tf; if (size > QNX4_BLOCK_SIZE) { size = QNX4_BLOCK_SIZE; } do { b = *bmPart++; if ((b & 1) == 0) tot++; if ((b & 2) == 0) tot++; if ((b & 4) == 0) tot++; if ((b & 8) == 0) tot++; if ((b & 16) == 0) tot++; if ((b & 32) == 0) tot++; if ((b & 64) == 0) tot++; if ((b & 128) == 0) tot++; size--; } while (size != 0); *tf = tot; } unsigned long qnx4_count_free_blocks(struct super_block *sb) { int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; int total = 0; int total_free = 0; int offset = 0; int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); struct buffer_head *bh; while (total < size) { if ((bh = sb_bread(sb, start + offset)) == NULL) { printk("qnx4: I/O error in counting free blocks\n"); break; } count_bits(bh->b_data, size - total, &total_free); brelse(bh); total += QNX4_BLOCK_SIZE; offset++; } return total_free; } #ifdef CONFIG_QNX4FS_RW int qnx4_is_free(struct super_block *sb, long block) { int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); struct buffer_head *bh; const char *g; int ret = -EIO; start += block / (QNX4_BLOCK_SIZE * 8); QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n", (unsigned long) block, (unsigned long) start)); (void) size; /* CHECKME */ bh = sb_bread(sb, start); if (bh == NULL) { return -EIO; } g = bh->b_data + (block % QNX4_BLOCK_SIZE); if (((*g) & (1 << (block % 8))) == 0) { QNX4DEBUG(("qnx4: is_free -> block is free\n")); ret = 1; } else { QNX4DEBUG(("qnx4: is_free -> block is busy\n")); ret = 0; } brelse(bh); return ret; } int qnx4_set_bitmap(struct super_block *sb, long block, int busy) { int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); struct buffer_head *bh; char *g; start += block / (QNX4_BLOCK_SIZE * 8); QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n", (unsigned long) block, (unsigned long) start)); (void) size; /* CHECKME */ bh = sb_bread(sb, start); if (bh == NULL) { return -EIO; } g = bh->b_data + (block % QNX4_BLOCK_SIZE); if (busy == 0) { (*g) &= ~(1 << (block % 8)); } else { (*g) |= (1 << (block % 8)); } mark_buffer_dirty(bh); brelse(bh); return 0; } static void qnx4_clear_inode(struct inode *inode) { struct qnx4_inode_entry *qnx4_ino = qnx4_raw_inode(inode); /* What for? */ memset(qnx4_ino->di_fname, 0, sizeof qnx4_ino->di_fname); qnx4_ino->di_size = 0; qnx4_ino->di_num_xtnts = 0; qnx4_ino->di_mode = 0; qnx4_ino->di_status = 0; } void qnx4_free_inode(struct inode *inode) { if (inode->i_ino < 1) { printk("free_inode: inode 0 or nonexistent inode\n"); return; } qnx4_clear_inode(inode); clear_inode(inode); } #endif linux-2.6.17/fs/qnx4/.svn/text-base/README.svn-base0000444000000000000000000000042710574207636020136 0ustar rootroot This is a snapshot of the QNX4 filesystem for Linux. Please send diffs and remarks to . Credits : Richard "Scuba" A. Frowijn Frank "Jedi/Sector One" Denis Anders Larsen (Maintainer) linux-2.6.17/fs/qnx4/.svn/text-base/file.c.svn-base0000444000000000000000000000151610574207636020341 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 25-05-1998 by Richard Frowijn : first release. * 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read. * 27-06-1998 by Frank Denis : file overwriting. */ #include #include /* * We have mostly NULL's here: the current defaults are ok for * the qnx4 filesystem. */ const struct file_operations qnx4_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .mmap = generic_file_mmap, .sendfile = generic_file_sendfile, #ifdef CONFIG_QNX4FS_RW .write = generic_file_write, .fsync = qnx4_sync_file, #endif }; struct inode_operations qnx4_file_inode_operations = { #ifdef CONFIG_QNX4FS_RW .truncate = qnx4_truncate, #endif }; linux-2.6.17/fs/qnx4/.svn/text-base/namei.c.svn-base0000444000000000000000000001325410574207636020515 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 01-06-1998 by Richard Frowijn : first release. * 21-06-1998 by Frank Denis : dcache support, fixed error codes. * 04-07-1998 by Frank Denis : first step for rmdir/unlink. */ #include #include #include #include #include #include #include #include #include #include #include /* * check if the filename is correct. For some obscure reason, qnx writes a * new file twice in the directory entry, first with all possible options at 0 * and for a second time the way it is, they want us not to access the qnx * filesystem when whe are using linux. */ static int qnx4_match(int len, const char *name, struct buffer_head *bh, unsigned long *offset) { struct qnx4_inode_entry *de; int namelen, thislen; if (bh == NULL) { printk("qnx4: matching unassigned buffer !\n"); return 0; } de = (struct qnx4_inode_entry *) (bh->b_data + *offset); *offset += QNX4_DIR_ENTRY_SIZE; if ((de->di_status & QNX4_FILE_LINK) != 0) { namelen = QNX4_NAME_MAX; } else { namelen = QNX4_SHORT_NAME_MAX; } /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) { return 1; } thislen = strlen( de->di_fname ); if ( thislen > namelen ) thislen = namelen; if (len != thislen) { return 0; } if (strncmp(name, de->di_fname, len) == 0) { if ((de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK)) != 0) { return 1; } } return 0; } static struct buffer_head *qnx4_find_entry(int len, struct inode *dir, const char *name, struct qnx4_inode_entry **res_dir, int *ino) { unsigned long block, offset, blkofs; struct buffer_head *bh; *res_dir = NULL; if (!dir->i_sb) { printk("qnx4: no superblock on dir.\n"); return NULL; } bh = NULL; block = offset = blkofs = 0; while (blkofs * QNX4_BLOCK_SIZE + offset < dir->i_size) { if (!bh) { bh = qnx4_bread(dir, blkofs, 0); if (!bh) { blkofs++; continue; } } *res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset); if (qnx4_match(len, name, bh, &offset)) { block = qnx4_block_map( dir, blkofs ); *ino = block * QNX4_INODES_PER_BLOCK + (offset / QNX4_DIR_ENTRY_SIZE) - 1; return bh; } if (offset < bh->b_size) { continue; } brelse(bh); bh = NULL; offset = 0; blkofs++; } brelse(bh); *res_dir = NULL; return NULL; } struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { int ino; struct qnx4_inode_entry *de; struct qnx4_link_info *lnk; struct buffer_head *bh; const char *name = dentry->d_name.name; int len = dentry->d_name.len; struct inode *foundinode = NULL; lock_kernel(); if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) goto out; /* The entry is linked, let's get the real info */ if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) { lnk = (struct qnx4_link_info *) de; ino = (le32_to_cpu(lnk->dl_inode_blk) - 1) * QNX4_INODES_PER_BLOCK + lnk->dl_inode_ndx; } brelse(bh); if ((foundinode = iget(dir->i_sb, ino)) == NULL) { unlock_kernel(); QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); return ERR_PTR(-EACCES); } out: unlock_kernel(); d_add(dentry, foundinode); return NULL; } #ifdef CONFIG_QNX4FS_RW int qnx4_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { QNX4DEBUG(("qnx4: qnx4_create\n")); if (dir == NULL) { return -ENOENT; } return -ENOSPC; } int qnx4_rmdir(struct inode *dir, struct dentry *dentry) { struct buffer_head *bh; struct qnx4_inode_entry *de; struct inode *inode; int retval; int ino; QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name)); lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; if (inode->i_ino != ino) { retval = -EIO; goto end_rmdir; } #if 0 if (!empty_dir(inode)) { retval = -ENOTEMPTY; goto end_rmdir; } #endif if (inode->i_nlink != 2) { QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink)); } QNX4DEBUG(("qnx4: deleting directory\n")); de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; mark_buffer_dirty(bh); inode->i_nlink = 0; mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; dir->i_nlink--; mark_inode_dirty(dir); retval = 0; end_rmdir: brelse(bh); unlock_kernel(); return retval; } int qnx4_unlink(struct inode *dir, struct dentry *dentry) { struct buffer_head *bh; struct qnx4_inode_entry *de; struct inode *inode; int retval; int ino; QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name)); lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; if (inode->i_ino != ino) { retval = -EIO; goto end_unlink; } retval = -EPERM; if (!inode->i_nlink) { QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n", inode->i_sb->s_id, inode->i_ino, inode->i_nlink)); inode->i_nlink = 1; } de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; mark_buffer_dirty(bh); dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; mark_inode_dirty(dir); inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); retval = 0; end_unlink: unlock_kernel(); brelse(bh); return retval; } #endif linux-2.6.17/fs/qnx4/.svn/text-base/inode.c.svn-base0000444000000000000000000003614110574207636020522 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 01-06-1998 by Richard Frowijn : first release. * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc. * 30-06-1998 by Frank Denis : first step to write inodes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define QNX4_VERSION 4 #define QNX4_BMNAME ".bitmap" static struct super_operations qnx4_sops; #ifdef CONFIG_QNX4FS_RW int qnx4_sync_inode(struct inode *inode) { int err = 0; # if 0 struct buffer_head *bh; bh = qnx4_update_inode(inode); if (bh && buffer_dirty(bh)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { printk ("IO error syncing qnx4 inode [%s:%08lx]\n", inode->i_sb->s_id, inode->i_ino); err = -1; } brelse (bh); } else if (!bh) { err = -1; } # endif return err; } static void qnx4_delete_inode(struct inode *inode) { QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); truncate_inode_pages(&inode->i_data, 0); inode->i_size = 0; qnx4_truncate(inode); lock_kernel(); qnx4_free_inode(inode); unlock_kernel(); } static void qnx4_write_super(struct super_block *sb) { lock_kernel(); QNX4DEBUG(("qnx4: write_super\n")); sb->s_dirt = 0; unlock_kernel(); } static int qnx4_write_inode(struct inode *inode, int unused) { struct qnx4_inode_entry *raw_inode; int block, ino; struct buffer_head *bh; ino = inode->i_ino; QNX4DEBUG(("qnx4: write inode 1.\n")); if (inode->i_nlink == 0) { return 0; } if (!ino) { printk("qnx4: bad inode number on dev %s: %d is out of range\n", inode->i_sb->s_id, ino); return -EIO; } QNX4DEBUG(("qnx4: write inode 2.\n")); block = ino / QNX4_INODES_PER_BLOCK; lock_kernel(); if (!(bh = sb_bread(inode->i_sb, block))) { printk("qnx4: major problem: unable to read inode from dev " "%s\n", inode->i_sb->s_id); unlock_kernel(); return -EIO; } raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); raw_inode->di_mode = cpu_to_le16(inode->i_mode); raw_inode->di_uid = cpu_to_le16(fs_high2lowuid(inode->i_uid)); raw_inode->di_gid = cpu_to_le16(fs_high2lowgid(inode->i_gid)); raw_inode->di_nlink = cpu_to_le16(inode->i_nlink); raw_inode->di_size = cpu_to_le32(inode->i_size); raw_inode->di_mtime = cpu_to_le32(inode->i_mtime.tv_sec); raw_inode->di_atime = cpu_to_le32(inode->i_atime.tv_sec); raw_inode->di_ctime = cpu_to_le32(inode->i_ctime.tv_sec); raw_inode->di_first_xtnt.xtnt_size = cpu_to_le32(inode->i_blocks); mark_buffer_dirty(bh); brelse(bh); unlock_kernel(); return 0; } #endif static void qnx4_put_super(struct super_block *sb); static struct inode *qnx4_alloc_inode(struct super_block *sb); static void qnx4_destroy_inode(struct inode *inode); static void qnx4_read_inode(struct inode *); static int qnx4_remount(struct super_block *sb, int *flags, char *data); static int qnx4_statfs(struct super_block *, struct kstatfs *); static struct super_operations qnx4_sops = { .alloc_inode = qnx4_alloc_inode, .destroy_inode = qnx4_destroy_inode, .read_inode = qnx4_read_inode, .put_super = qnx4_put_super, .statfs = qnx4_statfs, .remount_fs = qnx4_remount, #ifdef CONFIG_QNX4FS_RW .write_inode = qnx4_write_inode, .delete_inode = qnx4_delete_inode, .write_super = qnx4_write_super, #endif }; static int qnx4_remount(struct super_block *sb, int *flags, char *data) { struct qnx4_sb_info *qs; qs = qnx4_sb(sb); qs->Version = QNX4_VERSION; #ifndef CONFIG_QNX4FS_RW *flags |= MS_RDONLY; #endif if (*flags & MS_RDONLY) { return 0; } mark_buffer_dirty(qs->sb_buf); return 0; } static struct buffer_head *qnx4_getblk(struct inode *inode, int nr, int create) { struct buffer_head *result = NULL; if ( nr >= 0 ) nr = qnx4_block_map( inode, nr ); if (nr) { result = sb_getblk(inode->i_sb, nr); return result; } if (!create) { return NULL; } #if 0 tmp = qnx4_new_block(inode->i_sb); if (!tmp) { return NULL; } result = sb_getblk(inode->i_sb, tmp); if (tst) { qnx4_free_block(inode->i_sb, tmp); brelse(result); goto repeat; } tst = tmp; #endif inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); return result; } struct buffer_head *qnx4_bread(struct inode *inode, int block, int create) { struct buffer_head *bh; bh = qnx4_getblk(inode, block, create); if (!bh || buffer_uptodate(bh)) { return bh; } ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); if (buffer_uptodate(bh)) { return bh; } brelse(bh); return NULL; } static int qnx4_get_block( struct inode *inode, sector_t iblock, struct buffer_head *bh, int create ) { unsigned long phys; QNX4DEBUG(("qnx4: qnx4_get_block inode=[%ld] iblock=[%ld]\n",inode->i_ino,iblock)); phys = qnx4_block_map( inode, iblock ); if ( phys ) { // logical block is before EOF map_bh(bh, inode->i_sb, phys); } else if ( create ) { // to be done. } return 0; } unsigned long qnx4_block_map( struct inode *inode, long iblock ) { int ix; long offset, i_xblk; unsigned long block = 0; struct buffer_head *bh = NULL; struct qnx4_xblk *xblk = NULL; struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); u16 nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts); if ( iblock < le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size) ) { // iblock is in the first extent. This is easy. block = le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_blk) + iblock - 1; } else { // iblock is beyond first extent. We have to follow the extent chain. i_xblk = le32_to_cpu(qnx4_inode->di_xblk); offset = iblock - le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size); ix = 0; while ( --nxtnt > 0 ) { if ( ix == 0 ) { // read next xtnt block. bh = sb_bread(inode->i_sb, i_xblk - 1); if ( !bh ) { QNX4DEBUG(("qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1)); return -EIO; } xblk = (struct qnx4_xblk*)bh->b_data; if ( memcmp( xblk->xblk_signature, "IamXblk", 7 ) ) { QNX4DEBUG(("qnx4: block at %ld is not a valid xtnt\n", qnx4_inode->i_xblk)); return -EIO; } } if ( offset < le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size) ) { // got it! block = le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_blk) + offset - 1; break; } offset -= le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size); if ( ++ix >= xblk->xblk_num_xtnts ) { i_xblk = le32_to_cpu(xblk->xblk_next_xblk); ix = 0; brelse( bh ); bh = NULL; } } if ( bh ) brelse( bh ); } QNX4DEBUG(("qnx4: mapping block %ld of inode %ld = %ld\n",iblock,inode->i_ino,block)); return block; } static int qnx4_statfs(struct super_block *sb, struct kstatfs *buf) { lock_kernel(); buf->f_type = sb->s_magic; buf->f_bsize = sb->s_blocksize; buf->f_blocks = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size) * 8; buf->f_bfree = qnx4_count_free_blocks(sb); buf->f_bavail = buf->f_bfree; buf->f_namelen = QNX4_NAME_MAX; unlock_kernel(); return 0; } /* * Check the root directory of the filesystem to make sure * it really _is_ a qnx4 filesystem, and to check the size * of the directory entry. */ static const char *qnx4_checkroot(struct super_block *sb) { struct buffer_head *bh; struct qnx4_inode_entry *rootdir; int rd, rl; int i, j; int found = 0; if (*(qnx4_sb(sb)->sb->RootDir.di_fname) != '/') { return "no qnx4 filesystem (no root dir)."; } else { QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", sb->s_id)); rd = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_blk) - 1; rl = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_size); for (j = 0; j < rl; j++) { bh = sb_bread(sb, rd + j); /* root dir, first block */ if (bh == NULL) { return "unable to read root entry."; } for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) { rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE); if (rootdir->di_fname != NULL) { QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname)); if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) { found = 1; qnx4_sb(sb)->BitMap = kmalloc( sizeof( struct qnx4_inode_entry ), GFP_KERNEL ); if (!qnx4_sb(sb)->BitMap) { brelse (bh); return "not enough memory for bitmap inode"; } memcpy( qnx4_sb(sb)->BitMap, rootdir, sizeof( struct qnx4_inode_entry ) ); /* keep bitmap inode known */ break; } } } brelse(bh); if (found != 0) { break; } } if (found == 0) { return "bitmap file not found."; } } return NULL; } static int qnx4_fill_super(struct super_block *s, void *data, int silent) { struct buffer_head *bh; struct inode *root; const char *errmsg; struct qnx4_sb_info *qs; qs = kmalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL); if (!qs) return -ENOMEM; s->s_fs_info = qs; memset(qs, 0, sizeof(struct qnx4_sb_info)); sb_set_blocksize(s, QNX4_BLOCK_SIZE); /* Check the superblock signature. Since the qnx4 code is dangerous, we should leave as quickly as possible if we don't belong here... */ bh = sb_bread(s, 1); if (!bh) { printk("qnx4: unable to read the superblock\n"); goto outnobh; } if ( le32_to_cpup((__le32*) bh->b_data) != QNX4_SUPER_MAGIC ) { if (!silent) printk("qnx4: wrong fsid in superblock.\n"); goto out; } s->s_op = &qnx4_sops; s->s_magic = QNX4_SUPER_MAGIC; #ifndef CONFIG_QNX4FS_RW s->s_flags |= MS_RDONLY; /* Yup, read-only yet */ #endif qnx4_sb(s)->sb_buf = bh; qnx4_sb(s)->sb = (struct qnx4_super_block *) bh->b_data; /* check before allocating dentries, inodes, .. */ errmsg = qnx4_checkroot(s); if (errmsg != NULL) { if (!silent) printk("qnx4: %s\n", errmsg); goto out; } /* does root not have inode number QNX4_ROOT_INO ?? */ root = iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK); if (!root) { printk("qnx4: get inode failed\n"); goto out; } s->s_root = d_alloc_root(root); if (s->s_root == NULL) goto outi; brelse(bh); return 0; outi: iput(root); out: brelse(bh); outnobh: kfree(qs); s->s_fs_info = NULL; return -EINVAL; } static void qnx4_put_super(struct super_block *sb) { struct qnx4_sb_info *qs = qnx4_sb(sb); kfree( qs->BitMap ); kfree( qs ); sb->s_fs_info = NULL; return; } static int qnx4_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page,qnx4_get_block, wbc); } static int qnx4_readpage(struct file *file, struct page *page) { return block_read_full_page(page,qnx4_get_block); } static int qnx4_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host); return cont_prepare_write(page, from, to, qnx4_get_block, &qnx4_inode->mmu_private); } static sector_t qnx4_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,qnx4_get_block); } static struct address_space_operations qnx4_aops = { .readpage = qnx4_readpage, .writepage = qnx4_writepage, .sync_page = block_sync_page, .prepare_write = qnx4_prepare_write, .commit_write = generic_commit_write, .bmap = qnx4_bmap }; static void qnx4_read_inode(struct inode *inode) { struct buffer_head *bh; struct qnx4_inode_entry *raw_inode; int block, ino; struct super_block *sb = inode->i_sb; struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); ino = inode->i_ino; inode->i_mode = 0; QNX4DEBUG(("Reading inode : [%d]\n", ino)); if (!ino) { printk("qnx4: bad inode number on dev %s: %d is out of range\n", sb->s_id, ino); return; } block = ino / QNX4_INODES_PER_BLOCK; if (!(bh = sb_bread(sb, block))) { printk("qnx4: major problem: unable to read inode from dev " "%s\n", sb->s_id); return; } raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); inode->i_mode = le16_to_cpu(raw_inode->di_mode); inode->i_uid = (uid_t)le16_to_cpu(raw_inode->di_uid); inode->i_gid = (gid_t)le16_to_cpu(raw_inode->di_gid); inode->i_nlink = le16_to_cpu(raw_inode->di_nlink); inode->i_size = le32_to_cpu(raw_inode->di_size); inode->i_mtime.tv_sec = le32_to_cpu(raw_inode->di_mtime); inode->i_mtime.tv_nsec = 0; inode->i_atime.tv_sec = le32_to_cpu(raw_inode->di_atime); inode->i_atime.tv_nsec = 0; inode->i_ctime.tv_sec = le32_to_cpu(raw_inode->di_ctime); inode->i_ctime.tv_nsec = 0; inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size); inode->i_blksize = QNX4_DIR_ENTRY_SIZE; memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE); if (S_ISREG(inode->i_mode)) { inode->i_op = &qnx4_file_inode_operations; inode->i_fop = &qnx4_file_operations; inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &qnx4_dir_inode_operations; inode->i_fop = &qnx4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; } else printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id); brelse(bh); } static kmem_cache_t *qnx4_inode_cachep; static struct inode *qnx4_alloc_inode(struct super_block *sb) { struct qnx4_inode_info *ei; ei = kmem_cache_alloc(qnx4_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } static void qnx4_destroy_inode(struct inode *inode) { kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode)); } static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags) { struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) inode_init_once(&ei->vfs_inode); } static int init_inodecache(void) { qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache", sizeof(struct qnx4_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), init_once, NULL); if (qnx4_inode_cachep == NULL) return -ENOMEM; return 0; } static void destroy_inodecache(void) { if (kmem_cache_destroy(qnx4_inode_cachep)) printk(KERN_INFO "qnx4_inode_cache: not all structures were freed\n"); } static struct super_block *qnx4_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_bdev(fs_type, flags, dev_name, data, qnx4_fill_super); } static struct file_system_type qnx4_fs_type = { .owner = THIS_MODULE, .name = "qnx4", .get_sb = qnx4_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static int __init init_qnx4_fs(void) { int err; err = init_inodecache(); if (err) return err; err = register_filesystem(&qnx4_fs_type); if (err) { destroy_inodecache(); return err; } printk("QNX4 filesystem 0.2.3 registered.\n"); return 0; } static void __exit exit_qnx4_fs(void) { unregister_filesystem(&qnx4_fs_type); destroy_inodecache(); } module_init(init_qnx4_fs) module_exit(exit_qnx4_fs) MODULE_LICENSE("GPL"); linux-2.6.17/fs/qnx4/.svn/text-base/truncate.c.svn-base0000444000000000000000000000136110574207636021245 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.1 * * Using parts of the xiafs filesystem. * * History : * * 30-06-1998 by Frank DENIS : ugly filler. */ #include #include #include #include #include #include #include #ifdef CONFIG_QNX4FS_RW void qnx4_truncate(struct inode *inode) { if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) { return; } lock_kernel(); if (!(S_ISDIR(inode->i_mode))) { /* TODO */ } QNX4DEBUG(("qnx4: qnx4_truncate called\n")); inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); unlock_kernel(); } #endif linux-2.6.17/fs/qnx4/.svn/prop-base/0000755000000000000000000000000010574207636015534 5ustar rootrootlinux-2.6.17/fs/qnx4/bitmap.c0000644000000000000000000000717310564537530014406 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 28-05-1998 by Richard Frowijn : first release. * 20-06-1998 by Frank Denis : basic optimisations. * 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap . * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) . */ #include #include #include #include #include #include #include #include #include #if 0 int qnx4_new_block(struct super_block *sb) { return 0; } #endif /* 0 */ static void count_bits(register const char *bmPart, register int size, int *const tf) { char b; int tot = *tf; if (size > QNX4_BLOCK_SIZE) { size = QNX4_BLOCK_SIZE; } do { b = *bmPart++; if ((b & 1) == 0) tot++; if ((b & 2) == 0) tot++; if ((b & 4) == 0) tot++; if ((b & 8) == 0) tot++; if ((b & 16) == 0) tot++; if ((b & 32) == 0) tot++; if ((b & 64) == 0) tot++; if ((b & 128) == 0) tot++; size--; } while (size != 0); *tf = tot; } unsigned long qnx4_count_free_blocks(struct super_block *sb) { int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; int total = 0; int total_free = 0; int offset = 0; int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); struct buffer_head *bh; while (total < size) { if ((bh = sb_bread(sb, start + offset)) == NULL) { printk("qnx4: I/O error in counting free blocks\n"); break; } count_bits(bh->b_data, size - total, &total_free); brelse(bh); total += QNX4_BLOCK_SIZE; offset++; } return total_free; } #ifdef CONFIG_QNX4FS_RW int qnx4_is_free(struct super_block *sb, long block) { int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); struct buffer_head *bh; const char *g; int ret = -EIO; start += block / (QNX4_BLOCK_SIZE * 8); QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n", (unsigned long) block, (unsigned long) start)); (void) size; /* CHECKME */ bh = sb_bread(sb, start); if (bh == NULL) { return -EIO; } g = bh->b_data + (block % QNX4_BLOCK_SIZE); if (((*g) & (1 << (block % 8))) == 0) { QNX4DEBUG(("qnx4: is_free -> block is free\n")); ret = 1; } else { QNX4DEBUG(("qnx4: is_free -> block is busy\n")); ret = 0; } brelse(bh); return ret; } int qnx4_set_bitmap(struct super_block *sb, long block, int busy) { int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); struct buffer_head *bh; char *g; start += block / (QNX4_BLOCK_SIZE * 8); QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n", (unsigned long) block, (unsigned long) start)); (void) size; /* CHECKME */ bh = sb_bread(sb, start); if (bh == NULL) { return -EIO; } g = bh->b_data + (block % QNX4_BLOCK_SIZE); if (busy == 0) { (*g) &= ~(1 << (block % 8)); } else { (*g) |= (1 << (block % 8)); } mark_buffer_dirty(bh); brelse(bh); return 0; } static void qnx4_clear_inode(struct inode *inode) { struct qnx4_inode_entry *qnx4_ino = qnx4_raw_inode(inode); /* What for? */ memset(qnx4_ino->di_fname, 0, sizeof qnx4_ino->di_fname); qnx4_ino->di_size = 0; qnx4_ino->di_num_xtnts = 0; qnx4_ino->di_mode = 0; qnx4_ino->di_status = 0; } void qnx4_free_inode(struct inode *inode) { if (inode->i_ino < 1) { printk("free_inode: inode 0 or nonexistent inode\n"); return; } qnx4_clear_inode(inode); clear_inode(inode); } #endif linux-2.6.17/fs/qnx4/fsync.c0000644000000000000000000000601410564537530014245 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.1 * * Using parts of the xiafs filesystem. * * History : * * 24-03-1998 by Richard Frowijn : first release. */ #include #include #include #include #include #include #include #include #include #include /* * The functions for qnx4 fs file synchronization. */ #ifdef CONFIG_QNX4FS_RW static int sync_block(struct inode *inode, unsigned short *block, int wait) { struct buffer_head *bh; unsigned short tmp; if (!*block) return 0; tmp = *block; bh = sb_find_get_block(inode->i_sb, *block); if (!bh) return 0; if (*block != tmp) { brelse(bh); return 1; } if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { brelse(bh); return -1; } if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { brelse(bh); return 0; } ll_rw_block(WRITE, 1, &bh); atomic_dec(&bh->b_count); return 0; } #ifdef WTF static int sync_iblock(struct inode *inode, unsigned short *iblock, struct buffer_head **bh, int wait) { int rc; unsigned short tmp; *bh = NULL; tmp = *iblock; if (!tmp) return 0; rc = sync_block(inode, iblock, wait); if (rc) return rc; *bh = sb_bread(inode->i_sb, tmp); if (tmp != *iblock) { brelse(*bh); *bh = NULL; return 1; } if (!*bh) return -1; return 0; } #endif static int sync_direct(struct inode *inode, int wait) { int i; int rc, err = 0; for (i = 0; i < 7; i++) { rc = sync_block(inode, (unsigned short *) qnx4_raw_inode(inode)->di_first_xtnt.xtnt_blk + i, wait); if (rc > 0) break; if (rc) err = rc; } return err; } #ifdef WTF static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait) { int i; struct buffer_head *ind_bh; int rc, err = 0; rc = sync_iblock(inode, iblock, &ind_bh, wait); if (rc || !ind_bh) return rc; for (i = 0; i < 512; i++) { rc = sync_block(inode, ((unsigned short *) ind_bh->b_data) + i, wait); if (rc > 0) break; if (rc) err = rc; } brelse(ind_bh); return err; } static int sync_dindirect(struct inode *inode, unsigned short *diblock, int wait) { int i; struct buffer_head *dind_bh; int rc, err = 0; rc = sync_iblock(inode, diblock, &dind_bh, wait); if (rc || !dind_bh) return rc; for (i = 0; i < 512; i++) { rc = sync_indirect(inode, ((unsigned short *) dind_bh->b_data) + i, wait); if (rc > 0) break; if (rc) err = rc; } brelse(dind_bh); return err; } #endif int qnx4_sync_file(struct file *file, struct dentry *dentry, int unused) { struct inode *inode = dentry->d_inode; int wait, err = 0; (void) file; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return -EINVAL; lock_kernel(); for (wait = 0; wait <= 1; wait++) { err |= sync_direct(inode, wait); } err |= qnx4_sync_inode(inode); unlock_kernel(); return (err < 0) ? -EIO : 0; } #endif linux-2.6.17/fs/qnx4/README0000644000000000000000000000042710564537530013641 0ustar rootroot This is a snapshot of the QNX4 filesystem for Linux. Please send diffs and remarks to . Credits : Richard "Scuba" A. Frowijn Frank "Jedi/Sector One" Denis Anders Larsen (Maintainer) linux-2.6.17/fs/qnx4/inode.c0000644000000000000000000003614110564537530014225 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 01-06-1998 by Richard Frowijn : first release. * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc. * 30-06-1998 by Frank Denis : first step to write inodes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define QNX4_VERSION 4 #define QNX4_BMNAME ".bitmap" static struct super_operations qnx4_sops; #ifdef CONFIG_QNX4FS_RW int qnx4_sync_inode(struct inode *inode) { int err = 0; # if 0 struct buffer_head *bh; bh = qnx4_update_inode(inode); if (bh && buffer_dirty(bh)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { printk ("IO error syncing qnx4 inode [%s:%08lx]\n", inode->i_sb->s_id, inode->i_ino); err = -1; } brelse (bh); } else if (!bh) { err = -1; } # endif return err; } static void qnx4_delete_inode(struct inode *inode) { QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); truncate_inode_pages(&inode->i_data, 0); inode->i_size = 0; qnx4_truncate(inode); lock_kernel(); qnx4_free_inode(inode); unlock_kernel(); } static void qnx4_write_super(struct super_block *sb) { lock_kernel(); QNX4DEBUG(("qnx4: write_super\n")); sb->s_dirt = 0; unlock_kernel(); } static int qnx4_write_inode(struct inode *inode, int unused) { struct qnx4_inode_entry *raw_inode; int block, ino; struct buffer_head *bh; ino = inode->i_ino; QNX4DEBUG(("qnx4: write inode 1.\n")); if (inode->i_nlink == 0) { return 0; } if (!ino) { printk("qnx4: bad inode number on dev %s: %d is out of range\n", inode->i_sb->s_id, ino); return -EIO; } QNX4DEBUG(("qnx4: write inode 2.\n")); block = ino / QNX4_INODES_PER_BLOCK; lock_kernel(); if (!(bh = sb_bread(inode->i_sb, block))) { printk("qnx4: major problem: unable to read inode from dev " "%s\n", inode->i_sb->s_id); unlock_kernel(); return -EIO; } raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); raw_inode->di_mode = cpu_to_le16(inode->i_mode); raw_inode->di_uid = cpu_to_le16(fs_high2lowuid(inode->i_uid)); raw_inode->di_gid = cpu_to_le16(fs_high2lowgid(inode->i_gid)); raw_inode->di_nlink = cpu_to_le16(inode->i_nlink); raw_inode->di_size = cpu_to_le32(inode->i_size); raw_inode->di_mtime = cpu_to_le32(inode->i_mtime.tv_sec); raw_inode->di_atime = cpu_to_le32(inode->i_atime.tv_sec); raw_inode->di_ctime = cpu_to_le32(inode->i_ctime.tv_sec); raw_inode->di_first_xtnt.xtnt_size = cpu_to_le32(inode->i_blocks); mark_buffer_dirty(bh); brelse(bh); unlock_kernel(); return 0; } #endif static void qnx4_put_super(struct super_block *sb); static struct inode *qnx4_alloc_inode(struct super_block *sb); static void qnx4_destroy_inode(struct inode *inode); static void qnx4_read_inode(struct inode *); static int qnx4_remount(struct super_block *sb, int *flags, char *data); static int qnx4_statfs(struct super_block *, struct kstatfs *); static struct super_operations qnx4_sops = { .alloc_inode = qnx4_alloc_inode, .destroy_inode = qnx4_destroy_inode, .read_inode = qnx4_read_inode, .put_super = qnx4_put_super, .statfs = qnx4_statfs, .remount_fs = qnx4_remount, #ifdef CONFIG_QNX4FS_RW .write_inode = qnx4_write_inode, .delete_inode = qnx4_delete_inode, .write_super = qnx4_write_super, #endif }; static int qnx4_remount(struct super_block *sb, int *flags, char *data) { struct qnx4_sb_info *qs; qs = qnx4_sb(sb); qs->Version = QNX4_VERSION; #ifndef CONFIG_QNX4FS_RW *flags |= MS_RDONLY; #endif if (*flags & MS_RDONLY) { return 0; } mark_buffer_dirty(qs->sb_buf); return 0; } static struct buffer_head *qnx4_getblk(struct inode *inode, int nr, int create) { struct buffer_head *result = NULL; if ( nr >= 0 ) nr = qnx4_block_map( inode, nr ); if (nr) { result = sb_getblk(inode->i_sb, nr); return result; } if (!create) { return NULL; } #if 0 tmp = qnx4_new_block(inode->i_sb); if (!tmp) { return NULL; } result = sb_getblk(inode->i_sb, tmp); if (tst) { qnx4_free_block(inode->i_sb, tmp); brelse(result); goto repeat; } tst = tmp; #endif inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); return result; } struct buffer_head *qnx4_bread(struct inode *inode, int block, int create) { struct buffer_head *bh; bh = qnx4_getblk(inode, block, create); if (!bh || buffer_uptodate(bh)) { return bh; } ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); if (buffer_uptodate(bh)) { return bh; } brelse(bh); return NULL; } static int qnx4_get_block( struct inode *inode, sector_t iblock, struct buffer_head *bh, int create ) { unsigned long phys; QNX4DEBUG(("qnx4: qnx4_get_block inode=[%ld] iblock=[%ld]\n",inode->i_ino,iblock)); phys = qnx4_block_map( inode, iblock ); if ( phys ) { // logical block is before EOF map_bh(bh, inode->i_sb, phys); } else if ( create ) { // to be done. } return 0; } unsigned long qnx4_block_map( struct inode *inode, long iblock ) { int ix; long offset, i_xblk; unsigned long block = 0; struct buffer_head *bh = NULL; struct qnx4_xblk *xblk = NULL; struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); u16 nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts); if ( iblock < le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size) ) { // iblock is in the first extent. This is easy. block = le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_blk) + iblock - 1; } else { // iblock is beyond first extent. We have to follow the extent chain. i_xblk = le32_to_cpu(qnx4_inode->di_xblk); offset = iblock - le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size); ix = 0; while ( --nxtnt > 0 ) { if ( ix == 0 ) { // read next xtnt block. bh = sb_bread(inode->i_sb, i_xblk - 1); if ( !bh ) { QNX4DEBUG(("qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1)); return -EIO; } xblk = (struct qnx4_xblk*)bh->b_data; if ( memcmp( xblk->xblk_signature, "IamXblk", 7 ) ) { QNX4DEBUG(("qnx4: block at %ld is not a valid xtnt\n", qnx4_inode->i_xblk)); return -EIO; } } if ( offset < le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size) ) { // got it! block = le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_blk) + offset - 1; break; } offset -= le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size); if ( ++ix >= xblk->xblk_num_xtnts ) { i_xblk = le32_to_cpu(xblk->xblk_next_xblk); ix = 0; brelse( bh ); bh = NULL; } } if ( bh ) brelse( bh ); } QNX4DEBUG(("qnx4: mapping block %ld of inode %ld = %ld\n",iblock,inode->i_ino,block)); return block; } static int qnx4_statfs(struct super_block *sb, struct kstatfs *buf) { lock_kernel(); buf->f_type = sb->s_magic; buf->f_bsize = sb->s_blocksize; buf->f_blocks = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size) * 8; buf->f_bfree = qnx4_count_free_blocks(sb); buf->f_bavail = buf->f_bfree; buf->f_namelen = QNX4_NAME_MAX; unlock_kernel(); return 0; } /* * Check the root directory of the filesystem to make sure * it really _is_ a qnx4 filesystem, and to check the size * of the directory entry. */ static const char *qnx4_checkroot(struct super_block *sb) { struct buffer_head *bh; struct qnx4_inode_entry *rootdir; int rd, rl; int i, j; int found = 0; if (*(qnx4_sb(sb)->sb->RootDir.di_fname) != '/') { return "no qnx4 filesystem (no root dir)."; } else { QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", sb->s_id)); rd = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_blk) - 1; rl = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_size); for (j = 0; j < rl; j++) { bh = sb_bread(sb, rd + j); /* root dir, first block */ if (bh == NULL) { return "unable to read root entry."; } for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) { rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE); if (rootdir->di_fname != NULL) { QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname)); if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) { found = 1; qnx4_sb(sb)->BitMap = kmalloc( sizeof( struct qnx4_inode_entry ), GFP_KERNEL ); if (!qnx4_sb(sb)->BitMap) { brelse (bh); return "not enough memory for bitmap inode"; } memcpy( qnx4_sb(sb)->BitMap, rootdir, sizeof( struct qnx4_inode_entry ) ); /* keep bitmap inode known */ break; } } } brelse(bh); if (found != 0) { break; } } if (found == 0) { return "bitmap file not found."; } } return NULL; } static int qnx4_fill_super(struct super_block *s, void *data, int silent) { struct buffer_head *bh; struct inode *root; const char *errmsg; struct qnx4_sb_info *qs; qs = kmalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL); if (!qs) return -ENOMEM; s->s_fs_info = qs; memset(qs, 0, sizeof(struct qnx4_sb_info)); sb_set_blocksize(s, QNX4_BLOCK_SIZE); /* Check the superblock signature. Since the qnx4 code is dangerous, we should leave as quickly as possible if we don't belong here... */ bh = sb_bread(s, 1); if (!bh) { printk("qnx4: unable to read the superblock\n"); goto outnobh; } if ( le32_to_cpup((__le32*) bh->b_data) != QNX4_SUPER_MAGIC ) { if (!silent) printk("qnx4: wrong fsid in superblock.\n"); goto out; } s->s_op = &qnx4_sops; s->s_magic = QNX4_SUPER_MAGIC; #ifndef CONFIG_QNX4FS_RW s->s_flags |= MS_RDONLY; /* Yup, read-only yet */ #endif qnx4_sb(s)->sb_buf = bh; qnx4_sb(s)->sb = (struct qnx4_super_block *) bh->b_data; /* check before allocating dentries, inodes, .. */ errmsg = qnx4_checkroot(s); if (errmsg != NULL) { if (!silent) printk("qnx4: %s\n", errmsg); goto out; } /* does root not have inode number QNX4_ROOT_INO ?? */ root = iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK); if (!root) { printk("qnx4: get inode failed\n"); goto out; } s->s_root = d_alloc_root(root); if (s->s_root == NULL) goto outi; brelse(bh); return 0; outi: iput(root); out: brelse(bh); outnobh: kfree(qs); s->s_fs_info = NULL; return -EINVAL; } static void qnx4_put_super(struct super_block *sb) { struct qnx4_sb_info *qs = qnx4_sb(sb); kfree( qs->BitMap ); kfree( qs ); sb->s_fs_info = NULL; return; } static int qnx4_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page,qnx4_get_block, wbc); } static int qnx4_readpage(struct file *file, struct page *page) { return block_read_full_page(page,qnx4_get_block); } static int qnx4_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host); return cont_prepare_write(page, from, to, qnx4_get_block, &qnx4_inode->mmu_private); } static sector_t qnx4_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,qnx4_get_block); } static struct address_space_operations qnx4_aops = { .readpage = qnx4_readpage, .writepage = qnx4_writepage, .sync_page = block_sync_page, .prepare_write = qnx4_prepare_write, .commit_write = generic_commit_write, .bmap = qnx4_bmap }; static void qnx4_read_inode(struct inode *inode) { struct buffer_head *bh; struct qnx4_inode_entry *raw_inode; int block, ino; struct super_block *sb = inode->i_sb; struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); ino = inode->i_ino; inode->i_mode = 0; QNX4DEBUG(("Reading inode : [%d]\n", ino)); if (!ino) { printk("qnx4: bad inode number on dev %s: %d is out of range\n", sb->s_id, ino); return; } block = ino / QNX4_INODES_PER_BLOCK; if (!(bh = sb_bread(sb, block))) { printk("qnx4: major problem: unable to read inode from dev " "%s\n", sb->s_id); return; } raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); inode->i_mode = le16_to_cpu(raw_inode->di_mode); inode->i_uid = (uid_t)le16_to_cpu(raw_inode->di_uid); inode->i_gid = (gid_t)le16_to_cpu(raw_inode->di_gid); inode->i_nlink = le16_to_cpu(raw_inode->di_nlink); inode->i_size = le32_to_cpu(raw_inode->di_size); inode->i_mtime.tv_sec = le32_to_cpu(raw_inode->di_mtime); inode->i_mtime.tv_nsec = 0; inode->i_atime.tv_sec = le32_to_cpu(raw_inode->di_atime); inode->i_atime.tv_nsec = 0; inode->i_ctime.tv_sec = le32_to_cpu(raw_inode->di_ctime); inode->i_ctime.tv_nsec = 0; inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size); inode->i_blksize = QNX4_DIR_ENTRY_SIZE; memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE); if (S_ISREG(inode->i_mode)) { inode->i_op = &qnx4_file_inode_operations; inode->i_fop = &qnx4_file_operations; inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &qnx4_dir_inode_operations; inode->i_fop = &qnx4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; } else printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id); brelse(bh); } static kmem_cache_t *qnx4_inode_cachep; static struct inode *qnx4_alloc_inode(struct super_block *sb) { struct qnx4_inode_info *ei; ei = kmem_cache_alloc(qnx4_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } static void qnx4_destroy_inode(struct inode *inode) { kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode)); } static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags) { struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) inode_init_once(&ei->vfs_inode); } static int init_inodecache(void) { qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache", sizeof(struct qnx4_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), init_once, NULL); if (qnx4_inode_cachep == NULL) return -ENOMEM; return 0; } static void destroy_inodecache(void) { if (kmem_cache_destroy(qnx4_inode_cachep)) printk(KERN_INFO "qnx4_inode_cache: not all structures were freed\n"); } static struct super_block *qnx4_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_bdev(fs_type, flags, dev_name, data, qnx4_fill_super); } static struct file_system_type qnx4_fs_type = { .owner = THIS_MODULE, .name = "qnx4", .get_sb = qnx4_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static int __init init_qnx4_fs(void) { int err; err = init_inodecache(); if (err) return err; err = register_filesystem(&qnx4_fs_type); if (err) { destroy_inodecache(); return err; } printk("QNX4 filesystem 0.2.3 registered.\n"); return 0; } static void __exit exit_qnx4_fs(void) { unregister_filesystem(&qnx4_fs_type); destroy_inodecache(); } module_init(init_qnx4_fs) module_exit(exit_qnx4_fs) MODULE_LICENSE("GPL"); linux-2.6.17/fs/qnx4/Makefile0000644000000000000000000000024110564537530014413 0ustar rootroot# # Makefile for the linux qnx4-filesystem routines. # obj-$(CONFIG_QNX4FS_FS) += qnx4.o qnx4-objs := inode.o dir.o namei.o file.o bitmap.o truncate.o fsync.o linux-2.6.17/fs/qnx4/file.c0000644000000000000000000000151610564537530014044 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 25-05-1998 by Richard Frowijn : first release. * 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read. * 27-06-1998 by Frank Denis : file overwriting. */ #include #include /* * We have mostly NULL's here: the current defaults are ok for * the qnx4 filesystem. */ const struct file_operations qnx4_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .mmap = generic_file_mmap, .sendfile = generic_file_sendfile, #ifdef CONFIG_QNX4FS_RW .write = generic_file_write, .fsync = qnx4_sync_file, #endif }; struct inode_operations qnx4_file_inode_operations = { #ifdef CONFIG_QNX4FS_RW .truncate = qnx4_truncate, #endif }; linux-2.6.17/fs/qnx4/namei.c0000644000000000000000000001325410564537530014220 0ustar rootroot/* * QNX4 file system, Linux implementation. * * Version : 0.2.1 * * Using parts of the xiafs filesystem. * * History : * * 01-06-1998 by Richard Frowijn : first release. * 21-06-1998 by Frank Denis : dcache support, fixed error codes. * 04-07-1998 by Frank Denis : first step for rmdir/unlink. */ #include #include #include #include #include #include #include #include #include #include #include /* * check if the filename is correct. For some obscure reason, qnx writes a * new file twice in the directory entry, first with all possible options at 0 * and for a second time the way it is, they want us not to access the qnx * filesystem when whe are using linux. */ static int qnx4_match(int len, const char *name, struct buffer_head *bh, unsigned long *offset) { struct qnx4_inode_entry *de; int namelen, thislen; if (bh == NULL) { printk("qnx4: matching unassigned buffer !\n"); return 0; } de = (struct qnx4_inode_entry *) (bh->b_data + *offset); *offset += QNX4_DIR_ENTRY_SIZE; if ((de->di_status & QNX4_FILE_LINK) != 0) { namelen = QNX4_NAME_MAX; } else { namelen = QNX4_SHORT_NAME_MAX; } /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) { return 1; } thislen = strlen( de->di_fname ); if ( thislen > namelen ) thislen = namelen; if (len != thislen) { return 0; } if (strncmp(name, de->di_fname, len) == 0) { if ((de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK)) != 0) { return 1; } } return 0; } static struct buffer_head *qnx4_find_entry(int len, struct inode *dir, const char *name, struct qnx4_inode_entry **res_dir, int *ino) { unsigned long block, offset, blkofs; struct buffer_head *bh; *res_dir = NULL; if (!dir->i_sb) { printk("qnx4: no superblock on dir.\n"); return NULL; } bh = NULL; block = offset = blkofs = 0; while (blkofs * QNX4_BLOCK_SIZE + offset < dir->i_size) { if (!bh) { bh = qnx4_bread(dir, blkofs, 0); if (!bh) { blkofs++; continue; } } *res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset); if (qnx4_match(len, name, bh, &offset)) { block = qnx4_block_map( dir, blkofs ); *ino = block * QNX4_INODES_PER_BLOCK + (offset / QNX4_DIR_ENTRY_SIZE) - 1; return bh; } if (offset < bh->b_size) { continue; } brelse(bh); bh = NULL; offset = 0; blkofs++; } brelse(bh); *res_dir = NULL; return NULL; } struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { int ino; struct qnx4_inode_entry *de; struct qnx4_link_info *lnk; struct buffer_head *bh; const char *name = dentry->d_name.name; int len = dentry->d_name.len; struct inode *foundinode = NULL; lock_kernel(); if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) goto out; /* The entry is linked, let's get the real info */ if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) { lnk = (struct qnx4_link_info *) de; ino = (le32_to_cpu(lnk->dl_inode_blk) - 1) * QNX4_INODES_PER_BLOCK + lnk->dl_inode_ndx; } brelse(bh); if ((foundinode = iget(dir->i_sb, ino)) == NULL) { unlock_kernel(); QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); return ERR_PTR(-EACCES); } out: unlock_kernel(); d_add(dentry, foundinode); return NULL; } #ifdef CONFIG_QNX4FS_RW int qnx4_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { QNX4DEBUG(("qnx4: qnx4_create\n")); if (dir == NULL) { return -ENOENT; } return -ENOSPC; } int qnx4_rmdir(struct inode *dir, struct dentry *dentry) { struct buffer_head *bh; struct qnx4_inode_entry *de; struct inode *inode; int retval; int ino; QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name)); lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; if (inode->i_ino != ino) { retval = -EIO; goto end_rmdir; } #if 0 if (!empty_dir(inode)) { retval = -ENOTEMPTY; goto end_rmdir; } #endif if (inode->i_nlink != 2) { QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink)); } QNX4DEBUG(("qnx4: deleting directory\n")); de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; mark_buffer_dirty(bh); inode->i_nlink = 0; mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; dir->i_nlink--; mark_inode_dirty(dir); retval = 0; end_rmdir: brelse(bh); unlock_kernel(); return retval; } int qnx4_unlink(struct inode *dir, struct dentry *dentry) { struct buffer_head *bh; struct qnx4_inode_entry *de; struct inode *inode; int retval; int ino; QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name)); lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; if (inode->i_ino != ino) { retval = -EIO; goto end_unlink; } retval = -EPERM; if (!inode->i_nlink) { QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n", inode->i_sb->s_id, inode->i_ino, inode->i_nlink)); inode->i_nlink = 1; } de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; mark_buffer_dirty(bh); dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; mark_inode_dirty(dir); inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); retval = 0; end_unlink: unlock_kernel(); brelse(bh); return retval; } #endif linux-2.6.17/fs/befs/0000755000000000000000000000000010574207633013002 5ustar rootrootlinux-2.6.17/fs/befs/super.h0000644000000000000000000000020610564537530014310 0ustar rootroot/* * super.h */ int befs_load_sb(struct super_block *sb, befs_super_block * disk_sb); int befs_check_sb(struct super_block *sb); linux-2.6.17/fs/befs/io.c0000644000000000000000000000340510564537530013560 0ustar rootroot/* * linux/fs/befs/io.c * * Copyright (C) 2001 Will Dyson #include "befs.h" #include "io.h" /* * Converts befs notion of disk addr to a disk offset and uses * linux kernel function sb_bread() to get the buffer containing * the offset. -Will Dyson * */ struct buffer_head * befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr) { struct buffer_head *bh = NULL; befs_blocknr_t block = 0; befs_sb_info *befs_sb = BEFS_SB(sb); befs_debug(sb, "---> Enter befs_read_iaddr() " "[%u, %hu, %hu]", iaddr.allocation_group, iaddr.start, iaddr.len); if (iaddr.allocation_group > befs_sb->num_ags) { befs_error(sb, "BEFS: Invalid allocation group %u, max is %u", iaddr.allocation_group, befs_sb->num_ags); goto error; } block = iaddr2blockno(sb, &iaddr); befs_debug(sb, "befs_read_iaddr: offset = %lu", block); bh = sb_bread(sb, block); if (bh == NULL) { befs_error(sb, "Failed to read block %lu", block); goto error; } befs_debug(sb, "<--- befs_read_iaddr()"); return bh; error: befs_debug(sb, "<--- befs_read_iaddr() ERROR"); return NULL; } struct buffer_head * befs_bread(struct super_block *sb, befs_blocknr_t block) { struct buffer_head *bh = NULL; befs_debug(sb, "---> Enter befs_read() %Lu", block); bh = sb_bread(sb, block); if (bh == NULL) { befs_error(sb, "Failed to read block %lu", block); goto error; } befs_debug(sb, "<--- befs_read()"); return bh; error: befs_debug(sb, "<--- befs_read() ERROR"); return NULL; } linux-2.6.17/fs/befs/datastream.h0000644000000000000000000000100210564537530015272 0ustar rootroot/* * datastream.h * */ struct buffer_head *befs_read_datastream(struct super_block *sb, befs_data_stream * ds, befs_off_t pos, uint * off); int befs_fblock2brun(struct super_block *sb, befs_data_stream * data, befs_blocknr_t fblock, befs_block_run * run); size_t befs_read_lsymlink(struct super_block *sb, befs_data_stream * data, void *buff, befs_off_t len); befs_blocknr_t befs_count_blocks(struct super_block *sb, befs_data_stream * ds); extern const befs_inode_addr BAD_IADDR; linux-2.6.17/fs/befs/btree.h0000644000000000000000000000045410564537530014260 0ustar rootroot/* * btree.h * */ int befs_btree_find(struct super_block *sb, befs_data_stream * ds, const char *key, befs_off_t * value); int befs_btree_read(struct super_block *sb, befs_data_stream * ds, loff_t key_no, size_t bufsize, char *keybuf, size_t * keysize, befs_off_t * value); linux-2.6.17/fs/befs/befs.h0000644000000000000000000000624210564537530014077 0ustar rootroot/* * befs.h * * Copyright (C) 2001-2002 Will Dyson * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp) */ #ifndef _LINUX_BEFS_H #define _LINUX_BEFS_H #include "befs_fs_types.h" /* used in debug.c */ #define BEFS_VERSION "0.9.3" typedef u64 befs_blocknr_t; /* * BeFS in memory structures */ typedef struct befs_mount_options { gid_t gid; uid_t uid; int use_gid; int use_uid; int debug; char *iocharset; } befs_mount_options; typedef struct befs_sb_info { u32 magic1; u32 block_size; u32 block_shift; int byte_order; befs_off_t num_blocks; befs_off_t used_blocks; u32 inode_size; u32 magic2; /* Allocation group information */ u32 blocks_per_ag; u32 ag_shift; u32 num_ags; /* jornal log entry */ befs_block_run log_blocks; befs_off_t log_start; befs_off_t log_end; befs_inode_addr root_dir; befs_inode_addr indices; u32 magic3; befs_mount_options mount_opts; struct nls_table *nls; } befs_sb_info; typedef struct befs_inode_info { u32 i_flags; u32 i_type; befs_inode_addr i_inode_num; befs_inode_addr i_parent; befs_inode_addr i_attribute; union { befs_data_stream ds; char symlink[BEFS_SYMLINK_LEN]; } i_data; struct inode vfs_inode; } befs_inode_info; enum befs_err { BEFS_OK, BEFS_ERR, BEFS_BAD_INODE, BEFS_BT_END, BEFS_BT_EMPTY, BEFS_BT_MATCH, BEFS_BT_PARMATCH, BEFS_BT_NOT_FOUND }; /****************************/ /* debug.c */ void befs_error(const struct super_block *sb, const char *fmt, ...); void befs_warning(const struct super_block *sb, const char *fmt, ...); void befs_debug(const struct super_block *sb, const char *fmt, ...); void befs_dump_super_block(const struct super_block *sb, befs_super_block *); void befs_dump_inode(const struct super_block *sb, befs_inode *); void befs_dump_index_entry(const struct super_block *sb, befs_btree_super *); void befs_dump_index_node(const struct super_block *sb, befs_btree_nodehead *); /****************************/ /* Gets a pointer to the private portion of the super_block * structure from the public part */ static inline befs_sb_info * BEFS_SB(const struct super_block *super) { return (befs_sb_info *) super->s_fs_info; } static inline befs_inode_info * BEFS_I(const struct inode *inode) { return list_entry(inode, struct befs_inode_info, vfs_inode); } static inline befs_blocknr_t iaddr2blockno(struct super_block *sb, befs_inode_addr * iaddr) { return ((iaddr->allocation_group << BEFS_SB(sb)->ag_shift) + iaddr->start); } static inline befs_inode_addr blockno2iaddr(struct super_block *sb, befs_blocknr_t blockno) { befs_inode_addr iaddr; iaddr.allocation_group = blockno >> BEFS_SB(sb)->ag_shift; iaddr.start = blockno - (iaddr.allocation_group << BEFS_SB(sb)->ag_shift); iaddr.len = 1; return iaddr; } static inline unsigned int befs_iaddrs_per_block(struct super_block *sb) { return BEFS_SB(sb)->block_size / sizeof (befs_inode_addr); } static inline int befs_iaddr_is_empty(befs_inode_addr * iaddr) { return (!iaddr->allocation_group) && (!iaddr->start) && (!iaddr->len); } static inline size_t befs_brun_size(struct super_block *sb, befs_block_run run) { return BEFS_SB(sb)->block_size * run.len; } #endif /* _LINUX_BEFS_H */ linux-2.6.17/fs/befs/debug.c0000644000000000000000000001706510564537530014246 0ustar rootroot/* * linux/fs/befs/debug.c * * Copyright (C) 2001 Will Dyson (will_dyson at pobox.com) * * With help from the ntfs-tng driver by Anton Altparmakov * * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp) * * debug functions */ #ifdef __KERNEL__ #include #include #include #include #include #endif /* __KERNEL__ */ #include "befs.h" #include "endian.h" #define ERRBUFSIZE 1024 void befs_error(const struct super_block *sb, const char *fmt, ...) { va_list args; char *err_buf = (char *) kmalloc(ERRBUFSIZE, GFP_KERNEL); if (err_buf == NULL) { printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); return; } va_start(args, fmt); vsnprintf(err_buf, ERRBUFSIZE, fmt, args); va_end(args); printk(KERN_ERR "BeFS(%s): %s\n", sb->s_id, err_buf); kfree(err_buf); } void befs_warning(const struct super_block *sb, const char *fmt, ...) { va_list args; char *err_buf = (char *) kmalloc(ERRBUFSIZE, GFP_KERNEL); if (err_buf == NULL) { printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); return; } va_start(args, fmt); vsnprintf(err_buf, ERRBUFSIZE, fmt, args); va_end(args); printk(KERN_WARNING "BeFS(%s): %s\n", sb->s_id, err_buf); kfree(err_buf); } void befs_debug(const struct super_block *sb, const char *fmt, ...) { #ifdef CONFIG_BEFS_DEBUG va_list args; char *err_buf = NULL; if (BEFS_SB(sb)->mount_opts.debug) { err_buf = (char *) kmalloc(ERRBUFSIZE, GFP_KERNEL); if (err_buf == NULL) { printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); return; } va_start(args, fmt); vsnprintf(err_buf, ERRBUFSIZE, fmt, args); va_end(args); printk(KERN_DEBUG "BeFS(%s): %s\n", sb->s_id, err_buf); kfree(err_buf); } #endif //CONFIG_BEFS_DEBUG } void befs_dump_inode(const struct super_block *sb, befs_inode * inode) { #ifdef CONFIG_BEFS_DEBUG befs_block_run tmp_run; befs_debug(sb, "befs_inode information"); befs_debug(sb, " magic1 %08x", fs32_to_cpu(sb, inode->magic1)); tmp_run = fsrun_to_cpu(sb, inode->inode_num); befs_debug(sb, " inode_num %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " uid %u", fs32_to_cpu(sb, inode->uid)); befs_debug(sb, " gid %u", fs32_to_cpu(sb, inode->gid)); befs_debug(sb, " mode %08x", fs32_to_cpu(sb, inode->mode)); befs_debug(sb, " flags %08x", fs32_to_cpu(sb, inode->flags)); befs_debug(sb, " create_time %Lu", fs64_to_cpu(sb, inode->create_time)); befs_debug(sb, " last_modified_time %Lu", fs64_to_cpu(sb, inode->last_modified_time)); tmp_run = fsrun_to_cpu(sb, inode->parent); befs_debug(sb, " parent [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); tmp_run = fsrun_to_cpu(sb, inode->attributes); befs_debug(sb, " attributes [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " type %08x", fs32_to_cpu(sb, inode->type)); befs_debug(sb, " inode_size %u", fs32_to_cpu(sb, inode->inode_size)); if (S_ISLNK(inode->mode)) { befs_debug(sb, " Symbolic link [%s]", inode->data.symlink); } else { int i; for (i = 0; i < BEFS_NUM_DIRECT_BLOCKS; i++) { tmp_run = fsrun_to_cpu(sb, inode->data.datastream.direct[i]); befs_debug(sb, " direct %d [%u, %hu, %hu]", i, tmp_run.allocation_group, tmp_run.start, tmp_run.len); } befs_debug(sb, " max_direct_range %Lu", fs64_to_cpu(sb, inode->data.datastream. max_direct_range)); tmp_run = fsrun_to_cpu(sb, inode->data.datastream.indirect); befs_debug(sb, " indirect [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " max_indirect_range %Lu", fs64_to_cpu(sb, inode->data.datastream. max_indirect_range)); tmp_run = fsrun_to_cpu(sb, inode->data.datastream.double_indirect); befs_debug(sb, " double indirect [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " max_double_indirect_range %Lu", fs64_to_cpu(sb, inode->data.datastream. max_double_indirect_range)); befs_debug(sb, " size %Lu", fs64_to_cpu(sb, inode->data.datastream.size)); } #endif //CONFIG_BEFS_DEBUG } /* * Display super block structure for debug. */ void befs_dump_super_block(const struct super_block *sb, befs_super_block * sup) { #ifdef CONFIG_BEFS_DEBUG befs_block_run tmp_run; befs_debug(sb, "befs_super_block information"); befs_debug(sb, " name %s", sup->name); befs_debug(sb, " magic1 %08x", fs32_to_cpu(sb, sup->magic1)); befs_debug(sb, " fs_byte_order %08x", fs32_to_cpu(sb, sup->fs_byte_order)); befs_debug(sb, " block_size %u", fs32_to_cpu(sb, sup->block_size)); befs_debug(sb, " block_shift %u", fs32_to_cpu(sb, sup->block_shift)); befs_debug(sb, " num_blocks %Lu", fs64_to_cpu(sb, sup->num_blocks)); befs_debug(sb, " used_blocks %Lu", fs64_to_cpu(sb, sup->used_blocks)); befs_debug(sb, " magic2 %08x", fs32_to_cpu(sb, sup->magic2)); befs_debug(sb, " blocks_per_ag %u", fs32_to_cpu(sb, sup->blocks_per_ag)); befs_debug(sb, " ag_shift %u", fs32_to_cpu(sb, sup->ag_shift)); befs_debug(sb, " num_ags %u", fs32_to_cpu(sb, sup->num_ags)); befs_debug(sb, " flags %08x", fs32_to_cpu(sb, sup->flags)); tmp_run = fsrun_to_cpu(sb, sup->log_blocks); befs_debug(sb, " log_blocks %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " log_start %Ld", fs64_to_cpu(sb, sup->log_start)); befs_debug(sb, " log_end %Ld", fs64_to_cpu(sb, sup->log_end)); befs_debug(sb, " magic3 %08x", fs32_to_cpu(sb, sup->magic3)); tmp_run = fsrun_to_cpu(sb, sup->root_dir); befs_debug(sb, " root_dir %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); tmp_run = fsrun_to_cpu(sb, sup->indices); befs_debug(sb, " indices %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); #endif //CONFIG_BEFS_DEBUG } #if 0 /* unused */ void befs_dump_small_data(const struct super_block *sb, befs_small_data * sd) { } /* unused */ void befs_dump_run(const struct super_block *sb, befs_block_run run) { #ifdef CONFIG_BEFS_DEBUG run = fsrun_to_cpu(sb, run); befs_debug(sb, "[%u, %hu, %hu]", run.allocation_group, run.start, run.len); #endif //CONFIG_BEFS_DEBUG } #endif /* 0 */ void befs_dump_index_entry(const struct super_block *sb, befs_btree_super * super) { #ifdef CONFIG_BEFS_DEBUG befs_debug(sb, "Btree super structure"); befs_debug(sb, " magic %08x", fs32_to_cpu(sb, super->magic)); befs_debug(sb, " node_size %u", fs32_to_cpu(sb, super->node_size)); befs_debug(sb, " max_depth %08x", fs32_to_cpu(sb, super->max_depth)); befs_debug(sb, " data_type %08x", fs32_to_cpu(sb, super->data_type)); befs_debug(sb, " root_node_pointer %016LX", fs64_to_cpu(sb, super->root_node_ptr)); befs_debug(sb, " free_node_pointer %016LX", fs64_to_cpu(sb, super->free_node_ptr)); befs_debug(sb, " maximum size %016LX", fs64_to_cpu(sb, super->max_size)); #endif //CONFIG_BEFS_DEBUG } void befs_dump_index_node(const struct super_block *sb, befs_btree_nodehead * node) { #ifdef CONFIG_BEFS_DEBUG befs_debug(sb, "Btree node structure"); befs_debug(sb, " left %016LX", fs64_to_cpu(sb, node->left)); befs_debug(sb, " right %016LX", fs64_to_cpu(sb, node->right)); befs_debug(sb, " overflow %016LX", fs64_to_cpu(sb, node->overflow)); befs_debug(sb, " all_key_count %hu", fs16_to_cpu(sb, node->all_key_count)); befs_debug(sb, " all_key_length %hu", fs16_to_cpu(sb, node->all_key_length)); #endif //CONFIG_BEFS_DEBUG } linux-2.6.17/fs/befs/TODO0000644000000000000000000000060110564537530013470 0ustar rootrootTODO ========== * Convert comments to the Kernel-Doc format. * Befs_fs.h has gotten big and messy. No reason not to break it up into smaller peices. * See if Alexander Viro's option parser made it into the kernel tree. Use that if we can. (include/linux/parser.h) * See if we really need separate types for on-disk and in-memory representations of the superblock and inode. linux-2.6.17/fs/befs/super.c0000644000000000000000000000604310564537530014310 0ustar rootroot/* * super.c * * Copyright (C) 2001-2002 Will Dyson * * Licensed under the GNU GPL. See the file COPYING for details. * */ #include #include "befs.h" #include "super.h" #include "endian.h" /** * load_befs_sb -- Read from disk and properly byteswap all the fields * of the befs superblock * * * * */ int befs_load_sb(struct super_block *sb, befs_super_block * disk_sb) { befs_sb_info *befs_sb = BEFS_SB(sb); /* Check the byte order of the filesystem */ if (le32_to_cpu(disk_sb->fs_byte_order) == BEFS_BYTEORDER_NATIVE) befs_sb->byte_order = BEFS_BYTESEX_LE; else if (be32_to_cpu(disk_sb->fs_byte_order) == BEFS_BYTEORDER_NATIVE) befs_sb->byte_order = BEFS_BYTESEX_BE; befs_sb->magic1 = fs32_to_cpu(sb, disk_sb->magic1); befs_sb->magic2 = fs32_to_cpu(sb, disk_sb->magic2); befs_sb->magic3 = fs32_to_cpu(sb, disk_sb->magic3); befs_sb->block_size = fs32_to_cpu(sb, disk_sb->block_size); befs_sb->block_shift = fs32_to_cpu(sb, disk_sb->block_shift); befs_sb->num_blocks = fs64_to_cpu(sb, disk_sb->num_blocks); befs_sb->used_blocks = fs64_to_cpu(sb, disk_sb->used_blocks); befs_sb->inode_size = fs32_to_cpu(sb, disk_sb->inode_size); befs_sb->blocks_per_ag = fs32_to_cpu(sb, disk_sb->blocks_per_ag); befs_sb->ag_shift = fs32_to_cpu(sb, disk_sb->ag_shift); befs_sb->num_ags = fs32_to_cpu(sb, disk_sb->num_ags); befs_sb->log_blocks = fsrun_to_cpu(sb, disk_sb->log_blocks); befs_sb->log_start = fs64_to_cpu(sb, disk_sb->log_start); befs_sb->log_end = fs64_to_cpu(sb, disk_sb->log_end); befs_sb->root_dir = fsrun_to_cpu(sb, disk_sb->root_dir); befs_sb->indices = fsrun_to_cpu(sb, disk_sb->indices); befs_sb->nls = NULL; return BEFS_OK; } int befs_check_sb(struct super_block *sb) { befs_sb_info *befs_sb = BEFS_SB(sb); /* Check magic headers of super block */ if ((befs_sb->magic1 != BEFS_SUPER_MAGIC1) || (befs_sb->magic2 != BEFS_SUPER_MAGIC2) || (befs_sb->magic3 != BEFS_SUPER_MAGIC3)) { befs_error(sb, "invalid magic header"); return BEFS_ERR; } /* * Check blocksize of BEFS. * * Blocksize of BEFS is 1024, 2048, 4096 or 8192. */ if ((befs_sb->block_size != 1024) && (befs_sb->block_size != 2048) && (befs_sb->block_size != 4096) && (befs_sb->block_size != 8192)) { befs_error(sb, "invalid blocksize: %u", befs_sb->block_size); return BEFS_ERR; } if (befs_sb->block_size > PAGE_SIZE) { befs_error(sb, "blocksize(%u) cannot be larger" "than system pagesize(%lu)", befs_sb->block_size, PAGE_SIZE); return BEFS_ERR; } /* * block_shift and block_size encode the same information * in different ways as a consistency check. */ if ((1 << befs_sb->block_shift) != befs_sb->block_size) { befs_error(sb, "block_shift disagrees with block_size. " "Corruption likely."); return BEFS_ERR; } if (befs_sb->log_start != befs_sb->log_end) { befs_error(sb, "Filesystem not clean! There are blocks in the " "journal. You must boot into BeOS and mount this volume " "to make it clean."); return BEFS_ERR; } return BEFS_OK; } linux-2.6.17/fs/befs/io.h0000644000000000000000000000027610564537530013570 0ustar rootroot/* * io.h */ struct buffer_head *befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr); struct buffer_head *befs_bread(struct super_block *sb, befs_blocknr_t block); linux-2.6.17/fs/befs/ChangeLog0000644000000000000000000003403210564537530014557 0ustar rootrootVersion 0.92 (2002-03-29) ========== * Minor cleanup. Ran Lindent on the sources. Version 0.92 (2002-03-27) ========== * Fixed module makefile problem. It was not compiling all the correct source files! * Removed duplicated function definition * Fixed potential null pointer dereference when reporting an error Version 0.91 (2002-03-26) ========== * Oy! Fixed stupid bug that would cause an unresolved symbol error. Thanks to Laszlo Boszormenyi for pointing this out to me. Version 0.9 (2002-03-14) ========== * Added Sergey S. Kostyliov's patch to eliminate memcpy() overhead from b+tree operations. Changes the befs_read_datastream() interface. * Segregated the functions that interface directly with the linux vfs interface into their own file called linuxvfs.c. [WD] Version 0.64 (2002-02-07) ========== * Did the string comparision really right this time (btree.c) [WD] * Fixed up some places where I assumed that a long int could hold a pointer value. (btree.c) [WD] * Andrew Farnham pointed out that the module wouldn't work on older (<2.4.10) kernels due to an unresolved symbol. This is bad, since 2.4.9 is still the current RedHat kernel. I added a workaround for this problem (compatibility.h) [WD] * Sergey S. Kostyliov made befs_find_key() use a binary search to find keys within btree nodes, rather than the linear search we were using before. (btree.c) [Sergey S. Kostyliov ] * Made a debian package of the source for use with kernel-package. [WD] Version 0.63 (2002-01-31) ========== * Fixed bug in befs_find_brun_indirect() that would result in the wrong block being read. It was introduced when adding byteswapping in 0.61. (datastream.c) [WD] * Fixed a longstanding bug in befs_find_key() that would result in it finding the first key that is a substring of the string it is searching for. For example, this would cause files in the same directory with names like file1 and file2 to mysteriously be duplicates of each other (because they have the same inode number). Many thanks to Pavel Roskin for reporting this serious bug!!! (btree.c) [WD] * Added support for long symlinks, after Axel Dorfler explained up how they work. I had forgotten all about them. (inode.c, symlink.c) [WD] * Documentation improvements in source. [WD] * Makefile fix for independent module when CONFIG_MODVERSION is set in kernel config [Pavel Roskin ] * Compile warning fix for namei.c. [Sergey S. Kostyliov ] Version 0.62 ========== * Fixed makefile for module install [WD] Version 0.61 (2002-01-20) ========== * Made functions in endian.h to do the correct byteswapping, no matter the arch. [WD] * Abbandoned silly checks for a NULL superblock pointer in debug.c. [WD] * Misc code cleanups. Also cleanup of this changelog file. [WD] * Added byteswapping to all metadata reads from disk. Uses the functions from endian.h [WD] * Remove the typedef of struct super_block to vfs_sb, as it offended certain peoples' aesthetic sense. [WD] * Ditto with the befs_read_block() interface. [WD] Version 0.6 (2001-12-15) ========== * Cleanup of NLS functions (util.c) [WD] * Make directory lookup/read use the NLS if an iocharset is provided. [WD] * Fixed stupid bug where specifying the uid or gid mount options as '0' would result in the filesystem using the on-disk uid and gid. [WD] * Added mount option to control debug printing. The option is, simply enough, 'debug'. (super.c, debug.c) [WD] * Removed notion of btree handle from btree.c. It was unnecessary, as the linux VFS doesn't allow us to keep any state between calls. Updated dir.c, namei.c befs_fs.h to account for it. [WD] * Improved handleing of overflow nodes when listing directories. Now works for overflow nodes hanging off of nodes other than the root node. This is the cleaner solution to Brent Miszalaski's problem. [WD] * Added new debug/warning/error print functions in debug.c. More flexible. Will soon be controllable at mount time (see TODO). [WD] * Rewrote datastream positon lookups. (datastream.c) [WD] * Moved the TODO list to its own file. Version 0.50 (2001-11-13) ========== * Added workaround for mis-understanding of the nature of the b+trees used in directories. A cleaner solution will come after I've thought about it for a while. Thanks to Brent Miszalaski for finding and reporting this bug. (btree.c) [WD] * Minor cleanups * Added test for "impossible" condition of empty internal nodes in seekleaf() in btree.c [WD] * Implemented the abstracted read_block() in io.c [WD] * Cleaned up the inode validation in inode.c [WD] * Anton Altaparmakov figured out (by asking Linus :) ) what was causing the hanging disk io problem. It turns out you need to have the sync_pages callback defined in your address_space_ops, even if it just uses the default linux-supplied implementation. Fixed. Works now. (file.c) [WD] * Anton Altaparmakov and Christoph Hellwig alerted me to the fact that filesystem code should be using GFP_NOFS instead of GFP_KERNEL as the priority parameter to kmalloc(). Fixed. (datastream.c, btree.c super.c inode.c) [WD] * Anton also told me that the blocksize is not allowed to be larger than the page size in linux, which is 4k i386. Oops. Added a test for (blocksize > PAGE_SIZE), and refuse to mount in that case. What this practicaly means is that 8k blocksize volumes won't work without a major restructuring of the driver (or an alpha or other 64bit hardware). [WD] * Cleaned up the befs_count_blocks() function. Much smarter now. And somewhat smaller too. [WD] * Made inode allocations use a slab cache (super.c inode.c) [WD] * Moved the freeing of the private inode section from put_inode() to clear_inode(). This fixes a potential free twice type bug. Put_inode() can be called multiple times for each inode struct. [WD] * Converted all non vfs-callback functions to use befs_sb_info as the superblock type, rather than struct super_block. This is for portablity. [WD] * Fixed a couple of compile warnings due to use of malloc.h, when slab.h is the new way. (inode.c, super.c) [WD] * Fixed erronous includes of linux/befs_fs_i.h and linux/befs_fs_sb.h in inode.c [WD] Version 0.45 (2001-10-29) ========== * Added functions to get the private superblock and inode structures from their enclosing public structures. Switched all references to the private portions to use them. (many files) [WD] * Made read_super and read_inode allocate the private portions of those structures into the generic pointer fields of the public structures with kmalloc(). put_super and put_inode free them. This allows us not to have to touch the definitions of the public structures in include/linux/fs.h. Also, befs_inode_info is huge (becuase of the symlink string). (super.c, inode.c, befs_fs.h) [WD] * Fixed a thinko that was corrupting file reads after the first block_run is done being read. (datastream.c) [WD] * Removed fsync() hooks, since a read-only filesystem doesn't need them. [Christoph Hellwig]. * Fixed befs_readlink() (symlink.c) [Christoph Hellwig]. * Removed all the Read-Write stuff. I'll redo it when it is time to add write support (various files) [WD]. * Removed prototypes for functions who's definitions have been removed (befs_fs.h) [WD]. Version 0.4 (2001-10-28) ========== * Made it an option to use the old non-pagecache befs_file_read() for testing purposes. (fs/Config.in) * Fixed unused variable warnings when compiling without debugging. * Fixed a bug where the inode and super_block didn't get their blockbits fields set (inode.c and super.c). * Release patch version 11. AKA befs-driver version 0.4. * Thats right. New versioning scheme. I've done some serious testing on it now (on my box anyhow), and it seems stable and not outragously slow. Existing features are more-or-less correct (see TODO list). But it isn't 1.0 yet. I think 0.4 gives me some headroom before the big 1.0. 2001-10-26 ========== * Fixed date format in this file. Was I smoking crack? * Removed old datastream code from file.c, since it is nolonger used. * Generic_read_file() is now used to read regular file data. It doesn't chew up the buffer cache (it does page io instead), and seems to be about as fast (even though it has to look up each file block indivdualy). And it knows about doing readahead, which is a major plus. So it does i/o in much larger chunks. It is the correct linux way. It uses befs_get_block() by way of befs_readpage() to find the disk offsets of blocks, which in turn calls befs_fpos2brun() in datastream.c to do the hard work of finding the disk block number. * Changed method of checking for a dirty filesystem in befs_read_super (super.c). Now we check to see if log_start and log_end differ. If so, the journal needs to be replayed, and the filesystem cannot be mounted. * Fixed an extra instance of MOD_DEC_USE_COUNT in super.c * Fixed a problem with reading the superblock on devices with large sector sizes (such as cdroms) on linux 2.4.10 and up. 2001-10-24 ========== * Fix nasty bug in converting block numbers to struct befs_inode_addr. Subtle, because the old version was only sometimes wrong. Probably responsible for lots of problems. (inode.c) * Fix bug with reading an empty directory. (btree.c and dir.c) * This one looks good. Release patch version 10 2001-10-23 ========== * Added btree searching function. * Use befs_btree_find in befs_lookup (namei.c) * Additional comments in btree.c 2001-10-22 ========== * Added B+tree reading functions (in btree.c). Made befs_readdir() use them them instead of the cruft in index.c. 2001-09-11 ========== * Converted befs_read_file() to use the new datastream code. * Finally updated the README file. * Added many comments. * Posted version 6 * Removed byte-order conversion code. I have no intention of supporting it, and it was very ugly. Flow control with #ifdef (ugh). Maybe I'll redo it once native byteorder works 100%. 2001-09-10 ========== * Finished implementing read_datastream() * made befs_read_brun() more general Supports an offset to start at and a max bytes to read Added a wrapper function to give the old call 2001-09-30 ========== * Discovered that the datastream handleing code in file.c is quite deficient in several respects. For one thing, it doesn't deal with indirect blocks * Rewrote datastream handleing. * Created io.c, for io related functions. Previously, the befs_bread() funtions lived in file.c Created the befs_read_brun() function. 2001-09-07 ========== * Made a function to actually count the number of fs blocks used by a file. And helper functions. (fs/befs/inode.c) 2001-09-05 ========== * Fixed a misunderstanding of the inode fields. This fixed the problmem with wrong file sizes from du and others. The i_blocks field of the inode struct is not the number of blocks for the inode, it is the number of blocks for the file. Also, i_blksize is not necessarily the size of the inode, although in practice it works out. Changed to blocksize of filesystem. (fs/befs/inode.c) * Permanently removed code that had been provisionally ifdefed out of befs_fs.h * Since we don't support access time, make that field zero, instead of copying m_time. (fs/befs/inode.c) * Added sanity check for inode reading Make sure inode we got was the one we asked for. (fs/befs/inode.c) * Code cleanup Local pointers to commonly used structures in inode.c. Got rid of abominations befs_iaddr2inode() and befs_inode2ino(). Replaced with single function iaddr2blockno(). (fs/befs/super.c) (fs/befs/inode.c) 2001-09-01 ========== * Fixed the problem with statfs where it would always claim the disk was half full, due to improper understanding of the statfs fields. (fs/befs/super.c) * Posted verion 4 of the patch 2001-09-01 ========== * Changed the macros in befs_fs.h to inline functions. More readable. Typesafe. Better (include/linux/befs_fs.h) * Moved type definitions from befs_fs.h to a new file, befs_fs_types.h Because befs_fs_i.h and befs_fs_sb.h were including befs_fs.h for the typedefs, and they are inlcuded in , which has definitions that I want the inline functions in befs_fs.h to be able to see. Nasty circularity. (include/linux/befs_fs.h) 2001-08-30 ========== * Cleaned up some wording. * Added additional consitency checks on mount Check block_size agrees with block_shift Check flags == BEFS_CLEAN (fs/befs/super.c) * Tell the kernel to only mount befs read-only. By setting the MS_RDONLY flag in befs_read_super(). Not that it was possible to write before. But now the kernel won't even try. (fs/befs/super.c) * Got rid of kernel warning on mount. The kernel doesn't like it if you call set_blocksize() on a device when you have some of its blocks open. Moved the second set_blocksize() to the very end of befs_read_super(), after we are done with the disk superblock. (fs/befs/super.c) * Fixed wrong number of args bug in befs_dump_inode (fs/befs/debug.c) * Solved lots of type mismatches in kprint()s (everwhere) 2001-08-27 ========== * Cleaned up the fs/Config.in entries a bit, now slightly more descriptive. * BeFS depends on NLS, so I made activating BeFS enable the NLS questions (fs/nls/Config.in) * Added Configure.help entries for CONFIG_BEFS_FS and CONFIG_DEBUG_BEFS (Documentation/Configure.help) 2001-08-?? ========== * Removed superblock locking calls in befs_read_super(). In 2.4, the VFS hands us a super_block struct that is already locked. 2001-08-13 ========== * Will Dyson is now attempting to maintain this module Makoto Kato is original author.Daniel Berlin also did some work on it (fixing it up for the later 2.3.x kernels, IIRC). * Fixed compile errors on 2.4.1 kernel (WD) Resolve rejected patches Accomodate changed NLS interface (util.h) Needed to include in most files Makefile changes fs/Config.in changes * Tried to niceify the code using the ext2 fs as a guide Declare befs_fs_type using the DECLARE_FSTYPE_DEV() macro * Made it a configure option to turn on debugging (fs/Config.in) * Compiles on 2.4.7 linux-2.6.17/fs/befs/.svn/0000755000000000000000000000000010613366602013662 5ustar rootrootlinux-2.6.17/fs/befs/.svn/format0000644000000000000000000000000210574207632015070 0ustar rootroot8 linux-2.6.17/fs/befs/.svn/entries0000444000000000000000000000444310613366571015266 0ustar rootroot8 dir 88 svn+ssh://192.168.2.6/home/svn-root/repos/FVT/linux-2.6.17/fs/befs svn+ssh://192.168.2.6/home/svn-root/repos/FVT 2007-02-14T07:50:48.156956Z 1 root svn:special svn:externals svn:needs-lock 4bdaaa2a-274d-416c-a84c-08d2865ac13c ChangeLog file 2007-02-14T07:50:48.000000Z d9ab2e842a1a25093ffd2d8dd3ea8f2a 2007-02-14T07:50:48.156956Z 1 root Makefile file 2007-02-14T07:50:48.000000Z bc9f1f5371a9e0b5a119525989ab71e7 2007-02-14T07:50:48.156956Z 1 root TODO file 2007-02-14T07:50:48.000000Z 0c6eef560e5bc44c396036d3da6825ae 2007-02-14T07:50:48.156956Z 1 root befs.h file 2007-02-14T07:50:48.000000Z 083ef4b3b959753ad50377449dc0c72b 2007-02-14T07:50:48.156956Z 1 root befs_fs_types.h file 2007-02-14T07:50:48.000000Z ecb81b791163d67ab6866f81451c5e11 2007-02-14T07:50:48.156956Z 1 root btree.c file 2007-02-14T07:50:48.000000Z 90378d76f8beb19be399387fc45d7a11 2007-02-14T07:50:48.156956Z 1 root btree.h file 2007-02-14T07:50:48.000000Z 7614752b38fb99716e01f3275ea1abf4 2007-02-14T07:50:48.156956Z 1 root datastream.c file 2007-02-14T07:50:48.000000Z 3535fdc894e89940ce3bc45d173c7a63 2007-02-14T07:50:48.156956Z 1 root datastream.h file 2007-02-14T07:50:48.000000Z 84a14094fba765524b155afbea9d6b85 2007-02-14T07:50:48.156956Z 1 root debug.c file 2007-02-14T07:50:48.000000Z 2686edffc0465e68583f9478597f3e37 2007-02-14T07:50:48.156956Z 1 root endian.h file 2007-02-14T07:50:48.000000Z 4e6e4d32a5207adf9471212b2943c18c 2007-02-14T07:50:48.156956Z 1 root inode.c file 2007-02-14T07:50:48.000000Z 9d63f03c377966032ee3cba220249385 2007-02-14T07:50:48.156956Z 1 root inode.h file 2007-02-14T07:50:48.000000Z ee07f4e04219345b8f65cdf59bb7f1a3 2007-02-14T07:50:48.156956Z 1 root io.c file 2007-02-14T07:50:48.000000Z b5708c72a4a9acf4ff5e053b4fcb7168 2007-02-14T07:50:48.156956Z 1 root io.h file 2007-02-14T07:50:48.000000Z af65731b9e30c0e9908f8e256ed65b6f 2007-02-14T07:50:48.156956Z 1 root linuxvfs.c file 2007-02-14T07:50:48.000000Z 13fca4867571e0bf437855087d624945 2007-02-14T07:50:48.156956Z 1 root super.c file 2007-02-14T07:50:48.000000Z aa7a8f0ea4227c09c7f57deb6404f769 2007-02-14T07:50:48.156956Z 1 root super.h file 2007-02-14T07:50:48.000000Z 3a4b5696150dc69fe3a469a853a13cc6 2007-02-14T07:50:48.156956Z 1 root linux-2.6.17/fs/befs/.svn/tmp/0000755000000000000000000000000010613366571014467 5ustar rootrootlinux-2.6.17/fs/befs/.svn/tmp/props/0000755000000000000000000000000010574207632015630 5ustar rootrootlinux-2.6.17/fs/befs/.svn/tmp/text-base/0000755000000000000000000000000010574207633016362 5ustar rootrootlinux-2.6.17/fs/befs/.svn/tmp/prop-base/0000755000000000000000000000000010574207632016355 5ustar rootrootlinux-2.6.17/fs/befs/.svn/props/0000755000000000000000000000000010574207632015030 5ustar rootrootlinux-2.6.17/fs/befs/.svn/text-base/0000755000000000000000000000000010574207633015562 5ustar rootrootlinux-2.6.17/fs/befs/.svn/text-base/btree.c.svn-base0000444000000000000000000005400010574207632020540 0ustar rootroot/* * linux/fs/befs/btree.c * * Copyright (C) 2001-2002 Will Dyson * * Licensed under the GNU GPL. See the file COPYING for details. * * 2002-02-05: Sergey S. Kostyliov added binary search withing * btree nodes. * * Many thanks to: * * Dominic Giampaolo, author of "Practical File System * Design with the Be File System", for such a helpful book. * * Marcus J. Ranum, author of the b+tree package in * comp.sources.misc volume 10. This code is not copied from that * work, but it is partially based on it. * * Makoto Kato, author of the original BeFS for linux filesystem * driver. */ #include #include #include #include #include #include "befs.h" #include "btree.h" #include "datastream.h" #include "endian.h" /* * The btree functions in this file are built on top of the * datastream.c interface, which is in turn built on top of the * io.c interface. */ /* Befs B+tree structure: * * The first thing in the tree is the tree superblock. It tells you * all kinds of useful things about the tree, like where the rootnode * is located, and the size of the nodes (always 1024 with current version * of BeOS). * * The rest of the tree consists of a series of nodes. Nodes contain a header * (struct befs_btree_nodehead), the packed key data, an array of shorts * containing the ending offsets for each of the keys, and an array of * befs_off_t values. In interior nodes, the keys are the ending keys for * the childnode they point to, and the values are offsets into the * datastream containing the tree. */ /* Note: * * The book states 2 confusing things about befs b+trees. First, * it states that the overflow field of node headers is used by internal nodes * to point to another node that "effectively continues this one". Here is what * I believe that means. Each key in internal nodes points to another node that * contains key values less than itself. Inspection reveals that the last key * in the internal node is not the last key in the index. Keys that are * greater than the last key in the internal node go into the overflow node. * I imagine there is a performance reason for this. * * Second, it states that the header of a btree node is sufficient to * distinguish internal nodes from leaf nodes. Without saying exactly how. * After figuring out the first, it becomes obvious that internal nodes have * overflow nodes and leafnodes do not. */ /* * Currently, this code is only good for directory B+trees. * In order to be used for other BFS indexes, it needs to be extended to handle * duplicate keys and non-string keytypes (int32, int64, float, double). */ /* * In memory structure of each btree node */ typedef struct { befs_btree_nodehead head; /* head of node converted to cpu byteorder */ struct buffer_head *bh; befs_btree_nodehead *od_node; /* on disk node */ } befs_btree_node; /* local constants */ static const befs_off_t befs_bt_inval = 0xffffffffffffffffULL; /* local functions */ static int befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds, befs_btree_super * bt_super, befs_btree_node * this_node, befs_off_t * node_off); static int befs_bt_read_super(struct super_block *sb, befs_data_stream * ds, befs_btree_super * sup); static int befs_bt_read_node(struct super_block *sb, befs_data_stream * ds, befs_btree_node * node, befs_off_t node_off); static int befs_leafnode(befs_btree_node * node); static u16 *befs_bt_keylen_index(befs_btree_node * node); static befs_off_t *befs_bt_valarray(befs_btree_node * node); static char *befs_bt_keydata(befs_btree_node * node); static int befs_find_key(struct super_block *sb, befs_btree_node * node, const char *findkey, befs_off_t * value); static char *befs_bt_get_key(struct super_block *sb, befs_btree_node * node, int index, u16 * keylen); static int befs_compare_strings(const void *key1, int keylen1, const void *key2, int keylen2); /** * befs_bt_read_super - read in btree superblock convert to cpu byteorder * @sb: Filesystem superblock * @ds: Datastream to read from * @sup: Buffer in which to place the btree superblock * * Calls befs_read_datastream to read in the btree superblock and * makes sure it is in cpu byteorder, byteswapping if necessary. * * On success, returns BEFS_OK and *@sup contains the btree superblock, * in cpu byte order. * * On failure, BEFS_ERR is returned. */ static int befs_bt_read_super(struct super_block *sb, befs_data_stream * ds, befs_btree_super * sup) { struct buffer_head *bh = NULL; befs_btree_super *od_sup = NULL; befs_debug(sb, "---> befs_btree_read_super()"); bh = befs_read_datastream(sb, ds, 0, NULL); if (!bh) { befs_error(sb, "Couldn't read index header."); goto error; } od_sup = (befs_btree_super *) bh->b_data; befs_dump_index_entry(sb, od_sup); sup->magic = fs32_to_cpu(sb, od_sup->magic); sup->node_size = fs32_to_cpu(sb, od_sup->node_size); sup->max_depth = fs32_to_cpu(sb, od_sup->max_depth); sup->data_type = fs32_to_cpu(sb, od_sup->data_type); sup->root_node_ptr = fs64_to_cpu(sb, od_sup->root_node_ptr); sup->free_node_ptr = fs64_to_cpu(sb, od_sup->free_node_ptr); sup->max_size = fs64_to_cpu(sb, od_sup->max_size); brelse(bh); if (sup->magic != BEFS_BTREE_MAGIC) { befs_error(sb, "Index header has bad magic."); goto error; } befs_debug(sb, "<--- befs_btree_read_super()"); return BEFS_OK; error: befs_debug(sb, "<--- befs_btree_read_super() ERROR"); return BEFS_ERR; } /** * befs_bt_read_node - read in btree node and convert to cpu byteorder * @sb: Filesystem superblock * @ds: Datastream to read from * @node: Buffer in which to place the btree node * @node_off: Starting offset (in bytes) of the node in @ds * * Calls befs_read_datastream to read in the indicated btree node and * makes sure its header fields are in cpu byteorder, byteswapping if * necessary. * Note: node->bh must be NULL when this function called first * time. Don't forget brelse(node->bh) after last call. * * On success, returns BEFS_OK and *@node contains the btree node that * starts at @node_off, with the node->head fields in cpu byte order. * * On failure, BEFS_ERR is returned. */ static int befs_bt_read_node(struct super_block *sb, befs_data_stream * ds, befs_btree_node * node, befs_off_t node_off) { uint off = 0; befs_debug(sb, "---> befs_bt_read_node()"); if (node->bh) brelse(node->bh); node->bh = befs_read_datastream(sb, ds, node_off, &off); if (!node->bh) { befs_error(sb, "befs_bt_read_node() failed to read " "node at %Lu", node_off); befs_debug(sb, "<--- befs_bt_read_node() ERROR"); return BEFS_ERR; } node->od_node = (befs_btree_nodehead *) ((void *) node->bh->b_data + off); befs_dump_index_node(sb, node->od_node); node->head.left = fs64_to_cpu(sb, node->od_node->left); node->head.right = fs64_to_cpu(sb, node->od_node->right); node->head.overflow = fs64_to_cpu(sb, node->od_node->overflow); node->head.all_key_count = fs16_to_cpu(sb, node->od_node->all_key_count); node->head.all_key_length = fs16_to_cpu(sb, node->od_node->all_key_length); befs_debug(sb, "<--- befs_btree_read_node()"); return BEFS_OK; } /** * befs_btree_find - Find a key in a befs B+tree * @sb: Filesystem superblock * @ds: Datastream containing btree * @key: Key string to lookup in btree * @value: Value stored with @key * * On sucess, returns BEFS_OK and sets *@value to the value stored * with @key (usually the disk block number of an inode). * * On failure, returns BEFS_ERR or BEFS_BT_NOT_FOUND. * * Algorithm: * Read the superblock and rootnode of the b+tree. * Drill down through the interior nodes using befs_find_key(). * Once at the correct leaf node, use befs_find_key() again to get the * actuall value stored with the key. */ int befs_btree_find(struct super_block *sb, befs_data_stream * ds, const char *key, befs_off_t * value) { befs_btree_node *this_node = NULL; befs_btree_super bt_super; befs_off_t node_off; int res; befs_debug(sb, "---> befs_btree_find() Key: %s", key); if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read index superblock"); goto error; } this_node = (befs_btree_node *) kmalloc(sizeof (befs_btree_node), GFP_NOFS); if (!this_node) { befs_error(sb, "befs_btree_find() failed to allocate %u " "bytes of memory", sizeof (befs_btree_node)); goto error; } this_node->bh = NULL; /* read in root node */ node_off = bt_super.root_node_ptr; if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read " "node at %Lu", node_off); goto error_alloc; } while (!befs_leafnode(this_node)) { res = befs_find_key(sb, this_node, key, &node_off); if (res == BEFS_BT_NOT_FOUND) node_off = this_node->head.overflow; /* if no match, go to overflow node */ if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read " "node at %Lu", node_off); goto error_alloc; } } /* at the correct leaf node now */ res = befs_find_key(sb, this_node, key, value); brelse(this_node->bh); kfree(this_node); if (res != BEFS_BT_MATCH) { befs_debug(sb, "<--- befs_btree_find() Key %s not found", key); *value = 0; return BEFS_BT_NOT_FOUND; } befs_debug(sb, "<--- befs_btree_find() Found key %s, value %Lu", key, *value); return BEFS_OK; error_alloc: kfree(this_node); error: *value = 0; befs_debug(sb, "<--- befs_btree_find() ERROR"); return BEFS_ERR; } /** * befs_find_key - Search for a key within a node * @sb: Filesystem superblock * @node: Node to find the key within * @key: Keystring to search for * @value: If key is found, the value stored with the key is put here * * finds exact match if one exists, and returns BEFS_BT_MATCH * If no exact match, finds first key in node that is greater * (alphabetically) than the search key and returns BEFS_BT_PARMATCH * (for partial match, I guess). Can you think of something better to * call it? * * If no key was a match or greater than the search key, return * BEFS_BT_NOT_FOUND. * * Use binary search instead of a linear. */ static int befs_find_key(struct super_block *sb, befs_btree_node * node, const char *findkey, befs_off_t * value) { int first, last, mid; int eq; u16 keylen; int findkey_len; char *thiskey; befs_off_t *valarray; befs_debug(sb, "---> befs_find_key() %s", findkey); *value = 0; findkey_len = strlen(findkey); /* if node can not contain key, just skeep this node */ last = node->head.all_key_count - 1; thiskey = befs_bt_get_key(sb, node, last, &keylen); eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len); if (eq < 0) { befs_debug(sb, "<--- befs_find_key() %s not found", findkey); return BEFS_BT_NOT_FOUND; } valarray = befs_bt_valarray(node); /* simple binary search */ first = 0; mid = 0; while (last >= first) { mid = (last + first) / 2; befs_debug(sb, "first: %d, last: %d, mid: %d", first, last, mid); thiskey = befs_bt_get_key(sb, node, mid, &keylen); eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len); if (eq == 0) { befs_debug(sb, "<--- befs_find_key() found %s at %d", thiskey, mid); *value = fs64_to_cpu(sb, valarray[mid]); return BEFS_BT_MATCH; } if (eq > 0) last = mid - 1; else first = mid + 1; } if (eq < 0) *value = fs64_to_cpu(sb, valarray[mid + 1]); else *value = fs64_to_cpu(sb, valarray[mid]); befs_debug(sb, "<--- befs_find_key() found %s at %d", thiskey, mid); return BEFS_BT_PARMATCH; } /** * befs_btree_read - Traverse leafnodes of a btree * @sb: Filesystem superblock * @ds: Datastream containing btree * @key_no: Key number (alphabetical order) of key to read * @bufsize: Size of the buffer to return key in * @keybuf: Pointer to a buffer to put the key in * @keysize: Length of the returned key * @value: Value stored with the returned key * * Heres how it works: Key_no is the index of the key/value pair to * return in keybuf/value. * Bufsize is the size of keybuf (BEFS_NAME_LEN+1 is a good size). Keysize is * the number of charecters in the key (just a convenience). * * Algorithm: * Get the first leafnode of the tree. See if the requested key is in that * node. If not, follow the node->right link to the next leafnode. Repeat * until the (key_no)th key is found or the tree is out of keys. */ int befs_btree_read(struct super_block *sb, befs_data_stream * ds, loff_t key_no, size_t bufsize, char *keybuf, size_t * keysize, befs_off_t * value) { befs_btree_node *this_node; befs_btree_super bt_super; befs_off_t node_off = 0; int cur_key; befs_off_t *valarray; char *keystart; u16 keylen; int res; uint key_sum = 0; befs_debug(sb, "---> befs_btree_read()"); if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) { befs_error(sb, "befs_btree_read() failed to read index superblock"); goto error; } if ((this_node = (befs_btree_node *) kmalloc(sizeof (befs_btree_node), GFP_NOFS)) == NULL) { befs_error(sb, "befs_btree_read() failed to allocate %u " "bytes of memory", sizeof (befs_btree_node)); goto error; } node_off = bt_super.root_node_ptr; this_node->bh = NULL; /* seeks down to first leafnode, reads it into this_node */ res = befs_btree_seekleaf(sb, ds, &bt_super, this_node, &node_off); if (res == BEFS_BT_EMPTY) { brelse(this_node->bh); kfree(this_node); *value = 0; *keysize = 0; befs_debug(sb, "<--- befs_btree_read() Tree is EMPTY"); return BEFS_BT_EMPTY; } else if (res == BEFS_ERR) { goto error_alloc; } /* find the leaf node containing the key_no key */ while (key_sum + this_node->head.all_key_count <= key_no) { /* no more nodes to look in: key_no is too large */ if (this_node->head.right == befs_bt_inval) { *keysize = 0; *value = 0; befs_debug(sb, "<--- befs_btree_read() END of keys at %Lu", key_sum + this_node->head.all_key_count); brelse(this_node->bh); kfree(this_node); return BEFS_BT_END; } key_sum += this_node->head.all_key_count; node_off = this_node->head.right; if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_read() failed to read " "node at %Lu", node_off); goto error_alloc; } } /* how many keys into this_node is key_no */ cur_key = key_no - key_sum; /* get pointers to datastructures within the node body */ valarray = befs_bt_valarray(this_node); keystart = befs_bt_get_key(sb, this_node, cur_key, &keylen); befs_debug(sb, "Read [%Lu,%d]: keysize %d", node_off, cur_key, keylen); if (bufsize < keylen + 1) { befs_error(sb, "befs_btree_read() keybuf too small (%u) " "for key of size %d", bufsize, keylen); brelse(this_node->bh); goto error_alloc; }; strncpy(keybuf, keystart, keylen); *value = fs64_to_cpu(sb, valarray[cur_key]); *keysize = keylen; keybuf[keylen] = '\0'; befs_debug(sb, "Read [%Lu,%d]: Key \"%.*s\", Value %Lu", node_off, cur_key, keylen, keybuf, *value); brelse(this_node->bh); kfree(this_node); befs_debug(sb, "<--- befs_btree_read()"); return BEFS_OK; error_alloc: kfree(this_node); error: *keysize = 0; *value = 0; befs_debug(sb, "<--- befs_btree_read() ERROR"); return BEFS_ERR; } /** * befs_btree_seekleaf - Find the first leafnode in the btree * @sb: Filesystem superblock * @ds: Datastream containing btree * @bt_super: Pointer to the superblock of the btree * @this_node: Buffer to return the leafnode in * @node_off: Pointer to offset of current node within datastream. Modified * by the function. * * * Helper function for btree traverse. Moves the current position to the * start of the first leaf node. * * Also checks for an empty tree. If there are no keys, returns BEFS_BT_EMPTY. */ static int befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds, befs_btree_super * bt_super, befs_btree_node * this_node, befs_off_t * node_off) { befs_debug(sb, "---> befs_btree_seekleaf()"); if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) { befs_error(sb, "befs_btree_seekleaf() failed to read " "node at %Lu", *node_off); goto error; } befs_debug(sb, "Seekleaf to root node %Lu", *node_off); if (this_node->head.all_key_count == 0 && befs_leafnode(this_node)) { befs_debug(sb, "<--- befs_btree_seekleaf() Tree is EMPTY"); return BEFS_BT_EMPTY; } while (!befs_leafnode(this_node)) { if (this_node->head.all_key_count == 0) { befs_debug(sb, "befs_btree_seekleaf() encountered " "an empty interior node: %Lu. Using Overflow " "node: %Lu", *node_off, this_node->head.overflow); *node_off = this_node->head.overflow; } else { befs_off_t *valarray = befs_bt_valarray(this_node); *node_off = fs64_to_cpu(sb, valarray[0]); } if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) { befs_error(sb, "befs_btree_seekleaf() failed to read " "node at %Lu", *node_off); goto error; } befs_debug(sb, "Seekleaf to child node %Lu", *node_off); } befs_debug(sb, "Node %Lu is a leaf node", *node_off); return BEFS_OK; error: befs_debug(sb, "<--- befs_btree_seekleaf() ERROR"); return BEFS_ERR; } /** * befs_leafnode - Determine if the btree node is a leaf node or an * interior node * @node: Pointer to node structure to test * * Return 1 if leaf, 0 if interior */ static int befs_leafnode(befs_btree_node * node) { /* all interior nodes (and only interior nodes) have an overflow node */ if (node->head.overflow == befs_bt_inval) return 1; else return 0; } /** * befs_bt_keylen_index - Finds start of keylen index in a node * @node: Pointer to the node structure to find the keylen index within * * Returns a pointer to the start of the key length index array * of the B+tree node *@node * * "The length of all the keys in the node is added to the size of the * header and then rounded up to a multiple of four to get the beginning * of the key length index" (p.88, practical filesystem design). * * Except that rounding up to 8 works, and rounding up to 4 doesn't. */ static u16 * befs_bt_keylen_index(befs_btree_node * node) { const int keylen_align = 8; unsigned long int off = (sizeof (befs_btree_nodehead) + node->head.all_key_length); ulong tmp = off % keylen_align; if (tmp) off += keylen_align - tmp; return (u16 *) ((void *) node->od_node + off); } /** * befs_bt_valarray - Finds the start of value array in a node * @node: Pointer to the node structure to find the value array within * * Returns a pointer to the start of the value array * of the node pointed to by the node header */ static befs_off_t * befs_bt_valarray(befs_btree_node * node) { void *keylen_index_start = (void *) befs_bt_keylen_index(node); size_t keylen_index_size = node->head.all_key_count * sizeof (u16); return (befs_off_t *) (keylen_index_start + keylen_index_size); } /** * befs_bt_keydata - Finds start of keydata array in a node * @node: Pointer to the node structure to find the keydata array within * * Returns a pointer to the start of the keydata array * of the node pointed to by the node header */ static char * befs_bt_keydata(befs_btree_node * node) { return (char *) ((void *) node->od_node + sizeof (befs_btree_nodehead)); } /** * befs_bt_get_key - returns a pointer to the start of a key * @sb: filesystem superblock * @node: node in which to look for the key * @index: the index of the key to get * @keylen: modified to be the length of the key at @index * * Returns a valid pointer into @node on success. * Returns NULL on failure (bad input) and sets *@keylen = 0 */ static char * befs_bt_get_key(struct super_block *sb, befs_btree_node * node, int index, u16 * keylen) { int prev_key_end; char *keystart; u16 *keylen_index; if (index < 0 || index > node->head.all_key_count) { *keylen = 0; return NULL; } keystart = befs_bt_keydata(node); keylen_index = befs_bt_keylen_index(node); if (index == 0) prev_key_end = 0; else prev_key_end = fs16_to_cpu(sb, keylen_index[index - 1]); *keylen = fs16_to_cpu(sb, keylen_index[index]) - prev_key_end; return keystart + prev_key_end; } /** * befs_compare_strings - compare two strings * @key1: pointer to the first key to be compared * @keylen1: length in bytes of key1 * @key2: pointer to the second key to be compared * @kelen2: length in bytes of key2 * * Returns 0 if @key1 and @key2 are equal. * Returns >0 if @key1 is greater. * Returns <0 if @key2 is greater.. */ static int befs_compare_strings(const void *key1, int keylen1, const void *key2, int keylen2) { int len = min_t(int, keylen1, keylen2); int result = strncmp(key1, key2, len); if (result == 0) result = keylen1 - keylen2; return result; } /* These will be used for non-string keyed btrees */ #if 0 static int btree_compare_int32(cont void *key1, int keylen1, const void *key2, int keylen2) { return *(int32_t *) key1 - *(int32_t *) key2; } static int btree_compare_uint32(cont void *key1, int keylen1, const void *key2, int keylen2) { if (*(u_int32_t *) key1 == *(u_int32_t *) key2) return 0; else if (*(u_int32_t *) key1 > *(u_int32_t *) key2) return 1; return -1; } static int btree_compare_int64(cont void *key1, int keylen1, const void *key2, int keylen2) { if (*(int64_t *) key1 == *(int64_t *) key2) return 0; else if (*(int64_t *) key1 > *(int64_t *) key2) return 1; return -1; } static int btree_compare_uint64(cont void *key1, int keylen1, const void *key2, int keylen2) { if (*(u_int64_t *) key1 == *(u_int64_t *) key2) return 0; else if (*(u_int64_t *) key1 > *(u_int64_t *) key2) return 1; return -1; } static int btree_compare_float(cont void *key1, int keylen1, const void *key2, int keylen2) { float result = *(float *) key1 - *(float *) key2; if (result == 0.0f) return 0; return (result < 0.0f) ? -1 : 1; } static int btree_compare_double(cont void *key1, int keylen1, const void *key2, int keylen2) { double result = *(double *) key1 - *(double *) key2; if (result == 0.0) return 0; return (result < 0.0) ? -1 : 1; } #endif //0 linux-2.6.17/fs/befs/.svn/text-base/Makefile.svn-base0000444000000000000000000000024410574207632020734 0ustar rootroot# # Makefile for the linux BeOS filesystem routines. # obj-$(CONFIG_BEFS_FS) += befs.o befs-objs := datastream.o btree.o super.o inode.o debug.o io.o linuxvfs.o linux-2.6.17/fs/befs/.svn/text-base/endian.h.svn-base0000444000000000000000000000552610574207632020713 0ustar rootroot/* * linux/fs/befs/endian.h * * Copyright (C) 2001 Will Dyson * * Partially based on similar funtions in the sysv driver. */ #ifndef LINUX_BEFS_ENDIAN #define LINUX_BEFS_ENDIAN #include #include "befs.h" static inline u64 fs64_to_cpu(const struct super_block *sb, u64 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return le64_to_cpu(n); else return be64_to_cpu(n); } static inline u64 cpu_to_fs64(const struct super_block *sb, u64 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return cpu_to_le64(n); else return cpu_to_be64(n); } static inline u32 fs32_to_cpu(const struct super_block *sb, u32 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return le32_to_cpu(n); else return be32_to_cpu(n); } static inline u32 cpu_to_fs32(const struct super_block *sb, u32 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return cpu_to_le32(n); else return cpu_to_be32(n); } static inline u16 fs16_to_cpu(const struct super_block *sb, u16 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return le16_to_cpu(n); else return be16_to_cpu(n); } static inline u16 cpu_to_fs16(const struct super_block *sb, u16 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return cpu_to_le16(n); else return cpu_to_be16(n); } /* Composite types below here */ static inline befs_block_run fsrun_to_cpu(const struct super_block *sb, befs_block_run n) { befs_block_run run; if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) { run.allocation_group = le32_to_cpu(n.allocation_group); run.start = le16_to_cpu(n.start); run.len = le16_to_cpu(n.len); } else { run.allocation_group = be32_to_cpu(n.allocation_group); run.start = be16_to_cpu(n.start); run.len = be16_to_cpu(n.len); } return run; } static inline befs_block_run cpu_to_fsrun(const struct super_block *sb, befs_block_run n) { befs_block_run run; if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) { run.allocation_group = cpu_to_le32(n.allocation_group); run.start = cpu_to_le16(n.start); run.len = cpu_to_le16(n.len); } else { run.allocation_group = cpu_to_be32(n.allocation_group); run.start = cpu_to_be16(n.start); run.len = cpu_to_be16(n.len); } return run; } static inline befs_data_stream fsds_to_cpu(const struct super_block *sb, befs_data_stream n) { befs_data_stream data; int i; for (i = 0; i < BEFS_NUM_DIRECT_BLOCKS; ++i) data.direct[i] = fsrun_to_cpu(sb, n.direct[i]); data.max_direct_range = fs64_to_cpu(sb, n.max_direct_range); data.indirect = fsrun_to_cpu(sb, n.indirect); data.max_indirect_range = fs64_to_cpu(sb, n.max_indirect_range); data.double_indirect = fsrun_to_cpu(sb, n.double_indirect); data.max_double_indirect_range = fs64_to_cpu(sb, n. max_double_indirect_range); data.size = fs64_to_cpu(sb, n.size); return data; } #endif //LINUX_BEFS_ENDIAN linux-2.6.17/fs/befs/.svn/text-base/io.c.svn-base0000444000000000000000000000340510574207632020051 0ustar rootroot/* * linux/fs/befs/io.c * * Copyright (C) 2001 Will Dyson #include "befs.h" #include "io.h" /* * Converts befs notion of disk addr to a disk offset and uses * linux kernel function sb_bread() to get the buffer containing * the offset. -Will Dyson * */ struct buffer_head * befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr) { struct buffer_head *bh = NULL; befs_blocknr_t block = 0; befs_sb_info *befs_sb = BEFS_SB(sb); befs_debug(sb, "---> Enter befs_read_iaddr() " "[%u, %hu, %hu]", iaddr.allocation_group, iaddr.start, iaddr.len); if (iaddr.allocation_group > befs_sb->num_ags) { befs_error(sb, "BEFS: Invalid allocation group %u, max is %u", iaddr.allocation_group, befs_sb->num_ags); goto error; } block = iaddr2blockno(sb, &iaddr); befs_debug(sb, "befs_read_iaddr: offset = %lu", block); bh = sb_bread(sb, block); if (bh == NULL) { befs_error(sb, "Failed to read block %lu", block); goto error; } befs_debug(sb, "<--- befs_read_iaddr()"); return bh; error: befs_debug(sb, "<--- befs_read_iaddr() ERROR"); return NULL; } struct buffer_head * befs_bread(struct super_block *sb, befs_blocknr_t block) { struct buffer_head *bh = NULL; befs_debug(sb, "---> Enter befs_read() %Lu", block); bh = sb_bread(sb, block); if (bh == NULL) { befs_error(sb, "Failed to read block %lu", block); goto error; } befs_debug(sb, "<--- befs_read()"); return bh; error: befs_debug(sb, "<--- befs_read() ERROR"); return NULL; } linux-2.6.17/fs/befs/.svn/text-base/ChangeLog.svn-base0000444000000000000000000003403210574207632021050 0ustar rootrootVersion 0.92 (2002-03-29) ========== * Minor cleanup. Ran Lindent on the sources. Version 0.92 (2002-03-27) ========== * Fixed module makefile problem. It was not compiling all the correct source files! * Removed duplicated function definition * Fixed potential null pointer dereference when reporting an error Version 0.91 (2002-03-26) ========== * Oy! Fixed stupid bug that would cause an unresolved symbol error. Thanks to Laszlo Boszormenyi for pointing this out to me. Version 0.9 (2002-03-14) ========== * Added Sergey S. Kostyliov's patch to eliminate memcpy() overhead from b+tree operations. Changes the befs_read_datastream() interface. * Segregated the functions that interface directly with the linux vfs interface into their own file called linuxvfs.c. [WD] Version 0.64 (2002-02-07) ========== * Did the string comparision really right this time (btree.c) [WD] * Fixed up some places where I assumed that a long int could hold a pointer value. (btree.c) [WD] * Andrew Farnham pointed out that the module wouldn't work on older (<2.4.10) kernels due to an unresolved symbol. This is bad, since 2.4.9 is still the current RedHat kernel. I added a workaround for this problem (compatibility.h) [WD] * Sergey S. Kostyliov made befs_find_key() use a binary search to find keys within btree nodes, rather than the linear search we were using before. (btree.c) [Sergey S. Kostyliov ] * Made a debian package of the source for use with kernel-package. [WD] Version 0.63 (2002-01-31) ========== * Fixed bug in befs_find_brun_indirect() that would result in the wrong block being read. It was introduced when adding byteswapping in 0.61. (datastream.c) [WD] * Fixed a longstanding bug in befs_find_key() that would result in it finding the first key that is a substring of the string it is searching for. For example, this would cause files in the same directory with names like file1 and file2 to mysteriously be duplicates of each other (because they have the same inode number). Many thanks to Pavel Roskin for reporting this serious bug!!! (btree.c) [WD] * Added support for long symlinks, after Axel Dorfler explained up how they work. I had forgotten all about them. (inode.c, symlink.c) [WD] * Documentation improvements in source. [WD] * Makefile fix for independent module when CONFIG_MODVERSION is set in kernel config [Pavel Roskin ] * Compile warning fix for namei.c. [Sergey S. Kostyliov ] Version 0.62 ========== * Fixed makefile for module install [WD] Version 0.61 (2002-01-20) ========== * Made functions in endian.h to do the correct byteswapping, no matter the arch. [WD] * Abbandoned silly checks for a NULL superblock pointer in debug.c. [WD] * Misc code cleanups. Also cleanup of this changelog file. [WD] * Added byteswapping to all metadata reads from disk. Uses the functions from endian.h [WD] * Remove the typedef of struct super_block to vfs_sb, as it offended certain peoples' aesthetic sense. [WD] * Ditto with the befs_read_block() interface. [WD] Version 0.6 (2001-12-15) ========== * Cleanup of NLS functions (util.c) [WD] * Make directory lookup/read use the NLS if an iocharset is provided. [WD] * Fixed stupid bug where specifying the uid or gid mount options as '0' would result in the filesystem using the on-disk uid and gid. [WD] * Added mount option to control debug printing. The option is, simply enough, 'debug'. (super.c, debug.c) [WD] * Removed notion of btree handle from btree.c. It was unnecessary, as the linux VFS doesn't allow us to keep any state between calls. Updated dir.c, namei.c befs_fs.h to account for it. [WD] * Improved handleing of overflow nodes when listing directories. Now works for overflow nodes hanging off of nodes other than the root node. This is the cleaner solution to Brent Miszalaski's problem. [WD] * Added new debug/warning/error print functions in debug.c. More flexible. Will soon be controllable at mount time (see TODO). [WD] * Rewrote datastream positon lookups. (datastream.c) [WD] * Moved the TODO list to its own file. Version 0.50 (2001-11-13) ========== * Added workaround for mis-understanding of the nature of the b+trees used in directories. A cleaner solution will come after I've thought about it for a while. Thanks to Brent Miszalaski for finding and reporting this bug. (btree.c) [WD] * Minor cleanups * Added test for "impossible" condition of empty internal nodes in seekleaf() in btree.c [WD] * Implemented the abstracted read_block() in io.c [WD] * Cleaned up the inode validation in inode.c [WD] * Anton Altaparmakov figured out (by asking Linus :) ) what was causing the hanging disk io problem. It turns out you need to have the sync_pages callback defined in your address_space_ops, even if it just uses the default linux-supplied implementation. Fixed. Works now. (file.c) [WD] * Anton Altaparmakov and Christoph Hellwig alerted me to the fact that filesystem code should be using GFP_NOFS instead of GFP_KERNEL as the priority parameter to kmalloc(). Fixed. (datastream.c, btree.c super.c inode.c) [WD] * Anton also told me that the blocksize is not allowed to be larger than the page size in linux, which is 4k i386. Oops. Added a test for (blocksize > PAGE_SIZE), and refuse to mount in that case. What this practicaly means is that 8k blocksize volumes won't work without a major restructuring of the driver (or an alpha or other 64bit hardware). [WD] * Cleaned up the befs_count_blocks() function. Much smarter now. And somewhat smaller too. [WD] * Made inode allocations use a slab cache (super.c inode.c) [WD] * Moved the freeing of the private inode section from put_inode() to clear_inode(). This fixes a potential free twice type bug. Put_inode() can be called multiple times for each inode struct. [WD] * Converted all non vfs-callback functions to use befs_sb_info as the superblock type, rather than struct super_block. This is for portablity. [WD] * Fixed a couple of compile warnings due to use of malloc.h, when slab.h is the new way. (inode.c, super.c) [WD] * Fixed erronous includes of linux/befs_fs_i.h and linux/befs_fs_sb.h in inode.c [WD] Version 0.45 (2001-10-29) ========== * Added functions to get the private superblock and inode structures from their enclosing public structures. Switched all references to the private portions to use them. (many files) [WD] * Made read_super and read_inode allocate the private portions of those structures into the generic pointer fields of the public structures with kmalloc(). put_super and put_inode free them. This allows us not to have to touch the definitions of the public structures in include/linux/fs.h. Also, befs_inode_info is huge (becuase of the symlink string). (super.c, inode.c, befs_fs.h) [WD] * Fixed a thinko that was corrupting file reads after the first block_run is done being read. (datastream.c) [WD] * Removed fsync() hooks, since a read-only filesystem doesn't need them. [Christoph Hellwig]. * Fixed befs_readlink() (symlink.c) [Christoph Hellwig]. * Removed all the Read-Write stuff. I'll redo it when it is time to add write support (various files) [WD]. * Removed prototypes for functions who's definitions have been removed (befs_fs.h) [WD]. Version 0.4 (2001-10-28) ========== * Made it an option to use the old non-pagecache befs_file_read() for testing purposes. (fs/Config.in) * Fixed unused variable warnings when compiling without debugging. * Fixed a bug where the inode and super_block didn't get their blockbits fields set (inode.c and super.c). * Release patch version 11. AKA befs-driver version 0.4. * Thats right. New versioning scheme. I've done some serious testing on it now (on my box anyhow), and it seems stable and not outragously slow. Existing features are more-or-less correct (see TODO list). But it isn't 1.0 yet. I think 0.4 gives me some headroom before the big 1.0. 2001-10-26 ========== * Fixed date format in this file. Was I smoking crack? * Removed old datastream code from file.c, since it is nolonger used. * Generic_read_file() is now used to read regular file data. It doesn't chew up the buffer cache (it does page io instead), and seems to be about as fast (even though it has to look up each file block indivdualy). And it knows about doing readahead, which is a major plus. So it does i/o in much larger chunks. It is the correct linux way. It uses befs_get_block() by way of befs_readpage() to find the disk offsets of blocks, which in turn calls befs_fpos2brun() in datastream.c to do the hard work of finding the disk block number. * Changed method of checking for a dirty filesystem in befs_read_super (super.c). Now we check to see if log_start and log_end differ. If so, the journal needs to be replayed, and the filesystem cannot be mounted. * Fixed an extra instance of MOD_DEC_USE_COUNT in super.c * Fixed a problem with reading the superblock on devices with large sector sizes (such as cdroms) on linux 2.4.10 and up. 2001-10-24 ========== * Fix nasty bug in converting block numbers to struct befs_inode_addr. Subtle, because the old version was only sometimes wrong. Probably responsible for lots of problems. (inode.c) * Fix bug with reading an empty directory. (btree.c and dir.c) * This one looks good. Release patch version 10 2001-10-23 ========== * Added btree searching function. * Use befs_btree_find in befs_lookup (namei.c) * Additional comments in btree.c 2001-10-22 ========== * Added B+tree reading functions (in btree.c). Made befs_readdir() use them them instead of the cruft in index.c. 2001-09-11 ========== * Converted befs_read_file() to use the new datastream code. * Finally updated the README file. * Added many comments. * Posted version 6 * Removed byte-order conversion code. I have no intention of supporting it, and it was very ugly. Flow control with #ifdef (ugh). Maybe I'll redo it once native byteorder works 100%. 2001-09-10 ========== * Finished implementing read_datastream() * made befs_read_brun() more general Supports an offset to start at and a max bytes to read Added a wrapper function to give the old call 2001-09-30 ========== * Discovered that the datastream handleing code in file.c is quite deficient in several respects. For one thing, it doesn't deal with indirect blocks * Rewrote datastream handleing. * Created io.c, for io related functions. Previously, the befs_bread() funtions lived in file.c Created the befs_read_brun() function. 2001-09-07 ========== * Made a function to actually count the number of fs blocks used by a file. And helper functions. (fs/befs/inode.c) 2001-09-05 ========== * Fixed a misunderstanding of the inode fields. This fixed the problmem with wrong file sizes from du and others. The i_blocks field of the inode struct is not the number of blocks for the inode, it is the number of blocks for the file. Also, i_blksize is not necessarily the size of the inode, although in practice it works out. Changed to blocksize of filesystem. (fs/befs/inode.c) * Permanently removed code that had been provisionally ifdefed out of befs_fs.h * Since we don't support access time, make that field zero, instead of copying m_time. (fs/befs/inode.c) * Added sanity check for inode reading Make sure inode we got was the one we asked for. (fs/befs/inode.c) * Code cleanup Local pointers to commonly used structures in inode.c. Got rid of abominations befs_iaddr2inode() and befs_inode2ino(). Replaced with single function iaddr2blockno(). (fs/befs/super.c) (fs/befs/inode.c) 2001-09-01 ========== * Fixed the problem with statfs where it would always claim the disk was half full, due to improper understanding of the statfs fields. (fs/befs/super.c) * Posted verion 4 of the patch 2001-09-01 ========== * Changed the macros in befs_fs.h to inline functions. More readable. Typesafe. Better (include/linux/befs_fs.h) * Moved type definitions from befs_fs.h to a new file, befs_fs_types.h Because befs_fs_i.h and befs_fs_sb.h were including befs_fs.h for the typedefs, and they are inlcuded in , which has definitions that I want the inline functions in befs_fs.h to be able to see. Nasty circularity. (include/linux/befs_fs.h) 2001-08-30 ========== * Cleaned up some wording. * Added additional consitency checks on mount Check block_size agrees with block_shift Check flags == BEFS_CLEAN (fs/befs/super.c) * Tell the kernel to only mount befs read-only. By setting the MS_RDONLY flag in befs_read_super(). Not that it was possible to write before. But now the kernel won't even try. (fs/befs/super.c) * Got rid of kernel warning on mount. The kernel doesn't like it if you call set_blocksize() on a device when you have some of its blocks open. Moved the second set_blocksize() to the very end of befs_read_super(), after we are done with the disk superblock. (fs/befs/super.c) * Fixed wrong number of args bug in befs_dump_inode (fs/befs/debug.c) * Solved lots of type mismatches in kprint()s (everwhere) 2001-08-27 ========== * Cleaned up the fs/Config.in entries a bit, now slightly more descriptive. * BeFS depends on NLS, so I made activating BeFS enable the NLS questions (fs/nls/Config.in) * Added Configure.help entries for CONFIG_BEFS_FS and CONFIG_DEBUG_BEFS (Documentation/Configure.help) 2001-08-?? ========== * Removed superblock locking calls in befs_read_super(). In 2.4, the VFS hands us a super_block struct that is already locked. 2001-08-13 ========== * Will Dyson is now attempting to maintain this module Makoto Kato is original author.Daniel Berlin also did some work on it (fixing it up for the later 2.3.x kernels, IIRC). * Fixed compile errors on 2.4.1 kernel (WD) Resolve rejected patches Accomodate changed NLS interface (util.h) Needed to include in most files Makefile changes fs/Config.in changes * Tried to niceify the code using the ext2 fs as a guide Declare befs_fs_type using the DECLARE_FSTYPE_DEV() macro * Made it a configure option to turn on debugging (fs/Config.in) * Compiles on 2.4.7 linux-2.6.17/fs/befs/.svn/text-base/datastream.c.svn-base0000444000000000000000000003706710574207632021602 0ustar rootroot/* * linux/fs/befs/datastream.c * * Copyright (C) 2001 Will Dyson * * Based on portions of file.c by Makoto Kato * * Many thanks to Dominic Giampaolo, author of "Practical File System * Design with the Be File System", for such a helpful book. * */ #include #include #include #include #include "befs.h" #include "datastream.h" #include "io.h" #include "endian.h" const befs_inode_addr BAD_IADDR = { 0, 0, 0 }; static int befs_find_brun_direct(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run); static int befs_find_brun_indirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run); static int befs_find_brun_dblindirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run); /** * befs_read_datastream - get buffer_head containing data, starting from pos. * @sb: Filesystem superblock * @ds: datastrem to find data with * @pos: start of data * @off: offset of data in buffer_head->b_data * * Returns pointer to buffer_head containing data starting with offset @off, * if you don't need to know offset just set @off = NULL. */ struct buffer_head * befs_read_datastream(struct super_block *sb, befs_data_stream * ds, befs_off_t pos, uint * off) { struct buffer_head *bh = NULL; befs_block_run run; befs_blocknr_t block; /* block coresponding to pos */ befs_debug(sb, "---> befs_read_datastream() %Lu", pos); block = pos >> BEFS_SB(sb)->block_shift; if (off) *off = pos - (block << BEFS_SB(sb)->block_shift); if (befs_fblock2brun(sb, ds, block, &run) != BEFS_OK) { befs_error(sb, "BeFS: Error finding disk addr of block %lu", block); befs_debug(sb, "<--- befs_read_datastream() ERROR"); return NULL; } bh = befs_bread_iaddr(sb, run); if (!bh) { befs_error(sb, "BeFS: Error reading block %lu from datastream", block); return NULL; } befs_debug(sb, "<--- befs_read_datastream() read data, starting at %Lu", pos); return bh; } /* * Takes a file position and gives back a brun who's starting block * is block number fblock of the file. * * Returns BEFS_OK or BEFS_ERR. * * Calls specialized functions for each of the three possible * datastream regions. * * 2001-11-15 Will Dyson */ int befs_fblock2brun(struct super_block *sb, befs_data_stream * data, befs_blocknr_t fblock, befs_block_run * run) { int err; befs_off_t pos = fblock << BEFS_SB(sb)->block_shift; if (pos < data->max_direct_range) { err = befs_find_brun_direct(sb, data, fblock, run); } else if (pos < data->max_indirect_range) { err = befs_find_brun_indirect(sb, data, fblock, run); } else if (pos < data->max_double_indirect_range) { err = befs_find_brun_dblindirect(sb, data, fblock, run); } else { befs_error(sb, "befs_fblock2brun() was asked to find block %lu, " "which is not mapped by the datastream\n", fblock); err = BEFS_ERR; } return err; } /** * befs_read_lsmylink - read long symlink from datastream. * @sb: Filesystem superblock * @ds: Datastrem to read from * @buf: Buffer in which to place long symlink data * @len: Length of the long symlink in bytes * * Returns the number of bytes read */ size_t befs_read_lsymlink(struct super_block * sb, befs_data_stream * ds, void *buff, befs_off_t len) { befs_off_t bytes_read = 0; /* bytes readed */ u16 plen; struct buffer_head *bh = NULL; befs_debug(sb, "---> befs_read_lsymlink() length: %Lu", len); while (bytes_read < len) { bh = befs_read_datastream(sb, ds, bytes_read, NULL); if (!bh) { befs_error(sb, "BeFS: Error reading datastream block " "starting from %Lu", bytes_read); befs_debug(sb, "<--- befs_read_lsymlink() ERROR"); return bytes_read; } plen = ((bytes_read + BEFS_SB(sb)->block_size) < len) ? BEFS_SB(sb)->block_size : len - bytes_read; memcpy(buff + bytes_read, bh->b_data, plen); brelse(bh); bytes_read += plen; } befs_debug(sb, "<--- befs_read_lsymlink() read %u bytes", bytes_read); return bytes_read; } /** * befs_count_blocks - blocks used by a file * @sb: Filesystem superblock * @ds: Datastream of the file * * Counts the number of fs blocks that the file represented by * inode occupies on the filesystem, counting both regular file * data and filesystem metadata (and eventually attribute data * when we support attributes) */ befs_blocknr_t befs_count_blocks(struct super_block * sb, befs_data_stream * ds) { befs_blocknr_t blocks; befs_blocknr_t datablocks; /* File data blocks */ befs_blocknr_t metablocks; /* FS metadata blocks */ befs_sb_info *befs_sb = BEFS_SB(sb); befs_debug(sb, "---> befs_count_blocks()"); datablocks = ds->size >> befs_sb->block_shift; if (ds->size & (befs_sb->block_size - 1)) datablocks += 1; metablocks = 1; /* Start with 1 block for inode */ /* Size of indirect block */ if (ds->size > ds->max_direct_range) metablocks += ds->indirect.len; /* Double indir block, plus all the indirect blocks it mapps In the double-indirect range, all block runs of data are BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know how many data block runs are in the double-indirect region, and from that we know how many indirect blocks it takes to map them. We assume that the indirect blocks are also BEFS_DBLINDIR_BRUN_LEN blocks long. */ if (ds->size > ds->max_indirect_range && ds->max_indirect_range != 0) { uint dbl_bytes; uint dbl_bruns; uint indirblocks; dbl_bytes = ds->max_double_indirect_range - ds->max_indirect_range; dbl_bruns = dbl_bytes / (befs_sb->block_size * BEFS_DBLINDIR_BRUN_LEN); indirblocks = dbl_bruns / befs_iaddrs_per_block(sb); metablocks += ds->double_indirect.len; metablocks += indirblocks; } blocks = datablocks + metablocks; befs_debug(sb, "<--- befs_count_blocks() %u blocks", blocks); return blocks; } /* Finds the block run that starts at file block number blockno in the file represented by the datastream data, if that blockno is in the direct region of the datastream. sb: the superblock data: the datastream blockno: the blocknumber to find run: The found run is passed back through this pointer Return value is BEFS_OK if the blockrun is found, BEFS_ERR otherwise. Algorithm: Linear search. Checks each element of array[] to see if it contains the blockno-th filesystem block. This is necessary because the block runs map variable amounts of data. Simply keeps a count of the number of blocks searched so far (sum), incrementing this by the length of each block run as we come across it. Adds sum to *count before returning (this is so you can search multiple arrays that are logicaly one array, as in the indirect region code). When/if blockno is found, if blockno is inside of a block run as stored on disk, we offset the start and lenght members of the block run, so that blockno is the start and len is still valid (the run ends in the same place). 2001-11-15 Will Dyson */ static int befs_find_brun_direct(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run) { int i; befs_block_run *array = data->direct; befs_blocknr_t sum; befs_blocknr_t max_block = data->max_direct_range >> BEFS_SB(sb)->block_shift; befs_debug(sb, "---> befs_find_brun_direct(), find %lu", blockno); if (blockno > max_block) { befs_error(sb, "befs_find_brun_direct() passed block outside of" "direct region"); return BEFS_ERR; } for (i = 0, sum = 0; i < BEFS_NUM_DIRECT_BLOCKS; sum += array[i].len, i++) { if (blockno >= sum && blockno < sum + (array[i].len)) { int offset = blockno - sum; run->allocation_group = array[i].allocation_group; run->start = array[i].start + offset; run->len = array[i].len - offset; befs_debug(sb, "---> befs_find_brun_direct(), " "found %lu at direct[%d]", blockno, i); return BEFS_OK; } } befs_debug(sb, "---> befs_find_brun_direct() ERROR"); return BEFS_ERR; } /* Finds the block run that starts at file block number blockno in the file represented by the datastream data, if that blockno is in the indirect region of the datastream. sb: the superblock data: the datastream blockno: the blocknumber to find run: The found run is passed back through this pointer Return value is BEFS_OK if the blockrun is found, BEFS_ERR otherwise. Algorithm: For each block in the indirect run of the datastream, read it in and search through it for search_blk. XXX: Really should check to make sure blockno is inside indirect region. 2001-11-15 Will Dyson */ static int befs_find_brun_indirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run) { int i, j; befs_blocknr_t sum = 0; befs_blocknr_t indir_start_blk; befs_blocknr_t search_blk; struct buffer_head *indirblock; befs_block_run *array; befs_block_run indirect = data->indirect; befs_blocknr_t indirblockno = iaddr2blockno(sb, &indirect); int arraylen = befs_iaddrs_per_block(sb); befs_debug(sb, "---> befs_find_brun_indirect(), find %lu", blockno); indir_start_blk = data->max_direct_range >> BEFS_SB(sb)->block_shift; search_blk = blockno - indir_start_blk; /* Examine blocks of the indirect run one at a time */ for (i = 0; i < indirect.len; i++) { indirblock = befs_bread(sb, indirblockno + i); if (indirblock == NULL) { befs_debug(sb, "---> befs_find_brun_indirect() failed to " "read disk block %lu from the indirect brun", indirblockno + i); return BEFS_ERR; } array = (befs_block_run *) indirblock->b_data; for (j = 0; j < arraylen; ++j) { int len = fs16_to_cpu(sb, array[j].len); if (search_blk >= sum && search_blk < sum + len) { int offset = search_blk - sum; run->allocation_group = fs32_to_cpu(sb, array[j].allocation_group); run->start = fs16_to_cpu(sb, array[j].start) + offset; run->len = fs16_to_cpu(sb, array[j].len) - offset; brelse(indirblock); befs_debug(sb, "<--- befs_find_brun_indirect() found " "file block %lu at indirect[%d]", blockno, j + (i * arraylen)); return BEFS_OK; } sum += len; } brelse(indirblock); } /* Only fallthrough is an error */ befs_error(sb, "BeFS: befs_find_brun_indirect() failed to find " "file block %lu", blockno); befs_debug(sb, "<--- befs_find_brun_indirect() ERROR"); return BEFS_ERR; } /* Finds the block run that starts at file block number blockno in the file represented by the datastream data, if that blockno is in the double-indirect region of the datastream. sb: the superblock data: the datastream blockno: the blocknumber to find run: The found run is passed back through this pointer Return value is BEFS_OK if the blockrun is found, BEFS_ERR otherwise. Algorithm: The block runs in the double-indirect region are different. They are always allocated 4 fs blocks at a time, so each block run maps a constant amount of file data. This means that we can directly calculate how many block runs into the double-indirect region we need to go to get to the one that maps a particular filesystem block. We do this in two stages. First we calculate which of the inode addresses in the double-indirect block will point us to the indirect block that contains the mapping for the data, then we calculate which of the inode addresses in that indirect block maps the data block we are after. Oh, and once we've done that, we actually read in the blocks that contain the inode addresses we calculated above. Even though the double-indirect run may be several blocks long, we can calculate which of those blocks will contain the index we are after and only read that one. We then follow it to the indirect block and perform a similar process to find the actual block run that maps the data block we are interested in. Then we offset the run as in befs_find_brun_array() and we are done. 2001-11-15 Will Dyson */ static int befs_find_brun_dblindirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run) { int dblindir_indx; int indir_indx; int offset; int dbl_which_block; int which_block; int dbl_block_indx; int block_indx; off_t dblindir_leftover; befs_blocknr_t blockno_at_run_start; struct buffer_head *dbl_indir_block; struct buffer_head *indir_block; befs_block_run indir_run; befs_inode_addr *iaddr_array = NULL; befs_sb_info *befs_sb = BEFS_SB(sb); befs_blocknr_t indir_start_blk = data->max_indirect_range >> befs_sb->block_shift; off_t dbl_indir_off = blockno - indir_start_blk; /* number of data blocks mapped by each of the iaddrs in * the indirect block pointed to by the double indirect block */ size_t iblklen = BEFS_DBLINDIR_BRUN_LEN; /* number of data blocks mapped by each of the iaddrs in * the double indirect block */ size_t diblklen = iblklen * befs_iaddrs_per_block(sb) * BEFS_DBLINDIR_BRUN_LEN; befs_debug(sb, "---> befs_find_brun_dblindirect() find %lu", blockno); /* First, discover which of the double_indir->indir blocks * contains pos. Then figure out how much of pos that * accounted for. Then discover which of the iaddrs in * the indirect block contains pos. */ dblindir_indx = dbl_indir_off / diblklen; dblindir_leftover = dbl_indir_off % diblklen; indir_indx = dblindir_leftover / diblklen; /* Read double indirect block */ dbl_which_block = dblindir_indx / befs_iaddrs_per_block(sb); if (dbl_which_block > data->double_indirect.len) { befs_error(sb, "The double-indirect index calculated by " "befs_read_brun_dblindirect(), %d, is outside the range " "of the double-indirect block", dblindir_indx); return BEFS_ERR; } dbl_indir_block = befs_bread(sb, iaddr2blockno(sb, &data->double_indirect) + dbl_which_block); if (dbl_indir_block == NULL) { befs_error(sb, "befs_read_brun_dblindirect() couldn't read the " "double-indirect block at blockno %lu", iaddr2blockno(sb, &data->double_indirect) + dbl_which_block); brelse(dbl_indir_block); return BEFS_ERR; } dbl_block_indx = dblindir_indx - (dbl_which_block * befs_iaddrs_per_block(sb)); iaddr_array = (befs_inode_addr *) dbl_indir_block->b_data; indir_run = fsrun_to_cpu(sb, iaddr_array[dbl_block_indx]); brelse(dbl_indir_block); iaddr_array = NULL; /* Read indirect block */ which_block = indir_indx / befs_iaddrs_per_block(sb); if (which_block > indir_run.len) { befs_error(sb, "The indirect index calculated by " "befs_read_brun_dblindirect(), %d, is outside the range " "of the indirect block", indir_indx); return BEFS_ERR; } indir_block = befs_bread(sb, iaddr2blockno(sb, &indir_run) + which_block); if (indir_block == NULL) { befs_error(sb, "befs_read_brun_dblindirect() couldn't read the " "indirect block at blockno %lu", iaddr2blockno(sb, &indir_run) + which_block); brelse(indir_block); return BEFS_ERR; } block_indx = indir_indx - (which_block * befs_iaddrs_per_block(sb)); iaddr_array = (befs_inode_addr *) indir_block->b_data; *run = fsrun_to_cpu(sb, iaddr_array[block_indx]); brelse(indir_block); iaddr_array = NULL; blockno_at_run_start = indir_start_blk; blockno_at_run_start += diblklen * dblindir_indx; blockno_at_run_start += iblklen * indir_indx; offset = blockno - blockno_at_run_start; run->start += offset; run->len -= offset; befs_debug(sb, "Found file block %lu in double_indirect[%d][%d]," " double_indirect_leftover = %lu", blockno, dblindir_indx, indir_indx, dblindir_leftover); return BEFS_OK; } linux-2.6.17/fs/befs/.svn/text-base/befs.h.svn-base0000444000000000000000000000624210574207632020370 0ustar rootroot/* * befs.h * * Copyright (C) 2001-2002 Will Dyson * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp) */ #ifndef _LINUX_BEFS_H #define _LINUX_BEFS_H #include "befs_fs_types.h" /* used in debug.c */ #define BEFS_VERSION "0.9.3" typedef u64 befs_blocknr_t; /* * BeFS in memory structures */ typedef struct befs_mount_options { gid_t gid; uid_t uid; int use_gid; int use_uid; int debug; char *iocharset; } befs_mount_options; typedef struct befs_sb_info { u32 magic1; u32 block_size; u32 block_shift; int byte_order; befs_off_t num_blocks; befs_off_t used_blocks; u32 inode_size; u32 magic2; /* Allocation group information */ u32 blocks_per_ag; u32 ag_shift; u32 num_ags; /* jornal log entry */ befs_block_run log_blocks; befs_off_t log_start; befs_off_t log_end; befs_inode_addr root_dir; befs_inode_addr indices; u32 magic3; befs_mount_options mount_opts; struct nls_table *nls; } befs_sb_info; typedef struct befs_inode_info { u32 i_flags; u32 i_type; befs_inode_addr i_inode_num; befs_inode_addr i_parent; befs_inode_addr i_attribute; union { befs_data_stream ds; char symlink[BEFS_SYMLINK_LEN]; } i_data; struct inode vfs_inode; } befs_inode_info; enum befs_err { BEFS_OK, BEFS_ERR, BEFS_BAD_INODE, BEFS_BT_END, BEFS_BT_EMPTY, BEFS_BT_MATCH, BEFS_BT_PARMATCH, BEFS_BT_NOT_FOUND }; /****************************/ /* debug.c */ void befs_error(const struct super_block *sb, const char *fmt, ...); void befs_warning(const struct super_block *sb, const char *fmt, ...); void befs_debug(const struct super_block *sb, const char *fmt, ...); void befs_dump_super_block(const struct super_block *sb, befs_super_block *); void befs_dump_inode(const struct super_block *sb, befs_inode *); void befs_dump_index_entry(const struct super_block *sb, befs_btree_super *); void befs_dump_index_node(const struct super_block *sb, befs_btree_nodehead *); /****************************/ /* Gets a pointer to the private portion of the super_block * structure from the public part */ static inline befs_sb_info * BEFS_SB(const struct super_block *super) { return (befs_sb_info *) super->s_fs_info; } static inline befs_inode_info * BEFS_I(const struct inode *inode) { return list_entry(inode, struct befs_inode_info, vfs_inode); } static inline befs_blocknr_t iaddr2blockno(struct super_block *sb, befs_inode_addr * iaddr) { return ((iaddr->allocation_group << BEFS_SB(sb)->ag_shift) + iaddr->start); } static inline befs_inode_addr blockno2iaddr(struct super_block *sb, befs_blocknr_t blockno) { befs_inode_addr iaddr; iaddr.allocation_group = blockno >> BEFS_SB(sb)->ag_shift; iaddr.start = blockno - (iaddr.allocation_group << BEFS_SB(sb)->ag_shift); iaddr.len = 1; return iaddr; } static inline unsigned int befs_iaddrs_per_block(struct super_block *sb) { return BEFS_SB(sb)->block_size / sizeof (befs_inode_addr); } static inline int befs_iaddr_is_empty(befs_inode_addr * iaddr) { return (!iaddr->allocation_group) && (!iaddr->start) && (!iaddr->len); } static inline size_t befs_brun_size(struct super_block *sb, befs_block_run run) { return BEFS_SB(sb)->block_size * run.len; } #endif /* _LINUX_BEFS_H */ linux-2.6.17/fs/befs/.svn/text-base/super.h.svn-base0000444000000000000000000000020610574207632020601 0ustar rootroot/* * super.h */ int befs_load_sb(struct super_block *sb, befs_super_block * disk_sb); int befs_check_sb(struct super_block *sb); linux-2.6.17/fs/befs/.svn/text-base/datastream.h.svn-base0000444000000000000000000000100210574207632021563 0ustar rootroot/* * datastream.h * */ struct buffer_head *befs_read_datastream(struct super_block *sb, befs_data_stream * ds, befs_off_t pos, uint * off); int befs_fblock2brun(struct super_block *sb, befs_data_stream * data, befs_blocknr_t fblock, befs_block_run * run); size_t befs_read_lsymlink(struct super_block *sb, befs_data_stream * data, void *buff, befs_off_t len); befs_blocknr_t befs_count_blocks(struct super_block *sb, befs_data_stream * ds); extern const befs_inode_addr BAD_IADDR; linux-2.6.17/fs/befs/.svn/text-base/btree.h.svn-base0000444000000000000000000000045410574207632020551 0ustar rootroot/* * btree.h * */ int befs_btree_find(struct super_block *sb, befs_data_stream * ds, const char *key, befs_off_t * value); int befs_btree_read(struct super_block *sb, befs_data_stream * ds, loff_t key_no, size_t bufsize, char *keybuf, size_t * keysize, befs_off_t * value); linux-2.6.17/fs/befs/.svn/text-base/inode.h.svn-base0000444000000000000000000000017310574207632020544 0ustar rootroot/* * inode.h * */ int befs_check_inode(struct super_block *sb, befs_inode * raw_inode, befs_blocknr_t inode); linux-2.6.17/fs/befs/.svn/text-base/linuxvfs.c.svn-base0000444000000000000000000005723710574207632021334 0ustar rootroot/* * linux/fs/befs/linuxvfs.c * * Copyright (C) 2001 Will Dyson #include #include #include #include #include #include #include #include #include #include "befs.h" #include "btree.h" #include "inode.h" #include "datastream.h" #include "super.h" #include "io.h" #include "endian.h" MODULE_DESCRIPTION("BeOS File System (BeFS) driver"); MODULE_AUTHOR("Will Dyson"); MODULE_LICENSE("GPL"); /* The units the vfs expects inode->i_blocks to be in */ #define VFS_BLOCK_SIZE 512 static int befs_readdir(struct file *, void *, filldir_t); static int befs_get_block(struct inode *, sector_t, struct buffer_head *, int); static int befs_readpage(struct file *file, struct page *page); static sector_t befs_bmap(struct address_space *mapping, sector_t block); static struct dentry *befs_lookup(struct inode *, struct dentry *, struct nameidata *); static void befs_read_inode(struct inode *ino); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static int befs_init_inodecache(void); static void befs_destroy_inodecache(void); static void *befs_follow_link(struct dentry *, struct nameidata *); static void befs_put_link(struct dentry *, struct nameidata *, void *); static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static void befs_put_super(struct super_block *); static int befs_remount(struct super_block *, int *, char *); static int befs_statfs(struct super_block *, struct kstatfs *); static int parse_options(char *, befs_mount_options *); static const struct super_operations befs_sops = { .read_inode = befs_read_inode, /* initialize & read inode */ .alloc_inode = befs_alloc_inode, /* allocate a new inode */ .destroy_inode = befs_destroy_inode, /* deallocate an inode */ .put_super = befs_put_super, /* uninit super */ .statfs = befs_statfs, /* statfs */ .remount_fs = befs_remount, }; /* slab cache for befs_inode_info objects */ static kmem_cache_t *befs_inode_cachep; static const struct file_operations befs_dir_operations = { .read = generic_read_dir, .readdir = befs_readdir, }; static struct inode_operations befs_dir_inode_operations = { .lookup = befs_lookup, }; static struct address_space_operations befs_aops = { .readpage = befs_readpage, .sync_page = block_sync_page, .bmap = befs_bmap, }; static struct inode_operations befs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = befs_follow_link, .put_link = befs_put_link, }; /* * Called by generic_file_read() to read a page of data * * In turn, simply calls a generic block read function and * passes it the address of befs_get_block, for mapping file * positions to disk blocks. */ static int befs_readpage(struct file *file, struct page *page) { return block_read_full_page(page, befs_get_block); } static sector_t befs_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping, block, befs_get_block); } /* * Generic function to map a file position (block) to a * disk offset (passed back in bh_result). * * Used by many higher level functions. * * Calls befs_fblock2brun() in datastream.c to do the real work. * * -WD 10-26-01 */ static int befs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) { struct super_block *sb = inode->i_sb; befs_data_stream *ds = &BEFS_I(inode)->i_data.ds; befs_block_run run = BAD_IADDR; int res = 0; ulong disk_off; befs_debug(sb, "---> befs_get_block() for inode %lu, block %ld", inode->i_ino, block); if (block < 0) { befs_error(sb, "befs_get_block() was asked for a block " "number less than zero: block %ld in inode %lu", block, inode->i_ino); return -EIO; } if (create) { befs_error(sb, "befs_get_block() was asked to write to " "block %ld in inode %lu", block, inode->i_ino); return -EPERM; } res = befs_fblock2brun(sb, ds, block, &run); if (res != BEFS_OK) { befs_error(sb, "<--- befs_get_block() for inode %lu, block " "%ld ERROR", inode->i_ino, block); return -EFBIG; } disk_off = (ulong) iaddr2blockno(sb, &run); map_bh(bh_result, inode->i_sb, disk_off); befs_debug(sb, "<--- befs_get_block() for inode %lu, block %ld, " "disk address %lu", inode->i_ino, block, disk_off); return 0; } static struct dentry * befs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct inode *inode = NULL; struct super_block *sb = dir->i_sb; befs_data_stream *ds = &BEFS_I(dir)->i_data.ds; befs_off_t offset; int ret; int utfnamelen; char *utfname; const char *name = dentry->d_name.name; befs_debug(sb, "---> befs_lookup() " "name %s inode %ld", dentry->d_name.name, dir->i_ino); /* Convert to UTF-8 */ if (BEFS_SB(sb)->nls) { ret = befs_nls2utf(sb, name, strlen(name), &utfname, &utfnamelen); if (ret < 0) { befs_debug(sb, "<--- befs_lookup() ERROR"); return ERR_PTR(ret); } ret = befs_btree_find(sb, ds, utfname, &offset); kfree(utfname); } else { ret = befs_btree_find(sb, ds, dentry->d_name.name, &offset); } if (ret == BEFS_BT_NOT_FOUND) { befs_debug(sb, "<--- befs_lookup() %s not found", dentry->d_name.name); return ERR_PTR(-ENOENT); } else if (ret != BEFS_OK || offset == 0) { befs_warning(sb, "<--- befs_lookup() Error"); return ERR_PTR(-ENODATA); } inode = iget(dir->i_sb, (ino_t) offset); if (!inode) return ERR_PTR(-EACCES); d_add(dentry, inode); befs_debug(sb, "<--- befs_lookup()"); return NULL; } static int befs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; befs_data_stream *ds = &BEFS_I(inode)->i_data.ds; befs_off_t value; int result; size_t keysize; unsigned char d_type; char keybuf[BEFS_NAME_LEN + 1]; char *nlsname; int nlsnamelen; const char *dirname = filp->f_dentry->d_name.name; befs_debug(sb, "---> befs_readdir() " "name %s, inode %ld, filp->f_pos %Ld", dirname, inode->i_ino, filp->f_pos); result = befs_btree_read(sb, ds, filp->f_pos, BEFS_NAME_LEN + 1, keybuf, &keysize, &value); if (result == BEFS_ERR) { befs_debug(sb, "<--- befs_readdir() ERROR"); befs_error(sb, "IO error reading %s (inode %lu)", dirname, inode->i_ino); return -EIO; } else if (result == BEFS_BT_END) { befs_debug(sb, "<--- befs_readdir() END"); return 0; } else if (result == BEFS_BT_EMPTY) { befs_debug(sb, "<--- befs_readdir() Empty directory"); return 0; } d_type = DT_UNKNOWN; /* Convert to NLS */ if (BEFS_SB(sb)->nls) { result = befs_utf2nls(sb, keybuf, keysize, &nlsname, &nlsnamelen); if (result < 0) { befs_debug(sb, "<--- befs_readdir() ERROR"); return result; } result = filldir(dirent, nlsname, nlsnamelen, filp->f_pos, (ino_t) value, d_type); kfree(nlsname); } else { result = filldir(dirent, keybuf, keysize, filp->f_pos, (ino_t) value, d_type); } filp->f_pos++; befs_debug(sb, "<--- befs_readdir() filp->f_pos %Ld", filp->f_pos); return 0; } static struct inode * befs_alloc_inode(struct super_block *sb) { struct befs_inode_info *bi; bi = (struct befs_inode_info *)kmem_cache_alloc(befs_inode_cachep, SLAB_KERNEL); if (!bi) return NULL; return &bi->vfs_inode; } static void befs_destroy_inode(struct inode *inode) { kmem_cache_free(befs_inode_cachep, BEFS_I(inode)); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct befs_inode_info *bi = (struct befs_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { inode_init_once(&bi->vfs_inode); } } static void befs_read_inode(struct inode *inode) { struct buffer_head *bh = NULL; befs_inode *raw_inode = NULL; struct super_block *sb = inode->i_sb; befs_sb_info *befs_sb = BEFS_SB(sb); befs_inode_info *befs_ino = NULL; befs_debug(sb, "---> befs_read_inode() " "inode = %lu", inode->i_ino); befs_ino = BEFS_I(inode); /* convert from vfs's inode number to befs's inode number */ befs_ino->i_inode_num = blockno2iaddr(sb, inode->i_ino); befs_debug(sb, " real inode number [%u, %hu, %hu]", befs_ino->i_inode_num.allocation_group, befs_ino->i_inode_num.start, befs_ino->i_inode_num.len); bh = befs_bread(sb, inode->i_ino); if (!bh) { befs_error(sb, "unable to read inode block - " "inode = %lu", inode->i_ino); goto unaquire_none; } raw_inode = (befs_inode *) bh->b_data; befs_dump_inode(sb, raw_inode); if (befs_check_inode(sb, raw_inode, inode->i_ino) != BEFS_OK) { befs_error(sb, "Bad inode: %lu", inode->i_ino); goto unaquire_bh; } inode->i_mode = (umode_t) fs32_to_cpu(sb, raw_inode->mode); /* * set uid and gid. But since current BeOS is single user OS, so * you can change by "uid" or "gid" options. */ inode->i_uid = befs_sb->mount_opts.use_uid ? befs_sb->mount_opts.uid : (uid_t) fs32_to_cpu(sb, raw_inode->uid); inode->i_gid = befs_sb->mount_opts.use_gid ? befs_sb->mount_opts.gid : (gid_t) fs32_to_cpu(sb, raw_inode->gid); inode->i_nlink = 1; /* * BEFS's time is 64 bits, but current VFS is 32 bits... * BEFS don't have access time. Nor inode change time. VFS * doesn't have creation time. * Also, the lower 16 bits of the last_modified_time and * create_time are just a counter to help ensure uniqueness * for indexing purposes. (PFD, page 54) */ inode->i_mtime.tv_sec = fs64_to_cpu(sb, raw_inode->last_modified_time) >> 16; inode->i_mtime.tv_nsec = 0; /* lower 16 bits are not a time */ inode->i_ctime = inode->i_mtime; inode->i_atime = inode->i_mtime; inode->i_blksize = befs_sb->block_size; befs_ino->i_inode_num = fsrun_to_cpu(sb, raw_inode->inode_num); befs_ino->i_parent = fsrun_to_cpu(sb, raw_inode->parent); befs_ino->i_attribute = fsrun_to_cpu(sb, raw_inode->attributes); befs_ino->i_flags = fs32_to_cpu(sb, raw_inode->flags); if (S_ISLNK(inode->i_mode) && !(befs_ino->i_flags & BEFS_LONG_SYMLINK)){ inode->i_size = 0; inode->i_blocks = befs_sb->block_size / VFS_BLOCK_SIZE; strncpy(befs_ino->i_data.symlink, raw_inode->data.symlink, BEFS_SYMLINK_LEN); } else { int num_blks; befs_ino->i_data.ds = fsds_to_cpu(sb, raw_inode->data.datastream); num_blks = befs_count_blocks(sb, &befs_ino->i_data.ds); inode->i_blocks = num_blks * (befs_sb->block_size / VFS_BLOCK_SIZE); inode->i_size = befs_ino->i_data.ds.size; } inode->i_mapping->a_ops = &befs_aops; if (S_ISREG(inode->i_mode)) { inode->i_fop = &generic_ro_fops; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &befs_dir_inode_operations; inode->i_fop = &befs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &befs_symlink_inode_operations; } else { befs_error(sb, "Inode %lu is not a regular file, " "directory or symlink. THAT IS WRONG! BeFS has no " "on disk special files", inode->i_ino); goto unaquire_bh; } brelse(bh); befs_debug(sb, "<--- befs_read_inode()"); return; unaquire_bh: brelse(bh); unaquire_none: make_bad_inode(inode); befs_debug(sb, "<--- befs_read_inode() - Bad inode"); return; } /* Initialize the inode cache. Called at fs setup. * * Taken from NFS implementation by Al Viro. */ static int befs_init_inodecache(void) { befs_inode_cachep = kmem_cache_create("befs_inode_cache", sizeof (struct befs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), init_once, NULL); if (befs_inode_cachep == NULL) { printk(KERN_ERR "befs_init_inodecache: " "Couldn't initalize inode slabcache\n"); return -ENOMEM; } return 0; } /* Called at fs teardown. * * Taken from NFS implementation by Al Viro. */ static void befs_destroy_inodecache(void) { if (kmem_cache_destroy(befs_inode_cachep)) printk(KERN_ERR "befs_destroy_inodecache: " "not all structures were freed\n"); } /* * The inode of symbolic link is different to data stream. * The data stream become link name. Unless the LONG_SYMLINK * flag is set. */ static void * befs_follow_link(struct dentry *dentry, struct nameidata *nd) { befs_inode_info *befs_ino = BEFS_I(dentry->d_inode); char *link; if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { struct super_block *sb = dentry->d_sb; befs_data_stream *data = &befs_ino->i_data.ds; befs_off_t len = data->size; befs_debug(sb, "Follow long symlink"); link = kmalloc(len, GFP_NOFS); if (!link) { link = ERR_PTR(-ENOMEM); } else if (befs_read_lsymlink(sb, data, link, len) != len) { kfree(link); befs_error(sb, "Failed to read entire long symlink"); link = ERR_PTR(-EIO); } } else { link = befs_ino->i_data.symlink; } nd_set_link(nd, link); return NULL; } static void befs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) { befs_inode_info *befs_ino = BEFS_I(dentry->d_inode); if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { char *p = nd_get_link(nd); if (!IS_ERR(p)) kfree(p); } } /* * UTF-8 to NLS charset convert routine * * * Changed 8/10/01 by Will Dyson. Now use uni2char() / char2uni() rather than * the nls tables directly */ static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len) { struct nls_table *nls = BEFS_SB(sb)->nls; int i, o; wchar_t uni; int unilen, utflen; char *result; /* The utf8->nls conversion won't make the final nls string bigger * than the utf one, but if the string is pure ascii they'll have the * same width and an extra char is needed to save the additional \0 */ int maxlen = in_len + 1; befs_debug(sb, "---> utf2nls()"); if (!nls) { befs_error(sb, "befs_utf2nls called with no NLS table loaded"); return -EINVAL; } *out = result = kmalloc(maxlen, GFP_NOFS); if (!*out) { befs_error(sb, "befs_utf2nls() cannot allocate memory"); *out_len = 0; return -ENOMEM; } for (i = o = 0; i < in_len; i += utflen, o += unilen) { /* convert from UTF-8 to Unicode */ utflen = utf8_mbtowc(&uni, &in[i], in_len - i); if (utflen < 0) { goto conv_err; } /* convert from Unicode to nls */ unilen = nls->uni2char(uni, &result[o], in_len - o); if (unilen < 0) { goto conv_err; } } result[o] = '\0'; *out_len = o; befs_debug(sb, "<--- utf2nls()"); return o; conv_err: befs_error(sb, "Name using character set %s contains a character that " "cannot be converted to unicode.", nls->charset); befs_debug(sb, "<--- utf2nls()"); kfree(result); return -EILSEQ; } /** * befs_nls2utf - Convert NLS string to utf8 encodeing * @sb: Superblock * @src: Input string buffer in NLS format * @srclen: Length of input string in bytes * @dest: The output string in UTF-8 format * @destlen: Length of the output buffer * * Converts input string @src, which is in the format of the loaded NLS map, * into a utf8 string. * * The destination string @dest is allocated by this function and the caller is * responsible for freeing it with kfree() * * On return, *@destlen is the length of @dest in bytes. * * On success, the return value is the number of utf8 characters written to * the output buffer @dest. * * On Failure, a negative number coresponding to the error code is returned. */ static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, char **out, int *out_len) { struct nls_table *nls = BEFS_SB(sb)->nls; int i, o; wchar_t uni; int unilen, utflen; char *result; /* There're nls characters that will translate to 3-chars-wide UTF-8 * characters, a additional byte is needed to save the final \0 * in special cases */ int maxlen = (3 * in_len) + 1; befs_debug(sb, "---> nls2utf()\n"); if (!nls) { befs_error(sb, "befs_nls2utf called with no NLS table loaded."); return -EINVAL; } *out = result = kmalloc(maxlen, GFP_NOFS); if (!*out) { befs_error(sb, "befs_nls2utf() cannot allocate memory"); *out_len = 0; return -ENOMEM; } for (i = o = 0; i < in_len; i += unilen, o += utflen) { /* convert from nls to unicode */ unilen = nls->char2uni(&in[i], in_len - i, &uni); if (unilen < 0) { goto conv_err; } /* convert from unicode to UTF-8 */ utflen = utf8_wctomb(&result[o], uni, 3); if (utflen <= 0) { goto conv_err; } } result[o] = '\0'; *out_len = o; befs_debug(sb, "<--- nls2utf()"); return i; conv_err: befs_error(sb, "Name using charecter set %s contains a charecter that " "cannot be converted to unicode.", nls->charset); befs_debug(sb, "<--- nls2utf()"); kfree(result); return -EILSEQ; } /** * Use the * */ enum { Opt_uid, Opt_gid, Opt_charset, Opt_debug, Opt_err, }; static match_table_t befs_tokens = { {Opt_uid, "uid=%d"}, {Opt_gid, "gid=%d"}, {Opt_charset, "iocharset=%s"}, {Opt_debug, "debug"}, {Opt_err, NULL} }; static int parse_options(char *options, befs_mount_options * opts) { char *p; substring_t args[MAX_OPT_ARGS]; int option; /* Initialize options */ opts->uid = 0; opts->gid = 0; opts->use_uid = 0; opts->use_gid = 0; opts->iocharset = NULL; opts->debug = 0; if (!options) return 1; while ((p = strsep(&options, ",")) != NULL) { int token; if (!*p) continue; token = match_token(p, befs_tokens, args); switch (token) { case Opt_uid: if (match_int(&args[0], &option)) return 0; if (option < 0) { printk(KERN_ERR "BeFS: Invalid uid %d, " "using default\n", option); break; } opts->uid = option; opts->use_uid = 1; break; case Opt_gid: if (match_int(&args[0], &option)) return 0; if (option < 0) { printk(KERN_ERR "BeFS: Invalid gid %d, " "using default\n", option); break; } opts->gid = option; opts->use_gid = 1; break; case Opt_charset: kfree(opts->iocharset); opts->iocharset = match_strdup(&args[0]); if (!opts->iocharset) { printk(KERN_ERR "BeFS: allocation failure for " "iocharset string\n"); return 0; } break; case Opt_debug: opts->debug = 1; break; default: printk(KERN_ERR "BeFS: Unrecognized mount option \"%s\" " "or missing value\n", p); return 0; } } return 1; } /* This function has the responsibiltiy of getting the * filesystem ready for unmounting. * Basicly, we free everything that we allocated in * befs_read_inode */ static void befs_put_super(struct super_block *sb) { kfree(BEFS_SB(sb)->mount_opts.iocharset); BEFS_SB(sb)->mount_opts.iocharset = NULL; if (BEFS_SB(sb)->nls) { unload_nls(BEFS_SB(sb)->nls); BEFS_SB(sb)->nls = NULL; } kfree(sb->s_fs_info); sb->s_fs_info = NULL; return; } /* Allocate private field of the superblock, fill it. * * Finish filling the public superblock fields * Make the root directory * Load a set of NLS translations if needed. */ static int befs_fill_super(struct super_block *sb, void *data, int silent) { struct buffer_head *bh; befs_sb_info *befs_sb; befs_super_block *disk_sb; struct inode *root; const unsigned long sb_block = 0; const off_t x86_sb_off = 512; sb->s_fs_info = kmalloc(sizeof (*befs_sb), GFP_KERNEL); if (sb->s_fs_info == NULL) { printk(KERN_ERR "BeFS(%s): Unable to allocate memory for private " "portion of superblock. Bailing.\n", sb->s_id); goto unaquire_none; } befs_sb = BEFS_SB(sb); memset(befs_sb, 0, sizeof(befs_sb_info)); if (!parse_options((char *) data, &befs_sb->mount_opts)) { befs_error(sb, "cannot parse mount options"); goto unaquire_priv_sbp; } befs_debug(sb, "---> befs_fill_super()"); #ifndef CONFIG_BEFS_RW if (!(sb->s_flags & MS_RDONLY)) { befs_warning(sb, "No write support. Marking filesystem read-only"); sb->s_flags |= MS_RDONLY; } #endif /* CONFIG_BEFS_RW */ /* * Set dummy blocksize to read super block. * Will be set to real fs blocksize later. * * Linux 2.4.10 and later refuse to read blocks smaller than * the hardsect size for the device. But we also need to read at * least 1k to get the second 512 bytes of the volume. * -WD 10-26-01 */ sb_min_blocksize(sb, 1024); if (!(bh = sb_bread(sb, sb_block))) { befs_error(sb, "unable to read superblock"); goto unaquire_priv_sbp; } /* account for offset of super block on x86 */ disk_sb = (befs_super_block *) bh->b_data; if ((le32_to_cpu(disk_sb->magic1) == BEFS_SUPER_MAGIC1) || (be32_to_cpu(disk_sb->magic1) == BEFS_SUPER_MAGIC1)) { befs_debug(sb, "Using PPC superblock location"); } else { befs_debug(sb, "Using x86 superblock location"); disk_sb = (befs_super_block *) ((void *) bh->b_data + x86_sb_off); } if (befs_load_sb(sb, disk_sb) != BEFS_OK) goto unaquire_bh; befs_dump_super_block(sb, disk_sb); brelse(bh); if (befs_check_sb(sb) != BEFS_OK) goto unaquire_priv_sbp; if( befs_sb->num_blocks > ~((sector_t)0) ) { befs_error(sb, "blocks count: %Lu " "is larger than the host can use", befs_sb->num_blocks); goto unaquire_priv_sbp; } /* * set up enough so that it can read an inode * Fill in kernel superblock fields from private sb */ sb->s_magic = BEFS_SUPER_MAGIC; /* Set real blocksize of fs */ sb_set_blocksize(sb, (ulong) befs_sb->block_size); sb->s_op = (struct super_operations *) &befs_sops; root = iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir))); sb->s_root = d_alloc_root(root); if (!sb->s_root) { iput(root); befs_error(sb, "get root inode failed"); goto unaquire_priv_sbp; } /* load nls library */ if (befs_sb->mount_opts.iocharset) { befs_debug(sb, "Loading nls: %s", befs_sb->mount_opts.iocharset); befs_sb->nls = load_nls(befs_sb->mount_opts.iocharset); if (!befs_sb->nls) { befs_warning(sb, "Cannot load nls %s" " loading default nls", befs_sb->mount_opts.iocharset); befs_sb->nls = load_nls_default(); } /* load default nls if none is specified in mount options */ } else { befs_debug(sb, "Loading default nls"); befs_sb->nls = load_nls_default(); } return 0; /*****************/ unaquire_bh: brelse(bh); unaquire_priv_sbp: kfree(sb->s_fs_info); unaquire_none: sb->s_fs_info = NULL; return -EINVAL; } static int befs_remount(struct super_block *sb, int *flags, char *data) { if (!(*flags & MS_RDONLY)) return -EINVAL; return 0; } static int befs_statfs(struct super_block *sb, struct kstatfs *buf) { befs_debug(sb, "---> befs_statfs()"); buf->f_type = BEFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = BEFS_SB(sb)->num_blocks; buf->f_bfree = BEFS_SB(sb)->num_blocks - BEFS_SB(sb)->used_blocks; buf->f_bavail = buf->f_bfree; buf->f_files = 0; /* UNKNOWN */ buf->f_ffree = 0; /* UNKNOWN */ buf->f_namelen = BEFS_NAME_LEN; befs_debug(sb, "<--- befs_statfs()"); return 0; } static struct super_block * befs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_bdev(fs_type, flags, dev_name, data, befs_fill_super); } static struct file_system_type befs_fs_type = { .owner = THIS_MODULE, .name = "befs", .get_sb = befs_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static int __init init_befs_fs(void) { int err; printk(KERN_INFO "BeFS version: %s\n", BEFS_VERSION); err = befs_init_inodecache(); if (err) goto unaquire_none; err = register_filesystem(&befs_fs_type); if (err) goto unaquire_inodecache; return 0; unaquire_inodecache: befs_destroy_inodecache(); unaquire_none: return err; } static void __exit exit_befs_fs(void) { befs_destroy_inodecache(); unregister_filesystem(&befs_fs_type); } /* Macros that typecheck the init and exit functions, ensures that they are called at init and cleanup, and eliminates warnings about unused functions. */ module_init(init_befs_fs) module_exit(exit_befs_fs) linux-2.6.17/fs/befs/.svn/text-base/super.c.svn-base0000444000000000000000000000604310574207632020601 0ustar rootroot/* * super.c * * Copyright (C) 2001-2002 Will Dyson * * Licensed under the GNU GPL. See the file COPYING for details. * */ #include #include "befs.h" #include "super.h" #include "endian.h" /** * load_befs_sb -- Read from disk and properly byteswap all the fields * of the befs superblock * * * * */ int befs_load_sb(struct super_block *sb, befs_super_block * disk_sb) { befs_sb_info *befs_sb = BEFS_SB(sb); /* Check the byte order of the filesystem */ if (le32_to_cpu(disk_sb->fs_byte_order) == BEFS_BYTEORDER_NATIVE) befs_sb->byte_order = BEFS_BYTESEX_LE; else if (be32_to_cpu(disk_sb->fs_byte_order) == BEFS_BYTEORDER_NATIVE) befs_sb->byte_order = BEFS_BYTESEX_BE; befs_sb->magic1 = fs32_to_cpu(sb, disk_sb->magic1); befs_sb->magic2 = fs32_to_cpu(sb, disk_sb->magic2); befs_sb->magic3 = fs32_to_cpu(sb, disk_sb->magic3); befs_sb->block_size = fs32_to_cpu(sb, disk_sb->block_size); befs_sb->block_shift = fs32_to_cpu(sb, disk_sb->block_shift); befs_sb->num_blocks = fs64_to_cpu(sb, disk_sb->num_blocks); befs_sb->used_blocks = fs64_to_cpu(sb, disk_sb->used_blocks); befs_sb->inode_size = fs32_to_cpu(sb, disk_sb->inode_size); befs_sb->blocks_per_ag = fs32_to_cpu(sb, disk_sb->blocks_per_ag); befs_sb->ag_shift = fs32_to_cpu(sb, disk_sb->ag_shift); befs_sb->num_ags = fs32_to_cpu(sb, disk_sb->num_ags); befs_sb->log_blocks = fsrun_to_cpu(sb, disk_sb->log_blocks); befs_sb->log_start = fs64_to_cpu(sb, disk_sb->log_start); befs_sb->log_end = fs64_to_cpu(sb, disk_sb->log_end); befs_sb->root_dir = fsrun_to_cpu(sb, disk_sb->root_dir); befs_sb->indices = fsrun_to_cpu(sb, disk_sb->indices); befs_sb->nls = NULL; return BEFS_OK; } int befs_check_sb(struct super_block *sb) { befs_sb_info *befs_sb = BEFS_SB(sb); /* Check magic headers of super block */ if ((befs_sb->magic1 != BEFS_SUPER_MAGIC1) || (befs_sb->magic2 != BEFS_SUPER_MAGIC2) || (befs_sb->magic3 != BEFS_SUPER_MAGIC3)) { befs_error(sb, "invalid magic header"); return BEFS_ERR; } /* * Check blocksize of BEFS. * * Blocksize of BEFS is 1024, 2048, 4096 or 8192. */ if ((befs_sb->block_size != 1024) && (befs_sb->block_size != 2048) && (befs_sb->block_size != 4096) && (befs_sb->block_size != 8192)) { befs_error(sb, "invalid blocksize: %u", befs_sb->block_size); return BEFS_ERR; } if (befs_sb->block_size > PAGE_SIZE) { befs_error(sb, "blocksize(%u) cannot be larger" "than system pagesize(%lu)", befs_sb->block_size, PAGE_SIZE); return BEFS_ERR; } /* * block_shift and block_size encode the same information * in different ways as a consistency check. */ if ((1 << befs_sb->block_shift) != befs_sb->block_size) { befs_error(sb, "block_shift disagrees with block_size. " "Corruption likely."); return BEFS_ERR; } if (befs_sb->log_start != befs_sb->log_end) { befs_error(sb, "Filesystem not clean! There are blocks in the " "journal. You must boot into BeOS and mount this volume " "to make it clean."); return BEFS_ERR; } return BEFS_OK; } linux-2.6.17/fs/befs/.svn/text-base/befs_fs_types.h.svn-base0000444000000000000000000000752210574207632022306 0ustar rootroot/* * include/linux/befs_fs_types.h * * Copyright (C) 2001 Will Dyson (will@cs.earlham.edu) * * * * from linux/include/linux/befs_fs.h * * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp) * */ #ifndef _LINUX_BEFS_FS_TYPES #define _LINUX_BEFS_FS_TYPES #ifdef __KERNEL__ #include #endif /*__KERNEL__*/ #define PACKED __attribute__ ((__packed__)) /* * Max name lengths of BFS */ #define BEFS_NAME_LEN 255 #define BEFS_SYMLINK_LEN 144 #define BEFS_NUM_DIRECT_BLOCKS 12 #define B_OS_NAME_LENGTH 32 /* The datastream blocks mapped by the double-indirect * block are always 4 fs blocks long. * This eliminates the need for linear searches among * the potentially huge number of indirect blocks * * Err. Should that be 4 fs blocks or 4k??? * It matters on large blocksize volumes */ #define BEFS_DBLINDIR_BRUN_LEN 4 /* * Flags of superblock */ enum super_flags { BEFS_BYTESEX_BE, BEFS_BYTESEX_LE, BEFS_CLEAN = 0x434c454e, BEFS_DIRTY = 0x44495254, BEFS_SUPER_MAGIC1 = 0x42465331, /* BFS1 */ BEFS_SUPER_MAGIC2 = 0xdd121031, BEFS_SUPER_MAGIC3 = 0x15b6830e, }; #define BEFS_BYTEORDER_NATIVE 0x42494745 #define BEFS_SUPER_MAGIC BEFS_SUPER_MAGIC1 /* * Flags of inode */ #define BEFS_INODE_MAGIC1 0x3bbe0ad9 enum inode_flags { BEFS_INODE_IN_USE = 0x00000001, BEFS_ATTR_INODE = 0x00000004, BEFS_INODE_LOGGED = 0x00000008, BEFS_INODE_DELETED = 0x00000010, BEFS_LONG_SYMLINK = 0x00000040, BEFS_PERMANENT_FLAG = 0x0000ffff, BEFS_INODE_NO_CREATE = 0x00010000, BEFS_INODE_WAS_WRITTEN = 0x00020000, BEFS_NO_TRANSACTION = 0x00040000, }; /* * On-Disk datastructures of BeFS */ typedef u64 befs_off_t; typedef u64 befs_time_t; typedef void befs_binode_etc; /* Block runs */ typedef struct { u32 allocation_group; u16 start; u16 len; } PACKED befs_block_run; typedef befs_block_run befs_inode_addr; /* * The Superblock Structure */ typedef struct { char name[B_OS_NAME_LENGTH]; u32 magic1; u32 fs_byte_order; u32 block_size; u32 block_shift; befs_off_t num_blocks; befs_off_t used_blocks; u32 inode_size; u32 magic2; u32 blocks_per_ag; u32 ag_shift; u32 num_ags; u32 flags; befs_block_run log_blocks; befs_off_t log_start; befs_off_t log_end; u32 magic3; befs_inode_addr root_dir; befs_inode_addr indices; } PACKED befs_super_block; /* * Note: the indirect and dbl_indir block_runs may * be longer than one block! */ typedef struct { befs_block_run direct[BEFS_NUM_DIRECT_BLOCKS]; befs_off_t max_direct_range; befs_block_run indirect; befs_off_t max_indirect_range; befs_block_run double_indirect; befs_off_t max_double_indirect_range; befs_off_t size; } PACKED befs_data_stream; /* Attribute */ typedef struct { u32 type; u16 name_size; u16 data_size; char name[1]; } PACKED befs_small_data; /* Inode structure */ typedef struct { u32 magic1; befs_inode_addr inode_num; u32 uid; u32 gid; u32 mode; u32 flags; befs_time_t create_time; befs_time_t last_modified_time; befs_inode_addr parent; befs_inode_addr attributes; u32 type; u32 inode_size; u32 etc; /* not use */ union { befs_data_stream datastream; char symlink[BEFS_SYMLINK_LEN]; } data; u32 pad[4]; /* not use */ befs_small_data small_data[1]; } PACKED befs_inode; /* * B+tree superblock */ #define BEFS_BTREE_MAGIC 0x69f6c2e8 enum btree_types { BTREE_STRING_TYPE = 0, BTREE_INT32_TYPE = 1, BTREE_UINT32_TYPE = 2, BTREE_INT64_TYPE = 3, BTREE_UINT64_TYPE = 4, BTREE_FLOAT_TYPE = 5, BTREE_DOUBLE_TYPE = 6 }; typedef struct { u32 magic; u32 node_size; u32 max_depth; u32 data_type; befs_off_t root_node_ptr; befs_off_t free_node_ptr; befs_off_t max_size; } PACKED befs_btree_super; /* * Header stucture of each btree node */ typedef struct { befs_off_t left; befs_off_t right; befs_off_t overflow; u16 all_key_count; u16 all_key_length; } PACKED befs_btree_nodehead; #endif /* _LINUX_BEFS_FS_TYPES */ linux-2.6.17/fs/befs/.svn/text-base/inode.c.svn-base0000444000000000000000000000222510574207632020537 0ustar rootroot/* * inode.c * * Copyright (C) 2001 Will Dyson */ #include #include "befs.h" #include "inode.h" #include "endian.h" /* Validates the correctness of the befs inode Returns BEFS_OK if the inode should be used, otherwise returns BEFS_BAD_INODE */ int befs_check_inode(struct super_block *sb, befs_inode * raw_inode, befs_blocknr_t inode) { u32 magic1 = fs32_to_cpu(sb, raw_inode->magic1); befs_inode_addr ino_num = fsrun_to_cpu(sb, raw_inode->inode_num); u32 flags = fs32_to_cpu(sb, raw_inode->flags); /* check magic header. */ if (magic1 != BEFS_INODE_MAGIC1) { befs_error(sb, "Inode has a bad magic header - inode = %lu", inode); return BEFS_BAD_INODE; } /* * Sanity check2: inodes store their own block address. Check it. */ if (inode != iaddr2blockno(sb, &ino_num)) { befs_error(sb, "inode blocknr field disagrees with vfs " "VFS: %lu, Inode %lu", inode, iaddr2blockno(sb, &ino_num)); return BEFS_BAD_INODE; } /* * check flag */ if (!(flags & BEFS_INODE_IN_USE)) { befs_error(sb, "inode is not used - inode = %lu", inode); return BEFS_BAD_INODE; } return BEFS_OK; } linux-2.6.17/fs/befs/.svn/text-base/debug.c.svn-base0000444000000000000000000001706510574207632020537 0ustar rootroot/* * linux/fs/befs/debug.c * * Copyright (C) 2001 Will Dyson (will_dyson at pobox.com) * * With help from the ntfs-tng driver by Anton Altparmakov * * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp) * * debug functions */ #ifdef __KERNEL__ #include #include #include #include #include #endif /* __KERNEL__ */ #include "befs.h" #include "endian.h" #define ERRBUFSIZE 1024 void befs_error(const struct super_block *sb, const char *fmt, ...) { va_list args; char *err_buf = (char *) kmalloc(ERRBUFSIZE, GFP_KERNEL); if (err_buf == NULL) { printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); return; } va_start(args, fmt); vsnprintf(err_buf, ERRBUFSIZE, fmt, args); va_end(args); printk(KERN_ERR "BeFS(%s): %s\n", sb->s_id, err_buf); kfree(err_buf); } void befs_warning(const struct super_block *sb, const char *fmt, ...) { va_list args; char *err_buf = (char *) kmalloc(ERRBUFSIZE, GFP_KERNEL); if (err_buf == NULL) { printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); return; } va_start(args, fmt); vsnprintf(err_buf, ERRBUFSIZE, fmt, args); va_end(args); printk(KERN_WARNING "BeFS(%s): %s\n", sb->s_id, err_buf); kfree(err_buf); } void befs_debug(const struct super_block *sb, const char *fmt, ...) { #ifdef CONFIG_BEFS_DEBUG va_list args; char *err_buf = NULL; if (BEFS_SB(sb)->mount_opts.debug) { err_buf = (char *) kmalloc(ERRBUFSIZE, GFP_KERNEL); if (err_buf == NULL) { printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); return; } va_start(args, fmt); vsnprintf(err_buf, ERRBUFSIZE, fmt, args); va_end(args); printk(KERN_DEBUG "BeFS(%s): %s\n", sb->s_id, err_buf); kfree(err_buf); } #endif //CONFIG_BEFS_DEBUG } void befs_dump_inode(const struct super_block *sb, befs_inode * inode) { #ifdef CONFIG_BEFS_DEBUG befs_block_run tmp_run; befs_debug(sb, "befs_inode information"); befs_debug(sb, " magic1 %08x", fs32_to_cpu(sb, inode->magic1)); tmp_run = fsrun_to_cpu(sb, inode->inode_num); befs_debug(sb, " inode_num %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " uid %u", fs32_to_cpu(sb, inode->uid)); befs_debug(sb, " gid %u", fs32_to_cpu(sb, inode->gid)); befs_debug(sb, " mode %08x", fs32_to_cpu(sb, inode->mode)); befs_debug(sb, " flags %08x", fs32_to_cpu(sb, inode->flags)); befs_debug(sb, " create_time %Lu", fs64_to_cpu(sb, inode->create_time)); befs_debug(sb, " last_modified_time %Lu", fs64_to_cpu(sb, inode->last_modified_time)); tmp_run = fsrun_to_cpu(sb, inode->parent); befs_debug(sb, " parent [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); tmp_run = fsrun_to_cpu(sb, inode->attributes); befs_debug(sb, " attributes [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " type %08x", fs32_to_cpu(sb, inode->type)); befs_debug(sb, " inode_size %u", fs32_to_cpu(sb, inode->inode_size)); if (S_ISLNK(inode->mode)) { befs_debug(sb, " Symbolic link [%s]", inode->data.symlink); } else { int i; for (i = 0; i < BEFS_NUM_DIRECT_BLOCKS; i++) { tmp_run = fsrun_to_cpu(sb, inode->data.datastream.direct[i]); befs_debug(sb, " direct %d [%u, %hu, %hu]", i, tmp_run.allocation_group, tmp_run.start, tmp_run.len); } befs_debug(sb, " max_direct_range %Lu", fs64_to_cpu(sb, inode->data.datastream. max_direct_range)); tmp_run = fsrun_to_cpu(sb, inode->data.datastream.indirect); befs_debug(sb, " indirect [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " max_indirect_range %Lu", fs64_to_cpu(sb, inode->data.datastream. max_indirect_range)); tmp_run = fsrun_to_cpu(sb, inode->data.datastream.double_indirect); befs_debug(sb, " double indirect [%u, %hu, %hu]", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " max_double_indirect_range %Lu", fs64_to_cpu(sb, inode->data.datastream. max_double_indirect_range)); befs_debug(sb, " size %Lu", fs64_to_cpu(sb, inode->data.datastream.size)); } #endif //CONFIG_BEFS_DEBUG } /* * Display super block structure for debug. */ void befs_dump_super_block(const struct super_block *sb, befs_super_block * sup) { #ifdef CONFIG_BEFS_DEBUG befs_block_run tmp_run; befs_debug(sb, "befs_super_block information"); befs_debug(sb, " name %s", sup->name); befs_debug(sb, " magic1 %08x", fs32_to_cpu(sb, sup->magic1)); befs_debug(sb, " fs_byte_order %08x", fs32_to_cpu(sb, sup->fs_byte_order)); befs_debug(sb, " block_size %u", fs32_to_cpu(sb, sup->block_size)); befs_debug(sb, " block_shift %u", fs32_to_cpu(sb, sup->block_shift)); befs_debug(sb, " num_blocks %Lu", fs64_to_cpu(sb, sup->num_blocks)); befs_debug(sb, " used_blocks %Lu", fs64_to_cpu(sb, sup->used_blocks)); befs_debug(sb, " magic2 %08x", fs32_to_cpu(sb, sup->magic2)); befs_debug(sb, " blocks_per_ag %u", fs32_to_cpu(sb, sup->blocks_per_ag)); befs_debug(sb, " ag_shift %u", fs32_to_cpu(sb, sup->ag_shift)); befs_debug(sb, " num_ags %u", fs32_to_cpu(sb, sup->num_ags)); befs_debug(sb, " flags %08x", fs32_to_cpu(sb, sup->flags)); tmp_run = fsrun_to_cpu(sb, sup->log_blocks); befs_debug(sb, " log_blocks %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); befs_debug(sb, " log_start %Ld", fs64_to_cpu(sb, sup->log_start)); befs_debug(sb, " log_end %Ld", fs64_to_cpu(sb, sup->log_end)); befs_debug(sb, " magic3 %08x", fs32_to_cpu(sb, sup->magic3)); tmp_run = fsrun_to_cpu(sb, sup->root_dir); befs_debug(sb, " root_dir %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); tmp_run = fsrun_to_cpu(sb, sup->indices); befs_debug(sb, " indices %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); #endif //CONFIG_BEFS_DEBUG } #if 0 /* unused */ void befs_dump_small_data(const struct super_block *sb, befs_small_data * sd) { } /* unused */ void befs_dump_run(const struct super_block *sb, befs_block_run run) { #ifdef CONFIG_BEFS_DEBUG run = fsrun_to_cpu(sb, run); befs_debug(sb, "[%u, %hu, %hu]", run.allocation_group, run.start, run.len); #endif //CONFIG_BEFS_DEBUG } #endif /* 0 */ void befs_dump_index_entry(const struct super_block *sb, befs_btree_super * super) { #ifdef CONFIG_BEFS_DEBUG befs_debug(sb, "Btree super structure"); befs_debug(sb, " magic %08x", fs32_to_cpu(sb, super->magic)); befs_debug(sb, " node_size %u", fs32_to_cpu(sb, super->node_size)); befs_debug(sb, " max_depth %08x", fs32_to_cpu(sb, super->max_depth)); befs_debug(sb, " data_type %08x", fs32_to_cpu(sb, super->data_type)); befs_debug(sb, " root_node_pointer %016LX", fs64_to_cpu(sb, super->root_node_ptr)); befs_debug(sb, " free_node_pointer %016LX", fs64_to_cpu(sb, super->free_node_ptr)); befs_debug(sb, " maximum size %016LX", fs64_to_cpu(sb, super->max_size)); #endif //CONFIG_BEFS_DEBUG } void befs_dump_index_node(const struct super_block *sb, befs_btree_nodehead * node) { #ifdef CONFIG_BEFS_DEBUG befs_debug(sb, "Btree node structure"); befs_debug(sb, " left %016LX", fs64_to_cpu(sb, node->left)); befs_debug(sb, " right %016LX", fs64_to_cpu(sb, node->right)); befs_debug(sb, " overflow %016LX", fs64_to_cpu(sb, node->overflow)); befs_debug(sb, " all_key_count %hu", fs16_to_cpu(sb, node->all_key_count)); befs_debug(sb, " all_key_length %hu", fs16_to_cpu(sb, node->all_key_length)); #endif //CONFIG_BEFS_DEBUG } linux-2.6.17/fs/befs/.svn/text-base/io.h.svn-base0000444000000000000000000000027610574207632020061 0ustar rootroot/* * io.h */ struct buffer_head *befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr); struct buffer_head *befs_bread(struct super_block *sb, befs_blocknr_t block); linux-2.6.17/fs/befs/.svn/text-base/TODO.svn-base0000444000000000000000000000060110574207632017761 0ustar rootrootTODO ========== * Convert comments to the Kernel-Doc format. * Befs_fs.h has gotten big and messy. No reason not to break it up into smaller peices. * See if Alexander Viro's option parser made it into the kernel tree. Use that if we can. (include/linux/parser.h) * See if we really need separate types for on-disk and in-memory representations of the superblock and inode. linux-2.6.17/fs/befs/.svn/prop-base/0000755000000000000000000000000010574207632015555 5ustar rootrootlinux-2.6.17/fs/befs/endian.h0000644000000000000000000000552610564537530014422 0ustar rootroot/* * linux/fs/befs/endian.h * * Copyright (C) 2001 Will Dyson * * Partially based on similar funtions in the sysv driver. */ #ifndef LINUX_BEFS_ENDIAN #define LINUX_BEFS_ENDIAN #include #include "befs.h" static inline u64 fs64_to_cpu(const struct super_block *sb, u64 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return le64_to_cpu(n); else return be64_to_cpu(n); } static inline u64 cpu_to_fs64(const struct super_block *sb, u64 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return cpu_to_le64(n); else return cpu_to_be64(n); } static inline u32 fs32_to_cpu(const struct super_block *sb, u32 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return le32_to_cpu(n); else return be32_to_cpu(n); } static inline u32 cpu_to_fs32(const struct super_block *sb, u32 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return cpu_to_le32(n); else return cpu_to_be32(n); } static inline u16 fs16_to_cpu(const struct super_block *sb, u16 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return le16_to_cpu(n); else return be16_to_cpu(n); } static inline u16 cpu_to_fs16(const struct super_block *sb, u16 n) { if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) return cpu_to_le16(n); else return cpu_to_be16(n); } /* Composite types below here */ static inline befs_block_run fsrun_to_cpu(const struct super_block *sb, befs_block_run n) { befs_block_run run; if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) { run.allocation_group = le32_to_cpu(n.allocation_group); run.start = le16_to_cpu(n.start); run.len = le16_to_cpu(n.len); } else { run.allocation_group = be32_to_cpu(n.allocation_group); run.start = be16_to_cpu(n.start); run.len = be16_to_cpu(n.len); } return run; } static inline befs_block_run cpu_to_fsrun(const struct super_block *sb, befs_block_run n) { befs_block_run run; if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) { run.allocation_group = cpu_to_le32(n.allocation_group); run.start = cpu_to_le16(n.start); run.len = cpu_to_le16(n.len); } else { run.allocation_group = cpu_to_be32(n.allocation_group); run.start = cpu_to_be16(n.start); run.len = cpu_to_be16(n.len); } return run; } static inline befs_data_stream fsds_to_cpu(const struct super_block *sb, befs_data_stream n) { befs_data_stream data; int i; for (i = 0; i < BEFS_NUM_DIRECT_BLOCKS; ++i) data.direct[i] = fsrun_to_cpu(sb, n.direct[i]); data.max_direct_range = fs64_to_cpu(sb, n.max_direct_range); data.indirect = fsrun_to_cpu(sb, n.indirect); data.max_indirect_range = fs64_to_cpu(sb, n.max_indirect_range); data.double_indirect = fsrun_to_cpu(sb, n.double_indirect); data.max_double_indirect_range = fs64_to_cpu(sb, n. max_double_indirect_range); data.size = fs64_to_cpu(sb, n.size); return data; } #endif //LINUX_BEFS_ENDIAN linux-2.6.17/fs/befs/linuxvfs.c0000644000000000000000000005723710564537530015043 0ustar rootroot/* * linux/fs/befs/linuxvfs.c * * Copyright (C) 2001 Will Dyson #include #include #include #include #include #include #include #include #include #include "befs.h" #include "btree.h" #include "inode.h" #include "datastream.h" #include "super.h" #include "io.h" #include "endian.h" MODULE_DESCRIPTION("BeOS File System (BeFS) driver"); MODULE_AUTHOR("Will Dyson"); MODULE_LICENSE("GPL"); /* The units the vfs expects inode->i_blocks to be in */ #define VFS_BLOCK_SIZE 512 static int befs_readdir(struct file *, void *, filldir_t); static int befs_get_block(struct inode *, sector_t, struct buffer_head *, int); static int befs_readpage(struct file *file, struct page *page); static sector_t befs_bmap(struct address_space *mapping, sector_t block); static struct dentry *befs_lookup(struct inode *, struct dentry *, struct nameidata *); static void befs_read_inode(struct inode *ino); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static int befs_init_inodecache(void); static void befs_destroy_inodecache(void); static void *befs_follow_link(struct dentry *, struct nameidata *); static void befs_put_link(struct dentry *, struct nameidata *, void *); static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static void befs_put_super(struct super_block *); static int befs_remount(struct super_block *, int *, char *); static int befs_statfs(struct super_block *, struct kstatfs *); static int parse_options(char *, befs_mount_options *); static const struct super_operations befs_sops = { .read_inode = befs_read_inode, /* initialize & read inode */ .alloc_inode = befs_alloc_inode, /* allocate a new inode */ .destroy_inode = befs_destroy_inode, /* deallocate an inode */ .put_super = befs_put_super, /* uninit super */ .statfs = befs_statfs, /* statfs */ .remount_fs = befs_remount, }; /* slab cache for befs_inode_info objects */ static kmem_cache_t *befs_inode_cachep; static const struct file_operations befs_dir_operations = { .read = generic_read_dir, .readdir = befs_readdir, }; static struct inode_operations befs_dir_inode_operations = { .lookup = befs_lookup, }; static struct address_space_operations befs_aops = { .readpage = befs_readpage, .sync_page = block_sync_page, .bmap = befs_bmap, }; static struct inode_operations befs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = befs_follow_link, .put_link = befs_put_link, }; /* * Called by generic_file_read() to read a page of data * * In turn, simply calls a generic block read function and * passes it the address of befs_get_block, for mapping file * positions to disk blocks. */ static int befs_readpage(struct file *file, struct page *page) { return block_read_full_page(page, befs_get_block); } static sector_t befs_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping, block, befs_get_block); } /* * Generic function to map a file position (block) to a * disk offset (passed back in bh_result). * * Used by many higher level functions. * * Calls befs_fblock2brun() in datastream.c to do the real work. * * -WD 10-26-01 */ static int befs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) { struct super_block *sb = inode->i_sb; befs_data_stream *ds = &BEFS_I(inode)->i_data.ds; befs_block_run run = BAD_IADDR; int res = 0; ulong disk_off; befs_debug(sb, "---> befs_get_block() for inode %lu, block %ld", inode->i_ino, block); if (block < 0) { befs_error(sb, "befs_get_block() was asked for a block " "number less than zero: block %ld in inode %lu", block, inode->i_ino); return -EIO; } if (create) { befs_error(sb, "befs_get_block() was asked to write to " "block %ld in inode %lu", block, inode->i_ino); return -EPERM; } res = befs_fblock2brun(sb, ds, block, &run); if (res != BEFS_OK) { befs_error(sb, "<--- befs_get_block() for inode %lu, block " "%ld ERROR", inode->i_ino, block); return -EFBIG; } disk_off = (ulong) iaddr2blockno(sb, &run); map_bh(bh_result, inode->i_sb, disk_off); befs_debug(sb, "<--- befs_get_block() for inode %lu, block %ld, " "disk address %lu", inode->i_ino, block, disk_off); return 0; } static struct dentry * befs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct inode *inode = NULL; struct super_block *sb = dir->i_sb; befs_data_stream *ds = &BEFS_I(dir)->i_data.ds; befs_off_t offset; int ret; int utfnamelen; char *utfname; const char *name = dentry->d_name.name; befs_debug(sb, "---> befs_lookup() " "name %s inode %ld", dentry->d_name.name, dir->i_ino); /* Convert to UTF-8 */ if (BEFS_SB(sb)->nls) { ret = befs_nls2utf(sb, name, strlen(name), &utfname, &utfnamelen); if (ret < 0) { befs_debug(sb, "<--- befs_lookup() ERROR"); return ERR_PTR(ret); } ret = befs_btree_find(sb, ds, utfname, &offset); kfree(utfname); } else { ret = befs_btree_find(sb, ds, dentry->d_name.name, &offset); } if (ret == BEFS_BT_NOT_FOUND) { befs_debug(sb, "<--- befs_lookup() %s not found", dentry->d_name.name); return ERR_PTR(-ENOENT); } else if (ret != BEFS_OK || offset == 0) { befs_warning(sb, "<--- befs_lookup() Error"); return ERR_PTR(-ENODATA); } inode = iget(dir->i_sb, (ino_t) offset); if (!inode) return ERR_PTR(-EACCES); d_add(dentry, inode); befs_debug(sb, "<--- befs_lookup()"); return NULL; } static int befs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; befs_data_stream *ds = &BEFS_I(inode)->i_data.ds; befs_off_t value; int result; size_t keysize; unsigned char d_type; char keybuf[BEFS_NAME_LEN + 1]; char *nlsname; int nlsnamelen; const char *dirname = filp->f_dentry->d_name.name; befs_debug(sb, "---> befs_readdir() " "name %s, inode %ld, filp->f_pos %Ld", dirname, inode->i_ino, filp->f_pos); result = befs_btree_read(sb, ds, filp->f_pos, BEFS_NAME_LEN + 1, keybuf, &keysize, &value); if (result == BEFS_ERR) { befs_debug(sb, "<--- befs_readdir() ERROR"); befs_error(sb, "IO error reading %s (inode %lu)", dirname, inode->i_ino); return -EIO; } else if (result == BEFS_BT_END) { befs_debug(sb, "<--- befs_readdir() END"); return 0; } else if (result == BEFS_BT_EMPTY) { befs_debug(sb, "<--- befs_readdir() Empty directory"); return 0; } d_type = DT_UNKNOWN; /* Convert to NLS */ if (BEFS_SB(sb)->nls) { result = befs_utf2nls(sb, keybuf, keysize, &nlsname, &nlsnamelen); if (result < 0) { befs_debug(sb, "<--- befs_readdir() ERROR"); return result; } result = filldir(dirent, nlsname, nlsnamelen, filp->f_pos, (ino_t) value, d_type); kfree(nlsname); } else { result = filldir(dirent, keybuf, keysize, filp->f_pos, (ino_t) value, d_type); } filp->f_pos++; befs_debug(sb, "<--- befs_readdir() filp->f_pos %Ld", filp->f_pos); return 0; } static struct inode * befs_alloc_inode(struct super_block *sb) { struct befs_inode_info *bi; bi = (struct befs_inode_info *)kmem_cache_alloc(befs_inode_cachep, SLAB_KERNEL); if (!bi) return NULL; return &bi->vfs_inode; } static void befs_destroy_inode(struct inode *inode) { kmem_cache_free(befs_inode_cachep, BEFS_I(inode)); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct befs_inode_info *bi = (struct befs_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { inode_init_once(&bi->vfs_inode); } } static void befs_read_inode(struct inode *inode) { struct buffer_head *bh = NULL; befs_inode *raw_inode = NULL; struct super_block *sb = inode->i_sb; befs_sb_info *befs_sb = BEFS_SB(sb); befs_inode_info *befs_ino = NULL; befs_debug(sb, "---> befs_read_inode() " "inode = %lu", inode->i_ino); befs_ino = BEFS_I(inode); /* convert from vfs's inode number to befs's inode number */ befs_ino->i_inode_num = blockno2iaddr(sb, inode->i_ino); befs_debug(sb, " real inode number [%u, %hu, %hu]", befs_ino->i_inode_num.allocation_group, befs_ino->i_inode_num.start, befs_ino->i_inode_num.len); bh = befs_bread(sb, inode->i_ino); if (!bh) { befs_error(sb, "unable to read inode block - " "inode = %lu", inode->i_ino); goto unaquire_none; } raw_inode = (befs_inode *) bh->b_data; befs_dump_inode(sb, raw_inode); if (befs_check_inode(sb, raw_inode, inode->i_ino) != BEFS_OK) { befs_error(sb, "Bad inode: %lu", inode->i_ino); goto unaquire_bh; } inode->i_mode = (umode_t) fs32_to_cpu(sb, raw_inode->mode); /* * set uid and gid. But since current BeOS is single user OS, so * you can change by "uid" or "gid" options. */ inode->i_uid = befs_sb->mount_opts.use_uid ? befs_sb->mount_opts.uid : (uid_t) fs32_to_cpu(sb, raw_inode->uid); inode->i_gid = befs_sb->mount_opts.use_gid ? befs_sb->mount_opts.gid : (gid_t) fs32_to_cpu(sb, raw_inode->gid); inode->i_nlink = 1; /* * BEFS's time is 64 bits, but current VFS is 32 bits... * BEFS don't have access time. Nor inode change time. VFS * doesn't have creation time. * Also, the lower 16 bits of the last_modified_time and * create_time are just a counter to help ensure uniqueness * for indexing purposes. (PFD, page 54) */ inode->i_mtime.tv_sec = fs64_to_cpu(sb, raw_inode->last_modified_time) >> 16; inode->i_mtime.tv_nsec = 0; /* lower 16 bits are not a time */ inode->i_ctime = inode->i_mtime; inode->i_atime = inode->i_mtime; inode->i_blksize = befs_sb->block_size; befs_ino->i_inode_num = fsrun_to_cpu(sb, raw_inode->inode_num); befs_ino->i_parent = fsrun_to_cpu(sb, raw_inode->parent); befs_ino->i_attribute = fsrun_to_cpu(sb, raw_inode->attributes); befs_ino->i_flags = fs32_to_cpu(sb, raw_inode->flags); if (S_ISLNK(inode->i_mode) && !(befs_ino->i_flags & BEFS_LONG_SYMLINK)){ inode->i_size = 0; inode->i_blocks = befs_sb->block_size / VFS_BLOCK_SIZE; strncpy(befs_ino->i_data.symlink, raw_inode->data.symlink, BEFS_SYMLINK_LEN); } else { int num_blks; befs_ino->i_data.ds = fsds_to_cpu(sb, raw_inode->data.datastream); num_blks = befs_count_blocks(sb, &befs_ino->i_data.ds); inode->i_blocks = num_blks * (befs_sb->block_size / VFS_BLOCK_SIZE); inode->i_size = befs_ino->i_data.ds.size; } inode->i_mapping->a_ops = &befs_aops; if (S_ISREG(inode->i_mode)) { inode->i_fop = &generic_ro_fops; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &befs_dir_inode_operations; inode->i_fop = &befs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &befs_symlink_inode_operations; } else { befs_error(sb, "Inode %lu is not a regular file, " "directory or symlink. THAT IS WRONG! BeFS has no " "on disk special files", inode->i_ino); goto unaquire_bh; } brelse(bh); befs_debug(sb, "<--- befs_read_inode()"); return; unaquire_bh: brelse(bh); unaquire_none: make_bad_inode(inode); befs_debug(sb, "<--- befs_read_inode() - Bad inode"); return; } /* Initialize the inode cache. Called at fs setup. * * Taken from NFS implementation by Al Viro. */ static int befs_init_inodecache(void) { befs_inode_cachep = kmem_cache_create("befs_inode_cache", sizeof (struct befs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), init_once, NULL); if (befs_inode_cachep == NULL) { printk(KERN_ERR "befs_init_inodecache: " "Couldn't initalize inode slabcache\n"); return -ENOMEM; } return 0; } /* Called at fs teardown. * * Taken from NFS implementation by Al Viro. */ static void befs_destroy_inodecache(void) { if (kmem_cache_destroy(befs_inode_cachep)) printk(KERN_ERR "befs_destroy_inodecache: " "not all structures were freed\n"); } /* * The inode of symbolic link is different to data stream. * The data stream become link name. Unless the LONG_SYMLINK * flag is set. */ static void * befs_follow_link(struct dentry *dentry, struct nameidata *nd) { befs_inode_info *befs_ino = BEFS_I(dentry->d_inode); char *link; if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { struct super_block *sb = dentry->d_sb; befs_data_stream *data = &befs_ino->i_data.ds; befs_off_t len = data->size; befs_debug(sb, "Follow long symlink"); link = kmalloc(len, GFP_NOFS); if (!link) { link = ERR_PTR(-ENOMEM); } else if (befs_read_lsymlink(sb, data, link, len) != len) { kfree(link); befs_error(sb, "Failed to read entire long symlink"); link = ERR_PTR(-EIO); } } else { link = befs_ino->i_data.symlink; } nd_set_link(nd, link); return NULL; } static void befs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) { befs_inode_info *befs_ino = BEFS_I(dentry->d_inode); if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { char *p = nd_get_link(nd); if (!IS_ERR(p)) kfree(p); } } /* * UTF-8 to NLS charset convert routine * * * Changed 8/10/01 by Will Dyson. Now use uni2char() / char2uni() rather than * the nls tables directly */ static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len) { struct nls_table *nls = BEFS_SB(sb)->nls; int i, o; wchar_t uni; int unilen, utflen; char *result; /* The utf8->nls conversion won't make the final nls string bigger * than the utf one, but if the string is pure ascii they'll have the * same width and an extra char is needed to save the additional \0 */ int maxlen = in_len + 1; befs_debug(sb, "---> utf2nls()"); if (!nls) { befs_error(sb, "befs_utf2nls called with no NLS table loaded"); return -EINVAL; } *out = result = kmalloc(maxlen, GFP_NOFS); if (!*out) { befs_error(sb, "befs_utf2nls() cannot allocate memory"); *out_len = 0; return -ENOMEM; } for (i = o = 0; i < in_len; i += utflen, o += unilen) { /* convert from UTF-8 to Unicode */ utflen = utf8_mbtowc(&uni, &in[i], in_len - i); if (utflen < 0) { goto conv_err; } /* convert from Unicode to nls */ unilen = nls->uni2char(uni, &result[o], in_len - o); if (unilen < 0) { goto conv_err; } } result[o] = '\0'; *out_len = o; befs_debug(sb, "<--- utf2nls()"); return o; conv_err: befs_error(sb, "Name using character set %s contains a character that " "cannot be converted to unicode.", nls->charset); befs_debug(sb, "<--- utf2nls()"); kfree(result); return -EILSEQ; } /** * befs_nls2utf - Convert NLS string to utf8 encodeing * @sb: Superblock * @src: Input string buffer in NLS format * @srclen: Length of input string in bytes * @dest: The output string in UTF-8 format * @destlen: Length of the output buffer * * Converts input string @src, which is in the format of the loaded NLS map, * into a utf8 string. * * The destination string @dest is allocated by this function and the caller is * responsible for freeing it with kfree() * * On return, *@destlen is the length of @dest in bytes. * * On success, the return value is the number of utf8 characters written to * the output buffer @dest. * * On Failure, a negative number coresponding to the error code is returned. */ static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, char **out, int *out_len) { struct nls_table *nls = BEFS_SB(sb)->nls; int i, o; wchar_t uni; int unilen, utflen; char *result; /* There're nls characters that will translate to 3-chars-wide UTF-8 * characters, a additional byte is needed to save the final \0 * in special cases */ int maxlen = (3 * in_len) + 1; befs_debug(sb, "---> nls2utf()\n"); if (!nls) { befs_error(sb, "befs_nls2utf called with no NLS table loaded."); return -EINVAL; } *out = result = kmalloc(maxlen, GFP_NOFS); if (!*out) { befs_error(sb, "befs_nls2utf() cannot allocate memory"); *out_len = 0; return -ENOMEM; } for (i = o = 0; i < in_len; i += unilen, o += utflen) { /* convert from nls to unicode */ unilen = nls->char2uni(&in[i], in_len - i, &uni); if (unilen < 0) { goto conv_err; } /* convert from unicode to UTF-8 */ utflen = utf8_wctomb(&result[o], uni, 3); if (utflen <= 0) { goto conv_err; } } result[o] = '\0'; *out_len = o; befs_debug(sb, "<--- nls2utf()"); return i; conv_err: befs_error(sb, "Name using charecter set %s contains a charecter that " "cannot be converted to unicode.", nls->charset); befs_debug(sb, "<--- nls2utf()"); kfree(result); return -EILSEQ; } /** * Use the * */ enum { Opt_uid, Opt_gid, Opt_charset, Opt_debug, Opt_err, }; static match_table_t befs_tokens = { {Opt_uid, "uid=%d"}, {Opt_gid, "gid=%d"}, {Opt_charset, "iocharset=%s"}, {Opt_debug, "debug"}, {Opt_err, NULL} }; static int parse_options(char *options, befs_mount_options * opts) { char *p; substring_t args[MAX_OPT_ARGS]; int option; /* Initialize options */ opts->uid = 0; opts->gid = 0; opts->use_uid = 0; opts->use_gid = 0; opts->iocharset = NULL; opts->debug = 0; if (!options) return 1; while ((p = strsep(&options, ",")) != NULL) { int token; if (!*p) continue; token = match_token(p, befs_tokens, args); switch (token) { case Opt_uid: if (match_int(&args[0], &option)) return 0; if (option < 0) { printk(KERN_ERR "BeFS: Invalid uid %d, " "using default\n", option); break; } opts->uid = option; opts->use_uid = 1; break; case Opt_gid: if (match_int(&args[0], &option)) return 0; if (option < 0) { printk(KERN_ERR "BeFS: Invalid gid %d, " "using default\n", option); break; } opts->gid = option; opts->use_gid = 1; break; case Opt_charset: kfree(opts->iocharset); opts->iocharset = match_strdup(&args[0]); if (!opts->iocharset) { printk(KERN_ERR "BeFS: allocation failure for " "iocharset string\n"); return 0; } break; case Opt_debug: opts->debug = 1; break; default: printk(KERN_ERR "BeFS: Unrecognized mount option \"%s\" " "or missing value\n", p); return 0; } } return 1; } /* This function has the responsibiltiy of getting the * filesystem ready for unmounting. * Basicly, we free everything that we allocated in * befs_read_inode */ static void befs_put_super(struct super_block *sb) { kfree(BEFS_SB(sb)->mount_opts.iocharset); BEFS_SB(sb)->mount_opts.iocharset = NULL; if (BEFS_SB(sb)->nls) { unload_nls(BEFS_SB(sb)->nls); BEFS_SB(sb)->nls = NULL; } kfree(sb->s_fs_info); sb->s_fs_info = NULL; return; } /* Allocate private field of the superblock, fill it. * * Finish filling the public superblock fields * Make the root directory * Load a set of NLS translations if needed. */ static int befs_fill_super(struct super_block *sb, void *data, int silent) { struct buffer_head *bh; befs_sb_info *befs_sb; befs_super_block *disk_sb; struct inode *root; const unsigned long sb_block = 0; const off_t x86_sb_off = 512; sb->s_fs_info = kmalloc(sizeof (*befs_sb), GFP_KERNEL); if (sb->s_fs_info == NULL) { printk(KERN_ERR "BeFS(%s): Unable to allocate memory for private " "portion of superblock. Bailing.\n", sb->s_id); goto unaquire_none; } befs_sb = BEFS_SB(sb); memset(befs_sb, 0, sizeof(befs_sb_info)); if (!parse_options((char *) data, &befs_sb->mount_opts)) { befs_error(sb, "cannot parse mount options"); goto unaquire_priv_sbp; } befs_debug(sb, "---> befs_fill_super()"); #ifndef CONFIG_BEFS_RW if (!(sb->s_flags & MS_RDONLY)) { befs_warning(sb, "No write support. Marking filesystem read-only"); sb->s_flags |= MS_RDONLY; } #endif /* CONFIG_BEFS_RW */ /* * Set dummy blocksize to read super block. * Will be set to real fs blocksize later. * * Linux 2.4.10 and later refuse to read blocks smaller than * the hardsect size for the device. But we also need to read at * least 1k to get the second 512 bytes of the volume. * -WD 10-26-01 */ sb_min_blocksize(sb, 1024); if (!(bh = sb_bread(sb, sb_block))) { befs_error(sb, "unable to read superblock"); goto unaquire_priv_sbp; } /* account for offset of super block on x86 */ disk_sb = (befs_super_block *) bh->b_data; if ((le32_to_cpu(disk_sb->magic1) == BEFS_SUPER_MAGIC1) || (be32_to_cpu(disk_sb->magic1) == BEFS_SUPER_MAGIC1)) { befs_debug(sb, "Using PPC superblock location"); } else { befs_debug(sb, "Using x86 superblock location"); disk_sb = (befs_super_block *) ((void *) bh->b_data + x86_sb_off); } if (befs_load_sb(sb, disk_sb) != BEFS_OK) goto unaquire_bh; befs_dump_super_block(sb, disk_sb); brelse(bh); if (befs_check_sb(sb) != BEFS_OK) goto unaquire_priv_sbp; if( befs_sb->num_blocks > ~((sector_t)0) ) { befs_error(sb, "blocks count: %Lu " "is larger than the host can use", befs_sb->num_blocks); goto unaquire_priv_sbp; } /* * set up enough so that it can read an inode * Fill in kernel superblock fields from private sb */ sb->s_magic = BEFS_SUPER_MAGIC; /* Set real blocksize of fs */ sb_set_blocksize(sb, (ulong) befs_sb->block_size); sb->s_op = (struct super_operations *) &befs_sops; root = iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir))); sb->s_root = d_alloc_root(root); if (!sb->s_root) { iput(root); befs_error(sb, "get root inode failed"); goto unaquire_priv_sbp; } /* load nls library */ if (befs_sb->mount_opts.iocharset) { befs_debug(sb, "Loading nls: %s", befs_sb->mount_opts.iocharset); befs_sb->nls = load_nls(befs_sb->mount_opts.iocharset); if (!befs_sb->nls) { befs_warning(sb, "Cannot load nls %s" " loading default nls", befs_sb->mount_opts.iocharset); befs_sb->nls = load_nls_default(); } /* load default nls if none is specified in mount options */ } else { befs_debug(sb, "Loading default nls"); befs_sb->nls = load_nls_default(); } return 0; /*****************/ unaquire_bh: brelse(bh); unaquire_priv_sbp: kfree(sb->s_fs_info); unaquire_none: sb->s_fs_info = NULL; return -EINVAL; } static int befs_remount(struct super_block *sb, int *flags, char *data) { if (!(*flags & MS_RDONLY)) return -EINVAL; return 0; } static int befs_statfs(struct super_block *sb, struct kstatfs *buf) { befs_debug(sb, "---> befs_statfs()"); buf->f_type = BEFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = BEFS_SB(sb)->num_blocks; buf->f_bfree = BEFS_SB(sb)->num_blocks - BEFS_SB(sb)->used_blocks; buf->f_bavail = buf->f_bfree; buf->f_files = 0; /* UNKNOWN */ buf->f_ffree = 0; /* UNKNOWN */ buf->f_namelen = BEFS_NAME_LEN; befs_debug(sb, "<--- befs_statfs()"); return 0; } static struct super_block * befs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_bdev(fs_type, flags, dev_name, data, befs_fill_super); } static struct file_system_type befs_fs_type = { .owner = THIS_MODULE, .name = "befs", .get_sb = befs_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static int __init init_befs_fs(void) { int err; printk(KERN_INFO "BeFS version: %s\n", BEFS_VERSION); err = befs_init_inodecache(); if (err) goto unaquire_none; err = register_filesystem(&befs_fs_type); if (err) goto unaquire_inodecache; return 0; unaquire_inodecache: befs_destroy_inodecache(); unaquire_none: return err; } static void __exit exit_befs_fs(void) { befs_destroy_inodecache(); unregister_filesystem(&befs_fs_type); } /* Macros that typecheck the init and exit functions, ensures that they are called at init and cleanup, and eliminates warnings about unused functions. */ module_init(init_befs_fs) module_exit(exit_befs_fs) linux-2.6.17/fs/befs/inode.c0000644000000000000000000000222510564537530014246 0ustar rootroot/* * inode.c * * Copyright (C) 2001 Will Dyson */ #include #include "befs.h" #include "inode.h" #include "endian.h" /* Validates the correctness of the befs inode Returns BEFS_OK if the inode should be used, otherwise returns BEFS_BAD_INODE */ int befs_check_inode(struct super_block *sb, befs_inode * raw_inode, befs_blocknr_t inode) { u32 magic1 = fs32_to_cpu(sb, raw_inode->magic1); befs_inode_addr ino_num = fsrun_to_cpu(sb, raw_inode->inode_num); u32 flags = fs32_to_cpu(sb, raw_inode->flags); /* check magic header. */ if (magic1 != BEFS_INODE_MAGIC1) { befs_error(sb, "Inode has a bad magic header - inode = %lu", inode); return BEFS_BAD_INODE; } /* * Sanity check2: inodes store their own block address. Check it. */ if (inode != iaddr2blockno(sb, &ino_num)) { befs_error(sb, "inode blocknr field disagrees with vfs " "VFS: %lu, Inode %lu", inode, iaddr2blockno(sb, &ino_num)); return BEFS_BAD_INODE; } /* * check flag */ if (!(flags & BEFS_INODE_IN_USE)) { befs_error(sb, "inode is not used - inode = %lu", inode); return BEFS_BAD_INODE; } return BEFS_OK; } linux-2.6.17/fs/befs/Makefile0000644000000000000000000000024410564537530014443 0ustar rootroot# # Makefile for the linux BeOS filesystem routines. # obj-$(CONFIG_BEFS_FS) += befs.o befs-objs := datastream.o btree.o super.o inode.o debug.o io.o linuxvfs.o linux-2.6.17/fs/befs/inode.h0000644000000000000000000000017310564537530014253 0ustar rootroot/* * inode.h * */ int befs_check_inode(struct super_block *sb, befs_inode * raw_inode, befs_blocknr_t inode); linux-2.6.17/fs/befs/btree.c0000644000000000000000000005400010564537530014247 0ustar rootroot/* * linux/fs/befs/btree.c * * Copyright (C) 2001-2002 Will Dyson * * Licensed under the GNU GPL. See the file COPYING for details. * * 2002-02-05: Sergey S. Kostyliov added binary search withing * btree nodes. * * Many thanks to: * * Dominic Giampaolo, author of "Practical File System * Design with the Be File System", for such a helpful book. * * Marcus J. Ranum, author of the b+tree package in * comp.sources.misc volume 10. This code is not copied from that * work, but it is partially based on it. * * Makoto Kato, author of the original BeFS for linux filesystem * driver. */ #include #include #include #include #include #include "befs.h" #include "btree.h" #include "datastream.h" #include "endian.h" /* * The btree functions in this file are built on top of the * datastream.c interface, which is in turn built on top of the * io.c interface. */ /* Befs B+tree structure: * * The first thing in the tree is the tree superblock. It tells you * all kinds of useful things about the tree, like where the rootnode * is located, and the size of the nodes (always 1024 with current version * of BeOS). * * The rest of the tree consists of a series of nodes. Nodes contain a header * (struct befs_btree_nodehead), the packed key data, an array of shorts * containing the ending offsets for each of the keys, and an array of * befs_off_t values. In interior nodes, the keys are the ending keys for * the childnode they point to, and the values are offsets into the * datastream containing the tree. */ /* Note: * * The book states 2 confusing things about befs b+trees. First, * it states that the overflow field of node headers is used by internal nodes * to point to another node that "effectively continues this one". Here is what * I believe that means. Each key in internal nodes points to another node that * contains key values less than itself. Inspection reveals that the last key * in the internal node is not the last key in the index. Keys that are * greater than the last key in the internal node go into the overflow node. * I imagine there is a performance reason for this. * * Second, it states that the header of a btree node is sufficient to * distinguish internal nodes from leaf nodes. Without saying exactly how. * After figuring out the first, it becomes obvious that internal nodes have * overflow nodes and leafnodes do not. */ /* * Currently, this code is only good for directory B+trees. * In order to be used for other BFS indexes, it needs to be extended to handle * duplicate keys and non-string keytypes (int32, int64, float, double). */ /* * In memory structure of each btree node */ typedef struct { befs_btree_nodehead head; /* head of node converted to cpu byteorder */ struct buffer_head *bh; befs_btree_nodehead *od_node; /* on disk node */ } befs_btree_node; /* local constants */ static const befs_off_t befs_bt_inval = 0xffffffffffffffffULL; /* local functions */ static int befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds, befs_btree_super * bt_super, befs_btree_node * this_node, befs_off_t * node_off); static int befs_bt_read_super(struct super_block *sb, befs_data_stream * ds, befs_btree_super * sup); static int befs_bt_read_node(struct super_block *sb, befs_data_stream * ds, befs_btree_node * node, befs_off_t node_off); static int befs_leafnode(befs_btree_node * node); static u16 *befs_bt_keylen_index(befs_btree_node * node); static befs_off_t *befs_bt_valarray(befs_btree_node * node); static char *befs_bt_keydata(befs_btree_node * node); static int befs_find_key(struct super_block *sb, befs_btree_node * node, const char *findkey, befs_off_t * value); static char *befs_bt_get_key(struct super_block *sb, befs_btree_node * node, int index, u16 * keylen); static int befs_compare_strings(const void *key1, int keylen1, const void *key2, int keylen2); /** * befs_bt_read_super - read in btree superblock convert to cpu byteorder * @sb: Filesystem superblock * @ds: Datastream to read from * @sup: Buffer in which to place the btree superblock * * Calls befs_read_datastream to read in the btree superblock and * makes sure it is in cpu byteorder, byteswapping if necessary. * * On success, returns BEFS_OK and *@sup contains the btree superblock, * in cpu byte order. * * On failure, BEFS_ERR is returned. */ static int befs_bt_read_super(struct super_block *sb, befs_data_stream * ds, befs_btree_super * sup) { struct buffer_head *bh = NULL; befs_btree_super *od_sup = NULL; befs_debug(sb, "---> befs_btree_read_super()"); bh = befs_read_datastream(sb, ds, 0, NULL); if (!bh) { befs_error(sb, "Couldn't read index header."); goto error; } od_sup = (befs_btree_super *) bh->b_data; befs_dump_index_entry(sb, od_sup); sup->magic = fs32_to_cpu(sb, od_sup->magic); sup->node_size = fs32_to_cpu(sb, od_sup->node_size); sup->max_depth = fs32_to_cpu(sb, od_sup->max_depth); sup->data_type = fs32_to_cpu(sb, od_sup->data_type); sup->root_node_ptr = fs64_to_cpu(sb, od_sup->root_node_ptr); sup->free_node_ptr = fs64_to_cpu(sb, od_sup->free_node_ptr); sup->max_size = fs64_to_cpu(sb, od_sup->max_size); brelse(bh); if (sup->magic != BEFS_BTREE_MAGIC) { befs_error(sb, "Index header has bad magic."); goto error; } befs_debug(sb, "<--- befs_btree_read_super()"); return BEFS_OK; error: befs_debug(sb, "<--- befs_btree_read_super() ERROR"); return BEFS_ERR; } /** * befs_bt_read_node - read in btree node and convert to cpu byteorder * @sb: Filesystem superblock * @ds: Datastream to read from * @node: Buffer in which to place the btree node * @node_off: Starting offset (in bytes) of the node in @ds * * Calls befs_read_datastream to read in the indicated btree node and * makes sure its header fields are in cpu byteorder, byteswapping if * necessary. * Note: node->bh must be NULL when this function called first * time. Don't forget brelse(node->bh) after last call. * * On success, returns BEFS_OK and *@node contains the btree node that * starts at @node_off, with the node->head fields in cpu byte order. * * On failure, BEFS_ERR is returned. */ static int befs_bt_read_node(struct super_block *sb, befs_data_stream * ds, befs_btree_node * node, befs_off_t node_off) { uint off = 0; befs_debug(sb, "---> befs_bt_read_node()"); if (node->bh) brelse(node->bh); node->bh = befs_read_datastream(sb, ds, node_off, &off); if (!node->bh) { befs_error(sb, "befs_bt_read_node() failed to read " "node at %Lu", node_off); befs_debug(sb, "<--- befs_bt_read_node() ERROR"); return BEFS_ERR; } node->od_node = (befs_btree_nodehead *) ((void *) node->bh->b_data + off); befs_dump_index_node(sb, node->od_node); node->head.left = fs64_to_cpu(sb, node->od_node->left); node->head.right = fs64_to_cpu(sb, node->od_node->right); node->head.overflow = fs64_to_cpu(sb, node->od_node->overflow); node->head.all_key_count = fs16_to_cpu(sb, node->od_node->all_key_count); node->head.all_key_length = fs16_to_cpu(sb, node->od_node->all_key_length); befs_debug(sb, "<--- befs_btree_read_node()"); return BEFS_OK; } /** * befs_btree_find - Find a key in a befs B+tree * @sb: Filesystem superblock * @ds: Datastream containing btree * @key: Key string to lookup in btree * @value: Value stored with @key * * On sucess, returns BEFS_OK and sets *@value to the value stored * with @key (usually the disk block number of an inode). * * On failure, returns BEFS_ERR or BEFS_BT_NOT_FOUND. * * Algorithm: * Read the superblock and rootnode of the b+tree. * Drill down through the interior nodes using befs_find_key(). * Once at the correct leaf node, use befs_find_key() again to get the * actuall value stored with the key. */ int befs_btree_find(struct super_block *sb, befs_data_stream * ds, const char *key, befs_off_t * value) { befs_btree_node *this_node = NULL; befs_btree_super bt_super; befs_off_t node_off; int res; befs_debug(sb, "---> befs_btree_find() Key: %s", key); if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read index superblock"); goto error; } this_node = (befs_btree_node *) kmalloc(sizeof (befs_btree_node), GFP_NOFS); if (!this_node) { befs_error(sb, "befs_btree_find() failed to allocate %u " "bytes of memory", sizeof (befs_btree_node)); goto error; } this_node->bh = NULL; /* read in root node */ node_off = bt_super.root_node_ptr; if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read " "node at %Lu", node_off); goto error_alloc; } while (!befs_leafnode(this_node)) { res = befs_find_key(sb, this_node, key, &node_off); if (res == BEFS_BT_NOT_FOUND) node_off = this_node->head.overflow; /* if no match, go to overflow node */ if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read " "node at %Lu", node_off); goto error_alloc; } } /* at the correct leaf node now */ res = befs_find_key(sb, this_node, key, value); brelse(this_node->bh); kfree(this_node); if (res != BEFS_BT_MATCH) { befs_debug(sb, "<--- befs_btree_find() Key %s not found", key); *value = 0; return BEFS_BT_NOT_FOUND; } befs_debug(sb, "<--- befs_btree_find() Found key %s, value %Lu", key, *value); return BEFS_OK; error_alloc: kfree(this_node); error: *value = 0; befs_debug(sb, "<--- befs_btree_find() ERROR"); return BEFS_ERR; } /** * befs_find_key - Search for a key within a node * @sb: Filesystem superblock * @node: Node to find the key within * @key: Keystring to search for * @value: If key is found, the value stored with the key is put here * * finds exact match if one exists, and returns BEFS_BT_MATCH * If no exact match, finds first key in node that is greater * (alphabetically) than the search key and returns BEFS_BT_PARMATCH * (for partial match, I guess). Can you think of something better to * call it? * * If no key was a match or greater than the search key, return * BEFS_BT_NOT_FOUND. * * Use binary search instead of a linear. */ static int befs_find_key(struct super_block *sb, befs_btree_node * node, const char *findkey, befs_off_t * value) { int first, last, mid; int eq; u16 keylen; int findkey_len; char *thiskey; befs_off_t *valarray; befs_debug(sb, "---> befs_find_key() %s", findkey); *value = 0; findkey_len = strlen(findkey); /* if node can not contain key, just skeep this node */ last = node->head.all_key_count - 1; thiskey = befs_bt_get_key(sb, node, last, &keylen); eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len); if (eq < 0) { befs_debug(sb, "<--- befs_find_key() %s not found", findkey); return BEFS_BT_NOT_FOUND; } valarray = befs_bt_valarray(node); /* simple binary search */ first = 0; mid = 0; while (last >= first) { mid = (last + first) / 2; befs_debug(sb, "first: %d, last: %d, mid: %d", first, last, mid); thiskey = befs_bt_get_key(sb, node, mid, &keylen); eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len); if (eq == 0) { befs_debug(sb, "<--- befs_find_key() found %s at %d", thiskey, mid); *value = fs64_to_cpu(sb, valarray[mid]); return BEFS_BT_MATCH; } if (eq > 0) last = mid - 1; else first = mid + 1; } if (eq < 0) *value = fs64_to_cpu(sb, valarray[mid + 1]); else *value = fs64_to_cpu(sb, valarray[mid]); befs_debug(sb, "<--- befs_find_key() found %s at %d", thiskey, mid); return BEFS_BT_PARMATCH; } /** * befs_btree_read - Traverse leafnodes of a btree * @sb: Filesystem superblock * @ds: Datastream containing btree * @key_no: Key number (alphabetical order) of key to read * @bufsize: Size of the buffer to return key in * @keybuf: Pointer to a buffer to put the key in * @keysize: Length of the returned key * @value: Value stored with the returned key * * Heres how it works: Key_no is the index of the key/value pair to * return in keybuf/value. * Bufsize is the size of keybuf (BEFS_NAME_LEN+1 is a good size). Keysize is * the number of charecters in the key (just a convenience). * * Algorithm: * Get the first leafnode of the tree. See if the requested key is in that * node. If not, follow the node->right link to the next leafnode. Repeat * until the (key_no)th key is found or the tree is out of keys. */ int befs_btree_read(struct super_block *sb, befs_data_stream * ds, loff_t key_no, size_t bufsize, char *keybuf, size_t * keysize, befs_off_t * value) { befs_btree_node *this_node; befs_btree_super bt_super; befs_off_t node_off = 0; int cur_key; befs_off_t *valarray; char *keystart; u16 keylen; int res; uint key_sum = 0; befs_debug(sb, "---> befs_btree_read()"); if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) { befs_error(sb, "befs_btree_read() failed to read index superblock"); goto error; } if ((this_node = (befs_btree_node *) kmalloc(sizeof (befs_btree_node), GFP_NOFS)) == NULL) { befs_error(sb, "befs_btree_read() failed to allocate %u " "bytes of memory", sizeof (befs_btree_node)); goto error; } node_off = bt_super.root_node_ptr; this_node->bh = NULL; /* seeks down to first leafnode, reads it into this_node */ res = befs_btree_seekleaf(sb, ds, &bt_super, this_node, &node_off); if (res == BEFS_BT_EMPTY) { brelse(this_node->bh); kfree(this_node); *value = 0; *keysize = 0; befs_debug(sb, "<--- befs_btree_read() Tree is EMPTY"); return BEFS_BT_EMPTY; } else if (res == BEFS_ERR) { goto error_alloc; } /* find the leaf node containing the key_no key */ while (key_sum + this_node->head.all_key_count <= key_no) { /* no more nodes to look in: key_no is too large */ if (this_node->head.right == befs_bt_inval) { *keysize = 0; *value = 0; befs_debug(sb, "<--- befs_btree_read() END of keys at %Lu", key_sum + this_node->head.all_key_count); brelse(this_node->bh); kfree(this_node); return BEFS_BT_END; } key_sum += this_node->head.all_key_count; node_off = this_node->head.right; if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_read() failed to read " "node at %Lu", node_off); goto error_alloc; } } /* how many keys into this_node is key_no */ cur_key = key_no - key_sum; /* get pointers to datastructures within the node body */ valarray = befs_bt_valarray(this_node); keystart = befs_bt_get_key(sb, this_node, cur_key, &keylen); befs_debug(sb, "Read [%Lu,%d]: keysize %d", node_off, cur_key, keylen); if (bufsize < keylen + 1) { befs_error(sb, "befs_btree_read() keybuf too small (%u) " "for key of size %d", bufsize, keylen); brelse(this_node->bh); goto error_alloc; }; strncpy(keybuf, keystart, keylen); *value = fs64_to_cpu(sb, valarray[cur_key]); *keysize = keylen; keybuf[keylen] = '\0'; befs_debug(sb, "Read [%Lu,%d]: Key \"%.*s\", Value %Lu", node_off, cur_key, keylen, keybuf, *value); brelse(this_node->bh); kfree(this_node); befs_debug(sb, "<--- befs_btree_read()"); return BEFS_OK; error_alloc: kfree(this_node); error: *keysize = 0; *value = 0; befs_debug(sb, "<--- befs_btree_read() ERROR"); return BEFS_ERR; } /** * befs_btree_seekleaf - Find the first leafnode in the btree * @sb: Filesystem superblock * @ds: Datastream containing btree * @bt_super: Pointer to the superblock of the btree * @this_node: Buffer to return the leafnode in * @node_off: Pointer to offset of current node within datastream. Modified * by the function. * * * Helper function for btree traverse. Moves the current position to the * start of the first leaf node. * * Also checks for an empty tree. If there are no keys, returns BEFS_BT_EMPTY. */ static int befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds, befs_btree_super * bt_super, befs_btree_node * this_node, befs_off_t * node_off) { befs_debug(sb, "---> befs_btree_seekleaf()"); if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) { befs_error(sb, "befs_btree_seekleaf() failed to read " "node at %Lu", *node_off); goto error; } befs_debug(sb, "Seekleaf to root node %Lu", *node_off); if (this_node->head.all_key_count == 0 && befs_leafnode(this_node)) { befs_debug(sb, "<--- befs_btree_seekleaf() Tree is EMPTY"); return BEFS_BT_EMPTY; } while (!befs_leafnode(this_node)) { if (this_node->head.all_key_count == 0) { befs_debug(sb, "befs_btree_seekleaf() encountered " "an empty interior node: %Lu. Using Overflow " "node: %Lu", *node_off, this_node->head.overflow); *node_off = this_node->head.overflow; } else { befs_off_t *valarray = befs_bt_valarray(this_node); *node_off = fs64_to_cpu(sb, valarray[0]); } if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) { befs_error(sb, "befs_btree_seekleaf() failed to read " "node at %Lu", *node_off); goto error; } befs_debug(sb, "Seekleaf to child node %Lu", *node_off); } befs_debug(sb, "Node %Lu is a leaf node", *node_off); return BEFS_OK; error: befs_debug(sb, "<--- befs_btree_seekleaf() ERROR"); return BEFS_ERR; } /** * befs_leafnode - Determine if the btree node is a leaf node or an * interior node * @node: Pointer to node structure to test * * Return 1 if leaf, 0 if interior */ static int befs_leafnode(befs_btree_node * node) { /* all interior nodes (and only interior nodes) have an overflow node */ if (node->head.overflow == befs_bt_inval) return 1; else return 0; } /** * befs_bt_keylen_index - Finds start of keylen index in a node * @node: Pointer to the node structure to find the keylen index within * * Returns a pointer to the start of the key length index array * of the B+tree node *@node * * "The length of all the keys in the node is added to the size of the * header and then rounded up to a multiple of four to get the beginning * of the key length index" (p.88, practical filesystem design). * * Except that rounding up to 8 works, and rounding up to 4 doesn't. */ static u16 * befs_bt_keylen_index(befs_btree_node * node) { const int keylen_align = 8; unsigned long int off = (sizeof (befs_btree_nodehead) + node->head.all_key_length); ulong tmp = off % keylen_align; if (tmp) off += keylen_align - tmp; return (u16 *) ((void *) node->od_node + off); } /** * befs_bt_valarray - Finds the start of value array in a node * @node: Pointer to the node structure to find the value array within * * Returns a pointer to the start of the value array * of the node pointed to by the node header */ static befs_off_t * befs_bt_valarray(befs_btree_node * node) { void *keylen_index_start = (void *) befs_bt_keylen_index(node); size_t keylen_index_size = node->head.all_key_count * sizeof (u16); return (befs_off_t *) (keylen_index_start + keylen_index_size); } /** * befs_bt_keydata - Finds start of keydata array in a node * @node: Pointer to the node structure to find the keydata array within * * Returns a pointer to the start of the keydata array * of the node pointed to by the node header */ static char * befs_bt_keydata(befs_btree_node * node) { return (char *) ((void *) node->od_node + sizeof (befs_btree_nodehead)); } /** * befs_bt_get_key - returns a pointer to the start of a key * @sb: filesystem superblock * @node: node in which to look for the key * @index: the index of the key to get * @keylen: modified to be the length of the key at @index * * Returns a valid pointer into @node on success. * Returns NULL on failure (bad input) and sets *@keylen = 0 */ static char * befs_bt_get_key(struct super_block *sb, befs_btree_node * node, int index, u16 * keylen) { int prev_key_end; char *keystart; u16 *keylen_index; if (index < 0 || index > node->head.all_key_count) { *keylen = 0; return NULL; } keystart = befs_bt_keydata(node); keylen_index = befs_bt_keylen_index(node); if (index == 0) prev_key_end = 0; else prev_key_end = fs16_to_cpu(sb, keylen_index[index - 1]); *keylen = fs16_to_cpu(sb, keylen_index[index]) - prev_key_end; return keystart + prev_key_end; } /** * befs_compare_strings - compare two strings * @key1: pointer to the first key to be compared * @keylen1: length in bytes of key1 * @key2: pointer to the second key to be compared * @kelen2: length in bytes of key2 * * Returns 0 if @key1 and @key2 are equal. * Returns >0 if @key1 is greater. * Returns <0 if @key2 is greater.. */ static int befs_compare_strings(const void *key1, int keylen1, const void *key2, int keylen2) { int len = min_t(int, keylen1, keylen2); int result = strncmp(key1, key2, len); if (result == 0) result = keylen1 - keylen2; return result; } /* These will be used for non-string keyed btrees */ #if 0 static int btree_compare_int32(cont void *key1, int keylen1, const void *key2, int keylen2) { return *(int32_t *) key1 - *(int32_t *) key2; } static int btree_compare_uint32(cont void *key1, int keylen1, const void *key2, int keylen2) { if (*(u_int32_t *) key1 == *(u_int32_t *) key2) return 0; else if (*(u_int32_t *) key1 > *(u_int32_t *) key2) return 1; return -1; } static int btree_compare_int64(cont void *key1, int keylen1, const void *key2, int keylen2) { if (*(int64_t *) key1 == *(int64_t *) key2) return 0; else if (*(int64_t *) key1 > *(int64_t *) key2) return 1; return -1; } static int btree_compare_uint64(cont void *key1, int keylen1, const void *key2, int keylen2) { if (*(u_int64_t *) key1 == *(u_int64_t *) key2) return 0; else if (*(u_int64_t *) key1 > *(u_int64_t *) key2) return 1; return -1; } static int btree_compare_float(cont void *key1, int keylen1, const void *key2, int keylen2) { float result = *(float *) key1 - *(float *) key2; if (result == 0.0f) return 0; return (result < 0.0f) ? -1 : 1; } static int btree_compare_double(cont void *key1, int keylen1, const void *key2, int keylen2) { double result = *(double *) key1 - *(double *) key2; if (result == 0.0) return 0; return (result < 0.0) ? -1 : 1; } #endif //0 linux-2.6.17/fs/befs/befs_fs_types.h0000644000000000000000000000752210564537530016015 0ustar rootroot/* * include/linux/befs_fs_types.h * * Copyright (C) 2001 Will Dyson (will@cs.earlham.edu) * * * * from linux/include/linux/befs_fs.h * * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp) * */ #ifndef _LINUX_BEFS_FS_TYPES #define _LINUX_BEFS_FS_TYPES #ifdef __KERNEL__ #include #endif /*__KERNEL__*/ #define PACKED __attribute__ ((__packed__)) /* * Max name lengths of BFS */ #define BEFS_NAME_LEN 255 #define BEFS_SYMLINK_LEN 144 #define BEFS_NUM_DIRECT_BLOCKS 12 #define B_OS_NAME_LENGTH 32 /* The datastream blocks mapped by the double-indirect * block are always 4 fs blocks long. * This eliminates the need for linear searches among * the potentially huge number of indirect blocks * * Err. Should that be 4 fs blocks or 4k??? * It matters on large blocksize volumes */ #define BEFS_DBLINDIR_BRUN_LEN 4 /* * Flags of superblock */ enum super_flags { BEFS_BYTESEX_BE, BEFS_BYTESEX_LE, BEFS_CLEAN = 0x434c454e, BEFS_DIRTY = 0x44495254, BEFS_SUPER_MAGIC1 = 0x42465331, /* BFS1 */ BEFS_SUPER_MAGIC2 = 0xdd121031, BEFS_SUPER_MAGIC3 = 0x15b6830e, }; #define BEFS_BYTEORDER_NATIVE 0x42494745 #define BEFS_SUPER_MAGIC BEFS_SUPER_MAGIC1 /* * Flags of inode */ #define BEFS_INODE_MAGIC1 0x3bbe0ad9 enum inode_flags { BEFS_INODE_IN_USE = 0x00000001, BEFS_ATTR_INODE = 0x00000004, BEFS_INODE_LOGGED = 0x00000008, BEFS_INODE_DELETED = 0x00000010, BEFS_LONG_SYMLINK = 0x00000040, BEFS_PERMANENT_FLAG = 0x0000ffff, BEFS_INODE_NO_CREATE = 0x00010000, BEFS_INODE_WAS_WRITTEN = 0x00020000, BEFS_NO_TRANSACTION = 0x00040000, }; /* * On-Disk datastructures of BeFS */ typedef u64 befs_off_t; typedef u64 befs_time_t; typedef void befs_binode_etc; /* Block runs */ typedef struct { u32 allocation_group; u16 start; u16 len; } PACKED befs_block_run; typedef befs_block_run befs_inode_addr; /* * The Superblock Structure */ typedef struct { char name[B_OS_NAME_LENGTH]; u32 magic1; u32 fs_byte_order; u32 block_size; u32 block_shift; befs_off_t num_blocks; befs_off_t used_blocks; u32 inode_size; u32 magic2; u32 blocks_per_ag; u32 ag_shift; u32 num_ags; u32 flags; befs_block_run log_blocks; befs_off_t log_start; befs_off_t log_end; u32 magic3; befs_inode_addr root_dir; befs_inode_addr indices; } PACKED befs_super_block; /* * Note: the indirect and dbl_indir block_runs may * be longer than one block! */ typedef struct { befs_block_run direct[BEFS_NUM_DIRECT_BLOCKS]; befs_off_t max_direct_range; befs_block_run indirect; befs_off_t max_indirect_range; befs_block_run double_indirect; befs_off_t max_double_indirect_range; befs_off_t size; } PACKED befs_data_stream; /* Attribute */ typedef struct { u32 type; u16 name_size; u16 data_size; char name[1]; } PACKED befs_small_data; /* Inode structure */ typedef struct { u32 magic1; befs_inode_addr inode_num; u32 uid; u32 gid; u32 mode; u32 flags; befs_time_t create_time; befs_time_t last_modified_time; befs_inode_addr parent; befs_inode_addr attributes; u32 type; u32 inode_size; u32 etc; /* not use */ union { befs_data_stream datastream; char symlink[BEFS_SYMLINK_LEN]; } data; u32 pad[4]; /* not use */ befs_small_data small_data[1]; } PACKED befs_inode; /* * B+tree superblock */ #define BEFS_BTREE_MAGIC 0x69f6c2e8 enum btree_types { BTREE_STRING_TYPE = 0, BTREE_INT32_TYPE = 1, BTREE_UINT32_TYPE = 2, BTREE_INT64_TYPE = 3, BTREE_UINT64_TYPE = 4, BTREE_FLOAT_TYPE = 5, BTREE_DOUBLE_TYPE = 6 }; typedef struct { u32 magic; u32 node_size; u32 max_depth; u32 data_type; befs_off_t root_node_ptr; befs_off_t free_node_ptr; befs_off_t max_size; } PACKED befs_btree_super; /* * Header stucture of each btree node */ typedef struct { befs_off_t left; befs_off_t right; befs_off_t overflow; u16 all_key_count; u16 all_key_length; } PACKED befs_btree_nodehead; #endif /* _LINUX_BEFS_FS_TYPES */ linux-2.6.17/fs/befs/datastream.c0000644000000000000000000003706710564537530015311 0ustar rootroot/* * linux/fs/befs/datastream.c * * Copyright (C) 2001 Will Dyson * * Based on portions of file.c by Makoto Kato * * Many thanks to Dominic Giampaolo, author of "Practical File System * Design with the Be File System", for such a helpful book. * */ #include #include #include #include #include "befs.h" #include "datastream.h" #include "io.h" #include "endian.h" const befs_inode_addr BAD_IADDR = { 0, 0, 0 }; static int befs_find_brun_direct(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run); static int befs_find_brun_indirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run); static int befs_find_brun_dblindirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run); /** * befs_read_datastream - get buffer_head containing data, starting from pos. * @sb: Filesystem superblock * @ds: datastrem to find data with * @pos: start of data * @off: offset of data in buffer_head->b_data * * Returns pointer to buffer_head containing data starting with offset @off, * if you don't need to know offset just set @off = NULL. */ struct buffer_head * befs_read_datastream(struct super_block *sb, befs_data_stream * ds, befs_off_t pos, uint * off) { struct buffer_head *bh = NULL; befs_block_run run; befs_blocknr_t block; /* block coresponding to pos */ befs_debug(sb, "---> befs_read_datastream() %Lu", pos); block = pos >> BEFS_SB(sb)->block_shift; if (off) *off = pos - (block << BEFS_SB(sb)->block_shift); if (befs_fblock2brun(sb, ds, block, &run) != BEFS_OK) { befs_error(sb, "BeFS: Error finding disk addr of block %lu", block); befs_debug(sb, "<--- befs_read_datastream() ERROR"); return NULL; } bh = befs_bread_iaddr(sb, run); if (!bh) { befs_error(sb, "BeFS: Error reading block %lu from datastream", block); return NULL; } befs_debug(sb, "<--- befs_read_datastream() read data, starting at %Lu", pos); return bh; } /* * Takes a file position and gives back a brun who's starting block * is block number fblock of the file. * * Returns BEFS_OK or BEFS_ERR. * * Calls specialized functions for each of the three possible * datastream regions. * * 2001-11-15 Will Dyson */ int befs_fblock2brun(struct super_block *sb, befs_data_stream * data, befs_blocknr_t fblock, befs_block_run * run) { int err; befs_off_t pos = fblock << BEFS_SB(sb)->block_shift; if (pos < data->max_direct_range) { err = befs_find_brun_direct(sb, data, fblock, run); } else if (pos < data->max_indirect_range) { err = befs_find_brun_indirect(sb, data, fblock, run); } else if (pos < data->max_double_indirect_range) { err = befs_find_brun_dblindirect(sb, data, fblock, run); } else { befs_error(sb, "befs_fblock2brun() was asked to find block %lu, " "which is not mapped by the datastream\n", fblock); err = BEFS_ERR; } return err; } /** * befs_read_lsmylink - read long symlink from datastream. * @sb: Filesystem superblock * @ds: Datastrem to read from * @buf: Buffer in which to place long symlink data * @len: Length of the long symlink in bytes * * Returns the number of bytes read */ size_t befs_read_lsymlink(struct super_block * sb, befs_data_stream * ds, void *buff, befs_off_t len) { befs_off_t bytes_read = 0; /* bytes readed */ u16 plen; struct buffer_head *bh = NULL; befs_debug(sb, "---> befs_read_lsymlink() length: %Lu", len); while (bytes_read < len) { bh = befs_read_datastream(sb, ds, bytes_read, NULL); if (!bh) { befs_error(sb, "BeFS: Error reading datastream block " "starting from %Lu", bytes_read); befs_debug(sb, "<--- befs_read_lsymlink() ERROR"); return bytes_read; } plen = ((bytes_read + BEFS_SB(sb)->block_size) < len) ? BEFS_SB(sb)->block_size : len - bytes_read; memcpy(buff + bytes_read, bh->b_data, plen); brelse(bh); bytes_read += plen; } befs_debug(sb, "<--- befs_read_lsymlink() read %u bytes", bytes_read); return bytes_read; } /** * befs_count_blocks - blocks used by a file * @sb: Filesystem superblock * @ds: Datastream of the file * * Counts the number of fs blocks that the file represented by * inode occupies on the filesystem, counting both regular file * data and filesystem metadata (and eventually attribute data * when we support attributes) */ befs_blocknr_t befs_count_blocks(struct super_block * sb, befs_data_stream * ds) { befs_blocknr_t blocks; befs_blocknr_t datablocks; /* File data blocks */ befs_blocknr_t metablocks; /* FS metadata blocks */ befs_sb_info *befs_sb = BEFS_SB(sb); befs_debug(sb, "---> befs_count_blocks()"); datablocks = ds->size >> befs_sb->block_shift; if (ds->size & (befs_sb->block_size - 1)) datablocks += 1; metablocks = 1; /* Start with 1 block for inode */ /* Size of indirect block */ if (ds->size > ds->max_direct_range) metablocks += ds->indirect.len; /* Double indir block, plus all the indirect blocks it mapps In the double-indirect range, all block runs of data are BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know how many data block runs are in the double-indirect region, and from that we know how many indirect blocks it takes to map them. We assume that the indirect blocks are also BEFS_DBLINDIR_BRUN_LEN blocks long. */ if (ds->size > ds->max_indirect_range && ds->max_indirect_range != 0) { uint dbl_bytes; uint dbl_bruns; uint indirblocks; dbl_bytes = ds->max_double_indirect_range - ds->max_indirect_range; dbl_bruns = dbl_bytes / (befs_sb->block_size * BEFS_DBLINDIR_BRUN_LEN); indirblocks = dbl_bruns / befs_iaddrs_per_block(sb); metablocks += ds->double_indirect.len; metablocks += indirblocks; } blocks = datablocks + metablocks; befs_debug(sb, "<--- befs_count_blocks() %u blocks", blocks); return blocks; } /* Finds the block run that starts at file block number blockno in the file represented by the datastream data, if that blockno is in the direct region of the datastream. sb: the superblock data: the datastream blockno: the blocknumber to find run: The found run is passed back through this pointer Return value is BEFS_OK if the blockrun is found, BEFS_ERR otherwise. Algorithm: Linear search. Checks each element of array[] to see if it contains the blockno-th filesystem block. This is necessary because the block runs map variable amounts of data. Simply keeps a count of the number of blocks searched so far (sum), incrementing this by the length of each block run as we come across it. Adds sum to *count before returning (this is so you can search multiple arrays that are logicaly one array, as in the indirect region code). When/if blockno is found, if blockno is inside of a block run as stored on disk, we offset the start and lenght members of the block run, so that blockno is the start and len is still valid (the run ends in the same place). 2001-11-15 Will Dyson */ static int befs_find_brun_direct(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run) { int i; befs_block_run *array = data->direct; befs_blocknr_t sum; befs_blocknr_t max_block = data->max_direct_range >> BEFS_SB(sb)->block_shift; befs_debug(sb, "---> befs_find_brun_direct(), find %lu", blockno); if (blockno > max_block) { befs_error(sb, "befs_find_brun_direct() passed block outside of" "direct region"); return BEFS_ERR; } for (i = 0, sum = 0; i < BEFS_NUM_DIRECT_BLOCKS; sum += array[i].len, i++) { if (blockno >= sum && blockno < sum + (array[i].len)) { int offset = blockno - sum; run->allocation_group = array[i].allocation_group; run->start = array[i].start + offset; run->len = array[i].len - offset; befs_debug(sb, "---> befs_find_brun_direct(), " "found %lu at direct[%d]", blockno, i); return BEFS_OK; } } befs_debug(sb, "---> befs_find_brun_direct() ERROR"); return BEFS_ERR; } /* Finds the block run that starts at file block number blockno in the file represented by the datastream data, if that blockno is in the indirect region of the datastream. sb: the superblock data: the datastream blockno: the blocknumber to find run: The found run is passed back through this pointer Return value is BEFS_OK if the blockrun is found, BEFS_ERR otherwise. Algorithm: For each block in the indirect run of the datastream, read it in and search through it for search_blk. XXX: Really should check to make sure blockno is inside indirect region. 2001-11-15 Will Dyson */ static int befs_find_brun_indirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run) { int i, j; befs_blocknr_t sum = 0; befs_blocknr_t indir_start_blk; befs_blocknr_t search_blk; struct buffer_head *indirblock; befs_block_run *array; befs_block_run indirect = data->indirect; befs_blocknr_t indirblockno = iaddr2blockno(sb, &indirect); int arraylen = befs_iaddrs_per_block(sb); befs_debug(sb, "---> befs_find_brun_indirect(), find %lu", blockno); indir_start_blk = data->max_direct_range >> BEFS_SB(sb)->block_shift; search_blk = blockno - indir_start_blk; /* Examine blocks of the indirect run one at a time */ for (i = 0; i < indirect.len; i++) { indirblock = befs_bread(sb, indirblockno + i); if (indirblock == NULL) { befs_debug(sb, "---> befs_find_brun_indirect() failed to " "read disk block %lu from the indirect brun", indirblockno + i); return BEFS_ERR; } array = (befs_block_run *) indirblock->b_data; for (j = 0; j < arraylen; ++j) { int len = fs16_to_cpu(sb, array[j].len); if (search_blk >= sum && search_blk < sum + len) { int offset = search_blk - sum; run->allocation_group = fs32_to_cpu(sb, array[j].allocation_group); run->start = fs16_to_cpu(sb, array[j].start) + offset; run->len = fs16_to_cpu(sb, array[j].len) - offset; brelse(indirblock); befs_debug(sb, "<--- befs_find_brun_indirect() found " "file block %lu at indirect[%d]", blockno, j + (i * arraylen)); return BEFS_OK; } sum += len; } brelse(indirblock); } /* Only fallthrough is an error */ befs_error(sb, "BeFS: befs_find_brun_indirect() failed to find " "file block %lu", blockno); befs_debug(sb, "<--- befs_find_brun_indirect() ERROR"); return BEFS_ERR; } /* Finds the block run that starts at file block number blockno in the file represented by the datastream data, if that blockno is in the double-indirect region of the datastream. sb: the superblock data: the datastream blockno: the blocknumber to find run: The found run is passed back through this pointer Return value is BEFS_OK if the blockrun is found, BEFS_ERR otherwise. Algorithm: The block runs in the double-indirect region are different. They are always allocated 4 fs blocks at a time, so each block run maps a constant amount of file data. This means that we can directly calculate how many block runs into the double-indirect region we need to go to get to the one that maps a particular filesystem block. We do this in two stages. First we calculate which of the inode addresses in the double-indirect block will point us to the indirect block that contains the mapping for the data, then we calculate which of the inode addresses in that indirect block maps the data block we are after. Oh, and once we've done that, we actually read in the blocks that contain the inode addresses we calculated above. Even though the double-indirect run may be several blocks long, we can calculate which of those blocks will contain the index we are after and only read that one. We then follow it to the indirect block and perform a similar process to find the actual block run that maps the data block we are interested in. Then we offset the run as in befs_find_brun_array() and we are done. 2001-11-15 Will Dyson */ static int befs_find_brun_dblindirect(struct super_block *sb, befs_data_stream * data, befs_blocknr_t blockno, befs_block_run * run) { int dblindir_indx; int indir_indx; int offset; int dbl_which_block; int which_block; int dbl_block_indx; int block_indx; off_t dblindir_leftover; befs_blocknr_t blockno_at_run_start; struct buffer_head *dbl_indir_block; struct buffer_head *indir_block; befs_block_run indir_run; befs_inode_addr *iaddr_array = NULL; befs_sb_info *befs_sb = BEFS_SB(sb); befs_blocknr_t indir_start_blk = data->max_indirect_range >> befs_sb->block_shift; off_t dbl_indir_off = blockno - indir_start_blk; /* number of data blocks mapped by each of the iaddrs in * the indirect block pointed to by the double indirect block */ size_t iblklen = BEFS_DBLINDIR_BRUN_LEN; /* number of data blocks mapped by each of the iaddrs in * the double indirect block */ size_t diblklen = iblklen * befs_iaddrs_per_block(sb) * BEFS_DBLINDIR_BRUN_LEN; befs_debug(sb, "---> befs_find_brun_dblindirect() find %lu", blockno); /* First, discover which of the double_indir->indir blocks * contains pos. Then figure out how much of pos that * accounted for. Then discover which of the iaddrs in * the indirect block contains pos. */ dblindir_indx = dbl_indir_off / diblklen; dblindir_leftover = dbl_indir_off % diblklen; indir_indx = dblindir_leftover / diblklen; /* Read double indirect block */ dbl_which_block = dblindir_indx / befs_iaddrs_per_block(sb); if (dbl_which_block > data->double_indirect.len) { befs_error(sb, "The double-indirect index calculated by " "befs_read_brun_dblindirect(), %d, is outside the range " "of the double-indirect block", dblindir_indx); return BEFS_ERR; } dbl_indir_block = befs_bread(sb, iaddr2blockno(sb, &data->double_indirect) + dbl_which_block); if (dbl_indir_block == NULL) { befs_error(sb, "befs_read_brun_dblindirect() couldn't read the " "double-indirect block at blockno %lu", iaddr2blockno(sb, &data->double_indirect) + dbl_which_block); brelse(dbl_indir_block); return BEFS_ERR; } dbl_block_indx = dblindir_indx - (dbl_which_block * befs_iaddrs_per_block(sb)); iaddr_array = (befs_inode_addr *) dbl_indir_block->b_data; indir_run = fsrun_to_cpu(sb, iaddr_array[dbl_block_indx]); brelse(dbl_indir_block); iaddr_array = NULL; /* Read indirect block */ which_block = indir_indx / befs_iaddrs_per_block(sb); if (which_block > indir_run.len) { befs_error(sb, "The indirect index calculated by " "befs_read_brun_dblindirect(), %d, is outside the range " "of the indirect block", indir_indx); return BEFS_ERR; } indir_block = befs_bread(sb, iaddr2blockno(sb, &indir_run) + which_block); if (indir_block == NULL) { befs_error(sb, "befs_read_brun_dblindirect() couldn't read the " "indirect block at blockno %lu", iaddr2blockno(sb, &indir_run) + which_block); brelse(indir_block); return BEFS_ERR; } block_indx = indir_indx - (which_block * befs_iaddrs_per_block(sb)); iaddr_array = (befs_inode_addr *) indir_block->b_data; *run = fsrun_to_cpu(sb, iaddr_array[block_indx]); brelse(indir_block); iaddr_array = NULL; blockno_at_run_start = indir_start_blk; blockno_at_run_start += diblklen * dblindir_indx; blockno_at_run_start += iblklen * indir_indx; offset = blockno - blockno_at_run_start; run->start += offset; run->len -= offset; befs_debug(sb, "Found file block %lu in double_indirect[%d][%d]," " double_indirect_leftover = %lu", blockno, dblindir_indx, indir_indx, dblindir_leftover); return BEFS_OK; } linux-2.6.17/fs/pnode.c0000644000000000000000000001725410564537530013346 0ustar rootroot/* * linux/fs/pnode.c * * (C) Copyright IBM Corporation 2005. * Released under GPL v2. * Author : Ram Pai (linuxram@us.ibm.com) * */ #include #include #include #include "pnode.h" /* return the next shared peer mount of @p */ static inline struct vfsmount *next_peer(struct vfsmount *p) { return list_entry(p->mnt_share.next, struct vfsmount, mnt_share); } static inline struct vfsmount *first_slave(struct vfsmount *p) { return list_entry(p->mnt_slave_list.next, struct vfsmount, mnt_slave); } static inline struct vfsmount *next_slave(struct vfsmount *p) { return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave); } static int do_make_slave(struct vfsmount *mnt) { struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; struct vfsmount *slave_mnt; /* * slave 'mnt' to a peer mount that has the * same root dentry. If none is available than * slave it to anything that is available. */ while ((peer_mnt = next_peer(peer_mnt)) != mnt && peer_mnt->mnt_root != mnt->mnt_root) ; if (peer_mnt == mnt) { peer_mnt = next_peer(mnt); if (peer_mnt == mnt) peer_mnt = NULL; } list_del_init(&mnt->mnt_share); if (peer_mnt) master = peer_mnt; if (master) { list_for_each_entry(slave_mnt, &mnt->mnt_slave_list, mnt_slave) slave_mnt->mnt_master = master; list_del(&mnt->mnt_slave); list_add(&mnt->mnt_slave, &master->mnt_slave_list); list_splice(&mnt->mnt_slave_list, master->mnt_slave_list.prev); INIT_LIST_HEAD(&mnt->mnt_slave_list); } else { struct list_head *p = &mnt->mnt_slave_list; while (!list_empty(p)) { slave_mnt = list_entry(p->next, struct vfsmount, mnt_slave); list_del_init(&slave_mnt->mnt_slave); slave_mnt->mnt_master = NULL; } } mnt->mnt_master = master; CLEAR_MNT_SHARED(mnt); INIT_LIST_HEAD(&mnt->mnt_slave_list); return 0; } void change_mnt_propagation(struct vfsmount *mnt, int type) { if (type == MS_SHARED) { set_mnt_shared(mnt); return; } do_make_slave(mnt); if (type != MS_SLAVE) { list_del_init(&mnt->mnt_slave); mnt->mnt_master = NULL; if (type == MS_UNBINDABLE) mnt->mnt_flags |= MNT_UNBINDABLE; } } /* * get the next mount in the propagation tree. * @m: the mount seen last * @origin: the original mount from where the tree walk initiated */ static struct vfsmount *propagation_next(struct vfsmount *m, struct vfsmount *origin) { /* are there any slaves of this mount? */ if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list)) return first_slave(m); while (1) { struct vfsmount *next; struct vfsmount *master = m->mnt_master; if (master == origin->mnt_master) { next = next_peer(m); return ((next == origin) ? NULL : next); } else if (m->mnt_slave.next != &master->mnt_slave_list) return next_slave(m); /* back at master */ m = master; } } /* * return the source mount to be used for cloning * * @dest the current destination mount * @last_dest the last seen destination mount * @last_src the last seen source mount * @type return CL_SLAVE if the new mount has to be * cloned as a slave. */ static struct vfsmount *get_source(struct vfsmount *dest, struct vfsmount *last_dest, struct vfsmount *last_src, int *type) { struct vfsmount *p_last_src = NULL; struct vfsmount *p_last_dest = NULL; *type = CL_PROPAGATION; if (IS_MNT_SHARED(dest)) *type |= CL_MAKE_SHARED; while (last_dest != dest->mnt_master) { p_last_dest = last_dest; p_last_src = last_src; last_dest = last_dest->mnt_master; last_src = last_src->mnt_master; } if (p_last_dest) { do { p_last_dest = next_peer(p_last_dest); } while (IS_MNT_NEW(p_last_dest)); } if (dest != p_last_dest) { *type |= CL_SLAVE; return last_src; } else return p_last_src; } /* * mount 'source_mnt' under the destination 'dest_mnt' at * dentry 'dest_dentry'. And propagate that mount to * all the peer and slave mounts of 'dest_mnt'. * Link all the new mounts into a propagation tree headed at * source_mnt. Also link all the new mounts using ->mnt_list * headed at source_mnt's ->mnt_list * * @dest_mnt: destination mount. * @dest_dentry: destination dentry. * @source_mnt: source mount. * @tree_list : list of heads of trees to be attached. */ int propagate_mnt(struct vfsmount *dest_mnt, struct dentry *dest_dentry, struct vfsmount *source_mnt, struct list_head *tree_list) { struct vfsmount *m, *child; int ret = 0; struct vfsmount *prev_dest_mnt = dest_mnt; struct vfsmount *prev_src_mnt = source_mnt; LIST_HEAD(tmp_list); LIST_HEAD(umount_list); for (m = propagation_next(dest_mnt, dest_mnt); m; m = propagation_next(m, dest_mnt)) { int type; struct vfsmount *source; if (IS_MNT_NEW(m)) continue; source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); if (!(child = copy_tree(source, source->mnt_root, type))) { ret = -ENOMEM; list_splice(tree_list, tmp_list.prev); goto out; } if (is_subdir(dest_dentry, m->mnt_root)) { mnt_set_mountpoint(m, dest_dentry, child); list_add_tail(&child->mnt_hash, tree_list); } else { /* * This can happen if the parent mount was bind mounted * on some subdirectory of a shared/slave mount. */ list_add_tail(&child->mnt_hash, &tmp_list); } prev_dest_mnt = m; prev_src_mnt = child; } out: spin_lock(&vfsmount_lock); while (!list_empty(&tmp_list)) { child = list_entry(tmp_list.next, struct vfsmount, mnt_hash); list_del_init(&child->mnt_hash); umount_tree(child, 0, &umount_list); } spin_unlock(&vfsmount_lock); release_mounts(&umount_list); return ret; } /* * return true if the refcount is greater than count */ static inline int do_refcount_check(struct vfsmount *mnt, int count) { int mycount = atomic_read(&mnt->mnt_count); return (mycount > count); } /* * check if the mount 'mnt' can be unmounted successfully. * @mnt: the mount to be checked for unmount * NOTE: unmounting 'mnt' would naturally propagate to all * other mounts its parent propagates to. * Check if any of these mounts that **do not have submounts** * have more references than 'refcnt'. If so return busy. */ int propagate_mount_busy(struct vfsmount *mnt, int refcnt) { struct vfsmount *m, *child; struct vfsmount *parent = mnt->mnt_parent; int ret = 0; if (mnt == parent) return do_refcount_check(mnt, refcnt); /* * quickly check if the current mount can be unmounted. * If not, we don't have to go checking for all other * mounts */ if (!list_empty(&mnt->mnt_mounts) || do_refcount_check(mnt, refcnt)) return 1; for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { child = __lookup_mnt(m, mnt->mnt_mountpoint, 0); if (child && list_empty(&child->mnt_mounts) && (ret = do_refcount_check(child, 1))) break; } return ret; } /* * NOTE: unmounting 'mnt' naturally propagates to all other mounts its * parent propagates to. */ static void __propagate_umount(struct vfsmount *mnt) { struct vfsmount *parent = mnt->mnt_parent; struct vfsmount *m; BUG_ON(parent == mnt); for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { struct vfsmount *child = __lookup_mnt(m, mnt->mnt_mountpoint, 0); /* * umount the child only if the child has no * other children */ if (child && list_empty(&child->mnt_mounts)) { list_del(&child->mnt_hash); list_add_tail(&child->mnt_hash, &mnt->mnt_hash); } } } /* * collect all mounts that receive propagation from the mount in @list, * and return these additional mounts in the same list. * @list: the list of mounts to be unmounted. */ int propagate_umount(struct list_head *list) { struct vfsmount *mnt; list_for_each_entry(mnt, list, mnt_hash) __propagate_umount(mnt); return 0; } linux-2.6.17/fs/binfmt_elf_fdpic.c0000644000000000000000000007375010564537530015516 0ustar rootroot/* binfmt_elf_fdpic.c: FDPIC ELF binary format * * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * Derived from binfmt_elf.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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef char *elf_caddr_t; #ifndef elf_addr_t #define elf_addr_t unsigned long #endif #if 0 #define kdebug(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ ) #else #define kdebug(fmt, ...) do {} while(0) #endif MODULE_LICENSE("GPL"); static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs); //static int load_elf_fdpic_library(struct file *); static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file); static int elf_fdpic_map_file(struct elf_fdpic_params *params, struct file *file, struct mm_struct *mm, const char *what); static int create_elf_fdpic_tables(struct linux_binprm *bprm, struct mm_struct *mm, struct elf_fdpic_params *exec_params, struct elf_fdpic_params *interp_params); #ifndef CONFIG_MMU static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp); static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params, struct file *file, struct mm_struct *mm); #endif static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, struct file *file, struct mm_struct *mm); static struct linux_binfmt elf_fdpic_format = { .module = THIS_MODULE, .load_binary = load_elf_fdpic_binary, // .load_shlib = load_elf_fdpic_library, // .core_dump = elf_fdpic_core_dump, .min_coredump = ELF_EXEC_PAGESIZE, }; static int __init init_elf_fdpic_binfmt(void) { return register_binfmt(&elf_fdpic_format); } static void __exit exit_elf_fdpic_binfmt(void) { unregister_binfmt(&elf_fdpic_format); } module_init(init_elf_fdpic_binfmt) module_exit(exit_elf_fdpic_binfmt) static int is_elf_fdpic(struct elfhdr *hdr, struct file *file) { if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0) return 0; if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) return 0; if (!elf_check_arch(hdr) || !elf_check_fdpic(hdr)) return 0; if (!file->f_op || !file->f_op->mmap) return 0; return 1; } /*****************************************************************************/ /* * read the program headers table into memory */ static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file) { struct elf32_phdr *phdr; unsigned long size; int retval, loop; if (params->hdr.e_phentsize != sizeof(struct elf_phdr)) return -ENOMEM; if (params->hdr.e_phnum > 65536U / sizeof(struct elf_phdr)) return -ENOMEM; size = params->hdr.e_phnum * sizeof(struct elf_phdr); params->phdrs = kmalloc(size, GFP_KERNEL); if (!params->phdrs) return -ENOMEM; retval = kernel_read(file, params->hdr.e_phoff, (char *) params->phdrs, size); if (retval < 0) return retval; /* determine stack size for this binary */ phdr = params->phdrs; for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { if (phdr->p_type != PT_GNU_STACK) continue; if (phdr->p_flags & PF_X) params->flags |= ELF_FDPIC_FLAG_EXEC_STACK; else params->flags |= ELF_FDPIC_FLAG_NOEXEC_STACK; params->stack_size = phdr->p_memsz; break; } return 0; } /* end elf_fdpic_fetch_phdrs() */ /*****************************************************************************/ /* * load an fdpic binary into various bits of memory */ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs) { struct elf_fdpic_params exec_params, interp_params; struct elf_phdr *phdr; unsigned long stack_size; struct file *interpreter = NULL; /* to shut gcc up */ char *interpreter_name = NULL; int executable_stack; int retval, i; memset(&exec_params, 0, sizeof(exec_params)); memset(&interp_params, 0, sizeof(interp_params)); exec_params.hdr = *(struct elfhdr *) bprm->buf; exec_params.flags = ELF_FDPIC_FLAG_PRESENT | ELF_FDPIC_FLAG_EXECUTABLE; /* check that this is a binary we know how to deal with */ retval = -ENOEXEC; if (!is_elf_fdpic(&exec_params.hdr, bprm->file)) goto error; /* read the program header table */ retval = elf_fdpic_fetch_phdrs(&exec_params, bprm->file); if (retval < 0) goto error; /* scan for a program header that specifies an interpreter */ phdr = exec_params.phdrs; for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) { switch (phdr->p_type) { case PT_INTERP: retval = -ENOMEM; if (phdr->p_filesz > PATH_MAX) goto error; retval = -ENOENT; if (phdr->p_filesz < 2) goto error; /* read the name of the interpreter into memory */ interpreter_name = kmalloc(phdr->p_filesz, GFP_KERNEL); if (!interpreter_name) goto error; retval = kernel_read(bprm->file, phdr->p_offset, interpreter_name, phdr->p_filesz); if (retval < 0) goto error; retval = -ENOENT; if (interpreter_name[phdr->p_filesz - 1] != '\0') goto error; kdebug("Using ELF interpreter %s", interpreter_name); /* replace the program with the interpreter */ interpreter = open_exec(interpreter_name); retval = PTR_ERR(interpreter); if (IS_ERR(interpreter)) { interpreter = NULL; goto error; } retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE); if (retval < 0) goto error; interp_params.hdr = *((struct elfhdr *) bprm->buf); break; case PT_LOAD: #ifdef CONFIG_MMU if (exec_params.load_addr == 0) exec_params.load_addr = phdr->p_vaddr; #endif break; } } if (elf_check_const_displacement(&exec_params.hdr)) exec_params.flags |= ELF_FDPIC_FLAG_CONSTDISP; /* perform insanity checks on the interpreter */ if (interpreter_name) { retval = -ELIBBAD; if (!is_elf_fdpic(&interp_params.hdr, interpreter)) goto error; interp_params.flags = ELF_FDPIC_FLAG_PRESENT; /* read the interpreter's program header table */ retval = elf_fdpic_fetch_phdrs(&interp_params, interpreter); if (retval < 0) goto error; } stack_size = exec_params.stack_size; if (stack_size < interp_params.stack_size) stack_size = interp_params.stack_size; if (exec_params.flags & ELF_FDPIC_FLAG_EXEC_STACK) executable_stack = EXSTACK_ENABLE_X; else if (exec_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK) executable_stack = EXSTACK_DISABLE_X; else if (interp_params.flags & ELF_FDPIC_FLAG_EXEC_STACK) executable_stack = EXSTACK_ENABLE_X; else if (interp_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK) executable_stack = EXSTACK_DISABLE_X; else executable_stack = EXSTACK_DEFAULT; retval = -ENOEXEC; if (stack_size == 0) goto error; if (elf_check_const_displacement(&interp_params.hdr)) interp_params.flags |= ELF_FDPIC_FLAG_CONSTDISP; /* flush all traces of the currently running executable */ retval = flush_old_exec(bprm); if (retval) goto error; /* there's now no turning back... the old userspace image is dead, * defunct, deceased, etc. after this point we have to exit via * error_kill */ set_personality(PER_LINUX_FDPIC); set_binfmt(&elf_fdpic_format); current->mm->start_code = 0; current->mm->end_code = 0; current->mm->start_stack = 0; current->mm->start_data = 0; current->mm->end_data = 0; current->mm->context.exec_fdpic_loadmap = 0; current->mm->context.interp_fdpic_loadmap = 0; current->flags &= ~PF_FORKNOEXEC; #ifdef CONFIG_MMU elf_fdpic_arch_lay_out_mm(&exec_params, &interp_params, ¤t->mm->start_stack, ¤t->mm->start_brk); retval = setup_arg_pages(bprm, current->mm->start_stack, executable_stack); if (retval < 0) { send_sig(SIGKILL, current, 0); goto error_kill; } #endif /* load the executable and interpreter into memory */ retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm, "executable"); if (retval < 0) goto error_kill; if (interpreter_name) { retval = elf_fdpic_map_file(&interp_params, interpreter, current->mm, "interpreter"); if (retval < 0) { printk(KERN_ERR "Unable to load interpreter\n"); goto error_kill; } allow_write_access(interpreter); fput(interpreter); interpreter = NULL; } #ifdef CONFIG_MMU if (!current->mm->start_brk) current->mm->start_brk = current->mm->end_data; current->mm->brk = current->mm->start_brk = PAGE_ALIGN(current->mm->start_brk); #else /* create a stack and brk area big enough for everyone * - the brk heap starts at the bottom and works up * - the stack starts at the top and works down */ stack_size = (stack_size + PAGE_SIZE - 1) & PAGE_MASK; if (stack_size < PAGE_SIZE * 2) stack_size = PAGE_SIZE * 2; down_write(¤t->mm->mmap_sem); current->mm->start_brk = do_mmap(NULL, 0, stack_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN, 0); if (IS_ERR((void *) current->mm->start_brk)) { up_write(¤t->mm->mmap_sem); retval = current->mm->start_brk; current->mm->start_brk = 0; goto error_kill; } if (do_mremap(current->mm->start_brk, stack_size, ksize((char *) current->mm->start_brk), 0, 0 ) == current->mm->start_brk ) stack_size = ksize((char *) current->mm->start_brk); up_write(¤t->mm->mmap_sem); current->mm->brk = current->mm->start_brk; current->mm->context.end_brk = current->mm->start_brk; current->mm->context.end_brk += (stack_size > PAGE_SIZE) ? (stack_size - PAGE_SIZE) : 0; current->mm->start_stack = current->mm->start_brk + stack_size; #endif compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; if (create_elf_fdpic_tables(bprm, current->mm, &exec_params, &interp_params) < 0) goto error_kill; kdebug("- start_code %lx", (long) current->mm->start_code); kdebug("- end_code %lx", (long) current->mm->end_code); kdebug("- start_data %lx", (long) current->mm->start_data); kdebug("- end_data %lx", (long) current->mm->end_data); kdebug("- start_brk %lx", (long) current->mm->start_brk); kdebug("- brk %lx", (long) current->mm->brk); kdebug("- start_stack %lx", (long) current->mm->start_stack); #ifdef ELF_FDPIC_PLAT_INIT /* * The ABI may specify that certain registers be set up in special * ways (on i386 %edx is the address of a DT_FINI function, for * example. This macro performs whatever initialization to * the regs structure is required. */ ELF_FDPIC_PLAT_INIT(regs, exec_params.map_addr, interp_params.map_addr, interp_params.dynamic_addr ?: exec_params.dynamic_addr ); #endif /* everything is now ready... get the userspace context ready to roll */ start_thread(regs, interp_params.entry_addr ?: exec_params.entry_addr, current->mm->start_stack); if (unlikely(current->ptrace & PT_PTRACED)) { if (current->ptrace & PT_TRACE_EXEC) ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); else send_sig(SIGTRAP, current, 0); } retval = 0; error: if (interpreter) { allow_write_access(interpreter); fput(interpreter); } kfree(interpreter_name); kfree(exec_params.phdrs); kfree(exec_params.loadmap); kfree(interp_params.phdrs); kfree(interp_params.loadmap); return retval; /* unrecoverable error - kill the process */ error_kill: send_sig(SIGSEGV, current, 0); goto error; } /* end load_elf_fdpic_binary() */ /*****************************************************************************/ /* * present useful information to the program */ static int create_elf_fdpic_tables(struct linux_binprm *bprm, struct mm_struct *mm, struct elf_fdpic_params *exec_params, struct elf_fdpic_params *interp_params) { unsigned long sp, csp, nitems; elf_caddr_t *argv, *envp; size_t platform_len = 0, len; char *k_platform, *u_platform, *p; long hwcap; int loop; /* we're going to shovel a whole load of stuff onto the stack */ #ifdef CONFIG_MMU sp = bprm->p; #else sp = mm->start_stack; /* stack the program arguments and environment */ if (elf_fdpic_transfer_args_to_stack(bprm, &sp) < 0) return -EFAULT; #endif /* get hold of platform and hardware capabilities masks for the machine * we are running on. In some cases (Sparc), this info is impossible * to get, in others (i386) it is merely difficult. */ hwcap = ELF_HWCAP; k_platform = ELF_PLATFORM; if (k_platform) { platform_len = strlen(k_platform) + 1; sp -= platform_len; if (__copy_to_user(u_platform, k_platform, platform_len) != 0) return -EFAULT; } u_platform = (char *) sp; #if defined(__i386__) && defined(CONFIG_SMP) /* in some cases (e.g. Hyper-Threading), we want to avoid L1 evictions * by the processes running on the same package. One thing we can do * is to shuffle the initial stack for them. * * the conditionals here are unneeded, but kept in to make the * code behaviour the same as pre change unless we have hyperthreaded * processors. This keeps Mr Marcelo Person happier but should be * removed for 2.5 */ if (smp_num_siblings > 1) sp = sp - ((current->pid % 64) << 7); #endif sp &= ~7UL; /* stack the load map(s) */ len = sizeof(struct elf32_fdpic_loadmap); len += sizeof(struct elf32_fdpic_loadseg) * exec_params->loadmap->nsegs; sp = (sp - len) & ~7UL; exec_params->map_addr = sp; if (copy_to_user((void *) sp, exec_params->loadmap, len) != 0) return -EFAULT; current->mm->context.exec_fdpic_loadmap = (unsigned long) sp; if (interp_params->loadmap) { len = sizeof(struct elf32_fdpic_loadmap); len += sizeof(struct elf32_fdpic_loadseg) * interp_params->loadmap->nsegs; sp = (sp - len) & ~7UL; interp_params->map_addr = sp; if (copy_to_user((void *) sp, interp_params->loadmap, len) != 0) return -EFAULT; current->mm->context.interp_fdpic_loadmap = (unsigned long) sp; } /* force 16 byte _final_ alignment here for generality */ #define DLINFO_ITEMS 13 nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0); #ifdef DLINFO_ARCH_ITEMS nitems += DLINFO_ARCH_ITEMS; #endif csp = sp; sp -= nitems * 2 * sizeof(unsigned long); sp -= (bprm->envc + 1) * sizeof(char *); /* envv[] */ sp -= (bprm->argc + 1) * sizeof(char *); /* argv[] */ sp -= 1 * sizeof(unsigned long); /* argc */ csp -= sp & 15UL; sp -= sp & 15UL; /* put the ELF interpreter info on the stack */ #define NEW_AUX_ENT(nr, id, val) \ do { \ struct { unsigned long _id, _val; } *ent = (void *) csp; \ __put_user((id), &ent[nr]._id); \ __put_user((val), &ent[nr]._val); \ } while (0) csp -= 2 * sizeof(unsigned long); NEW_AUX_ENT(0, AT_NULL, 0); if (k_platform) { csp -= 2 * sizeof(unsigned long); NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform); } csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long); NEW_AUX_ENT( 0, AT_HWCAP, hwcap); NEW_AUX_ENT( 1, AT_PAGESZ, PAGE_SIZE); NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC); NEW_AUX_ENT( 3, AT_PHDR, exec_params->ph_addr); NEW_AUX_ENT( 4, AT_PHENT, sizeof(struct elf_phdr)); NEW_AUX_ENT( 5, AT_PHNUM, exec_params->hdr.e_phnum); NEW_AUX_ENT( 6, AT_BASE, interp_params->elfhdr_addr); NEW_AUX_ENT( 7, AT_FLAGS, 0); NEW_AUX_ENT( 8, AT_ENTRY, exec_params->entry_addr); NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid); NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid); NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid); NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid); #ifdef ARCH_DLINFO /* ARCH_DLINFO must come last so platform specific code can enforce * special alignment requirements on the AUXV if necessary (eg. PPC). */ ARCH_DLINFO; #endif #undef NEW_AUX_ENT /* allocate room for argv[] and envv[] */ csp -= (bprm->envc + 1) * sizeof(elf_caddr_t); envp = (elf_caddr_t *) csp; csp -= (bprm->argc + 1) * sizeof(elf_caddr_t); argv = (elf_caddr_t *) csp; /* stack argc */ csp -= sizeof(unsigned long); __put_user(bprm->argc, (unsigned long *) csp); BUG_ON(csp != sp); /* fill in the argv[] array */ #ifdef CONFIG_MMU current->mm->arg_start = bprm->p; #else current->mm->arg_start = current->mm->start_stack - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p); #endif p = (char *) current->mm->arg_start; for (loop = bprm->argc; loop > 0; loop--) { __put_user((elf_caddr_t) p, argv++); len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES); if (!len || len > PAGE_SIZE * MAX_ARG_PAGES) return -EINVAL; p += len; } __put_user(NULL, argv); current->mm->arg_end = (unsigned long) p; /* fill in the envv[] array */ current->mm->env_start = (unsigned long) p; for (loop = bprm->envc; loop > 0; loop--) { __put_user((elf_caddr_t)(unsigned long) p, envp++); len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES); if (!len || len > PAGE_SIZE * MAX_ARG_PAGES) return -EINVAL; p += len; } __put_user(NULL, envp); current->mm->env_end = (unsigned long) p; mm->start_stack = (unsigned long) sp; return 0; } /* end create_elf_fdpic_tables() */ /*****************************************************************************/ /* * transfer the program arguments and environment from the holding pages onto * the stack */ #ifndef CONFIG_MMU static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp) { unsigned long index, stop, sp; char *src; int ret = 0; stop = bprm->p >> PAGE_SHIFT; sp = *_sp; for (index = MAX_ARG_PAGES - 1; index >= stop; index--) { src = kmap(bprm->page[index]); sp -= PAGE_SIZE; if (copy_to_user((void *) sp, src, PAGE_SIZE) != 0) ret = -EFAULT; kunmap(bprm->page[index]); if (ret < 0) goto out; } *_sp = (*_sp - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p)) & ~15; out: return ret; } /* end elf_fdpic_transfer_args_to_stack() */ #endif /*****************************************************************************/ /* * load the appropriate binary image (executable or interpreter) into memory * - we assume no MMU is available * - if no other PIC bits are set in params->hdr->e_flags * - we assume that the LOADable segments in the binary are independently relocatable * - we assume R/O executable segments are shareable * - else * - we assume the loadable parts of the image to require fixed displacement * - the image is not shareable */ static int elf_fdpic_map_file(struct elf_fdpic_params *params, struct file *file, struct mm_struct *mm, const char *what) { struct elf32_fdpic_loadmap *loadmap; #ifdef CONFIG_MMU struct elf32_fdpic_loadseg *mseg; #endif struct elf32_fdpic_loadseg *seg; struct elf32_phdr *phdr; unsigned long load_addr, stop; unsigned nloads, tmp; size_t size; int loop, ret; /* allocate a load map table */ nloads = 0; for (loop = 0; loop < params->hdr.e_phnum; loop++) if (params->phdrs[loop].p_type == PT_LOAD) nloads++; if (nloads == 0) return -ELIBBAD; size = sizeof(*loadmap) + nloads * sizeof(*seg); loadmap = kmalloc(size, GFP_KERNEL); if (!loadmap) return -ENOMEM; params->loadmap = loadmap; memset(loadmap, 0, size); loadmap->version = ELF32_FDPIC_LOADMAP_VERSION; loadmap->nsegs = nloads; load_addr = params->load_addr; seg = loadmap->segs; /* map the requested LOADs into the memory space */ switch (params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) { case ELF_FDPIC_FLAG_CONSTDISP: case ELF_FDPIC_FLAG_CONTIGUOUS: #ifndef CONFIG_MMU ret = elf_fdpic_map_file_constdisp_on_uclinux(params, file, mm); if (ret < 0) return ret; break; #endif default: ret = elf_fdpic_map_file_by_direct_mmap(params, file, mm); if (ret < 0) return ret; break; } /* map the entry point */ if (params->hdr.e_entry) { seg = loadmap->segs; for (loop = loadmap->nsegs; loop > 0; loop--, seg++) { if (params->hdr.e_entry >= seg->p_vaddr && params->hdr.e_entry < seg->p_vaddr + seg->p_memsz ) { params->entry_addr = (params->hdr.e_entry - seg->p_vaddr) + seg->addr; break; } } } /* determine where the program header table has wound up if mapped */ stop = params->hdr.e_phoff + params->hdr.e_phnum * sizeof (struct elf_phdr); phdr = params->phdrs; for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { if (phdr->p_type != PT_LOAD) continue; if (phdr->p_offset > params->hdr.e_phoff || phdr->p_offset + phdr->p_filesz < stop) continue; seg = loadmap->segs; for (loop = loadmap->nsegs; loop > 0; loop--, seg++) { if (phdr->p_vaddr >= seg->p_vaddr && phdr->p_vaddr + phdr->p_filesz <= seg->p_vaddr + seg->p_memsz ) { params->ph_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr + params->hdr.e_phoff - phdr->p_offset; break; } } break; } /* determine where the dynamic section has wound up if there is one */ phdr = params->phdrs; for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { if (phdr->p_type != PT_DYNAMIC) continue; seg = loadmap->segs; for (loop = loadmap->nsegs; loop > 0; loop--, seg++) { if (phdr->p_vaddr >= seg->p_vaddr && phdr->p_vaddr + phdr->p_memsz <= seg->p_vaddr + seg->p_memsz ) { params->dynamic_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr; /* check the dynamic section contains at least one item, and that * the last item is a NULL entry */ if (phdr->p_memsz == 0 || phdr->p_memsz % sizeof(Elf32_Dyn) != 0) goto dynamic_error; tmp = phdr->p_memsz / sizeof(Elf32_Dyn); if (((Elf32_Dyn *) params->dynamic_addr)[tmp - 1].d_tag != 0) goto dynamic_error; break; } } break; } /* now elide adjacent segments in the load map on MMU linux * - on uClinux the holes between may actually be filled with system stuff or stuff from * other processes */ #ifdef CONFIG_MMU nloads = loadmap->nsegs; mseg = loadmap->segs; seg = mseg + 1; for (loop = 1; loop < nloads; loop++) { /* see if we have a candidate for merging */ if (seg->p_vaddr - mseg->p_vaddr == seg->addr - mseg->addr) { load_addr = PAGE_ALIGN(mseg->addr + mseg->p_memsz); if (load_addr == (seg->addr & PAGE_MASK)) { mseg->p_memsz += load_addr - (mseg->addr + mseg->p_memsz); mseg->p_memsz += seg->addr & ~PAGE_MASK; mseg->p_memsz += seg->p_memsz; loadmap->nsegs--; continue; } } mseg++; if (mseg != seg) *mseg = *seg; } #endif kdebug("Mapped Object [%s]:", what); kdebug("- elfhdr : %lx", params->elfhdr_addr); kdebug("- entry : %lx", params->entry_addr); kdebug("- PHDR[] : %lx", params->ph_addr); kdebug("- DYNAMIC[]: %lx", params->dynamic_addr); seg = loadmap->segs; for (loop = 0; loop < loadmap->nsegs; loop++, seg++) kdebug("- LOAD[%d] : %08x-%08x [va=%x ms=%x]", loop, seg->addr, seg->addr + seg->p_memsz - 1, seg->p_vaddr, seg->p_memsz); return 0; dynamic_error: printk("ELF FDPIC %s with invalid DYNAMIC section (inode=%lu)\n", what, file->f_dentry->d_inode->i_ino); return -ELIBBAD; } /* end elf_fdpic_map_file() */ /*****************************************************************************/ /* * map a file with constant displacement under uClinux */ #ifndef CONFIG_MMU static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params, struct file *file, struct mm_struct *mm) { struct elf32_fdpic_loadseg *seg; struct elf32_phdr *phdr; unsigned long load_addr, base = ULONG_MAX, top = 0, maddr = 0, mflags; loff_t fpos; int loop, ret; load_addr = params->load_addr; seg = params->loadmap->segs; /* determine the bounds of the contiguous overall allocation we must make */ phdr = params->phdrs; for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { if (params->phdrs[loop].p_type != PT_LOAD) continue; if (base > phdr->p_vaddr) base = phdr->p_vaddr; if (top < phdr->p_vaddr + phdr->p_memsz) top = phdr->p_vaddr + phdr->p_memsz; } /* allocate one big anon block for everything */ mflags = MAP_PRIVATE; if (params->flags & ELF_FDPIC_FLAG_EXECUTABLE) mflags |= MAP_EXECUTABLE; down_write(&mm->mmap_sem); maddr = do_mmap(NULL, load_addr, top - base, PROT_READ | PROT_WRITE | PROT_EXEC, mflags, 0); up_write(&mm->mmap_sem); if (IS_ERR((void *) maddr)) return (int) maddr; if (load_addr != 0) load_addr += PAGE_ALIGN(top - base); /* and then load the file segments into it */ phdr = params->phdrs; for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { if (params->phdrs[loop].p_type != PT_LOAD) continue; fpos = phdr->p_offset; seg->addr = maddr + (phdr->p_vaddr - base); seg->p_vaddr = phdr->p_vaddr; seg->p_memsz = phdr->p_memsz; ret = file->f_op->read(file, (void *) seg->addr, phdr->p_filesz, &fpos); if (ret < 0) return ret; /* map the ELF header address if in this segment */ if (phdr->p_offset == 0) params->elfhdr_addr = seg->addr; /* clear any space allocated but not loaded */ if (phdr->p_filesz < phdr->p_memsz) clear_user((void *) (seg->addr + phdr->p_filesz), phdr->p_memsz - phdr->p_filesz); if (mm) { if (phdr->p_flags & PF_X) { mm->start_code = seg->addr; mm->end_code = seg->addr + phdr->p_memsz; } else if (!mm->start_data) { mm->start_data = seg->addr; #ifndef CONFIG_MMU mm->end_data = seg->addr + phdr->p_memsz; #endif } #ifdef CONFIG_MMU if (seg->addr + phdr->p_memsz > mm->end_data) mm->end_data = seg->addr + phdr->p_memsz; #endif } seg++; } return 0; } /* end elf_fdpic_map_file_constdisp_on_uclinux() */ #endif /*****************************************************************************/ /* * map a binary by direct mmap() of the individual PT_LOAD segments */ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, struct file *file, struct mm_struct *mm) { struct elf32_fdpic_loadseg *seg; struct elf32_phdr *phdr; unsigned long load_addr, delta_vaddr; int loop, dvset; load_addr = params->load_addr; delta_vaddr = 0; dvset = 0; seg = params->loadmap->segs; /* deal with each load segment separately */ phdr = params->phdrs; for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { unsigned long maddr, disp, excess, excess1; int prot = 0, flags; if (phdr->p_type != PT_LOAD) continue; kdebug("[LOAD] va=%lx of=%lx fs=%lx ms=%lx", (unsigned long) phdr->p_vaddr, (unsigned long) phdr->p_offset, (unsigned long) phdr->p_filesz, (unsigned long) phdr->p_memsz); /* determine the mapping parameters */ if (phdr->p_flags & PF_R) prot |= PROT_READ; if (phdr->p_flags & PF_W) prot |= PROT_WRITE; if (phdr->p_flags & PF_X) prot |= PROT_EXEC; flags = MAP_PRIVATE | MAP_DENYWRITE; if (params->flags & ELF_FDPIC_FLAG_EXECUTABLE) flags |= MAP_EXECUTABLE; maddr = 0; switch (params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) { case ELF_FDPIC_FLAG_INDEPENDENT: /* PT_LOADs are independently locatable */ break; case ELF_FDPIC_FLAG_HONOURVADDR: /* the specified virtual address must be honoured */ maddr = phdr->p_vaddr; flags |= MAP_FIXED; break; case ELF_FDPIC_FLAG_CONSTDISP: /* constant displacement * - can be mapped anywhere, but must be mapped as a unit */ if (!dvset) { maddr = load_addr; delta_vaddr = phdr->p_vaddr; dvset = 1; } else { maddr = load_addr + phdr->p_vaddr - delta_vaddr; flags |= MAP_FIXED; } break; case ELF_FDPIC_FLAG_CONTIGUOUS: /* contiguity handled later */ break; default: BUG(); } maddr &= PAGE_MASK; /* create the mapping */ disp = phdr->p_vaddr & ~PAGE_MASK; down_write(&mm->mmap_sem); maddr = do_mmap(file, maddr, phdr->p_memsz + disp, prot, flags, phdr->p_offset - disp); up_write(&mm->mmap_sem); kdebug("mmap[%d] sz=%lx pr=%x fl=%x of=%lx --> %08lx", loop, phdr->p_memsz + disp, prot, flags, phdr->p_offset - disp, maddr); if (IS_ERR((void *) maddr)) return (int) maddr; if ((params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) == ELF_FDPIC_FLAG_CONTIGUOUS) load_addr += PAGE_ALIGN(phdr->p_memsz + disp); seg->addr = maddr + disp; seg->p_vaddr = phdr->p_vaddr; seg->p_memsz = phdr->p_memsz; /* map the ELF header address if in this segment */ if (phdr->p_offset == 0) params->elfhdr_addr = seg->addr; /* clear the bit between beginning of mapping and beginning of PT_LOAD */ if (prot & PROT_WRITE && disp > 0) { kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr, disp); clear_user((void *) maddr, disp); maddr += disp; } /* clear any space allocated but not loaded * - on uClinux we can just clear the lot * - on MMU linux we'll get a SIGBUS beyond the last page * extant in the file */ excess = phdr->p_memsz - phdr->p_filesz; excess1 = PAGE_SIZE - ((maddr + phdr->p_filesz) & ~PAGE_MASK); #ifdef CONFIG_MMU if (excess > excess1) { unsigned long xaddr = maddr + phdr->p_filesz + excess1; unsigned long xmaddr; flags |= MAP_FIXED | MAP_ANONYMOUS; down_write(&mm->mmap_sem); xmaddr = do_mmap(NULL, xaddr, excess - excess1, prot, flags, 0); up_write(&mm->mmap_sem); kdebug("mmap[%d] " " ad=%lx sz=%lx pr=%x fl=%x of=0 --> %08lx", loop, xaddr, excess - excess1, prot, flags, xmaddr); if (xmaddr != xaddr) return -ENOMEM; } if (prot & PROT_WRITE && excess1 > 0) { kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr + phdr->p_filesz, excess1); clear_user((void *) maddr + phdr->p_filesz, excess1); } #else if (excess > 0) { kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr + phdr->p_filesz, excess); clear_user((void *) maddr + phdr->p_filesz, excess); } #endif if (mm) { if (phdr->p_flags & PF_X) { mm->start_code = maddr; mm->end_code = maddr + phdr->p_memsz; } else if (!mm->start_data) { mm->start_data = maddr; mm->end_data = maddr + phdr->p_memsz; } } seg++; } return 0; } /* end elf_fdpic_map_file_by_direct_mmap() */ linux-2.6.17/fs/ext2/0000755000000000000000000000000010574207634012746 5ustar rootrootlinux-2.6.17/fs/ext2/xattr_security.c0000644000000000000000000000342610564537530016210 0ustar rootroot/* * linux/fs/ext2/xattr_security.c * Handler for storing security labels as extended attributes. */ #include #include #include #include #include #include #include "xattr.h" static size_t ext2_xattr_security_list(struct inode *inode, char *list, size_t list_size, const char *name, size_t name_len) { const int prefix_len = sizeof(XATTR_SECURITY_PREFIX)-1; const size_t total_len = prefix_len + name_len + 1; if (list && total_len <= list_size) { memcpy(list, XATTR_SECURITY_PREFIX, prefix_len); memcpy(list+prefix_len, name, name_len); list[prefix_len + name_len] = '\0'; } return total_len; } static int ext2_xattr_security_get(struct inode *inode, const char *name, void *buffer, size_t size) { if (strcmp(name, "") == 0) return -EINVAL; return ext2_xattr_get(inode, EXT2_XATTR_INDEX_SECURITY, name, buffer, size); } static int ext2_xattr_security_set(struct inode *inode, const char *name, const void *value, size_t size, int flags) { if (strcmp(name, "") == 0) return -EINVAL; return ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, name, value, size, flags); } int ext2_init_security(struct inode *inode, struct inode *dir) { int err; size_t len; void *value; char *name; err = security_inode_init_security(inode, dir, &name, &value, &len); if (err) { if (err == -EOPNOTSUPP) return 0; return err; } err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, name, value, len, 0); kfree(name); kfree(value); return err; } struct xattr_handler ext2_xattr_security_handler = { .prefix = XATTR_SECURITY_PREFIX, .list = ext2_xattr_security_list, .get = ext2_xattr_security_get, .set = ext2_xattr_security_set, }; linux-2.6.17/fs/ext2/dir.c0000644000000000000000000004062410564537530013676 0ustar rootroot/* * linux/fs/ext2/dir.c * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/fs/minix/dir.c * * Copyright (C) 1991, 1992 Linus Torvalds * * ext2 directory handling functions * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 * * All code that works with directory layout had been switched to pagecache * and moved here. AV */ #include "ext2.h" #include #include typedef struct ext2_dir_entry_2 ext2_dirent; /* * ext2 uses block-sized chunks. Arguably, sector-sized ones would be * more robust, but we have what we have */ static inline unsigned ext2_chunk_size(struct inode *inode) { return inode->i_sb->s_blocksize; } static inline void ext2_put_page(struct page *page) { kunmap(page); page_cache_release(page); } static inline unsigned long dir_pages(struct inode *inode) { return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; } /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. */ static unsigned ext2_last_byte(struct inode *inode, unsigned long page_nr) { unsigned last_byte = inode->i_size; last_byte -= page_nr << PAGE_CACHE_SHIFT; if (last_byte > PAGE_CACHE_SIZE) last_byte = PAGE_CACHE_SIZE; return last_byte; } static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to) { struct inode *dir = page->mapping->host; int err = 0; dir->i_version++; page->mapping->a_ops->commit_write(NULL, page, from, to); if (IS_DIRSYNC(dir)) err = write_one_page(page, 1); else unlock_page(page); return err; } static void ext2_check_page(struct page *page) { struct inode *dir = page->mapping->host; struct super_block *sb = dir->i_sb; unsigned chunk_size = ext2_chunk_size(dir); char *kaddr = page_address(page); u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count); unsigned offs, rec_len; unsigned limit = PAGE_CACHE_SIZE; ext2_dirent *p; char *error; if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { limit = dir->i_size & ~PAGE_CACHE_MASK; if (limit & (chunk_size - 1)) goto Ebadsize; if (!limit) goto out; } for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { p = (ext2_dirent *)(kaddr + offs); rec_len = le16_to_cpu(p->rec_len); if (rec_len < EXT2_DIR_REC_LEN(1)) goto Eshort; if (rec_len & 3) goto Ealign; if (rec_len < EXT2_DIR_REC_LEN(p->name_len)) goto Enamelen; if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) goto Espan; if (le32_to_cpu(p->inode) > max_inumber) goto Einumber; } if (offs != limit) goto Eend; out: SetPageChecked(page); return; /* Too bad, we had an error */ Ebadsize: ext2_error(sb, "ext2_check_page", "size of directory #%lu is not a multiple of chunk size", dir->i_ino ); goto fail; Eshort: error = "rec_len is smaller than minimal"; goto bad_entry; Ealign: error = "unaligned directory entry"; goto bad_entry; Enamelen: error = "rec_len is too small for name_len"; goto bad_entry; Espan: error = "directory entry across blocks"; goto bad_entry; Einumber: error = "inode out of bounds"; bad_entry: ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - " "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", dir->i_ino, error, (page->index<inode), rec_len, p->name_len); goto fail; Eend: p = (ext2_dirent *)(kaddr + offs); ext2_error (sb, "ext2_check_page", "entry in directory #%lu spans the page boundary" "offset=%lu, inode=%lu", dir->i_ino, (page->index<inode)); fail: SetPageChecked(page); SetPageError(page); } static struct page * ext2_get_page(struct inode *dir, unsigned long n) { struct address_space *mapping = dir->i_mapping; struct page *page = read_cache_page(mapping, n, (filler_t*)mapping->a_ops->readpage, NULL); if (!IS_ERR(page)) { wait_on_page_locked(page); kmap(page); if (!PageUptodate(page)) goto fail; if (!PageChecked(page)) ext2_check_page(page); if (PageError(page)) goto fail; } return page; fail: ext2_put_page(page); return ERR_PTR(-EIO); } /* * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. * * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller. */ static inline int ext2_match (int len, const char * const name, struct ext2_dir_entry_2 * de) { if (len != de->name_len) return 0; if (!de->inode) return 0; return !memcmp(name, de->name, len); } /* * p is at least 6 bytes before the end of page */ static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) { return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len)); } static inline unsigned ext2_validate_entry(char *base, unsigned offset, unsigned mask) { ext2_dirent *de = (ext2_dirent*)(base + offset); ext2_dirent *p = (ext2_dirent*)(base + (offset&mask)); while ((char*)p < (char*)de) { if (p->rec_len == 0) break; p = ext2_next_entry(p); } return (char *)p - base; } static unsigned char ext2_filetype_table[EXT2_FT_MAX] = { [EXT2_FT_UNKNOWN] = DT_UNKNOWN, [EXT2_FT_REG_FILE] = DT_REG, [EXT2_FT_DIR] = DT_DIR, [EXT2_FT_CHRDEV] = DT_CHR, [EXT2_FT_BLKDEV] = DT_BLK, [EXT2_FT_FIFO] = DT_FIFO, [EXT2_FT_SOCK] = DT_SOCK, [EXT2_FT_SYMLINK] = DT_LNK, }; #define S_SHIFT 12 static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFREG >> S_SHIFT] = EXT2_FT_REG_FILE, [S_IFDIR >> S_SHIFT] = EXT2_FT_DIR, [S_IFCHR >> S_SHIFT] = EXT2_FT_CHRDEV, [S_IFBLK >> S_SHIFT] = EXT2_FT_BLKDEV, [S_IFIFO >> S_SHIFT] = EXT2_FT_FIFO, [S_IFSOCK >> S_SHIFT] = EXT2_FT_SOCK, [S_IFLNK >> S_SHIFT] = EXT2_FT_SYMLINK, }; static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode) { mode_t mode = inode->i_mode; if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) de->file_type = ext2_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; else de->file_type = 0; } static int ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) { loff_t pos = filp->f_pos; struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; unsigned int offset = pos & ~PAGE_CACHE_MASK; unsigned long n = pos >> PAGE_CACHE_SHIFT; unsigned long npages = dir_pages(inode); unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); unsigned char *types = NULL; int need_revalidate = filp->f_version != inode->i_version; if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) return 0; if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) types = ext2_filetype_table; for ( ; n < npages; n++, offset = 0) { char *kaddr, *limit; ext2_dirent *de; struct page *page = ext2_get_page(inode, n); if (IS_ERR(page)) { ext2_error(sb, __FUNCTION__, "bad page in #%lu", inode->i_ino); filp->f_pos += PAGE_CACHE_SIZE - offset; return -EIO; } kaddr = page_address(page); if (unlikely(need_revalidate)) { if (offset) { offset = ext2_validate_entry(kaddr, offset, chunk_mask); filp->f_pos = (n<f_version = inode->i_version; need_revalidate = 0; } de = (ext2_dirent *)(kaddr+offset); limit = kaddr + ext2_last_byte(inode, n) - EXT2_DIR_REC_LEN(1); for ( ;(char*)de <= limit; de = ext2_next_entry(de)) { if (de->rec_len == 0) { ext2_error(sb, __FUNCTION__, "zero-length directory entry"); ext2_put_page(page); return -EIO; } if (de->inode) { int over; unsigned char d_type = DT_UNKNOWN; if (types && de->file_type < EXT2_FT_MAX) d_type = types[de->file_type]; offset = (char *)de - kaddr; over = filldir(dirent, de->name, de->name_len, (n<inode), d_type); if (over) { ext2_put_page(page); return 0; } } filp->f_pos += le16_to_cpu(de->rec_len); } ext2_put_page(page); } return 0; } /* * ext2_find_entry() * * finds an entry in the specified directory with the wanted name. It * returns the page in which the entry was found, and the entry itself * (as a parameter - res_dir). Page is returned mapped and unlocked. * Entry is guaranteed to be valid. */ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, struct dentry *dentry, struct page ** res_page) { const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; unsigned reclen = EXT2_DIR_REC_LEN(namelen); unsigned long start, n; unsigned long npages = dir_pages(dir); struct page *page = NULL; struct ext2_inode_info *ei = EXT2_I(dir); ext2_dirent * de; if (npages == 0) goto out; /* OFFSET_CACHE */ *res_page = NULL; start = ei->i_dir_start_lookup; if (start >= npages) start = 0; n = start; do { char *kaddr; page = ext2_get_page(dir, n); if (!IS_ERR(page)) { kaddr = page_address(page); de = (ext2_dirent *) kaddr; kaddr += ext2_last_byte(dir, n) - reclen; while ((char *) de <= kaddr) { if (de->rec_len == 0) { ext2_error(dir->i_sb, __FUNCTION__, "zero-length directory entry"); ext2_put_page(page); goto out; } if (ext2_match (namelen, name, de)) goto found; de = ext2_next_entry(de); } ext2_put_page(page); } if (++n >= npages) n = 0; } while (n != start); out: return NULL; found: *res_page = page; ei->i_dir_start_lookup = n; return de; } struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) { struct page *page = ext2_get_page(dir, 0); ext2_dirent *de = NULL; if (!IS_ERR(page)) { de = ext2_next_entry((ext2_dirent *) page_address(page)); *p = page; } return de; } ino_t ext2_inode_by_name(struct inode * dir, struct dentry *dentry) { ino_t res = 0; struct ext2_dir_entry_2 * de; struct page *page; de = ext2_find_entry (dir, dentry, &page); if (de) { res = le32_to_cpu(de->inode); kunmap(page); page_cache_release(page); } return res; } /* Releases the page */ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, struct page *page, struct inode *inode) { unsigned from = (char *) de - (char *) page_address(page); unsigned to = from + le16_to_cpu(de->rec_len); int err; lock_page(page); err = page->mapping->a_ops->prepare_write(NULL, page, from, to); BUG_ON(err); de->inode = cpu_to_le32(inode->i_ino); ext2_set_de_type (de, inode); err = ext2_commit_chunk(page, from, to); ext2_put_page(page); dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); } /* * Parent is locked. */ int ext2_add_link (struct dentry *dentry, struct inode *inode) { struct inode *dir = dentry->d_parent->d_inode; const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; unsigned chunk_size = ext2_chunk_size(dir); unsigned reclen = EXT2_DIR_REC_LEN(namelen); unsigned short rec_len, name_len; struct page *page = NULL; ext2_dirent * de; unsigned long npages = dir_pages(dir); unsigned long n; char *kaddr; unsigned from, to; int err; /* * We take care of directory expansion in the same loop. * This code plays outside i_size, so it locks the page * to protect that region. */ for (n = 0; n <= npages; n++) { char *dir_end; page = ext2_get_page(dir, n); err = PTR_ERR(page); if (IS_ERR(page)) goto out; lock_page(page); kaddr = page_address(page); dir_end = kaddr + ext2_last_byte(dir, n); de = (ext2_dirent *)kaddr; kaddr += PAGE_CACHE_SIZE - reclen; while ((char *)de <= kaddr) { if ((char *)de == dir_end) { /* We hit i_size */ name_len = 0; rec_len = chunk_size; de->rec_len = cpu_to_le16(chunk_size); de->inode = 0; goto got_it; } if (de->rec_len == 0) { ext2_error(dir->i_sb, __FUNCTION__, "zero-length directory entry"); err = -EIO; goto out_unlock; } err = -EEXIST; if (ext2_match (namelen, name, de)) goto out_unlock; name_len = EXT2_DIR_REC_LEN(de->name_len); rec_len = le16_to_cpu(de->rec_len); if (!de->inode && rec_len >= reclen) goto got_it; if (rec_len >= name_len + reclen) goto got_it; de = (ext2_dirent *) ((char *) de + rec_len); } unlock_page(page); ext2_put_page(page); } BUG(); return -EINVAL; got_it: from = (char*)de - (char*)page_address(page); to = from + rec_len; err = page->mapping->a_ops->prepare_write(NULL, page, from, to); if (err) goto out_unlock; if (de->inode) { ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); de1->rec_len = cpu_to_le16(rec_len - name_len); de->rec_len = cpu_to_le16(name_len); de = de1; } de->name_len = namelen; memcpy (de->name, name, namelen); de->inode = cpu_to_le32(inode->i_ino); ext2_set_de_type (de, inode); err = ext2_commit_chunk(page, from, to); dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); /* OFFSET_CACHE */ out_put: ext2_put_page(page); out: return err; out_unlock: unlock_page(page); goto out_put; } /* * ext2_delete_entry deletes a directory entry by merging it with the * previous entry. Page is up-to-date. Releases the page. */ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) { struct address_space *mapping = page->mapping; struct inode *inode = mapping->host; char *kaddr = page_address(page); unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len); ext2_dirent * pde = NULL; ext2_dirent * de = (ext2_dirent *) (kaddr + from); int err; while ((char*)de < (char*)dir) { if (de->rec_len == 0) { ext2_error(inode->i_sb, __FUNCTION__, "zero-length directory entry"); err = -EIO; goto out; } pde = de; de = ext2_next_entry(de); } if (pde) from = (char*)pde - (char*)page_address(page); lock_page(page); err = mapping->a_ops->prepare_write(NULL, page, from, to); BUG_ON(err); if (pde) pde->rec_len = cpu_to_le16(to-from); dir->inode = 0; err = ext2_commit_chunk(page, from, to); inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(inode); out: ext2_put_page(page); return err; } /* * Set the first fragment of directory. */ int ext2_make_empty(struct inode *inode, struct inode *parent) { struct address_space *mapping = inode->i_mapping; struct page *page = grab_cache_page(mapping, 0); unsigned chunk_size = ext2_chunk_size(inode); struct ext2_dir_entry_2 * de; int err; void *kaddr; if (!page) return -ENOMEM; err = mapping->a_ops->prepare_write(NULL, page, 0, chunk_size); if (err) { unlock_page(page); goto fail; } kaddr = kmap_atomic(page, KM_USER0); memset(kaddr, 0, chunk_size); de = (struct ext2_dir_entry_2 *)kaddr; de->name_len = 1; de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(1)); memcpy (de->name, ".\0\0", 4); de->inode = cpu_to_le32(inode->i_ino); ext2_set_de_type (de, inode); de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1)); de->name_len = 2; de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1)); de->inode = cpu_to_le32(parent->i_ino); memcpy (de->name, "..\0", 4); ext2_set_de_type (de, inode); kunmap_atomic(kaddr, KM_USER0); err = ext2_commit_chunk(page, 0, chunk_size); fail: page_cache_release(page); return err; } /* * routine to check that the specified directory is empty (for rmdir) */ int ext2_empty_dir (struct inode * inode) { struct page *page = NULL; unsigned long i, npages = dir_pages(inode); for (i = 0; i < npages; i++) { char *kaddr; ext2_dirent * de; page = ext2_get_page(inode, i); if (IS_ERR(page)) continue; kaddr = page_address(page); de = (ext2_dirent *)kaddr; kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1); while ((char *)de <= kaddr) { if (de->rec_len == 0) { ext2_error(inode->i_sb, __FUNCTION__, "zero-length directory entry"); printk("kaddr=%p, de=%p\n", kaddr, de); goto not_empty; } if (de->inode != 0) { /* check for . and .. */ if (de->name[0] != '.') goto not_empty; if (de->name_len > 2) goto not_empty; if (de->name_len < 2) { if (de->inode != cpu_to_le32(inode->i_ino)) goto not_empty; } else if (de->name[1] != '.') goto not_empty; } de = ext2_next_entry(de); } ext2_put_page(page); } return 1; not_empty: ext2_put_page(page); return 0; } const struct file_operations ext2_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = ext2_readdir, .ioctl = ext2_ioctl, .fsync = ext2_sync_file, }; linux-2.6.17/fs/ext2/super.c0000644000000000000000000010537010564537530014256 0ustar rootroot/* * linux/fs/ext2/super.c * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/fs/minix/inode.c * * Copyright (C) 1991, 1992 Linus Torvalds * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ext2.h" #include "xattr.h" #include "acl.h" #include "xip.h" static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es); static int ext2_remount (struct super_block * sb, int * flags, char * data); static int ext2_statfs (struct super_block * sb, struct kstatfs * buf); void ext2_error (struct super_block * sb, const char * function, const char * fmt, ...) { va_list args; struct ext2_sb_info *sbi = EXT2_SB(sb); struct ext2_super_block *es = sbi->s_es; if (!(sb->s_flags & MS_RDONLY)) { sbi->s_mount_state |= EXT2_ERROR_FS; es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) | EXT2_ERROR_FS); ext2_sync_super(sb, es); } va_start(args, fmt); printk(KERN_CRIT "EXT2-fs error (device %s): %s: ",sb->s_id, function); vprintk(fmt, args); printk("\n"); va_end(args); if (test_opt(sb, ERRORS_PANIC)) panic("EXT2-fs panic from previous error\n"); if (test_opt(sb, ERRORS_RO)) { printk("Remounting filesystem read-only\n"); sb->s_flags |= MS_RDONLY; } } void ext2_warning (struct super_block * sb, const char * function, const char * fmt, ...) { va_list args; va_start(args, fmt); printk(KERN_WARNING "EXT2-fs warning (device %s): %s: ", sb->s_id, function); vprintk(fmt, args); printk("\n"); va_end(args); } void ext2_update_dynamic_rev(struct super_block *sb) { struct ext2_super_block *es = EXT2_SB(sb)->s_es; if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV) return; ext2_warning(sb, __FUNCTION__, "updating to rev %d because of new feature flag, " "running e2fsck is recommended", EXT2_DYNAMIC_REV); es->s_first_ino = cpu_to_le32(EXT2_GOOD_OLD_FIRST_INO); es->s_inode_size = cpu_to_le16(EXT2_GOOD_OLD_INODE_SIZE); es->s_rev_level = cpu_to_le32(EXT2_DYNAMIC_REV); /* leave es->s_feature_*compat flags alone */ /* es->s_uuid will be set by e2fsck if empty */ /* * The rest of the superblock fields should be zero, and if not it * means they are likely already in use, so leave them alone. We * can leave it up to e2fsck to clean up any inconsistencies there. */ } static void ext2_put_super (struct super_block * sb) { int db_count; int i; struct ext2_sb_info *sbi = EXT2_SB(sb); ext2_xattr_put_super(sb); if (!(sb->s_flags & MS_RDONLY)) { struct ext2_super_block *es = sbi->s_es; es->s_state = cpu_to_le16(sbi->s_mount_state); ext2_sync_super(sb, es); } db_count = sbi->s_gdb_count; for (i = 0; i < db_count; i++) if (sbi->s_group_desc[i]) brelse (sbi->s_group_desc[i]); kfree(sbi->s_group_desc); kfree(sbi->s_debts); percpu_counter_destroy(&sbi->s_freeblocks_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_dirs_counter); brelse (sbi->s_sbh); sb->s_fs_info = NULL; kfree(sbi); return; } static kmem_cache_t * ext2_inode_cachep; static struct inode *ext2_alloc_inode(struct super_block *sb) { struct ext2_inode_info *ei; ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; #ifdef CONFIG_EXT2_FS_POSIX_ACL ei->i_acl = EXT2_ACL_NOT_CACHED; ei->i_default_acl = EXT2_ACL_NOT_CACHED; #endif ei->vfs_inode.i_version = 1; return &ei->vfs_inode; } static void ext2_destroy_inode(struct inode *inode) { kmem_cache_free(ext2_inode_cachep, EXT2_I(inode)); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct ext2_inode_info *ei = (struct ext2_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { rwlock_init(&ei->i_meta_lock); #ifdef CONFIG_EXT2_FS_XATTR init_rwsem(&ei->xattr_sem); #endif inode_init_once(&ei->vfs_inode); } } static int init_inodecache(void) { ext2_inode_cachep = kmem_cache_create("ext2_inode_cache", sizeof(struct ext2_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), init_once, NULL); if (ext2_inode_cachep == NULL) return -ENOMEM; return 0; } static void destroy_inodecache(void) { if (kmem_cache_destroy(ext2_inode_cachep)) printk(KERN_INFO "ext2_inode_cache: not all structures were freed\n"); } static void ext2_clear_inode(struct inode *inode) { #ifdef CONFIG_EXT2_FS_POSIX_ACL struct ext2_inode_info *ei = EXT2_I(inode); if (ei->i_acl && ei->i_acl != EXT2_ACL_NOT_CACHED) { posix_acl_release(ei->i_acl); ei->i_acl = EXT2_ACL_NOT_CACHED; } if (ei->i_default_acl && ei->i_default_acl != EXT2_ACL_NOT_CACHED) { posix_acl_release(ei->i_default_acl); ei->i_default_acl = EXT2_ACL_NOT_CACHED; } #endif } static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct ext2_sb_info *sbi = EXT2_SB(vfs->mnt_sb); if (sbi->s_mount_opt & EXT2_MOUNT_GRPID) seq_puts(seq, ",grpid"); #if defined(CONFIG_QUOTA) if (sbi->s_mount_opt & EXT2_MOUNT_USRQUOTA) seq_puts(seq, ",usrquota"); if (sbi->s_mount_opt & EXT2_MOUNT_GRPQUOTA) seq_puts(seq, ",grpquota"); #endif #if defined(CONFIG_EXT2_FS_XIP) if (sbi->s_mount_opt & EXT2_MOUNT_XIP) seq_puts(seq, ",xip"); #endif return 0; } #ifdef CONFIG_QUOTA static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off); static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off); #endif static struct super_operations ext2_sops = { .alloc_inode = ext2_alloc_inode, .destroy_inode = ext2_destroy_inode, .read_inode = ext2_read_inode, .write_inode = ext2_write_inode, .put_inode = ext2_put_inode, .delete_inode = ext2_delete_inode, .put_super = ext2_put_super, .write_super = ext2_write_super, .statfs = ext2_statfs, .remount_fs = ext2_remount, .clear_inode = ext2_clear_inode, .show_options = ext2_show_options, #ifdef CONFIG_QUOTA .quota_read = ext2_quota_read, .quota_write = ext2_quota_write, #endif }; static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp) { __u32 *objp = vobjp; unsigned long ino = objp[0]; __u32 generation = objp[1]; struct inode *inode; struct dentry *result; if (ino != EXT2_ROOT_INO && ino < EXT2_FIRST_INO(sb)) return ERR_PTR(-ESTALE); if (ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count)) return ERR_PTR(-ESTALE); /* iget isn't really right if the inode is currently unallocated!! * ext2_read_inode currently does appropriate checks, but * it might be "neater" to call ext2_get_inode first and check * if the inode is valid..... */ inode = iget(sb, ino); if (inode == NULL) return ERR_PTR(-ENOMEM); if (is_bad_inode(inode) || (generation && inode->i_generation != generation) ) { /* we didn't find the right inode.. */ iput(inode); return ERR_PTR(-ESTALE); } /* now to find a dentry. * If possible, get a well-connected one */ result = d_alloc_anon(inode); if (!result) { iput(inode); return ERR_PTR(-ENOMEM); } return result; } /* Yes, most of these are left as NULL!! * A NULL value implies the default, which works with ext2-like file * systems, but can be improved upon. * Currently only get_parent is required. */ static struct export_operations ext2_export_ops = { .get_parent = ext2_get_parent, .get_dentry = ext2_get_dentry, }; static unsigned long get_sb_block(void **data) { unsigned long sb_block; char *options = (char *) *data; if (!options || strncmp(options, "sb=", 3) != 0) return 1; /* Default location */ options += 3; sb_block = simple_strtoul(options, &options, 0); if (*options && *options != ',') { printk("EXT2-fs: Invalid sb specification: %s\n", (char *) *data); return 1; } if (*options == ',') options++; *data = (void *) options; return sb_block; } enum { Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota, Opt_usrquota, Opt_grpquota }; static match_table_t tokens = { {Opt_bsd_df, "bsddf"}, {Opt_minix_df, "minixdf"}, {Opt_grpid, "grpid"}, {Opt_grpid, "bsdgroups"}, {Opt_nogrpid, "nogrpid"}, {Opt_nogrpid, "sysvgroups"}, {Opt_resgid, "resgid=%u"}, {Opt_resuid, "resuid=%u"}, {Opt_sb, "sb=%u"}, {Opt_err_cont, "errors=continue"}, {Opt_err_panic, "errors=panic"}, {Opt_err_ro, "errors=remount-ro"}, {Opt_nouid32, "nouid32"}, {Opt_nocheck, "check=none"}, {Opt_nocheck, "nocheck"}, {Opt_debug, "debug"}, {Opt_oldalloc, "oldalloc"}, {Opt_orlov, "orlov"}, {Opt_nobh, "nobh"}, {Opt_user_xattr, "user_xattr"}, {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, {Opt_noacl, "noacl"}, {Opt_xip, "xip"}, {Opt_grpquota, "grpquota"}, {Opt_ignore, "noquota"}, {Opt_quota, "quota"}, {Opt_usrquota, "usrquota"}, {Opt_err, NULL} }; static int parse_options (char * options, struct ext2_sb_info *sbi) { char * p; substring_t args[MAX_OPT_ARGS]; unsigned long kind = EXT2_MOUNT_ERRORS_CON